foxmath 0.6.0__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {foxmath-0.6.0/src/foxmath.egg-info → foxmath-0.6.2}/PKG-INFO +44 -3
- {foxmath-0.6.0 → foxmath-0.6.2}/README.md +43 -2
- {foxmath-0.6.0 → foxmath-0.6.2}/pyproject.toml +1 -1
- {foxmath-0.6.0 → foxmath-0.6.2/src/foxmath.egg-info}/PKG-INFO +44 -3
- {foxmath-0.6.0 → foxmath-0.6.2}/tests/test_cli.py +58 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/tests/test_foxmath.py +103 -3
- {foxmath-0.6.0 → foxmath-0.6.2}/LICENSE +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/setup.cfg +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath/__init__.py +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath/foxmath.py +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath.egg-info/SOURCES.txt +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath.egg-info/dependency_links.txt +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath.egg-info/entry_points.txt +0 -0
- {foxmath-0.6.0 → foxmath-0.6.2}/src/foxmath.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: foxmath
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Minimalist CLI for math & cryptography exploration 🦊
|
|
5
5
|
Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,8 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
**Minimalist CLI for math & cryptography exploration.**
|
|
23
23
|
|
|
24
|
+
**More about me / other projects:** [abhrankan.netlify.app](https://abhrankan.netlify.app)
|
|
25
|
+
|
|
24
26
|
One binary. Clean output. Zero footguns. Educational by design.
|
|
25
27
|
|
|
26
28
|
Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
@@ -30,12 +32,14 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
30
32
|
- Euler/Hermann Machin-like π approximation
|
|
31
33
|
- Elliptic curve point addition (over finite fields), including
|
|
32
34
|
named curves (secp256k1) with on-curve result validation
|
|
35
|
+
- Elliptic curve scalar multiplication k·P (double-and-add), including
|
|
36
|
+
the point at infinity as a first-class result
|
|
33
37
|
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
34
38
|
- Continued fraction expansion and convergents
|
|
35
39
|
- Challenge/quiz mode — practice problems with scoring, or a
|
|
36
40
|
worksheet mode for self-study
|
|
37
41
|
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd,
|
|
38
|
-
foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
42
|
+
foxmath-ecmul, foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
39
43
|
- JSON output mode
|
|
40
44
|
|
|
41
45
|
## Installation
|
|
@@ -51,6 +55,14 @@ cd foxmath
|
|
|
51
55
|
pip install -e .
|
|
52
56
|
```
|
|
53
57
|
|
|
58
|
+
Check which version you have installed:
|
|
59
|
+
```bash
|
|
60
|
+
foxmath --version
|
|
61
|
+
```
|
|
62
|
+
```
|
|
63
|
+
foxmath 0.6.2
|
|
64
|
+
```
|
|
65
|
+
|
|
54
66
|
## Usage Examples
|
|
55
67
|
|
|
56
68
|
```bash
|
|
@@ -84,6 +96,25 @@ Result point: (89565891926547004231252920425935692360644145829622209833684329913
|
|
|
84
96
|
✓ on curve
|
|
85
97
|
```
|
|
86
98
|
|
|
99
|
+
```bash
|
|
100
|
+
# Scalar multiplication — same curves, O(log k) double-and-add
|
|
101
|
+
foxmath ecmul --curve toy193 --k 7
|
|
102
|
+
```
|
|
103
|
+
```
|
|
104
|
+
Result point: (66, 24)
|
|
105
|
+
✓ on curve
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# toy193: y² = x³ + x + 5 (mod 193), #E = 193 (prime) — small enough to
|
|
110
|
+
# hand-verify, structured exactly like secp256k1. G = (1, 59).
|
|
111
|
+
# k = 0 or a multiple of the group order lands on the identity:
|
|
112
|
+
foxmath ecmul --curve toy193 --k 193
|
|
113
|
+
```
|
|
114
|
+
```
|
|
115
|
+
Result: point at infinity (identity)
|
|
116
|
+
```
|
|
117
|
+
|
|
87
118
|
```bash
|
|
88
119
|
# Chinese Remainder Theorem
|
|
89
120
|
foxmath crt --r 2 3 2 --m 3 5 7
|
|
@@ -114,8 +145,18 @@ foxmath challenge --topic legendre --count 2 --seed 1 --reveal
|
|
|
114
145
|
Answer: -1
|
|
115
146
|
```
|
|
116
147
|
|
|
148
|
+
```bash
|
|
149
|
+
# Baby-size discrete log: given G and Q = k·G, recover k.
|
|
150
|
+
# Easy at this scale — the same problem at secp256k1's ~10^77 is why ECDSA works.
|
|
151
|
+
foxmath challenge --topic ecdlp --count 1 --seed 9 --reveal
|
|
152
|
+
```
|
|
153
|
+
```
|
|
154
|
+
1. On y² = x³ + 1x + 5 (mod 193) with G = (1, 59): find k (2 ≤ k ≤ 20) such that k·G = (60, 171)
|
|
155
|
+
Answer: 16
|
|
156
|
+
```
|
|
157
|
+
|
|
117
158
|
Drop `--reveal` to answer interactively instead, with scoring at the end.
|
|
118
|
-
`--topic` accepts `legendre`, `crt`, or `mixed` (default).
|
|
159
|
+
`--topic` accepts `legendre`, `crt`, `ecdlp`, or `mixed` (default).
|
|
119
160
|
|
|
120
161
|
```bash
|
|
121
162
|
# JSON output
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**Minimalist CLI for math & cryptography exploration.**
|
|
4
4
|
|
|
5
|
+
**More about me / other projects:** [abhrankan.netlify.app](https://abhrankan.netlify.app)
|
|
6
|
+
|
|
5
7
|
One binary. Clean output. Zero footguns. Educational by design.
|
|
6
8
|
|
|
7
9
|
Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
@@ -11,12 +13,14 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
11
13
|
- Euler/Hermann Machin-like π approximation
|
|
12
14
|
- Elliptic curve point addition (over finite fields), including
|
|
13
15
|
named curves (secp256k1) with on-curve result validation
|
|
16
|
+
- Elliptic curve scalar multiplication k·P (double-and-add), including
|
|
17
|
+
the point at infinity as a first-class result
|
|
14
18
|
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
15
19
|
- Continued fraction expansion and convergents
|
|
16
20
|
- Challenge/quiz mode — practice problems with scoring, or a
|
|
17
21
|
worksheet mode for self-study
|
|
18
22
|
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd,
|
|
19
|
-
foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
23
|
+
foxmath-ecmul, foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
20
24
|
- JSON output mode
|
|
21
25
|
|
|
22
26
|
## Installation
|
|
@@ -32,6 +36,14 @@ cd foxmath
|
|
|
32
36
|
pip install -e .
|
|
33
37
|
```
|
|
34
38
|
|
|
39
|
+
Check which version you have installed:
|
|
40
|
+
```bash
|
|
41
|
+
foxmath --version
|
|
42
|
+
```
|
|
43
|
+
```
|
|
44
|
+
foxmath 0.6.2
|
|
45
|
+
```
|
|
46
|
+
|
|
35
47
|
## Usage Examples
|
|
36
48
|
|
|
37
49
|
```bash
|
|
@@ -65,6 +77,25 @@ Result point: (89565891926547004231252920425935692360644145829622209833684329913
|
|
|
65
77
|
✓ on curve
|
|
66
78
|
```
|
|
67
79
|
|
|
80
|
+
```bash
|
|
81
|
+
# Scalar multiplication — same curves, O(log k) double-and-add
|
|
82
|
+
foxmath ecmul --curve toy193 --k 7
|
|
83
|
+
```
|
|
84
|
+
```
|
|
85
|
+
Result point: (66, 24)
|
|
86
|
+
✓ on curve
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# toy193: y² = x³ + x + 5 (mod 193), #E = 193 (prime) — small enough to
|
|
91
|
+
# hand-verify, structured exactly like secp256k1. G = (1, 59).
|
|
92
|
+
# k = 0 or a multiple of the group order lands on the identity:
|
|
93
|
+
foxmath ecmul --curve toy193 --k 193
|
|
94
|
+
```
|
|
95
|
+
```
|
|
96
|
+
Result: point at infinity (identity)
|
|
97
|
+
```
|
|
98
|
+
|
|
68
99
|
```bash
|
|
69
100
|
# Chinese Remainder Theorem
|
|
70
101
|
foxmath crt --r 2 3 2 --m 3 5 7
|
|
@@ -95,8 +126,18 @@ foxmath challenge --topic legendre --count 2 --seed 1 --reveal
|
|
|
95
126
|
Answer: -1
|
|
96
127
|
```
|
|
97
128
|
|
|
129
|
+
```bash
|
|
130
|
+
# Baby-size discrete log: given G and Q = k·G, recover k.
|
|
131
|
+
# Easy at this scale — the same problem at secp256k1's ~10^77 is why ECDSA works.
|
|
132
|
+
foxmath challenge --topic ecdlp --count 1 --seed 9 --reveal
|
|
133
|
+
```
|
|
134
|
+
```
|
|
135
|
+
1. On y² = x³ + 1x + 5 (mod 193) with G = (1, 59): find k (2 ≤ k ≤ 20) such that k·G = (60, 171)
|
|
136
|
+
Answer: 16
|
|
137
|
+
```
|
|
138
|
+
|
|
98
139
|
Drop `--reveal` to answer interactively instead, with scoring at the end.
|
|
99
|
-
`--topic` accepts `legendre`, `crt`, or `mixed` (default).
|
|
140
|
+
`--topic` accepts `legendre`, `crt`, `ecdlp`, or `mixed` (default).
|
|
100
141
|
|
|
101
142
|
```bash
|
|
102
143
|
# JSON output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: foxmath
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Minimalist CLI for math & cryptography exploration 🦊
|
|
5
5
|
Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -21,6 +21,8 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
**Minimalist CLI for math & cryptography exploration.**
|
|
23
23
|
|
|
24
|
+
**More about me / other projects:** [abhrankan.netlify.app](https://abhrankan.netlify.app)
|
|
25
|
+
|
|
24
26
|
One binary. Clean output. Zero footguns. Educational by design.
|
|
25
27
|
|
|
26
28
|
Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
@@ -30,12 +32,14 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
|
|
|
30
32
|
- Euler/Hermann Machin-like π approximation
|
|
31
33
|
- Elliptic curve point addition (over finite fields), including
|
|
32
34
|
named curves (secp256k1) with on-curve result validation
|
|
35
|
+
- Elliptic curve scalar multiplication k·P (double-and-add), including
|
|
36
|
+
the point at infinity as a first-class result
|
|
33
37
|
- Chinese Remainder Theorem solver (handles non-coprime moduli)
|
|
34
38
|
- Continued fraction expansion and convergents
|
|
35
39
|
- Challenge/quiz mode — practice problems with scoring, or a
|
|
36
40
|
worksheet mode for self-study
|
|
37
41
|
- Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd,
|
|
38
|
-
foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
42
|
+
foxmath-ecmul, foxmath-crt, foxmath-cf, foxmath-challenge)
|
|
39
43
|
- JSON output mode
|
|
40
44
|
|
|
41
45
|
## Installation
|
|
@@ -51,6 +55,14 @@ cd foxmath
|
|
|
51
55
|
pip install -e .
|
|
52
56
|
```
|
|
53
57
|
|
|
58
|
+
Check which version you have installed:
|
|
59
|
+
```bash
|
|
60
|
+
foxmath --version
|
|
61
|
+
```
|
|
62
|
+
```
|
|
63
|
+
foxmath 0.6.2
|
|
64
|
+
```
|
|
65
|
+
|
|
54
66
|
## Usage Examples
|
|
55
67
|
|
|
56
68
|
```bash
|
|
@@ -84,6 +96,25 @@ Result point: (89565891926547004231252920425935692360644145829622209833684329913
|
|
|
84
96
|
✓ on curve
|
|
85
97
|
```
|
|
86
98
|
|
|
99
|
+
```bash
|
|
100
|
+
# Scalar multiplication — same curves, O(log k) double-and-add
|
|
101
|
+
foxmath ecmul --curve toy193 --k 7
|
|
102
|
+
```
|
|
103
|
+
```
|
|
104
|
+
Result point: (66, 24)
|
|
105
|
+
✓ on curve
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# toy193: y² = x³ + x + 5 (mod 193), #E = 193 (prime) — small enough to
|
|
110
|
+
# hand-verify, structured exactly like secp256k1. G = (1, 59).
|
|
111
|
+
# k = 0 or a multiple of the group order lands on the identity:
|
|
112
|
+
foxmath ecmul --curve toy193 --k 193
|
|
113
|
+
```
|
|
114
|
+
```
|
|
115
|
+
Result: point at infinity (identity)
|
|
116
|
+
```
|
|
117
|
+
|
|
87
118
|
```bash
|
|
88
119
|
# Chinese Remainder Theorem
|
|
89
120
|
foxmath crt --r 2 3 2 --m 3 5 7
|
|
@@ -114,8 +145,18 @@ foxmath challenge --topic legendre --count 2 --seed 1 --reveal
|
|
|
114
145
|
Answer: -1
|
|
115
146
|
```
|
|
116
147
|
|
|
148
|
+
```bash
|
|
149
|
+
# Baby-size discrete log: given G and Q = k·G, recover k.
|
|
150
|
+
# Easy at this scale — the same problem at secp256k1's ~10^77 is why ECDSA works.
|
|
151
|
+
foxmath challenge --topic ecdlp --count 1 --seed 9 --reveal
|
|
152
|
+
```
|
|
153
|
+
```
|
|
154
|
+
1. On y² = x³ + 1x + 5 (mod 193) with G = (1, 59): find k (2 ≤ k ≤ 20) such that k·G = (60, 171)
|
|
155
|
+
Answer: 16
|
|
156
|
+
```
|
|
157
|
+
|
|
117
158
|
Drop `--reveal` to answer interactively instead, with scoring at the end.
|
|
118
|
-
`--topic` accepts `legendre`, `crt`, or `mixed` (default).
|
|
159
|
+
`--topic` accepts `legendre`, `crt`, `ecdlp`, or `mixed` (default).
|
|
119
160
|
|
|
120
161
|
```bash
|
|
121
162
|
# JSON output
|
|
@@ -98,6 +98,12 @@ class TestSymlinkInvocation(unittest.TestCase):
|
|
|
98
98
|
self.assertEqual(code, 0)
|
|
99
99
|
self.assertIn("Answer:", out)
|
|
100
100
|
|
|
101
|
+
def test_ecmul_symlink(self):
|
|
102
|
+
script = self._make_symlink_script("foxmath-ecmul")
|
|
103
|
+
code, out, err = run("--curve", "toy193", "--k", "7", script=script)
|
|
104
|
+
self.assertEqual(code, 0)
|
|
105
|
+
self.assertIn("Result point: (66, 24)", out)
|
|
106
|
+
|
|
101
107
|
def test_symlink_with_json(self):
|
|
102
108
|
script = self._make_symlink_script("foxmath-legendre")
|
|
103
109
|
code, out, err = run("2", "7", "--json", script=script)
|
|
@@ -140,6 +146,58 @@ class TestNormalUsage(unittest.TestCase):
|
|
|
140
146
|
self.assertEqual(code, 0)
|
|
141
147
|
self.assertIn("Result point: (14, 2)", out)
|
|
142
148
|
|
|
149
|
+
def test_ecmul_readme_example_toy193(self):
|
|
150
|
+
# Exact usage/output shown in the README.
|
|
151
|
+
code, out, err = run("ecmul", "--curve", "toy193", "--k", "7")
|
|
152
|
+
self.assertEqual(code, 0)
|
|
153
|
+
self.assertIn("Result point: (66, 24)", out)
|
|
154
|
+
self.assertIn("on curve", out)
|
|
155
|
+
|
|
156
|
+
def test_ecmul_readme_example_identity(self):
|
|
157
|
+
code, out, err = run("ecmul", "--curve", "toy193", "--k", "193")
|
|
158
|
+
self.assertEqual(code, 0)
|
|
159
|
+
self.assertIn("point at infinity", out)
|
|
160
|
+
|
|
161
|
+
def test_ecmul_secp256k1_matches_known_doubling(self):
|
|
162
|
+
code, out, err = run("ecmul", "--curve", "secp256k1", "--k", "2")
|
|
163
|
+
self.assertEqual(code, 0)
|
|
164
|
+
self.assertIn("89565891926547004231252920425935692360644145829622209833684329913297188986597", out)
|
|
165
|
+
|
|
166
|
+
def test_ecmul_json_output(self):
|
|
167
|
+
code, out, err = run("ecmul", "--curve", "toy193", "--k", "7", "--json")
|
|
168
|
+
self.assertEqual(code, 0)
|
|
169
|
+
import json
|
|
170
|
+
parsed = json.loads(out)
|
|
171
|
+
self.assertEqual(parsed["result"], [66, 24])
|
|
172
|
+
self.assertTrue(parsed["result_on_curve"])
|
|
173
|
+
|
|
174
|
+
def test_ecmul_json_output_infinity(self):
|
|
175
|
+
code, out, err = run("ecmul", "--curve", "toy193", "--k", "193", "--json")
|
|
176
|
+
self.assertEqual(code, 0)
|
|
177
|
+
import json
|
|
178
|
+
parsed = json.loads(out)
|
|
179
|
+
self.assertEqual(parsed["result"], "infinity")
|
|
180
|
+
|
|
181
|
+
def test_ecmul_manual_mode_no_curve(self):
|
|
182
|
+
code, out, err = run("ecmul", "--p", "97", "--x", "3", "--y", "6",
|
|
183
|
+
"--a", "2", "--k", "3")
|
|
184
|
+
self.assertEqual(code, 0)
|
|
185
|
+
self.assertIn("Result point:", out)
|
|
186
|
+
|
|
187
|
+
def test_ecmul_a_override(self):
|
|
188
|
+
code, out, err = run("ecmul", "--curve", "toy97", "--a", "2", "--k", "3")
|
|
189
|
+
self.assertEqual(code, 0)
|
|
190
|
+
|
|
191
|
+
def test_ecmul_missing_curve_and_incomplete_manual_args_errors(self):
|
|
192
|
+
code, out, err = run("ecmul", "--k", "5")
|
|
193
|
+
self.assertEqual(code, 1)
|
|
194
|
+
self.assertIn("Error", err)
|
|
195
|
+
|
|
196
|
+
def test_ecmul_negative_k_errors(self):
|
|
197
|
+
code, out, err = run("ecmul", "--curve", "toy193", "--k", "-5")
|
|
198
|
+
self.assertEqual(code, 1)
|
|
199
|
+
self.assertIn("Error", err)
|
|
200
|
+
|
|
143
201
|
def test_crt(self):
|
|
144
202
|
code, out, err = run("crt", "--r", "2", "3", "2", "--m", "3", "5", "7")
|
|
145
203
|
self.assertEqual(code, 0)
|
|
@@ -13,7 +13,7 @@ from decimal import Decimal
|
|
|
13
13
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
|
14
14
|
|
|
15
15
|
from foxmath.foxmath import (
|
|
16
|
-
legendre_symbol, euler_hermann_pi_approx, ec_point_add, crt_solve,
|
|
16
|
+
legendre_symbol, euler_hermann_pi_approx, ec_point_add, ec_mul, crt_solve,
|
|
17
17
|
continued_fraction, cf_convergents, CURVES, is_on_curve,
|
|
18
18
|
generate_problem,
|
|
19
19
|
)
|
|
@@ -154,6 +154,103 @@ class TestContinuedFractions(unittest.TestCase):
|
|
|
154
154
|
self.assertEqual(convs[-1], (expected.numerator, expected.denominator))
|
|
155
155
|
|
|
156
156
|
|
|
157
|
+
class TestECMul(unittest.TestCase):
|
|
158
|
+
"""
|
|
159
|
+
ec_mul (scalar multiplication / double-and-add) had no test coverage
|
|
160
|
+
at all despite being a documented headline feature -- these tests
|
|
161
|
+
were added after finding and closing that gap, not in response to
|
|
162
|
+
any bug (cross-validation below found none).
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
def _naive_mul(self, k, x, y, a, p):
|
|
166
|
+
# Independent reference: k repeated additions via the
|
|
167
|
+
# user-facing ec_point_add, rather than ec_mul's own
|
|
168
|
+
# double-and-add logic.
|
|
169
|
+
if k == 0:
|
|
170
|
+
return None
|
|
171
|
+
result = None
|
|
172
|
+
point = (x, y)
|
|
173
|
+
for _ in range(k):
|
|
174
|
+
if result is None:
|
|
175
|
+
result = point
|
|
176
|
+
else:
|
|
177
|
+
try:
|
|
178
|
+
result = ec_point_add(result[0], result[1], point[0], point[1], a, p)
|
|
179
|
+
except ValueError:
|
|
180
|
+
result = None # hit the point at infinity
|
|
181
|
+
return result
|
|
182
|
+
|
|
183
|
+
def test_matches_naive_repeated_addition_toy97(self):
|
|
184
|
+
c = CURVES["toy97"]
|
|
185
|
+
for k in [1, 2, 3, 5, 7, 10, 13, 20]:
|
|
186
|
+
fast = ec_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
187
|
+
naive = self._naive_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
188
|
+
self.assertEqual(fast, naive, f"mismatch at k={k}")
|
|
189
|
+
|
|
190
|
+
def test_matches_naive_repeated_addition_toy193(self):
|
|
191
|
+
c = CURVES["toy193"]
|
|
192
|
+
for k in [1, 2, 3, 5, 7, 10, 13, 20]:
|
|
193
|
+
fast = ec_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
194
|
+
naive = self._naive_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
195
|
+
self.assertEqual(fast, naive, f"mismatch at k={k}")
|
|
196
|
+
|
|
197
|
+
def test_secp256k1_matches_independent_reference_including_large_k(self):
|
|
198
|
+
# Reference points cross-checked independently against the
|
|
199
|
+
# `ecdsa` library's SECP256k1.generator * k (same verification
|
|
200
|
+
# approach the pre-existing doubling test above uses). Values
|
|
201
|
+
# are baked in rather than imported at test time so this suite
|
|
202
|
+
# doesn't pick up a runtime dependency on `ecdsa` -- foxmath
|
|
203
|
+
# itself stays pure stdlib (dependencies = [] in pyproject.toml).
|
|
204
|
+
c = CURVES["secp256k1"]
|
|
205
|
+
reference = {
|
|
206
|
+
1: (55066263022277343669578718895168534326250603453777594175500187360389116729240,
|
|
207
|
+
32670510020758816978083085130507043184471273380659243275938904335757337482424),
|
|
208
|
+
2: (89565891926547004231252920425935692360644145829622209833684329913297188986597,
|
|
209
|
+
12158399299693830322967808612713398636155367887041628176798871954788371653930),
|
|
210
|
+
3: (112711660439710606056748659173929673102114977341539408544630613555209775888121,
|
|
211
|
+
25583027980570883691656905877401976406448868254816295069919888960541586679410),
|
|
212
|
+
7: (41948375291644419605210209193538855353224492619856392092318293986323063962044,
|
|
213
|
+
48361766907851246668144012348516735800090617714386977531302791340517493990618),
|
|
214
|
+
100: (107303582290733097924842193972465022053148211775194373671539518313500194639752,
|
|
215
|
+
103795966108782717446806684023742168462365449272639790795591544606836007446638),
|
|
216
|
+
12345: (108607064596551879580190606910245687803607295064141551927605737287325610911759,
|
|
217
|
+
6661302038839728943522144359728938428925407345457796456954441906546235843221),
|
|
218
|
+
# A scalar well beyond anything repeated addition could feasibly verify.
|
|
219
|
+
2**64 + 17: (5136902638900309784563324385263118307222550852398366895311091889317425008288,
|
|
220
|
+
19220983661714808463696268177333916812952649794945216772534518833957341771155),
|
|
221
|
+
}
|
|
222
|
+
for k, expected in reference.items():
|
|
223
|
+
fast = ec_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
224
|
+
self.assertEqual(fast, expected, f"mismatch at k={k}")
|
|
225
|
+
|
|
226
|
+
def test_toy193_has_prime_order_full_cycle(self):
|
|
227
|
+
# README's documented claim: #E = 193 (prime), so every non-zero
|
|
228
|
+
# multiple up to 192 should land on a distinct on-curve point,
|
|
229
|
+
# and k=193 (the order) should land on infinity.
|
|
230
|
+
c = CURVES["toy193"]
|
|
231
|
+
seen = set()
|
|
232
|
+
for k in range(1, 193):
|
|
233
|
+
pt = ec_mul(k, c["gx"], c["gy"], c["a"], c["p"])
|
|
234
|
+
self.assertIsNotNone(pt, f"unexpected infinity at k={k}")
|
|
235
|
+
self.assertTrue(is_on_curve(pt[0], pt[1], c["a"], c["b"], c["p"]))
|
|
236
|
+
self.assertNotIn(pt, seen, f"duplicate point at k={k} -- order is not 193")
|
|
237
|
+
seen.add(pt)
|
|
238
|
+
self.assertEqual(len(seen), 192)
|
|
239
|
+
|
|
240
|
+
def test_k_zero_is_infinity(self):
|
|
241
|
+
c = CURVES["toy193"]
|
|
242
|
+
self.assertIsNone(ec_mul(0, c["gx"], c["gy"], c["a"], c["p"]))
|
|
243
|
+
|
|
244
|
+
def test_k_equal_to_order_is_infinity(self):
|
|
245
|
+
c = CURVES["toy193"]
|
|
246
|
+
self.assertIsNone(ec_mul(193, c["gx"], c["gy"], c["a"], c["p"]))
|
|
247
|
+
|
|
248
|
+
def test_negative_k_raises(self):
|
|
249
|
+
c = CURVES["toy193"]
|
|
250
|
+
with self.assertRaises(ValueError):
|
|
251
|
+
ec_mul(-5, c["gx"], c["gy"], c["a"], c["p"])
|
|
252
|
+
|
|
253
|
+
|
|
157
254
|
class TestNamedCurves(unittest.TestCase):
|
|
158
255
|
def test_secp256k1_generator_is_on_curve(self):
|
|
159
256
|
c = CURVES["secp256k1"]
|
|
@@ -193,10 +290,13 @@ class TestChallengeGenerators(unittest.TestCase):
|
|
|
193
290
|
self.assertEqual(prob["topic"], "crt")
|
|
194
291
|
self.assertIsInstance(prob["answer"], int)
|
|
195
292
|
|
|
196
|
-
def
|
|
293
|
+
def test_mixed_topic_produces_all_kinds(self):
|
|
294
|
+
# mixed draws from every registered generator -- legendre, crt,
|
|
295
|
+
# and ecdlp (this assertion was stale: it predated ecdlp being
|
|
296
|
+
# added to _TOPIC_GENERATORS, and only checked for {legendre, crt}).
|
|
197
297
|
rng = random.Random(1)
|
|
198
298
|
topics = {generate_problem("mixed", rng)["topic"] for _ in range(30)}
|
|
199
|
-
self.assertEqual(topics, {"legendre", "crt"})
|
|
299
|
+
self.assertEqual(topics, {"legendre", "crt", "ecdlp"})
|
|
200
300
|
|
|
201
301
|
def test_reproducible_with_same_seed(self):
|
|
202
302
|
p1 = generate_problem("legendre", random.Random(99))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|