cryptonite/Crypto/PubKey/RSA/PSS.hs
Vincent Hanquez db7c3bbb4f [hash] massive overhaul of the hash interface
use the typeclass for the lowest IO impure C bindings definitions,
and define the pure interface as generic on top of this.

At the same time define an Hash.IO interface to allow mutable manipulations
of hash contextes when necessary.

Use HashAlgorithm instead of HashFunction in the [PubKey] sections

Tweak the HMAC, PBKDF2 functions to be more efficient and use the new interface
2015-04-30 06:18:07 +01:00

135 lines
5.5 KiB
Haskell

-- |
-- 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
, sign
, signSafer
, verify
) where
import Crypto.Random.Types
import Crypto.PubKey.RSA.Types
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Crypto.PubKey.RSA.Prim
import Crypto.PubKey.RSA (generateBlinder)
import Crypto.PubKey.MaskGenFunction
import Crypto.Hash
import Data.Bits (xor, shiftR, (.&.))
import Data.Word
import qualified Crypto.Internal.ByteArray as B (convert)
-- | Parameters for PSS signature/verification.
data PSSParams hash = PSSParams
{ pssHash :: hash -- ^ Hash function to use
, pssMaskGenAlg :: MaskGenAlgorithm -- ^ Mask Gen algorithm to use
, pssSaltLength :: Int -- ^ Length of salt. need to be <= to hLen.
, pssTrailerField :: Word8 -- ^ Trailer field, usually 0xbc
}
-- | Default Params with a specified hash function
defaultPSSParams :: HashAlgorithm hash => hash -> PSSParams hash
defaultPSSParams hashAlg =
PSSParams { pssHash = hashAlg
, pssMaskGenAlg = mgf1 hashAlg
, pssSaltLength = hashDigestSize hashAlg
, pssTrailerField = 0xbc
}
-- | Default Params using SHA1 algorithm.
defaultPSSParamsSHA1 :: PSSParams SHA1
defaultPSSParamsSHA1 = defaultPSSParams SHA1
-- | Sign using the PSS parameters and the salt explicitely passed as parameters.
--
-- the function ignore SaltLength from the PSS Parameters
signWithSalt :: HashAlgorithm hash
=> ByteString -- ^ Salt to use
-> Maybe Blinder -- ^ optional blinder to use
-> PSSParams hash -- ^ PSS Parameters to use
-> PrivateKey -- ^ RSA Private Key
-> ByteString -- ^ Message to sign
-> Either Error ByteString
signWithSalt salt blinder params pk m
| k < hashLen + saltLen + 2 = Left InvalidParameters
| otherwise = Right $ dp blinder pk em
where mHash = B.convert $ hashWith (pssHash params) m
k = private_size pk
dbLen = k - hashLen - 1
saltLen = B.length salt
hashLen = hashDigestSize (pssHash params)
pubBits = private_size pk * 8 -- to change if public_size is converted in bytes
m' = B.concat [B.replicate 8 0,mHash,salt]
h = B.convert $ hashWith (pssHash params) m'
db = B.concat [B.replicate (dbLen - saltLen - 1) 0,B.singleton 1,salt]
dbmask = (pssMaskGenAlg params) h dbLen
maskedDB = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor db dbmask
em = B.concat [maskedDB, h, B.singleton (pssTrailerField params)]
-- | Sign using the PSS Parameters
sign :: (HashAlgorithm hash, MonadRandom m)
=> Maybe Blinder -- ^ optional blinder to use
-> PSSParams hash -- ^ PSS Parameters to use
-> PrivateKey -- ^ RSA Private Key
-> ByteString -- ^ Message to sign
-> m (Either Error ByteString)
sign blinder params pk m = do
salt <- getRandomBytes (pssSaltLength params)
return (signWithSalt salt blinder params pk m)
-- | Sign using the PSS Parameters and an automatically generated blinder.
signSafer :: (HashAlgorithm hash, MonadRandom m)
=> PSSParams hash -- ^ PSS Parameters to use
-> PrivateKey -- ^ private key
-> ByteString -- ^ message to sign
-> m (Either Error ByteString)
signSafer params pk m = do
blinder <- generateBlinder (private_n pk)
sign (Just blinder) params pk m
-- | Verify a signature using the PSS Parameters
verify :: HashAlgorithm hash
=> PSSParams hash -- ^ 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 params pk m s
| public_size pk /= B.length s = False
| B.last em /= pssTrailerField params = False
| not (B.all (== 0) ps0) = False
| b1 /= B.singleton 1 = False
| otherwise = h == B.convert h'
where -- parameters
hashLen = hashDigestSize (pssHash params)
dbLen = public_size pk - hashLen - 1
pubBits = public_size pk * 8 -- to change if public_size is converted in bytes
-- unmarshall fields
em = ep pk s
maskedDB = B.take (B.length em - hashLen - 1) em
h = B.take hashLen $ B.drop (B.length maskedDB) em
dbmask = (pssMaskGenAlg params) h dbLen
db = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor maskedDB dbmask
(ps0,z) = B.break (== 1) db
(b1,salt) = B.splitAt 1 z
mHash = B.convert $ hashWith (pssHash params) m
m' = B.concat [B.replicate 8 0,mHash,salt]
h' = hashWith (pssHash params) m'
normalizeToKeySize :: Int -> [Word8] -> [Word8]
normalizeToKeySize _ [] = [] -- very unlikely
normalizeToKeySize bits (x:xs) = x .&. mask : xs
where mask = if sh > 0 then 0xff `shiftR` (8-sh) else 0xff
sh = ((bits-1) .&. 0x7)