haskoin-crypto-0.0.1: Network/Haskoin/Crypto/Hash.hs
-- | Hashing functions and HMAC DRBG definition
module Network.Haskoin.Crypto.Hash
( Hash512
, Hash256
, Hash160
, CheckSum32
, hash512
, hash256
, hash160
, hash512BS
, hash256BS
, hash160BS
, doubleHash256
, doubleHash256BS
, chksum32
, hmac512
, hmac512BS
, hmac256
, hmac256BS
, hmacDRBGNew
, hmacDRBGUpd
, hmacDRBGRsd
, hmacDRBGGen
, WorkingState
, split512
, join512
) where
import Data.Word (Word16, Word32)
import Crypto.Hash
( Digest
, SHA512
, SHA256
, RIPEMD160
, hash
)
import Crypto.MAC.HMAC (hmac)
import Data.Byteable (toBytes)
import Data.Binary (Binary, get, put)
import Data.Bits (shiftL, shiftR)
import Control.Applicative ((<$>))
import qualified Data.ByteString as BS
( ByteString
, null
, append
, cons
, concat
, take
, empty
, length
, replicate
)
import Network.Haskoin.Util (runGet', decode')
import Network.Haskoin.Crypto.Ring
( Hash512
, Hash256
, Hash160
, toMod512
)
-- | Data type representing a 32 bit checksum
newtype CheckSum32 = CheckSum32 Word32 deriving (Show, Eq)
instance Binary CheckSum32 where
get = CheckSum32 <$> get
put (CheckSum32 w) = put w
run512 :: BS.ByteString -> BS.ByteString
run512 = (toBytes :: Digest SHA512 -> BS.ByteString) . hash
run256 :: BS.ByteString -> BS.ByteString
run256 = (toBytes :: Digest SHA256 -> BS.ByteString) . hash
run160 :: BS.ByteString -> BS.ByteString
run160 = (toBytes :: Digest RIPEMD160 -> BS.ByteString) . hash
-- | Computes SHA-512.
hash512 :: BS.ByteString -> Hash512
hash512 bs = runGet' get (run512 bs)
-- | Computes SHA-512 and returns the result as a bytestring.
hash512BS :: BS.ByteString -> BS.ByteString
hash512BS bs = run512 bs
-- | Computes SHA-256.
hash256 :: BS.ByteString -> Hash256
hash256 bs = runGet' get (run256 bs)
-- | Computes SHA-256 and returns the result as a bytestring.
hash256BS :: BS.ByteString -> BS.ByteString
hash256BS bs = run256 bs
-- | Computes RIPEMD-160.
hash160 :: BS.ByteString -> Hash160
hash160 bs = runGet' get (run160 bs)
-- | Computes RIPEMD-160 and returns the result as a bytestring.
hash160BS :: BS.ByteString -> BS.ByteString
hash160BS bs = run160 bs
-- | Computes two rounds of SHA-256.
doubleHash256 :: BS.ByteString -> Hash256
doubleHash256 bs = runGet' get (run256 $ run256 bs)
-- | Computes two rounds of SHA-256 and returns the result as a bytestring.
doubleHash256BS :: BS.ByteString -> BS.ByteString
doubleHash256BS bs = run256 $ run256 bs
{- CheckSum -}
-- | Computes a 32 bit checksum.
chksum32 :: BS.ByteString -> CheckSum32
chksum32 bs = CheckSum32 $ fromIntegral $ (doubleHash256 bs) `shiftR` 224
{- HMAC -}
-- | Computes HMAC over SHA-512.
hmac512 :: BS.ByteString -> BS.ByteString -> Hash512
hmac512 key = decode' . (hmac512BS key)
-- | Computes HMAC over SHA-512 and return the result as a bytestring.
hmac512BS :: BS.ByteString -> BS.ByteString -> BS.ByteString
hmac512BS key msg = hmac hash512BS 128 key msg
-- | Computes HMAC over SHA-256.
hmac256 :: BS.ByteString -> BS.ByteString -> Hash256
hmac256 key = decode' . (hmac256BS key)
-- | Computes HMAC over SHA-256 and return the result as a bytestring.
hmac256BS :: BS.ByteString -> BS.ByteString -> BS.ByteString
hmac256BS key msg = hmac hash256BS 64 key msg
-- | Split a 'Hash512' into a pair of 'Hash256'.
split512 :: Hash512 -> (Hash256, Hash256)
split512 i = (fromIntegral $ i `shiftR` 256, fromIntegral i)
-- | Join a pair of 'Hash256' into a 'Hash512'.
join512 :: (Hash256, Hash256) -> Hash512
join512 (a,b) = ((toMod512 a) `shiftL` 256) + (toMod512 b)
{- 10.1.2 HMAC_DRBG with HMAC-SHA256
http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf
Constants are based on recommentations in Appendix D section 2 (D.2)
-}
type WorkingState = (BS.ByteString, BS.ByteString, Word16)
type AdditionalInput = BS.ByteString
type ProvidedData = BS.ByteString
type EntropyInput = BS.ByteString
type Nonce = BS.ByteString
type PersString = BS.ByteString
-- 10.1.2.2 HMAC DRBG Update FUnction
hmacDRBGUpd :: ProvidedData -> BS.ByteString -> BS.ByteString
-> (BS.ByteString, BS.ByteString)
hmacDRBGUpd info k0 v0
| BS.null info = (k1,v1) -- 10.1.2.2.3
| otherwise = (k2,v2) -- 10.1.2.2.6
where
k1 = hmac256BS k0 $ v0 `BS.append` (0 `BS.cons` info) -- 10.1.2.2.1
v1 = hmac256BS k1 v0 -- 10.1.2.2.2
k2 = hmac256BS k1 $ v1 `BS.append` (1 `BS.cons` info) -- 10.1.2.2.4
v2 = hmac256BS k2 v1 -- 10.1.2.2.5
-- 10.1.2.3 HMAC DRBG Instantiation
hmacDRBGNew :: EntropyInput -> Nonce -> PersString -> WorkingState
hmacDRBGNew seed nonce info
| (BS.length seed + BS.length nonce) * 8 < 384 = error $
"Entropy + nonce input length must be at least 384 bit"
| (BS.length seed + BS.length nonce) * 8 > 1000 = error $
"Entropy + nonce input length can not be greater than 1000 bit"
| BS.length info * 8 > 256 = error $
"Maximum personalization string length is 256 bit"
| otherwise = (k1,v1,1) -- 10.1.2.3.6
where
s = BS.concat [seed, nonce, info] -- 10.1.2.3.1
k0 = BS.replicate 32 0 -- 10.1.2.3.2
v0 = BS.replicate 32 1 -- 10.1.2.3.3
(k1,v1) = hmacDRBGUpd s k0 v0 -- 10.1.2.3.4
-- 10.1.2.4 HMAC DRBG Reseeding
hmacDRBGRsd :: WorkingState -> EntropyInput -> AdditionalInput -> WorkingState
hmacDRBGRsd (k,v,_) seed info
| BS.length seed * 8 < 256 = error $
"Entropy input length must be at least 256 bit"
| BS.length seed * 8 > 1000 = error $
"Entropy input length can not be greater than 1000 bit"
| otherwise = (k0,v0,1) -- 10.1.2.4.4
where
s = seed `BS.append` info -- 10.1.2.4.1
(k0,v0) = hmacDRBGUpd s k v -- 10.1.2.4.2
-- 10.1.2.5 HMAC DRBG Generation
hmacDRBGGen :: WorkingState -> Word16 -> AdditionalInput
-> (WorkingState, Maybe BS.ByteString)
hmacDRBGGen (k0,v0,c0) bytes info
| bytes * 8 > 7500 = error "Maximum bits per request is 7500"
| c0 > 10000 = ((k0,v0,c0), Nothing) -- 10.1.2.5.1
| otherwise = ((k2,v3,c1), Just res) -- 10.1.2.5.8
where
(k1,v1) | BS.null info = (k0,v0)
| otherwise = hmacDRBGUpd info k0 v0 -- 10.1.2.5.2
(tmp,v2) = go (fromIntegral bytes) k1 v1 BS.empty -- 10.1.2.5.3/4
res = BS.take (fromIntegral bytes) tmp -- 10.1.2.5.5
(k2,v3) = hmacDRBGUpd info k1 v2 -- 10.1.2.5.6
c1 = c0 + 1 -- 10.1.2.5.7
go l k v acc | BS.length acc >= l = (acc,v)
| otherwise = let vn = hmac256BS k v
in go l k vn (acc `BS.append` vn)