packages feed

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 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]