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 +5/−0
- Crypto/HPKE.hs +71/−0
- Crypto/HPKE/AEAD.hs +193/−0
- Crypto/HPKE/Context.hs +122/−0
- Crypto/HPKE/ID.hs +183/−0
- Crypto/HPKE/Internal.hs +40/−0
- Crypto/HPKE/KDF.hs +71/−0
- Crypto/HPKE/KEM.hs +185/−0
- Crypto/HPKE/KeyPair.hs +25/−0
- Crypto/HPKE/KeySchedule.hs +66/−0
- Crypto/HPKE/PublicKey.hs +60/−0
- Crypto/HPKE/Setup.hs +233/−0
- Crypto/HPKE/Types.hs +152/−0
- LICENSE +29/−0
- hpke.cabal +62/−0
- test/A1Spec.hs +99/−0
- test/A2Spec.hs +98/−0
- test/A3Spec.hs +98/−0
- test/A4Spec.hs +97/−0
- test/A5Spec.hs +97/−0
- test/A6Spec.hs +99/−0
- test/Spec.hs +1/−0
- test/Test.hs +100/−0
+ 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"