|
Packit |
f46cda |
{-# LANGUAGE BangPatterns #-}
|
|
Packit |
f46cda |
{-# LANGUAGE Trustworthy #-}
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- |
|
|
Packit |
f46cda |
-- Module : Crypto.Hash.SHA256
|
|
Packit |
f46cda |
-- License : BSD-3
|
|
Packit |
f46cda |
-- Maintainer : Herbert Valerio Riedel <hvr@gnu.org>
|
|
Packit |
f46cda |
-- Stability : stable
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- A module containing <https://en.wikipedia.org/wiki/SHA-2 SHA-256> bindings
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
module Crypto.Hash.SHA256
|
|
Packit |
f46cda |
(
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- * Incremental API
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- | This API is based on 4 different functions, similar to the
|
|
Packit |
f46cda |
-- lowlevel operations of a typical hash:
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- - 'init': create a new hash context
|
|
Packit |
f46cda |
-- - 'update': update non-destructively a new hash context with a strict bytestring
|
|
Packit |
f46cda |
-- - 'updates': same as update, except that it takes a list of strict bytestrings
|
|
Packit |
f46cda |
-- - 'finalize': finalize the context and returns a digest bytestring.
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- all those operations are completely pure, and instead of
|
|
Packit |
f46cda |
-- changing the context as usual in others language, it
|
|
Packit |
f46cda |
-- re-allocates a new context each time.
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- Example:
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- > import qualified Data.ByteString
|
|
Packit |
f46cda |
-- > import qualified Crypto.Hash.SHA256 as SHA256
|
|
Packit |
f46cda |
-- >
|
|
Packit |
f46cda |
-- > main = print digest
|
|
Packit |
f46cda |
-- > where
|
|
Packit |
f46cda |
-- > digest = SHA256.finalize ctx
|
|
Packit |
f46cda |
-- > ctx = foldl SHA256.update ctx0 (map Data.ByteString.pack [ [1,2,3], [4,5,6] ])
|
|
Packit |
f46cda |
-- > ctx0 = SHA256.init
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
Ctx(..)
|
|
Packit |
f46cda |
, init -- :: Ctx
|
|
Packit |
f46cda |
, update -- :: Ctx -> ByteString -> Ctx
|
|
Packit |
f46cda |
, updates -- :: Ctx -> [ByteString] -> Ctx
|
|
Packit |
f46cda |
, finalize -- :: Ctx -> ByteString
|
|
Packit |
f46cda |
, finalizeAndLength -- :: Ctx -> (ByteString,Word64)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- * Single Pass API
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- | This API use the incremental API under the hood to provide
|
|
Packit |
f46cda |
-- the common all-in-one operations to create digests out of a
|
|
Packit |
f46cda |
-- 'ByteString' and lazy 'L.ByteString'.
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- - 'hash': create a digest ('init' + 'update' + 'finalize') from a strict 'ByteString'
|
|
Packit |
f46cda |
-- - 'hashlazy': create a digest ('init' + 'update' + 'finalize') from a lazy 'L.ByteString'
|
|
Packit |
f46cda |
-- - 'hashlazyAndLength': create a digest ('init' + 'update' + 'finalizeAndLength') from a lazy 'L.ByteString'
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- Example:
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- > import qualified Data.ByteString
|
|
Packit |
f46cda |
-- > import qualified Crypto.Hash.SHA256 as SHA256
|
|
Packit |
f46cda |
-- >
|
|
Packit |
f46cda |
-- > main = print $ SHA256.hash (Data.ByteString.pack [0..255])
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- __NOTE__: The returned digest is a binary 'ByteString'. For
|
|
Packit |
f46cda |
-- converting to a base16/hex encoded digest the
|
|
Packit |
f46cda |
-- <https://hackage.haskell.org/package/base16-bytestring base16-bytestring>
|
|
Packit |
f46cda |
-- package is recommended.
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
, hash -- :: ByteString -> ByteString
|
|
Packit |
f46cda |
, hashlazy -- :: L.ByteString -> ByteString
|
|
Packit |
f46cda |
, hashlazyAndLength -- :: L.ByteString -> (ByteString,Int64)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- ** HMAC-SHA-256
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- | <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible
|
|
Packit |
f46cda |
-- <https://en.wikipedia.org/wiki/HMAC HMAC>-SHA-256 digests
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
, hmac -- :: ByteString -> ByteString -> ByteString
|
|
Packit |
f46cda |
, hmaclazy -- :: ByteString -> L.ByteString -> ByteString
|
|
Packit |
f46cda |
, hmaclazyAndLength -- :: ByteString -> L.ByteString -> (ByteString,Word64)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- ** HKDF-SHA-256
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- | <https://tools.ietf.org/html/rfc5869 RFC5869>-compatible
|
|
Packit |
f46cda |
-- <https://en.wikipedia.org/wiki/HKDF HKDF>-SHA-256 key derivation function
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
, hkdf
|
|
Packit |
f46cda |
) where
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
import Data.Bits (xor)
|
|
Packit |
f46cda |
import Data.ByteString (ByteString)
|
|
Packit |
f46cda |
import qualified Data.ByteString as B
|
|
Packit |
f46cda |
import Data.ByteString.Internal (ByteString (PS), create,
|
|
Packit |
f46cda |
createAndTrim, mallocByteString,
|
|
Packit |
f46cda |
memcpy, toForeignPtr)
|
|
Packit |
f46cda |
import qualified Data.ByteString.Lazy as L
|
|
Packit |
f46cda |
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
|
|
Packit |
f46cda |
import Data.Word
|
|
Packit |
f46cda |
import Foreign.C.Types
|
|
Packit |
f46cda |
import Foreign.ForeignPtr (withForeignPtr)
|
|
Packit |
f46cda |
import Foreign.Marshal.Alloc
|
|
Packit |
f46cda |
import Foreign.Ptr
|
|
Packit |
f46cda |
import Prelude hiding (init)
|
|
Packit |
f46cda |
import System.IO.Unsafe (unsafeDupablePerformIO)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
import Crypto.Hash.SHA256.FFI
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- | perform IO for hashes that do allocation and ffi.
|
|
Packit |
f46cda |
-- unsafeDupablePerformIO is used when possible as the
|
|
Packit |
f46cda |
-- computation is pure and the output is directly linked
|
|
Packit |
f46cda |
-- to the input. we also do not modify anything after it has
|
|
Packit |
f46cda |
-- been returned to the user.
|
|
Packit |
f46cda |
unsafeDoIO :: IO a -> a
|
|
Packit |
f46cda |
unsafeDoIO = unsafeDupablePerformIO
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- keep this synchronised with cbits/sha256.h
|
|
Packit |
f46cda |
{-# INLINE digestSize #-}
|
|
Packit |
f46cda |
digestSize :: Int
|
|
Packit |
f46cda |
digestSize = 32
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# INLINE sizeCtx #-}
|
|
Packit |
f46cda |
sizeCtx :: Int
|
|
Packit |
f46cda |
sizeCtx = 104
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# INLINE withByteStringPtr #-}
|
|
Packit |
f46cda |
withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a
|
|
Packit |
f46cda |
withByteStringPtr b f =
|
|
Packit |
f46cda |
withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)
|
|
Packit |
f46cda |
where (fptr, off, _) = toForeignPtr b
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# INLINE create' #-}
|
|
Packit |
f46cda |
-- | Variant of 'create' which allows to return an argument
|
|
Packit |
f46cda |
create' :: Int -> (Ptr Word8 -> IO a) -> IO (ByteString,a)
|
|
Packit |
f46cda |
create' l f = do
|
|
Packit |
f46cda |
fp <- mallocByteString l
|
|
Packit |
f46cda |
x <- withForeignPtr fp $ \p -> f p
|
|
Packit |
f46cda |
let bs = PS fp 0 l
|
|
Packit |
f46cda |
return $! x `seq` bs `seq` (bs,x)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
copyCtx :: Ptr Ctx -> Ptr Ctx -> IO ()
|
|
Packit |
f46cda |
copyCtx dst src = memcpy (castPtr dst) (castPtr src) (fromIntegral sizeCtx)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx
|
|
Packit |
f46cda |
withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx
|
|
Packit |
f46cda |
where
|
|
Packit |
f46cda |
createCtx = create sizeCtx $ \dstPtr ->
|
|
Packit |
f46cda |
withByteStringPtr ctxB $ \srcPtr -> do
|
|
Packit |
f46cda |
copyCtx (castPtr dstPtr) (castPtr srcPtr)
|
|
Packit |
f46cda |
f (castPtr dstPtr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a
|
|
Packit |
f46cda |
withCtxThrow (Ctx ctxB) f =
|
|
Packit |
f46cda |
allocaBytes sizeCtx $ \dstPtr ->
|
|
Packit |
f46cda |
withByteStringPtr ctxB $ \srcPtr -> do
|
|
Packit |
f46cda |
copyCtx (castPtr dstPtr) (castPtr srcPtr)
|
|
Packit |
f46cda |
f (castPtr dstPtr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx
|
|
Packit |
f46cda |
withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a
|
|
Packit |
f46cda |
withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- 'safe' call overhead neglible for 4KiB and more
|
|
Packit |
f46cda |
c_sha256_update :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()
|
|
Packit |
f46cda |
c_sha256_update pctx pbuf sz
|
|
Packit |
f46cda |
| sz < 4096 = c_sha256_update_unsafe pctx pbuf sz
|
|
Packit |
f46cda |
| otherwise = c_sha256_update_safe pctx pbuf sz
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- 'safe' call overhead neglible for 4KiB and more
|
|
Packit |
f46cda |
c_sha256_hash :: Ptr Word8 -> CSize -> Ptr Word8 -> IO ()
|
|
Packit |
f46cda |
c_sha256_hash pbuf sz pout
|
|
Packit |
f46cda |
| sz < 4096 = c_sha256_hash_unsafe pbuf sz pout
|
|
Packit |
f46cda |
| otherwise = c_sha256_hash_safe pbuf sz pout
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
updateInternalIO :: Ptr Ctx -> ByteString -> IO ()
|
|
Packit |
f46cda |
updateInternalIO ptr d =
|
|
Packit |
f46cda |
unsafeUseAsCStringLen d (\(cs, len) -> c_sha256_update ptr (castPtr cs) (fromIntegral len))
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
finalizeInternalIO :: Ptr Ctx -> IO ByteString
|
|
Packit |
f46cda |
finalizeInternalIO ptr = create digestSize (c_sha256_finalize ptr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
finalizeInternalIO' :: Ptr Ctx -> IO (ByteString,Word64)
|
|
Packit |
f46cda |
finalizeInternalIO' ptr = create' digestSize (c_sha256_finalize_len ptr)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE init #-}
|
|
Packit |
f46cda |
-- | create a new hash context
|
|
Packit |
f46cda |
init :: Ctx
|
|
Packit |
f46cda |
init = unsafeDoIO $ withCtxNew c_sha256_init
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
validCtx :: Ctx -> Bool
|
|
Packit |
f46cda |
validCtx (Ctx b) = B.length b == sizeCtx
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE update #-}
|
|
Packit |
f46cda |
-- | update a context with a bytestring
|
|
Packit |
f46cda |
update :: Ctx -> ByteString -> Ctx
|
|
Packit |
f46cda |
update ctx d
|
|
Packit |
f46cda |
| validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d
|
|
Packit |
f46cda |
| otherwise = error "SHA256.update: invalid Ctx"
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE updates #-}
|
|
Packit |
f46cda |
-- | updates a context with multiple bytestrings
|
|
Packit |
f46cda |
updates :: Ctx -> [ByteString] -> Ctx
|
|
Packit |
f46cda |
updates ctx d
|
|
Packit |
f46cda |
| validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> mapM_ (updateInternalIO ptr) d
|
|
Packit |
f46cda |
| otherwise = error "SHA256.updates: invalid Ctx"
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE finalize #-}
|
|
Packit |
f46cda |
-- | finalize the context into a digest bytestring (32 bytes)
|
|
Packit |
f46cda |
finalize :: Ctx -> ByteString
|
|
Packit |
f46cda |
finalize ctx
|
|
Packit |
f46cda |
| validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO
|
|
Packit |
f46cda |
| otherwise = error "SHA256.finalize: invalid Ctx"
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE finalizeAndLength #-}
|
|
Packit |
f46cda |
-- | Variant of 'finalize' also returning length of hashed content
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.101.0
|
|
Packit |
f46cda |
finalizeAndLength :: Ctx -> (ByteString,Word64)
|
|
Packit |
f46cda |
finalizeAndLength ctx
|
|
Packit |
f46cda |
| validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO'
|
|
Packit |
f46cda |
| otherwise = error "SHA256.finalize: invalid Ctx"
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE hash #-}
|
|
Packit |
f46cda |
-- | hash a strict bytestring into a digest bytestring (32 bytes)
|
|
Packit |
f46cda |
hash :: ByteString -> ByteString
|
|
Packit |
f46cda |
-- hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr
|
|
Packit |
f46cda |
hash d = unsafeDoIO $ unsafeUseAsCStringLen d $ \(cs, len) -> create digestSize (c_sha256_hash (castPtr cs) (fromIntegral len))
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE hashlazy #-}
|
|
Packit |
f46cda |
-- | hash a lazy bytestring into a digest bytestring (32 bytes)
|
|
Packit |
f46cda |
hashlazy :: L.ByteString -> ByteString
|
|
Packit |
f46cda |
hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr ->
|
|
Packit |
f46cda |
c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE hashlazyAndLength #-}
|
|
Packit |
f46cda |
-- | Variant of 'hashlazy' which simultaneously computes the hash and length of a lazy bytestring.
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.101.0
|
|
Packit |
f46cda |
hashlazyAndLength :: L.ByteString -> (ByteString,Word64)
|
|
Packit |
f46cda |
hashlazyAndLength l = unsafeDoIO $ withCtxNewThrow $ \ptr ->
|
|
Packit |
f46cda |
c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO' ptr
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- | Compute 32-byte <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible
|
|
Packit |
f46cda |
-- HMAC-SHA-256 digest for a strict bytestring message
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.100.0
|
|
Packit |
f46cda |
hmac :: ByteString -- ^ secret
|
|
Packit |
f46cda |
-> ByteString -- ^ message
|
|
Packit |
f46cda |
-> ByteString -- ^ digest (32 bytes)
|
|
Packit |
f46cda |
hmac secret msg = hash $ B.append opad (hashlazy $ L.fromChunks [ipad,msg])
|
|
Packit |
f46cda |
where
|
|
Packit |
f46cda |
opad = B.map (xor 0x5c) k'
|
|
Packit |
f46cda |
ipad = B.map (xor 0x36) k'
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
k' = B.append kt pad
|
|
Packit |
f46cda |
kt = if B.length secret > 64 then hash secret else secret
|
|
Packit |
f46cda |
pad = B.replicate (64 - B.length kt) 0
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- | Compute 32-byte <https://tools.ietf.org/html/rfc2104 RFC2104>-compatible
|
|
Packit |
f46cda |
-- HMAC-SHA-256 digest for a lazy bytestring message
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.100.0
|
|
Packit |
f46cda |
hmaclazy :: ByteString -- ^ secret
|
|
Packit |
f46cda |
-> L.ByteString -- ^ message
|
|
Packit |
f46cda |
-> ByteString -- ^ digest (32 bytes)
|
|
Packit |
f46cda |
hmaclazy secret msg = hash $ B.append opad (hashlazy $ L.append ipad msg)
|
|
Packit |
f46cda |
where
|
|
Packit |
f46cda |
opad = B.map (xor 0x5c) k'
|
|
Packit |
f46cda |
ipad = L.fromChunks [B.map (xor 0x36) k']
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
k' = B.append kt pad
|
|
Packit |
f46cda |
kt = if B.length secret > 64 then hash secret else secret
|
|
Packit |
f46cda |
pad = B.replicate (64 - B.length kt) 0
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
-- | Variant of 'hmaclazy' which also returns length of message
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.101.0
|
|
Packit |
f46cda |
hmaclazyAndLength :: ByteString -- ^ secret
|
|
Packit |
f46cda |
-> L.ByteString -- ^ message
|
|
Packit |
f46cda |
-> (ByteString,Word64) -- ^ digest (32 bytes) and length of message
|
|
Packit |
f46cda |
hmaclazyAndLength secret msg =
|
|
Packit |
f46cda |
(hash (B.append opad htmp), sz' - fromIntegral ipadLen)
|
|
Packit |
f46cda |
where
|
|
Packit |
f46cda |
(htmp, sz') = hashlazyAndLength (L.append ipad msg)
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
opad = B.map (xor 0x5c) k'
|
|
Packit |
f46cda |
ipad = L.fromChunks [B.map (xor 0x36) k']
|
|
Packit |
f46cda |
ipadLen = B.length k'
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
k' = B.append kt pad
|
|
Packit |
f46cda |
kt = if B.length secret > 64 then hash secret else secret
|
|
Packit |
f46cda |
pad = B.replicate (64 - B.length kt) 0
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
{-# NOINLINE hkdf #-}
|
|
Packit |
f46cda |
-- | <https://tools.ietf.org/html/rfc6234 RFC6234>-compatible
|
|
Packit |
f46cda |
-- HKDF-SHA-256 key derivation function.
|
|
Packit |
f46cda |
--
|
|
Packit |
f46cda |
-- @since 0.11.101.0
|
|
Packit |
f46cda |
hkdf :: ByteString -- ^ /IKM/ Input keying material
|
|
Packit |
f46cda |
-> ByteString -- ^ /salt/ Optional salt value, a non-secret random value (can be @""@)
|
|
Packit |
f46cda |
-> ByteString -- ^ /info/ Optional context and application specific information (can be @""@)
|
|
Packit |
f46cda |
-> Int -- ^ /L/ length of output keying material in octets (at most 255*32 bytes)
|
|
Packit |
f46cda |
-> ByteString -- ^ /OKM/ Output keying material (/L/ bytes)
|
|
Packit |
f46cda |
hkdf ikm salt info l
|
|
Packit |
f46cda |
| l == 0 = B.empty
|
|
Packit |
f46cda |
| 0 > l || l > 255*32 = error "hkdf: invalid L parameter"
|
|
Packit |
f46cda |
| otherwise = unsafeDoIO $ createAndTrim (32*fromIntegral cnt) (go 0 B.empty)
|
|
Packit |
f46cda |
where
|
|
Packit |
f46cda |
prk = hmac salt ikm
|
|
Packit |
f46cda |
cnt = fromIntegral ((l+31) `div` 32) :: Word8
|
|
Packit |
f46cda |
|
|
Packit |
f46cda |
go :: Word8 -> ByteString -> Ptr Word8 -> IO Int
|
|
Packit |
f46cda |
go !i t !p | i == cnt = return l
|
|
Packit |
f46cda |
| otherwise = do
|
|
Packit |
f46cda |
let t' = hmaclazy prk (L.fromChunks [t,info,B.singleton (i+1)])
|
|
Packit |
f46cda |
withByteStringPtr t' $ \tptr' -> memcpy p tptr' 32
|
|
Packit |
f46cda |
go (i+1) t' (p `plusPtr` 32)
|