packages feed

hpke (empty) → 0.0.0

raw patch · 23 files changed

+2186/−0 lines, 23 filesdep +QuickCheckdep +basedep +base16-bytestring

Dependencies added: QuickCheck, base, base16-bytestring, bytestring, crypton, hpke, hspec, memory

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# ChangeLog for hpke++## 0.0.0++* Initial release
+ Crypto/HPKE.hs view
@@ -0,0 +1,71 @@+-- | Hybrid Public Key Encryption (RFC9180).+module Crypto.HPKE (+    -- * IDs+    KEM_ID (..),+    KDF_ID (..),+    AEAD_ID (..),++    -- * Setup++    -- ** For mode_base and mode_auth+    setupBaseS,+    setupBaseR,++    -- ** For mode_psk and mode_auth_psk+    setupPSKS,+    setupPSKR,++    -- * Encryption and Decyption+    seal,+    open,++    -- * Secret export+    exportS,+    exportR,++    -- * Types+    ContextS,+    ContextR,+    EncodedSecretKey (..),+    EncodedPublicKey (..),+    SharedSecret (..),+    Info,+    PSK,+    PSK_ID,+    AAD,+    PlainText,+    CipherText,+    Key,++    -- * Error+    HPKEError (..),++    -- * Misc+    nEnc,+    nTag,+) where++import Crypto.HPKE.Context+import Crypto.HPKE.ID+import Crypto.HPKE.Setup+import Crypto.HPKE.Types++-- | Length of "enc", aka sender's public key.+{- FOURMOLU_DISABLE -}+nEnc :: KEM_ID -> Int+nEnc DHKEM_P256_HKDF_SHA256   =  65+nEnc DHKEM_P384_HKDF_SHA384   =  97+nEnc DHKEM_P521_HKDF_SHA512   = 133+nEnc DHKEM_X25519_HKDF_SHA256 =  32+nEnc DHKEM_X448_HKDF_SHA512   =  56+nEnc _                        =  0+{- FOURMOLU_ENABLE -}++-- | Length of AEAD tag.+{- FOURMOLU_DISABLE -}+nTag :: AEAD_ID -> Int+nTag AES_128_GCM      = 16+nTag AES_256_GCM      = 16+nTag ChaCha20Poly1305 = 16+nTag _                =  0+{- FOURMOLU_ENABLE -}
+ Crypto/HPKE/AEAD.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Crypto.HPKE.AEAD (+    Aead (..),+) where++import Crypto.Cipher.AES (AES128, AES256)+import qualified Crypto.Cipher.ChaChaPoly1305 as CCP+import Crypto.Cipher.Types (AEAD (..), AuthTag (..), BlockCipher)+import qualified Crypto.Cipher.Types as Cipher+import Data.ByteArray (ByteArray, ByteArrayAccess)+import qualified Data.ByteString as BS+import Data.Tuple (swap)++import Crypto.HPKE.Types++-- $setup+-- >>> :set -XOverloadedStrings+-- >>> import Data.ByteString++----------------------------------------------------------------++class Aead a where+    sealA :: Proxy a -> Key -> Seal+    openA :: Proxy a -> Key -> Open+    nK :: Proxy a -> Int+    nN :: Proxy a -> Int+    nT :: Proxy a -> Int++mkSealA :: AeadEncrypt -> p -> Key -> Seal+mkSealA enc _ key nonce aad plain = do+    (cipher, AuthTag tag) <- enc key nonce aad plain+    return (cipher <> convert tag)++mkOpenA :: AeadDecrypt -> Int -> p -> Key -> Open+mkOpenA dec len _ key nonce aad cipher = do+    (plain, AuthTag tag) <- dec key nonce aad cipher'+    if tag == convert tag'+        then Right plain+        else Left $ OpenError "tag mismatch"+  where+    brkpt = BS.length cipher - len+    (cipher', tag') = BS.splitAt brkpt cipher++----------------------------------------------------------------++-- 'forall' is necessary because of 'type'+type AeadEncrypt =+    forall k n a t+     . ( ByteArray k+       , ByteArrayAccess n+       , ByteArrayAccess a+       , ByteArray t+       )+    => k -> n -> a -> t -> Either HPKEError (t, AuthTag)++type AeadDecrypt =+    forall k n a t+     . ( ByteArray k+       , ByteArrayAccess n+       , ByteArrayAccess a+       , ByteArray t+       )+    => k -> n -> a -> t -> Either HPKEError (t, AuthTag)++----------------------------------------------------------------++initAES+    :: ( ByteArray k+       , ByteArrayAccess n+       , BlockCipher c+       )+    => k -> n -> Maybe (AEAD c)+initAES key nonce = case mst of+    CryptoPassed st -> Just st+    CryptoFailed _ -> Nothing+  where+    mst = do+        st0 <- Cipher.cipherInit key+        Cipher.aeadInit Cipher.AEAD_GCM st0 nonce++----------------------------------------------------------------++-- | From RFC 9180 A.1+--+-- >>> let key = "\x45\x31\x68\x5d\x41\xd6\x5f\x03\xdc\x48\xf6\xb8\x30\x2c\x05\xb0" :: ByteString+-- >>> let nonce = "\x56\xd8\x90\xe5\xac\xca\xaf\x01\x1c\xff\x4b\x7d" :: ByteString+-- >>> let aad = "\x43\x6f\x75\x6e\x74\x2d\x30" :: ByteString+-- >>> let plain = "The quick brown fox jumps over the very lazy dog." :: ByteString+-- >>> let proxy = Proxy :: Proxy AES128+-- >>> sealA proxy key nonce aad plain >>= openA proxy key nonce aad+-- Right "The quick brown fox jumps over the very lazy dog."+instance Aead AES128 where+    sealA = mkSealA encryptAes128gcm+    openA = mkOpenA decryptAes128gcm aes128tagLength+    nK = const 16+    nN = const 12+    nT = const 16++encryptAes128gcm :: AeadEncrypt+encryptAes128gcm key nonce aad plain = case initAES key nonce of+    Nothing -> Left $ SealError "encryptAes128gcm"+    Just st -> Right $ simpleEncrypt (st :: AEAD AES128) aad plain aes128tagLength++decryptAes128gcm :: AeadDecrypt+decryptAes128gcm key nonce aad cipher = case initAES key nonce of+    Nothing -> Left $ OpenError "decrypttAes128gcm"+    Just st -> Right $ simpleDecrypt (st :: AEAD AES128) aad cipher aes128tagLength++aes128tagLength :: Int+aes128tagLength = 16++----------------------------------------------------------------++-- | From RFC 9180 A.6+--+-- >>> let key = "\x75\x1e\x34\x6c\xe8\xf0\xdd\xb2\x30\x5c\x8a\x2a\x85\xc7\x0d\x5c\xf5\x59\xc5\x30\x93\x65\x6b\xe6\x36\xb9\x40\x6d\x4d\x7d\x1b\x70" :: ByteString+-- >>> let nonce = "\x55\xff\x7a\x7d\x73\x9c\x69\xf4\x4b\x25\x44\x7b" :: ByteString+-- >>> let aad = "\x43\x6f\x75\x6e\x74\x2d\x30" :: ByteString+-- >>> let plain = "The quick brown fox jumps over the very lazy dog." :: ByteString+-- >>> let proxy = Proxy :: Proxy AES256+-- >>> sealA proxy key nonce aad plain >>= openA proxy key nonce aad+-- Right "The quick brown fox jumps over the very lazy dog."+instance Aead AES256 where+    sealA = mkSealA encryptAes256gcm+    openA = mkOpenA decryptAes256gcm aes256tagLength+    nK = const 32+    nN = const 12+    nT = const 16++encryptAes256gcm :: AeadEncrypt+encryptAes256gcm key nonce aad plain = case initAES key nonce of+    Nothing -> Left $ SealError "encryptAes256gcm"+    Just st -> Right $ simpleEncrypt (st :: AEAD AES256) aad plain aes256tagLength++decryptAes256gcm :: AeadDecrypt+decryptAes256gcm key nonce aad cipher = case initAES key nonce of+    Nothing -> Left $ OpenError "decryptAes256gcm"+    Just st -> Right $ simpleDecrypt (st :: AEAD AES256) aad cipher aes256tagLength++aes256tagLength :: Int+aes256tagLength = 16++----------------------------------------------------------------++-- | From RFC 9180 A.5+--+-- >>> let key = "\xa8\xf4\x54\x90\xa9\x2a\x3b\x04\xd1\xdb\xf6\xcf\x2c\x39\x39\xad\x8b\xfc\x9b\xfc\xb9\x7c\x04\xbf\xfe\x11\x67\x30\xc9\xdf\xe3\xfc" :: ByteString+-- >>> let nonce = "\x72\x6b\x43\x90\xed\x22\x09\x80\x9f\x58\xc6\x93" :: ByteString+-- >>> let aad = "\x43\x6f\x75\x6e\x74\x2d\x30" :: ByteString+-- >>> let plain = "The quick brown fox jumps over the very lazy dog." :: ByteString+-- >>> let proxy = Proxy :: Proxy CCP.ChaCha20Poly1305+-- >>> sealA proxy key nonce aad plain >>= openA proxy key nonce aad+-- Right "The quick brown fox jumps over the very lazy dog."+instance Aead CCP.ChaCha20Poly1305 where+    sealA = mkSealA encryptChacha20poly1305+    openA = mkOpenA decryptChacha20poly1305 chacha20poly1305tagLength+    nK = const 32+    nN = const 12+    nT = const 16++encryptChacha20poly1305 :: AeadEncrypt+encryptChacha20poly1305 key nonce aad plain =+    case CCP.aeadChacha20poly1305Init key nonce of+        CryptoPassed st -> Right $ simpleEncrypt st aad plain chacha20poly1305tagLength+        CryptoFailed _ -> Left $ SealError "encryptChacha20poly1305"++decryptChacha20poly1305 :: AeadDecrypt+decryptChacha20poly1305 key nonce aad cipher =+    case CCP.aeadChacha20poly1305Init key nonce of+        CryptoPassed st -> Right $ simpleDecrypt st aad cipher chacha20poly1305tagLength+        CryptoFailed _ -> Left $ SealError "decryptChacha20poly1305"++chacha20poly1305tagLength :: Int+chacha20poly1305tagLength = 16++----------------------------------------------------------------++simpleEncrypt+    :: (ByteArrayAccess a, ByteArray t)+    => AEAD cipher -> a -> t -> Int -> (t, AuthTag)+simpleEncrypt st aad plain taglen =+    swap $ Cipher.aeadSimpleEncrypt st aad plain taglen++simpleDecrypt+    :: (ByteArrayAccess a, ByteArray t)+    => AEAD cipher -> a -> t -> Int -> (t, AuthTag)+simpleDecrypt st aad cipher taglen = (plain, tag)+  where+    st2 = Cipher.aeadAppendHeader st aad+    (plain, st3) = Cipher.aeadDecrypt st2 cipher+    tag = Cipher.aeadFinalize st3 taglen
+ Crypto/HPKE/Context.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE RecordWildCards #-}++module Crypto.HPKE.Context (+    -- * Sender+    ContextS,+    newContextS,+    seal,+    exportS,++    -- * Receiver+    ContextR,+    newContextR,+    open,+    exportR,+) where++import qualified Control.Exception as E+import Data.ByteArray (xor)+import qualified Data.ByteString as BS+import Data.IORef (IORef, newIORef, readIORef, writeIORef)++import Crypto.HPKE.Types++----------------------------------------------------------------++-- | Context for senders.+data ContextS = ContextS+    { seqRefS :: IORef Integer+    , sealS :: Seal+    , nonceBaseS :: Nonce+    , expandS :: Info -> Int -> Key+    }++-- | Context for receivers.+data ContextR = ContextR+    { seqRefR :: IORef Integer+    , openR :: Open+    , nonceBaseR :: Nonce+    , expandR :: Info -> Int -> Key+    }++----------------------------------------------------------------++-- | Encryption.+--   This throws 'HPKEError'.+seal :: ContextS -> AAD -> PlainText -> IO CipherText+seal ContextS{..} aad pt = do+    seqI <- readIORef seqRefS+    let len = BS.length nonceBaseS+        seqBS = i2ospOf_ len seqI :: ByteString+        nonce = seqBS `xor` nonceBaseS+        ect = sealS nonce aad pt+        seqI' = seqI + 1+    writeIORef seqRefS seqI'+    case ect of+        Right ct -> return ct+        Left err -> E.throwIO err++-- | Decryption.+--   This throws 'HPKEError'.+open+    :: ContextR -> AAD -> CipherText -> IO PlainText+open ContextR{..} aad ct = do+    seqI <- readIORef seqRefR+    let len = BS.length nonceBaseR+        seqBS = i2ospOf_ len seqI :: ByteString+        nonce = seqBS `xor` nonceBaseR+        ept = openR nonce aad ct+    case ept of+        Left err -> E.throwIO err+        Right pt -> do+            let seqI' = seqI + 1+            writeIORef seqRefR seqI'+            return pt++----------------------------------------------------------------++-- | Exporting secret.+exportS :: ContextS -> Info -> Int -> Key+exportS ContextS{..} exporter_context len =+    expandS exporter_context len++-- | Exporting secret.+exportR :: ContextR -> Info -> Int -> Key+exportR ContextR{..} exporter_context len =+    expandR exporter_context len++----------------------------------------------------------------++newContextS+    :: Key+    -> Nonce+    -> (Key -> Seal)+    -> (Info -> Int -> Key)+    -> IO ContextS+newContextS key nonce_base seal' expand = do+    seqref <- newIORef 0+    return $+        ContextS+            { seqRefS = seqref+            , sealS = seal' key+            , nonceBaseS = nonce_base+            , expandS = expand+            }++----------------------------------------------------------------++newContextR+    :: Key+    -> Nonce+    -> (Key -> Open)+    -> (Info -> Int -> Key)+    -> IO ContextR+newContextR key nonce_base open' expand = do+    seqref <- newIORef 0+    return $+        ContextR+            { seqRefR = seqref+            , openR = open' key+            , nonceBaseR = nonce_base+            , expandR = expand+            }
+ Crypto/HPKE/ID.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE PatternSynonyms #-}++module Crypto.HPKE.ID (+    AEAD_ID (AES_128_GCM, AES_256_GCM, ChaCha20Poly1305, ..),+    AEADCipher (..),+    defaultAEADMap,+    --+    KDF_ID (HKDF_SHA256, HKDF_SHA384, HKDF_SHA512, ..),+    KDFHash (..),+    defaultKDFMap,+    --+    KEM_ID (+        DHKEM_P256_HKDF_SHA256,+        DHKEM_P384_HKDF_SHA384,+        DHKEM_P521_HKDF_SHA512,+        DHKEM_X25519_HKDF_SHA256,+        DHKEM_X448_HKDF_SHA512,+        ..+    ),+    defaultKEMMap,+    KEMGroup (..),+    --+    HPKEMap (..),+    defaultHPKEMap,+) where++import Crypto.Cipher.AES (AES128, AES256)+import Crypto.Cipher.ChaChaPoly1305 (ChaCha20Poly1305)+import Crypto.ECC (+    Curve_P256R1,+    Curve_P384R1,+    Curve_P521R1,+    Curve_X25519,+    Curve_X448,+    EllipticCurve (..),+    EllipticCurveDH (..),+ )+import Data.Proxy (Proxy (..))+import Data.Word (Word16)+import Text.Printf (printf)++import Crypto.HPKE.AEAD+import Crypto.HPKE.KDF++----------------------------------------------------------------++-- | ID for authenticated encryption with additional data+newtype AEAD_ID = AEAD_ID {fromAEAD_ID :: Word16} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern AES_128_GCM      :: AEAD_ID+pattern AES_128_GCM       = AEAD_ID 0x0001+pattern AES_256_GCM      :: AEAD_ID+pattern AES_256_GCM       = AEAD_ID 0x0002+pattern ChaCha20Poly1305 :: AEAD_ID+pattern ChaCha20Poly1305  = AEAD_ID 0x0003++instance Show AEAD_ID where+    show AES_128_GCM      = "AES_128_GCM"+    show AES_256_GCM      = "AES_256_GCM"+    show ChaCha20Poly1305 = "ChaCha20Poly1305"+    show (AEAD_ID n)      = "AEAD_ID 0x" ++ printf "%04x" n+{- FOURMOLU_ENABLE -}++{- FOURMOLU_DISABLE -}+aes128 :: Proxy AES128+aes128  = Proxy :: Proxy AES128+aes256 :: Proxy AES256+aes256  = Proxy :: Proxy AES256+chacha :: Proxy ChaCha20Poly1305+chacha  = Proxy :: Proxy ChaCha20Poly1305+{- FOURMOLU_ENABLE -}++data AEADCipher = forall a. Aead a => AEADCipher (Proxy a)++{- FOURMOLU_DISABLE -}+defaultAEADMap :: [(AEAD_ID, AEADCipher)]+defaultAEADMap =+    [ (AES_128_GCM,      AEADCipher aes128)+    , (AES_256_GCM,      AEADCipher aes256)+    , (ChaCha20Poly1305, AEADCipher chacha)+    ]+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++-- | ID for key derivation function.+newtype KDF_ID = KDF_ID {fromKDF_ID :: Word16} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern HKDF_SHA256 :: KDF_ID+pattern HKDF_SHA256  = KDF_ID 0x0001+pattern HKDF_SHA384 :: KDF_ID+pattern HKDF_SHA384  = KDF_ID 0x0002+pattern HKDF_SHA512 :: KDF_ID+pattern HKDF_SHA512  = KDF_ID 0x0003++instance Show KDF_ID where+    show HKDF_SHA256 = "HKDF_SHA256"+    show HKDF_SHA384 = "HKDF_SHA384"+    show HKDF_SHA512 = "HKDF_SHA512"+    show (KDF_ID n)  = "HKDF_ID 0x" ++ printf "%04x" n+{- FOURMOLU_ENABLE -}++data KDFHash = forall h. (HashAlgorithm h, KDF h) => KDFHash h++defaultKDFMap :: [(KDF_ID, KDFHash)]+defaultKDFMap =+    [ (HKDF_SHA256, KDFHash SHA256)+    , (HKDF_SHA384, KDFHash SHA384)+    , (HKDF_SHA512, KDFHash SHA512)+    ]++----------------------------------------------------------------+----------------------------------------------------------------++-- | ID for key encapsulation mechanism.+newtype KEM_ID = KEM_ID {fromKEM_ID :: Word16} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern DHKEM_P256_HKDF_SHA256   :: KEM_ID+pattern DHKEM_P256_HKDF_SHA256    = KEM_ID 0x0010+pattern DHKEM_P384_HKDF_SHA384   :: KEM_ID+pattern DHKEM_P384_HKDF_SHA384    = KEM_ID 0x0011+pattern DHKEM_P521_HKDF_SHA512   :: KEM_ID+pattern DHKEM_P521_HKDF_SHA512    = KEM_ID 0x0012+pattern DHKEM_X25519_HKDF_SHA256 :: KEM_ID+pattern DHKEM_X25519_HKDF_SHA256  = KEM_ID 0x0020+pattern DHKEM_X448_HKDF_SHA512   :: KEM_ID+pattern DHKEM_X448_HKDF_SHA512    = KEM_ID 0x0021++instance Show KEM_ID where+    show DHKEM_P256_HKDF_SHA256   = "DHKEM(P-256, HKDF-SHA256)"+    show DHKEM_P384_HKDF_SHA384   = "DHKEM(P-384, HKDF-SHA384)"+    show DHKEM_P521_HKDF_SHA512   = "DHKEM(P-521, HKDF-SHA512)"+    show DHKEM_X25519_HKDF_SHA256 = "DHKEM(X25519, HKDF-SHA256)"+    show DHKEM_X448_HKDF_SHA512   = "DHKEM(X448, HKDF-SHA512)"+    show (KEM_ID n)               = "DHKEM_ID 0x" ++ printf "%04x" n+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++{- FOURMOLU_DISABLE -}+p256   :: Proxy Curve_P256R1+p256    = Proxy :: Proxy Curve_P256R1+p384   :: Proxy Curve_P384R1+p384    = Proxy :: Proxy Curve_P384R1+p521   :: Proxy Curve_P521R1+p521    = Proxy :: Proxy Curve_P521R1+x25519 :: Proxy Curve_X25519+x25519  = Proxy :: Proxy Curve_X25519+x448   :: Proxy Curve_X448+x448    = Proxy :: Proxy Curve_X448++data KEMGroup+    = forall c. (EllipticCurve c, EllipticCurveDH c) => KEMGroup (Proxy c)++defaultKEMMap :: [(KEM_ID, (KEMGroup, KDFHash))]+defaultKEMMap =+    [ (DHKEM_P256_HKDF_SHA256,   (KEMGroup p256,   KDFHash SHA256))+    , (DHKEM_P384_HKDF_SHA384,   (KEMGroup p384,   KDFHash SHA384))+    , (DHKEM_P521_HKDF_SHA512,   (KEMGroup p521,   KDFHash SHA512))+    , (DHKEM_X25519_HKDF_SHA256, (KEMGroup x25519, KDFHash SHA256))+    , (DHKEM_X448_HKDF_SHA512,   (KEMGroup x448,   KDFHash SHA512))+    ]+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++data HPKEMap = HPKEMap+    { kemMap :: [(KEM_ID, (KEMGroup, KDFHash))]+    , kdfMap :: [(KDF_ID, KDFHash)]+    , cipherMap :: [(AEAD_ID, AEADCipher)]+    }++defaultHPKEMap :: HPKEMap+defaultHPKEMap =+    HPKEMap+        { kemMap = defaultKEMMap+        , kdfMap = defaultKDFMap+        , cipherMap = defaultAEADMap+        }
+ Crypto/HPKE/Internal.hs view
@@ -0,0 +1,40 @@+module Crypto.HPKE.Internal (+    -- * Extensible map+    HPKEMap (..),+    defaultHPKEMap,+    setupS,+    setupR,++    -- * Unified types+    KEMGroup (..),+    KDFHash (..),+    AEADCipher (..),++    -- * API+    Aead (..),+    KDF (..),++    -- * Types+    Mode (..),+    PublicKey,+    SecretKey,+    Seal,+    Open,+    Nonce,+    Suite,+    Salt,+    Label,+    IKM,++    -- * Generating key pair+    genKeyPair,+) where++import Crypto.HPKE.AEAD+import Crypto.HPKE.ID+import Crypto.HPKE.KDF+import Crypto.HPKE.KeyPair+import Crypto.HPKE.KeySchedule+import Crypto.HPKE.PublicKey+import Crypto.HPKE.Setup+import Crypto.HPKE.Types
+ Crypto/HPKE/KDF.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Crypto.HPKE.KDF (+    KDF (..),+    HashAlgorithm,+    SHA256 (..),+    SHA384 (..),+    SHA512 (..),+    PRK,+    extractAndExpand,+)+where++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+    labeledExpand :: Suite -> PRK h -> Label -> Info -> Int -> 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_+    :: HashAlgorithm a => Suite -> PRK a -> Label -> Info -> Int -> Key+labeledExpand_ suite prk label info len = HKDF.expand prk labeled_info len+  where+    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+    shared_secret =+        labeledExpand suite eae_prk "shared_secret" kem_context siz
+ Crypto/HPKE/KEM.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Crypto.HPKE.KEM (+    encapGen,+    encapEnv,+    decapEnv,+    genKeyPairP,+)+where++import qualified Control.Exception as E+import Crypto.ECC (+    EllipticCurve (..),+    EllipticCurveDH (..),+    KeyPair (..),+ )+import Crypto.Random (drgNew, withDRG)++import Crypto.HPKE.PublicKey+import Crypto.HPKE.Types++----------------------------------------------------------------++encap+    :: (EllipticCurve group, EllipticCurveDH group)+    => Env group+    -> Encap+encap Env{..} enc0@(EncodedPublicKey pkRm) = do+    pkR <- deserializePublicKey envProxy enc0+    let skE = envSecretKey+    dh0 <- ecdh' envProxy skE pkR $ EncapError "encap"+    (dh, pkSm) <- case envAuthKey of+        Nothing -> return (dh0, "")+        Just skS -> do+            let pkS = scalarToPoint envProxy skS+            dh1 <- ecdh' envProxy skS pkR $ EncapError "encap"+            let EncodedPublicKey pk = serializePublicKey envProxy pkS+            return (dh0 <> dh1, pk)+    let pkE = scalarToPoint envProxy skE+    let enc@(EncodedPublicKey pkEm) = serializePublicKey envProxy pkE+        kem_context = pkEm <> pkRm <> pkSm+        shared_secret = SharedSecret $ convert $ envDerive dh kem_context+    return (shared_secret, enc)++encapGen+    :: (EllipticCurve group, EllipticCurveDH group)+    => Proxy group+    -> KeyDeriveFunction+    -> Maybe EncodedSecretKey+    -> IO Encap+encapGen proxy derive mskSm = do+    mskS <- case mskSm of+        Nothing -> return $ Nothing+        Just skSm -> case deserializeSecretKey proxy skSm of+            Left err -> E.throwIO err+            Right x -> return $ Just x+    env <- genEnv proxy derive mskS+    return $ encap env++encapEnv+    :: (EllipticCurve group, EllipticCurveDH group)+    => Proxy group+    -> KeyDeriveFunction+    -> EncodedSecretKey+    -> Maybe EncodedSecretKey+    -> Encap+encapEnv proxy derive skRm skSm enc = do+    env <- newEnvDeserialize proxy derive skRm skSm+    encap env enc++----------------------------------------------------------------++decap+    :: (EllipticCurve group, EllipticCurveDH group)+    => Env group+    -> Decap+decap Env{..} enc@(EncodedPublicKey pkEm) = do+    pkE <- deserializePublicKey envProxy enc+    let skR = envSecretKey+    dh0 <- ecdh' envProxy skR pkE $ DecapError "decap"+    (dh, pkSm) <- case envAuthKey of+        Nothing -> return (dh0, "")+        Just skS -> do+            let pkS = scalarToPoint envProxy skS+            dh1 <- ecdh' envProxy skR pkS $ EncapError "decap"+            let EncodedPublicKey pk = serializePublicKey envProxy pkS+            return (dh0 <> dh1, pk)++    let pkR = scalarToPoint envProxy skR+    let EncodedPublicKey pkRm = serializePublicKey envProxy pkR+        kem_context = pkEm <> pkRm <> pkSm+        shared_secret = SharedSecret $ convert $ envDerive dh kem_context+    return shared_secret++decapEnv+    :: (EllipticCurve group, EllipticCurveDH group)+    => Proxy group+    -> KeyDeriveFunction+    -> EncodedSecretKey+    -> Maybe EncodedSecretKey+    -> Decap+decapEnv proxy derive skRm mskSm enc = do+    env <- newEnvDeserialize proxy derive skRm mskSm+    decap env enc++----------------------------------------------------------------++{- FOURMOLU_DISABLE -}+data Env group = Env+    { envSecretKey :: SecretKey group+    , envAuthKey   :: Maybe (SecretKey group)+    , envProxy     :: Proxy group+    , envDerive    :: KeyDeriveFunction+    }+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++newEnv+    :: forall group+     . EllipticCurve group+    => KeyDeriveFunction+    -> SecretKey group+    -> Maybe (SecretKey group)+    -> Env group+newEnv derive skR mskS =+    Env+        { envSecretKey = skR+        , envAuthKey = mskS+        , envProxy = proxy+        , envDerive = derive+        }+  where+    proxy = Proxy :: Proxy group++----------------------------------------------------------------++genEnv+    :: EllipticCurve group+    => Proxy group+    -> KeyDeriveFunction+    -> Maybe (SecretKey group)+    -> IO (Env group)+genEnv proxy derive mskS = do+    (_, sk) <- genKeyPairP proxy+    return $ newEnv derive sk mskS++genKeyPairP+    :: EllipticCurve curve+    => proxy curve -> IO (Point curve, Scalar curve)+genKeyPairP proxy = do+    gen <- drgNew+    let (KeyPair pk sk, _) = withDRG gen $ curveGenerateKeyPair proxy+    return (pk, sk)++----------------------------------------------------------------++newEnvDeserialize+    :: EllipticCurve group+    => Proxy group+    -> KeyDeriveFunction+    -> EncodedSecretKey+    -> Maybe EncodedSecretKey+    -> Either HPKEError (Env group)+newEnvDeserialize proxy derive skRm mskSm = do+    skR <- deserializeSecretKey proxy skRm+    mskS <- case mskSm of+        Nothing -> Right $ Nothing+        Just skSm -> Just <$> deserializeSecretKey proxy skSm+    return $ newEnv derive skR mskS++----------------------------------------------------------------++ecdh'+    :: EllipticCurveDH group+    => Proxy group+    -> SecretKey group+    -> PublicKey group+    -> a+    -> Either a SharedSecret+ecdh' proxy sk pk err = case ecdh proxy sk pk of+    CryptoPassed a -> Right a+    CryptoFailed _ -> Left err
+ Crypto/HPKE/KeyPair.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE RecordWildCards #-}++module Crypto.HPKE.KeyPair where++import qualified Control.Exception as E+import Crypto.ECC (+    EllipticCurve (..),+ )+import Crypto.HPKE.ID+import Crypto.HPKE.KEM (genKeyPairP)+import Crypto.HPKE.Types++----------------------------------------------------------------++-- | Generating a pair of public key and secret key based on+-- 'KEM_ID'.+genKeyPair+    :: HPKEMap -> KEM_ID -> IO (EncodedPublicKey, EncodedSecretKey)+genKeyPair HPKEMap{..} kem_id = case lookup kem_id kemMap of+    Nothing -> E.throwIO $ Unsupported $ show kem_id+    Just (KEMGroup proxy, _) -> do+        (pk, sk) <- genKeyPairP proxy+        let pkm = EncodedPublicKey $ encodePoint proxy pk+            skm = EncodedSecretKey $ encodeScalar proxy sk+        return (pkm, skm)
+ Crypto/HPKE/KeySchedule.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Crypto.HPKE.KeySchedule (+    -- * Types+    Mode (..),++    -- * Key schedule+    keySchedule,+) where++import Crypto.KDF.HKDF (toPRK)+import qualified Data.ByteString as BS++import Crypto.HPKE.KDF+import Crypto.HPKE.Types++----------------------------------------------------------------++data Mode+    = ModeBase+    | ModePsk+    | ModeAuth+    | ModeAuthPsk+    deriving (Eq, Show)++{- FOURMOLU_DISABLE -}+fromMode :: Mode -> Word8+fromMode ModeBase    = 0x00+fromMode ModePsk     = 0x01+fromMode ModeAuth    = 0x02+fromMode ModeAuthPsk = 0x03+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++keySchedule+    :: forall h+     . (HashAlgorithm h, KDF h)+    => h+    -> Suite+    -> Int+    -> Int+    -> Mode+    -> Info+    -> PSK+    -> PSK_ID+    -> SharedSecret+    -> Either HPKEError (Key, Nonce, Int, PRK h)+keySchedule h suite nk nn mode info psk psk_id shared_secret =+    case toPRK exporter_secret of+        Nothing -> Left $ KeyScheduleError "cannot convert to PRK"+        Just prk -> Right (key, base_nonce, 0, prk)+  where+    psk_id_hash = labeledExtract suite "" "psk_id_hash" psk_id :: PRK h+    info_hash = labeledExtract suite "" "info_hash" info :: PRK h+    key_schedule_context =+        BS.singleton (fromMode mode) <> convert psk_id_hash <> convert info_hash+            :: ByteString++    secret = labeledExtract suite (convert shared_secret) "secret" psk :: PRK h++    key = labeledExpand suite secret "key" key_schedule_context nk+    base_nonce = labeledExpand suite secret "base_nonce" key_schedule_context nn++    exporter_secret = labeledExpand suite secret "exp" key_schedule_context $ hashDigestSize h
+ Crypto/HPKE/PublicKey.hs view
@@ -0,0 +1,60 @@+module Crypto.HPKE.PublicKey (+    SecretKey,+    PublicKey,+    serializePublicKey,+    deserializePublicKey,+    serializeSecretKey,+    deserializeSecretKey,+)+where++import Crypto.ECC (+    EllipticCurve (..),+    Point,+    Scalar,+    decodePoint,+    decodeScalar,+    encodePoint,+    encodeScalar,+ )++import Crypto.HPKE.Types++-- $setup+-- >>> :set -XOverloadedStrings+-- >>> import Crypto.ECC+-- >>> import Crypto.Hash.Algorithms+-- >>> import Data.ByteString++----------------------------------------------------------------++type PublicKey group = Point group+type SecretKey group = Scalar group++----------------------------------------------------------------++serializePublicKey+    :: EllipticCurve group+    => Proxy group -> PublicKey group -> EncodedPublicKey+serializePublicKey proxy pk = EncodedPublicKey $ encodePoint proxy pk++deserializePublicKey+    :: EllipticCurve group+    => Proxy group -> EncodedPublicKey -> Either HPKEError (PublicKey group)+deserializePublicKey proxy (EncodedPublicKey pkm) =+    case decodePoint proxy pkm of+        CryptoPassed a -> Right a+        CryptoFailed _ -> Left $ DeserializeError "deserializePublicKey"++serializeSecretKey+    :: EllipticCurve group+    => Proxy group -> SecretKey group -> EncodedSecretKey+serializeSecretKey proxy pk = EncodedSecretKey $ encodeScalar proxy pk++deserializeSecretKey+    :: EllipticCurve group+    => Proxy group -> EncodedSecretKey -> Either HPKEError (SecretKey group)+deserializeSecretKey proxy (EncodedSecretKey pkm) =+    case decodeScalar proxy pkm of+        CryptoPassed a -> Right a+        CryptoFailed _ -> Left $ DeserializeError "deserializeSecretKey"
+ Crypto/HPKE/Setup.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Crypto.HPKE.Setup (+    setupBaseS,+    setupBaseR,+    setupPSKS,+    setupPSKR,+    setupS,+    setupR,+) where++import qualified Control.Exception as E++import Crypto.HPKE.AEAD+import Crypto.HPKE.Context+import Crypto.HPKE.ID+import Crypto.HPKE.KDF+import Crypto.HPKE.KEM+import Crypto.HPKE.KeySchedule+import Crypto.HPKE.Types++-- | Setting up base/auth mode for a sender.+--   This throws 'HPKEError'.+setupBaseS+    :: KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> Maybe EncodedSecretKey+    -- ^ My ephemeral secret key. Automatically generated if 'Nothing'+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> IO (EncodedPublicKey, ContextS)+setupBaseS kem_id kdf_id aead_id mskEm mskSm pkRm info =+    setupS defaultHPKEMap mode kem_id kdf_id aead_id mskEm mskSm pkRm info "" ""+  where+    mode = case mskSm of+        Nothing -> ModeBase+        _ -> ModeAuth++-- | Setting up base/auth mode for a receiver with its key pair.+--   This throws 'HPKEError'.+setupBaseR+    :: KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> EncodedSecretKey+    -- ^ My secret key+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> IO ContextR+setupBaseR kem_id kdf_id aead_id skRm mskSm enc info =+    setupR defaultHPKEMap mode kem_id kdf_id aead_id skRm mskSm enc info "" ""+  where+    mode = case mskSm of+        Nothing -> ModeBase+        _ -> ModeAuth++----------------------------------------------------------------++-- | Setting up psk/auth_psk mode for a sender.+--   This throws 'HPKEError'.+setupPSKS+    :: KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> Maybe EncodedSecretKey+    -- ^ My ephemeral secret key. Automatically generated if 'Nothing'+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> PSK+    -> PSK_ID+    -> IO (EncodedPublicKey, ContextS)+setupPSKS kem_id kdf_id aead_id skRm mskSm =+    setupS defaultHPKEMap mode kem_id kdf_id aead_id skRm mskSm+  where+    mode = case mskSm of+        Nothing -> ModePsk+        _ -> ModeAuthPsk++-- | Setting up psk/auth_psk mode for a receiver with its key pair.+--   This throws 'HPKEError'.+setupPSKR+    :: KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> EncodedSecretKey+    -- ^ My secret key+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> PSK+    -> PSK_ID+    -> IO ContextR+setupPSKR kem_id kdf_id aead_id skRm mskSm =+    setupR defaultHPKEMap mode kem_id kdf_id aead_id skRm mskSm+  where+    mode = case mskSm of+        Nothing -> ModePsk+        _ -> ModeAuthPsk++----------------------------------------------------------------++setupS+    :: HPKEMap+    -> Mode+    -> KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> Maybe EncodedSecretKey+    -- ^ My ephemeral secret key. Automatically generated if 'Nothing'+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> PSK+    -> PSK_ID+    -> IO (EncodedPublicKey, ContextS)+setupS hpkeMap mode kem_id kdf_id aead_id mskEm mskSm pkRm info psk psk_id = do+    verifyPSKInput mode psk psk_id+    let r = look hpkeMap kem_id kdf_id aead_id+    throwOnError r $ \((KEMGroup group, KDFHash h), KDFHash h', AEADCipher c) -> do+        let derive = extractAndExpand h $ suiteKEM kem_id+        encap <- case mskEm of+            Nothing -> encapGen group derive mskSm+            Just skEm -> return $ encapEnv group derive skEm mskSm+        throwOnError (encap pkRm) $ \(shared_secret, enc) -> do+            let (nk, nn, seal', _) = aeadParams c+                suite' = suiteHPKE kem_id kdf_id aead_id+                keys = keySchedule h' suite' nk nn mode info psk psk_id shared_secret+            throwOnError keys $ \(key, nonce, _, prk) -> do+                let expand' = labeledExpand suite' prk "sec"+                ctx <- newContextS key nonce seal' expand'+                return (enc, ctx)++setupR+    :: HPKEMap+    -> Mode+    -> KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> EncodedSecretKey+    -- ^ My secret key+    -> Maybe EncodedSecretKey+    -- ^ My secret key for authentication.+    --   'mode_base' is used if 'Nothing'. 'base_auth' is used, otherwise.+    -> EncodedPublicKey+    -- ^ Peer's public key.+    -> Info+    -> PSK+    -> PSK_ID+    -> IO ContextR+setupR hpkeMap mode kem_id kdf_id aead_id skRm mskSm enc info psk psk_id = do+    verifyPSKInput mode psk psk_id+    let r = look hpkeMap kem_id kdf_id aead_id+    throwOnError r $ \((KEMGroup group, KDFHash h), KDFHash h', AEADCipher c) -> do+        let derive = extractAndExpand h $ suiteKEM kem_id+            decap = decapEnv group derive skRm mskSm+        throwOnError (decap enc) $ \shared_secret -> do+            let (nk, nn, _, open') = aeadParams c+                suite' = suiteHPKE kem_id kdf_id aead_id+                keys = keySchedule h' suite' nk nn mode info psk psk_id shared_secret+            throwOnError keys $ \(key, nonce, _, prk) -> do+                let expand' = labeledExpand suite' prk "sec"+                newContextR key nonce open' expand'++aeadParams+    :: Aead a+    => Proxy a -> (Int, Int, Key -> Seal, Key -> Open)+aeadParams c = (nK c, nN c, sealA c, openA c)++throwOnError :: Either HPKEError v -> (v -> IO a) -> IO a+throwOnError (Left err) _body = E.throwIO err+throwOnError (Right ss) body = body ss++----------------------------------------------------------------++look+    :: HPKEMap+    -> KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> Either HPKEError ((KEMGroup, KDFHash), KDFHash, AEADCipher)+look HPKEMap{..} kem_id kdf_id aead_id = do+    k <- lookupE kem_id kemMap+    h <- lookupE kdf_id kdfMap+    a <- lookupE aead_id cipherMap+    return (k, h, a)++verifyPSKInput :: Mode -> PSK -> PSK_ID -> IO ()+verifyPSKInput mode psk psk_id+    | got_psk /= got_psk_id =+        E.throwIO $ ValidationError "mismatch for psk and psk_id"+    | got_psk && mode `elem` [ModeBase, ModeAuth] =+        E.throwIO $ ValidationError "invalid mode (1)"+    | (not got_psk) && mode `elem` [ModePsk, ModeAuthPsk] =+        E.throwIO $ ValidationError "invalid mode (2)"+    | otherwise = return ()+  where+    got_psk = psk /= ""+    got_psk_id = psk_id /= ""++----------------------------------------------------------------++suiteKEM :: KEM_ID -> Suite+suiteKEM kem_id = "KEM" <> i+  where+    i = i2ospOf_ 2 $ fromIntegral $ fromKEM_ID kem_id++suiteHPKE :: KEM_ID -> KDF_ID -> AEAD_ID -> Suite+suiteHPKE kem_id hkdf_id aead_id = "HPKE" <> i0 <> i1 <> i2+  where+    i0 = i2ospOf_ 2 $ fromIntegral $ fromKEM_ID kem_id+    i1 = i2ospOf_ 2 $ fromIntegral $ fromKDF_ID hkdf_id+    i2 = i2ospOf_ 2 $ fromIntegral $ fromAEAD_ID aead_id
+ Crypto/HPKE/Types.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Crypto.HPKE.Types (+    HPKEError (..),+    Salt,+    IKM,+    Key,+    Suite,+    Label,+    KeyDeriveFunction,+    Nonce,+    AAD,+    PlainText,+    CipherText,+    Seal,+    Open,+    EncodedPublicKey (..),+    EncodedSecretKey (..),+    Info,+    PSK,+    PSK_ID,+    Encap,+    Decap,+    ExporterSecret,+    -- rexport+    CryptoFailable (..),+    SharedSecret (..),+    hashDigestSize,+    i2ospOf_,+    convert,+    Proxy (..),+    ByteString,+    Word8,+    Word16,+    printf,+    lookupE,+) where++import Control.Exception (Exception)+import Crypto.ECC (SharedSecret (..))+import Crypto.Error (CryptoFailable (..))+import Crypto.Hash.IO (hashDigestSize)+import Crypto.Number.Serialize (i2ospOf_)+import Data.ByteArray (convert)+import Data.ByteString (ByteString)+import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Char8 as C8+import Data.Proxy (Proxy (..))+import Data.String+import Data.Word (Word16, Word8)+import Text.Printf (printf)++----------------------------------------------------------------++-- | Errors for HPKE+data HPKEError+    = ValidationError String+    | DeserializeError String+    | EncapError String+    | DecapError String+    | -- | Original+      SealError String+    | OpenError String+    | MessageLimitReachedError String+    | DeriveKeyPairError String+    | -- | Original+      KeyScheduleError String+    | Unsupported String+    deriving (Eq, Show)++instance Exception HPKEError++----------------------------------------------------------------++-- | Encryption key.+type Key = ByteString++type Salt = ByteString+type IKM = ByteString -- Input Keying Material+type Suite = ByteString+type Label = ByteString+type KeyDeriveFunction = SharedSecret -> ByteString -> Key++----------------------------------------------------------------++type Nonce = ByteString++-- | Additional authenticated data for AEAD.+type AAD = ByteString++-- | Plain text.+type PlainText = ByteString++-- | Cipher text (including a authentication tag)+type CipherText = ByteString++type Seal = Nonce -> AAD -> PlainText -> Either HPKEError CipherText+type Open = Nonce -> AAD -> CipherText -> Either HPKEError PlainText++----------------------------------------------------------------++-- | Encoded public key.+newtype EncodedPublicKey = EncodedPublicKey ByteString deriving (Eq)++-- | Encoded secret key.+newtype EncodedSecretKey = EncodedSecretKey ByteString deriving (Eq)++instance Show EncodedPublicKey where+    show (EncodedPublicKey pk) = showBS16 pk++instance Show EncodedSecretKey where+    show (EncodedSecretKey pk) = showBS16 pk++instance IsString EncodedPublicKey where+    fromString = EncodedPublicKey . fromString++instance IsString EncodedSecretKey where+    fromString = EncodedSecretKey . fromString++showBS16 :: ByteString -> String+showBS16 bs = "\"" <> s16 <> "\""+  where+    s16 = C8.unpack $ B16.encode bs++----------------------------------------------------------------++type Encap =+    EncodedPublicKey -> Either HPKEError (SharedSecret, EncodedPublicKey)+type Decap = EncodedPublicKey -> Either HPKEError SharedSecret++----------------------------------------------------------------++-- | Information string.+type Info = ByteString++-- | Pre-shared key.+type PSK = ByteString++-- | ID for pre-shared key.+type PSK_ID = ByteString++----------------------------------------------------------------++lookupE :: (Eq k, Show k) => k -> [(k, v)] -> Either HPKEError v+lookupE k table = case lookup k table of+    Nothing -> Left $ Unsupported $ show k+    Just v -> Right v++----------------------------------------------------------------++type ExporterSecret = ByteString
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2025, IIJ Innovation Institute Inc.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++  * Redistributions of source code must retain the above copyright+    notice, this list of conditions and the following disclaimer.+  * Redistributions in binary form must reproduce the above copyright+    notice, this list of conditions and the following disclaimer in+    the documentation and/or other materials provided with the+    distribution.+  * Neither the name of the copyright holders nor the names of its+    contributors may be used to endorse or promote products derived+    from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ hpke.cabal view
@@ -0,0 +1,62 @@+cabal-version:      >=1.10+name:               hpke+version:            0.0.0+license:            BSD3+license-file:       LICENSE+maintainer:         kazu@iij.ad.jp+author:             Kazu Yamamoto+synopsis:           Hybrid Public Key Encryption+description:+    Hybrid Public Key Encryption defined in RFC9180++category:           Cryptography+build-type:         Simple+extra-source-files: ChangeLog.md++library+    exposed-modules:  Crypto.HPKE+                      Crypto.HPKE.Internal+    other-modules:    Crypto.HPKE.AEAD+                      Crypto.HPKE.Context+                      Crypto.HPKE.ID+                      Crypto.HPKE.KDF+                      Crypto.HPKE.KEM+                      Crypto.HPKE.KeyPair+                      Crypto.HPKE.KeySchedule+                      Crypto.HPKE.PublicKey+                      Crypto.HPKE.Setup+                      Crypto.HPKE.Types+    default-language: Haskell2010+    ghc-options:      -Wall+    build-depends:+        base >=4.7 && <5,+        base16-bytestring,+        bytestring,+        crypton >= 1.0.2,+        memory++    default-extensions: Strict StrictData++test-suite spec+    type:               exitcode-stdio-1.0+    main-is:            Spec.hs+    build-tool-depends: hspec-discover:hspec-discover+    hs-source-dirs:     test+    other-modules:      A1Spec+                        A2Spec+                        A3Spec+                        A4Spec+                        A5Spec+                        A6Spec+                        Test++    default-language:   Haskell2010+    default-extensions: Strict StrictData+    ghc-options:        -Wall -threaded -rtsopts+    build-depends:+        base >=4.9 && <5,+        QuickCheck,+        bytestring,+        base16-bytestring,+        hpke,+        hspec
+ test/A1Spec.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings #-}++module A1Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_X25519_HKDF_SHA256+kdf_id :: KDF_ID+kdf_id = HKDF_SHA256+aead_id :: AEAD_ID+aead_id = AES_128_GCM++spec :: Spec+spec = do+    describe "A.1. DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM" $ do+        it "A.1.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"+                "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"+                "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"+                "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"+                ""+                ""+                ""+                "f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"+                "af2d7e9ac9ae7e270f46ba1f975be53c09f8d875bdc8535458c2494e8a6eab251c03d0c22a56b8ca42c2063b84"+                "3853fe2b4035195a573ffc53856e77058e15d9ea064de3e59f4961d0095250ee"+                "2e8f0b54673c7029649d4eb9d5e33bf1872cf76d623ff164ac185da9e88c21a5"+                "e9e43065102c3836401bed8c3c3c75ae46be1639869391d62c61f1ec7af54931"++        it "A.1.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "0ad0950d9fb9588e59690b74f1237ecdf1d775cd60be2eca57af5a4b0471c91b"+                "463426a9ffb42bb17dbe6044b9abd1d4e4d95f9041cef0e99d7824eef2b6f588"+                "9fed7e8c17387560e92cc6462a68049657246a09bfa8ade7aefe589672016366"+                "c5eb01eb457fe6c6f57577c5413b931550a162c71a03ac8d196babbd4e5ce0fd"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "e52c6fed7f758d0cf7145689f21bc1be6ec9ea097fef4e959440012f4feb73fb611b946199e681f4cfc34db8ea"+                "49f3b19b28a9ea9f43e8c71204c00d4a490ee7f61387b6719db765e948123b45b61633ef059ba22cd62437c8ba"+                "dff17af354c8b41673567db6259fd6029967b4e1aad13023c2ae5df8f4f43bf6"+                "6a847261d8207fe596befb52928463881ab493da345b10e1dcc645e3b94e2d95"+                "8aff52b45a1be3a734bc7a41e20b4e055ad4c4d22104b0c20285a7c4302401cd"++        it "A.1.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "23fb952571a14a25e3d678140cd0e5eb47a0961bb18afcf85896e5453c312e76"+                "ff4442ef24fbc3c1ff86375b0be1e77e88a0de1e79b30896d73411c5ff4c3518"+                "1632d5c2f71c2b38d0a8fcc359355200caa8b1ffdf28618080466c909cb69b2e"+                "fdea67cf831f1ca98d8e27b1f6abeb5b7745e9d35348b80fa407ff6958f9137e"+                "dc4a146313cce60a278a5323d321f051c5707e9c45ba21a3479fecdf76fc69dd"+                ""+                ""+                "5fd92cc9d46dbf8943e72a07e42f363ed5f721212cd90bcfd072bfd9f44e06b80fd17824947496e21b680c141b"+                "d3736bb256c19bfa93d79e8f80b7971262cb7c887e35c26370cfed62254369a1b52e3d505b79dd699f002bc8ed"+                "28c70088017d70c896a8420f04702c5a321d9cbf0279fba899b59e51bac72c85"+                "25dfc004b0892be1888c3914977aa9c9bbaf2c7471708a49e1195af48a6f29ce"+                "5a0131813abc9a522cad678eb6bafaabc43389934adb8097d23c5ff68059eb64"+        it "A.1.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "820818d3c23993492cc5623ab437a48a0a7ca3e9639c140fe1e33811eb844b7c"+                "14de82a5897b613616a00c39b87429df35bc2b426bcfd73febcb45e903490768"+                "1d11a3cd247ae48e901939659bd4d79b6b959e1f3e7d66663fbc9412dd4e0976"+                "cb29a95649dc5656c2d054c1aa0d3df0493155e9d5da6d7e344ed8b6a64a9423"+                "fc1c87d2f3832adb178b431fce2ac77c7ca2fd680f3406c77b5ecdf818b119f4"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "a84c64df1e11d8fd11450039d4fe64ff0c8a99fca0bd72c2d4c3e0400bc14a40f27e45e141a24001697737533e"+                "4d19303b848f424fc3c3beca249b2c6de0a34083b8e909b6aa4c3688505c05ffe0c8f57a0a4c5ab9da127435d9"+                "08f7e20644bb9b8af54ad66d2067457c5f9fcb2a23d9f6cb4445c0797b330067"+                "52e51ff7d436557ced5265ff8b94ce69cf7583f49cdb374e6aad801fc063b010"+                "a30c20370c026bbea4dca51cb63761695132d342bae33a6a11527d3e7679436d"
+ test/A2Spec.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE OverloadedStrings #-}++module A2Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_X25519_HKDF_SHA256+kdf_id :: KDF_ID+kdf_id = HKDF_SHA256+aead_id :: AEAD_ID+aead_id = ChaCha20Poly1305++spec :: Spec+spec = do+    describe "A.2.  DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305" $ do+        it "A.2.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"+                "f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600"+                "4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"+                "8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb"+                ""+                ""+                ""+                "1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"+                "6b53c051e4199c518de79594e1c4ab18b96f081549d45ce015be002090bb119e85285337cc95ba5f59992dc98c"+                "4bbd6243b8bb54cec311fac9df81841b6fd61f56538a775e7c80a9f40160606e"+                "8c1df14732580e5501b00f82b10a1647b40713191b7c1240ac80e2b68808ba69"+                "5acb09211139c43b3090489a9da433e8a30ee7188ba8b0a9a1ccf0c229283e53"+        it "A.2.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "2261299c3f40a9afc133b969a97f05e95be2c514e54f3de26cbe5644ac735b04"+                "0c35fdf49df7aa01cd330049332c40411ebba36e0c718ebc3edf5845795f6321"+                "13640af826b722fc04feaa4de2f28fbd5ecc03623b317834e7ff4120dbe73062"+                "77d114e0212be51cb1d76fa99dd41cfd4d0166b08caa09074430a6c59ef17879"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "4a177f9c0d6f15cfdf533fb65bf84aecdc6ab16b8b85b4cf65a370e07fc1d78d28fb073214525276f4a89608ff"+                "5c3cabae2f0b3e124d8d864c116fd8f20f3f56fda988c3573b40b09997fd6c769e77c8eda6cda4f947f5b704a8"+                "813c1bfc516c99076ae0f466671f0ba5ff244a41699f7b2417e4c59d46d39f40"+                "2745cf3d5bb65c333658732954ee7af49eb895ce77f8022873a62a13c94cb4e1"+                "ad40e3ae14f21c99bfdebc20ae14ab86f4ca2dc9a4799d200f43a25f99fa78ae"+        it "A.2.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "f7674cc8cd7baa5872d1f33dbaffe3314239f6197ddf5ded1746760bfc847e0e"+                "c94619e1af28971c8fa7957192b7e62a71ca2dcdde0a7cc4a8a9e741d600ab13"+                "1a478716d63cb2e16786ee93004486dc151e988b34b475043d3e0175bdb01c44"+                "3ca22a6d1cda1bb9480949ec5329d3bf0b080ca4c45879c95eddb55c70b80b82"+                "2def0cb58ffcf83d1062dd085c8aceca7f4c0c3fd05912d847b61f3e54121f05"+                ""+                ""+                "ab1a13c9d4f01a87ec3440dbd756e2677bd2ecf9df0ce7ed73869b98e00c09be111cb9fdf077347aeb88e61bdf"+                "3265c7807ffff7fdace21659a2c6ccffee52a26d270c76468ed74202a65478bfaedfff9c2b7634e24f10b71016"+                "070cffafd89b67b7f0eeb800235303a223e6ff9d1e774dce8eac585c8688c872"+                "2852e728568d40ddb0edde284d36a4359c56558bb2fb8837cd3d92e46a3a14a8"+                "1df39dc5dd60edcbf5f9ae804e15ada66e885b28ed7929116f768369a3f950ee"++        it "A.2.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "656a2e00dc9990fd189e6e473459392df556e9a2758754a09db3f51179a3fc02"+                "5e6dd73e82b856339572b7245d3cbb073a7561c0bee52873490e305cbb710410"+                "a5099431c35c491ec62ca91df1525d6349cb8aa170c51f9581f8627be6334851"+                "7b36a42822e75bf3362dfabbe474b3016236408becb83b859a6909e22803cb0c"+                "90761c5b0a7ef0985ed66687ad708b921d9803d51637c8d1cb72d03ed0f64418"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "9aa52e29274fc6172e38a4461361d2342585d3aeec67fb3b721ecd63f059577c7fe886be0ede01456ebc67d597"+                "59460bacdbe7a920ef2806a74937d5a691d6d5062d7daafcad7db7e4d8c649adffe575c1889c5c2e3a49af8e3e"+                "c23ebd4e7a0ad06a5dddf779f65004ce9481069ce0f0e6dd51a04539ddcbd5cd"+                "ed7ff5ca40a3d84561067ebc8e01702bc36cf1eb99d42a92004642b9dfaadd37"+                "d3bae066aa8da27d527d85c040f7dd6ccb60221c902ee36a82f70bcd62a60ee4"
+ test/A3Spec.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE OverloadedStrings #-}++module A3Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_P256_HKDF_SHA256+kdf_id :: KDF_ID+kdf_id = HKDF_SHA256+aead_id :: AEAD_ID+aead_id = AES_128_GCM++spec :: Spec+spec = do+    describe "A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM" $ do+        it "A.3.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4"+                "4995788ef4b9d6132b249ce59a77281493eb39af373d236a1fe415cb0c2d7beb"+                "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0"+                "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2"+                ""+                ""+                ""+                "5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434"+                "fa6f037b47fc21826b610172ca9637e82d6e5801eb31cbd3748271affd4ecb06646e0329cbdf3c3cd655b28e82"+                "5e9bc3d236e1911d95e65b576a8a86d478fb827e8bdfe77b741b289890490d4d"+                "6cff87658931bda83dc857e6353efe4987a201b849658d9b047aab4cf216e796"+                "d8f1ea7942adbba7412c6d431c62d01371ea476b823eb697e1f6e6cae1dab85a"+        it "A.3.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04305d35563527bce037773d79a13deabed0e8e7cde61eecee403496959e89e4d0ca701726696d1485137ccb5341b3c1c7aaee90a4a02449725e744b1193b53b5f"+                "57427244f6cc016cddf1c19c8973b4060aa13579b4c067fd5d93a5d74e32a90f"+                "040d97419ae99f13007a93996648b2674e5260a8ebd2b822e84899cd52d87446ea394ca76223b76639eccdf00e1967db10ade37db4e7db476261fcc8df97c5ffd1"+                "438d8bcef33b89e0e9ae5eb0957c353c25a94584b0dd59c991372a75b43cb661"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "90c4deb5b75318530194e4bb62f890b019b1397bbf9d0d6eb918890e1fb2be1ac2603193b60a49c2126b75d0eb"+                "9e223384a3620f4a75b5a52f546b7262d8826dea18db5a365feb8b997180b22d72dc1287f7089a1073a7102c27"+                "a115a59bf4dd8dc49332d6a0093af8efca1bcbfd3627d850173f5c4a55d0c185"+                "4517eaede0669b16aac7c92d5762dd459c301fa10e02237cd5aeb9be969430c4"+                "164e02144d44b607a7722e58b0f4156e67c0c2874d74cf71da6ca48a4cbdc5e0"+        it "A.3.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "042224f3ea800f7ec55c03f29fc9865f6ee27004f818fcbdc6dc68932c1e52e15b79e264a98f2c535ef06745f3d308624414153b22c7332bc1e691cb4af4d53454"+                "6b8de0873aed0c1b2d09b8c7ed54cbf24fdf1dfc7a47fa501f918810642d7b91"+                "04423e363e1cd54ce7b7573110ac121399acbc9ed815fae03b72ffbd4c18b01836835c5a09513f28fc971b7266cfde2e96afe84bb0f266920e82c4f53b36e1a78d"+                "d929ab4be2e59f6954d6bedd93e638f02d4046cef21115b00cdda2acb2a4440e"+                "1120ac99fb1fccc1e8230502d245719d1b217fe20505c7648795139d177f0de9"+                ""+                ""+                "82ffc8c44760db691a07c5627e5fc2c08e7a86979ee79b494a17cc3405446ac2bdb8f265db4a099ed3289ffe19"+                "b0a705a54532c7b4f5907de51c13dffe1e08d55ee9ba59686114b05945494d96725b239468f1229e3966aa1250"+                "837e49c3ff629250c8d80d3c3fb957725ed481e59e2feb57afd9fe9a8c7c4497"+                "594213f9018d614b82007a7021c3135bda7b380da4acd9ab27165c508640dbda"+                "14fe634f95ca0d86e15247cca7de7ba9b73c9b9deb6437e1c832daf7291b79d5"++        it "A.3.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "046a1de3fc26a3d43f4e4ba97dbe24f7e99181136129c48fbe872d4743e2b131357ed4f29a7b317dc22509c7b00991ae990bf65f8b236700c82ab7c11a84511401"+                "36f771e411cf9cf72f0701ef2b991ce9743645b472e835fe234fb4d6eb2ff5a0"+                "04d824d7e897897c172ac8a9e862e4bd820133b8d090a9b188b8233a64dfbc5f725aa0aa52c8462ab7c9188f1c4872f0c99087a867e8a773a13df48a627058e1b3"+                "bdf4e2e587afdf0930644a0c45053889ebcadeca662d7c755a353d5b4e2a8394"+                "b0ed8721db6185435898650f7a677affce925aba7975a582653c4cb13c72d240"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "b9f36d58d9eb101629a3e5a7b63d2ee4af42b3644209ab37e0a272d44365407db8e655c72e4fa46f4ff81b9246"+                "51788c4e5d56276771032749d015d3eea651af0c7bb8e3da669effffed299ea1f641df621af65579c10fc09736"+                "595ce0eff405d4b3bb1d08308d70a4e77226ce11766e0a94c4fdb5d90025c978"+                "110472ee0ae328f57ef7332a9886a1992d2c45b9b8d5abc9424ff68630f7d38d"+                "18ee4d001a9d83a4c67e76f88dd747766576cac438723bad0700a910a4d717e6"
+ test/A4Spec.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE OverloadedStrings #-}++module A4Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_P256_HKDF_SHA256+kdf_id :: KDF_ID+kdf_id = HKDF_SHA512+aead_id :: AEAD_ID+aead_id = AES_128_GCM++spec :: Spec+spec = do+    describe "A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM" $ do+        it "A.4.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "0493ed86735bdfb978cc055c98b45695ad7ce61ce748f4dd63c525a3b8d53a15565c6897888070070c1579db1f86aaa56deb8297e64db7e8924e72866f9a472580"+                "2292bf14bb6e15b8c81a0f45b7a6e93e32d830e48cca702e0affcfb4d07e1b5c"+                "04085aa5b665dc3826f9650ccbcc471be268c8ada866422f739e2d531d4a8818a9466bc6b449357096232919ec4fe9070ccbac4aac30f4a1a53efcf7af90610edd"+                "3ac8530ad1b01885960fab38cf3cdc4f7aef121eaa239f222623614b4079fb38"+                ""+                ""+                ""+                "d3cf4984931484a080f74c1bb2a6782700dc1fef9abe8442e44a6f09044c88907200b332003543754eb51917ba"+                "d14414555a47269dfead9fbf26abb303365e40709a4ed16eaefe1f2070f1ddeb1bdd94d9e41186f124e0acc62d"+                "a32186b8946f61aeead1c093fe614945f85833b165b28c46bf271abf16b57208"+                "84998b304a0ea2f11809398755f0abd5f9d2c141d1822def79dd15c194803c2a"+                "93fb9411430b2cfa2cf0bed448c46922a5be9beff20e2e621df7e4655852edbc"+        it "A.4.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04a307934180ad5287f95525fe5bc6244285d7273c15e061f0f2efb211c35057f3079f6e0abae200992610b25f48b63aacfcb669106ddee8aa023feed301901371"+                "a5901ff7d6931959c2755382ea40a4869b1dec3694ed3b009dda2d77dd488f18"+                "043f5266fba0742db649e1043102b8a5afd114465156719cea90373229aabdd84d7f45dabfc1f55664b888a7e86d594853a6cccdc9b189b57839cbbe3b90b55873"+                "bc6f0b5e22429e5ff47d5969003f3cae0f4fec50e23602e880038364f33b8522"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "57624b6e320d4aba0afd11f548780772932f502e2ba2a8068676b2a0d3b5129a45b9faa88de39e8306da41d4cc"+                "159d6b4c24bacaf2f5049b7863536d8f3ffede76302dace42080820fa51925d4e1c72a64f87b14291a3057e00a"+                "8158bea21a6700d37022bb7802866edca30ebf2078273757b656ef7fc2e428cf"+                "6a348ba6e0e72bb3ef22479214a139ef8dac57be34509a61087a12565473da8d"+                "2f6d4f7a18ec48de1ef4469f596aada4afdf6d79b037ed3c07e0118f8723bffc"+        it "A.4.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04fec59fa9f76f5d0f6c1660bb179cb314ed97953c53a60ab38f8e6ace60fd59178084d0dd66e0f79172992d4ddb2e91172ce24949bcebfff158dcc417f2c6e9c6"+                "93cddd5288e7ef4884c8fe321d075df01501b993ff49ffab8184116f39b3c655"+                "04378bad519aab406e04d0e5608bcca809c02d6afd2272d4dd03e9357bd0eee8adf84c8deba3155c9cf9506d1d4c8bfefe3cf033a75716cc3cc07295100ec96276"+                "1ea4484be482bf25fdb2ed39e6a02ed9156b3e57dfb18dff82e4a048de990236"+                "02b266d66919f7b08f42ae0e7d97af4ca98b2dae3043bb7e0740ccadc1957579"+                ""+                ""+                "2480179d880b5f458154b8bfe3c7e8732332de84aabf06fc440f6b31f169e154157fa9eb44f2fa4d7b38a9236e"+                "10cd81e3a816d29942b602a92884348171a31cbd0f042c3057c65cd93c540943a5b05115bd520c09281061935b"+                "f03fbc82f321a0ab4840e487cb75d07aafd8e6f68485e4f7ff72b2f55ff24ad6"+                "1ce0cadec0a8f060f4b5070c8f8888dcdfefc2e35819df0cd559928a11ff0891"+                "70c405c707102fd0041ea716090753be47d68d238b111d542846bd0d84ba907c"+        it "A.4.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04801740f4b1b35823f7fb2930eac2efc8c4893f34ba111c0bb976e3c7d5dc0aef5a7ef0bf4057949a140285f774f1efc53b3860936b92279a11b68395d898d138"+                "778f2254ae5d661d5c7fca8c4a7495a25bd13f26258e459159f3899df0de76c1"+                "04a4ca7af2fc2cce48edbf2f1700983e927743a4e85bb5035ad562043e25d9a111cbf6f7385fac55edc5c9d2ca6ed351a5643de95c36748e11dbec98730f4d43e9"+                "00510a70fde67af487c093234fc4215c1cdec09579c4b30cc8e48cb530414d0e"+                "d743b20821e6326f7a26684a4beed7088b35e392114480ca9f6c325079dcf10b"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "840669634db51e28df54f189329c1b727fd303ae413f003020aff5e26276aaa910fc4296828cb9d862c2fd7d16"+                "d4680a48158d9a75fd09355878d6e33997a36ee01d4a8f22032b22373b795a941b7b9c5205ff99e0ff284beef4"+                "c8c917e137a616d3d4e4c9fcd9c50202f366cb0d37862376bc79f9b72e8a8db9"+                "33a5d4df232777008a06d0684f23bb891cfaef702f653c8601b6ad4d08dddddf"+                "bed80f2e54f1285895c4a3f3b3625e6206f78f1ed329a0cfb5864f7c139b3c6a"
+ test/A5Spec.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE OverloadedStrings #-}++module A5Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_P256_HKDF_SHA256+kdf_id :: KDF_ID+kdf_id = HKDF_SHA256+aead_id :: AEAD_ID+aead_id = ChaCha20Poly1305++spec :: Spec+spec = do+    describe "A.5. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305" $ do+        it "A.5.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04c07836a0206e04e31d8ae99bfd549380b072a1b1b82e563c935c095827824fc1559eac6fb9e3c70cd3193968994e7fe9781aa103f5b50e934b5b2f387e381291"+                "7550253e1147aae48839c1f8af80d2770fb7a4c763afe7d0afa7e0f42a5b3689"+                "04a697bffde9405c992883c5c439d6cc358170b51af72812333b015621dc0f40bad9bb726f68a5c013806a790ec716ab8669f84f6b694596c2987cf35baba2a006"+                "a4d1c55836aa30f9b3fbb6ac98d338c877c2867dd3a77396d13f68d3ab150d3b"+                ""+                ""+                ""+                "6469c41c5c81d3aa85432531ecf6460ec945bde1eb428cb2fedf7a29f5a685b4ccb0d057f03ea2952a27bb458b"+                "f1564199f7e0e110ec9c1bcdde332177fc35c1adf6e57f8d1df24022227ffa8716862dbda2b1dc546c9d114374"+                "9b13c510416ac977b553bf1741018809c246a695f45eff6d3b0356dbefe1e660"+                "6c8b7be3a20a5684edecb4253619d9051ce8583baf850e0cb53c402bdcaf8ebb"+                "477a50d804c7c51941f69b8e32fe8288386ee1a84905fe4938d58972f24ac938"+        it "A.5.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04f336578b72ad7932fe867cc4d2d44a718a318037a0ec271163699cee653fa805c1fec955e562663e0c2061bb96a87d78892bff0cc0bad7906c2d998ebe1a7246"+                "7d6e4e006cee68af9b3fdd583a0ee8962df9d59fab029997ee3f456cbc857904"+                "041eb8f4f20ab72661af369ff3231a733672fa26f385ffb959fd1bae46bfda43ad55e2d573b880831381d9367417f554ce5b2134fbba5235b44db465feffc6189e"+                "12ecde2c8bc2d5d7ed2219c71f27e3943d92b344174436af833337c557c300b3"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "21433eaff24d7706f3ed5b9b2e709b07230e2b11df1f2b1fe07b3c70d5948a53d6fa5c8bed194020bd9df0877b"+                "c74a764b4892072ea8c2c56b9bcd46c7f1e9ca8cb0a263f8b40c2ba59ac9c857033f176019562218769d3e0452"+                "530bbc2f68f078dccc89cc371b4f4ade372c9472bafe4601a8432cbb934f528d"+                "6e25075ddcc528c90ef9218f800ca3dfe1b8ff4042de5033133adb8bd54c401d"+                "6f6fbd0d1c7733f796461b3235a856cc34f676fe61ed509dfc18fa16efe6be78"+        it "A.5.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "040d5176aedba55bc41709261e9195c5146bb62d783031280775f32e507d79b5cbc5748b6be6359760c73cfe10ca19521af704ca6d91ff32fc0739527b9385d415"+                "085fd5d5e6ce6497c79df960cac93710006b76217d8bcfafbd2bb2c20ea03c42"+                "0444f6ee41818d9fe0f8265bffd016b7e2dd3964d610d0f7514244a60dbb7a11ece876bb110a97a2ac6a9542d7344bf7d2bd59345e3e75e497f7416cf38d296233"+                "3cb2c125b8c5a81d165a333048f5dcae29a2ab2072625adad66dbb0f48689af9"+                "39b19402e742d48d319d24d68e494daa4492817342e593285944830320912519"+                ""+                ""+                "25881f219935eec5ba70d7b421f13c35005734f3e4d959680270f55d71e2f5cb3bd2daced2770bf3d9d4916872"+                "653f0036e52a376f5d2dd85b3204b55455b7835c231255ae098d09ed138719b97185129786338ab6543f753193"+                "56c4d6c1d3a46c70fd8f4ecda5d27c70886e348efb51bd5edeaa39ff6ce34389"+                "d2d3e48ed76832b6b3f28fa84be5f11f09533c0e3c71825a34fb0f1320891b51"+                "eb0d312b6263995b4c7761e64b688c215ffd6043ff3bad2368c862784cbe6eff"+        it "A.5.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "043539917ee26f8ae0aa5f784a387981b13de33124a3cde88b94672030183110f331400115855808244ff0c5b6ca6104483ac95724481d41bdcd9f15b430ad16f6"+                "11b7e4de2d919240616a31ab14944cced79bc2372108bb98f6792e3b645fe546"+                "04d383fd920c42d018b9d57fd73a01f1eee480008923f67d35169478e55d2e8817068daf62a06b10e0aad4a9e429fa7f904481be96b79a9c231a33e956c20b81b6"+                "c29fc577b7e74d525c0043f1c27540a1248e4f2c8d297298e99010a92e94865c"+                "53541bd995f874a67f8bfd8038afa67fd68876801f42ff47d0dc2a4deea067ae"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "9eadfa0f954835e7e920ffe56dec6b31a046271cf71fdda55db72926e1d8fae94cc6280fcfabd8db71eaa65c05"+                "e357ad10d75240224d4095c9f6150a2ed2179c0f878e4f2db8ca95d365d174d059ff8c3eb38ea9a65cfc8eaeb8"+                "c52b4592cd33dd38b2a3613108ddda28dcf7f03d30f2a09703f758bfa8029c9a"+                "2f03bebc577e5729e148554991787222b5c2a02b77e9b1ac380541f710e5a318"+                "e01dd49e8bfc3d9216abc1be832f0418adf8b47a7b5a330a7436c31e33d765d7"
+ test/A6Spec.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings #-}++module A6Spec where++import Crypto.HPKE+import Crypto.HPKE.Internal+import Data.ByteString ()+import Test.Hspec++import Test++kem_id :: KEM_ID+kem_id = DHKEM_P521_HKDF_SHA512+kdf_id :: KDF_ID+kdf_id = HKDF_SHA512+aead_id :: AEAD_ID+aead_id = AES_256_GCM++spec :: Spec+spec = do+    describe "A.6 DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM" $ do+        it "A.6.1. Base Setup Information" $ do+            runTest+                ModeBase+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0"+                "014784c692da35df6ecde98ee43ac425dbdd0969c0c72b42f2e708ab9d535415a8569bdacfcc0a114c85b8e3f26acf4d68115f8c91a66178cdbd03b7bcc5291e374b"+                "0401b45498c1714e2dce167d3caf162e45e0642afc7ed435df7902ccae0e84ba0f7d373f646b7738bbbdca11ed91bdeae3cdcba3301f2457be452f271fa6837580e661012af49583a62e48d44bed350c7118c0d8dc861c238c72a2bda17f64704f464b57338e7f40b60959480c0e58e6559b190d81663ed816e523b6b6a418f66d2451ec64"+                "01462680369ae375e4b3791070a7458ed527842f6a98a79ff5e0d4cbde83c27196a3916956655523a6a2556a7af62c5cadabe2ef9da3760bb21e005202f7b2462847"+                ""+                ""+                ""+                "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a"+                "d9ee248e220ca24ac00bbbe7e221a832e4f7fa64c4fbab3945b6f3af0c5ecd5e16815b328be4954a05fd352256"+                "05e2e5bd9f0c30832b80a279ff211cc65eceb0d97001524085d609ead60d0412"+                "fca69744bb537f5b7a1596dbf34eaa8d84bf2e3ee7f1a155d41bd3624aa92b63"+                "f389beaac6fcf6c0d9376e20f97e364f0609a88f1bc76d7328e9104df8477013"++        it "A.6.2. PSK Setup Information" $ do+            runTest+                ModePsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "040085eff0835cc84351f32471d32aa453cdc1f6418eaaecf1c2824210eb1d48d0768b368110fab21407c324b8bb4bec63f042cfa4d0868d19b760eb4beba1bff793b30036d2c614d55730bd2a40c718f9466faf4d5f8170d22b6df98dfe0c067d02b349ae4a142e0c03418f0a1479ff78a3db07ae2c2e89e5840f712c174ba2118e90fdcb"+                "012e5cfe0daf5fe2a1cd617f4c4bae7c86f1f527b3207f115e262a98cc65268ec88cb8645aec73b7aa0a472d0292502d1078e762646e0c093cf873243d12c39915f6"+                "04006917e049a2be7e1482759fb067ddb94e9c4f7f5976f655088dec45246614ff924ed3b385fc2986c0ecc39d14f907bf837d7306aada59dd5889086125ecd038ead400603394b5d81f89ebfd556a898cc1d6a027e143d199d3db845cb91c5289fb26c5ff80832935b0e8dd08d37c6185a6f77683347e472d1edb6daa6bd7652fea628fae"+                "011bafd9c7a52e3e71afbdab0d2f31b03d998a0dc875dd7555c63560e142bde264428de03379863b4ec6138f813fa009927dc5d15f62314c56d4e7ff2b485753eb72"+                ""+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "de69e9d943a5d0b70be3359a19f317bd9aca4a2ebb4332a39bcdfc97d5fe62f3a77702f4822c3be531aa7843a1"+                "77a16162831f90de350fea9152cfc685ecfa10acb4f7994f41aed43fa5431f2382d078ec88baec53943984553e"+                "62691f0f971e34de38370bff24deb5a7d40ab628093d304be60946afcdb3a936"+                "76083c6d1b6809da088584674327b39488eaf665f0731151128452e04ce81bff"+                "0c7cfc0976e25ae7680cf909ae2de1859cd9b679610a14bec40d69b91785b2f6"+        it "A.6.3. PSK Setup Information" $ do+            runTest+                ModeAuth+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04017de12ede7f72cb101dab36a111265c97b3654816dcd6183f809d4b3d111fe759497f8aefdc5dbb40d3e6d21db15bdc60f15f2a420761bcaeef73b891c2b117e9cf01e29320b799bbc86afdc5ea97d941ea1c5bd5ebeeac7a784b3bab524746f3e640ec26ee1bd91255f9330d974f845084637ee0e6fe9f505c5b87c86a4e1a6c3096dd"+                "0185f03560de87bb2c543ef03607f3c33ac09980000de25eabe3b224312946330d2e65d192d3b4aa46ca92fc5ca50736b624402d95f6a80dc04d1f10ae9517137261"+                "04007d419b8834e7513d0e7cc66424a136ec5e11395ab353da324e3586673ee73d53ab34f30a0b42a92d054d0db321b80f6217e655e304f72793767c4231785c4a4a6e008f31b93b7a4f2b8cd12e5fe5a0523dc71353c66cbdad51c86b9e0bdfcd9a45698f2dab1809ab1b0f88f54227232c858accc44d9a8d41775ac026341564a2d749f4"+                "013ef326940998544a899e15e1726548ff43bbdb23a8587aa3bef9d1b857338d87287df5667037b519d6a14661e9503cfc95a154d93566d8c84e95ce93ad05293a0b"+                "001018584599625ff9953b9305849850d5e34bd789d4b81101139662fbea8b6508ddb9d019b0d692e737f66beae3f1f783e744202aaf6fea01506c27287e359fe776"+                ""+                ""+                "0116aeb3a1c405c61b1ce47600b7ecd11d89b9c08c408b7e2d1e00a4d64696d12e6881dc61688209a8207427f9"+                "37ece0cf6741f443e9d73b9966dc0b228499bb21fbf313948327231e70a18380e080529c0267f399ba7c539cc6"+                "8d78748d632f95b8ce0c67d70f4ad1757e61e872b5941e146986804b3990154b"+                "80a4753230900ea785b6c80775092801fe91183746479f9b04c305e1db9d1f4d"+                "620b176d737cf366bcc20d96adb54ec156978220879b67923689e6dca36210ed"++        it "A.6.4. PSK Setup Information" $ do+            runTest+                ModeAuthPsk+                kem_id+                kdf_id+                aead_id+                "4f6465206f6e2061204772656369616e2055726e"+                "04000a5096a6e6e002c83517b494bfc2e36bfb8632fae8068362852b70d0ff71e560b15aff96741ecffb63d8ac3090c3769679009ac59a99a1feb4713c5f090fc0dbed01ad73c45d29d369e36744e9ed37d12f80700c16d816485655169a5dd66e4ddf27f2acffe0f56f7f77ea2b473b4bf0518b975d9527009a3d14e5a4957e3e8a9074f8"+                "003430af19716084efeced1241bb1a5625b6c826f11ef31649095eb27952619e36f62a79ea28001ac452fb20ddfbb66e62c6c0b1be03c0d28c97794a1fb638207a83"+                "0401655b5d3b7cfafaba30851d25edc44c6dd17d99410efbed8591303b4dbeea8cb1045d5255f9a60384c3bbd4a3386ae6e6fab341dc1f8db0eed5f0ab1aaac6d7838e00dadf8a1c2c64b48f89c633721e88369e54104b31368f26e35d04a442b0b428510fb23caada686add16492f333b0f7ba74c391d779b788df2c38d7a7f4778009d91"+                "0053c0bc8c1db4e9e5c3e3158bfdd7fc716aef12db13c8515adf821dd692ba3ca53041029128ee19c8556e345c4bcb840bb7fd789f97fe10f17f0e2c6c2528072843"+                "003f64675fc8914ec9e2b3ecf13585b26dbaf3d5d805042ba487a5070b8c5ac1d39b17e2161771cc1b4d0a3ba6e866f4ea4808684b56af2a49b5e5111146d45d9326"+                "0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82"+                "456e6e796e20447572696e206172616e204d6f726961"+                "942a2a92e0817cf032ce61abccf4f3a7c5d21b794ed943227e07b7df2d6dd92c9b8a9371949e65cca262448ab7"+                "c0a83b5ec3d7933a090f681717290337b4fede5bfaa0a40ec29f93acad742888a1513c649104c391c78d1d7f29"+                "a39502ef5ca116aa1317bd9583dd52f15b0502b71d900fc8a622d19623d0cb5d"+                "749eda112c4cfdd6671d84595f12cd13198fc3ef93ed72369178f344fe6e09c3"+                "f8b4e72cefbff4ca6c4eabb8c0383287082cfcbb953d900aed4959afd0017095"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/Test.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE OverloadedStrings #-}++module Test (runTest) where++import Data.ByteString (ByteString)+import qualified Data.ByteString.Base16 as B16+import Test.Hspec++import Crypto.HPKE+import Crypto.HPKE.Internal++runTest+    :: Mode+    -> KEM_ID+    -> KDF_ID+    -> AEAD_ID+    -> Info+    -> ByteString -- pkEm+    -> ByteString -- skEm+    -> ByteString -- pkRm+    -> ByteString -- skRm+    -> ByteString -- skSm+    -> PSK+    -> PSK_ID+    -> CipherText+    -> CipherText+    -> Key+    -> Key+    -> Key+    -> IO ()+runTest mode kem_id kdf_id aead_id _info _pkEm _skEm _pkRm _skRm _skSm _psk _psk_id _ct0 _ct1 _sec0 _sec1 _sec2 = do+    (enc, ctxS) <-+        setupS+            defaultHPKEMap+            mode+            kem_id+            kdf_id+            aead_id+            (Just skEm) -- key provided+            mskSm -- auth+            pkRm+            info+            psk+            psk_id+    ctxR <-+        setupR+            defaultHPKEMap+            mode+            kem_id+            kdf_id+            aead_id+            skRm+            mskSm -- auth+            pkEm+            info+            psk+            psk_id+    enc `shouldBe` pkEm+    ct0' <- seal ctxS aad0 pt+    ct0' `shouldBe` ct0+    pt0 <- open ctxR aad0 ct0+    pt0 `shouldBe` pt+    ct1' <- seal ctxS aad1 pt+    ct1' `shouldBe` ct1+    pt1 <- open ctxR aad1 ct1+    pt1 `shouldBe` pt++    exportS ctxS exporter_context0 32 `shouldBe` sec0+    exportR ctxR exporter_context0 32 `shouldBe` sec0+    exportS ctxS exporter_context1 32 `shouldBe` sec1+    exportS ctxS exporter_context2 32 `shouldBe` sec2+  where+    info = B16.decodeLenient _info+    pkEm = EncodedPublicKey $ B16.decodeLenient _pkEm+    skEm = EncodedSecretKey $ B16.decodeLenient _skEm+    pkRm = EncodedPublicKey $ B16.decodeLenient _pkRm+    skRm = EncodedSecretKey $ B16.decodeLenient _skRm+    mskSm+        | _skSm == "" = Nothing+        | otherwise = Just $ EncodedSecretKey $ B16.decodeLenient _skSm+    psk = B16.decodeLenient _psk+    psk_id = B16.decodeLenient _psk_id+    ct0 = B16.decodeLenient _ct0+    ct1 = B16.decodeLenient _ct1+    sec0 = B16.decodeLenient _sec0+    sec1 = B16.decodeLenient _sec1+    sec2 = B16.decodeLenient _sec2++aad0 :: AAD+aad0 = "\x43\x6f\x75\x6e\x74\x2d\x30"+aad1 :: AAD+aad1 = "\x43\x6f\x75\x6e\x74\x2d\x31"+pt :: PlainText+pt = "Beauty is truth, truth beauty"+exporter_context0 :: Info+exporter_context0 = ""+exporter_context1 :: Info+exporter_context1 = "\x00"+exporter_context2 :: Info+exporter_context2 = "\x54\x65\x73\x74\x43\x6f\x6e\x74\x65\x78\x74"