Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code review and feature additions #79

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dist
/dist
/dist-newstyle
.cabal-sandbox
cabal.sandbox.config
.stack-work
Expand Down
57 changes: 33 additions & 24 deletions jose.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,42 @@ library
Crypto.JOSE.JWA.JWS
Crypto.JOSE.JWA.JWE
Crypto.JOSE.JWA.JWE.Alg
Crypto.JOSE.Types.WrappedURI

other-modules:
Crypto.JOSE.TH
Crypto.JOSE.Types.Internal
Crypto.JOSE.Types.Orphans
Crypto.JOSE.Types.WrappedNonEmpty

build-depends:
base >= 4.8 && < 5
, attoparsec
, base64-bytestring == 1.0.*
, 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
, quickcheck-instances
, x509 >= 1.4
, vector
aeson >= 1.4.2 && < 1.5,
attoparsec >= 0.13.2 && < 0.14,
base >= 4.8 && < 5,
base64-bytestring == 1.0.*,
bytestring == 0.10.*,
concise >= 0.1 && < 0.2,
containers >= 0.5 && < 0.7,
contravariant >= 1.4 && < 1.5,
cryptonite >= 0.7 && < 0.26,
exceptions >= 0.10.0 && < 0.11,
lens >= 4.16 && < 4.18,
mtl >= 2 && < 2.3,
memory >= 0.7 && < 0.15,
modern-uri >= 0.3.0 && < 0.4,
monad-time >= 0.1 && < 0.4,
network-uri >= 2.6 && < 2.7,
QuickCheck >= 2 && < 2.13,
quickcheck-instances >= 0.3 && < 0.4,
safe >= 0.3 && < 0.4,
semigroups >= 0.15 && < 0.19,
semigroupoids >= 5 && < 6,
template-haskell >= 2.4 && < 2.15,
text >= 1.1 && < 1.3,
time >= 1.5 && < 1.10,
unordered-containers == 0.2.*,
vector >= 0.12 && < 0.13,
waargonaut >= 0.5 && < 0.6,
x509 >= 1.4 && < 1.8

ghc-options: -Wall
hs-source-dirs: src
Expand All @@ -103,6 +109,8 @@ test-suite tests
JWT
Properties
Types
WrappedExceptT
ghc-options: -Wall

build-depends:
base
Expand Down Expand Up @@ -135,6 +143,7 @@ test-suite tests
, hspec
, QuickCheck
, quickcheck-instances
, modern-uri

executable jose-example
if !flag(demos)
Expand Down
1 change: 0 additions & 1 deletion src/Crypto/JOSE.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,5 @@ import Crypto.JOSE.Error
import Crypto.JOSE.JWK
import Crypto.JOSE.JWK.Store
import Crypto.JOSE.JWS
import Crypto.JOSE.Types (base64url)

{-# ANN module ("HLint: ignore Use import/export shortcut" :: String) #-}
14 changes: 0 additions & 14 deletions src/Crypto/JOSE/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

{-|

Expand All @@ -38,16 +36,13 @@ module Crypto.JOSE.Error
import Data.Semigroup ((<>))
import Numeric.Natural

import Control.Monad.Trans (MonadTrans(..))
import qualified Crypto.PubKey.RSA as RSA
import Crypto.Error (CryptoError)
import Crypto.Random (MonadRandom(..))
import Control.Lens (Getter, to)
import Control.Lens.TH (makeClassyPrisms, makePrisms)
import qualified Data.Text as T
import qualified Data.Text.Encoding.Error as T


-- | The wrong number of parts were found when decoding a
-- compact JOSE object.
--
Expand Down Expand Up @@ -117,12 +112,3 @@ data Error
-- that matched the allowed algorithms
deriving (Eq, Show)
makeClassyPrisms ''Error


instance (
MonadRandom m
, MonadTrans t
, Functor (t m)
, Monad (t m)
) => MonadRandom (t m) where
getRandomBytes = lift . getRandomBytes
118 changes: 65 additions & 53 deletions src/Crypto/JOSE/Header.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ module Crypto.JOSE.Header
, headerRequired
, headerRequiredProtected
, headerOptional
, headerOptional'
, headerOptionalNonEmpty
, headerOptionalURI
, headerOptionalProtected
, headerOptionalProtected'
, headerOptionalProtectedNonEmpty
, headerOptionalProtectedURI

-- * Parsing headers
, parseParams
Expand All @@ -45,41 +51,25 @@ module Crypto.JOSE.Header
-- * Encoding headers
, protectedParamsEncoded
, unprotectedParams


-- * Header fields shared by JWS and JWE
, HasAlg(..)
, HasJku(..)
, HasJwk(..)
, HasKid(..)
, HasX5u(..)
, HasX5c(..)
, HasX5t(..)
, HasX5tS256(..)
, HasTyp(..)
, HasCty(..)
, HasCrit(..)
) where


import Data.List.NonEmpty (NonEmpty)
import Data.Monoid ((<>))
import Data.Proxy (Proxy(..))

import Control.Lens (Lens', Getter, to)
import Control.Lens (Lens', Getter, to, Getting, view, _Wrapped)
import Data.Aeson (FromJSON(..), Object, Value, encode, object)
import Data.Aeson.Types (Pair, Parser)
import qualified Data.ByteString.Base64.URL.Lazy as B64UL
import qualified Data.ByteString.Lazy as L
import qualified Data.HashMap.Strict as M
import qualified Data.Text as T

import qualified Crypto.JOSE.JWA.JWS as JWA.JWS
import Crypto.JOSE.JWK (JWK)
import Crypto.JOSE.Types.Orphans ()
import Crypto.JOSE.Types.WrappedURI(WrappedURI)
import Crypto.JOSE.Types.WrappedNonEmpty(WrappedNonEmpty)
import Crypto.JOSE.Types.Internal (unpad)
import qualified Crypto.JOSE.Types as Types

import Text.URI(URI)

-- | A thing with parameters.
--
Expand Down Expand Up @@ -235,6 +225,34 @@ headerOptional k hp hu = case (hp >>= M.lookup k, hu >>= M.lookup k) of
getUnprotected
(Nothing, Nothing) -> pure Nothing

headerOptional'
:: (FromJSON a, ProtectionIndicator p)
=> Getting b a b
-> T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe (HeaderParam p b))
headerOptional' j k hp hu =
fmap (fmap (fmap (view j))) (headerOptional k hp hu)

headerOptionalNonEmpty
:: (FromJSON a, ProtectionIndicator p)
=> T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe (HeaderParam p (NonEmpty a)))
headerOptionalNonEmpty =
headerOptional' (_Wrapped :: Getting (NonEmpty a) (WrappedNonEmpty a) (NonEmpty a))

headerOptionalURI
:: ProtectionIndicator p
=> T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe (HeaderParam p URI))
headerOptionalURI =
headerOptional' (_Wrapped :: Getting URI WrappedURI URI)

-- | Parse an optional parameter that, if present, MUST be carried
-- in the protected header.
--
Expand All @@ -250,6 +268,33 @@ headerOptionalProtected k hp hu = case (hp >>= M.lookup k, hu >>= M.lookup k) of
(Just v, _) -> Just <$> parseJSON v
_ -> pure Nothing

headerOptionalProtected'
:: FromJSON a
=> Getting b a b
-> T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe b)
headerOptionalProtected' j k hp hu =
fmap (fmap (view j)) (headerOptionalProtected k hp hu)

headerOptionalProtectedNonEmpty
:: FromJSON a
=> T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe (NonEmpty a))
headerOptionalProtectedNonEmpty =
headerOptionalProtected' (_Wrapped :: Getting (NonEmpty a) (WrappedNonEmpty a) (NonEmpty a))

headerOptionalProtectedURI
:: T.Text
-> Maybe Object
-> Maybe Object
-> Parser (Maybe URI)
headerOptionalProtectedURI =
headerOptionalProtected' (_Wrapped :: Getting URI WrappedURI URI)

-- | Parse a required parameter that may be carried in either
-- the protected or the unprotected header.
--
Expand Down Expand Up @@ -311,36 +356,3 @@ parseCrit
parseCrit reserved exts o = mapM (mapM (critObjectParser reserved exts o))
-- TODO fail on duplicate strings


class HasAlg a where
alg :: Lens' (a p) (HeaderParam p JWA.JWS.Alg)

class HasJku a where
jku :: Lens' (a p) (Maybe (HeaderParam p Types.URI))

class HasJwk a where
jwk :: Lens' (a p) (Maybe (HeaderParam p JWK))

class HasKid a where
kid :: Lens' (a p) (Maybe (HeaderParam p T.Text))

class HasX5u a where
x5u :: Lens' (a p) (Maybe (HeaderParam p Types.URI))

class HasX5c a where
x5c :: Lens' (a p) (Maybe (HeaderParam p (NonEmpty Types.SignedCertificate)))

class HasX5t a where
x5t :: Lens' (a p) (Maybe (HeaderParam p Types.Base64SHA1))

class HasX5tS256 a where
x5tS256 :: Lens' (a p) (Maybe (HeaderParam p Types.Base64SHA256))

class HasTyp a where
typ :: Lens' (a p) (Maybe (HeaderParam p T.Text))

class HasCty a where
cty :: Lens' (a p) (Maybe (HeaderParam p T.Text))

class HasCrit a where
crit :: Lens' (a p) (Maybe (NonEmpty T.Text))
9 changes: 4 additions & 5 deletions src/Crypto/JOSE/JWA/JWK.hs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ import qualified Crypto.JOSE.JWA.JWS as JWA.JWS
import qualified Crypto.JOSE.TH
import qualified Crypto.JOSE.Types as Types
import qualified Crypto.JOSE.Types.Internal as Types
import Crypto.JOSE.Types.Orphans ()

import Crypto.JOSE.Types.WrappedNonEmpty (parseNonEmpty, kvNonEmpty, gettingGenMaybeNonEmpty)

-- | \"crv\" (Curve) Parameter
--
Expand Down Expand Up @@ -159,7 +158,7 @@ instance FromJSON RSAPrivateKeyOptionalParameters where
o .: "dp" <*>
o .: "dq" <*>
o .: "qi" <*>
o .:? "oth")
(o `parseNonEmpty` "oth"))

instance ToJSON RSAPrivateKeyOptionalParameters where
toJSON RSAPrivateKeyOptionalParameters{..} = object $ [
Expand All @@ -168,7 +167,7 @@ instance ToJSON RSAPrivateKeyOptionalParameters where
, "dp" .= rsaDp
, "dq" .= rsaDq
, "qi" .= rsaQi
] ++ maybe [] ((:[]) . ("oth" .=)) rsaOth
] ++ maybe [] ((:[]) . ("oth" `kvNonEmpty`)) rsaOth

instance Arbitrary RSAPrivateKeyOptionalParameters where
arbitrary = RSAPrivateKeyOptionalParameters
Expand All @@ -177,7 +176,7 @@ instance Arbitrary RSAPrivateKeyOptionalParameters where
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> gettingGenMaybeNonEmpty


-- | RSA private key parameters
Expand Down
Loading