jose-jwt 0.7.6 → 0.7.7
raw patch · 5 files changed
+146/−95 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Jose.Internal.Crypto: decryptPayload :: Enc -> ByteString -> IV -> ByteString -> Tag -> ByteString -> Maybe ByteString
+ Jose.Internal.Crypto: decryptPayload :: forall ba. (ByteArray ba) => Enc -> ScrubbedBytes -> IV -> ba -> Tag -> ba -> Maybe ba
- Jose.Internal.Crypto: encryptPayload :: Enc -> ByteString -> ByteString -> ByteString -> ByteString -> Maybe (AuthTag, ByteString)
+ Jose.Internal.Crypto: encryptPayload :: forall ba iv. (ByteArray ba, ByteArray iv) => Enc -> ScrubbedBytes -> iv -> ba -> ba -> Maybe (AuthTag, ba)
- Jose.Internal.Crypto: generateCmkAndIV :: MonadRandom m => Enc -> m (ByteString, ByteString)
+ Jose.Internal.Crypto: generateCmkAndIV :: MonadRandom m => Enc -> m (ScrubbedBytes, ScrubbedBytes)
- Jose.Internal.Crypto: keyWrap :: JweAlg -> ByteString -> ByteString -> Either JwtError ByteString
+ Jose.Internal.Crypto: keyWrap :: ByteArray ba => JweAlg -> ScrubbedBytes -> ScrubbedBytes -> Either JwtError ba
- Jose.Internal.Crypto: rsaDecrypt :: Maybe Blinder -> PrivateKey -> JweAlg -> ByteString -> Either JwtError ByteString
+ Jose.Internal.Crypto: rsaDecrypt :: ByteArray ct => Maybe Blinder -> PrivateKey -> JweAlg -> ct -> Either JwtError ScrubbedBytes
- Jose.Internal.Crypto: rsaEncrypt :: MonadRandom m => PublicKey -> JweAlg -> ByteString -> m (Either JwtError ByteString)
+ Jose.Internal.Crypto: rsaEncrypt :: (MonadRandom m, ByteArray msg, ByteArray out) => PublicKey -> JweAlg -> msg -> m (Either JwtError out)
Files
- CHANGELOG.md +5/−0
- Jose/Internal/Crypto.hs +106/−74
- Jose/Jwe.hs +9/−8
- jose-jwt.cabal +1/−1
- tests/Tests/JweSpec.hs +25/−12
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.7.7+-----++* User ByteArray and ScrubbedBytes from memory package in preference to ByteString in internal crypto code.+ 0.7.6 -----
Jose/Internal/Crypto.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_HADDOCK prune #-} -- | Internal functions for encrypting and signing / decrypting@@ -23,6 +24,7 @@ where +import Control.Applicative import Control.Monad (when, unless) import Crypto.Error import Crypto.Cipher.AES@@ -36,6 +38,7 @@ import Crypto.Random (MonadRandom, getRandomBytes) import Crypto.MAC.HMAC (HMAC (..), hmac) import Data.Bits (xor)+import Data.ByteArray (ByteArray, ScrubbedBytes) import qualified Data.ByteArray as BA import Data.ByteString (ByteString) import qualified Data.ByteString as B@@ -126,8 +129,10 @@ -- -- Used to encrypt a message. generateCmkAndIV :: MonadRandom m- => Enc -- ^ The encryption algorithm to be used- -> m (B.ByteString, B.ByteString) -- ^ The key, IV+ => Enc+ -- ^ The encryption algorithm to be used+ -> m (ScrubbedBytes, ScrubbedBytes)+ -- ^ The key, IV generateCmkAndIV e = do cmk <- getRandomBytes (keySize e) iv <- getRandomBytes (ivSize e) -- iv for aes gcm or cbc@@ -146,35 +151,46 @@ ivSize _ = 16 -- | Encrypts a message (typically a symmetric key) using RSA.-rsaEncrypt :: MonadRandom m- => RSA.PublicKey -- ^ The encryption key- -> JweAlg -- ^ The algorithm (either @RSA1_5@ or @RSA_OAEP@)- -> B.ByteString -- ^ The message to encrypt- -> m (Either JwtError B.ByteString) -- ^ The encrypted message-rsaEncrypt k a bs = case a of+rsaEncrypt :: (MonadRandom m, ByteArray msg, ByteArray out)+ => RSA.PublicKey+ -- ^ The encryption key+ -> JweAlg+ -- ^ The algorithm (either @RSA1_5@ or @RSA_OAEP@)+ -> msg+ -- ^ The message to encrypt+ -> m (Either JwtError out)+ -- ^ The encrypted message+rsaEncrypt k a msg = fmap BA.convert <$> case a of RSA1_5 -> mapErr (PKCS15.encrypt k bs) RSA_OAEP -> mapErr (OAEP.encrypt (OAEP.defaultOAEPParams SHA1) k bs) _ -> return (Left (BadAlgorithm "Not an RSA algorithm")) where+ bs = BA.convert msg mapErr = fmap (mapLeft (const BadCrypto)) -- | Decrypts an RSA encrypted message.-rsaDecrypt :: Maybe RSA.Blinder- -> RSA.PrivateKey -- ^ The decryption key- -> JweAlg -- ^ The RSA algorithm to use- -> B.ByteString -- ^ The encrypted content- -> Either JwtError B.ByteString -- ^ The decrypted key-rsaDecrypt blinder rsaKey a jweKey = case a of- RSA1_5 -> mapErr (PKCS15.decrypt blinder rsaKey jweKey)- RSA_OAEP -> mapErr (OAEP.decrypt blinder (OAEP.defaultOAEPParams SHA1) rsaKey jweKey)+rsaDecrypt :: ByteArray ct+ => Maybe RSA.Blinder+ -> RSA.PrivateKey+ -- ^ The decryption key+ -> JweAlg+ -- ^ The RSA algorithm to use+ -> ct+ -- ^ The encrypted content+ -> Either JwtError ScrubbedBytes+ -- ^ The decrypted key+rsaDecrypt blinder rsaKey a ct = BA.convert <$> case a of+ RSA1_5 -> mapErr (PKCS15.decrypt blinder rsaKey bs)+ RSA_OAEP -> mapErr (OAEP.decrypt blinder (OAEP.defaultOAEPParams SHA1) rsaKey bs) _ -> Left (BadAlgorithm "Not an RSA algorithm") where+ bs = BA.convert ct mapErr = mapLeft (const BadCrypto) -- Dummy type to constrain Cipher type data C c = C -initCipher :: BlockCipher c => C c -> ByteString -> Either JwtError c+initCipher :: BlockCipher c => C c -> ScrubbedBytes -> Either JwtError c initCipher _ k = mapFail (cipherInit k) -- Map CryptoFailable to JwtError@@ -186,13 +202,20 @@ -- | Decrypt an AES encrypted message.-decryptPayload :: Enc -- ^ Encryption algorithm- -> ByteString -- ^ Content encryption key- -> IV -- ^ IV- -> ByteString -- ^ Additional authentication data- -> Tag -- ^ The integrity protection value to be checked- -> ByteString -- ^ The encrypted JWT payload- -> Maybe ByteString+decryptPayload :: forall ba. (ByteArray ba)+ => Enc+ -- ^ Encryption algorithm+ -> ScrubbedBytes+ -- ^ Content encryption key+ -> IV+ -- ^ IV+ -> ba+ -- ^ Additional authentication data+ -> Tag+ -- ^ The integrity protection value to be checked+ -> ba+ -- ^ The encrypted JWT payload+ -> Maybe ba decryptPayload enc cek iv_ aad tag_ ct = case (enc, iv_, tag_) of (A128GCM, IV12 b, Tag16 t) -> doGCM (C :: C AES128) b t (A192GCM, IV12 b, Tag16 t) -> doGCM (C :: C AES192) b t@@ -202,21 +225,21 @@ (A256CBC_HS512, IV16 b, Tag32 t) -> doCBC (C :: C AES256) b t SHA512 32 _ -> Nothing -- This shouldn't be possible if the JWT was parsed first where- (cbcMacKey, cbcEncKey) = B.splitAt (B.length cek `div` 2) cek- al = fromIntegral (B.length aad) * 8 :: Word64+ (cbcMacKey, cbcEncKey) = BA.splitAt (BA.length cek `div` 2) cek :: (ScrubbedBytes, ScrubbedBytes)+ al = fromIntegral (BA.length aad) * 8 :: Word64 - doGCM :: BlockCipher c => C c -> ByteString -> ByteString -> Maybe ByteString+ doGCM :: BlockCipher c => C c -> ByteString -> ByteString -> Maybe ba doGCM c iv tag = do cipher <- rightToMaybe (initCipher c cek) aead <- maybeCryptoError (aeadInit AEAD_GCM cipher iv) aeadSimpleDecrypt aead aad ct (AuthTag $ BA.convert tag) - doCBC :: (HashAlgorithm a, BlockCipher c) => C c -> ByteString -> ByteString -> a -> Int -> Maybe ByteString+ doCBC :: (HashAlgorithm a, BlockCipher c) => C c -> ByteString -> ByteString -> a -> Int -> Maybe ba doCBC c iv tag a tagLen = do checkMac a tag iv tagLen cipher <- rightToMaybe (initCipher c cbcEncKey) iv' <- makeIV iv- unless (B.length ct `mod` blockSize cipher == 0) Nothing+ unless (BA.length ct `mod` blockSize cipher == 0) Nothing unpad $ cbcDecrypt cipher iv' ct checkMac :: HashAlgorithm a => a -> ByteString -> ByteString -> Int -> Maybe ()@@ -225,15 +248,22 @@ unless (tag `BA.constEq` mac) Nothing doMac :: HashAlgorithm a => a -> ByteString -> HMAC a- doMac _ iv = hmac cbcMacKey $ B.concat [aad, iv, ct, Serialize.encode al]+ doMac _ iv = hmac cbcMacKey (BA.concat [BA.convert aad, iv, BA.convert ct, Serialize.encode al] :: ByteString) -- | Encrypt a message using AES.-encryptPayload :: Enc -- ^ Encryption algorithm- -> ByteString -- ^ Content management key- -> ByteString -- ^ IV- -> ByteString -- ^ Additional authenticated data- -> ByteString -- ^ The message/JWT claims- -> Maybe (AuthTag, ByteString) -- ^ Ciphertext claims and signature tag+encryptPayload :: forall ba iv. (ByteArray ba, ByteArray iv)+ => Enc+ -- ^ Encryption algorithm+ -> ScrubbedBytes+ -- ^ Content management key+ -> iv+ -- ^ IV+ -> ba+ -- ^ Additional authenticated data+ -> ba+ -- ^ The message/JWT claims+ -> Maybe (AuthTag, ba)+ -- ^ Ciphertext claims and signature tag encryptPayload e cek iv aad msg = case e of A128GCM -> doGCM (C :: C AES128) A192GCM -> doGCM (C :: C AES192)@@ -242,16 +272,15 @@ A192CBC_HS384 -> doCBC (C :: C AES192) SHA384 24 A256CBC_HS512 -> doCBC (C :: C AES256) SHA512 32 where- (cbcMacKey, cbcEncKey) = B.splitAt (B.length cek `div` 2) cek- al = fromIntegral (B.length aad) * 8 :: Word64+ (cbcMacKey, cbcEncKey) = BA.splitAt (BA.length cek `div` 2) cek :: (ScrubbedBytes, ScrubbedBytes)+ al = fromIntegral (BA.length aad) * 8 :: Word64 - doGCM :: BlockCipher c => C c -> Maybe (AuthTag, ByteString) doGCM c = do cipher <- rightToMaybe (initCipher c cek) aead <- maybeCryptoError (aeadInit AEAD_GCM cipher iv) return $ aeadSimpleEncrypt aead aad msg 16 - doCBC :: (HashAlgorithm a, BlockCipher c) => C c -> a -> Int -> Maybe (AuthTag, ByteString)+ doCBC :: (HashAlgorithm a, BlockCipher c) => C c -> a -> Int -> Maybe (AuthTag, ba) doCBC c a tagLen = do cipher <- rightToMaybe (initCipher c cbcEncKey) iv' <- makeIV iv@@ -260,39 +289,39 @@ tag = BA.take tagLen (BA.convert mac) return (AuthTag tag, ct) - doMac :: HashAlgorithm a => a -> ByteString -> HMAC a- doMac _ ct = hmac cbcMacKey $ B.concat [aad, iv, ct, Serialize.encode al]+ doMac :: HashAlgorithm a => a -> ba -> HMAC a+ doMac _ ct = hmac cbcMacKey (BA.concat [BA.convert aad, BA.convert iv, BA.convert ct, Serialize.encode al] :: ByteString) -unpad :: ByteString -> Maybe ByteString+unpad :: (ByteArray ba) => ba -> Maybe ba unpad bs- | padLen > 16 || padLen /= B.length padding = Nothing- | B.any (/= padByte) padding = Nothing+ | padLen > 16 || padLen /= BA.length padding = Nothing+ | BA.any (/= padByte) padding = Nothing | otherwise = return pt where- len = B.length bs- padByte = B.last bs+ len = BA.length bs+ padByte = BA.index bs (len-1) padLen = fromIntegral padByte- (pt, padding) = B.splitAt (len - padLen) bs+ (pt, padding) = BA.splitAt (len - padLen) bs -pad :: ByteString -> ByteString-pad bs = B.append bs padding+pad :: (ByteArray ba) => ba -> ba+pad bs = BA.append bs padding where- lastBlockSize = B.length bs `mod` 16+ lastBlockSize = BA.length bs `mod` 16 padByte = fromIntegral $ 16 - lastBlockSize :: Word8- padding = B.replicate (fromIntegral padByte) padByte+ padding = BA.replicate (fromIntegral padByte) padByte -- Key wrapping and unwrapping functions -- | <https://tools.ietf.org/html/rfc3394#section-2.2.1>-keyWrap :: JweAlg -> ByteString -> ByteString -> Either JwtError ByteString+keyWrap :: ByteArray ba => JweAlg -> ScrubbedBytes -> ScrubbedBytes -> Either JwtError ba keyWrap alg kek cek = case alg of A128KW -> doKeyWrap (C :: C AES128) A192KW -> doKeyWrap (C :: C AES192) A256KW -> doKeyWrap (C :: C AES256) _ -> Left (BadAlgorithm "Not a keywrap algorithm") where- l = B.length cek+ l = BA.length cek n = l `div` 8 iv = BA.replicate 8 166 :: ByteString @@ -300,36 +329,41 @@ when (l < 16 || l `mod` 8 /= 0) (Left (KeyError "Invalid content key")) cipher <- initCipher c kek let p = toBlocks cek- (r0, r) = foldl (doRound (ecbEncrypt cipher) 1) (iv, p) [0..5]- Right $ B.concat (r0 : r)+ (r0, r) = foldl (doRound (ecbEncrypt cipher) 1) (BA.convert iv, p) [0..5]+ Right $ BA.concat (r0 : r) doRound _ _ (a, []) _ = (a, []) doRound enc i (a, r:rs) j =- let b = enc $ B.concat [a, r]+ let b = enc $ BA.concat [a, r] t = fromIntegral ((n*j) + i) :: Word8- a' = txor t (B.take 8 b)- r' = B.drop 8 b+ a' = txor t (BA.take 8 b)+ r' = BA.drop 8 b next = doRound enc (i+1) (a', rs) j in (fst next, r' : snd next) - txor t b = B.snoc (B.init b) (B.last b `xor` t)+txor :: ByteArray ba => Word8 -> ba -> ba+txor t b =+ let n = BA.length b+ lastByte = BA.index b (n-1)+ initBytes = BA.take (n-1) b+ in BA.snoc initBytes (lastByte `xor` t) -toBlocks :: ByteString -> [ByteString]+toBlocks :: ByteArray ba => ba -> [ba] toBlocks bytes- | bytes == B.empty = []- | otherwise = let (b, bs') = B.splitAt 8 bytes- in b : toBlocks bs'+ | BA.null bytes = []+ | otherwise = let (b, bs') = BA.splitAt 8 bytes+ in b : toBlocks bs' -keyUnwrap :: ByteString -> JweAlg -> ByteString -> Either JwtError ByteString+keyUnwrap :: ByteArray ba => ScrubbedBytes -> JweAlg -> ba -> Either JwtError ScrubbedBytes keyUnwrap kek alg encK = case alg of A128KW -> doUnWrap (C :: C AES128) A192KW -> doUnWrap (C :: C AES192) A256KW -> doUnWrap (C :: C AES256) _ -> Left (BadAlgorithm "Not a keywrap algorithm") where- l = B.length encK+ l = BA.length encK n = (l `div` 8) - 1- iv = BA.replicate 8 166 :: ByteString+ iv = BA.replicate 8 166 doUnWrap c = do when (l < 24 || l `mod` 8 /= 0) (Left BadCrypto)@@ -337,15 +371,13 @@ let r = toBlocks encK (p0, p) = foldl (doRound (ecbDecrypt cipher) n) (head r, reverse (tail r)) (reverse [0..5]) unless (p0 == iv) (Left BadCrypto)- Right $ B.concat (reverse p)+ Right $ BA.concat (reverse p) doRound _ _ (a, []) _ = (a, []) doRound dec i (a, r:rs) j =- let b = dec $ B.concat [txor t a, r]+ let b = dec $ BA.concat [txor t a, r] t = fromIntegral ((n*j) + i) :: Word8- a' = B.take 8 b- r' = B.drop 8 b+ a' = BA.take 8 b+ r' = BA.drop 8 b next = doRound dec (i-1) (a', rs) j in (fst next, r' : snd next)-- txor t b = B.snoc (B.init b) (B.last b `xor` t)
Jose/Jwe.hs view
@@ -46,6 +46,7 @@ import Crypto.Cipher.Types (AuthTag(..)) import Crypto.PubKey.RSA (PrivateKey(..), PublicKey(..), generateBlinder, private_pub) import Crypto.Random (MonadRandom)+import Data.ByteArray (ByteArray, ScrubbedBytes) import qualified Data.ByteArray as BA import Data.ByteString (ByteString) import qualified Data.ByteString as B@@ -68,7 +69,7 @@ jwkEncode a e jwk payload = runEitherT $ case jwk of RsaPublicJwk kPub kid _ _ -> doEncode (hdr kid) (doRsa kPub) bytes RsaPrivateJwk kPr kid _ _ -> doEncode (hdr kid) (doRsa (private_pub kPr)) bytes- SymmetricJwk kek kid _ _ -> doEncode (hdr kid) (hoistEither . keyWrap a kek) bytes+ SymmetricJwk kek kid _ _ -> doEncode (hdr kid) (hoistEither . keyWrap a (BA.convert kek)) bytes _ -> left $ KeyError "JWK cannot encode a JWE" where doRsa kPub = EitherT . rsaEncrypt kPub a@@ -90,12 +91,12 @@ blinder <- lift $ generateBlinder (public_n $ private_pub kPr) e <- doDecode (rsaDecrypt (Just blinder) kPr) jwt return (Jwe e)- SymmetricJwk kb _ _ _ -> fmap Jwe (doDecode (keyUnwrap kb) jwt)+ SymmetricJwk kb _ _ _ -> fmap Jwe (doDecode (keyUnwrap (BA.convert kb)) jwt) _ -> left $ KeyError "JWK cannot decode a JWE" doDecode :: MonadRandom m- => (JweAlg -> ByteString -> Either JwtError ByteString)+ => (JweAlg -> ByteString -> Either JwtError ScrubbedBytes) -> ByteString -> EitherT JwtError m Jwe doDecode decodeCek jwt = do@@ -106,7 +107,7 @@ enc = jweEnc hdr (dummyCek, _) <- lift $ generateCmkAndIV enc let decryptedCek = either (const dummyCek) id $ decodeCek alg ek- cek = if B.length decryptedCek == B.length dummyCek+ cek = if BA.length decryptedCek == BA.length dummyCek then decryptedCek else dummyCek claims <- maybe (left BadCrypto) return $ decryptPayload enc cek iv aad tag payload@@ -115,16 +116,16 @@ _ -> left (BadHeader "Content is not a JWE") -doEncode :: MonadRandom m+doEncode :: (MonadRandom m, ByteArray ba) => JweHeader- -> (ByteString -> EitherT JwtError m ByteString)- -> ByteString+ -> (ScrubbedBytes -> EitherT JwtError m ByteString)+ -> ba -> EitherT JwtError m Jwt doEncode h encryptKey claims = do (cmk, iv) <- lift (generateCmkAndIV e) let Just (AuthTag sig, ct) = encryptPayload e cmk iv aad claims jweKey <- encryptKey cmk- let jwe = B.intercalate "." $ map B64.encode [hdr, jweKey, iv, ct, BA.convert sig]+ let jwe = B.intercalate "." $ map B64.encode [hdr, jweKey, BA.convert iv, BA.convert ct, BA.convert sig] return (Jwt jwe) where e = jweEnc h
jose-jwt.cabal view
@@ -1,5 +1,5 @@ Name: jose-jwt-Version: 0.7.6+Version: 0.7.7 Synopsis: JSON Object Signing and Encryption Library Homepage: http://github.com/tekul/jose-jwt Bug-Reports: http://github.com/tekul/jose-jwt/issues
tests/Tests/JweSpec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-} module Tests.JweSpec where@@ -39,7 +40,7 @@ let a1Header = defJweHdr {jweAlg = RSA_OAEP, jweEnc = A256GCM} it "generates the expected IV and CMK from the RNG" $- withDRG (RNG $ B.append a1cek a1iv)+ withDRG (RNG . BA.convert $ BA.append a1cek a1iv) (generateCmkAndIV A256GCM) @?= ((a1cek, a1iv), RNG "") it "generates the expected RSA-encrypted content key" $@@ -51,7 +52,7 @@ encryptPayload A256GCM a1cek a1iv aad a1Payload @?= Just (AuthTag a1Tag, a1Ciphertext) it "encodes the payload to the expected JWT, leaving the RNG empty" $- withDRG (RNG $ B.concat [a1cek, a1iv, a1oaepSeed])+ withDRG (RNG $ BA.concat [a1cek, a1iv, BA.convert a1oaepSeed]) (Jwe.rsaEncode RSA_OAEP A256GCM a1PubKey a1Payload) @?= (Right (Jwt a1), RNG "") it "decodes the JWT to the expected header and payload" $@@ -70,7 +71,7 @@ it "a truncated CEK returns BadCrypto" $ do let [hdr, _, iv, payload, tag] = BC.split '.' a1- (Right newEk, _) = withDRG blinderRNG (rsaEncrypt a1PubKey RSA_OAEP (B.tail a1cek))+ newEk = encryptCek a1PubKey RSA_OAEP (BA.drop 1 a1cek) withBlinder (Jwe.rsaDecode a1PrivKey (B.intercalate "." [hdr, B64.encode newEk, iv, payload, tag])) @?= Left BadCrypto it "a truncated payload returns BadCrypto" $ do@@ -87,7 +88,7 @@ it "a truncated IV returns BadCrypto" $ do let (fore, aft) = BC.breakSubstring (B64.encode a1iv) a1- newIv = B64.encode (B.tail a1iv)+ newIv = B64.encode (BA.drop 1 a1iv) withBlinder (Jwe.rsaDecode a1PrivKey (B.concat [fore, newIv, aft])) @?= Left BadCrypto @@ -102,7 +103,7 @@ encryptPayload A128CBC_HS256 a2cek a2iv aad a2Payload @?= Just (AuthTag (BA.convert a2Tag), a2Ciphertext) it "encodes the payload to the expected JWT" $- withDRG (RNG $ B.concat [a2cek, a2iv, a2seed])+ withDRG (RNG $ BA.concat [BA.convert a2cek, a2iv, a2seed]) (Jwe.rsaEncode RSA1_5 A128CBC_HS256 a2PubKey a2Payload) @?= (Right (Jwt a2), RNG "") it "decrypts the ciphertext to the correct payload" $@@ -113,7 +114,7 @@ it "a truncated CEK returns BadCrypto" $ do let [hdr, _, iv, payload, tag] = BC.split '.' a2- (Right newEk, _) = withDRG blinderRNG $ rsaEncrypt a2PubKey RSA1_5 (B.tail a2cek)+ newEk = encryptCek a2PubKey RSA1_5 (BA.drop 1 a2cek) withBlinder (Jwe.rsaDecode a2PrivKey (B.intercalate "." [hdr, B64.encode newEk, iv, payload, tag])) @?= Left BadCrypto it "a truncated payload returns BadCrypto" $ do@@ -145,14 +146,17 @@ context "miscellaneous tests" $ do it "Padding byte larger than 16 is rejected" $- unpad "111a" @?= Nothing+ up "111a" @?= Nothing it "Padding byte which doesn't match padding length is rejected" $- unpad "111\t\t\t\t\t\t\t" @?= Nothing+ up "111\t\t\t\t\t\t\t" @?= Nothing it "Padding byte which matches padding length is OK" $- unpad "1111111\t\t\t\t\t\t\t\t\t" @?= Just "1111111"+ up "1111111\t\t\t\t\t\t\t\t\t" @?= Just "1111111" it "Rejects invalid Base64 JWT" $ withBlinder (Jwe.rsaDecode a2PrivKey "=.") @?= Left BadCrypto +up :: BC.ByteString -> Maybe BC.ByteString+up = unpad+ -- verboseQuickCheckWith quickCheckWith stdArgs {maxSuccess=10000} jweRoundTrip jweRoundTrip :: RNG -> JWEAlgs -> [Word8] -> Bool jweRoundTrip g (JWEAlgs a e) msg = encodeDecode == Right (Jwe (defJweHdr {jweAlg = a, jweEnc = e}, bs))@@ -162,6 +166,12 @@ encodeDecode = fst (withDRG blinderRNG (decode jwks Nothing encoded)) Right encoded = unJwt <$> fst (withDRG g (encode jwks (JweEncoding a e) (Claims bs))) +encryptCek kpub a cek =+ let+ (Right newEk, _) = withDRG blinderRNG $ rsaEncrypt kpub a (BA.drop 1 cek)+ in+ newEk :: BC.ByteString+ withBlinder = fst . withDRG blinderRNG -- A decidedly non-random, random number generator which allows specific@@ -190,9 +200,11 @@ a1Payload = "The true sign of intelligence is not knowledge but imagination." -a1cek = B.pack [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252]+a1cek :: BA.ScrubbedBytes+a1cek = BA.pack [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154, 212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122, 234, 64, 252] -a1iv = B.pack [227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219]+a1iv :: BA.ScrubbedBytes+a1iv = BA.pack [227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219] a1aad = B.pack [101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69, 116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73, 54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81] @@ -221,7 +233,8 @@ a2Payload = "Live long and prosper." -a2cek = B.pack [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207]+a2cek :: BA.ScrubbedBytes+a2cek = BA.pack [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207] --a2cek = B.pack [203, 165, 180, 113, 62, 195, 22, 98, 91, 153, 210, 38, 112, 35, 230, 236]