-- |
-- 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.Types
import Crypto.Number.ModArithmetic (inverse, inverseCoprimes)
import Crypto.Number.Generate (generateMax)
import Crypto.Number.Prime (generatePrime)
import Crypto.PubKey.RSA.Types

{-
-- some bad implementation will not serialize ASN.1 integer properly, leading
-- to negative modulus.
-- TODO : Find a better place for this
toPositive :: Integer -> Integer
toPositive int
    | int < 0   = uintOfBytes $ bytesOfInt int
    | otherwise = int
  where uintOfBytes = foldl (\acc n -> (acc `shiftL` 8) + fromIntegral n) 0
        bytesOfInt :: Integer -> [Word8]
        bytesOfInt n = if testBit (head nints) 7 then nints else 0xff : nints
          where nints = reverse $ plusOne $ reverse $ map complement $ bytesOfUInt (abs n)
                plusOne []     = [1]
                plusOne (x:xs) = if x == 0xff then 0 : plusOne xs else (x+1) : xs
                bytesOfUInt x = reverse (list x)
                  where list i = if i <= 0xff then [fromIntegral i] else (fromIntegral i .&. 0xff) : list (i `shiftR` 8)
-}

-- | 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 exponent 'e'
             -> Maybe (PublicKey, PrivateKey)
generateWith :: (Integer, Integer)
-> Int -> Integer -> Maybe (PublicKey, PrivateKey)
generateWith (Integer
p,Integer
q) Int
size Integer
e =
    case Integer -> Integer -> Maybe Integer
inverse Integer
e Integer
phi of
        Maybe Integer
Nothing -> Maybe (PublicKey, PrivateKey)
forall a. Maybe a
Nothing
        Just 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
-Integer
1)Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
*(Integer
qInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-Integer
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 { 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 Integer
d = 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
-Integer
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
-Integer
1)
                            , private_qinv :: Integer
private_qinv = Integer
qinv
                            }

-- | generate a pair of (private, public) key of size in bytes.
generate :: MonadRandom m
         => Int     -- ^ size in bytes
         -> Integer -- ^ RSA public exponent 'e'
         -> m (PublicKey, PrivateKey)
generate :: forall (m :: * -> *).
MonadRandom m =>
Int -> Integer -> m (PublicKey, PrivateKey)
generate Int
size Integer
e = m (PublicKey, PrivateKey)
loop
  where
    loop :: m (PublicKey, PrivateKey)
loop = do -- loop until we find a valid key pair given e
        (Integer, Integer)
pq <- m (Integer, Integer)
generatePQ
        case (Integer, Integer)
-> Int -> Integer -> Maybe (PublicKey, PrivateKey)
generateWith (Integer, Integer)
pq Int
size Integer
e of
            Maybe (PublicKey, PrivateKey)
Nothing -> m (PublicKey, PrivateKey)
loop
            Just (PublicKey, PrivateKey)
pp -> (PublicKey, PrivateKey) -> m (PublicKey, PrivateKey)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (PublicKey, PrivateKey)
pp
    generatePQ :: m (Integer, Integer)
generatePQ = do
        Integer
p <- Int -> m Integer
forall (m :: * -> *). MonadRandom m => Int -> m Integer
generatePrime (Int
8 Int -> Int -> Int
forall a. Num a => a -> a -> a
* (Int
size Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2))
        Integer
q <- Integer -> m Integer
forall {m :: * -> *}. MonadRandom m => Integer -> m Integer
generateQ Integer
p
        (Integer, Integer) -> m (Integer, Integer)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
p,Integer
q)
    generateQ :: Integer -> m Integer
generateQ Integer
p = do
        Integer
q <- Int -> m Integer
forall (m :: * -> *). MonadRandom m => Int -> m Integer
generatePrime (Int
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` Int
2)))
        if Integer
p Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
q then Integer -> m Integer
generateQ Integer
p else Integer -> m Integer
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
q

-- | 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 :: MonadRandom m
                => Integer -- ^ RSA public N parameter.
                -> m Blinder
generateBlinder :: forall (m :: * -> *). MonadRandom m => Integer -> m Blinder
generateBlinder Integer
n =
    (\Integer
r -> Integer -> Integer -> Blinder
Blinder Integer
r (Integer -> Integer -> Integer
inverseCoprimes Integer
r Integer
n)) (Integer -> Blinder) -> m Integer -> m Blinder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Integer -> m Integer
forall {m :: * -> *}. MonadRandom m => Integer -> m Integer
generateMax Integer
n