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