Skip to content

Commit 86fd02f

Browse files
committed
Some extensions, better docs
1 parent 0a6261c commit 86fd02f

File tree

8 files changed

+853
-204
lines changed

8 files changed

+853
-204
lines changed

doc/kryptools.ipynb

Lines changed: 706 additions & 193 deletions
Large diffs are not rendered by default.

kryptools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .ec import EC_Weierstrass
77
from .factor import factorint
88
from .la import Matrix
9-
from .lat import hermite_nf, gram_schmidt, lll
9+
from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_plane_cvp, lagrange_lr, lll
1010
from .nt import cf, convergents, jacobi_symbol, sqrt_mod, euler_phi, order, carmichael_lambda
1111
from .poly import Poly
1212
from .primes import sieve_eratosthenes, isprime

kryptools/dlp_ic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
"""
44

55
from math import gcd
6-
from random import randint
6+
from random import randint, seed
77
from .primes import sieve_eratosthenes
88
from .dlp_qs import determine_factorbound, determine_trialdivison_bounds, is_smooth
9+
seed(0)
910

1011
def dlog_ic(a: int, b: int, n: int, m: int, pollard: bool = True, verbose: int = 0) -> int:
1112
"""Compute the discrete log_a(b) in Z_p of an element a of prime order m using Index Calculus."""

kryptools/dlp_qs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Discrete log solvers: Quadratic sieve
33
"""
44

5+
from math import exp, log, sqrt, gcd, isqrt
6+
from random import randint, seed
57
from .nt import sqrt_mod
68
from .primes import sieve_eratosthenes
7-
from math import exp, log, sqrt, gcd
8-
9+
seed(0)
910

1011
def determine_factorbound(n: int) -> (int, int):
1112
"""Determines the optimal factor bound and the expected number of trys until a for a given n."""

kryptools/factor_dix.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Integer factorization: Dixon's method
3+
"""
4+
5+
from math import isqrt, gcd, sqrt, log, exp, ceil
6+
from .primes import sieve_eratosthenes
7+
from .nt import legendre_symbol, sqrt_mod
8+
from .factor_qs import bytexor, byteset, bytetest
9+
10+
11+
def factor_dixon(n: int) -> list:
12+
"""Find factors of n using the method of Dixon."""
13+
# first determine the bound B for the factorbase: Choosing B=p^(1/u) Canfield-Erdös-Pomerance gives us
14+
# the expected running time |B|^2 u^u = u^(u+2) p^(2/u)/log(n). There is no explicit expression for the optimum, hence
15+
# we use Newton
16+
u = 2 * sqrt(log(n) / log(log(n))) # asymptotic value
17+
for _ in range(3):
18+
u = (2 * log(n) + u * u * (2 + log(u))) / (
19+
2 + 3 * u + 2 * u * log(u)
20+
) # Newton iteration
21+
B = int(exp(log(n) / u))
22+
23+
# B = int(exp(0.5 * sqrt( log(n) * log(log(n)) )*( 1 + 1/log(log(n)) )))
24+
factorbase = []
25+
for p in sieve_eratosthenes(B): # compute the factorbase
26+
ls = legendre_symbol(n, p)
27+
if ls == 0: # we already found a factor;-)
28+
if n == p:
29+
return n
30+
return p
31+
if ls == 1: # we only take primes such that n is quadratic residue
32+
factorbase.append(p)
33+
lf = len(factorbase) + 1 # length of the factorbase (including -1)
34+
lfb = ceil(lf / 8) # the number of bytes we need to store a relation
35+
m = isqrt(n - 1) + 1
36+
def process_relation(j: int, relation: bytes):
37+
nonlocal relation_no, values, relations
38+
relation_no += 1
39+
rhs = bytearray(b"\x00") * lfb # construct the k'th row of the identity matrix
40+
byteset(rhs, relation_no)
41+
relation += rhs # extend the relation with this row
42+
values[relation_no] = j # store the value which lead to the relation
43+
# do the Gauss elimination
44+
index = lf # this will be the index of the first nonzero entry
45+
# print(f'{j:3}', ' '.join(f'{b:08b}' for b in reversed(relation)))
46+
for i in range(lf):
47+
if bytetest(relation, i) and relations[i] != None: # make this entry zero if we can (Gauss elimination)
48+
bytexor(relation, relations[i])
49+
if bytetest(relation, i) and index == lf: # is this the index of the first nonzero entry?
50+
index = i
51+
# print(f'{j:3}', ' '.join(f'{b:08b}' for b in reversed(relation)))
52+
if index == lf: # the new relation is linearly dependent: we have found a linear combination of the 0 vector
53+
# now we need to determine the factors
54+
u = 1 # product over all values m - j such that f(m-j) is B-smooth
55+
v = 1 # sqrt of the product over all f(m-j) which are B-smooth
56+
w = 1 # save nonsquare terms for the next round
57+
for i in range(lf):
58+
if bytetest(relation, lfb * 8 + i): # select the relations which sum to the 0 vector
59+
ui = m + values[i]
60+
u = (u * ui) % n
61+
vi = ui ** 2 - n
62+
d = gcd(w, vi)
63+
v = (v * d) % n # sqrt of the part which is already square
64+
w = (w // d) * (vi // d) # this part is not square yet
65+
v = v * isqrt(w) % n
66+
res = gcd(u - v, n)
67+
if 1 < res < n:
68+
return res
69+
relation_no -= 1 # this one did not work, try again
70+
else:
71+
relations[index] = relation
72+
return None
73+
74+
relation_no = -1
75+
relations = [None] * lf # here we will store the relations in case we found a B-smooth number
76+
values = [None] * lf # here we will store the values leading to the B-smooth numbers
77+
for j in range(1,m): # loop over j=0,1,-1,2,-2,...
78+
if j & 1 == 0:
79+
j >>= 1
80+
else:
81+
j >>= 1
82+
j *= -1
83+
relation = is_smooth((j + m) ** 2 - n, factorbase, lfb) # test if f(j) is B-smooth
84+
if relation is None:
85+
continue
86+
res = process_relation(j, relation)
87+
if res:
88+
return(res)
89+
return n
90+

kryptools/factor_fmt.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Integer factorization: Fermat's method
3+
"""
4+
5+
from math import isqrt
6+
7+
8+
def factor_fermat(n: int) -> list:
9+
"""Find factors of n using the method of Fermat."""
10+
factors = []
11+
# Fermat only works if n has two factors which are either both even or both odd
12+
while n % 2 == 0:
13+
factors.append(2)
14+
n //= 2
15+
a = isqrt(n - 1) + 1
16+
step =2
17+
if n % 3 == 2: # if n % 3 = 2, then a must be a multiple of 3
18+
a += 2 - ((a - 1) % 3)
19+
step = 3
20+
elif (n % 4 == 1) ^ (a & 1): # if n % 4 = 1,3 then a must be odd, even, respectively
21+
a += 1
22+
while a <= (n + 9) // 6:
23+
b = isqrt(a * a - n)
24+
if b * b == a * a - n:
25+
factors.append(a - b)
26+
factors.append(a + b)
27+
return factors
28+
a += step
29+
factors.append(n)
30+
return factors

kryptools/lat.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@
66
from fractions import Fraction
77
from .la import Matrix, zeros, eye
88

9-
def babai_round_cvp(x: Matrix, U: Matrix) -> Matrix:
10-
"Babai's rounding algorithm for solving the CVP."
11-
s = U.inv() * x
12-
k = s.map(round)
13-
return U * k
14-
159
def hermite_nf(M: Matrix) -> Matrix:
1610
"Compute the Hermite normal form of a matrix M."
1711
n, m = M.cols, M.rows
@@ -68,13 +62,21 @@ def gram_schmidt(U: Matrix) -> (Matrix, Matrix):
6862
return Us, M
6963

7064
def gram_det(U: Matrix) -> float:
65+
"Compute the Gram determinant of a matrix."
7166
Us = gram_schmidt(U)[0]
7267
return prod([Us[:, i].norm() for i in range(U.rows)])
7368

7469
def hadamard_ratio(M: Matrix) -> float:
70+
"Compute the Hadamard ratio of a matrix."
7571
m = M.rows
7672
return (gram_det(M) / prod([M[:, i].norm() for i in range(m)])) ** (1 / m)
7773

74+
def babai_round_cvp(x: Matrix, U: Matrix) -> Matrix:
75+
"Babai's rounding algorithm for solving the CVP."
76+
s = U.inv() * x
77+
k = s.applyfunc(round)
78+
return U * k
79+
7880
def babai_plane_cvp(x: Matrix, U: Matrix) -> Matrix:
7981
"Babai's closest plane algorithm for solving the CVP."
8082
Us = gram_schmidt(U)[0]
@@ -83,6 +85,18 @@ def babai_plane_cvp(x: Matrix, U: Matrix) -> Matrix:
8385
y = y - round(y.dot(Us[:, k]) / norm2(Us[:, k])) * U[:, k]
8486
return (x - y).applyfunc(round)
8587

88+
def lagrange_lr(V: Matrix) -> Matrix:
89+
"Lagrange lattice reduction."
90+
assert (V.rows, V.cols) == (2, 2)
91+
v1, v2 = V[:, 0], V[:, 1]
92+
if norm2(v1) > norm2(v2):
93+
v1, v2 = v2, v1
94+
v3 = v2 - round(v1.dot(v2) / norm2(v1)) * v1
95+
while norm2(v3) < norm2(v1):
96+
v2, v1 = v1, v3
97+
v3 = v2 - round(v1.dot(v2) / norm2(v1)) * v1
98+
return Matrix([list(v1), list(v3)]).transpose()
99+
86100
def lll(V: Matrix, delta: float = 0.75, sort: bool = True) -> Matrix:
87101
"lll algorithm for lattice reduction"
88102

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "kryptools"
3-
version = "0.1"
3+
version = "0.2"
44
authors = [
55
{ name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
66
]

0 commit comments

Comments
 (0)