{-# LANGUAGE OverloadedStrings #-}

module Network.TLS.KeySchedule (
    hkdfExtract,
    hkdfExpandLabel,
    deriveSecret,
) where

import qualified Crypto.Hash as H
import Crypto.KDF.HKDF
import Data.ByteArray (convert)
import qualified Data.ByteString as BS
import Network.TLS.Crypto
import Network.TLS.Imports
import Network.TLS.Wire

----------------------------------------------------------------

-- | @HKDF-Extract@ function.  Returns the pseudorandom key (PRK) from salt and
-- input keying material (IKM).
hkdfExtract :: Hash -> ByteString -> ByteString -> ByteString
hkdfExtract :: Hash -> ByteString -> ByteString -> ByteString
hkdfExtract Hash
SHA1 ByteString
salt ByteString
ikm = PRK SHA1 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (ByteString -> ByteString -> PRK SHA1
forall a salt ikm.
(HashAlgorithm a, ByteArrayAccess salt, ByteArrayAccess ikm) =>
salt -> ikm -> PRK a
extract ByteString
salt ByteString
ikm :: PRK H.SHA1)
hkdfExtract Hash
SHA256 ByteString
salt ByteString
ikm = PRK SHA256 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (ByteString -> ByteString -> PRK SHA256
forall a salt ikm.
(HashAlgorithm a, ByteArrayAccess salt, ByteArrayAccess ikm) =>
salt -> ikm -> PRK a
extract ByteString
salt ByteString
ikm :: PRK H.SHA256)
hkdfExtract Hash
SHA384 ByteString
salt ByteString
ikm = PRK SHA384 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (ByteString -> ByteString -> PRK SHA384
forall a salt ikm.
(HashAlgorithm a, ByteArrayAccess salt, ByteArrayAccess ikm) =>
salt -> ikm -> PRK a
extract ByteString
salt ByteString
ikm :: PRK H.SHA384)
hkdfExtract Hash
SHA512 ByteString
salt ByteString
ikm = PRK SHA512 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (ByteString -> ByteString -> PRK SHA512
forall a salt ikm.
(HashAlgorithm a, ByteArrayAccess salt, ByteArrayAccess ikm) =>
salt -> ikm -> PRK a
extract ByteString
salt ByteString
ikm :: PRK H.SHA512)
hkdfExtract Hash
_ ByteString
_ ByteString
_ = [Char] -> ByteString
forall a. HasCallStack => [Char] -> a
error [Char]
"hkdfExtract: unsupported hash"

----------------------------------------------------------------

deriveSecret :: Hash -> ByteString -> ByteString -> ByteString -> ByteString
deriveSecret :: Hash -> ByteString -> ByteString -> ByteString -> ByteString
deriveSecret Hash
h ByteString
secret ByteString
label ByteString
hashedMsgs =
    Hash -> ByteString -> ByteString -> ByteString -> Int -> ByteString
hkdfExpandLabel Hash
h ByteString
secret ByteString
label ByteString
hashedMsgs Int
outlen
  where
    outlen :: Int
outlen = Hash -> Int
hashDigestSize Hash
h

----------------------------------------------------------------

-- | @HKDF-Expand-Label@ function.  Returns output keying material of the
-- specified length from the PRK, customized for a TLS label and context.
hkdfExpandLabel
    :: Hash
    -> ByteString
    -> ByteString
    -> ByteString
    -> Int
    -> ByteString
hkdfExpandLabel :: Hash -> ByteString -> ByteString -> ByteString -> Int -> ByteString
hkdfExpandLabel Hash
h ByteString
secret ByteString
label ByteString
ctx Int
outlen = Hash -> ByteString -> ByteString -> Int -> ByteString
expand' Hash
h ByteString
secret ByteString
hkdfLabel Int
outlen
  where
    hkdfLabel :: ByteString
hkdfLabel = Put -> ByteString
runPut (Put -> ByteString) -> Put -> ByteString
forall a b. (a -> b) -> a -> b
$ do
        Word16 -> Put
putWord16 (Word16 -> Put) -> Word16 -> Put
forall a b. (a -> b) -> a -> b
$ Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
outlen
        ByteString -> Put
putOpaque8 (ByteString
"tls13 " ByteString -> ByteString -> ByteString
`BS.append` ByteString
label)
        ByteString -> Put
putOpaque8 ByteString
ctx

expand' :: Hash -> ByteString -> ByteString -> Int -> ByteString
expand' :: Hash -> ByteString -> ByteString -> Int -> ByteString
expand' Hash
SHA1 ByteString
secret ByteString
label Int
len = PRK SHA1 -> ByteString -> Int -> ByteString
forall a info out.
(HashAlgorithm a, ByteArrayAccess info, ByteArray out) =>
PRK a -> info -> Int -> out
expand (ByteString -> PRK SHA1
forall ikm a. ByteArrayAccess ikm => ikm -> PRK a
extractSkip ByteString
secret :: PRK H.SHA1) ByteString
label Int
len
expand' Hash
SHA256 ByteString
secret ByteString
label Int
len = PRK SHA256 -> ByteString -> Int -> ByteString
forall a info out.
(HashAlgorithm a, ByteArrayAccess info, ByteArray out) =>
PRK a -> info -> Int -> out
expand (ByteString -> PRK SHA256
forall ikm a. ByteArrayAccess ikm => ikm -> PRK a
extractSkip ByteString
secret :: PRK H.SHA256) ByteString
label Int
len
expand' Hash
SHA384 ByteString
secret ByteString
label Int
len = PRK SHA384 -> ByteString -> Int -> ByteString
forall a info out.
(HashAlgorithm a, ByteArrayAccess info, ByteArray out) =>
PRK a -> info -> Int -> out
expand (ByteString -> PRK SHA384
forall ikm a. ByteArrayAccess ikm => ikm -> PRK a
extractSkip ByteString
secret :: PRK H.SHA384) ByteString
label Int
len
expand' Hash
SHA512 ByteString
secret ByteString
label Int
len = PRK SHA512 -> ByteString -> Int -> ByteString
forall a info out.
(HashAlgorithm a, ByteArrayAccess info, ByteArray out) =>
PRK a -> info -> Int -> out
expand (ByteString -> PRK SHA512
forall ikm a. ByteArrayAccess ikm => ikm -> PRK a
extractSkip ByteString
secret :: PRK H.SHA512) ByteString
label Int
len
expand' Hash
_ ByteString
_ ByteString
_ Int
_ = [Char] -> ByteString
forall a. HasCallStack => [Char] -> a
error [Char]
"expand'"

----------------------------------------------------------------