packages feed

jose-jwt 0.9.1 → 0.9.2

raw patch · 13 files changed

+64/−43 lines, 13 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+0.9.2+-----++* Add `UnsupportedJwt` constructor to `Jwk` type which wraps the JSON value. This prevents failure loading keys which have `alg` or `kty` values which we don't support (see #31).+ 0.9.1 ----- 
Jose/Internal/Crypto.hs view
@@ -26,7 +26,6 @@ where  -import           Control.Applicative import           Control.Monad (when, unless) import           Crypto.Error import           Crypto.Cipher.AES
Jose/Internal/Parser.hs view
@@ -16,7 +16,6 @@     ) where -import           Control.Applicative import           Data.Bifunctor (first) import Data.Aeson (eitherDecodeStrict') import           Data.Attoparsec.ByteString (Parser)
Jose/Jwa.hs view
@@ -10,7 +10,6 @@     ) where -import Control.Applicative (pure) import Data.Aeson import Data.Text (Text) import Data.Tuple (swap)
Jose/Jwe.hs view
@@ -13,7 +13,8 @@ -- RSA anyone can send you a JWE if they have a copy of your public key. -- -- In the example below, we show encoding and decoding using a 2048 bit RSA key pair--- (256 bytes).+-- (256 bytes). If using RSA, use one of the @RSA_OAEP@ algorithms. @RSA1_5@ is+-- deprecated due to <https://robotattack.org/ known vulnerabilities>. -- -- >>> import Jose.Jwe -- >>> import Jose.Jwa@@ -106,7 +107,8 @@         e <- doDecode (rsaDecrypt (Just blinder) kPr) jwt         return (Jwe e)     SymmetricJwk kb   _ _ _ -> fmap Jwe (doDecode (keyUnwrap (BA.convert kb)) jwt)-    _                       -> throwE $ KeyError "JWK cannot decode a JWE"+    UnsupportedJwk _ -> throwE (KeyError "Unsupported JWK cannot be used to decode JWE")+    _ -> throwE $ KeyError "This JWK cannot decode a JWE"   doDecode :: MonadRandom m
Jose/Jwk.hs view
@@ -20,7 +20,6 @@     ) where -import           Control.Applicative (pure) import           Control.Monad (unless) import           Crypto.Error (CryptoFailable(..)) import           Crypto.Random (MonadRandom, getRandomBytes)@@ -30,12 +29,13 @@ import qualified Crypto.PubKey.Ed448 as Ed448 import qualified Crypto.PubKey.ECC.Types as ECC import           Crypto.Number.Serialize-import           Data.Aeson (genericToJSON, Value(..), FromJSON(..), ToJSON(..), withText)+import           Data.Aeson (fromJSON, genericToJSON, Object, Result(..), Value(..), FromJSON(..), ToJSON(..), withText) import           Data.Aeson.Types (Parser, Options (..), defaultOptions) import qualified Data.ByteArray as BA import           Data.ByteString (ByteString) import qualified Data.ByteString as B-import           Data.Maybe (isNothing)+import qualified Data.HashMap.Strict as H+import           Data.Maybe (isNothing, fromMaybe) import           Data.Text (Text) import qualified Data.Text.Encoding as TE import           GHC.Generics (Generic)@@ -68,9 +68,10 @@          | Ed448PrivateJwk !Ed448.SecretKey !Ed448.PublicKey !(Maybe KeyId)          | Ed448PublicJwk !Ed448.PublicKey !(Maybe KeyId)          | SymmetricJwk  !ByteString      !(Maybe KeyId) !(Maybe KeyUse) !(Maybe Alg)+         | UnsupportedJwk Object            deriving (Show, Eq) -data JwkSet = JwkSet+newtype JwkSet = JwkSet     { keys :: [Jwk]     } deriving (Show, Eq, Generic) @@ -208,30 +209,33 @@     EcPublicJwk   _ keyId _ _ _ -> keyId     EcPrivateJwk  _ keyId _ _ _ -> keyId     SymmetricJwk  _ keyId _ _ -> keyId+    UnsupportedJwk _ -> Nothing  jwkUse :: Jwk -> Maybe KeyUse jwkUse key = case key of-    Ed25519PrivateJwk _ _ _ -> Just Sig+    Ed25519PrivateJwk {} -> Just Sig     Ed25519PublicJwk _ _ -> Just Sig-    Ed448PrivateJwk _ _ _ -> Just Sig+    Ed448PrivateJwk {} -> Just Sig     Ed448PublicJwk _ _ -> Just Sig     RsaPublicJwk  _ _ u _ -> u     RsaPrivateJwk _ _ u _ -> u     EcPublicJwk   _ _ u _ _ -> u     EcPrivateJwk  _ _ u _ _ -> u     SymmetricJwk  _ _ u _ -> u+    UnsupportedJwk _ -> Nothing  jwkAlg :: Jwk -> Maybe Alg jwkAlg key = case key of-    Ed25519PrivateJwk _ _ _ -> Just (Signed EdDSA)+    Ed25519PrivateJwk {} -> Just (Signed EdDSA)     Ed25519PublicJwk _ _ -> Just (Signed EdDSA)-    Ed448PrivateJwk _ _ _ -> Just (Signed EdDSA)+    Ed448PrivateJwk {} -> Just (Signed EdDSA)     Ed448PublicJwk _ _ -> Just (Signed EdDSA)     RsaPublicJwk  _ _ _ a -> a     RsaPrivateJwk _ _ _ a -> a     EcPublicJwk   _ _ _ a _ -> a     EcPrivateJwk  _ _ _ a _ -> a     SymmetricJwk  _ _ _ a -> a+    UnsupportedJwk _ -> Nothing   newtype JwkBytes = JwkBytes {bytes :: ByteString} deriving (Show)@@ -288,20 +292,33 @@     toJSON (JwkBytes b) = String . TE.decodeUtf8 $ B64.encode b  instance FromJSON Jwk where-    parseJSON o@(Object _) = do-        jwkData <- parseJSON o :: Parser JwkData-        case createJwk jwkData of-            Left  err -> fail err-            Right jwk -> return jwk+    parseJSON (Object k) = parseJwk k     parseJSON _            = fail "Jwk must be a JSON object" +parseJwk :: Object -> Parser Jwk+parseJwk k =+    case (checkAlg, checkKty) of+        (Success _, Success _) -> do+            jwkData <- parseJSON (Object k) :: Parser JwkData+            case createJwk jwkData of+                Left  err -> fail err+                Right jwk -> return jwk+        _ -> pure (UnsupportedJwk k)+  where+    algValue = fromMaybe Null (H.lookup "alg" k)+    -- kty is required so if it's missing here we do nothing and allow decoding to fail+    -- later+    ktyValue = fromMaybe Null (H.lookup "kty" k)+    checkAlg = fromJSON algValue :: Result (Maybe Alg)+    checkKty = fromJSON ktyValue :: Result (Maybe KeyType)+ instance ToJSON Jwk where-    toJSON jwk = toJSON $ case jwk of+    toJSON jwk = case jwk of         RsaPublicJwk pubKey mId mUse mAlg ->-          createPubData pubKey mId mUse mAlg+          toJSON $ createPubData pubKey mId mUse mAlg         RsaPrivateJwk privKey mId mUse mAlg ->             let pubData = createPubData (RSA.private_pub privKey) mId mUse mAlg-            in  pubData+            in  toJSON $ pubData                 { d  = Just . JwkBytes . i2osp $ RSA.private_d privKey                 , p  = i2b $ RSA.private_p    privKey                 , q  = i2b $ RSA.private_q    privKey@@ -310,7 +327,7 @@                 , qi = i2b $ RSA.private_qinv privKey                 } -        Ed25519PrivateJwk kPr kPub kid_ -> defJwk+        Ed25519PrivateJwk kPr kPub kid_ -> toJSON $ defJwk             { kty = Okp             , crv = Just "Ed25519"             , d = Just (JwkBytes (BA.convert kPr))@@ -318,30 +335,30 @@             , kid = kid_             } -        Ed25519PublicJwk kPub kid_ -> defJwk+        Ed25519PublicJwk kPub kid_ -> toJSON $ defJwk             { kty = Okp             , crv = Just "Ed25519"             , x = Just (JwkBytes (BA.convert kPub))             , kid = kid_             } -        Ed448PrivateJwk kPr kPub kid_ -> defJwk+        Ed448PrivateJwk kPr kPub kid_ -> toJSON $ defJwk             { kty = Okp-            , crv = Just "Ed25519"+            , crv = Just "Ed448"             , d = Just (JwkBytes (BA.convert kPr))             , x = Just (JwkBytes (BA.convert kPub))             , kid = kid_             } -        Ed448PublicJwk kPub kid_ -> defJwk+        Ed448PublicJwk kPub kid_ -> toJSON $ defJwk             { kty = Okp-            , crv = Just "Ed25519"+            , crv = Just "Ed448"             , x = Just (JwkBytes (BA.convert kPub))             , kid = kid_             }  -        SymmetricJwk bs mId mUse mAlg -> defJwk+        SymmetricJwk bs mId mUse mAlg -> toJSON $ defJwk             { kty = Oct             , k   = Just $ JwkBytes bs             , kid = mId@@ -349,7 +366,7 @@             , alg = mAlg             } -        EcPublicJwk pubKey mId mUse mAlg c -> defJwk+        EcPublicJwk pubKey mId mUse mAlg c -> toJSON $ defJwk             { kty = Ec             , x   = fst (ecPoint pubKey)             , y   = snd (ecPoint pubKey)@@ -359,7 +376,7 @@             , crv = Just (ecCurveName c)             } -        EcPrivateJwk kp mId mUse mAlg c -> defJwk+        EcPrivateJwk kp mId mUse mAlg c -> toJSON $ defJwk             { kty = Ec             , x   = fst (ecPoint (ECDSA.toPublicKey kp))             , y   = snd (ecPoint (ECDSA.toPublicKey kp))@@ -369,6 +386,8 @@             , alg = mAlg             , crv = Just (ecCurveName c)             }++        UnsupportedJwk k -> Object k       where         i2b 0 = Nothing         i2b i = Just . JwkBytes . i2osp $ i@@ -503,7 +522,7 @@     rsaPub nb eb  = let m  = os2ip $ bytes nb                         ex = os2ip $ bytes eb                     in RSA.PublicKey (rsaSize m 1) m ex-    rsaSize m i   = if (2 ^ (i * 8)) > m then i else rsaSize m (i+1)+    rsaSize m i   = if 2 ^ (i * 8) > m then i else rsaSize m (i+1)     ecPoint       = do         xb <- note "x is required for an EC key" x         yb <- note "y is required for an EC key" y
Jose/Jws.hs view
@@ -28,7 +28,6 @@     ) where -import Control.Applicative import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.Ed25519 as Ed25519 import qualified Crypto.PubKey.Ed448 as Ed448
Jose/Jwt.hs view
@@ -118,6 +118,7 @@         EcPublicJwk   kPub _ _ _ _ -> Jws.ecDecode kPub jwt         EcPrivateJwk  kPr  _ _ _ _ -> Jws.ecDecode (ECDSA.toPublicKey kPr) jwt         SymmetricJwk  kb   _ _ _ -> Jws.hmacDecode kb jwt+        UnsupportedJwk _ -> Left (KeyError "Unsupported JWKs cannot be used")      decodeWithJwe :: MonadRandom m => Jwk -> ExceptT JwtError m (Maybe JwtContent)     decodeWithJwe k = fmap (either (const Nothing) Just) (lift (Jwe.jwkDecode k jwt))
Jose/Types.hs view
@@ -22,9 +22,7 @@     ) where -import Control.Applicative import Data.Aeson-import Data.Aeson.Types import Data.Char (toUpper, toLower) import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as BL
jose-jwt.cabal view
@@ -1,5 +1,5 @@ Name:               jose-jwt-Version:            0.9.1+Version:            0.9.2 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
@@ -4,13 +4,11 @@  module Tests.JweSpec where -import Control.Applicative-import Data.Aeson (decodeStrict', ToJSON)+import Data.Aeson (decodeStrict') import Data.Bits (xor) import Data.Word (Word8, Word64) import qualified Data.ByteArray as BA import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Char8 as BC import Test.Hspec import Test.HUnit hiding (Test)@@ -68,7 +66,7 @@       it "decodes the JWT using the JWK" $ do         let Just k1 = decodeStrict' a1jwk             Just k2 = decodeStrict' a2jwk-        withBlinder (decode [k2, k1] (Just $ JweEncoding RSA_OAEP A256GCM) a1) @?= (Right $ Jwe (a1Header, a1Payload))+        withBlinder (decode [k2, k1] (Just $ JweEncoding RSA_OAEP A256GCM) a1) @?= Right (Jwe (a1Header, a1Payload))        it "a truncated CEK returns BadCrypto" $ do         let [hdr, _, iv, payload, tag] = BC.split '.' a1@@ -136,7 +134,7 @@             (Jwe.jwkEncode A128KW A128CBC_HS256 jwk (Claims a3Payload)) @?= (Right (Jwt a3), RNG "")        it "decodes the JWT using the JWK" $-        withBlinder (decode [jwk] Nothing a3) @?= (Right $ Jwe (a3Header, a3Payload))+        withBlinder (decode [jwk] Nothing a3) @?= Right (Jwe (a3Header, a3Payload))      context "when used with quickcheck" $ do       it "padded msg is always a multiple of 16" $ property $@@ -297,7 +295,7 @@     return $ JWEAlgs a e  instance Arbitrary RNG where-  arbitrary = (RNG . B.pack) <$> vector 600+  arbitrary = RNG . B.pack <$> vector 600   --------------------------------------------------------------------------------
tests/Tests/JwkSpec.hs view
@@ -18,7 +18,6 @@ import Jose.Jwk import Jose.Jwa --- TODO: Support for  {"kty": "oct", "alg": "A128KW", "k":"GawgguFyGrWKav7AX4VKUg"} spec :: Spec spec = do     jwkFile <- runIO (B.readFile "tests/jwks.json")@@ -45,7 +44,7 @@                 Ed25519PrivateJwk _ _ key9Id = kss !! 9                 Ed25519PublicJwk _ key10Id = kss !! 10                 Success utcKeyId = fromJSON (String "2015-05-16T18:00:14.259Z")-            length kss @?= 11+            length kss @?= 14             a0      @?= Nothing             key0Id  @?= Just (KeyId "a0")             key1Id  @?= Just (KeyId "a1")
tests/jwks.json view
@@ -10,5 +10,8 @@     , {"kty":"RSA", "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e":"AQAB", "d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", "p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", "q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", "dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", "dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", "qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", "alg":"RS256", "kid":"2015-05-16T18:00:14.259Z"}       , {"kty":"OKP","crv":"Ed25519", "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "kid": "rfc8037SecretKey"}       , {"kty":"OKP","crv":"Ed25519", "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "kid": "rfc8037PublicKey"}+      , {"kty": "oct", "alg": "A128KW", "k":"GawgguFyGrWKav7AX4VKUg"}+      , {"kty":"EC","use":"enc","crv":"P-256","kid":"jwe-encryption-key-from-pr-31","key_ops":["encrypt"],"x":"G8CZn6Zo7UqKLhpDFOh9njuDwBRn2xawrvf65c_t1UM","y":"L3tlkG4tQhNitkJp-5XxvqnbgBGjJFpFr-WEyIUbEvA","alg":"ECDH-ES" }+      , {"kty": "hypothetical-new-kty", "alg": "A256KW", "k":"GawgguFyGrWKav7AX4VKUg"}     ] }