summaryrefslogtreecommitdiffstats
path: root/internal/secp256k1/field.go
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-20 18:52:08 -0800
committerClawd <ai@clawd.bot>2026-02-20 18:52:08 -0800
commitd641f4566f051656bae79e406155c4f7f65ec338 (patch)
tree4cd01f3fa4585cf719116a303473e792ea67e82a /internal/secp256k1/field.go
parent84709fd67c02058334519ebee9c110f68b33d9b4 (diff)
embed secp256k1: replace btcec with internal pure-go implementation
This removes all external dependencies by embedding the secp256k1-learn implementation into internal/secp256k1. Changes: - Add internal/secp256k1 with field arithmetic, curve ops, keys, schnorr - Update keys.go to use internal secp256k1 package - Remove btcec/btcutil dependencies (go.mod is now clean) - All tests pass Tradeoffs: - ~10x slower crypto ops vs btcec (acceptable for nostr use case) - Not constant-time (documented limitation) - Zero external dependencies Refs: code.northwest.io/secp256k1-learn
Diffstat (limited to 'internal/secp256k1/field.go')
-rw-r--r--internal/secp256k1/field.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/internal/secp256k1/field.go b/internal/secp256k1/field.go
new file mode 100644
index 0000000..13cdffd
--- /dev/null
+++ b/internal/secp256k1/field.go
@@ -0,0 +1,92 @@
1package secp256k1
2
3import (
4 "fmt"
5 "math/big"
6)
7
8// The prime for secp256k1: 2^256 - 2^32 - 977
9// All field arithmetic happens mod this number
10var P, _ = new(big.Int).SetString(
11 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
12 16,
13)
14
15// FieldElement represents a number in our finite field (mod P)
16type FieldElement struct {
17 value *big.Int
18}
19
20// NewFieldElement creates a field element from a big.Int
21// It automatically reduces mod P
22func NewFieldElement(v *big.Int) *FieldElement {
23 result := new(big.Int).Mod(v, P)
24 return &FieldElement{value: result}
25}
26
27// NewFieldElementFromInt64 is a convenience for small numbers
28func NewFieldElementFromInt64(v int64) *FieldElement {
29 return NewFieldElement(big.NewInt(v))
30}
31
32// Add returns (a + b) mod P
33func (a *FieldElement) Add(b *FieldElement) *FieldElement {
34 result := new(big.Int).Add(a.value, b.value)
35 return NewFieldElement(result)
36}
37
38// Sub returns (a - b) mod P
39func (a *FieldElement) Sub(b *FieldElement) *FieldElement {
40 result := new(big.Int).Sub(a.value, b.value)
41 return NewFieldElement(result)
42}
43
44// Mul returns (a * b) mod P
45func (a *FieldElement) Mul(b *FieldElement) *FieldElement {
46 result := new(big.Int).Mul(a.value, b.value)
47 return NewFieldElement(result)
48}
49
50// Div returns (a / b) mod P
51// Division in a field = multiply by the inverse
52func (a *FieldElement) Div(b *FieldElement) *FieldElement {
53 // a / b = a * b^(-1)
54 // b^(-1) mod P = b^(P-2) mod P (Fermat's little theorem)
55 inverse := b.Inverse()
56 return a.Mul(inverse)
57}
58
59// Inverse returns a^(-1) mod P using Fermat's little theorem
60// a^(-1) = a^(P-2) mod P
61func (a *FieldElement) Inverse() *FieldElement {
62 // P - 2
63 exp := new(big.Int).Sub(P, big.NewInt(2))
64 // a^(P-2) mod P
65 result := new(big.Int).Exp(a.value, exp, P)
66 return &FieldElement{value: result}
67}
68
69// Square returns a² mod P (convenience method)
70func (a *FieldElement) Square() *FieldElement {
71 return a.Mul(a)
72}
73
74// Equal checks if two field elements are the same
75func (a *FieldElement) Equal(b *FieldElement) bool {
76 return a.value.Cmp(b.value) == 0
77}
78
79// IsZero checks if the element is zero
80func (a *FieldElement) IsZero() bool {
81 return a.value.Sign() == 0
82}
83
84// String returns hex representation
85func (a *FieldElement) String() string {
86 return fmt.Sprintf("%064x", a.value)
87}
88
89// Clone returns a copy
90func (a *FieldElement) Clone() *FieldElement {
91 return &FieldElement{value: new(big.Int).Set(a.value)}
92}