From d641f4566f051656bae79e406155c4f7f65ec338 Mon Sep 17 00:00:00 2001 From: Clawd Date: Fri, 20 Feb 2026 18:52:08 -0800 Subject: 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 --- internal/secp256k1/field.go | 92 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 internal/secp256k1/field.go (limited to 'internal/secp256k1/field.go') 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 @@ +package secp256k1 + +import ( + "fmt" + "math/big" +) + +// The prime for secp256k1: 2^256 - 2^32 - 977 +// All field arithmetic happens mod this number +var P, _ = new(big.Int).SetString( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", + 16, +) + +// FieldElement represents a number in our finite field (mod P) +type FieldElement struct { + value *big.Int +} + +// NewFieldElement creates a field element from a big.Int +// It automatically reduces mod P +func NewFieldElement(v *big.Int) *FieldElement { + result := new(big.Int).Mod(v, P) + return &FieldElement{value: result} +} + +// NewFieldElementFromInt64 is a convenience for small numbers +func NewFieldElementFromInt64(v int64) *FieldElement { + return NewFieldElement(big.NewInt(v)) +} + +// Add returns (a + b) mod P +func (a *FieldElement) Add(b *FieldElement) *FieldElement { + result := new(big.Int).Add(a.value, b.value) + return NewFieldElement(result) +} + +// Sub returns (a - b) mod P +func (a *FieldElement) Sub(b *FieldElement) *FieldElement { + result := new(big.Int).Sub(a.value, b.value) + return NewFieldElement(result) +} + +// Mul returns (a * b) mod P +func (a *FieldElement) Mul(b *FieldElement) *FieldElement { + result := new(big.Int).Mul(a.value, b.value) + return NewFieldElement(result) +} + +// Div returns (a / b) mod P +// Division in a field = multiply by the inverse +func (a *FieldElement) Div(b *FieldElement) *FieldElement { + // a / b = a * b^(-1) + // b^(-1) mod P = b^(P-2) mod P (Fermat's little theorem) + inverse := b.Inverse() + return a.Mul(inverse) +} + +// Inverse returns a^(-1) mod P using Fermat's little theorem +// a^(-1) = a^(P-2) mod P +func (a *FieldElement) Inverse() *FieldElement { + // P - 2 + exp := new(big.Int).Sub(P, big.NewInt(2)) + // a^(P-2) mod P + result := new(big.Int).Exp(a.value, exp, P) + return &FieldElement{value: result} +} + +// Square returns a² mod P (convenience method) +func (a *FieldElement) Square() *FieldElement { + return a.Mul(a) +} + +// Equal checks if two field elements are the same +func (a *FieldElement) Equal(b *FieldElement) bool { + return a.value.Cmp(b.value) == 0 +} + +// IsZero checks if the element is zero +func (a *FieldElement) IsZero() bool { + return a.value.Sign() == 0 +} + +// String returns hex representation +func (a *FieldElement) String() string { + return fmt.Sprintf("%064x", a.value) +} + +// Clone returns a copy +func (a *FieldElement) Clone() *FieldElement { + return &FieldElement{value: new(big.Int).Set(a.value)} +} -- cgit v1.2.3