package secp256k1 import ( "fmt" "math/big" ) // secp256k1 curve: y² = x³ + 7 // The 'a' coefficient is 0, 'b' is 7 var curveB = NewFieldElementFromInt64(7) // Generator point G for secp256k1 var ( Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16) Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16) G = &Point{ x: NewFieldElement(Gx), y: NewFieldElement(Gy), infinity: false, } ) // Curve order (number of points on the curve) var N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16) // Point represents a point on the secp256k1 curve type Point struct { x, y *FieldElement infinity bool // true if this is the point at infinity (identity) } // NewPoint creates a point from x, y coordinates // Returns error if the point is not on the curve func NewPoint(x, y *FieldElement) (*Point, error) { p := &Point{x: x, y: y, infinity: false} if !p.IsOnCurve() { return nil, fmt.Errorf("point (%s, %s) is not on the curve", x.String(), y.String()) } return p, nil } // Infinity returns the point at infinity (identity element) func Infinity() *Point { return &Point{infinity: true} } // IsInfinity returns true if this is the point at infinity func (p *Point) IsInfinity() bool { return p.infinity } // IsOnCurve checks if the point satisfies y² = x³ + 7 func (p *Point) IsOnCurve() bool { if p.infinity { return true } // y² = x³ + 7 left := p.y.Square() // y² right := p.x.Square().Mul(p.x).Add(curveB) // x³ + 7 return left.Equal(right) } // Equal checks if two points are the same func (p *Point) Equal(q *Point) bool { if p.infinity && q.infinity { return true } if p.infinity || q.infinity { return false } return p.x.Equal(q.x) && p.y.Equal(q.y) } // Add returns p + q using the elliptic curve addition formulas func (p *Point) Add(q *Point) *Point { // Handle infinity (identity element) if p.infinity { return q } if q.infinity { return p } // If points are inverses (same x, opposite y), return infinity if p.x.Equal(q.x) && !p.y.Equal(q.y) { return Infinity() } // If points are the same, use doubling formula if p.Equal(q) { return p.Double() } // Standard addition formula for P ≠ Q: // slope = (y2 - y1) / (x2 - x1) // x3 = slope² - x1 - x2 // y3 = slope * (x1 - x3) - y1 slope := q.y.Sub(p.y).Div(q.x.Sub(p.x)) x3 := slope.Square().Sub(p.x).Sub(q.x) y3 := slope.Mul(p.x.Sub(x3)).Sub(p.y) return &Point{x: x3, y: y3, infinity: false} } // Double returns 2P (point added to itself) func (p *Point) Double() *Point { if p.infinity { return Infinity() } // If y = 0, tangent is vertical, return infinity if p.y.IsZero() { return Infinity() } // Doubling formula: // slope = (3x² + a) / (2y) -- for secp256k1, a = 0 // x3 = slope² - 2x // y3 = slope * (x - x3) - y three := NewFieldElementFromInt64(3) two := NewFieldElementFromInt64(2) slope := three.Mul(p.x.Square()).Div(two.Mul(p.y)) x3 := slope.Square().Sub(two.Mul(p.x)) y3 := slope.Mul(p.x.Sub(x3)).Sub(p.y) return &Point{x: x3, y: y3, infinity: false} } // ScalarMul returns k * P (point multiplied by scalar) // Uses double-and-add algorithm func (p *Point) ScalarMul(k *big.Int) *Point { result := Infinity() addend := p // Clone k so we don't modify the original scalar := new(big.Int).Set(k) for scalar.Sign() > 0 { // If lowest bit is 1, add current addend if scalar.Bit(0) == 1 { result = result.Add(addend) } // Double the addend addend = addend.Double() // Shift scalar right by 1 scalar.Rsh(scalar, 1) } return result } // Negate returns -P (same x, negated y) func (p *Point) Negate() *Point { if p.infinity { return Infinity() } // -y mod P negY := NewFieldElement(new(big.Int).Sub(P, p.y.value)) return &Point{x: p.x.Clone(), y: negY, infinity: false} } // XBytes returns the x-coordinate as a 32-byte big-endian slice. // Returns nil for the point at infinity. func (p *Point) XBytes() []byte { if p.infinity { return nil } b := p.x.value.Bytes() out := make([]byte, 32) copy(out[32-len(b):], b) return out } // String returns a readable representation func (p *Point) String() string { if p.infinity { return "Point(infinity)" } return fmt.Sprintf("Point(%s, %s)", p.x.String()[:8]+"...", p.y.String()[:8]+"...") }