packages feed

jose 0.8.2.1 → 0.8.3

raw patch · 6 files changed

+138/−34 lines, 6 filesdep +pemdep ~aesondep ~basedep ~bytestring

Dependencies added: pem

Dependency ranges changed: aeson, base, bytestring, lens, mtl, semigroups, text

Files

jose.cabal view
@@ -1,5 +1,6 @@+cabal-version:       2.2 name:                jose-version:             0.8.2.1+version:             0.8.3 synopsis:   Javascript Object Signing and Encryption and JSON Web Token library description:@@ -29,7 +30,6 @@ copyright:           Copyright (C) 2013-2018  Fraser Tweedale category:            Cryptography build-type:          Simple-cabal-version:       >= 1.8 tested-with:   GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1 @@ -37,7 +37,22 @@   description: Build demonstration programs   default: False +common common+  default-language: Haskell2010+  ghc-options:    -Wall++  build-depends:+    base >= 4.8 && < 5+    , aeson >= 0.8.0.1+    , bytestring == 0.10.*+    , lens >= 4.16+    , mtl >= 2+    , semigroups >= 0.15+    , text >= 1.1+ library+  import: common+   exposed-modules:     Crypto.JOSE     Crypto.JOSE.Compact@@ -61,23 +76,16 @@     Crypto.JOSE.Types.Orphans    build-depends:-    base >= 4.8 && < 5-    , attoparsec+    attoparsec     , base64-bytestring >= 1.0 && < 1.2     , concise >= 0.1     , containers >= 0.5     , cryptonite >= 0.7-    , lens >= 4.16     , memory >= 0.7     , monad-time >= 0.1-    , mtl >= 2-    , semigroups >= 0.15     , template-haskell >= 2.4     , safe >= 0.3-    , aeson >= 0.8.0.1     , unordered-containers == 0.2.*-    , bytestring == 0.10.*-    , text >= 1.1     , time >= 1.5     , network-uri >= 2.6     , QuickCheck >= 2@@ -88,7 +96,6 @@   if impl(ghc<8)     build-depends: fail -  ghc-options:    -Wall   hs-source-dirs: src  source-repository head@@ -96,11 +103,13 @@   location: https://github.com/frasertweedale/hs-jose.git  test-suite tests+  import: common   type:           exitcode-stdio-1.0   hs-source-dirs: test   main-is:        Test.hs   other-modules:     AESKW+    Examples     JWK     JWS     JWT@@ -108,26 +117,20 @@     Types    build-depends:-    base-    , attoparsec+    attoparsec     , base64-bytestring     , containers     , cryptonite-    , lens     , memory     , monad-time-    , mtl-    , semigroups     , template-haskell     , safe-    , aeson     , unordered-containers-    , bytestring-    , text     , time     , network-uri     , vector     , x509+    , pem      , concise     , jose@@ -140,22 +143,15 @@     , quickcheck-instances  executable jose-example+  import: common   if !flag(demos)     buildable: False   hs-source-dirs: example-  ghc-options:    -Wall   main-is:  Main.hs   other-modules:     KeyDB     JWS    build-depends:-    base-    , aeson-    , bytestring-    , lens-    , mtl-    , semigroups-    , text-    , unix+    unix     , jose
src/Crypto/JOSE/JWA/JWK.hs view
@@ -37,6 +37,7 @@   , curve   , point   , ecPrivateKey+  , ecParametersFromX509    -- * Parameters for RSA Keys   , RSAPrivateKeyOthElem(..)@@ -100,6 +101,8 @@ import qualified Data.HashMap.Strict as M import Data.List.NonEmpty (NonEmpty) import qualified Data.Text as T+import Data.X509 as X509+import Data.X509.EC as X509.EC import Test.QuickCheck (Arbitrary(..), arbitrarySizedNatural, elements, oneof, vectorOf)  import Crypto.JOSE.Error@@ -296,11 +299,21 @@     $ B.splitAt (B.length s `div` 2) s  curve :: Crv -> ECC.Curve-curve = ECC.getCurveByName . curveName where-  curveName P_256 = ECC.SEC_p256r1-  curveName P_384 = ECC.SEC_p384r1-  curveName P_521 = ECC.SEC_p521r1+curve = ECC.getCurveByName . review fromCurveName +-- | Conversion from known curves and back again.+fromCurveName :: Prism' ECC.CurveName Crv+fromCurveName = prism'+  (\case+    P_256 -> ECC.SEC_p256r1+    P_384 -> ECC.SEC_p384r1+    P_521 -> ECC.SEC_p521r1)+  (\case+    ECC.SEC_p256r1 -> Just P_256+    ECC.SEC_p384r1 -> Just P_384+    ECC.SEC_p521r1 -> Just P_521+    _              -> Nothing)+ point :: ECKeyParameters -> ECC.Point point k = ECC.Point (f ecX) (f ecY) where   f l = case view l k of@@ -315,6 +328,17 @@ ecPrivateKey (ECKeyParameters _ _ _ (Just (Types.SizedBase64Integer _ d))) = pure d ecPrivateKey _ = throwing _KeyMismatch "Not an EC private key" +ecParametersFromX509 :: X509.PubKeyEC -> Maybe ECKeyParameters+ecParametersFromX509 pubKeyEC = do+  ecCurve <- X509.EC.ecPubKeyCurve pubKeyEC+  curveName <- X509.EC.ecPubKeyCurveName pubKeyEC+  crv <- preview fromCurveName curveName+  pt <- X509.EC.unserializePoint ecCurve (X509.pubkeyEC_pub pubKeyEC)+  (x, y) <- case pt of+    ECC.PointO    -> Nothing+    ECC.Point x y ->+      pure (Types.makeSizedBase64Integer x, Types.makeSizedBase64Integer y)+  pure $ ECKeyParameters crv x y Nothing  -- | Parameters for RSA Keys --
src/Crypto/JOSE/JWK.hs view
@@ -255,6 +255,12 @@ fromRSAPublic :: RSA.PublicKey -> JWK fromRSAPublic = fromKeyMaterial . RSAKeyMaterial . toRSAPublicKeyParameters +-- | Convert an EC public key into a JWK+--+fromECPublic :: X509.PubKeyEC -> Maybe JWK+fromECPublic = fmap (fromKeyMaterial . ECKeyMaterial) . ecParametersFromX509++ -- | Convert octet string into a JWK -- fromOctets :: Cons s s Word8 Word8 => s -> JWK@@ -281,7 +287,8 @@ fromX509CertificateMaybe cert = do   k <- case (X509.certPubKey . X509.signedObject . X509.getSigned) cert of     X509.PubKeyRSA k -> pure (fromRSAPublic k)-    _ -> {- TODO EC -} Nothing+    X509.PubKeyEC k -> fromECPublic k+    _ -> Nothing   pure $ k & set jwkX5cRaw (Just (pure cert))  
src/Crypto/JOSE/Types.hs view
@@ -25,6 +25,7 @@     Base64Integer(..)   , _Base64Integer   , SizedBase64Integer(..)+  , makeSizedBase64Integer   , genSizedBase64IntegerOf   , checkSize   , Base64Octets(..)@@ -127,6 +128,10 @@ genSizedBase64IntegerOf :: Int -> Gen SizedBase64Integer genSizedBase64IntegerOf n =   SizedBase64Integer n . bsToInteger <$> genByteStringOf n++-- | Create a 'SizedBase64Integer'' from an 'Integer'.+makeSizedBase64Integer :: Integer -> SizedBase64Integer+makeSizedBase64Integer x = SizedBase64Integer (intBytes x) x  instance FromJSON SizedBase64Integer where   parseJSON = withText "full size base64url integer" $ parseB64Url (\bytes ->
+ test/Examples.hs view
@@ -0,0 +1,70 @@+-- | Miscellaneous end-to-end examples.+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+module Examples where++import Control.Lens (_Right, (&), (?~), (.~))+import Control.Lens.Extras (is)+import qualified Data.ByteString.Char8 as BC8+import qualified Data.PEM as PEM+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified Data.X509 as X509++import Crypto.JOSE.JWK+import Crypto.JWT++import Test.Hspec++spec :: Spec+spec = do+  fromX509VerifySpec++fromX509VerifySpec :: Spec+fromX509VerifySpec =+  describe "Decode PEM-encoded X509 EC certificate and verify" $ do+    let certs = do+          pems <- PEM.pemParseBS pemEncodedX509+          mapM (X509.decodeSignedCertificate . PEM.pemContent) pems++    it "successfully parses the single X509 certificate" $ do+      certs `shouldSatisfy` \case Right [_] -> True; _ -> False++    case certs of+      Right [x509] -> do+        let errorOrJWK = fromX509Certificate x509 :: Either JWTError JWK+        it "successfully converts the X509 certificate to a JWK" $+          errorOrJWK `shouldSatisfy` is _Right++        it "verifies a token signed using ES256 and matches expected claims" $+          (do+            es256jwk <- errorOrJWK+            jwt <- decodeCompact es256token+            verifyClaimsAt valSettings es256jwk now jwt) `shouldBe`+          Right expectedClaims++      _ -> pure ()+  where++  es256token = "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJuYmYiOiAxNDQ0NDc4NDAwLCAiaXNzIjogInh4eCJ9.lArczfN-pIL8oUU-7PU83u-zfXougXBZj6drFeKFsPEoVhy9WAyiZlRshYqjTSXdaw8yw2L-ovt4zTUZb2PWMg"++  pemEncodedX509 = BC8.unlines+    [ "-----BEGIN CERTIFICATE-----"+    , "MIIBcDCCARagAwIBAgIJAMZmuGSIfvgzMAoGCCqGSM49BAMCMBMxETAPBgNVBAMM"+    , "CHdoYXRldmVyMB4XDTE4MDgxMDE0Mjg1NFoXDTE4MDkwOTE0Mjg1NFowEzERMA8G"+    , "A1UEAwwId2hhdGV2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATPwn3WCEXL"+    , "mjp/bFniDwuwsfu7bASlPae2PyWhqGeWwe23Xlyx+tSqxlkXYe4pZ23BkAAscpGj"+    , "yn5gXHExyDlKo1MwUTAdBgNVHQ4EFgQUElRjSoVgKjUqY5AXz2o74cLzzS8wHwYD"+    , "VR0jBBgwFoAUElRjSoVgKjUqY5AXz2o74cLzzS8wDwYDVR0TAQH/BAUwAwEB/zAK"+    , "BggqhkjOPQQDAgNIADBFAiEA4yQ/88ZrUX68c6kOe9G11u8NUaUzd8pLOtkKhniN"+    , "OHoCIHmNX37JOqTcTzGn2u9+c8NlnvZ0uDvsd1BmKPaUmjmm"+    , "-----END CERTIFICATE-----"+    ]++  now = posixSecondsToUTCTime 1588512349++  expectedClaims = emptyClaimsSet+    & claimIss ?~ "xxx"+    & claimNbf ?~ NumericDate (posixSecondsToUTCTime 1444478400)++  valSettings = defaultJWTValidationSettings (const True)+    & jwtValidationSettingsIssuerPredicate .~ (== "xxx")
test/Test.hs view
@@ -17,6 +17,7 @@ import Test.Tasty.QuickCheck  import AESKW+import Examples import JWK import JWS import JWT@@ -35,4 +36,5 @@   jwk <- testSpec "JWK" JWK.spec   jws <- testSpec "JWS" JWS.spec   jwt <- testSpec "JWT" JWT.spec-  return $ testGroup "Unit tests" [types, jwk, jws, jwt]+  examples <- testSpec "Examples" Examples.spec+  return $ testGroup "Unit tests" [types, jwk, jws, jwt, examples]