foxmath 0.3.0__tar.gz → 0.4.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foxmath
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Minimalist CLI for math & cryptography exploration 🦊
5
5
  Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
6
6
  License: MIT
@@ -28,12 +28,13 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
28
28
  ## Features
29
29
  - Legendre symbol
30
30
  - Euler/Hermann Machin-like π approximation
31
- - Elliptic curve point addition (over finite fields)
31
+ - Elliptic curve point addition (over finite fields), including
32
+ named curves (secp256k1) with on-curve result validation
32
33
  - Chinese Remainder Theorem solver (handles non-coprime moduli)
33
34
  - Continued fraction expansion and convergents
34
35
  - Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt, foxmath-cf)
35
36
  - JSON output mode
36
- - Coming soon: challenge mode, more curves
37
+ - Coming soon: challenge mode
37
38
 
38
39
  ## Installation
39
40
 
@@ -72,6 +73,15 @@ foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
72
73
  Result point: (14, 2)
73
74
  ```
74
75
 
76
+ ```bash
77
+ # Named curve (secp256k1) — computes 2G by default
78
+ foxmath ecadd --curve secp256k1
79
+ ```
80
+ ```
81
+ Result point: (89565891926547004231252920425935692360644145829622209833684329913297188986597, 12158399299693830322967808612713398636155367887041628176798871954788371653930)
82
+ ✓ on curve
83
+ ```
84
+
75
85
  ```bash
76
86
  # Chinese Remainder Theorem
77
87
  foxmath crt --r 2 3 2 --m 3 5 7
@@ -9,12 +9,13 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
9
9
  ## Features
10
10
  - Legendre symbol
11
11
  - Euler/Hermann Machin-like π approximation
12
- - Elliptic curve point addition (over finite fields)
12
+ - Elliptic curve point addition (over finite fields), including
13
+ named curves (secp256k1) with on-curve result validation
13
14
  - Chinese Remainder Theorem solver (handles non-coprime moduli)
14
15
  - Continued fraction expansion and convergents
15
16
  - Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt, foxmath-cf)
16
17
  - JSON output mode
17
- - Coming soon: challenge mode, more curves
18
+ - Coming soon: challenge mode
18
19
 
19
20
  ## Installation
20
21
 
@@ -53,6 +54,15 @@ foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
53
54
  Result point: (14, 2)
54
55
  ```
55
56
 
57
+ ```bash
58
+ # Named curve (secp256k1) — computes 2G by default
59
+ foxmath ecadd --curve secp256k1
60
+ ```
61
+ ```
62
+ Result point: (89565891926547004231252920425935692360644145829622209833684329913297188986597, 12158399299693830322967808612713398636155367887041628176798871954788371653930)
63
+ ✓ on curve
64
+ ```
65
+
56
66
  ```bash
57
67
  # Chinese Remainder Theorem
58
68
  foxmath crt --r 2 3 2 --m 3 5 7
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "foxmath"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "Minimalist CLI for math & cryptography exploration 🦊"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -54,6 +54,28 @@ def ec_point_add(x1, y1, x2, y2, a, p):
54
54
  y3 = (lam * (x1 - x3) - y1) % p
55
55
  return x3, y3
56
56
 
57
+ # Named curve presets: a, b, p, and the standard generator point G (if defined).
58
+ CURVES = {
59
+ "secp256k1": {
60
+ "a": 0,
61
+ "b": 7,
62
+ "p": 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
63
+ "gx": 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
64
+ "gy": 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
65
+ },
66
+ "toy97": { # small curve for quick hand-checkable examples: y^2 = x^3 + 2x + 3 (mod 97)
67
+ "a": 2,
68
+ "b": 3,
69
+ "p": 97,
70
+ "gx": 3,
71
+ "gy": 6,
72
+ },
73
+ }
74
+
75
+ def is_on_curve(x: int, y: int, a: int, b: int, p: int) -> bool:
76
+ """Check y^2 = x^3 + a*x + b (mod p)."""
77
+ return (y * y - (x ** 3 + a * x + b)) % p == 0
78
+
57
79
  def _extended_gcd(a: int, b: int):
58
80
  """Returns (g, x, y) such that a*x + b*y == g == gcd(a, b)."""
59
81
  old_r, r = a, b
@@ -171,12 +193,14 @@ def main():
171
193
 
172
194
  # Elliptic curve add
173
195
  ec = subparsers.add_parser("ecadd", help="Elliptic curve point addition")
174
- ec.add_argument("--x1", type=int, required=True)
175
- ec.add_argument("--y1", type=int, required=True)
176
- ec.add_argument("--x2", type=int, required=True)
177
- ec.add_argument("--y2", type=int, required=True)
178
- ec.add_argument("--a", type=int, default=-3, help="Curve parameter a")
179
- ec.add_argument("--p", type=int, required=True, help="Prime modulus")
196
+ ec.add_argument("--curve", choices=sorted(CURVES.keys()),
197
+ help="Named curve (provides a, b, p, and generator G)")
198
+ ec.add_argument("--x1", type=int, help="Defaults to G's x if --curve is given")
199
+ ec.add_argument("--y1", type=int, help="Defaults to G's y if --curve is given")
200
+ ec.add_argument("--x2", type=int, help="Defaults to (x1,y1) if omitted (i.e. doubling)")
201
+ ec.add_argument("--y2", type=int, help="Defaults to (x1,y1) if omitted (i.e. doubling)")
202
+ ec.add_argument("--a", type=int, help="Curve parameter a (overrides --curve if given)")
203
+ ec.add_argument("--p", type=int, help="Prime modulus (overrides --curve if given)")
180
204
 
181
205
  # Chinese Remainder Theorem
182
206
  crt = subparsers.add_parser("crt", help="Solve a system of congruences x = r (mod m)")
@@ -207,10 +231,41 @@ def main():
207
231
  print(f"π ≈ {approx} ({args.terms} terms)")
208
232
 
209
233
  elif cmd == "ecadd" or args.command == "ecadd":
210
- x3, y3 = ec_point_add(args.x1, args.y1, args.x2, args.y2, args.a, args.p)
211
- result.update({"command": "ecadd", "result": (x3, y3)})
234
+ a, b = args.a, None
235
+ p = args.p
236
+ x1, y1 = args.x1, args.y1
237
+
238
+ if args.curve:
239
+ preset = CURVES[args.curve]
240
+ a = preset["a"] if a is None else a
241
+ p = preset["p"] if p is None else p
242
+ b = preset["b"]
243
+ if x1 is None:
244
+ x1, y1 = preset["gx"], preset["gy"]
245
+ elif a is None:
246
+ a = -3 # historical default for manual (non-curve) mode
247
+
248
+ if a is None or p is None or x1 is None or y1 is None:
249
+ raise ValueError(
250
+ "Provide --curve NAME, or at least --p --x1 --y1 manually "
251
+ "(--a defaults to -3 if omitted)."
252
+ )
253
+
254
+ x2 = x1 if args.x2 is None else args.x2
255
+ y2 = y1 if args.y2 is None else args.y2
256
+
257
+ x3, y3 = ec_point_add(x1, y1, x2, y2, a, p)
258
+ result.update({
259
+ "command": "ecadd", "curve": args.curve,
260
+ "p1": [x1, y1], "p2": [x2, y2], "result": (x3, y3),
261
+ })
262
+ if b is not None:
263
+ result["result_on_curve"] = is_on_curve(x3, y3, a, b, p)
212
264
  if not json_output:
213
265
  print(f"Result point: ({x3}, {y3})")
266
+ if b is not None:
267
+ status = "✓ on curve" if result["result_on_curve"] else "✗ NOT on curve"
268
+ print(f" {status}")
214
269
 
215
270
  elif cmd == "crt" or args.command == "crt":
216
271
  x, m = crt_solve(args.r, args.m)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foxmath
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Minimalist CLI for math & cryptography exploration 🦊
5
5
  Author-email: Abhrankan Chakrabarti <abhrankan@gmail.com>
6
6
  License: MIT
@@ -28,12 +28,13 @@ Built with curiosity — for students, tinkerers, and crypto/math enthusiasts.
28
28
  ## Features
29
29
  - Legendre symbol
30
30
  - Euler/Hermann Machin-like π approximation
31
- - Elliptic curve point addition (over finite fields)
31
+ - Elliptic curve point addition (over finite fields), including
32
+ named curves (secp256k1) with on-curve result validation
32
33
  - Chinese Remainder Theorem solver (handles non-coprime moduli)
33
34
  - Continued fraction expansion and convergents
34
35
  - Symlink-friendly (foxmath-legendre, foxmath-pi, foxmath-ecadd, foxmath-crt, foxmath-cf)
35
36
  - JSON output mode
36
- - Coming soon: challenge mode, more curves
37
+ - Coming soon: challenge mode
37
38
 
38
39
  ## Installation
39
40
 
@@ -72,6 +73,15 @@ foxmath-ecadd --x1 1 --y1 2 --x2 3 --y2 4 --p 17
72
73
  Result point: (14, 2)
73
74
  ```
74
75
 
76
+ ```bash
77
+ # Named curve (secp256k1) — computes 2G by default
78
+ foxmath ecadd --curve secp256k1
79
+ ```
80
+ ```
81
+ Result point: (89565891926547004231252920425935692360644145829622209833684329913297188986597, 12158399299693830322967808612713398636155367887041628176798871954788371653930)
82
+ ✓ on curve
83
+ ```
84
+
75
85
  ```bash
76
86
  # Chinese Remainder Theorem
77
87
  foxmath crt --r 2 3 2 --m 3 5 7
@@ -110,6 +110,29 @@ class TestNormalUsage(unittest.TestCase):
110
110
  self.assertEqual(code, 1)
111
111
  self.assertIn("point at infinity", err)
112
112
 
113
+ def test_ecadd_curve_secp256k1_defaults_to_doubling_generator(self):
114
+ code, out, err = run("ecadd", "--curve", "secp256k1")
115
+ self.assertEqual(code, 0)
116
+ self.assertIn("89565891926547004231252920425935692360644145829622209833684329913297188986597", out)
117
+ self.assertIn("on curve", out)
118
+
119
+ def test_ecadd_curve_manual_a_override(self):
120
+ # --a explicitly provided should override the curve preset's a.
121
+ code, out, err = run("ecadd", "--curve", "toy97", "--a", "2")
122
+ self.assertEqual(code, 0)
123
+
124
+ def test_ecadd_no_args_at_all_errors_clearly(self):
125
+ code, out, err = run("ecadd")
126
+ self.assertEqual(code, 1)
127
+ self.assertIn("Error", err)
128
+
129
+ def test_ecadd_backward_compatible_manual_call(self):
130
+ # Original documented usage: --a omitted, defaults to -3.
131
+ code, out, err = run("ecadd", "--x1", "1", "--y1", "2",
132
+ "--x2", "3", "--y2", "4", "--p", "17")
133
+ self.assertEqual(code, 0)
134
+ self.assertIn("Result point: (14, 2)", out)
135
+
113
136
  def test_crt(self):
114
137
  code, out, err = run("crt", "--r", "2", "3", "2", "--m", "3", "5", "7")
115
138
  self.assertEqual(code, 0)
@@ -14,7 +14,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..",
14
14
 
15
15
  from foxmath.foxmath import (
16
16
  legendre_symbol, euler_hermann_pi_approx, ec_point_add, crt_solve,
17
- continued_fraction, cf_convergents,
17
+ continued_fraction, cf_convergents, CURVES, is_on_curve,
18
18
  )
19
19
 
20
20
  # Reference pi digits (mpmath, 250 dps of working precision), "3" + fractional digits.
@@ -152,5 +152,28 @@ class TestContinuedFractions(unittest.TestCase):
152
152
  self.assertEqual(convs[-1], (expected.numerator, expected.denominator))
153
153
 
154
154
 
155
+ class TestNamedCurves(unittest.TestCase):
156
+ def test_secp256k1_generator_is_on_curve(self):
157
+ c = CURVES["secp256k1"]
158
+ self.assertTrue(is_on_curve(c["gx"], c["gy"], c["a"], c["b"], c["p"]))
159
+
160
+ def test_secp256k1_doubling_matches_known_value(self):
161
+ # Cross-checked independently against the `ecdsa` library's
162
+ # SECP256k1.generator * 2.
163
+ c = CURVES["secp256k1"]
164
+ x2, y2 = ec_point_add(c["gx"], c["gy"], c["gx"], c["gy"], c["a"], c["p"])
165
+ self.assertEqual(
166
+ x2, 89565891926547004231252920425935692360644145829622209833684329913297188986597
167
+ )
168
+ self.assertEqual(
169
+ y2, 12158399299693830322967808612713398636155367887041628176798871954788371653930
170
+ )
171
+ self.assertTrue(is_on_curve(x2, y2, c["a"], c["b"], c["p"]))
172
+
173
+ def test_toy97_generator_is_on_curve(self):
174
+ c = CURVES["toy97"]
175
+ self.assertTrue(is_on_curve(c["gx"], c["gy"], c["a"], c["b"], c["p"]))
176
+
177
+
155
178
  if __name__ == "__main__":
156
179
  unittest.main()
File without changes
File without changes
File without changes