Ryan Rueger

ryan@rueg.re / picture / key / home
aboutsummaryrefslogtreecommitdiffhomepage
path: root/theta_lib/utilities/discrete_log.py
diff options
context:
space:
mode:
authorPierrick-Dartois <pierrickdartois@icloud.com>2025-05-22 18:51:58 +0200
committerPierrick-Dartois <pierrickdartois@icloud.com>2025-05-22 18:51:58 +0200
commitcb6080eaa4f326d9fce5f0a9157be46e91d55e09 (patch)
tree4d080ade8db9faa0da5268ab420dad2b02a4e248 /theta_lib/utilities/discrete_log.py
parentd40de259097c5e8d8fd35539560ca7c3d47523e7 (diff)
downloadpegasis-cb6080eaa4f326d9fce5f0a9157be46e91d55e09.tar.gz
pegasis-cb6080eaa4f326d9fce5f0a9157be46e91d55e09.tar.bz2
pegasis-cb6080eaa4f326d9fce5f0a9157be46e91d55e09.zip
Clean up PEGASIS submodule inclusion
Diffstat (limited to 'theta_lib/utilities/discrete_log.py')
-rw-r--r--theta_lib/utilities/discrete_log.py273
1 files changed, 0 insertions, 273 deletions
diff --git a/theta_lib/utilities/discrete_log.py b/theta_lib/utilities/discrete_log.py
deleted file mode 100644
index dff04dc..0000000
--- a/theta_lib/utilities/discrete_log.py
+++ /dev/null
@@ -1,273 +0,0 @@
-# ===================================== #
-# Fast DLP solving using Weil pairing #
-# ===================================== #
-
-"""
-This code has been taken from:
-https://github.com/FESTA-PKE/FESTA-SageMath
-
-Copyright (c) 2023 Andrea Basso, Luciano Maino and Giacomo Pope.
-"""
-
-
-
-# Sage imports
-from sage.all import (
- ZZ
-)
-
-# import pari for fast dlog
-import cypari2
-
-# Make instance of Pari
-pari = cypari2.Pari()
-
-def discrete_log_pari(a, base, order):
- """
- Wrapper around pari discrete log. Works like a.log(b),
- but allows us to use the optional argument order. This
- is important as we skip Pari attempting to factor the
- full order of Fp^k, which is slow.
- """
- x = pari.fflog(a, base, order)
- return ZZ(x)
-
-def ell_discrete_log_pari(E,P,Base,order):
- """
- Wrapper around pari elllog function to compute discrete
- logarithms in elliptic curves over finite fields.
- Works like P.log(Base).
- """
- x = pari.elllog(E,P,Base,order)
- return ZZ(x)
-
-def weil_pairing_pari(P, Q, D, check=False):
- """
- Wrapper around Pari's implementation of the Weil pairing
- Allows the check of whether P,Q are in E[D] to be optional
- """
- if check:
- nP, nQ = D*P, D*Q
- if nP.is_zero() or nQ.is_zero():
- raise ValueError("points must both be n-torsion")
-
- return pari.ellweilpairing(P.curve(), P, Q, D)
-
-def tate_pairing_pari(P, Q, D):
- """
- Wrapper around Pari's implementation of the Tate pairing
- NOTE: this is not the reduced Tate pairing, so to make this
- match with SageMath you need
-
- P.tate_pairing(Q, D, k) == pari.elltatepairing(E, P, Q, D)**((p^k - 1) / D)
- """
- E = P.curve()
- return pari.elltatepairing(E, P, Q, D)
-
-def _precompute_baby_steps(base, step, e):
- """
- Helper function to compute the baby steps for
- pohlig_hellman_base and windowed_pohlig_hellman.
- """
- baby_steps = [base]
- for _ in range(e):
- base = base**step
- baby_steps.append(base)
- return baby_steps
-
-def pohlig_hellman_base(a, base, e):
- """
- Solve the discrete log for a = base^x for
- elements base,a of order 2^e using the
- Pohlig-Hellman algorithm.
- """
- baby_steps = _precompute_baby_steps(base, 2, e)
-
- dlog = 0
- exp = 2**(e-1)
-
- # Solve the discrete log mod 2, only 2 choices
- # for each digit!
- for i in range(e):
- if a**exp != 1:
- a /= baby_steps[i]
- dlog += (2**i)
-
- if a == 1:
- break
-
- exp //= 2
-
- return dlog
-
-def windowed_pohlig_hellman(a, base, e, window):
- """
- Solve the discrete log for a = base^x for
- elements base,a of order 2^e using the
- windowed Pohlig-Hellman algorithm following
- https://ia.cr/2016/963.
-
- Algorithm runs recursively, computing in windows
- l^wi for window=[w1, w2, w3, ...].
-
- Runs the base case when window = []
- """
- # Base case when all windows have been used
- if not window:
- return pohlig_hellman_base(a, base, e)
-
- # Collect the next window
- w, window = window[0], window[1:]
- step = 2**w
-
- # When the window is not a divisor of e, we compute
- # e mod w and solve for both the lower e - e mod w
- # bits and then the e mod w bits at the end.
- e_div_w, e_rem = divmod(e, w)
- e_prime = e - e_rem
-
- # First force elements to have order e - e_rem
- a_prime = a**(2**e_rem)
- base_prime = base**(2**e_rem)
-
- # Compute base^(2^w*i) for i in (0, ..., e/w-1)
- baby_steps = _precompute_baby_steps(base_prime, step, e_div_w)
-
- # Initialise some pieces
- dlog = 0
- if e_prime:
- exp = 2**(e_prime - w)
-
- # Work in subgroup of size 2^w
- s = base_prime**(exp)
-
- # Windowed Pohlig-Hellman to recover dlog as
- # alpha = sum l^(i*w) * alpha_i
- for i in range(e_div_w):
- # Solve the dlog in 2^w
- ri = a_prime**exp
- alpha_i = windowed_pohlig_hellman(ri, s, w, window)
-
- # Update a value and dlog computation
- a_prime /= baby_steps[i]**(alpha_i)
- dlog += alpha_i * step**i
-
- if a_prime == 1:
- break
-
- exp //= step
-
- # TODO:
- # I don't know if this is a nice way to do
- # this last step... Works well enough but could
- # be improved I imagine...
- exp = 2**e_prime
- if e_rem:
- base_last = base**exp
- a_last = a / base**dlog
- dlog_last = pohlig_hellman_base(a_last, base_last, e_rem)
- dlog += exp * dlog_last
-
- return dlog
-
-def BiDLP(R, P, Q, D, ePQ=None):
- """
- Given a basis P,Q of E[D] finds
- a,b such that R = [a]P + [b]Q.
-
- Uses the fact that
- e([a]P + [b]Q, [c]P + [d]Q) = e(P,Q)^(ad-bc)
-
- Optional: include the pairing e(P,Q) which can be precomputed
- which is helpful when running multiple BiDLP problems with P,Q
- as input. This happens, for example, during compression.
- """
- # e(P,Q)
- if ePQ:
- pair_PQ = ePQ
- else:
- pair_PQ = weil_pairing_pari(P, Q, D)
-
- # Write R = aP + bQ for unknown a,b
- # e(R, Q) = e(P, Q)^a
- pair_a = weil_pairing_pari(R, Q, D)
-
- # e(R,-P) = e(P, Q)^b
- pair_b = weil_pairing_pari(R, -P, D)
-
- # Now solve the dlog in Fq
- a = discrete_log_pari(pair_a, pair_PQ, D)
- b = discrete_log_pari(pair_b, pair_PQ, D)
-
- return a, b
-
-def BiDLP_power_two(R, P, Q, e, window, ePQ=None):
- r"""
- Same as the above, but uses optimisations using that
- D = 2^e.
-
- First, rather than use the Weil pairing, we can use
- the Tate pairing which is approx 2x faster.
-
- Secondly, rather than solve the discrete log naively,
- we use an optimised windowed Pohlig-Hellman.
-
- NOTE: this could be optimised further, following the
- SIKE key-compression algorithms and for the Tate pairing
- we could reuse Miller loops to save even more time, but
- that seemed a little overkill for a SageMath PoC
-
- Finally, as the Tate pairing produces elements in \mu_n
- we also have fast inversion from conjugation, but SageMath
- has slow conjugation, so this doesn't help for now.
- """
- p = R.curve().base_ring().characteristic()
- D = 2**e
- exp = (p**2 - 1) // D
-
- # e(P,Q)
- if ePQ:
- pair_PQ = ePQ
- else:
- pair_PQ = tate_pairing_pari(P, Q, D)**exp
-
- # Write R = aP + bQ for unknown a,b
- # e(R, Q) = e(P, Q)^a
- pair_a = tate_pairing_pari(Q, -R, D)**exp
-
- # e(R,-P) = e(P, Q)^b
- pair_b = tate_pairing_pari(P, R, D)**exp
-
- # Now solve the dlog in Fq
- a = windowed_pohlig_hellman(pair_a, pair_PQ, e, window)
- b = windowed_pohlig_hellman(pair_b, pair_PQ, e, window)
-
- return a, b
-
-def DLP_power_two(R, P, Q, e, window, ePQ=None, first=True):
- r"""
- This is the same as BiDLP but it only returns either a or b
- depending on whether first is true or false.
- This is used in compression, where we only send 3 of the 4
- scalars from BiDLP
- """
- p = R.curve().base_ring().characteristic()
- D = 2**e
- exp = (p**2 - 1) // D
-
- # e(P,Q)
- if ePQ:
- pair_PQ = ePQ
- else:
- pair_PQ = tate_pairing_pari(P, Q, D)**exp
-
- if first:
- pair_a = tate_pairing_pari(Q, -R, D)**exp
- x = windowed_pohlig_hellman(pair_a, pair_PQ, e, window)
-
- else:
- pair_b = tate_pairing_pari(P, R, D)**exp
- x = windowed_pohlig_hellman(pair_b, pair_PQ, e, window)
-
- return x
-