1-- | 2-- Module : Crypto.PubKey.RSA.PKCS15 3-- License : BSD-style 4-- Maintainer : Vincent Hanquez <vincent@snarc.org> 5-- Stability : experimental 6-- Portability : Good 7-- 8module Crypto.PubKey.RSA.PKCS15 9 ( 10 -- * Padding and unpadding 11 pad 12 , padSignature 13 , unpad 14 -- * Private key operations 15 , decrypt 16 , decryptSafer 17 , sign 18 , signSafer 19 -- * Public key operations 20 , encrypt 21 , verify 22 -- * Hash ASN1 description 23 , HashAlgorithmASN1 24 ) where 25 26import Crypto.Random.Types 27import Crypto.PubKey.Internal (and') 28import Crypto.PubKey.RSA.Types 29import Crypto.PubKey.RSA.Prim 30import Crypto.PubKey.RSA (generateBlinder) 31import Crypto.Hash 32 33import Data.ByteString (ByteString) 34import Data.Word 35 36import Crypto.Internal.ByteArray (ByteArray, Bytes) 37import qualified Crypto.Internal.ByteArray as B 38 39-- | A specialized class for hash algorithm that can product 40-- a ASN1 wrapped description the algorithm plus the content 41-- of the digest. 42class HashAlgorithm hashAlg => HashAlgorithmASN1 hashAlg where 43 -- | Convert a Digest into an ASN1 wrapped descriptive ByteArray 44 hashDigestASN1 :: ByteArray out => Digest hashAlg -> out 45 46-- http://uk.emc.com/emc-plus/rsa-labs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf 47-- EMSA-PKCS1-v1_5 48instance HashAlgorithmASN1 MD2 where 49 hashDigestASN1 = addDigestPrefix [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x02,0x05,0x00,0x04,0x10] 50instance HashAlgorithmASN1 MD5 where 51 hashDigestASN1 = addDigestPrefix [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10] 52instance HashAlgorithmASN1 SHA1 where 53 hashDigestASN1 = addDigestPrefix [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14] 54instance HashAlgorithmASN1 SHA224 where 55 hashDigestASN1 = addDigestPrefix [0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,0x1c] 56instance HashAlgorithmASN1 SHA256 where 57 hashDigestASN1 = addDigestPrefix [0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20] 58instance HashAlgorithmASN1 SHA384 where 59 hashDigestASN1 = addDigestPrefix [0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30] 60instance HashAlgorithmASN1 SHA512 where 61 hashDigestASN1 = addDigestPrefix [0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40] 62instance HashAlgorithmASN1 SHA512t_224 where 63 hashDigestASN1 = addDigestPrefix [0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x05,0x05,0x00,0x04,0x1c] 64instance HashAlgorithmASN1 SHA512t_256 where 65 hashDigestASN1 = addDigestPrefix [0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x06,0x05,0x00,0x04,0x20] 66instance HashAlgorithmASN1 RIPEMD160 where 67 hashDigestASN1 = addDigestPrefix [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x24,0x03,0x02,0x01,0x05,0x00,0x04,0x14] 68 69-- 70-- ** Hack ** 71-- 72-- this happens to not need a real ASN1 encoder, because 73-- thanks to the digest being a specific size AND 74-- that the digest data is the last bytes in the encoding, 75-- this allows to just prepend the right prefix to the 76-- computed digest, to make it in the expected and valid shape. 77-- 78-- Otherwise the expected structure is in the following form: 79-- 80-- Start Sequence 81-- ,Start Sequence 82-- ,OID oid 83-- ,Null 84-- ,End Sequence 85-- ,OctetString digest 86-- ,End Sequence 87addDigestPrefix :: ByteArray out => [Word8] -> Digest hashAlg -> out 88addDigestPrefix prefix digest = 89 B.pack prefix `B.append` B.convert digest 90 91-- | This produce a standard PKCS1.5 padding for encryption 92pad :: (MonadRandom m, ByteArray message) => Int -> message -> m (Either Error message) 93pad len m 94 | B.length m > len - 11 = return (Left MessageTooLong) 95 | otherwise = do 96 padding <- getNonNullRandom (len - B.length m - 3) 97 return $ Right $ B.concat [ B.pack [0,2], padding, B.pack [0], m ] 98 99 where 100 -- get random non-null bytes 101 getNonNullRandom :: (ByteArray bytearray, MonadRandom m) => Int -> m bytearray 102 getNonNullRandom n = do 103 bs0 <- getRandomBytes n 104 let bytes = B.pack $ filter (/= 0) $ B.unpack (bs0 :: Bytes) 105 left = n - B.length bytes 106 if left == 0 107 then return bytes 108 else do bend <- getNonNullRandom left 109 return (bytes `B.append` bend) 110 111-- | Produce a standard PKCS1.5 padding for signature 112padSignature :: ByteArray signature => Int -> signature -> Either Error signature 113padSignature klen signature 114 | klen < siglen + 11 = Left SignatureTooLong 115 | otherwise = Right (B.pack padding `B.append` signature) 116 where 117 siglen = B.length signature 118 padding = 0 : 1 : (replicate (klen - siglen - 3) 0xff ++ [0]) 119 120-- | Try to remove a standard PKCS1.5 encryption padding. 121unpad :: ByteArray bytearray => bytearray -> Either Error bytearray 122unpad packed 123 | paddingSuccess = Right m 124 | otherwise = Left MessageNotRecognized 125 where 126 (zt, ps0m) = B.splitAt 2 packed 127 (ps, zm) = B.span (/= 0) ps0m 128 (z, m) = B.splitAt 1 zm 129 paddingSuccess = and' [ zt `B.constEq` (B.pack [0,2] :: Bytes) 130 , z == B.zero 1 131 , B.length ps >= 8 132 ] 133 134-- | decrypt message using the private key. 135-- 136-- When the decryption is not in a context where an attacker could gain 137-- information from the timing of the operation, the blinder can be set to None. 138-- 139-- If unsure always set a blinder or use decryptSafer 140-- 141-- The message is returned un-padded. 142decrypt :: Maybe Blinder -- ^ optional blinder 143 -> PrivateKey -- ^ RSA private key 144 -> ByteString -- ^ cipher text 145 -> Either Error ByteString 146decrypt blinder pk c 147 | B.length c /= (private_size pk) = Left MessageSizeIncorrect 148 | otherwise = unpad $ dp blinder pk c 149 150-- | decrypt message using the private key and by automatically generating a blinder. 151decryptSafer :: MonadRandom m 152 => PrivateKey -- ^ RSA private key 153 -> ByteString -- ^ cipher text 154 -> m (Either Error ByteString) 155decryptSafer pk b = do 156 blinder <- generateBlinder (private_n pk) 157 return (decrypt (Just blinder) pk b) 158 159-- | encrypt a bytestring using the public key. 160-- 161-- The message needs to be smaller than the key size - 11. 162-- The message should not be padded. 163encrypt :: MonadRandom m => PublicKey -> ByteString -> m (Either Error ByteString) 164encrypt pk m = do 165 r <- pad (public_size pk) m 166 case r of 167 Left err -> return $ Left err 168 Right em -> return $ Right (ep pk em) 169 170-- | sign message using private key, a hash and its ASN1 description 171-- 172-- When the signature is not in a context where an attacker could gain 173-- information from the timing of the operation, the blinder can be set to None. 174-- 175-- If unsure always set a blinder or use signSafer 176sign :: HashAlgorithmASN1 hashAlg 177 => Maybe Blinder -- ^ optional blinder 178 -> Maybe hashAlg -- ^ hash algorithm 179 -> PrivateKey -- ^ private key 180 -> ByteString -- ^ message to sign 181 -> Either Error ByteString 182sign blinder hashDescr pk m = dp blinder pk `fmap` makeSignature hashDescr (private_size pk) m 183 184-- | sign message using the private key and by automatically generating a blinder. 185signSafer :: (HashAlgorithmASN1 hashAlg, MonadRandom m) 186 => Maybe hashAlg -- ^ Hash algorithm 187 -> PrivateKey -- ^ private key 188 -> ByteString -- ^ message to sign 189 -> m (Either Error ByteString) 190signSafer hashAlg pk m = do 191 blinder <- generateBlinder (private_n pk) 192 return (sign (Just blinder) hashAlg pk m) 193 194-- | verify message with the signed message 195verify :: HashAlgorithmASN1 hashAlg 196 => Maybe hashAlg 197 -> PublicKey 198 -> ByteString 199 -> ByteString 200 -> Bool 201verify hashAlg pk m sm = 202 case makeSignature hashAlg (public_size pk) m of 203 Left _ -> False 204 Right s -> s == (ep pk sm) 205 206-- | make signature digest, used in 'sign' and 'verify' 207makeSignature :: HashAlgorithmASN1 hashAlg 208 => Maybe hashAlg -- ^ optional hashing algorithm 209 -> Int 210 -> ByteString 211 -> Either Error ByteString 212makeSignature Nothing klen m = padSignature klen m 213makeSignature (Just hashAlg) klen m = padSignature klen (hashDigestASN1 $ hashWith hashAlg m) 214