packages feed

jwt 0.8.0 → 0.9.0

raw patch · 3 files changed

+58/−57 lines, 3 filesdep +x509dep +x509-storedep −HsOpenSSLdep −RSAdep ~basenew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: x509, x509-store

Dependencies removed: HsOpenSSL, RSA

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Web.JWT: instance GHC.Base.Semigroup Web.JWT.ClaimsMap
+ Web.JWT: instance GHC.Base.Semigroup Web.JWT.JOSEHeader
+ Web.JWT: instance GHC.Base.Semigroup Web.JWT.JWTClaimsSet
+ Web.JWT: readRsaSecret :: ByteString -> Maybe PrivateKey

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+# 2018-01-04 0.9.0++* Switch from RSA and HsOpenSSL to x509-store+* Add Semigroup instances for GHC 8.6 compatibility+ # 2018-03-21 0.8.0  * Support RS256 algorithm
jwt.cabal view
@@ -2,12 +2,12 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                jwt-version:             0.8.0+version:             0.9.0 synopsis:            JSON Web Token (JWT) decoding and encoding license:             MIT license-file:        LICENSE-author:              Stefan Saasen-maintainer:          stefan@saasen.me+author:              Brian McKenna+maintainer:          brian@brianmckenna.org homepage:            https://bitbucket.org/ssaasen/haskell-jwt bug-reports:         https://bitbucket.org/ssaasen/haskell-jwt/issues category:            Web@@ -46,8 +46,8 @@                      , vector                   >= 0.7.1                      , semigroups               >= 0.15.4                      , network-uri-                     , RSA-                     , HsOpenSSL+                     , x509+                     , x509-store    hs-source-dirs:      src   default-language:    Haskell2010@@ -68,6 +68,7 @@                      , Web.JWTTestsCompat                      , Data.Text.Extended                      , Data.Text.ExtendedTests+                     , Data.ByteString.Extended                      , Data.ByteString.ExtendedTests   hs-source-dirs:      tests/src, src   build-depends:       base < 5 && >= 4.4@@ -92,8 +93,8 @@                      , vector                   >= 0.7.1                      , semigroups               >= 0.15.4                      , network-uri-                     , RSA-                     , HsOpenSSL+                     , x509+                     , x509-store    cpp-options: -DTEST 
src/Web/JWT.hs view
@@ -40,7 +40,7 @@     -- ** Common     , tokenIssuer     , hmacSecret-    , rsaKeySecret+    , readRsaSecret     -- ** JWT structure     , claims     , header@@ -72,18 +72,23 @@     , StringOrURI     , JWTHeader     , JOSEHeader++    -- * Deprecated+    , rsaKeySecret     ) where +import qualified Data.ByteString.Char8      as C8 import qualified Data.ByteString.Lazy.Char8 as BL (fromStrict, toStrict) import qualified Data.ByteString.Extended as BS import qualified Data.Text.Extended         as T import qualified Data.Text.Encoding         as TE -import           Codec.Crypto.RSA           (PrivateKey(..), PublicKey(..), sign) import           Control.Applicative import           Control.Monad import           Crypto.Hash.Algorithms import           Crypto.MAC.HMAC+import           Crypto.PubKey.RSA          (PrivateKey)+import           Crypto.PubKey.RSA.PKCS15   (sign) import           Data.ByteArray.Encoding import           Data.Aeson                 hiding (decode, encode) import qualified Data.Aeson                 as JSON@@ -91,24 +96,11 @@ import qualified Data.Map                   as Map import           Data.Maybe import           Data.Scientific+import qualified Data.Semigroup             as Semigroup import           Data.Time.Clock            (NominalDiffTime)+import           Data.X509                  (PrivKey (PrivKeyRSA))+import           Data.X509.Memory           (readKeyFileFromMemory) import qualified Network.URI                as URI-import           OpenSSL.EVP.PKey           (toKeyPair)-import           OpenSSL.PEM                (PemPasswordSupply(..), readPrivateKey)-import           OpenSSL.RSA-    ( RSAKeyPair-    , RSAPubKey-    , rsaCopyPublic-    , rsaD-    , rsaDMP1-    , rsaDMQ1-    , rsaE-    , rsaIQMP-    , rsaN-    , rsaP-    , rsaQ-    , rsaSize-    ) import           Prelude                    hiding (exp)  -- $setup@@ -209,9 +201,12 @@ instance Monoid JOSEHeader where     mempty =       JOSEHeader Nothing Nothing Nothing-    mappend (JOSEHeader a b c) (JOSEHeader a' b' c') =-      JOSEHeader (a <|> a') (b <|> b') (c <|> c')+    mappend = (Semigroup.<>) +instance Semigroup.Semigroup JOSEHeader where+  JOSEHeader a b c <> JOSEHeader a' b' c' =+    JOSEHeader (a <|> a') (b <|> b') (c <|> c')+ -- | The JWT Claims Set represents a JSON object whose members are the claims conveyed by the JWT. data JWTClaimsSet = JWTClaimsSet {     -- Registered Claim Names@@ -245,9 +240,12 @@ instance Monoid JWTClaimsSet where   mempty =     JWTClaimsSet Nothing Nothing Nothing Nothing Nothing Nothing Nothing $ ClaimsMap Map.empty-  mappend (JWTClaimsSet a b c d e f g h) (JWTClaimsSet a' b' c' d' e' f' g' h') =-    JWTClaimsSet (a <|> a') (b <|> b') (c <|> c') (d <|> d') (e <|> e') (f <|> f') (g <|> g') (mappend h h')+  mappend = (Semigroup.<>) +instance Semigroup.Semigroup JWTClaimsSet where+  JWTClaimsSet a b c d e f g h <> JWTClaimsSet a' b' c' d' e' f' g' h' =+    JWTClaimsSet (a <|> a') (b <|> b') (c <|> c') (d <|> d') (e <|> e') (f <|> f') (g <|> g') (h Semigroup.<> h')+ -- | Encode a claims set using the given secret -- --  @@@ -378,11 +376,17 @@  -- | Create an RSAPrivateKey from PEM contents ----- > rsaKeySecret =<< readFile "foo.pem"+-- Please, consider using 'readRsaSecret' instead.+rsaKeySecret :: String -> IO (Maybe Signer)+rsaKeySecret = pure . fmap RSAPrivateKey . readRsaSecret . C8.pack++-- | Create an RSA 'PrivateKey' from PEM contents --+-- > readRsaSecret <$> BS.readFile "foo.pem"+-- -- >>> :{ --   -- A random example key created with `ssh-keygen -t rsa`---   fmap (\(Just (RSAPrivateKey pk)) -> pk) $ rsaKeySecret $ unlines+--   fromJust . readRsaSecret . C8.pack $ unlines --       [ "-----BEGIN RSA PRIVATE KEY-----" --       , "MIIEowIBAAKCAQEAkkmgbLluo5HommstpHr1h53uWfuN3CwYYYR6I6a2MzAHIMIv" --       , "8Ak2ha+N2UDeYsfVhZ/DOnE+PMm2RpYSaiYT0l2a7ZkmRSbcyvVFt3XLePJbmUgo"@@ -414,27 +418,11 @@ -- :} -- PrivateKey {private_pub = PublicKey {public_size = 256, public_n = 1846..., public_e = 65537}, private_d = 8823..., private_p = 135..., private_q = 1358..., private_dP = 1373..., private_dQ = 9100..., private_qinv = 8859...} ---rsaKeySecret :: String -> IO (Maybe Signer)-rsaKeySecret k = do-    mKeyPair <- toKeyPair <$> readPrivateKey k PwNone-    mPublicKey <- mapM rsaCopyPublic mKeyPair-    return $ RSAPrivateKey <$>-        (fromRSAKey <$> mKeyPair <*> mPublicKey)-  where-    fromRSAKey :: RSAKeyPair -> RSAPubKey -> PrivateKey-    fromRSAKey kp pk = PrivateKey-        { private_pub = PublicKey-            { public_size = rsaSize pk-            , public_n = rsaN pk-            , public_e = rsaE pk-            }-        , private_d = rsaD kp-        , private_p = rsaP kp-        , private_q = rsaQ kp-        , private_dP = fromMaybe 0 $ rsaDMP1 kp-        , private_dQ = fromMaybe 0 $ rsaDMQ1 kp-        , private_qinv = fromMaybe 0 $ rsaIQMP kp-        }+readRsaSecret :: BS.ByteString -> (Maybe PrivateKey)+readRsaSecret bs =+    case readKeyFileFromMemory bs of+        [(PrivKeyRSA k)] -> Just k+        _                -> Nothing  -- | Convert the `NominalDiffTime` into an IntDate. Returns a Nothing if the -- argument is invalid (e.g. the NominalDiffTime must be convertible into a@@ -494,10 +482,14 @@  calculateDigest (RSAPrivateKey key) msg = TE.decodeUtf8     $ convertToBase Base64URLUnpadded-    $ BL.toStrict-    $ sign key-    $ BL.fromStrict+    $ sign'     $ TE.encodeUtf8 msg+  where+    sign' :: BS.ByteString -> BS.ByteString+    sign' bs = case sign Nothing (Just SHA256) key bs of+        Right sig -> sig+        Left  _   -> error "impossible"  -- This function can only fail with @SignatureTooLong@,+                                         -- which is impossible because we use a hash.  -- ================================================================================= @@ -507,8 +499,11 @@ instance Monoid ClaimsMap where   mempty =     ClaimsMap mempty-  mappend (ClaimsMap a) (ClaimsMap b) =-    ClaimsMap $ mappend a b+  mappend = (Semigroup.<>)++instance Semigroup.Semigroup ClaimsMap where+  ClaimsMap a <> ClaimsMap b =+    ClaimsMap $ a Semigroup.<> b  fromHashMap :: Object -> ClaimsMap fromHashMap = ClaimsMap . Map.fromList . StrictMap.toList