packages feed

jose-0.6.0.0: test/Properties.hs

-- Copyright (C) 2015, 2016  Fraser Tweedale
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

{-# LANGUAGE ScopedTypeVariables #-}

module Properties where

import Control.Applicative
import Control.Monad.Except (runExceptT)

import Data.Aeson
import qualified Data.ByteString as B

import Test.Tasty
import Test.Tasty.QuickCheck
import Test.QuickCheck.Monadic
import Test.QuickCheck.Instances ()

import Crypto.JOSE.Error (Error(..))
import Crypto.JOSE.Types
import Crypto.JOSE.JWK
import Crypto.JOSE.JWS

properties = testGroup "Properties"
  [ testProperty "SizedBase64Integer round-trip"
    (prop_roundTrip :: SizedBase64Integer -> Bool)
  , testProperty "JWK round-trip" (prop_roundTrip :: JWK -> Bool)
  , testProperty "RSA gen, sign and verify" prop_rsaSignAndVerify
  , testProperty "gen, sign with best alg, verify" prop_bestJWSAlg
  ]

prop_roundTrip :: (Eq a, ToJSON a, FromJSON a) => a -> Bool
prop_roundTrip a = decode (encode [a]) == Just [a]

debugRoundTrip
  :: (Show a, Arbitrary a, ToJSON a, FromJSON a)
  => (a -> Bool)
  -> Property
debugRoundTrip f = monadicIO $ do
  a :: a <- pick arbitrary
  let encoded = encode [a]
  monitor $ counterexample $
    "JSON: \n" ++ show encoded ++ "\n\nDecoded: \n" ++ show (decode encoded :: Maybe [a])
  assert $ f a

prop_rsaSignAndVerify :: B.ByteString -> Property
prop_rsaSignAndVerify msg = monadicIO $ do
  keylen <- pick $ elements ((`div` 8) <$> [2048, 3072, 4096])
  k :: JWK <- run $ genJWK (RSAGenParam keylen)
  alg <- pick $ elements [RS256, RS384, RS512, PS256, PS384, PS512]
  monitor (collect alg)
  wp (runExceptT (signJWS msg [(newJWSHeader (Protected, alg), k)]
    >>= verifyJWS defaultValidationSettings k)) (checkSignVerifyResult msg)

prop_bestJWSAlg :: B.ByteString -> Property
prop_bestJWSAlg msg = monadicIO $ do
  genParam <- pick arbitrary
  k <- run $ genJWK genParam
  case bestJWSAlg k of
    Left (KeyMismatch _) -> pre False  -- skip non-signing keys
    Left _ -> assert False
    Right alg -> do
      monitor (collect alg)
      let
        go = do
          jws <- signJWS msg [(newJWSHeader (Protected, alg), k)]
          verifyJWS defaultValidationSettings k jws
      wp (runExceptT go) (checkSignVerifyResult msg)

checkSignVerifyResult :: Monad m => B.ByteString -> Either Error B.ByteString -> PropertyM m ()
checkSignVerifyResult msg = assert . either (const False) (== msg)