-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discrete log #130
Discrete log #130
Changes from 38 commits
df50d98
e4f6fe7
1747210
73e79af
b81e174
75f4371
31abbf7
de81a94
d8e45bc
ac3d84d
3d35432
56b33ed
6423c6a
8de36ca
e99b29c
ec5fa71
a6a1c1f
04a85cc
04119fe
6dffb74
90102bb
938d74f
0d78259
72239de
7251bf6
e77edcc
eaa5456
b13f650
36edecc
dbe983a
08d2481
6df55dd
6ec31ee
d9953ec
3942bcc
ed77e91
2ddd7e7
f2386ab
ac1e46a
1393fdf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
-- | | ||
-- Module: Math.NumberTheory.Moduli.DiscreteLogarithm | ||
-- Copyright: (c) 2018 Bhavik Mehta | ||
-- License: MIT | ||
-- Maintainer: Andrew Lelechenko <[email protected]> | ||
-- Stability: Provisional | ||
-- Portability: Non-portable | ||
-- | ||
|
||
{-# LANGUAGE ViewPatterns #-} | ||
{-# LANGUAGE BangPatterns #-} | ||
|
||
module Math.NumberTheory.Moduli.DiscreteLogarithm where | ||
|
||
import qualified Data.IntMap.Strict as M | ||
import Data.Maybe (maybeToList) | ||
import Numeric.Natural (Natural) | ||
import GHC.Integer.GMP.Internals (recipModInteger, powModInteger) | ||
|
||
import Math.NumberTheory.Moduli.Chinese (chineseRemainder2) | ||
import Math.NumberTheory.Moduli.Class (KnownNat, MultMod(..), Mod, getVal) | ||
import Math.NumberTheory.Moduli.Equations (solveLinear') | ||
import Math.NumberTheory.Moduli.PrimitiveRoot (PrimitiveRoot(..), CyclicGroup(..)) | ||
import Math.NumberTheory.Powers.Squares (integerSquareRoot) | ||
import Math.NumberTheory.UniqueFactorisation (unPrime) | ||
|
||
-- | Computes the discrete logarithm. Currently uses the baby-step giant-step method with Bach reduction. | ||
discreteLogarithm :: KnownNat m => PrimitiveRoot m -> MultMod m -> Natural | ||
discreteLogarithm a b = discreteLogarithm' (getGroup a) (multElement $ unPrimitiveRoot a) (multElement b) | ||
|
||
discreteLogarithm' | ||
:: KnownNat m | ||
=> CyclicGroup Natural -- ^ group structure (must be the multiplicative group mod m) | ||
-> Mod m -- ^ a | ||
-> Mod m -- ^ b | ||
-> Natural -- ^ result | ||
discreteLogarithm' cg a b = | ||
case cg of | ||
CG2 -> 0 | ||
-- the only valid input was a=1, b=1 | ||
CG4 -> if b == 1 then 0 else 1 | ||
-- the only possible input here is a=3 with b = 1 or 3 | ||
CGOddPrimePower (unPrime -> p) k -> discreteLogarithmPP p k (getVal a) (getVal b) | ||
CGDoubleOddPrimePower (unPrime -> p) k -> discreteLogarithmPP p k (getVal a `rem` p^k) (getVal b `rem` p^k) | ||
-- we have the isomorphism t -> t `rem` p^k from (Z/2p^kZ)* -> (Z/p^kZ)* | ||
|
||
-- Implementation of Bach reduction (https://www2.eecs.berkeley.edu/Pubs/TechRpts/1984/CSD-84-186.pdf) | ||
{-# INLINE discreteLogarithmPP #-} | ||
discreteLogarithmPP :: Integer -> Word -> Integer -> Integer -> Natural | ||
discreteLogarithmPP p 1 a b = discreteLogarithmPrime p a b | ||
discreteLogarithmPP p k a b = fromInteger result | ||
where | ||
baseSol = toInteger $ discreteLogarithmPrime p (a `rem` p) (b `rem` p) | ||
thetaA = theta p pkMinusOne a | ||
thetaB = theta p pkMinusOne b | ||
pkMinusOne = p^(k-1) | ||
c = (recipModInteger thetaA pkMinusOne * thetaB) `rem` pkMinusOne | ||
result = chineseRemainder2 (baseSol, p-1) (c, pkMinusOne) | ||
|
||
-- compute the homomorphism theta given in https://math.stackexchange.com/a/1864495/418148 | ||
{-# INLINE theta #-} | ||
theta :: Integer -> Integer -> Integer -> Integer | ||
theta p pkMinusOne a = (numerator `quot` pk) `rem` pkMinusOne | ||
where | ||
pk = pkMinusOne * p | ||
p2kMinusOne = pkMinusOne * pk | ||
numerator = (powModInteger a (pk - pkMinusOne) p2kMinusOne - 1) `rem` p2kMinusOne | ||
|
||
discreteLogarithmPrime :: Integer -> Integer -> Integer -> Natural | ||
discreteLogarithmPrime p a b | ||
| p < 100000000 = fromIntegral $ discreteLogarithmPrimeBSGS (fromInteger p) (fromInteger a) (fromInteger b) | ||
| otherwise = discreteLogarithmPrimePollard p a b | ||
|
||
discreteLogarithmPrimeBSGS :: Int -> Int -> Int -> Int | ||
discreteLogarithmPrimeBSGS p a b = head [i*m + j | (v,i) <- zip giants [0..m-1], j <- maybeToList (M.lookup v table)] | ||
where | ||
m = integerSquareRoot (p - 2) + 1 -- simple way of ceiling (sqrt (p-1)) | ||
babies = iterate (.* a) 1 | ||
table = M.fromList (zip babies [0..m-1]) | ||
aInv = recipModInteger (toInteger a) (toInteger p) | ||
bigGiant = fromInteger $ powModInteger aInv (toInteger m) (toInteger p) | ||
giants = iterate (.* bigGiant) b | ||
x .* y = x * y `rem` p | ||
|
||
discreteLogarithmPrimePollard :: Integer -> Integer -> Integer -> Natural | ||
discreteLogarithmPrimePollard p a b = | ||
case concatMap runPollard [(0,0),(0,1),(1,1)] of | ||
(t:_) -> fromInteger t | ||
[] -> error ("discreteLogarithm: pollard's rho failed, please report this as a bug. inputs " ++ show [p,a,b]) | ||
where | ||
n = p-1 -- order of the cyclic group | ||
halfN = n `quot` 2 | ||
mul2 m = if m < halfN then m * 2 else m * 2 - n | ||
step (xi,!ai,!bi) = case xi `rem` 3 of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each
case fromInteger xi .&. (7 :: Word) of
0 -> (xi*xi `rem` p, mul2 ai, mul2 bi)
1 -> (xi*xi `rem` p, mul2 ai, mul2 bi)
2 -> ( a*xi `rem` p, ai+1, bi)
3 -> ( a*xi `rem` p, ai+1, bi)
4 -> ( a*xi `rem` p, ai+1, bi)
_ -> ( b*xi `rem` p, ai, bi+1) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this idea! I recall reading that the expected time taken for Pollard can be improved to the expected random bound by using more partitions - I'm sure I read that tighter bounds were achieved with 20 but I can't seem to find that result now. With that in mind, it may be interesting to do something like In either case, would it be acceptable to take optimisation work like this into a different PR, and merge (something like) this one for now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like f_T and f_C from (Speeding up pollard's rho method for discrete logarithms, Teske 1998), also described in 2.2.2 and 2.2.3 here, in which experimental performance bounds are described. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sure. Could you please add a |
||
0 -> (xi*xi `rem` p, mul2 ai, mul2 bi) | ||
1 -> ( a*xi `rem` p, ai+1, bi) | ||
_ -> ( b*xi `rem` p, ai, bi+1) | ||
initialise (x,y) = (powModInteger a x n * powModInteger b y n `rem` n, x, y) | ||
begin t = go (step t) (step (step t)) | ||
check t = powModInteger a t p == b | ||
go tort@(xi,ai,bi) hare@(x2i,a2i,b2i) | ||
| xi == x2i = solveLinear' n (bi - b2i) (ai - a2i) | ||
| otherwise = go (step tort) (step (step hare)) | ||
runPollard = filter check . begin . initialise |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
|
||
module Math.NumberTheory.Moduli.Equations | ||
( solveLinear | ||
, solveLinear' | ||
, solveQuadratic | ||
) where | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been never good with names :)
Maybe
MultGroupMod
?