hpke 0.1.0 → 0.2.0
raw patch · 7 files changed
+72/−20 lines, 7 filesdep ~cryptonPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: crypton
API changes (from Hackage documentation)
+ Crypto.HPKE: ExportError :: String -> HPKEError
- Crypto.HPKE: exportR :: ContextR -> Info -> Int -> Key
+ Crypto.HPKE: exportR :: ContextR -> Info -> Int -> Either HPKEError Key
- Crypto.HPKE: exportS :: ContextS -> Info -> Int -> Key
+ Crypto.HPKE: exportS :: ContextS -> Info -> Int -> Either HPKEError Key
- Crypto.HPKE.Internal: labeledExpand :: KDF h => Suite -> PRK h -> Label -> Info -> Int -> Key
+ Crypto.HPKE.Internal: labeledExpand :: KDF h => Suite -> PRK h -> Label -> Info -> Int -> Either HPKEError Key
Files
- ChangeLog.md +13/−0
- Crypto/HPKE/Context.hs +12/−6
- Crypto/HPKE/KDF.hs +24/−4
- Crypto/HPKE/KeySchedule.hs +5/−4
- Crypto/HPKE/Types.hs +3/−0
- hpke.cabal +2/−2
- test/Test.hs +13/−4
ChangeLog.md view
@@ -1,5 +1,18 @@ # ChangeLog for hpke +## 0.2.0++* Breaking change: `exportS` and `exportR` return `Either HPKEError Key`+ rather than `Key`. RFC 9180 section 5.3 allows an export of at most+ `255 * Nh` octets and the length is the caller's to choose, so the refusal+ now has somewhere to go. Nothing was total here before -- a length beyond+ 65535 did not fit the two octets `LabeledExpand` writes it into, and+ crypton raised -- and from crypton 2.0.0 the shorter limit raises as well.+* Breaking change: `HPKEError` gains `ExportError`, which is what the above+ reports. It is appended, so the existing constructors are where they+ were, but an exhaustive `case` without a wildcard will warn.+* Supporting crypton 2.0.+ ## 0.1.0 * Using "ram" instead of "memory".
Crypto/HPKE/Context.hs view
@@ -28,7 +28,7 @@ { seqRefS :: IORef Integer , sealS :: Seal , nonceBaseS :: Nonce- , expandS :: Info -> Int -> Key+ , expandS :: Info -> Int -> Either HPKEError Key } -- | Context for receivers.@@ -36,7 +36,7 @@ { seqRefR :: IORef Integer , openR :: Open , nonceBaseR :: Nonce- , expandR :: Info -> Int -> Key+ , expandR :: Info -> Int -> Either HPKEError Key } ----------------------------------------------------------------@@ -76,12 +76,18 @@ ---------------------------------------------------------------- -- | Exporting secret.-exportS :: ContextS -> Info -> Int -> Key+--+-- RFC 9180 section 5.3 allows a length of at most @255 * Nh@, where @Nh@ is+-- the output of the KDF's hash; a longer one is 'Left' 'ExportError'.+exportS :: ContextS -> Info -> Int -> Either HPKEError Key exportS ContextS{..} exporter_context len = expandS exporter_context len -- | Exporting secret.-exportR :: ContextR -> Info -> Int -> Key+--+-- RFC 9180 section 5.3 allows a length of at most @255 * Nh@, where @Nh@ is+-- the output of the KDF's hash; a longer one is 'Left' 'ExportError'.+exportR :: ContextR -> Info -> Int -> Either HPKEError Key exportR ContextR{..} exporter_context len = expandR exporter_context len @@ -91,7 +97,7 @@ :: Key -> Nonce -> (Key -> Seal)- -> (Info -> Int -> Key)+ -> (Info -> Int -> Either HPKEError Key) -> IO ContextS newContextS key nonce_base seal' expand = do seqref <- newIORef 0@@ -109,7 +115,7 @@ :: Key -> Nonce -> (Key -> Open)- -> (Info -> Int -> Key)+ -> (Info -> Int -> Either HPKEError Key) -> IO ContextR newContextR key nonce_base open' expand = do seqref <- newIORef 0
Crypto/HPKE/KDF.hs view
@@ -12,6 +12,7 @@ ) where +import Crypto.Hash.IO (hashDigestSize) import Crypto.Hash.Algorithms ( HashAlgorithm, SHA256 (..),@@ -27,8 +28,14 @@ class KDF h where labeledExtract :: Suite -> Salt -> Label -> IKM -> PRK h- labeledExpand :: Suite -> PRK h -> Label -> Info -> Int -> Key + -- | RFC 9180 section 5.3 allows an output of at most @255 * Nh@ octets,+ -- which is also what HKDF's counter can reach. A longer one is refused+ -- here rather than left to the HKDF underneath, whose way of saying so+ -- is an exception.+ labeledExpand+ :: Suite -> PRK h -> Label -> Info -> Int -> Either HPKEError Key+ instance KDF SHA256 where labeledExtract = labeledExtract_ labeledExpand = labeledExpand_@@ -50,9 +57,20 @@ 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+ :: forall a+ . HashAlgorithm a+ => Suite -> PRK a -> Label -> Info -> Int -> Either HPKEError Key+labeledExpand_ suite prk label info len+ | len < 0 || len > maxLen =+ Left $+ ExportError $+ "length "+ ++ show len+ ++ " is outside 0 .. "+ ++ show maxLen+ | otherwise = Right $ HKDF.expand prk labeled_info len where+ maxLen = 255 * hashDigestSize (undefined :: a) labeled_info = i2ospOf_ 2 (fromIntegral len) <> "HPKE-v1" <> suite <> label <> info @@ -67,5 +85,7 @@ eae_prk :: PRK h eae_prk = labeledExtract suite "" "eae_prk" $ convert dh siz = hashDigestSize h+ -- the hash's own digest size, so the length is in range by construction shared_secret =- labeledExpand suite eae_prk "shared_secret" kem_context siz+ either (const "") id $+ labeledExpand suite eae_prk "shared_secret" kem_context siz
Crypto/HPKE/KeySchedule.hs view
@@ -47,7 +47,11 @@ -> PSK_ID -> SharedSecret -> Either HPKEError (Key, Nonce, Int, PRK h)-keySchedule h suite nk nn mode info psk psk_id shared_secret =+keySchedule h suite nk nn mode info psk psk_id shared_secret = do+ 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 case toPRK exporter_secret of Nothing -> Left $ KeyScheduleError "cannot convert to PRK" Just prk -> Right (key, base_nonce, 0, prk)@@ -60,7 +64,4 @@ 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/Types.hs view
@@ -66,6 +66,9 @@ | DeriveKeyPairError String | -- | Original KeyScheduleError String+ | -- | Original. An export longer than the @255 * Nh@ that RFC 9180+ -- section 5.3 allows.+ ExportError String | Unsupported String deriving (Eq, Show)
hpke.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: hpke-version: 0.1.0+version: 0.2.0 license: BSD3 license-file: LICENSE maintainer: kazu@iij.ad.jp@@ -32,7 +32,7 @@ base >=4.7 && <5, base16-bytestring, bytestring,- crypton >= 1.1.0 && <1.2,+ crypton >= 1.1.0 && <2.1, ram default-extensions: Strict StrictData
test/Test.hs view
@@ -65,10 +65,19 @@ 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+ exportS ctxS exporter_context0 32 `shouldBe` Right sec0+ exportR ctxR exporter_context0 32 `shouldBe` Right sec0+ exportS ctxS exporter_context1 32 `shouldBe` Right sec1+ exportS ctxS exporter_context2 32 `shouldBe` Right sec2++ -- RFC 9180 section 5.3 allows at most 255*Nh octets, which is 16320+ -- even for SHA-512, the widest hash here+ case exportS ctxS exporter_context0 20000 of+ Left (ExportError _) -> return ()+ r -> expectationFailure $ "an over-long export was not refused: " ++ show (fmap (const ()) r)+ case exportR ctxR exporter_context0 20000 of+ Left (ExportError _) -> return ()+ r -> expectationFailure $ "an over-long export was not refused: " ++ show (fmap (const ()) r) where info = B16.decodeLenient _info pkEm = EncodedPublicKey $ B16.decodeLenient _pkEm