1-- | 2-- Module : Crypto.PubKey.RSA.OAEP 3-- License : BSD-style 4-- Maintainer : Vincent Hanquez <vincent@snarc.org> 5-- Stability : experimental 6-- Portability : Good 7-- 8-- RSA OAEP mode 9-- <http://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding> 10-- 11module Crypto.PubKey.RSA.OAEP 12 ( 13 OAEPParams(..) 14 , defaultOAEPParams 15 -- * OAEP encryption 16 , encryptWithSeed 17 , encrypt 18 -- * OAEP decryption 19 , decrypt 20 , decryptSafer 21 ) where 22 23import Crypto.Hash 24import Crypto.Random.Types 25import Crypto.PubKey.RSA.Types 26import Crypto.PubKey.MaskGenFunction 27import Crypto.PubKey.RSA.Prim 28import Crypto.PubKey.RSA (generateBlinder) 29import Crypto.PubKey.Internal (and') 30import Data.ByteString (ByteString) 31import qualified Data.ByteString as B 32import Data.Bits (xor) 33 34import Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray) 35import qualified Crypto.Internal.ByteArray as B (convert) 36 37-- | Parameters for OAEP encryption/decryption 38data OAEPParams hash seed output = OAEPParams 39 { oaepHash :: hash -- ^ Hash function to use. 40 , oaepMaskGenAlg :: MaskGenAlgorithm seed output -- ^ Mask Gen algorithm to use. 41 , oaepLabel :: Maybe ByteString -- ^ Optional label prepended to message. 42 } 43 44-- | Default Params with a specified hash function 45defaultOAEPParams :: (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) 46 => hash 47 -> OAEPParams hash seed output 48defaultOAEPParams hashAlg = 49 OAEPParams { oaepHash = hashAlg 50 , oaepMaskGenAlg = mgf1 hashAlg 51 , oaepLabel = Nothing 52 } 53 54-- | Encrypt a message using OAEP with a predefined seed. 55encryptWithSeed :: HashAlgorithm hash 56 => ByteString -- ^ Seed 57 -> OAEPParams hash ByteString ByteString -- ^ OAEP params to use for encryption 58 -> PublicKey -- ^ Public key. 59 -> ByteString -- ^ Message to encrypt 60 -> Either Error ByteString 61encryptWithSeed seed oaep pk msg 62 | k < 2*hashLen+2 = Left InvalidParameters 63 | B.length seed /= hashLen = Left InvalidParameters 64 | mLen > k - 2*hashLen-2 = Left MessageTooLong 65 | otherwise = Right $ ep pk em 66 where -- parameters 67 k = public_size pk 68 mLen = B.length msg 69 mgf = oaepMaskGenAlg oaep 70 labelHash = hashWith (oaepHash oaep) (maybe B.empty id $ oaepLabel oaep) 71 hashLen = hashDigestSize (oaepHash oaep) 72 73 -- put fields 74 ps = B.replicate (k - mLen - 2*hashLen - 2) 0 75 db = B.concat [B.convert labelHash, ps, B.singleton 0x1, msg] 76 dbmask = mgf seed (k - hashLen - 1) 77 maskedDB = B.pack $ B.zipWith xor db dbmask 78 seedMask = mgf maskedDB hashLen 79 maskedSeed = B.pack $ B.zipWith xor seed seedMask 80 em = B.concat [B.singleton 0x0,maskedSeed,maskedDB] 81 82-- | Encrypt a message using OAEP 83encrypt :: (HashAlgorithm hash, MonadRandom m) 84 => OAEPParams hash ByteString ByteString -- ^ OAEP params to use for encryption. 85 -> PublicKey -- ^ Public key. 86 -> ByteString -- ^ Message to encrypt 87 -> m (Either Error ByteString) 88encrypt oaep pk msg = do 89 seed <- getRandomBytes hashLen 90 return (encryptWithSeed seed oaep pk msg) 91 where 92 hashLen = hashDigestSize (oaepHash oaep) 93 94-- | un-pad a OAEP encoded message. 95-- 96-- It doesn't apply the RSA decryption primitive 97unpad :: HashAlgorithm hash 98 => OAEPParams hash ByteString ByteString -- ^ OAEP params to use 99 -> Int -- ^ size of the key in bytes 100 -> ByteString -- ^ encoded message (not encrypted) 101 -> Either Error ByteString 102unpad oaep k em 103 | paddingSuccess = Right msg 104 | otherwise = Left MessageNotRecognized 105 where -- parameters 106 mgf = oaepMaskGenAlg oaep 107 labelHash = B.convert $ hashWith (oaepHash oaep) (maybe B.empty id $ oaepLabel oaep) 108 hashLen = hashDigestSize (oaepHash oaep) 109 -- getting em's fields 110 (pb, em0) = B.splitAt 1 em 111 (maskedSeed,maskedDB) = B.splitAt hashLen em0 112 seedMask = mgf maskedDB hashLen 113 seed = B.pack $ B.zipWith xor maskedSeed seedMask 114 dbmask = mgf seed (k - hashLen - 1) 115 db = B.pack $ B.zipWith xor maskedDB dbmask 116 -- getting db's fields 117 (labelHash',db1) = B.splitAt hashLen db 118 (_,db2) = B.break (/= 0) db1 119 (ps1,msg) = B.splitAt 1 db2 120 121 paddingSuccess = and' [ labelHash' == labelHash -- no need for constant eq 122 , ps1 == B.replicate 1 0x1 123 , pb == B.replicate 1 0x0 124 ] 125 126-- | Decrypt a ciphertext using OAEP 127-- 128-- When the signature is not in a context where an attacker could gain 129-- information from the timing of the operation, the blinder can be set to None. 130-- 131-- If unsure always set a blinder or use decryptSafer 132decrypt :: HashAlgorithm hash 133 => Maybe Blinder -- ^ Optional blinder 134 -> OAEPParams hash ByteString ByteString -- ^ OAEP params to use for decryption 135 -> PrivateKey -- ^ Private key 136 -> ByteString -- ^ Cipher text 137 -> Either Error ByteString 138decrypt blinder oaep pk cipher 139 | B.length cipher /= k = Left MessageSizeIncorrect 140 | k < 2*hashLen+2 = Left InvalidParameters 141 | otherwise = unpad oaep (private_size pk) $ dp blinder pk cipher 142 where -- parameters 143 k = private_size pk 144 hashLen = hashDigestSize (oaepHash oaep) 145 146-- | Decrypt a ciphertext using OAEP and by automatically generating a blinder. 147decryptSafer :: (HashAlgorithm hash, MonadRandom m) 148 => OAEPParams hash ByteString ByteString -- ^ OAEP params to use for decryption 149 -> PrivateKey -- ^ Private key 150 -> ByteString -- ^ Cipher text 151 -> m (Either Error ByteString) 152decryptSafer oaep pk cipher = do 153 blinder <- generateBlinder (private_n pk) 154 return (decrypt (Just blinder) oaep pk cipher) 155