hpke-0.2.0: Crypto/HPKE/KDF.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.HPKE.KDF (
KDF (..),
HashAlgorithm,
SHA256 (..),
SHA384 (..),
SHA512 (..),
PRK,
extractAndExpand,
)
where
import Crypto.Hash.IO (hashDigestSize)
import Crypto.Hash.Algorithms (
HashAlgorithm,
SHA256 (..),
SHA384 (..),
SHA512 (..),
)
import Crypto.KDF.HKDF (PRK)
import qualified Crypto.KDF.HKDF as HKDF
import Crypto.HPKE.Types
----------------------------------------------------------------
class KDF h where
labeledExtract :: Suite -> Salt -> Label -> IKM -> PRK h
-- | RFC 9180 section 5.3 allows an output of at most @255 * Nh@ octets,
-- which is also what HKDF's counter can reach. A longer one is refused
-- here rather than left to the HKDF underneath, whose way of saying so
-- is an exception.
labeledExpand
:: Suite -> PRK h -> Label -> Info -> Int -> Either HPKEError Key
instance KDF SHA256 where
labeledExtract = labeledExtract_
labeledExpand = labeledExpand_
instance KDF SHA384 where
labeledExtract = labeledExtract_
labeledExpand = labeledExpand_
instance KDF SHA512 where
labeledExtract = labeledExtract_
labeledExpand = labeledExpand_
----------------------------------------------------------------
labeledExtract_
:: HashAlgorithm a => Suite -> Salt -> Label -> IKM -> PRK a
labeledExtract_ suite salt label ikm = HKDF.extract salt labeled_ikm
where
labeled_ikm = "HPKE-v1" <> suite <> label <> ikm
labeledExpand_
:: forall a
. HashAlgorithm a
=> Suite -> PRK a -> Label -> Info -> Int -> Either HPKEError Key
labeledExpand_ suite prk label info len
| len < 0 || len > maxLen =
Left $
ExportError $
"length "
++ show len
++ " is outside 0 .. "
++ show maxLen
| otherwise = Right $ HKDF.expand prk labeled_info len
where
maxLen = 255 * hashDigestSize (undefined :: a)
labeled_info =
i2ospOf_ 2 (fromIntegral len) <> "HPKE-v1" <> suite <> label <> info
----------------------------------------------------------------
extractAndExpand
:: forall h
. (HashAlgorithm h, KDF h)
=> h -> Suite -> KeyDeriveFunction
extractAndExpand h suite dh kem_context = shared_secret
where
eae_prk :: PRK h
eae_prk = labeledExtract suite "" "eae_prk" $ convert dh
siz = hashDigestSize h
-- the hash's own digest size, so the length is in range by construction
shared_secret =
either (const "") id $
labeledExpand suite eae_prk "shared_secret" kem_context siz