From 6404eca99356ec334042f9eb5be2c9a745bff2f4 Mon Sep 17 00:00:00 2001 From: koba-e964 <3303362+koba-e964@users.noreply.github.com> Date: Sat, 2 Nov 2024 23:50:55 +0900 Subject: [PATCH] Add stub of approximate-gcd-experiment --- .github/workflows/check_scripts.yml | 4 +- .../approximate-gcd-experiment/README.md | 4 + .../approx_gcd.sage | 16 + .../approximate-gcd-experiment/exp-0.log | 554 ++++++++++++++++++ .../approximate-gcd-experiment/exp-0.sage | 41 ++ .../exp-1-error.log | 33 ++ .../exp-1-error.sage | 47 ++ .../approximate-gcd-experiment/exp-1-prob.log | 103 ++++ .../exp-1-prob.sage | 42 ++ .../approximate-gcd-experiment/exp-2.log | 45 ++ .../approximate-gcd-experiment/exp-2.sage | 43 ++ .../approximate-gcd-experiment/gen_rsa.py | 33 ++ algorithm/approximate-gcd-experiment/run.sh | 6 + 13 files changed, 970 insertions(+), 1 deletion(-) create mode 100644 algorithm/approximate-gcd-experiment/README.md create mode 100644 algorithm/approximate-gcd-experiment/approx_gcd.sage create mode 100644 algorithm/approximate-gcd-experiment/exp-0.log create mode 100644 algorithm/approximate-gcd-experiment/exp-0.sage create mode 100644 algorithm/approximate-gcd-experiment/exp-1-error.log create mode 100644 algorithm/approximate-gcd-experiment/exp-1-error.sage create mode 100644 algorithm/approximate-gcd-experiment/exp-1-prob.log create mode 100644 algorithm/approximate-gcd-experiment/exp-1-prob.sage create mode 100644 algorithm/approximate-gcd-experiment/exp-2.log create mode 100644 algorithm/approximate-gcd-experiment/exp-2.sage create mode 100644 algorithm/approximate-gcd-experiment/gen_rsa.py create mode 100755 algorithm/approximate-gcd-experiment/run.sh diff --git a/.github/workflows/check_scripts.yml b/.github/workflows/check_scripts.yml index 97a2881..164795e 100644 --- a/.github/workflows/check_scripts.yml +++ b/.github/workflows/check_scripts.yml @@ -18,7 +18,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pylint -r algorithm/smart-attack/requirements.txt + pip install pylint mypy -r algorithm/smart-attack/requirements.txt - name: Set up Go uses: actions/setup-go@v4 @@ -30,6 +30,8 @@ jobs: pylint algorithm/smart-attack/smart_attack.py --fail-under=9 python algorithm/smart-attack/smart_attack.py test pylint algorithm/ecpp/binary_quad.py --fail-under=9 + pylint algorithm/approximate-gcd-experiment/gen_rsa.py --fail-under=9 + mypy algorithm/approximate-gcd-experiment/gen_rsa.py - name: Check scripts in general run: | diff --git a/algorithm/approximate-gcd-experiment/README.md b/algorithm/approximate-gcd-experiment/README.md new file mode 100644 index 0000000..ef691be --- /dev/null +++ b/algorithm/approximate-gcd-experiment/README.md @@ -0,0 +1,4 @@ +# Approximate GCD の実験 +CTF の問題 Sign (ISITDTU CTF QUALS 2024) についての解析。問題内容は https://docs.google.com/spreadsheets/d/1nmj0uAPWkWbC0uXBoA7eBef_a6cRJ-UgTDuuh0gAc2Y/edit?usp=sharing を見ること。 + +解析した結果のスプレッドシートは https://docs.google.com/spreadsheets/d/1nmj0uAPWkWbC0uXBoA7eBef_a6cRJ-UgTDuuh0gAc2Y/edit?usp=sharing である。 diff --git a/algorithm/approximate-gcd-experiment/approx_gcd.sage b/algorithm/approximate-gcd-experiment/approx_gcd.sage new file mode 100644 index 0000000..8f4aa74 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/approx_gcd.sage @@ -0,0 +1,16 @@ +def approx_gcd(d: list[int], approx_error: int) -> int: + """ + Returns q where d[0] ~= qx and d[i]'s are close to multiples of x. + The caller must find (d[0] + q // 2) // q if they want to find x. + """ + l = len(d) + M = Matrix(ZZ, l, l) + M[0, 0] = approx_error + for i in range(1, l): + M[0, i] = d[i] + M[i, i] = -d[0] + L = M.LLL() + for row in L: + if row[0] != 0: + quot = abs(row[0] // approx_error) + return quot diff --git a/algorithm/approximate-gcd-experiment/exp-0.log b/algorithm/approximate-gcd-experiment/exp-0.log new file mode 100644 index 0000000..414c1cc --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-0.log @@ -0,0 +1,554 @@ +# (0.50s) num_sigs = 3 +(0.50s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(0.94s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(0.95s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(0.96s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 9337 +(0.97s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 9337 +(0.98s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 9334 +(0.99s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(1.00s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 9334 +(1.01s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 9335 +(1.02s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 9337 +(1.02s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(1.03s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 9332 +(1.04s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 9328 +(1.05s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 9329 +(1.06s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 9330 +(1.07s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 9326 +(1.08s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 9336 +(1.09s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 9336 +(1.10s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 9337 +(1.11s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 9341 +(1.12s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 9334 +(1.13s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 9333 +(1.14s) iter = 22 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(1.16s) iter = 23 +q = round(found_n / rsa.n); q.bit_length() = 9338 +(1.17s) iter = 24 +q = round(found_n / rsa.n); q.bit_length() = 9335 +(1.19s) iter = 25 +q = round(found_n / rsa.n); q.bit_length() = 9336 +(1.20s) iter = 26 +q = round(found_n / rsa.n); q.bit_length() = 9334 +success = 0 +count = 27 +# (1.21s) num_sigs = 4 +(1.21s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 5622 +(1.23s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 5630 +(1.25s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 5625 +(1.26s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.28s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 5630 +(1.30s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.32s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.33s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 5634 +(1.35s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 5626 +(1.37s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 5628 +(1.39s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 5627 +(1.40s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 5626 +(1.42s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 5628 +(1.44s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 5626 +(1.45s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 5627 +(1.47s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.49s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 5628 +(1.51s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.52s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 5625 +(1.54s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 5630 +(1.56s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.58s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 5630 +(1.59s) iter = 22 +q = round(found_n / rsa.n); q.bit_length() = 5629 +(1.61s) iter = 23 +q = round(found_n / rsa.n); q.bit_length() = 5628 +(1.63s) iter = 24 +q = round(found_n / rsa.n); q.bit_length() = 5628 +(1.65s) iter = 25 +q = round(found_n / rsa.n); q.bit_length() = 5623 +success = 0 +count = 26 +# (1.66s) num_sigs = 5 +(1.66s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 3777 +(1.69s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 3776 +(1.72s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 3771 +(1.75s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 3772 +(1.78s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(1.81s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(1.84s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 3774 +(1.87s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 3774 +(1.90s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 3769 +(1.93s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 3768 +(1.96s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(1.99s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 3776 +(2.02s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(2.05s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 3773 +(2.08s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 3769 +(2.11s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(2.14s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 3772 +(2.17s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 3774 +(2.20s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 3773 +(2.23s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 3773 +(2.26s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(2.29s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(2.31s) iter = 22 +q = round(found_n / rsa.n); q.bit_length() = 3775 +(2.34s) iter = 23 +q = round(found_n / rsa.n); q.bit_length() = 3771 +(2.37s) iter = 24 +q = round(found_n / rsa.n); q.bit_length() = 3775 +success = 0 +count = 25 +# (2.40s) num_sigs = 6 +(2.40s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 2659 +(2.45s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 2661 +(2.49s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 2660 +(2.54s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 2664 +(2.59s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 2665 +(2.63s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 2659 +(2.68s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 2664 +(2.72s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 2660 +(2.77s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 2664 +(2.82s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 2666 +(2.86s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 2662 +(2.91s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 2663 +(2.96s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 2662 +(3.00s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 2663 +(3.05s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 2664 +(3.10s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 2666 +(3.14s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 2659 +(3.19s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 2658 +(3.23s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 2662 +(3.28s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 2665 +(3.33s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 2665 +(3.38s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 2661 +(3.43s) iter = 22 +q = round(found_n / rsa.n); q.bit_length() = 2662 +(3.48s) iter = 23 +q = round(found_n / rsa.n); q.bit_length() = 2656 +success = 0 +count = 24 +# (3.52s) num_sigs = 7 +(3.52s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 1919 +(3.59s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 1921 +(3.66s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 1924 +(3.73s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 1923 +(3.79s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 1917 +(3.86s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 1923 +(3.93s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 1922 +(4.00s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 1918 +(4.07s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 1920 +(4.14s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 1916 +(4.20s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 1919 +(4.27s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 1922 +(4.34s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 1921 +(4.41s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 1920 +(4.48s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 1920 +(4.55s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 1912 +(4.62s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 1918 +(4.69s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 1919 +(4.76s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 1921 +(4.82s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 1920 +(4.89s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 1918 +(4.96s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 1921 +(5.03s) iter = 22 +q = round(found_n / rsa.n); q.bit_length() = 1920 +success = 0 +count = 23 +# (5.10s) num_sigs = 8 +(5.10s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 1392 +(5.20s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 1390 +(5.30s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(5.40s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 1390 +(5.51s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(5.61s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 1393 +(5.71s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 1392 +(5.81s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(5.91s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(6.01s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 1390 +(6.11s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 1388 +(6.21s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 1393 +(6.31s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 1393 +(6.41s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(6.51s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(6.61s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 1392 +(6.71s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 1390 +(6.81s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(6.94s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 1394 +(7.05s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 1391 +(7.15s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 1392 +(7.25s) iter = 21 +q = round(found_n / rsa.n); q.bit_length() = 1390 +success = 0 +count = 22 +# (7.35s) num_sigs = 9 +(7.35s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 1003 +(7.50s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 998 +(7.65s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 994 +(7.80s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 993 +(7.94s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 991 +(8.08s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 994 +(8.23s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 996 +(8.37s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 993 +(8.51s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 994 +(8.65s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 991 +(8.80s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 991 +(8.94s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 999 +(9.09s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 994 +(9.23s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 993 +(9.37s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 993 +(9.53s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 992 +(9.67s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 993 +(9.82s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 993 +(9.97s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 992 +(10.11s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 991 +(10.25s) iter = 20 +q = round(found_n / rsa.n); q.bit_length() = 993 +success = 0 +count = 21 +# (10.40s) num_sigs = 10 +(10.40s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 683 +(10.68s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 682 +(10.88s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 684 +(11.08s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 685 +(11.29s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 685 +(11.49s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 685 +(11.69s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 687 +(11.90s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 682 +(12.10s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 685 +(12.30s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 683 +(12.50s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 687 +(12.71s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 684 +(12.91s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 682 +(13.11s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 685 +(13.31s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 685 +(13.52s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 685 +(13.73s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 683 +(13.94s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 684 +(14.16s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 685 +(14.36s) iter = 19 +q = round(found_n / rsa.n); q.bit_length() = 683 +success = 0 +count = 20 +# (14.56s) num_sigs = 11 +(14.56s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 438 +(14.84s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 436 +(15.13s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 437 +(15.46s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 439 +(15.74s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 437 +(16.03s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 437 +(16.32s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 436 +(16.60s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 437 +(16.89s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 438 +(17.17s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 438 +(17.45s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 441 +(17.73s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 436 +(18.02s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 439 +(18.32s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 438 +(18.60s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 444 +(18.89s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 439 +(19.17s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 439 +(19.45s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 437 +(19.74s) iter = 18 +q = round(found_n / rsa.n); q.bit_length() = 439 +success = 0 +count = 19 +# (20.02s) num_sigs = 12 +(20.02s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 234 +(20.41s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 235 +(20.80s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 235 +(21.19s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 235 +(21.66s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 235 +(22.05s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 241 +(22.45s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 236 +(22.84s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 234 +(23.23s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 237 +(23.62s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 233 +(24.00s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 236 +(24.47s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 234 +(24.87s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 235 +(25.26s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 234 +(25.65s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 237 +(26.04s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 236 +(26.42s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 235 +(26.82s) iter = 17 +q = round(found_n / rsa.n); q.bit_length() = 235 +success = 0 +count = 18 +# (27.21s) num_sigs = 13 +(27.21s) iter = 0 +q = round(found_n / rsa.n); q.bit_length() = 69 +(27.74s) iter = 1 +q = round(found_n / rsa.n); q.bit_length() = 68 +(28.26s) iter = 2 +q = round(found_n / rsa.n); q.bit_length() = 72 +(28.79s) iter = 3 +q = round(found_n / rsa.n); q.bit_length() = 68 +(29.32s) iter = 4 +q = round(found_n / rsa.n); q.bit_length() = 65 +(30.09s) iter = 5 +q = round(found_n / rsa.n); q.bit_length() = 65 +(31.46s) iter = 6 +q = round(found_n / rsa.n); q.bit_length() = 65 +(32.13s) iter = 7 +q = round(found_n / rsa.n); q.bit_length() = 69 +(32.69s) iter = 8 +q = round(found_n / rsa.n); q.bit_length() = 65 +(33.25s) iter = 9 +q = round(found_n / rsa.n); q.bit_length() = 64 +(33.83s) iter = 10 +q = round(found_n / rsa.n); q.bit_length() = 68 +(34.35s) iter = 11 +q = round(found_n / rsa.n); q.bit_length() = 65 +(34.89s) iter = 12 +q = round(found_n / rsa.n); q.bit_length() = 68 +(35.47s) iter = 13 +q = round(found_n / rsa.n); q.bit_length() = 65 +(36.20s) iter = 14 +q = round(found_n / rsa.n); q.bit_length() = 66 +(36.76s) iter = 15 +q = round(found_n / rsa.n); q.bit_length() = 66 +(37.29s) iter = 16 +q = round(found_n / rsa.n); q.bit_length() = 66 +success = 0 +count = 17 +# (37.90s) num_sigs = 14 +(37.90s) iter = 0 +(38.63s) iter = 1 +(39.42s) iter = 2 +(40.17s) iter = 3 +(40.89s) iter = 4 +(41.61s) iter = 5 +(42.35s) iter = 6 +(43.08s) iter = 7 +(43.81s) iter = 8 +(44.54s) iter = 9 +(45.27s) iter = 10 +(45.99s) iter = 11 +(46.73s) iter = 12 +(47.47s) iter = 13 +(48.20s) iter = 14 +(48.92s) iter = 15 +success = 16 +count = 16 +# (49.64s) num_sigs = 15 +(49.64s) iter = 0 +(50.59s) iter = 1 +(51.54s) iter = 2 +(52.51s) iter = 3 +(53.49s) iter = 4 +(54.46s) iter = 5 +(55.42s) iter = 6 +(56.38s) iter = 7 +(57.35s) iter = 8 +(58.29s) iter = 9 +(59.25s) iter = 10 +(60.21s) iter = 11 +(61.17s) iter = 12 +(62.12s) iter = 13 +(63.09s) iter = 14 +success = 15 +count = 15 diff --git a/algorithm/approximate-gcd-experiment/exp-0.sage b/algorithm/approximate-gcd-experiment/exp-0.sage new file mode 100644 index 0000000..d292caa --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-0.sage @@ -0,0 +1,41 @@ +import time +from Crypto.Util.number import bytes_to_long +from Crypto.PublicKey import RSA +from gen_rsa import genkey, gensig +load("approx_gcd.sage") + + +def try_one(rsa: RSA.RsaKey, num_sigs: int) -> bool: + e = 11 + sigs: list[int] = [] + for _ in range(num_sigs): + sigs.append(bytes_to_long(gensig(rsa))) + diff = [abs(sigs[i]**e - sigs[i - 1]**e) for i in range(1, num_sigs)] + q = approx_gcd(diff, 2**256) + found_n = (diff[0] + q // 2) // q + if found_n == rsa.n: + return True + q = (found_n + rsa.n // 2) // rsa.n + print(f'q = round(found_n / rsa.n); {q.bit_length() = }') + return False + + +def main() -> None: + start = time.time() + rsa = genkey() + num_sigs_min = 3 + num_sigs_max = 15 + for num_sigs in range(num_sigs_min, num_sigs_max + 1): + print(f'# ({time.time() - start:.2f}s) {num_sigs = }') + success = 0 + count = 30 - num_sigs + for iter in range(count): + print(f'({time.time() - start:.2f}s) {iter = }') + if try_one(rsa, num_sigs): + success += 1 + print(f'{success = }') + print(f'{count = }') + + +if __name__ == "__main__": + main() diff --git a/algorithm/approximate-gcd-experiment/exp-1-error.log b/algorithm/approximate-gcd-experiment/exp-1-error.log new file mode 100644 index 0000000..d5df720 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-1-error.log @@ -0,0 +1,33 @@ +# (0.29s) num_sigs = 3 +avg = 9339.36666666667 +avg_uncertainty = 0.568287067896947 +# (0.85s) num_sigs = 4 +avg = 5630.53333333333 +avg_uncertainty = 0.461714280112017 +# (1.39s) num_sigs = 5 +avg = 3775.06666666667 +avg_uncertainty = 0.596798611252722 +# (2.29s) num_sigs = 6 +avg = 2664.23333333333 +avg_uncertainty = 0.520131135852287 +# (3.72s) num_sigs = 7 +avg = 1921.00000000000 +avg_uncertainty = 0.332181919424628 +# (5.82s) num_sigs = 8 +avg = 1391.03333333333 +avg_uncertainty = 0.350642541026395 +# (9.04s) num_sigs = 9 +avg = 994.266666666667 +avg_uncertainty = 0.395181708047722 +# (13.63s) num_sigs = 10 +avg = 684.566666666667 +avg_uncertainty = 0.256785313670725 +# (19.96s) num_sigs = 11 +avg = 437.366666666667 +avg_uncertainty = 0.316167180181040 +# (28.77s) num_sigs = 12 +avg = 234.600000000000 +avg_uncertainty = 0.232922462556173 +# (40.78s) num_sigs = 13 +avg = 66.3333333333333 +avg_uncertainty = 0.319242398205945 diff --git a/algorithm/approximate-gcd-experiment/exp-1-error.sage b/algorithm/approximate-gcd-experiment/exp-1-error.sage new file mode 100644 index 0000000..bda2135 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-1-error.sage @@ -0,0 +1,47 @@ +import time +from Crypto.Util.number import bytes_to_long +from Crypto.PublicKey import RSA +from gen_rsa import genkey, gensig +load("approx_gcd.sage") + + +def try_and_find_error(rsa: RSA.RsaKey, num_sigs: int) -> int: + """ + Returns how many bits bigger n is estimated than the actual n. + """ + e = 11 + sigs: list[int] = [] + for _ in range(num_sigs): + sigs.append(bytes_to_long(gensig(rsa))) + diff = [abs(sigs[i]**e - sigs[i - 1]**e) for i in range(1, num_sigs)] + q = approx_gcd(diff, 2**256) + found_n = (diff[0] + q // 2) // q + if found_n == rsa.n: + return True + q = (found_n + rsa.n // 2) // rsa.n + return q.bit_length() + + +def main() -> None: + start = time.time() + rsa = genkey() + num_sigs_min = 3 + num_sigs_max = 13 + for num_sigs in range(num_sigs_min, num_sigs_max + 1): + print(f'# ({time.time() - start:.2f}s) {num_sigs = }') + count = 30 + error_sum = 0.0 + error_sqsum = 0.0 + for _ in range(count): + res = try_and_find_error(rsa, num_sigs) + error_sum += res + error_sqsum += res**2 + avg = error_sum / count + sample_std = ((error_sqsum / count - avg**2) * count / (count - 1.0)) ** 0.5 + avg_uncertainty = sample_std / count ** 0.5 + print(f'{avg = }') + print(f'{avg_uncertainty = }') + + +if __name__ == "__main__": + main() diff --git a/algorithm/approximate-gcd-experiment/exp-1-prob.log b/algorithm/approximate-gcd-experiment/exp-1-prob.log new file mode 100644 index 0000000..3066035 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-1-prob.log @@ -0,0 +1,103 @@ +# (0.23s) num_sigs = 14 +(0.23s) iter = 0 +(8.57s) iter = 10 +(16.23s) iter = 20 +(24.03s) iter = 30 +(31.45s) iter = 40 +(39.47s) iter = 50 +(46.86s) iter = 60 +(54.11s) iter = 70 +(61.30s) iter = 80 +(68.69s) iter = 90 +(76.07s) iter = 100 +(84.30s) iter = 110 +(91.78s) iter = 120 +(99.39s) iter = 130 +(106.95s) iter = 140 +(114.30s) iter = 150 +(121.71s) iter = 160 +(129.01s) iter = 170 +(136.75s) iter = 180 +(144.77s) iter = 190 +(153.00s) iter = 200 +(160.36s) iter = 210 +(167.82s) iter = 220 +(175.22s) iter = 230 +(182.48s) iter = 240 +(189.89s) iter = 250 +(197.50s) iter = 260 +(205.05s) iter = 270 +(212.36s) iter = 280 +(219.69s) iter = 290 +(226.94s) iter = 300 +(234.22s) iter = 310 +(241.55s) iter = 320 +(248.75s) iter = 330 +(256.03s) iter = 340 +(263.68s) iter = 350 +(270.94s) iter = 360 +(278.36s) iter = 370 +(285.65s) iter = 380 +(292.85s) iter = 390 +(300.13s) iter = 400 +(307.36s) iter = 410 +(315.04s) iter = 420 +(322.57s) iter = 430 +(329.81s) iter = 440 +(337.04s) iter = 450 +(344.91s) iter = 460 +(352.32s) iter = 470 +(359.71s) iter = 480 +(366.94s) iter = 490 +(374.27s) iter = 500 +(381.67s) iter = 510 +(388.93s) iter = 520 +(396.33s) iter = 530 +(404.07s) iter = 540 +(411.34s) iter = 550 +(418.59s) iter = 560 +(425.90s) iter = 570 +(433.23s) iter = 580 +(441.05s) iter = 590 +(448.34s) iter = 600 +(455.86s) iter = 610 +(463.23s) iter = 620 +(470.53s) iter = 630 +(477.90s) iter = 640 +(485.24s) iter = 650 +(492.56s) iter = 660 +(500.29s) iter = 670 +(507.81s) iter = 680 +(515.35s) iter = 690 +(522.58s) iter = 700 +(529.87s) iter = 710 +(537.32s) iter = 720 +(545.40s) iter = 730 +(553.26s) iter = 740 +(560.81s) iter = 750 +(568.34s) iter = 760 +(575.82s) iter = 770 +(581.45s) iter = 780 +(589.33s) iter = 790 +(596.91s) iter = 800 +(604.37s) iter = 810 +(611.80s) iter = 820 +(619.25s) iter = 830 +(626.53s) iter = 840 +(633.85s) iter = 850 +(641.38s) iter = 860 +(648.73s) iter = 870 +(655.95s) iter = 880 +(663.17s) iter = 890 +(670.40s) iter = 900 +(677.70s) iter = 910 +(684.94s) iter = 920 +(692.17s) iter = 930 +(699.40s) iter = 940 +(706.62s) iter = 950 +(713.82s) iter = 960 +(721.09s) iter = 970 +(728.29s) iter = 980 +(735.98s) iter = 990 +success = 1000 +count = 1000 diff --git a/algorithm/approximate-gcd-experiment/exp-1-prob.sage b/algorithm/approximate-gcd-experiment/exp-1-prob.sage new file mode 100644 index 0000000..5132dee --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-1-prob.sage @@ -0,0 +1,42 @@ +import time +from Crypto.Util.number import bytes_to_long +from Crypto.PublicKey import RSA +from gen_rsa import genkey, gensig +load("approx_gcd.sage") + + +def try_one(rsa: RSA.RsaKey, num_sigs: int) -> bool: + e = 11 + sigs: list[int] = [] + for _ in range(num_sigs): + sigs.append(bytes_to_long(gensig(rsa))) + diff = [abs(sigs[i]**e - sigs[i - 1]**e) for i in range(1, num_sigs)] + q = approx_gcd(diff, 2**256) + found_n = (diff[0] + q // 2) // q + if found_n == rsa.n: + return True + q = (found_n + rsa.n // 2) // rsa.n + print(f'q = round(found_n / rsa.n); {q.bit_length() = }') + return False + + +def main() -> None: + start = time.time() + rsa = genkey() + num_sigs_min = 14 + num_sigs_max = 14 + for num_sigs in range(num_sigs_min, num_sigs_max + 1): + print(f'# ({time.time() - start:.2f}s) {num_sigs = }') + success = 0 + count = 1000 + for iter in range(count): + if iter % 10 == 0: + print(f'({time.time() - start:.2f}s) {iter = }') + if try_one(rsa, num_sigs): + success += 1 + print(f'{success = }') + print(f'{count = }') + + +if __name__ == "__main__": + main() diff --git a/algorithm/approximate-gcd-experiment/exp-2.log b/algorithm/approximate-gcd-experiment/exp-2.log new file mode 100644 index 0000000..c417f00 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-2.log @@ -0,0 +1,45 @@ +# (0.00s) num_nums = 2 +avg = 5129.28000000000 +avg_uncertainty = 0.254012694720592 +# (0.32s) num_nums = 3 +avg = 3506.02000000000 +avg_uncertainty = 0.227658892697369 +# (0.43s) num_nums = 4 +avg = 2694.26000000000 +avg_uncertainty = 0.207629969039358 +# (0.67s) num_nums = 5 +avg = 2207.48000000000 +avg_uncertainty = 0.251103686186223 +# (1.10s) num_nums = 6 +avg = 1882.94000000000 +avg_uncertainty = 0.198792271849437 +# (1.78s) num_nums = 7 +avg = 1650.88000000000 +avg_uncertainty = 0.268996623883370 +# (2.78s) num_nums = 8 +avg = 1477.16000000000 +avg_uncertainty = 0.252917668276664 +# (4.24s) num_nums = 9 +avg = 1341.62000000000 +avg_uncertainty = 0.243930401590380 +# (6.34s) num_nums = 10 +avg = 1233.10000000000 +avg_uncertainty = 0.198463485572188 +# (9.26s) num_nums = 11 +avg = 1144.86000000000 +avg_uncertainty = 0.209975703181458 +# (13.37s) num_nums = 12 +avg = 1071.00000000000 +avg_uncertainty = 0.197948663718317 +# (19.01s) num_nums = 13 +avg = 1008.84000000000 +avg_uncertainty = 0.229213384226733 +# (26.73s) num_nums = 14 +avg = 955.300000000000 +avg_uncertainty = 0.236038737740028 +# (36.95s) num_nums = 15 +avg = 908.720000000000 +avg_uncertainty = 0.159181580307488 +# (50.50s) num_nums = 16 +avg = 868.420000000000 +avg_uncertainty = 0.180904755645780 diff --git a/algorithm/approximate-gcd-experiment/exp-2.sage b/algorithm/approximate-gcd-experiment/exp-2.sage new file mode 100644 index 0000000..423ebcb --- /dev/null +++ b/algorithm/approximate-gcd-experiment/exp-2.sage @@ -0,0 +1,43 @@ +""" +Randomly generate numbers with the same bit length and see how long approx_gcd's results are. +""" +import os +import time +from Crypto.Util.number import bytes_to_long +load("approx_gcd.sage") + + +def try_and_find_magnitude(num_nums: int) -> int: + """ + Returns how big approx_gcd() is. + """ + nums: list[int] = [] + for _ in range(num_nums): + nums.append(bytes_to_long(os.urandom(1250))) + q = approx_gcd(nums, 2**256) + found_n = (nums[0] + q // 2) // q + return found_n.bit_length() + + +def main() -> None: + start = time.time() + num_nums_min = 2 + num_nums_max = 16 + for num_nums in range(num_nums_min, num_nums_max + 1): + print(f'# ({time.time() - start:.2f}s) {num_nums = }') + count = 50 + error_sum = 0.0 + error_sqsum = 0.0 + for _ in range(count): + res = try_and_find_magnitude(num_nums) + error_sum += res + error_sqsum += res**2 + avg = error_sum / count + sample_std = ((error_sqsum / count - avg**2) * count / (count - 1.0)) ** 0.5 + avg_uncertainty = sample_std / count ** 0.5 + print(f'{avg = }') + print(f'{avg_uncertainty = }') + + +if __name__ == "__main__": + main() diff --git a/algorithm/approximate-gcd-experiment/gen_rsa.py b/algorithm/approximate-gcd-experiment/gen_rsa.py new file mode 100644 index 0000000..fd499bc --- /dev/null +++ b/algorithm/approximate-gcd-experiment/gen_rsa.py @@ -0,0 +1,33 @@ +""" +This module provides functions to generate RSA key pairs and sign messages. +The functions are similar to the ones that are used in Sign (ISITDTU 2024 QUALS CTF). +""" +import os +from Crypto.Util.number import getPrime, GCD +from Crypto.Signature import PKCS1_v1_5 +from Crypto.PublicKey import RSA +from Crypto.Hash import SHA256 + + +def genkey(e=11): + """ + Generates an RSA key pair with the public exponent e. + """ + while True: + p = getPrime(1024) + q = getPrime(1024) + if GCD(p-1, e) == 1 and GCD(q-1, e) == 1: + break + n = p*q + d = pow(e, -1, (p-1)*(q-1)) + return RSA.construct((int(n), int(e), int(d))) + + +def gensig(key: RSA.RsaKey) -> bytes: + """ + Generates a PKCS#1 v1.5 RSA signature for a random message using the given RSA key. + """ + m = os.urandom(256) + h = SHA256.new(m) + s = PKCS1_v1_5.new(key).sign(h) + return s diff --git a/algorithm/approximate-gcd-experiment/run.sh b/algorithm/approximate-gcd-experiment/run.sh new file mode 100755 index 0000000..736aa40 --- /dev/null +++ b/algorithm/approximate-gcd-experiment/run.sh @@ -0,0 +1,6 @@ +set -e + +sage exp-0.sage >exp-0.log +sage exp-1-error.sage >exp-1-error.log +sage exp-1-prob.sage >exp-1-prob.log +sage exp-2.sage >exp-2.log