{-# LANGUAGE TypeFamilies #-}
-------------------------------------------------
-- |
-- Module      : Crypto.Noise.Hash.SHA512
-- Maintainer  : John Galt <jgalt@centromere.net>
-- Stability   : experimental
-- Portability : POSIX
module Crypto.Noise.Hash.SHA512
  ( -- * Types
    SHA512
  ) where

import qualified Crypto.Hash     as H
import qualified Crypto.MAC.HMAC as M
import Data.ByteArray (ScrubbedBytes, convert, empty, snoc)
import Data.List      (unfoldr)
import Data.Word      (Word8)

import Crypto.Noise.Hash

-- | Represents the SHA512 hash.
data SHA512

instance Hash SHA512 where
  newtype ChainingKey SHA512 = HCKSHA512 ScrubbedBytes
  newtype Digest      SHA512 = HDSHA512  (H.Digest H.SHA512)

  hashName :: forall (proxy :: * -> *). proxy SHA512 -> ScrubbedBytes
hashName   proxy SHA512
_  = ScrubbedBytes
"SHA512"
  hashLength :: forall (proxy :: * -> *). proxy SHA512 -> Int
hashLength proxy SHA512
_  = Int
64
  hash :: ScrubbedBytes -> Digest SHA512
hash          = ScrubbedBytes -> Digest SHA512
hash'
  hashHKDF :: ChainingKey SHA512 -> ScrubbedBytes -> Word8 -> [ScrubbedBytes]
hashHKDF      = ChainingKey SHA512 -> ScrubbedBytes -> Word8 -> [ScrubbedBytes]
hkdf
  hashBytesToCK :: ScrubbedBytes -> ChainingKey SHA512
hashBytesToCK = ScrubbedBytes -> ChainingKey SHA512
bytesToCK
  hashCKToBytes :: ChainingKey SHA512 -> ScrubbedBytes
hashCKToBytes = ChainingKey SHA512 -> ScrubbedBytes
ckToBytes
  hashToBytes :: Digest SHA512 -> ScrubbedBytes
hashToBytes   = Digest SHA512 -> ScrubbedBytes
toBytes

hash' :: ScrubbedBytes -> Digest SHA512
hash' :: ScrubbedBytes -> Digest SHA512
hash' ScrubbedBytes
bs = Digest SHA512 -> Digest SHA512
HDSHA512 (Digest SHA512 -> Digest SHA512) -> Digest SHA512 -> Digest SHA512
forall a b. (a -> b) -> a -> b
$ ScrubbedBytes -> Digest SHA512
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
H.hash ScrubbedBytes
bs

hkdf :: ChainingKey SHA512
     -> ScrubbedBytes
     -> Word8
     -> [ScrubbedBytes]
hkdf :: ChainingKey SHA512 -> ScrubbedBytes -> Word8 -> [ScrubbedBytes]
hkdf (HCKSHA512 ScrubbedBytes
ck) ScrubbedBytes
keyMat Word8
numOutputs = (ScrubbedBytes, Word8) -> [ScrubbedBytes]
loop (ScrubbedBytes
forall a. ByteArray a => a
empty, Word8
1)
  where
    hmac :: key -> message -> ScrubbedBytes
hmac key
key message
info = HMAC SHA512 -> ScrubbedBytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (key -> message -> HMAC SHA512
forall key message a.
(ByteArrayAccess key, ByteArrayAccess message, HashAlgorithm a) =>
key -> message -> HMAC a
M.hmac key
key message
info :: M.HMAC H.SHA512) :: ScrubbedBytes
    tempKey :: ScrubbedBytes
tempKey = ScrubbedBytes -> ScrubbedBytes -> ScrubbedBytes
forall {key} {message}.
(ByteArrayAccess key, ByteArrayAccess message) =>
key -> message -> ScrubbedBytes
hmac ScrubbedBytes
ck ScrubbedBytes
keyMat
    loop :: (ScrubbedBytes, Word8) -> [ScrubbedBytes]
loop = ((ScrubbedBytes, Word8)
 -> Maybe (ScrubbedBytes, (ScrubbedBytes, Word8)))
-> (ScrubbedBytes, Word8) -> [ScrubbedBytes]
forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (((ScrubbedBytes, Word8)
  -> Maybe (ScrubbedBytes, (ScrubbedBytes, Word8)))
 -> (ScrubbedBytes, Word8) -> [ScrubbedBytes])
-> ((ScrubbedBytes, Word8)
    -> Maybe (ScrubbedBytes, (ScrubbedBytes, Word8)))
-> (ScrubbedBytes, Word8)
-> [ScrubbedBytes]
forall a b. (a -> b) -> a -> b
$ \(ScrubbedBytes
c, Word8
i) -> let r :: ScrubbedBytes
r = ScrubbedBytes -> ScrubbedBytes -> ScrubbedBytes
forall {key} {message}.
(ByteArrayAccess key, ByteArrayAccess message) =>
key -> message -> ScrubbedBytes
hmac ScrubbedBytes
tempKey (ScrubbedBytes
c ScrubbedBytes -> Word8 -> ScrubbedBytes
forall a. ByteArray a => a -> Word8 -> a
`snoc` Word8
i) in
      if Word8
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0
        then Maybe (ScrubbedBytes, (ScrubbedBytes, Word8))
forall a. Maybe a
Nothing
        else if Word8
i Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
numOutputs
          then (ScrubbedBytes, (ScrubbedBytes, Word8))
-> Maybe (ScrubbedBytes, (ScrubbedBytes, Word8))
forall a. a -> Maybe a
Just (ScrubbedBytes
r, (ScrubbedBytes
r, Word8
i Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word8
1))
          else Maybe (ScrubbedBytes, (ScrubbedBytes, Word8))
forall a. Maybe a
Nothing

bytesToCK :: ScrubbedBytes -> ChainingKey SHA512
bytesToCK :: ScrubbedBytes -> ChainingKey SHA512
bytesToCK = ScrubbedBytes -> ChainingKey SHA512
HCKSHA512

ckToBytes :: ChainingKey SHA512 -> ScrubbedBytes
ckToBytes :: ChainingKey SHA512 -> ScrubbedBytes
ckToBytes (HCKSHA512 ScrubbedBytes
ck) = ScrubbedBytes
ck

toBytes :: Digest SHA512 -> ScrubbedBytes
toBytes :: Digest SHA512 -> ScrubbedBytes
toBytes (HDSHA512 Digest SHA512
d) = Digest SHA512 -> ScrubbedBytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert Digest SHA512
d