File: RSA.hs

package info (click to toggle)
haskell-crypton 1.0.4-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,548 kB
  • sloc: haskell: 26,764; ansic: 22,294; makefile: 6
file content (122 lines) | stat: -rw-r--r-- 3,702 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
-- |
-- 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.Number.Generate (generateMax)
import Crypto.Number.ModArithmetic (inverse, inverseCoprimes)
import Crypto.Number.Prime (generatePrime)
import Crypto.PubKey.RSA.Types
import Crypto.Random.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 (p, q) size e =
    case inverse e phi of
        Nothing -> Nothing
        Just d -> Just (pub, priv d)
  where
    n = p * q
    phi = (p - 1) * (q - 1)
    -- q and p should be *distinct* *prime* numbers, hence always coprime
    qinv = inverseCoprimes q p
    pub =
        PublicKey
            { public_size = size
            , public_n = n
            , public_e = e
            }
    priv d =
        PrivateKey
            { private_pub = pub
            , private_d = d
            , private_p = p
            , private_q = q
            , private_dP = d `mod` (p - 1)
            , private_dQ = d `mod` (q - 1)
            , private_qinv = 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 size e = loop
  where
    loop = do
        -- loop until we find a valid key pair given e
        pq <- generatePQ
        case generateWith pq size e of
            Nothing -> loop
            Just pp -> return pp
    generatePQ = do
        p <- generatePrime (8 * (size `div` 2))
        q <- generateQ p
        return (p, q)
    generateQ p = do
        q <- generatePrime (8 * (size - (size `div` 2)))
        if p == q then generateQ p else return 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 n =
    (\r -> Blinder r (inverseCoprimes r n)) <$> generateMax n