diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1de2d6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,184 @@ +# Created by .ignore support plugin (hsz.mobi) +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject +### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# idea folder, uncomment if you don't need it +.idea diff --git a/lib/gcd_tables.py b/lib/gcd_tables.py new file mode 100644 index 0000000..f09f0e3 --- /dev/null +++ b/lib/gcd_tables.py @@ -0,0 +1,262 @@ +''' +Source: extendedeuclideanalgorithm.com + +Modified to generate CSV tables +for iterative calculation of the modular inverse +''' + +import math + + +# Warning: can't handle b=0. See extendedeuclideanalgorithm.com/code for a version that can +def gcd_iterative(a, b): + """ Calculating the greatest common divisor + using the Euclidean Algorithm (non-recursive) + (Source: extendedeuclideanalgorithm.com/code) + """ + + # Set default values for the quotient and the remainder + q = 0 + r = 1 + + ''' + In each iteration of the loop below, we + calculate the new quotient, remainder, a and b. + r decreases, so we stop when r = 0 + ''' + while (r > 0): + # The calculations + q = math.floor(a / b) + r = a - q * b + + # The values for the next iteration + a = b + b = r if (r > 0) else b + + return abs(b) + + +# Can handle b=0 +def gcd_iterative_2(a, b): + """ Calculating the greatest common divisor + using the Euclidean Algorithm (non-recursive) + (Source: extendedeuclideanalgorithm.com/code) + """ + + # Set default values for the quotient and the remainder + q = 0 + r = 1 + + ''' + In each iteration of the loop below, we + calculate the new quotient, remainder, a and b. + r decreases, so we stop when r = 0 + ''' + while (b > 0): + # The calculations + q = math.floor(a / b) + r = a - q * b + + # The values for the next iteration + a = b + b = r + + return abs(a) + + +def gcd(a, b): + """ Calculating the greatest common divisor + using the Euclidean Algorithm (recursive) + (Source: extendedeuclideanalgorithm.com/code) + """ + if (b == 0): + return abs(a) + + q = math.floor(a / b) + r = a - q * b + return abs(b) if (r == 0) else gcd(b, r) + + +# Warning: this version can't handle b=0. See extendedeuclideanalgorithm.com/code for a version that can. +def xgcd_iterative(a, b): + """ Calculates the gcd and Bezout coefficients, + using the Extended Euclidean Algorithm (non-recursive). + (Source: extendedeuclideanalgorithm.com/code) + """ + # Set default values for the quotient, remainder, + # s-variables and t-variables + q = 0 + r = 1 + s1 = 1 + s2 = 0 + s3 = 1 + t1 = 0 + t2 = 1 + t3 = 0 + + ''' + In each iteration of the loop below, we + calculate the new quotient, remainder, a, b, + and the new s-variables and t-variables. + r decreases, so we stop when r = 0 + ''' + while (r > 0): + # The calculations + q = math.floor(a / b) + r = a - q * b + s3 = s1 - q * s2 + t3 = t1 - q * t2 + + ''' + The values for the next iteration, + (but only if there is a next iteration) + ''' + if (r > 0): + a = b + b = r + s1 = s2 + s2 = s3 + t1 = t2 + t2 = t3 + + return abs(b), s2, t2 + + +# Can handle b=0 +def xgcd_iterative_2(a, b): + """ Calculates the gcd and Bezout coefficients, + using the Extended Euclidean Algorithm (non-recursive). + (Source: extendedeuclideanalgorithm.com/code) + """ + # Set default values for the quotient, remainder, + # s-variables and t-variables + q = 0 + r = 1 + s1 = 1 + s2 = 0 + s3 = 1 + t1 = 0 + t2 = 1 + t3 = 0 + + ''' + In each iteration of the loop below, we + calculate the new quotient, remainder, a, b, + and the new s-variables and t-variables. + r decreases, so we stop when r = 0 + ''' + + # CSV output + print("i, n, b, q, r, t1, t2, t3") + i = 1 + + while (b > 0): + # The calculations + q = math.floor(a / b) + r = a - q * b + s3 = s1 - q * s2 + t3 = t1 - q * t2 + + # CSV output + print("{}, {}, {}, {}, {}, {}, {}, {}".format(i, a, b, q, r, t1, t2, t3)) + i += 1 + + ''' + The values for the next iteration, + (but only if there is a next iteration) + ''' + + a = b + b = r + s1 = s2 + s2 = s3 + t1 = t2 + t2 = t3 + + return abs(a), s1, t1 + + +def xgcd(a, b, s1=1, s2=0, t1=0, t2=1): + """ Calculates the gcd and Bezout coefficients, + using the Extended Euclidean Algorithm (recursive). + (Source: extendedeuclideanalgorithm.com/code) + """ + if (b == 0): + return abs(a), 1, 0 + + q = math.floor(a / b) + r = a - q * b + s3 = s1 - q * s2 + t3 = t1 - q * t2 + + # if r==0, then b will be the gcd and s2, t2 the Bezout coefficients + return (abs(b), s2, t2) if (r == 0) else xgcd(b, r, s2, s3, t2, t3) + + +def multinv(b, n): + """ + Calculates the multiplicative inverse of a number b mod n, + using the Extended Euclidean Algorithm. If b does not have a + multiplicative inverse mod n, then throw an exception. + (Source: extendedeuclideanalgorithm.com/code) + """ + + # Get the gcd and the second Bezout coefficient (t) + # from the Extended Euclidean Algorithm. (We don't need s) + my_gcd, _, t = xgcd(n, b) + + # It only has a multiplicative inverse if the gcd is 1 + if (my_gcd == 1): + return t % n + else: + raise ValueError('{} has no multiplicative inverse modulo {}'.format(b, n)) + + +def make_eea_table(a : int, b : int): + + + ''' + Euclidean algorithm: + see the output of gcd(a, b) + ''' + print('Euclidean Algorithm:') + print('The gcd of', a, 'and', b, 'is', gcd(a, b)) + + # ------------------------------------------------------------- + + ''' + Extended Euclidean Algorithm: + see the output of xgcd(a,b) and Bezout coefficients + And verify that they are correct + ''' + my_gcd, s, t = xgcd(a, b) + verification = abs(s * a + t * b) + print('Extended Euclidean Algorithm:') + print('The gcd of', a, 'and', b, 'is', my_gcd) + print('And the Bezout coefficients: s=', s, ' and t=', t, '.', sep='') + print('And', s, '*', a, '+', t, '*', b, '=', verification) + if (my_gcd == verification): + print('So as we expect, s*a+t*b is equal to the gcd we found.') + else: + print('Something went wrong') + + # ------------------------------------------------------------ + b = b + n = a + + ''' + Multiplicative Inverse: + Try to compute the multiplicative inverse of b mod n. + If that succeeds, verify that it's correct. + If it doesn't succeed, show the error raised by the function. + ''' + + print('Multiplicative inverse:') + try: + # inverse = multinv(b, n); + inverse = xgcd_iterative_2(b, n) + + except ValueError as error: + print(error) + + diff --git a/make_eea_takes.py b/make_eea_takes.py new file mode 100644 index 0000000..07877cb --- /dev/null +++ b/make_eea_takes.py @@ -0,0 +1,10 @@ +from lib.gcd_tables import * + + +def check(): + make_eea_table(7, 143) + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + check() diff --git a/shanks_baby_giant_step.py b/shanks_baby_giant_step.py new file mode 100644 index 0000000..3c5f5c9 --- /dev/null +++ b/shanks_baby_giant_step.py @@ -0,0 +1,60 @@ +from math import ceil, sqrt + +def bsgs(p, g, h): + ##Berechne d in h = g^d mod p + + N = ceil(sqrt(p - 1)) # phi(p) is p-1 if p is prime + + # Baby step: compute a look-up table of g^i values for i = 0, 1, ..., N + lookup_table = {pow(g, i, p): i for i in range(N)} + + # Precompute via Fermat's Little Theorem + c = pow(g, N * (p - 2), p) + + # Giant step: Search for an equivalence in the lookup_table + for j in range(N): + y = (h * pow(c, j, p)) % p + if y in lookup_table: + return j * N + lookup_table[y] + + # Solution not found + return None + + +def main(): + + p = 27893 + g = 3729 + alpha = 11819 + beta = 1531 + + # Find the discrete logarithm a such that g^a = alpha mod p + a = bsgs(p, g, alpha) + print(f"a = {a}") + + # Validate the result + if a is None: + print("No solution found for a") + else: + # Recompute the shared secret key: K = beta^a mod p + K = pow(beta, a, p) + print(f"Shared secret key K = {K}") + + # Find the discrete logarithm b such that g^b = beta mod p + b = bsgs(p, g, beta) + print(f"b = {b}") + + # Validate the result + if b is None: + print("No solution found for b") + else: + # Recompute the shared secret key: K = alpha^b mod p + K2 = pow(alpha, b, p) + if K2 == K: + print("b is correct") + else: + print("b is not correct") + + +if __name__ == '__main__': + main()