module Codec.Crypto.DSA.Pure(
ParameterSizes(..)
, Params(..)
, PublicKey(..)
, PrivateKey(..)
, Signature(..)
, DSAError(..)
, getN, getL
, generateKeyPair
, generateKeyPairWithParams
, signMessage
, verifyMessage
, HashFunction(..)
, signMessage'
, verifyMessage'
, KGenerator
, KSequence(..)
, kViaExtraRandomBits
, kViaTestingCandidates
, kViaRFC6979
, ProbablePrimesEvidence(..)
, generateProbablePrimes
, validateProbablePrimes
, ProvablePrimesEvidence(..)
, generateProvablePrimes
, validateProvablePrimes
, GenerationEvidence
, generateUnverifiableGenerator
, generatorIsValid
, generateVerifiableGenerator
, validateVerifiableGenerator
, millerRabin
, isDeterministicallyPrime
, shaweTaylor
, bs2int
, bss2int
, int2bs
, findAandM
, modExp
)
where
import Control.Exception(Exception)
import Crypto.Random
import Crypto.Types.PubKey.DSA
import Data.Bits
import Data.ByteString.Lazy(ByteString)
import qualified Data.ByteString as BSS
import qualified Data.ByteString.Lazy as BS
import Data.Digest.Pure.SHA
import Data.Either
import Data.Int
import Data.Maybe
import Data.Tagged
import Data.Typeable
import Data.Word
import Prelude hiding (length)
#if defined(USE_GMP_HELPERS)
import GHC.Integer.GMP.Internals
import GHC.Types
#endif
data ParameterSizes = L1024_N160 | L2048_N224 | L2048_N256 | L3072_N256
deriving (Eq, Show)
data DSAError = DSARandomGenerationError GenError
| DSAInvalidSeedLength
| DSAInvalidPrimeTestInput
| DSAInvalidInput
| DSAInternalInversionError
| DSAGaveUp
deriving (Eq, Show, Typeable)
instance Exception DSAError
getN :: ParameterSizes -> Integer
getN L1024_N160 = 160
getN L2048_N224 = 224
getN L2048_N256 = 256
getN L3072_N256 = 256
getL :: ParameterSizes -> Integer
getL L1024_N160 = 1024
getL L2048_N224 = 2048
getL L2048_N256 = 2048
getL L3072_N256 = 3072
generateKeyPair :: CryptoRandomGen g =>
g -> ParameterSizes ->
Either DSAError (PublicKey, PrivateKey,
ProvablePrimesEvidence, g)
generateKeyPair gen sizes =
case generateProvablePrimes sizes gen sha256' Nothing of
Left err -> Left err
Right (p, q, ev, gen') ->
case generateVerifiableGenerator p q ev 0 of
Nothing -> generateKeyPair gen' sizes
Just g ->
case generateKeyPairWithParams (Params p g q) gen' of
Left err -> Left err
Right (pub, priv, gen'') -> Right (pub, priv, ev, gen'')
where sha256' = bytestringDigest . sha256
generateKeyPairWithParams :: CryptoRandomGen g =>
Params -> g ->
Either DSAError (PublicKey, PrivateKey, g)
generateKeyPairWithParams params gen =
case genBytes ((fromIntegral bigN + 64) `div` 8) gen of
Left err -> Left (DSARandomGenerationError err)
Right (returned_bits, gen') ->
let c = bss2int returned_bits
x = (c `mod` (q 1)) + 1
y = modExp g x p
in Right (PublicKey params y, PrivateKey params x, gen')
where
bigN = intlen g
p = params_p params
g = params_g params
q = params_q params
signMessage :: PrivateKey -> ByteString -> Either DSAError Signature
signMessage priv msg =
case signMessage' SHA256 kViaRFC6979 NoGen priv msg of
Left err -> Left err
Right (res, _) -> Right res
verifyMessage :: PublicKey -> ByteString -> Signature -> Bool
verifyMessage = verifyMessage' SHA256
data HashFunction = SHA1 | SHA224 | SHA256 | SHA384 | SHA512
deriving (Eq, Show)
runHash :: HashFunction -> ByteString -> ByteString
runHash SHA1 = bytestringDigest . sha1
runHash SHA224 = bytestringDigest . sha224
runHash SHA256 = bytestringDigest . sha256
runHash SHA384 = bytestringDigest . sha384
runHash SHA512 = bytestringDigest . sha512
runHMac :: HashFunction -> ByteString -> ByteString -> ByteString
runHMac SHA1 k v = bytestringDigest (hmacSha1 k v)
runHMac SHA224 k v = bytestringDigest (hmacSha224 k v)
runHMac SHA256 k v = bytestringDigest (hmacSha256 k v)
runHMac SHA384 k v = bytestringDigest (hmacSha384 k v)
runHMac SHA512 k v = bytestringDigest (hmacSha512 k v)
getHashLength :: HashFunction -> Int64
getHashLength hash = BS.length (runHash hash BS.empty)
signMessage' :: CryptoRandomGen g =>
HashFunction -> KGenerator g -> g ->
PrivateKey -> ByteString ->
Either DSAError (Signature, g)
signMessage' hash genMeth gen privkey msg = loop kseq
where
params = private_params privkey
p = params_p params
q = params_q params
g = params_g params
x = private_x privkey
bigN = fromIntegral (intlen q)
outlen = getHashLength hash
kseq = genMeth gen hash privkey msg
loop (KFailure err) = Left err
loop (KValue k gen' next)
| isNothing kinvres = Left DSAInternalInversionError
| (r == 0) || (s == 0) = loop next
| otherwise = Right (Signature r s, gen')
where
r = (modExp g k p) `mod` q
z = bs2int (BS.take (min bigN outlen) (runHash hash msg))
s = (kinv * (z + (x * r))) `mod` q
kinvres = modInv k q
Just kinv = kinvres
verifyMessage' :: HashFunction -> PublicKey -> ByteString -> Signature -> Bool
verifyMessage' hash pubkey msg sig
| ((r' <= 0) || (r' >= q)) = False
| ((s' <= 0) || (s' >= q)) = False
| isNothing mw = False
| otherwise = v == r'
where
r' = sign_r sig
s' = sign_s sig
p = params_p (public_params pubkey)
q = params_q (public_params pubkey)
g = params_g (public_params pubkey)
y = public_y pubkey
bigN = fromIntegral (intlen q)
outlen = BS.length (runHash hash BS.empty)
mw = modInv s' q
w = fromJust mw
z = bs2int (BS.take (min bigN outlen) (runHash hash msg))
u1 = (z * w) `mod` q
u2 = (r' * w) `mod` q
v = (((modExp g u1 p) * (modExp y u2 p)) `mod` p) `mod` q
type KGenerator g = g -> HashFunction ->
PrivateKey -> ByteString ->
KSequence g
data CryptoRandomGen g => KSequence g = KValue Integer g (KSequence g)
| KFailure DSAError
kViaExtraRandomBits :: CryptoRandomGen g => KGenerator g
kViaExtraRandomBits g hash privkey msg
| isLeft randres = KFailure (DSARandomGenerationError err)
| otherwise = KValue k g' (kViaExtraRandomBits g' hash privkey msg)
where
q = params_q (private_params privkey)
bigN = intlen q
randres = genBytes (fromIntegral bigN + 64) g
Left err = randres
Right (returned_bits, g') = randres
c = bss2int returned_bits
k = (c `mod` (q 1)) + 1
kViaTestingCandidates :: CryptoRandomGen g => KGenerator g
kViaTestingCandidates g hash privkey msg
| isLeft randres = KFailure (DSARandomGenerationError err)
| c > (q 2) = kViaTestingCandidates g' hash privkey msg
| otherwise = KValue k g' (kViaTestingCandidates g' hash privkey msg)
where
params = private_params privkey
q = params_q params
bigN = intlen q
randres = genBytes (fromIntegral bigN) g
Left err = randres
Right (returned_bits, g') = randres
c = bss2int returned_bits
k = c + 1
kViaRFC6979 :: CryptoRandomGen g => KGenerator g
kViaRFC6979 g hash privkey msg = loop bigK_2 bigV_2
where
x = private_x privkey
q = params_q (private_params privkey)
qlen = fromInteger (intlen q)
h1 = runHash hash msg
hlen = BS.length h1
bigV_0 = BS.replicate hlen 1
bigK_0 = BS.replicate hlen 0
bigK_1 = runHMac hash bigK_0 (BS.concat [bigV_0, BS.singleton 0,
int2octets x, bits2octets h1])
bigV_1 = runHMac hash bigK_1 bigV_0
bigK_2 = runHMac hash bigK_1 (BS.concat [bigV_1, BS.singleton 1,
int2octets x, bits2octets h1])
bigV_2 = runHMac hash bigK_2 bigV_1
buildT bigK bigV bigT | BS.length bigT >= qlen = (bigV, bits2int bigT)
| otherwise = buildT bigK bigV' bigT'
where
bigV' = runHMac hash bigK bigV
bigT' = bigT `BS.append` bigV'
loop bigK bigV | (1 <= k) && (k <= (q 1)) = KValue k g (loop bigK' bigV'')
| otherwise = loop bigK' bigV''
where
(bigV', k) = buildT bigK bigV BS.empty
bigK' = runHMac hash bigK (bigV' `BS.append` BS.singleton 0)
bigV'' = runHMac hash bigK' bigV'
bitlen :: Integer -> Int
bitlen y = go y 0
where
go 0 acc = acc
go v acc = go (v `shiftR` 1) (acc + 1)
bits2int :: ByteString -> Integer
bits2int bstr | qbtlen < blen = value `shiftR` (blen qbtlen)
| otherwise = value
where
blen = fromIntegral (BS.length bstr * 8)
qbtlen = bitlen q
value = bs2int bstr
bits2octets :: ByteString -> ByteString
bits2octets bstr = BS.replicate (qlen BS.length res) 0 `BS.append` res
where
res = int2bs (z1 `mod` q)
z1 = bits2int bstr
int2octets :: Integer -> ByteString
int2octets y
| BS.length out < qlen = padding `BS.append` out
| BS.length out > qlen = BS.drop (BS.length out qlen) out
| otherwise = out
where
out = int2bs y
padding = BS.replicate (qlen BS.length out) 0
data ProbablePrimesEvidence = ProbablePrimesEvidence {
prpeDomainParameterSeed :: ByteString
, prpeCounter :: Integer
, prpeHash :: ByteString -> ByteString
}
generateProbablePrimes :: CryptoRandomGen g =>
ParameterSizes ->
g ->
(ByteString -> ByteString) ->
Maybe Integer ->
Either DSAError (Integer,Integer,
ProbablePrimesEvidence,
g)
generateProbablePrimes dsaParam gen hash Nothing =
generateProbablePrimes dsaParam gen hash (Just (getN dsaParam + 8))
generateProbablePrimes dsaParam gen hash (Just seedlen)
| seedlen < getN dsaParam = Left DSAInvalidSeedLength
| seedlen `mod` 8 /= 0 = Left DSAInvalidSeedLength
| otherwise = find_q gen
where
outlenB = fromIntegral (BS.length (hash BS.empty))
outlen = outlenB * 8
outlenF = fromInteger outlen :: Double
bigL = fromIntegral (getL dsaParam) :: Integer
bigN = fromIntegral (getN dsaParam) :: Integer
n = ceiling (fromInteger bigL / outlenF) 1
b = bigL 1 (n * outlen)
find_q g'
| isLeft dpsEth = Left (DSARandomGenerationError err)
| isLeft primeEth = Left primeErr
| isPrime = find_p g''' 1 0 q domParamSeed
| otherwise = find_q g'''
where
dpsEth = genBytes (fromIntegral ((seedlen + 7) `div` 8)) g'
Left err = dpsEth
Right (dpsBS, g'') = dpsEth
domParamSeed = BS.fromStrict dpsBS
mask = 2 ^ (bigN 1)
bigU = bs2int (hash domParamSeed) `mod` mask
q = mask + bigU + 1 (bigU `mod` 2)
primeEth = isPrimeC3 g'' dsaParam q
Left primeErr = primeEth
Right (isPrime, g''') = primeEth
find_p g' !off !ctr !q !dpsBS
| ctr == fourTimesL = find_q g'
| p < twoLm1 = find_p g' off' ctr' q dpsBS
| isLeft primeEth = Left primeErr
| isPrime = let ev = ProbablePrimesEvidence dpsBS ctr hash
in Right (p, q, ev, g'')
| otherwise = find_p g'' off' ctr' q dpsBS
where
dps = bs2int (dpsBS :: ByteString) :: Integer
!bigW = computeW hash dps off n b seedlen
bigX = bigW + (2 ^ (bigL 1))
c = bigX `mod` (2 * q)
p = bigX (c 1)
primeEth = isPrimeC3 g' dsaParam p
Left primeErr = primeEth
Right (isPrime, g'') = primeEth
off' = off + n + 1
ctr' = ctr + 1
fourTimesL = 4 * bigL
twoLm1 = 2 ^ (bigL 1)
computeW :: (ByteString -> ByteString) ->
Integer -> Integer -> Integer -> Integer -> Integer ->
Integer
computeW hash dps offset n b seedlen = loop 0 BS.empty
where
loop j acc | j == n = bs2int (vj' `BS.append` acc)
| otherwise = loop (j + 1) (vj `BS.append` acc)
where
vj = hash (int2bs ((dps + offset + j) `mod` (2 ^ seedlen)))
vj' = int2bs (bs2int vj `mod` (2 ^ b))
validateProbablePrimes :: CryptoRandomGen g =>
g ->
Integer ->
Integer ->
ProbablePrimesEvidence ->
(Bool, g)
validateProbablePrimes g p q (ProbablePrimesEvidence dps counter hash) =
if | not goodParam -> (False, g)
| counter > ((4 * bigL) 1) -> (False, g)
| seedlen < bigN -> (False, g)
| computed_q /= q -> (False, g)
| not computed_q_prime -> (False, g')
| otherwise -> counter_right
where
bigL = intlen p * 8
bigN = intlen q * 8
(param, goodParam) =
case (bigL, bigN) of
(1024, 160) -> (L1024_N160, True)
(2048, 224) -> (L2048_N224, True)
(2048, 256) -> (L2048_N256, True)
(3072, 256) -> (L3072_N256, True)
_ -> ((error ("PARAM: "++show bigL++" "++show bigN)), False)
seedlen = fromIntegral (BS.length dps * 8)
bigU = bs2int (hash dps) `mod` (2 ^ (bigN 1))
computed_q = (2 ^ (bigN 1)) + bigU + 1 (bigU `mod` 2)
(computed_q_prime, g') = case isPrimeC3 g param computed_q of
Left _ -> (False, g)
Right x -> x
outlenB = fromIntegral (BS.length (hash BS.empty))
outlen = outlenB * 8
outlenF = fromInteger outlen :: Double
n = ceiling (fromInteger bigL / outlenF) 1
b = bigL 1 (n * outlen)
offset = 1
counter_right = loop g' 0 offset
loop gen !i !off
| isLeft primeEth = (False, gen)
| i == counter = step14 gen i computed_p isPrime
| computed_p < (2 ^ (bigL 1)) = loop gen (i + 1) off'
| isPrime = step14 gen i computed_p isPrime
| otherwise = loop gen' (i + 1) off'
where
bigW = computeW hash (bs2int dps) off n b seedlen
bigX = bigW + (2 ^ (bigL 1))
c = bigX `mod` (2 * q)
computed_p = bigX (c 1)
primeEth = isPrimeC3 gen param computed_p
Right (isPrime, gen') = primeEth
off' = off + n + 1
step14 gen i computed_p isPrime = (res, gen)
where res = (i == counter) && (computed_p == p) && isPrime
data ProvablePrimesEvidence = ProvablePrimesEvidence {
pvpeFirstSeed :: Integer
, pvpePSeed :: Integer
, pvpeQSeed :: Integer
, pvpePGenCounter :: Integer
, pvpeQGenCounter :: Integer
, pvpeHash :: ByteString -> ByteString
}
instance Eq ProvablePrimesEvidence where
ev1 == ev2 = (pvpeFirstSeed ev1 == pvpeFirstSeed ev2) &&
(pvpePSeed ev1 == pvpePSeed ev2) &&
(pvpeQSeed ev1 == pvpeQSeed ev2) &&
(pvpePGenCounter ev1 == pvpePGenCounter ev2) &&
(pvpeQGenCounter ev1 == pvpeQGenCounter ev2)
generateProvablePrimes :: CryptoRandomGen g =>
ParameterSizes ->
g ->
(ByteString -> ByteString) ->
Maybe Integer ->
Either DSAError (Integer, Integer,
ProvablePrimesEvidence, g)
generateProvablePrimes params g hash Nothing =
generateProvablePrimes params g hash (Just (getN params))
generateProvablePrimes params g hash (Just seedlen)
| seedlen < bigN = Left DSAInvalidSeedLength
| seedlen `mod` 8 /= 0 = Left DSAInvalidSeedLength
| isLeft mfirstseed = reLeft mfirstseed
| otherwise =
case constructivePrimeGen hash bigL bigN firstseed of
Left DSAGaveUp -> generateProvablePrimes params g' hash (Just seedlen)
Left err -> Left err
Right (p,q,ev) -> Right (p,q,ev,g')
where
bigN = getN params :: Integer
bigL = getL params :: Integer
twonm1 = 2 ^ (bigN 1)
mfirstseed = getFirstSeed g 0
Right (firstseed, g') = getFirstSeed g 0
getFirstSeed gen first_seed
| first_seed >= twonm1 = Right (first_seed, gen)
| otherwise =
case genBytes (fromIntegral (bigN `div` 8)) gen of
Left err -> Left (DSARandomGenerationError err)
Right (bytes, gen') -> getFirstSeed gen' (bss2int bytes)
constructivePrimeGen :: (ByteString -> ByteString) ->
Integer -> Integer -> Integer ->
Either DSAError (Integer,Integer,ProvablePrimesEvidence)
constructivePrimeGen hash bigL bigN firstseed
| isLeft mqseed = reLeft mqseed
| isLeft mpseed = reLeft mpseed
| otherwise = runCheck pgen_counter pseed' t0
where
outlenF = fromIntegral (BS.length (hash BS.empty)) * (8.0 :: Double)
mqseed = shaweTaylor hash bigN firstseed
mpseed = shaweTaylor hash ((bigL `div` 2) + 1) qseed
Right (q, qseed, qgen_counter) = mqseed
Right (p0, pseed, pgen_counter) = mpseed
iterations = ceiling (fromInteger bigL / outlenF) 1
old_counter = pgen_counter
x = bs2int (BS.concat (map (\ i -> hash (int2bs (pseed + i)))
(reverse [0..iterations])))
pseed' = pseed + iterations + 1
x' = (2 ^ (bigL 1)) + (x `mod` (2 ^ (bigL 1)))
t0 = ceiling (fromInteger x' /
((2.0 :: Double) * fromInteger q * fromInteger p0))
runCheck pgc ps t
| (1 == gcd (z 1) p) && (1 == modExp z p0 p) =
let ev = ProvablePrimesEvidence firstseed ps' qseed
pgc' qgen_counter hash
in Right (p, q, ev)
| pgc' > ((4 * bigL) + old_counter) =
Left DSAGaveUp
| otherwise =
runCheck pgc' ps' (t + 1)
where
t' | (2 * t * q * p0) + 1 > (2 ^ bigL) =
ceiling (((2.0 :: Double) ^ (bigL 1)) /
((2.0 :: Double) * fromInteger q * fromInteger p0))
| otherwise = t
p = (2 * t' * q * p0) + 1
pgc' = pgc + 1
a = bs2int (BS.concat (map (\ i -> hash (int2bs (pseed + i)))
(reverse [0..iterations])))
ps' = ps + iterations + 1
a' = 2 + (a `mod` (p 3))
z = modExp a' (2 * t' * q) p
reLeft :: Either a b -> Either a c
reLeft (Left a) = Left a
reLeft (Right _) = error "Re-left of a Right value"
validateProvablePrimes :: Integer -> Integer ->
ProvablePrimesEvidence ->
Bool
validateProvablePrimes p q ev =
((bigL, bigN) `elem` [(1024,160),(2048,224),(2048,256),(3072,256)]) &&
(pvpeFirstSeed ev >= (2 ^ (bigN 1))) &&
((2 ^ bigN) > q) &&
((2 ^ bigL) > p) &&
((p 1) `mod` q == 0) &&
isRight mres && (p == p') && (q == q') && (ev == ev')
where
bigL = intlen p * 8
bigN = intlen q * 8
hash = pvpeHash ev
mres = constructivePrimeGen hash bigL bigN (pvpeFirstSeed ev)
Right (p', q', ev') = mres
generateUnverifiableGenerator :: Integer -> Integer -> Maybe Integer
generateUnverifiableGenerator p q = loop 2
where
e = (p 1) `div` q
loop h | h >= (p 1) = Nothing
| g == 1 = loop (h + 1)
| otherwise = Just g
where g = modExp h e p
generatorIsValid :: Integer -> Integer ->
Integer ->
Bool
generatorIsValid p q g = rangeOK && modOK
where
rangeOK = (2 <= g) && (g <= (p 1))
modOK = modExp g q p == 1
class GenerationEvidence a where
getHash :: a -> (ByteString -> ByteString)
getDomainParameterSeed :: a -> ByteString
instance GenerationEvidence ProbablePrimesEvidence where
getHash = prpeHash
getDomainParameterSeed = prpeDomainParameterSeed
instance GenerationEvidence ProvablePrimesEvidence where
getHash = pvpeHash
getDomainParameterSeed e = BS.concat [firstSeed, pseed, qseed]
where
firstSeed = int2bs (pvpeFirstSeed e)
pseed = int2bs (pvpePSeed e)
qseed = int2bs (pvpeQSeed e)
generateVerifiableGenerator :: GenerationEvidence ev =>
Integer -> Integer ->
ev ->
Word8 ->
Maybe Integer
generateVerifiableGenerator p q ev index = loop (1 :: Word16)
where
e = (p 1) `div` q
indexBS = BS.singleton index
ggen = int2bs 0x6767656e
loop count | count == 0 = Nothing
| g < 2 = loop (count + 1)
| otherwise = Just g
where
countBS = BS.pack [fromIntegral (count `shiftR` 8), fromIntegral (count .&. 0xFF)]
bigU = getDomainParameterSeed ev `BS.append` ggen `BS.append` indexBS `BS.append` countBS
bigW = bs2int (getHash ev bigU)
g = modExp bigW e p
validateVerifiableGenerator :: GenerationEvidence ev =>
Integer -> Integer ->
ev ->
Word8 ->
Integer ->
Bool
validateVerifiableGenerator p q ev index g = rangeOK && modOK && genOK
where
rangeOK = (2 <= g) && (g <= (p 1))
modOK = modExp g q p == 1
genOK = case generateVerifiableGenerator p q ev index of
Nothing -> False
Just computed_g -> computed_g == g
isPrimeC3 :: CryptoRandomGen g =>
g -> ParameterSizes -> Integer ->
Either DSAError (Bool, g)
isPrimeC3 g L1024_N160 !x = millerRabin g 40 x
isPrimeC3 g L2048_N224 !x = millerRabin g 56 x
isPrimeC3 g L2048_N256 !x = millerRabin g 56 x
isPrimeC3 g L3072_N256 !x = millerRabin g 64 x
millerRabin :: CryptoRandomGen g =>
g -> Int -> Integer ->
Either DSAError (Bool, g)
#if defined(USE_GMP_HELPERS)
millerRabin gen (I# its) w
| w == 1 = Right (False, gen)
| w == 2 = Right (True, gen)
| even w = Left DSAInvalidPrimeTestInput
| otherwise =
case testPrimeInteger w its of
0# -> Right (False, gen)
_ -> Right (True, gen)
#else
millerRabin gen iterations w
| w == 0 = Left DSAInvalidPrimeTestInput
| w == 1 = Right (False, gen)
| w == 2 = Right (True, gen)
| w == 3 = Right (True, gen)
| even w = Left DSAInvalidPrimeTestInput
| otherwise = result
where
(a, m) = findAandM (w 1)
wlen = intlen w
result = go gen iterations
go g 0 = Right (True, g)
go g count
| isLeft genEth = Left (DSARandomGenerationError err)
| ((b <= 1) || (b >= w 1)) = go g' count
| ((z == 1) || (z == w 1)) = go g' (count 1)
| otherwise = step45loop g' count z 1
where
genEth = genBytes (fromIntegral wlen) g
Left err = genEth
Right (bstr, g') = genEth
b = bss2int bstr
z = modExp b m w
step45loop g count !z !j | j == a = Right (False, g)
| z' == (w 1) = go g (count 1)
| z' == 1 = Right (False, g)
| otherwise = step45loop g count z' (j + 1)
where z' = modExp z 2 w
#endif
bss2int :: BSS.ByteString -> Integer
bss2int bstr = go 0 (BSS.unpack bstr)
where
go acc [] = acc
go acc (h:t) = go ((acc `shiftL` 8) + fromIntegral h) t
modExp :: Integer -> Integer -> Integer -> Integer
#if defined(USE_GMP_HELPERS)
modExp !x !y !m = powModInteger x y m
#else
modExp !x !y !m = go x y 1
where
go _ 0 !result = result
go !b !e !result = go ((b * b) `mod` m) (e `shiftR` 1) result'
where result' = if testBit e 0 then (result * b) `mod` m else result
#endif
modInv :: Integer -> Integer -> Maybe Integer
modInv !z !a = loop a z 0 1
where
loop i j y2 y1 | j' > 0 = loop i' j' y2' y1'
| i' /= 1 = Nothing
| otherwise = Just (y2' `mod` a)
where
quotient = i `div` j
remainder = i (j * quotient)
y = y2 (y1 * quotient)
i' = j
j' = remainder
y2' = y1
y1' = y
xorbs :: ByteString -> ByteString -> ByteString
xorbs a b = BS.pack (BS.zipWith xor a b)
findAandM :: Integer -> (Integer, Integer)
findAandM x = go 0 x
where
go acc v | even v = go (acc + 1) (v `div` 2)
| otherwise = (acc, v)
intlen :: Integer -> Integer
intlen 0 = 0
intlen x = intlen (x `shiftR` 8) + 1
bs2int :: ByteString -> Integer
bs2int bstr = go 0 (BS.unpack bstr)
where
go acc [] = acc
go acc (h:t) = go ((acc `shiftL` 8) + fromIntegral h) t
int2bs :: Integer -> ByteString
int2bs x
| x < 0 = error "int2bs: negative input"
| x == 0 = BS.empty
| otherwise = int2bs (x `shiftR` 8) `BS.append`
BS.singleton (fromIntegral (x .&. 0xFF))
shaweTaylor :: (ByteString -> ByteString) -> Integer -> Integer ->
Either DSAError (Integer, Integer, Integer)
shaweTaylor hash length input_seed
| length < 2 = Left DSAInvalidInput
| length >= 33 = largeVersion
| otherwise = smallVersion input_seed 0
where
smallVersion prime_seed prime_gen_counter
| isDeterministicallyPrime c7 = Right (c7, prime_seed, prime_gen_counter)
| prime_gen_counter > (4 * length) = Left DSAGaveUp
| otherwise = smallVersion ps' pgc'
where
c5 = bs2int ((hash (int2bs prime_seed)) `xorbs`
(hash (int2bs (prime_seed + 1))))
c6 = (2 ^ (length 1)) + (c5 `mod` (2 ^ (length 1)))
c7 = (2 * floor (fromInteger c6 / (2.0 :: Double))) + 1
pgc' = prime_gen_counter + 1
ps' = prime_seed + 2
largeVersion
| isLeft mstatus = reLeft mstatus
| otherwise = findLoop prime_gen_counter prime_seed' t0
where
outlenF = fromIntegral (BS.length (hash BS.empty)) * (8.0 :: Double)
ceildiv = ceiling (fromInteger length / (2 :: Double)) + 1
mstatus = shaweTaylor hash ceildiv input_seed
Right (c0, prime_seed, prime_gen_counter) = mstatus
iterations = ceiling (fromInteger length / outlenF) 1
old_counter = prime_gen_counter
x = bs2int (BS.concat (map (\ i -> hash (int2bs (prime_seed + i)))
(reverse [0..iterations])))
prime_seed' = prime_seed + iterations + 1
x' = (2 ^ (length 1)) + (x `mod` (2 ^ (length 1)))
t0 = ceiling (fromInteger x' / ((2.0 :: Double) * fromInteger c0))
findLoop pgc ps !t
| (1 == gcd (z 1) c) && (1 == modExp z c0 c) =
Right (c, ps', pgc')
| pgc' >= ((4 * length) + old_counter) =
Left DSAGaveUp
| otherwise =
findLoop pgc' ps' (t' + 1)
where
t' | ((2 * t * c0) + 1) > (2 ^ length) =
ceiling (((2 :: Double) ^ (length 1)) /
((2.0 :: Double) * fromInteger c0))
| otherwise = t
c = 2 * t * c0 + 1
pgc' = pgc + 1
a = bs2int (BS.concat (map (\ i -> hash (int2bs (ps + i)))
(reverse [0..iterations])))
ps' = ps + iterations + 1
a' = 2 + (a `mod` (c 3))
z = modExp a' (2 * t) c
isDeterministicallyPrime :: Integer -> Bool
isDeterministicallyPrime !x
| x <= 1 = False
| x == 2 = True
| even x = False
| otherwise = go 2
where
final = ceiling (sqrt (fromInteger x :: Double))
go !d | d > final = True
| x `mod` d == 0 = False
| otherwise = go (nextDivisor d)
nextDivisor 2 = 3
nextDivisor 3 = 5
nextDivisor 5 = 7
nextDivisor d | d' `mod` 3 == 0 = nextDivisor (d + 2)
| d' `mod` 5 == 0 = nextDivisor (d + 2)
| otherwise = d'
where d' = d + 2
data NoGen = NoGen
instance CryptoRandomGen NoGen where
newGen _ = Left NotEnoughEntropy
genSeedLength = Tagged 0
genBytes _ _ = Left NotEnoughEntropy
reseedInfo _ = Never
reseedPeriod _ = Never
reseed _ _ = Left NotEnoughEntropy