-- |
-- Module      : Crypto.PubKey.RSA
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
--
module Crypto.PubKey.RSA
    ( Error(..)
    , PublicKey(..)
    , PrivateKey(..)
    , Blinder(..)
    -- * generation function
    , generateWith
    , generate
    , generateBlinder
    ) where

import Crypto.Random
import Crypto.Types.PubKey.RSA
import Crypto.Number.ModArithmetic (inverse, inverseCoprimes)
import Crypto.Number.Generate (generateMax)
import Crypto.Number.Prime (generatePrime)
import Crypto.PubKey.RSA.Types

-- | Generate a key pair given p and q.
--
-- p and q need to be distinct prime numbers.
--
-- e need to be coprime to phi=(p-1)*(q-1). If that's not the
-- case, the function will not return a key pair.
-- A small hamming weight results in better performance.
--
-- * e=0x10001 is a popular choice
--
-- * e=3 is popular as well, but proven to not be as secure for some cases.
--
generateWith :: (Integer, Integer) -- ^ chosen distinct primes p and q
             -> Int                -- ^ size in bytes
             -> Integer            -- ^ RSA public exponant 'e'
             -> Maybe (PublicKey, PrivateKey)
generateWith :: (Integer, Integer)
-> Int -> Integer -> Maybe (PublicKey, PrivateKey)
generateWith (p :: Integer
p,q :: Integer
q) size :: Int
size e :: Integer
e =
    case Integer -> Integer -> Maybe Integer
inverse Integer
e Integer
phi of
        Nothing -> Maybe (PublicKey, PrivateKey)
forall a. Maybe a
Nothing
        Just d :: Integer
d  -> (PublicKey, PrivateKey) -> Maybe (PublicKey, PrivateKey)
forall a. a -> Maybe a
Just (PublicKey
pub,Integer -> PrivateKey
priv Integer
d)
  where n :: Integer
n   = Integer
pInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
*Integer
q
        phi :: Integer
phi = (Integer
pInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-1)Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
*(Integer
qInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-1)
        -- q and p should be *distinct* *prime* numbers, hence always coprime
        qinv :: Integer
qinv = Integer -> Integer -> Integer
inverseCoprimes Integer
q Integer
p
        pub :: PublicKey
pub = PublicKey :: Int -> Integer -> Integer -> PublicKey
PublicKey { public_size :: Int
public_size = Int
size
                        , public_n :: Integer
public_n    = Integer
n
                        , public_e :: Integer
public_e    = Integer
e
                        }
        priv :: Integer -> PrivateKey
priv d :: Integer
d = PrivateKey :: PublicKey
-> Integer
-> Integer
-> Integer
-> Integer
-> Integer
-> Integer
-> PrivateKey
PrivateKey { private_pub :: PublicKey
private_pub  = PublicKey
pub
                            , private_d :: Integer
private_d    = Integer
d
                            , private_p :: Integer
private_p    = Integer
p
                            , private_q :: Integer
private_q    = Integer
q
                            , private_dP :: Integer
private_dP   = Integer
d Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` (Integer
pInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-1)
                            , private_dQ :: Integer
private_dQ   = Integer
d Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` (Integer
qInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-1)
                            , private_qinv :: Integer
private_qinv = Integer
qinv
                            }

-- | generate a pair of (private, public) key of size in bytes.
generate :: CPRG g
         => g       -- ^ CPRG
         -> Int     -- ^ size in bytes
         -> Integer -- ^ RSA public exponant 'e'
         -> ((PublicKey, PrivateKey), g)
generate :: g -> Int -> Integer -> ((PublicKey, PrivateKey), g)
generate rng :: g
rng size :: Int
size e :: Integer
e = g -> ((PublicKey, PrivateKey), g)
forall b. CPRG b => b -> ((PublicKey, PrivateKey), b)
loop g
rng
  where loop :: b -> ((PublicKey, PrivateKey), b)
loop g :: b
g = -- loop until we find a valid key pair given e
            let (pq :: (Integer, Integer)
pq, g' :: b
g') = b -> ((Integer, Integer), b)
forall b. CPRG b => b -> ((Integer, Integer), b)
generatePQ b
g
             in case (Integer, Integer)
-> Int -> Integer -> Maybe (PublicKey, PrivateKey)
generateWith (Integer, Integer)
pq Int
size Integer
e of
                    Nothing -> b -> ((PublicKey, PrivateKey), b)
loop b
g'
                    Just pp :: (PublicKey, PrivateKey)
pp -> ((PublicKey, PrivateKey)
pp, b
g')
        generatePQ :: b -> ((Integer, Integer), b)
generatePQ g :: b
g =
            let (p :: Integer
p, g' :: b
g')  = b -> Int -> (Integer, b)
forall g. CPRG g => g -> Int -> (Integer, g)
generatePrime b
g (8 Int -> Int -> Int
forall a. Num a => a -> a -> a
* (Int
size Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 2))
                (q :: Integer
q, g'' :: b
g'') = Integer -> b -> (Integer, b)
forall b. CPRG b => Integer -> b -> (Integer, b)
generateQ Integer
p b
g'
             in ((Integer
p,Integer
q), b
g'')
        generateQ :: Integer -> b -> (Integer, b)
generateQ p :: Integer
p h :: b
h =
            let (q :: Integer
q, h' :: b
h') = b -> Int -> (Integer, b)
forall g. CPRG g => g -> Int -> (Integer, g)
generatePrime b
h (8 Int -> Int -> Int
forall a. Num a => a -> a -> a
* (Int
size Int -> Int -> Int
forall a. Num a => a -> a -> a
- (Int
size Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 2)))
             in if Integer
p Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
q then Integer -> b -> (Integer, b)
generateQ Integer
p b
h' else (Integer
q, b
h')

-- | Generate a blinder to use with decryption and signing operation
--
-- the unique parameter apart from the random number generator is the
-- public key value N.
generateBlinder :: CPRG g
                => g       -- ^ CPRG to use.
                -> Integer -- ^ RSA public N parameter.
                -> (Blinder, g)
generateBlinder :: g -> Integer -> (Blinder, g)
generateBlinder rng :: g
rng n :: Integer
n = (Integer -> Integer -> Blinder
Blinder Integer
r (Integer -> Integer -> Integer
inverseCoprimes Integer
r Integer
n), g
rng')
  where (r :: Integer
r, rng' :: g
rng') = g -> Integer -> (Integer, g)
forall g. CPRG g => g -> Integer -> (Integer, g)
generateMax g
rng Integer
n