-- | -- Module : Crypto.PubKey.RSA.PKCS15 -- License : BSD-style -- Maintainer : Vincent Hanquez -- Stability : experimental -- Portability : Good -- {-# LANGUAGE OverloadedStrings #-} module Crypto.PubKey.RSA.PKCS15 ( -- * padding and unpadding pad , padSignature , unpad -- * private key operations , decrypt , decryptSafer , sign , signSafer -- * public key operations , encrypt , verify ) where import Crypto.Random import Crypto.PubKey.Internal (and') import Crypto.Types.PubKey.RSA import Data.ByteString (ByteString) import qualified Data.ByteString as B import Crypto.PubKey.RSA.Prim import Crypto.PubKey.RSA.Types import Crypto.PubKey.RSA (generateBlinder) import Crypto.PubKey.HashDescr -- | This produce a standard PKCS1.5 padding for encryption pad :: CPRG g => g -> Int -> ByteString -> Either Error (ByteString, g) pad rng len m | B.length m > len - 11 = Left MessageTooLong | otherwise = let (padding, rng') = getNonNullRandom rng (len - B.length m - 3) in Right (B.concat [ B.singleton 0, B.singleton 2, padding, B.singleton 0, m ], rng') where {- get random non-null bytes -} getNonNullRandom :: CPRG g => g -> Int -> (ByteString, g) getNonNullRandom g n = let (bs0,g') = cprgGenerate n g bytes = B.pack $ filter (/= 0) $ B.unpack $ bs0 left = (n - B.length bytes) in if left == 0 then (bytes, g') else let (bend, g'') = getNonNullRandom g' left in (bytes `B.append` bend, g'') -- | Produce a standard PKCS1.5 padding for signature padSignature :: Int -> ByteString -> Either Error ByteString padSignature klen signature | klen < siglen+1 = Left SignatureTooLong | otherwise = Right $ B.concat [B.singleton 0,B.singleton 1,padding,B.singleton 0,signature] where siglen = B.length signature padding = B.replicate (klen - siglen - 3) 0xff -- | Try to remove a standard PKCS1.5 encryption padding. unpad :: ByteString -> Either Error ByteString unpad packed | paddingSuccess = Right m | otherwise = Left MessageNotRecognized where (zt, ps0m) = B.splitAt 2 packed (ps, zm) = B.span (/= 0) ps0m (z, m) = B.splitAt 1 zm paddingSuccess = and' [ zt == "\x00\x02" , z == "\x00" , B.length ps >= 8 ] -- | decrypt message using the private key. -- -- When the decryption is not in a context where an attacker could gain -- information from the timing of the operation, the blinder can be set to None. -- -- If unsure always set a blinder or use decryptSafer decrypt :: Maybe Blinder -- ^ optional blinder -> PrivateKey -- ^ RSA private key -> ByteString -- ^ cipher text -> Either Error ByteString decrypt blinder pk c | B.length c /= (private_size pk) = Left MessageSizeIncorrect | otherwise = unpad $ dp blinder pk c -- | decrypt message using the private key and by automatically generating a blinder. decryptSafer :: CPRG g => g -- ^ random generator -> PrivateKey -- ^ RSA private key -> ByteString -- ^ cipher text -> (Either Error ByteString, g) decryptSafer rng pk b = let (blinder, rng') = generateBlinder rng (private_n pk) in (decrypt (Just blinder) pk b, rng') -- | encrypt a bytestring using the public key and a CPRG random generator. -- -- the message need to be smaller than the key size - 11 encrypt :: CPRG g => g -> PublicKey -> ByteString -> (Either Error ByteString, g) encrypt rng pk m = do case pad rng (public_size pk) m of Left err -> (Left err, rng) Right (em, rng') -> (Right (ep pk em), rng') -- | sign message using private key, a hash and its ASN1 description -- -- When the signature is not in a context where an attacker could gain -- information from the timing of the operation, the blinder can be set to None. -- -- If unsure always set a blinder or use signSafer sign :: Maybe Blinder -- ^ optional blinder -> HashDescr -- ^ hash descriptor -> PrivateKey -- ^ private key -> ByteString -- ^ message to sign -> Either Error ByteString sign blinder hashDescr pk m = dp blinder pk `fmap` makeSignature hashDescr (private_size pk) m -- | sign message using the private key and by automatically generating a blinder. signSafer :: CPRG g => g -- ^ random generator -> HashDescr -- ^ Hash descriptor -> PrivateKey -- ^ private key -> ByteString -- ^ message to sign -> (Either Error ByteString, g) signSafer rng hashDescr pk m = let (blinder, rng') = generateBlinder rng (private_n pk) in (sign (Just blinder) hashDescr pk m, rng') -- | verify message with the signed message verify :: HashDescr -> PublicKey -> ByteString -> ByteString -> Bool verify hashDescr pk m sm = case makeSignature hashDescr (public_size pk) m of Left _ -> False Right s -> s == (ep pk sm) {- makeSignature for sign and verify -} makeSignature :: HashDescr -> Int -> ByteString -> Either Error ByteString makeSignature hashDescr klen m = padSignature klen signature where signature = (digestToASN1 hashDescr) $ (hashFunction hashDescr) m