-- |
-- Module      : Crypto.PubKey.RSA.PSS
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
module Crypto.PubKey.RSA.PSS (
    PSSParams (..),
    defaultPSSParams,
    defaultPSSParamsSHA1,

    -- * Sign and verify functions
    signWithSalt,
    signDigestWithSalt,
    sign,
    signDigest,
    signSafer,
    signDigestSafer,
    signBlinded,
    signDigestBlinded,
    verify,
    verifyDigest,
) where

import Crypto.Hash
import Crypto.Number.Basic (numBits)
import Crypto.Number.Serialize (os2ip)
import Crypto.PubKey.MaskGenFunction
import Crypto.PubKey.RSA.Blinded.Internal
import Crypto.PubKey.RSA.Prim
import Crypto.PubKey.RSA.Types
import Crypto.Random.Types
import Data.Bits (complement, shiftR, (.&.))
import Data.Word

import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess)
import qualified Crypto.Internal.ByteArray as B (bxor, convert, eq)

import Data.ByteString (ByteString)
import qualified Data.ByteString as B

-- | Parameters for PSS signature/verification.
data PSSParams hash seed output = PSSParams
    { forall hash seed output. PSSParams hash seed output -> hash
pssHash :: hash
    -- ^ Hash function to use
    , forall hash seed output.
PSSParams hash seed output -> MaskGenAlgorithm seed output
pssMaskGenAlg :: MaskGenAlgorithm seed output
    -- ^ Mask Gen algorithm to use
    , forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength :: Int
    -- ^ Length of salt. need to be <= to hLen.
    , forall hash seed output. PSSParams hash seed output -> Word8
pssTrailerField :: Word8
    -- ^ Trailer field, usually 0xbc
    }

-- | Default Params with a specified hash function
defaultPSSParams
    :: (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash)
    => hash
    -> PSSParams hash seed output
defaultPSSParams :: forall seed output hash.
(ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) =>
hash -> PSSParams hash seed output
defaultPSSParams hash
hashAlg =
    PSSParams
        { pssHash :: hash
pssHash = hash
hashAlg
        , pssMaskGenAlg :: MaskGenAlgorithm seed output
pssMaskGenAlg = hash -> MaskGenAlgorithm seed output
forall seed output hashAlg.
(ByteArrayAccess seed, ByteArray output, HashAlgorithm hashAlg) =>
hashAlg -> seed -> Int -> output
mgf1 hash
hashAlg
        , pssSaltLength :: Int
pssSaltLength = hash -> Int
forall a. HashAlgorithm a => a -> Int
hashDigestSize hash
hashAlg
        , pssTrailerField :: Word8
pssTrailerField = Word8
0xbc
        }

-- | Default Params using SHA1 algorithm.
defaultPSSParamsSHA1 :: PSSParams SHA1 ByteString ByteString
defaultPSSParamsSHA1 :: PSSParams SHA1 ByteString ByteString
defaultPSSParamsSHA1 = SHA1 -> PSSParams SHA1 ByteString ByteString
forall seed output hash.
(ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) =>
hash -> PSSParams hash seed output
defaultPSSParams SHA1
SHA1

-- | Sign using the PSS parameters and the salt explicitely passed as parameters.
--
-- the function ignore SaltLength from the PSS Parameters
--
-- See t'Blinder' for what the optional blinder covers and when leaving it out
-- is a decision rather than a default.  'signSafer' generates one for you.
signDigestWithSalt
    :: HashAlgorithm hash
    => ByteString
    -- ^ Salt to use
    -> Maybe Blinder
    -- ^ optional blinder to use
    -> PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ RSA Private Key
    -> Digest hash
    -- ^ Message digest
    -> Either Error ByteString
signDigestWithSalt :: forall hash.
HashAlgorithm hash =>
ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
signDigestWithSalt ByteString
salt Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest =
    Maybe Blinder -> PrivateKey -> ByteString -> ByteString
forall ba. ByteArray ba => Maybe Blinder -> PrivateKey -> ba -> ba
dp Maybe Blinder
blinder PrivateKey
pk (ByteString -> ByteString)
-> Either Error ByteString -> Either Error ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
encodeDigest ByteString
salt PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest

-- | The encoded message the private-key operation is applied to.
encodeDigest
    :: HashAlgorithm hash
    => ByteString
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> Digest hash
    -> Either Error ByteString
encodeDigest :: forall hash.
HashAlgorithm hash =>
ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
encodeDigest ByteString
salt PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest
    | Int
emLen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
hashLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
saltLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2 = Error -> Either Error ByteString
forall a b. a -> Either a b
Left Error
InvalidParameters
    | Bool
otherwise = ByteString -> Either Error ByteString
forall a b. b -> Either a b
Right ByteString
em
  where
    k :: Int
k = PrivateKey -> Int
private_size PrivateKey
pk
    emLen :: Int
emLen = if Int -> Bool
emTruncate Int
pubBits then Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 else Int
k
    mHash :: ByteString
mHash = Digest hash -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert Digest hash
digest
    dbLen :: Int
dbLen = Int
emLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
hashLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
    saltLen :: Int
saltLen = ByteString -> Int
B.length ByteString
salt
    hashLen :: Int
hashLen = hash -> Int
forall a. HashAlgorithm a => a -> Int
hashDigestSize (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params)
    pubBits :: Int
pubBits = Integer -> Int
numBits (PrivateKey -> Integer
private_n PrivateKey
pk)
    m' :: ByteString
m' = [ByteString] -> ByteString
B.concat [Int -> Word8 -> ByteString
B.replicate Int
8 Word8
0, ByteString
mHash, ByteString
salt]
    h :: ByteString
h = Digest hash -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert (Digest hash -> ByteString) -> Digest hash -> ByteString
forall a b. (a -> b) -> a -> b
$ hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m'
    db :: ByteString
db = [ByteString] -> ByteString
B.concat [Int -> Word8 -> ByteString
B.replicate (Int
dbLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
saltLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Word8
0, Word8 -> ByteString
B.singleton Word8
1, ByteString
salt]
    dbmask :: ByteString
dbmask = PSSParams hash ByteString ByteString
-> MaskGenAlgorithm ByteString ByteString
forall hash seed output.
PSSParams hash seed output -> MaskGenAlgorithm seed output
pssMaskGenAlg PSSParams hash ByteString ByteString
params ByteString
h Int
dbLen
    maskedDB :: ByteString
maskedDB = Int -> ByteString -> ByteString
normalizeToKeySize Int
pubBits (ByteString -> ByteString -> ByteString
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.bxor ByteString
db ByteString
dbmask :: ByteString)
    em :: ByteString
em = [ByteString] -> ByteString
B.concat [ByteString
maskedDB, ByteString
h, Word8 -> ByteString
B.singleton (PSSParams hash ByteString ByteString -> Word8
forall hash seed output. PSSParams hash seed output -> Word8
pssTrailerField PSSParams hash ByteString ByteString
params)]

-- | Sign using the PSS parameters and the salt explicitely passed as parameters.
--
-- the function ignore SaltLength from the PSS Parameters
--
-- See t'Blinder' for what the optional blinder covers and when leaving it out
-- is a decision rather than a default.  'signSafer' generates one for you.
signWithSalt
    :: HashAlgorithm hash
    => ByteString
    -- ^ Salt to use
    -> Maybe Blinder
    -- ^ optional blinder to use
    -> PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ RSA Private Key
    -> ByteString
    -- ^ Message to sign
    -> Either Error ByteString
signWithSalt :: forall hash.
HashAlgorithm hash =>
ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> ByteString
-> Either Error ByteString
signWithSalt ByteString
salt Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk ByteString
m = ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
signDigestWithSalt ByteString
salt Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
mHash
  where
    mHash :: Digest hash
mHash = hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m

-- | Sign using the PSS Parameters
--
-- See t'Blinder' for what the optional blinder covers and when leaving it out
-- is a decision rather than a default.  'signSafer' generates one for you.
sign
    :: (HashAlgorithm hash, MonadRandom m)
    => Maybe Blinder
    -- ^ optional blinder to use
    -> PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ RSA Private Key
    -> ByteString
    -- ^ Message to sign
    -> m (Either Error ByteString)
sign :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> ByteString
-> m (Either Error ByteString)
sign Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk ByteString
m = do
    ByteString
salt <- Int -> m ByteString
forall byteArray. ByteArray byteArray => Int -> m byteArray
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes (PSSParams hash ByteString ByteString -> Int
forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength PSSParams hash ByteString ByteString
params)
    Either Error ByteString -> m (Either Error ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> ByteString
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> ByteString
-> Either Error ByteString
signWithSalt ByteString
salt Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk ByteString
m)

-- | Sign using the PSS Parameters
--
-- See t'Blinder' for what the optional blinder covers and when leaving it out
-- is a decision rather than a default.  'signSafer' generates one for you.
signDigest
    :: (HashAlgorithm hash, MonadRandom m)
    => Maybe Blinder
    -- ^ optional blinder to use
    -> PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ RSA Private Key
    -> Digest hash
    -- ^ Message digest
    -> m (Either Error ByteString)
signDigest :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> m (Either Error ByteString)
signDigest Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest = do
    ByteString
salt <- Int -> m ByteString
forall byteArray. ByteArray byteArray => Int -> m byteArray
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes (PSSParams hash ByteString ByteString -> Int
forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength PSSParams hash ByteString ByteString
params)
    Either Error ByteString -> m (Either Error ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> Maybe Blinder
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
signDigestWithSalt ByteString
salt Maybe Blinder
blinder PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest)

-- | Sign using the PSS Parameters, blinded with the pair kept for this key;
-- see "Crypto.PubKey.RSA.Blinded".
signSafer
    :: (HashAlgorithm hash, MonadRandom m)
    => PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ private key
    -> ByteString
    -- ^ message to sign
    -> m (Either Error ByteString)
signSafer :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> PrivateKey -> ByteString -> m (Either Error ByteString)
signSafer PSSParams hash ByteString ByteString
params PrivateKey
pk ByteString
m = PSSParams hash ByteString ByteString
-> PrivateKey -> Digest hash -> m (Either Error ByteString)
forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> PrivateKey -> Digest hash -> m (Either Error ByteString)
signDigestSafer PSSParams hash ByteString ByteString
params PrivateKey
pk (hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m)

-- | Sign using the PSS Parameters, blinded with the pair kept for this key;
-- see "Crypto.PubKey.RSA.Blinded".
signDigestSafer
    :: (HashAlgorithm hash, MonadRandom m)
    => PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use
    -> PrivateKey
    -- ^ private key
    -> Digest hash
    -- ^ message digst
    -> m (Either Error ByteString)
signDigestSafer :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> PrivateKey -> Digest hash -> m (Either Error ByteString)
signDigestSafer PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest = do
    ByteString
salt <- Int -> m ByteString
forall byteArray. ByteArray byteArray => Int -> m byteArray
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes (PSSParams hash ByteString ByteString -> Int
forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength PSSParams hash ByteString ByteString
params)
    Draw
d <- PrivateKey -> m Draw
forall (m :: * -> *). MonadRandom m => PrivateKey -> m Draw
drawFor PrivateKey
pk
    Either Error ByteString -> m (Either Error ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either Error ByteString -> m (Either Error ByteString))
-> Either Error ByteString -> m (Either Error ByteString)
forall a b. (a -> b) -> a -> b
$ case ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
encodeDigest ByteString
salt PSSParams hash ByteString ByteString
params PrivateKey
pk Digest hash
digest of
        Left Error
err -> Error -> Either Error ByteString
forall a b. a -> Either a b
Left Error
err
        Right ByteString
em -> ByteString -> Either Error ByteString
forall a b. b -> Either a b
Right (PrivateKey
-> Draw -> ((Integer, Integer) -> ByteString) -> ByteString
forall a. PrivateKey -> Draw -> ((Integer, Integer) -> a) -> a
withBlindingFor PrivateKey
pk Draw
d (\(Integer, Integer)
pair -> (Integer, Integer) -> PrivateKey -> ByteString -> ByteString
forall ba.
ByteArray ba =>
(Integer, Integer) -> PrivateKey -> ba -> ba
dpWithPair (Integer, Integer)
pair PrivateKey
pk ByteString
em))

-- | 'signSafer' with the blinding a t'BlindedKey' keeps, and the key for the
-- next operation.
signBlinded
    :: (HashAlgorithm hash, MonadRandom m)
    => PSSParams hash ByteString ByteString
    -> BlindedKey
    -> ByteString
    -- ^ message to sign
    -> m (Either Error ByteString, BlindedKey)
signBlinded :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> BlindedKey
-> ByteString
-> m (Either Error ByteString, BlindedKey)
signBlinded PSSParams hash ByteString ByteString
params BlindedKey
bk ByteString
m = PSSParams hash ByteString ByteString
-> BlindedKey
-> Digest hash
-> m (Either Error ByteString, BlindedKey)
forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> BlindedKey
-> Digest hash
-> m (Either Error ByteString, BlindedKey)
signDigestBlinded PSSParams hash ByteString ByteString
params BlindedKey
bk (hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m)

-- | 'signDigestSafer' with the blinding a t'BlindedKey' keeps, and the key
-- for the next operation.
signDigestBlinded
    :: (HashAlgorithm hash, MonadRandom m)
    => PSSParams hash ByteString ByteString
    -> BlindedKey
    -> Digest hash
    -- ^ message digest
    -> m (Either Error ByteString, BlindedKey)
signDigestBlinded :: forall hash (m :: * -> *).
(HashAlgorithm hash, MonadRandom m) =>
PSSParams hash ByteString ByteString
-> BlindedKey
-> Digest hash
-> m (Either Error ByteString, BlindedKey)
signDigestBlinded PSSParams hash ByteString ByteString
params BlindedKey
bk Digest hash
digest = do
    ByteString
salt <- Int -> m ByteString
forall byteArray. ByteArray byteArray => Int -> m byteArray
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes (PSSParams hash ByteString ByteString -> Int
forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength PSSParams hash ByteString ByteString
params)
    case ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
forall hash.
HashAlgorithm hash =>
ByteString
-> PSSParams hash ByteString ByteString
-> PrivateKey
-> Digest hash
-> Either Error ByteString
encodeDigest ByteString
salt PSSParams hash ByteString ByteString
params (BlindedKey -> PrivateKey
blindedPrivateKey BlindedKey
bk) Digest hash
digest of
        Left Error
err -> (Either Error ByteString, BlindedKey)
-> m (Either Error ByteString, BlindedKey)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Error -> Either Error ByteString
forall a b. a -> Either a b
Left Error
err, BlindedKey
bk)
        Right ByteString
em -> do
            (ByteString
s, BlindedKey
bk') <- BlindedKey -> ByteString -> m (ByteString, BlindedKey)
forall (m :: * -> *) ba.
(MonadRandom m, ByteArray ba) =>
BlindedKey -> ba -> m (ba, BlindedKey)
dpBlinded BlindedKey
bk ByteString
em
            (Either Error ByteString, BlindedKey)
-> m (Either Error ByteString, BlindedKey)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Either Error ByteString
forall a b. b -> Either a b
Right ByteString
s, BlindedKey
bk')

-- | Verify a signature using the PSS Parameters
verify
    :: HashAlgorithm hash
    => PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use to verify,
    --   this need to be identical to the parameters when signing
    -> PublicKey
    -- ^ RSA Public Key
    -> ByteString
    -- ^ Message to verify
    -> ByteString
    -- ^ Signature
    -> Bool
verify :: forall hash.
HashAlgorithm hash =>
PSSParams hash ByteString ByteString
-> PublicKey -> ByteString -> ByteString -> Bool
verify PSSParams hash ByteString ByteString
params PublicKey
pk ByteString
m = PSSParams hash ByteString ByteString
-> PublicKey -> Digest hash -> ByteString -> Bool
forall hash.
HashAlgorithm hash =>
PSSParams hash ByteString ByteString
-> PublicKey -> Digest hash -> ByteString -> Bool
verifyDigest PSSParams hash ByteString ByteString
params PublicKey
pk Digest hash
mHash
  where
    mHash :: Digest hash
mHash = hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m

-- | Verify a signature using the PSS Parameters
--
-- Following RFC 8017, the signature is rejected unless it is exactly as long
-- as the modulus (section 8.1.2, step 1) and its integer representative is
-- below the modulus (RSAVP1, section 5.2.2, step 1).  The public-key operation
-- normalises any multiple of the modulus away, so without the second check
-- @s + n@ would verify as readily as @s@, and a third party could turn one
-- valid signature into another without the private key.
verifyDigest
    :: HashAlgorithm hash
    => PSSParams hash ByteString ByteString
    -- ^ PSS Parameters to use to verify,
    --   this need to be identical to the parameters when signing
    -> PublicKey
    -- ^ RSA Public Key
    -> Digest hash
    -- ^ Digest to verify
    -> ByteString
    -- ^ Signature
    -> Bool
verifyDigest :: forall hash.
HashAlgorithm hash =>
PSSParams hash ByteString ByteString
-> PublicKey -> Digest hash -> ByteString -> Bool
verifyDigest PSSParams hash ByteString ByteString
params PublicKey
pk Digest hash
digest ByteString
s
    | ByteString -> Int
B.length ByteString
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
k = Bool
False
    | ByteString -> Integer
forall ba. ByteArrayAccess ba => ba -> Integer
os2ip ByteString
s Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= PublicKey -> Integer
public_n PublicKey
pk = Bool
False
    | (Word8 -> Bool) -> ByteString -> Bool
B.any (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0) ByteString
pre = Bool
False
    | (Word8 -> Bool) -> ByteString -> Bool
B.any (\Word8
x -> Word8
x Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
topBits Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0) (Int -> ByteString -> ByteString
B.take Int
1 ByteString
maskedDB) = Bool
False
    | HasCallStack => ByteString -> Word8
ByteString -> Word8
B.last ByteString
em Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= PSSParams hash ByteString ByteString -> Word8
forall hash seed output. PSSParams hash seed output -> Word8
pssTrailerField PSSParams hash ByteString ByteString
params = Bool
False
    | (Word8 -> Bool) -> ByteString -> Bool
B.any (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0) ByteString
ps0 = Bool
False
    | ByteString
b1 ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8 -> ByteString
B.singleton Word8
1 = Bool
False
    | PSSParams hash ByteString ByteString -> Int
forall hash seed output. PSSParams hash seed output -> Int
pssSaltLength PSSParams hash ByteString ByteString
params Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= ByteString -> Int
B.length ByteString
salt = Bool
False
    | Bool
otherwise = ByteString -> Digest hash -> Bool
forall bs1 bs2.
(ByteArrayAccess bs1, ByteArrayAccess bs2) =>
bs1 -> bs2 -> Bool
B.eq ByteString
h Digest hash
h'
  where
    -- parameters
    hashLen :: Int
hashLen = hash -> Int
forall a. HashAlgorithm a => a -> Int
hashDigestSize (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params)
    mHash :: ByteString
mHash = Digest hash -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert Digest hash
digest
    k :: Int
k = PublicKey -> Int
public_size PublicKey
pk
    emLen :: Int
emLen = if Int -> Bool
emTruncate Int
pubBits then Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 else Int
k
    dbLen :: Int
dbLen = Int
emLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
hashLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
    pubBits :: Int
pubBits = Integer -> Int
numBits (PublicKey -> Integer
public_n PublicKey
pk)
    -- RFC 8017 9.1.2 step 6: the leftmost 8*emLen - emBits bits of the
    -- leftmost octet of maskedDB have to be zero already.  Step 9 clears
    -- them in DB, which is what normalizeToKeySize does below, and clearing
    -- is not checking: without this an encoding with the top bit set -- one
    -- the standard calls inconsistent -- verifies as though it were sound,
    -- because the bit that made it wrong is thrown away before it is read.
    topBits :: Word8
topBits = Word8 -> Word8
forall a. Bits a => a -> a
complement (Int -> Word8
normalizeMask Int
pubBits)
    -- unmarshall fields
    (ByteString
pre, ByteString
em) = Int -> ByteString -> (ByteString, ByteString)
B.splitAt (Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
emLen) (PublicKey -> ByteString -> ByteString
forall ba. ByteArray ba => PublicKey -> ba -> ba
ep PublicKey
pk ByteString
s) -- drop 0..1 byte
    maskedDB :: ByteString
maskedDB = Int -> ByteString -> ByteString
B.take Int
dbLen ByteString
em
    h :: ByteString
h = Int -> ByteString -> ByteString
B.take Int
hashLen (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Int -> ByteString -> ByteString
B.drop (ByteString -> Int
B.length ByteString
maskedDB) ByteString
em
    dbmask :: ByteString
dbmask = PSSParams hash ByteString ByteString
-> MaskGenAlgorithm ByteString ByteString
forall hash seed output.
PSSParams hash seed output -> MaskGenAlgorithm seed output
pssMaskGenAlg PSSParams hash ByteString ByteString
params ByteString
h Int
dbLen
    db :: ByteString
db = Int -> ByteString -> ByteString
normalizeToKeySize Int
pubBits (ByteString -> ByteString -> ByteString
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.bxor ByteString
maskedDB ByteString
dbmask :: ByteString)
    (ByteString
ps0, ByteString
z) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
B.break (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
1) ByteString
db
    (ByteString
b1, ByteString
salt) = Int -> ByteString -> (ByteString, ByteString)
B.splitAt Int
1 ByteString
z
    m' :: ByteString
m' = [ByteString] -> ByteString
B.concat [Int -> Word8 -> ByteString
B.replicate Int
8 Word8
0, ByteString
mHash, ByteString
salt]
    h' :: Digest hash
h' = hash -> ByteString -> Digest hash
forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith (PSSParams hash ByteString ByteString -> hash
forall hash seed output. PSSParams hash seed output -> hash
pssHash PSSParams hash ByteString ByteString
params) ByteString
m'

-- When the modulus has bit length 1 modulo 8 we drop the first byte.
emTruncate :: Int -> Bool
emTruncate :: Int -> Bool
emTruncate Int
bits = ((Int
bits Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0

normalizeToKeySize :: Int -> ByteString -> ByteString
normalizeToKeySize :: Int -> ByteString -> ByteString
normalizeToKeySize Int
bits ByteString
x = case ByteString -> Maybe (Word8, ByteString)
B.uncons ByteString
x of
    Maybe (Word8, ByteString)
Nothing -> ByteString
x -- very unlikely
    Just (Word8
h, ByteString
t) -> Word8 -> ByteString -> ByteString
B.cons (Word8
h Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Int -> Word8
normalizeMask Int
bits) ByteString
t

-- | The bits of the leftmost octet that belong to the encoding: the low
-- @emBits `mod` 8@ of them, or all eight when that is zero.  Its complement
-- is the bits RFC 8017 requires to be zero.
normalizeMask :: Int -> Word8
normalizeMask :: Int -> Word8
normalizeMask Int
bits = if Int
sh Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 then Word8
0xff Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
`shiftR` (Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
sh) else Word8
0xff
  where
    sh :: Int
sh = (Int
bits Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7