RSA-2: src/Codec/Crypto/RSA/Exceptions.hs
module Codec.Crypto.RSA.Exceptions(
RSAError(..)
, HashInfo(..)
-- * Keys and key generation
, PrivateKey
, PublicKey
, generateKeyPair
-- * High-level encryption and signature functions
, encrypt
, encryptOAEP
, encryptPKCS
, decrypt
, decryptOAEP
, decryptPKCS
, sign
, verify
-- * Core routines for OAEP
, MGF
, generateMGF1
, rsaes_oaep_encrypt
, rsaes_oaep_decrypt
-- * Core PSS routines
-- $pss
-- * Core PKCS1 (v1.5) Routines
, rsaes_pkcs1_v1_5_encrypt
, rsaes_pkcs1_v1_5_decrypt
, rsassa_pkcs1_v1_5_sign
, rsassa_pkcs1_v1_5_verify
-- * Hashing algorithm declarations for use in RSA functions
, hashMD5, hashSHA1, hashSHA256, hashSHA384, hashSHA512
-- * Other mathematical functions that are handy for implementing
-- other RSA primitives.
, largeRandomPrime
, generatePQ
, chunkify
, os2ip, i2osp
, rsa_dp, rsa_ep
, rsa_vp1, rsa_sp1
, modular_inverse
, modular_exponentiation
, randomBS, randomNZBS
)
where
import qualified Codec.Crypto.RSA.Pure as Pure
import Codec.Crypto.RSA.Pure(HashInfo,RSAError)
import Crypto.Random
import Crypto.Types.PubKey.RSA
import Data.ByteString.Lazy(ByteString)
import Data.Int
-- |Randomly generate a key pair of the given modules length (in bits) to use
-- in any of the following functions. Use of a good random number generator is
-- of considerable importance when using this function. The input
-- CryptoRandomGen should never be used again for any other purpose; either
-- use the output'd generator or throw it all away.
generateKeyPair :: CryptoRandomGen g =>
g -> Int ->
(PublicKey, PrivateKey, g)
generateKeyPair g sizeBits = throwLeft (Pure.generateKeyPair g sizeBits)
-- ----------------------------------------------------------------------------
-- |Compute a signature for the given ByteString, using the SHA256 algorithm
-- in the computation. This is currently defined as rsassa_pkcs1_v1_5_sign
-- hashSHA256. If you want to use a different function, simply use the PKCS
-- function, below; it will accept arbitrarily-length messages.
sign :: PrivateKey -> ByteString -> ByteString
sign pk bs = throwLeft (Pure.sign pk bs)
-- |Verify a signature for the given ByteString, using the SHA25 algorithm in
-- the computation. Again, if you'd like to use a different algorithm, use the
-- rsassa_pkcs1_v1_5_verify function.
verify :: PublicKey -> -- ^The key of the signer
ByteString -> -- ^The message
ByteString -> -- ^The purported signature
Bool
verify pk m s = throwLeft (Pure.verify pk m s)
-- ----------------------------------------------------------------------------
-- |Encrypt an arbitrarily-sized message given the public key and reasonable
-- options. This is equivalent to calling encryptOAEP with SHA-256 as the
-- hash function, MGF1(SHA-256) as the mask generation function, and no label.
-- NOTE: This hash choice means that your key size must be 1024 bits or larger.
encrypt :: CryptoRandomGen g =>
g -> PublicKey -> ByteString ->
(ByteString, g)
encrypt g k m = throwLeft (Pure.encrypt g k m)
-- |Encrypt an arbitrarily-sized message using OAEP encoding. This is the
-- encouraged encoding for doing RSA encryption. Note that your key size
-- must be greater than (2 * hash length + 2) * 8. (For example, the
-- 'encrypt' convenience function uses a 256 bit / 32 byte hash function.
-- Thus, its key must be greater than (2 * 32 + 2) * 8 = 528 bits long,
-- and we suggest 1024 as a lower bound.)
encryptOAEP :: CryptoRandomGen g =>
g ->
(ByteString -> ByteString) -> -- ^The hash function to use
MGF -> -- ^The mask generation function to use
ByteString -> -- ^An optional label to include
PublicKey -> -- ^The public key to encrypt with
ByteString -> -- ^The message to encrypt
(ByteString, g)
encryptOAEP g hash mgf l k m = throwLeft (Pure.encryptOAEP g hash mgf l k m)
-- |Encrypt an arbitrarily-sized message using PKCS1 v1.5 encoding. This
-- encoding is deprecated, and should only be used when interacting with
-- legacy software that cannot be modified.
encryptPKCS :: CryptoRandomGen g =>
g -> PublicKey -> ByteString ->
(ByteString, g)
encryptPKCS g k m = throwLeft (Pure.encryptPKCS g k m)
-- ----------------------------------------------------------------------------
-- |Decrypt an arbitrarily-sized message given the public key and reasonable
-- options. This is equivalent to calling encryptOAEP with SHA-256 as the
-- hash function, MGF1(SHA-256) as the mask generation function, and no label.
decrypt :: PrivateKey -> ByteString -> ByteString
decrypt k m = throwLeft (Pure.decrypt k m)
-- |Decrypt an arbitrarily-sized message using OAEP encoding. This is the
-- encouraged encoding for doing RSA encryption.
decryptOAEP :: (ByteString -> ByteString) -> -- ^The hash function to use
MGF -> -- ^The mask generation function to use
ByteString -> -- ^An optional label to include
PrivateKey -> -- ^The public key to encrypt with
ByteString -> -- ^The message to decrypt
ByteString
decryptOAEP hash mgf l k m = throwLeft (Pure.decryptOAEP hash mgf l k m)
-- |Decrypt an arbitrarily-sized message using PKCS1 v1.5 encoding. This
-- encoding is deprecated, and should only be used when interacting with
-- legacy software that cannot be modified.
decryptPKCS :: PrivateKey -> ByteString -> ByteString
decryptPKCS k m = throwLeft (Pure.decryptPKCS k m)
-- ----------------------------------------------------------------------------
chunkify :: ByteString -> Int64 -> [ByteString]
chunkify = Pure.chunkify
-- ----------------------------------------------------------------------------
-- |The generalized implementation of RSAES-OAEP-ENCRYPT. Using the default
-- instantiontion of this, provided by the 'encrypt' function, is a pretty
-- good plan if this makes no sense to you, as it is instantiated with
-- reasonable defaults.
--
-- The message to be encrypted may not be longer then (k - 2*hLen - 2),
-- where k is the length of the RSA modulus in bytes and hLen is the length
-- of a hash in bytes. Passing in a larger message will generate an error,
-- represented by the Left constructor. Note that this means that OAEP
-- encryption cannot be used with keys smaller than 512 bits.
--
-- I have not put in a check for the length of the label, because I don't
-- expect you to use more than 2^32 bytes. So don't make me regret that, eh?
--
rsaes_oaep_encrypt :: CryptoRandomGen g =>
g ->
(ByteString -> ByteString) -> -- ^The hash function to use
MGF -> -- ^An appropriate mask genereation function
PublicKey -> -- ^The recipient's public key
ByteString -> -- ^A label to associate with the message
-- (feel free to use BS.empty)
ByteString -> -- ^The message to encrypt
(ByteString, g)
rsaes_oaep_encrypt g hash mgf k l m =
throwLeft (Pure.rsaes_oaep_encrypt g hash mgf k l m)
-- |The generalized implementation of RSAES-OAEP-DECRYPT. Again, 'decrypt'
-- initializes this with a pretty good set of defaults if you don't understand
-- what all of the arguments involve.
--
-- The ciphertext message passed to this function must be k bytes long, where
-- k is the size of the modulus in bytes. If it is not, this function will
-- generate an error, represented by the Left constructor.
--
-- Futher, k (the length of the ciphertext in bytes) must be greater than or
-- equal to (2 * hLen + 2), where hLen is the length of the output of the
-- hash function in bytes. If this equation does not hold, a (different)
-- error will be generated.
--
-- Finally, there are any number of internal situations that may generate
-- an error indicating that decryption failed.
--
rsaes_oaep_decrypt :: (ByteString -> ByteString) -> -- ^The hash function to use
MGF -> -- ^A mask generation function
PrivateKey -> -- ^The private key to use
ByteString -> -- ^An optional label whose
-- association with the message
-- should be verified.
ByteString -> -- ^The ciphertext to decrypt
ByteString
rsaes_oaep_decrypt hash mgf k l c =
throwLeft (Pure.rsaes_oaep_decrypt hash mgf k l c)
-- ----------------------------------------------------------------------------
-- |Implements RSAES-PKCS1-v1.5-Encrypt, for completeness and backward
-- compatibility. Also because I've already written everything else, so why not?
--
-- This encryption / padding mechanism has several known attacks, which are
-- described in the literature. So unless you absolutely need to use this
-- for some historical reason, you should avoid it.
--
-- The message to be encrypted must be less then or equal to (k - 11) bytes
-- long, where k is the length of the key modulus in bytes.
--
-- Because this function uses an unknown amount of randomly-generated data,
-- it takes an instance of RandomGen rather than taking a random number as
-- input, and returns the resultant generator as output. You should take care
-- that you (a) do not reuse the input generator, thus losing important
-- randomness, and (b) choose a decent instance of RandomGen for passing to
-- this function.
rsaes_pkcs1_v1_5_encrypt :: CryptoRandomGen g =>
g ->
PublicKey ->
ByteString ->
(ByteString, g)
rsaes_pkcs1_v1_5_encrypt g k m =
throwLeft (Pure.rsaes_pkcs1_v1_5_encrypt g k m)
-- |Implements RSAES-PKCS1-v1.5-Decrypt, for completeness and possible backward
-- compatibility. Please see the notes for rsaes_pkcs_v1_5_encrypt regarding
-- use of this function in new applications without backwards compatibility
-- requirements.
--
-- The ciphertext message passed to this function must be of length k, where
-- k is the length of the key modulus in bytes.
rsaes_pkcs1_v1_5_decrypt :: PrivateKey -> ByteString -> ByteString
rsaes_pkcs1_v1_5_decrypt k c = throwLeft (Pure.rsaes_pkcs1_v1_5_decrypt k c)
-- ----------------------------------------------------------------------------
-- $pss
-- |RSASSA-PSS-Sign, RSASSA-PSS-Verify, and the related functions are not
-- included because they are covered by U.S. Patent 7036014, and it's not clear
-- what the restrictions on implementation are. Sorry.
-- ----------------------------------------------------------------------------
-- |Generate a signature for the given message using the given private key,
-- using the RSASSA-PKCS1-v1.5-Sign algorithm. Note that in researching the
-- requirements for this project, several independent sources suggested not
-- using the same key across sign/validate and encrypt/decrypt contexts. You've
-- been warned.
--
-- The output of this function is the signature only, not the message and
-- the signature.
--
-- SIZE CONSTRAINT: The size of the public key (in bytes) must be greater
-- than or equal to the length of the hash identifier plus the length of
-- a hash plus 1. Thus, for example, you cannot use a 256 bit RSA key with
-- MD5: 32 (the size of a 256-bit RSA key in bytes) is less than 18 (the
-- size of MD5's identier) + 16 (the size of an MD5 hash in bytes) + 1,
-- or 35.
--
-- Thus,
-- * for MD5, SHA1, and SHA256, use 512+ bit keys
-- * for SHA384 and SHA512, use 1024+ bit keys
--
rsassa_pkcs1_v1_5_sign :: HashInfo -> -- ^The hash function to use
PrivateKey -> -- ^The private key to sign with
ByteString -> -- ^The message to sign
ByteString -- ^ The signature
rsassa_pkcs1_v1_5_sign hi k m =
throwLeft (Pure.rsassa_pkcs1_v1_5_sign hi k m)
-- |Validate a signature for the given message using the given public key. The
-- signature must be exactly k bytes long, where k is the size of the RSA
-- modulus IN BYTES.
rsassa_pkcs1_v1_5_verify :: HashInfo -> -- ^The hash function to use
PublicKey -> -- ^The public key to validate against
ByteString -> -- ^The message that was signed
ByteString -> -- ^The purported signature
Bool
rsassa_pkcs1_v1_5_verify hi k m s =
throwLeft (Pure.rsassa_pkcs1_v1_5_verify hi k m s)
-- ----------------------------------------------------------------------------
-- |A 'mask generation function'. The input is a bytestring, and the output
-- is a hash of the given length. Unless you know what you're doing, you
-- should probably use a MGF1 formulation created with generate_MGF1.
type MGF = ByteString -> Int64 -> Either RSAError ByteString
-- |Generate a mask generation function for the rsaes_oaep_*. As
-- suggested by the name, the generated function is an instance of the MGF1
-- function. The arguments are the underlying hash function to use and the
-- size of a hash in bytes.
--
-- The bytestring passed to the generated function cannot be longer than
-- 2^32 * hLen, where hLen is the passed length of the hash.
generateMGF1 :: (ByteString -> ByteString) -> MGF
generateMGF1 = Pure.generateMGF1
-- ----------------------------------------------------------------------------
-- "i2osp converts a nonnegative integer to an octet string of a specified
-- length" -- RFC 3447
i2osp :: Integral a => a -> Int -> ByteString
i2osp x len = throwLeft (Pure.i2osp x len)
-- "os2ip converts an octet string to a nonnegative integer" - RFC 3447
os2ip :: ByteString -> Integer
os2ip = Pure.os2ip
-- the RSA encryption function
rsa_ep :: Integer -> Integer -> Integer -> Integer
rsa_ep n e m = throwLeft (Pure.rsa_ep n e m)
-- the RSA decryption function
rsa_dp :: Integer -> Integer -> Integer -> Integer
rsa_dp n d c = throwLeft (Pure.rsa_dp n d c)
-- the RSA signature generation function
rsa_sp1 :: Integer -> Integer -> Integer -> Integer
rsa_sp1 n d m = throwLeft (Pure.rsa_sp1 n d m)
-- the RSA signature verification function
rsa_vp1 :: Integer -> Integer -> Integer -> Integer
rsa_vp1 n e s = throwLeft (Pure.rsa_vp1 n e s)
-- ----------------------------------------------------------------------------
-- Generate p and q. This is not necessarily the best way to do this, but it
-- appears to work.
generatePQ :: CryptoRandomGen g =>
g ->
Int ->
(Integer, Integer, g)
generatePQ g len = throwLeft (Pure.generatePQ g len)
-- |Generate a large random prime of a given length in bytes.
largeRandomPrime :: CryptoRandomGen g => g -> Int -> (Integer, g)
largeRandomPrime g len = throwLeft (Pure.largeRandomPrime g len)
-- |Generate a random ByteString of the given length
randomBS :: CryptoRandomGen g => g -> Int -> (ByteString, g)
randomBS g n = throwLeft (Pure.randomBS g n)
-- |Create a random bytestring of non-zero bytes of the given length.
randomNZBS :: CryptoRandomGen g => g -> Int -> (ByteString, g)
randomNZBS gen size = throwLeft (Pure.randomNZBS gen size)
-- |Computes a^b mod c using a moderately good algorithm.
modular_exponentiation :: Integer -> Integer -> Integer -> Integer
modular_exponentiation = Pure.modular_exponentiation
-- |Compute the modular inverse (d = e^-1 mod phi) via the extended euclidean
-- algorithm.
modular_inverse :: Integer -> -- ^e
Integer -> -- ^phi
Integer
modular_inverse = Pure.modular_inverse
-- ----------------------------------------------------------------------------
hashMD5 :: HashInfo
hashMD5 = Pure.hashMD5
hashSHA1 :: HashInfo
hashSHA1 = Pure.hashSHA1
hashSHA256 :: HashInfo
hashSHA256 = Pure.hashSHA256
hashSHA384 :: HashInfo
hashSHA384 = Pure.hashSHA384
hashSHA512 :: HashInfo
hashSHA512 = Pure.hashSHA512