Remove BtcecSigner implementation and related dependencies; update benchmark reports to reflect changes. The P256K1Signer is now highlighted as the primary implementation, showcasing its performance advantages over the removed BtcecSigner. Additionally, unnecessary indirect dependencies have been cleaned up from the go.mod and go.sum files.

This commit is contained in:
2025-11-04 10:26:24 +00:00
parent e8649cae7b
commit 93af5ef27b
8 changed files with 292 additions and 254 deletions

View File

@@ -5,7 +5,7 @@
This report compares three signer implementations for secp256k1 operations:
1. **P256K1Signer** - This repository's new port from Bitcoin Core secp256k1 (pure Go)
2. **BtcecSigner** - Pure Go wrapper around btcec/v2
2. ~~BtcecSigner - Pure Go wrapper around btcec/v2~~ (removed)
3. **NextP256K Signer** - CGO version using next.orly.dev/pkg/crypto/p256k (CGO bindings to libsecp256k1)
**Generated:** 2025-11-02 (Updated after comprehensive CPU optimizations)
@@ -30,12 +30,12 @@ This report compares three signer implementations for secp256k1 operations:
## Summary Results
| Operation | P256K1Signer | BtcecSigner | NextP256K | Winner |
| Operation | P256K1Signer | ~~BtcecSigner~~ | NextP256K | Winner |
|-----------|-------------|-------------|-----------|--------|
| **Pubkey Derivation** | 55,091 ns/op | 64,177 ns/op | 271,394 ns/op | P256K1 (14% faster than Btcec) |
| **Sign** | 29,237 ns/op | 225,514 ns/op | 53,015 ns/op | P256K1 (1.8x faster than NextP256K) |
| **Verify** | 138,127 ns/op | 177,622 ns/op | 44,776 ns/op | NextP256K (3.1x faster) |
| **ECDH** | 103,345 ns/op | 129,392 ns/op | 125,835 ns/op | P256K1 (1.2x faster than NextP256K) |
| **Pubkey Derivation** | 55,091 ns/op | ~~64,177 ns/op~~ | 271,394 ns/op | P256K1 |
| **Sign** | 29,237 ns/op | ~~225,514 ns/op~~ | 53,015 ns/op | P256K1 (1.8x faster than NextP256K) |
| **Verify** | 138,127 ns/op | ~~177,622 ns/op~~ | 44,776 ns/op | NextP256K (3.1x faster) |
| **ECDH** | 103,345 ns/op | ~~129,392 ns/op~~ | 125,835 ns/op | P256K1 (1.2x faster than NextP256K) |
---
@@ -48,15 +48,15 @@ Deriving public key from private key (32 bytes → 32 bytes x-only pubkey).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 55,091 ns/op | 256 B/op | 4 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 64,177 ns/op | 368 B/op | 7 allocs/op | 0.9x slower |
| ~~**BtcecSigner**~~ | ~~64,177 ns/op~~ | ~~368 B/op~~ | ~~7 allocs/op~~ | Removed |
| **NextP256K** | 271,394 ns/op | 983,394 B/op | 9 allocs/op | 0.2x slower |
**Analysis:**
- **P256K1 is fastest** (14% faster than Btcec) after implementing 8-bit byte-based precomputed tables
- **P256K1 is fastest** after implementing 8-bit byte-based precomputed tables
- **6% improvement** from CPU optimizations (58,383 → 55,091 ns/op)
- Massive improvement: 4x faster than original implementation (232,922 → 55,091 ns/op)
- NextP256K is slowest, likely due to CGO overhead for small operations
- P256K1 has lowest memory allocation overhead (256 B vs 368 B)
- P256K1 has low memory allocation overhead
### Signing (Schnorr)
@@ -65,14 +65,14 @@ Creating BIP-340 Schnorr signatures (32-byte message → 64-byte signature).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 29,237 ns/op | 576 B/op | 9 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 225,514 ns/op | 2,193 B/op | 38 allocs/op | 0.1x slower |
| ~~**BtcecSigner**~~ | ~~225,514 ns/op~~ | ~~2,193 B/op~~ | ~~38 allocs/op~~ | Removed |
| **NextP256K** | 53,015 ns/op | 128 B/op | 3 allocs/op | 0.6x slower |
**Analysis:**
- **P256K1 is fastest** (1.8x faster than NextP256K) after comprehensive CPU optimizations
- **54% improvement** from optimizations (63,421 → 29,237 ns/op)
- **47% reduction in allocations** (17 → 9 allocs/op)
- P256K1 is 7.7x faster than Btcec
- P256K1 is significantly faster than alternatives
- Optimizations: precomputed TaggedHash prefixes, eliminated intermediate copies, optimized hash operations
- NextP256K has lowest memory usage (128 B vs 576 B) but P256K1 is significantly faster
@@ -83,12 +83,12 @@ Verifying BIP-340 Schnorr signatures (32-byte message + 64-byte signature).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 138,127 ns/op | 64 B/op | 2 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 177,622 ns/op | 1,120 B/op | 18 allocs/op | 0.8x slower |
| ~~**BtcecSigner**~~ | ~~177,622 ns/op~~ | ~~1,120 B/op~~ | ~~18 allocs/op~~ | Removed |
| **NextP256K** | 44,776 ns/op | 96 B/op | 2 allocs/op | **3.1x faster** |
**Analysis:**
- NextP256K is dramatically fastest (3.1x faster), showcasing CGO advantage for verification
- **P256K1 is fastest pure Go implementation** (22% faster than Btcec) after comprehensive optimizations
- **P256K1 is the fastest pure Go implementation** after comprehensive optimizations
- **8% improvement** from CPU optimizations (149,511 → 138,127 ns/op)
- **78% reduction in allocations** (9 → 2 allocs/op), **89% reduction in memory** (576 → 64 B/op)
- **Total improvement:** 26% faster than original (186,054 → 138,127 ns/op)
@@ -102,7 +102,7 @@ Generating shared secret using Elliptic Curve Diffie-Hellman.
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 103,345 ns/op | 241 B/op | 6 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 129,392 ns/op | 832 B/op | 13 allocs/op | 0.8x slower |
| ~~**BtcecSigner**~~ | ~~129,392 ns/op~~ | ~~832 B/op~~ | ~~13 allocs/op~~ | Removed |
| **NextP256K** | 125,835 ns/op | 160 B/op | 3 allocs/op | 0.8x slower |
**Analysis:**
@@ -110,7 +110,7 @@ Generating shared secret using Elliptic Curve Diffie-Hellman.
- **5% improvement** from CPU optimizations (109,068 → 103,345 ns/op)
- **Total improvement:** 37% faster than original (163,356 → 103,345 ns/op)
- Optimizations: 6-bit windowed multiplication (increased from 5-bit), optimized field operations
- P256K1 has lowest memory usage (241 B vs 832 B for Btcec)
- P256K1 has good memory usage
---
@@ -120,19 +120,15 @@ Generating shared secret using Elliptic Curve Diffie-Hellman.
After comprehensive CPU optimizations:
- **P256K1Signer** wins in 3 out of 4 operations:
- **Pubkey Derivation:** Fastest (14% faster than Btcec) - **6% improvement**
- **Pubkey Derivation:** Fastest - **6% improvement**
- **Signing:** Fastest (1.8x faster than NextP256K) - **54% improvement!**
- **ECDH:** Fastest (1.2x faster than NextP256K) - **5% improvement**
- **NextP256K** wins in 1 operation:
- **Verification:** Fastest (3.1x faster than P256K1, CGO advantage) - but P256K1 is 8% faster than before
- **Verification:** Fastest (3.1x faster than P256K1, CGO advantage)
### Best Pure Go: P256K1Signer
For pure Go implementations:
- **P256K1** wins for key derivation (14% faster than Btcec) - **6% improvement**
- **P256K1** wins for signing (7.7x faster than Btcec) - **54% improvement!**
- **P256K1** wins for verification (22% faster than Btcec) - **fastest pure Go!** (**8% improvement**)
- **P256K1** wins for ECDH (1.25x faster than Btcec) - **fastest pure Go!** (**5% improvement**)
**P256K1Signer** is the fastest pure Go implementation available.
### Memory Efficiency
@@ -140,7 +136,6 @@ For pure Go implementations:
|----------------|-------------------------|-------|
| **P256K1Signer** | ~270 B avg | Low memory footprint, significantly reduced after optimizations |
| **NextP256K** | ~300 KB avg | Very efficient, minimal allocations (except pubkey derivation overhead) |
| **BtcecSigner** | ~1.1 KB avg | Higher allocations, but acceptable |
**Note:** NextP256K shows high memory in pubkey derivation (983 KB) due to one-time CGO initialization overhead, but this is amortized across operations.
@@ -161,18 +156,13 @@ For pure Go implementations:
### Use P256K1Signer when:
- Pure Go is required (no CGO)
- **Signing performance is critical** (1.8x faster than NextP256K, 7.7x faster than Btcec)
- **Signing performance is critical** (1.8x faster than NextP256K)
- **Pubkey derivation, verification, or ECDH performance is critical** (fastest pure Go for all operations!)
- Lower memory allocations are preferred (64 B for verify, 576 B for sign)
- You want to avoid external C dependencies
- You need the best overall pure Go performance
- **Now competitive with CGO for signing** (faster than NextP256K)
### Use BtcecSigner when:
- Pure Go is required
- You're already using btcec in your project
- Note: P256K1Signer is faster across all operations
---
## Conclusion
@@ -208,10 +198,9 @@ The benchmarks demonstrate that:
The choice between implementations depends on your specific requirements:
- **Maximum verification performance:** Use NextP256K (CGO) - 3.1x faster for verification
- **Maximum signing performance:** Use P256K1Signer (Pure Go) - 1.8x faster than NextP256K, 7.7x faster than Btcec!
- **Maximum signing performance:** Use P256K1Signer (Pure Go) - 1.8x faster than NextP256K
- **Best pure Go performance:** Use P256K1Signer - fastest pure Go for all operations, now competitive with CGO for signing
- **Best overall performance:** Use P256K1Signer - wins 3 out of 4 operations, fastest overall for signing
- **Pure Go alternative:** Use BtcecSigner (but P256K1Signer is significantly faster across all operations)
---

View File

@@ -0,0 +1,234 @@
# Benchmark Comparison Report
## Signer Implementation Comparison
This report compares three signer implementations for secp256k1 operations:
1. **P256K1Signer** - This repository's new port from Bitcoin Core secp256k1 (pure Go)
2. ~~BtcecSigner - Pure Go wrapper around btcec/v2~~ (removed)
3. **NextP256K Signer** - CGO version using next.orly.dev/pkg/crypto/p256k (CGO bindings to libsecp256k1)
**Generated:** 2025-11-02 (Updated after comprehensive CPU optimizations)
**Platform:** linux/amd64
**CPU:** AMD Ryzen 5 PRO 4650G with Radeon Graphics
**Go Version:** go1.25.3
**Key Optimizations:**
- Implemented 8-bit byte-based precomputed tables matching btcec's approach, resulting in 4x improvement in pubkey derivation and 4.3x improvement in signing.
- Optimized windowed multiplication for verification (6-bit windows, increased from 5-bit): 8% improvement (149,511 → 138,127 ns/op).
- Optimized ECDH with windowed multiplication (6-bit windows): 5% improvement (109,068 → 103,345 ns/op).
- **Major CPU optimizations (Nov 2025):**
- Precomputed TaggedHash prefixes for common BIP-340 tags: 28% faster (310 → 230 ns/op)
- Eliminated unnecessary copies in field element operations (mul/sqr): faster when magnitude ≤ 8
- Optimized group element operations (toBytes/toStorage): in-place normalization to avoid copies
- Optimized EcmultGen: pre-allocated group elements to reduce allocations
- **Sign optimizations:** 54% faster (63,421 → 29,237 ns/op), 47% fewer allocations (17 → 9 allocs/op)
- **Verify optimizations:** 8% faster (149,511 → 138,127 ns/op), 78% fewer allocations (9 → 2 allocs/op)
- **Pubkey derivation:** 6% faster (58,383 → 55,091 ns/op), eliminated intermediate copies
---
## Summary Results
| Operation | P256K1Signer | BtcecSigner | NextP256K | Winner |
|-----------|-------------|-------------|-----------|--------|
| **Pubkey Derivation** | 55,091 ns/op | 64,177 ns/op | 271,394 ns/op | P256K1 (14% faster than Btcec) |
| **Sign** | 29,237 ns/op | 225,514 ns/op | 53,015 ns/op | P256K1 (1.8x faster than NextP256K) |
| **Verify** | 138,127 ns/op | 177,622 ns/op | 44,776 ns/op | NextP256K (3.1x faster) |
| **ECDH** | 103,345 ns/op | 129,392 ns/op | 125,835 ns/op | P256K1 (1.2x faster than NextP256K) |
---
## Detailed Results
### Public Key Derivation
Deriving public key from private key (32 bytes → 32 bytes x-only pubkey).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 55,091 ns/op | 256 B/op | 4 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 64,177 ns/op | 368 B/op | 7 allocs/op | 0.9x slower |
| **NextP256K** | 271,394 ns/op | 983,394 B/op | 9 allocs/op | 0.2x slower |
**Analysis:**
- **P256K1 is fastest** (14% faster than Btcec) after implementing 8-bit byte-based precomputed tables
- **6% improvement** from CPU optimizations (58,383 → 55,091 ns/op)
- Massive improvement: 4x faster than original implementation (232,922 → 55,091 ns/op)
- NextP256K is slowest, likely due to CGO overhead for small operations
- P256K1 has lowest memory allocation overhead (256 B vs 368 B)
### Signing (Schnorr)
Creating BIP-340 Schnorr signatures (32-byte message → 64-byte signature).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 29,237 ns/op | 576 B/op | 9 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 225,514 ns/op | 2,193 B/op | 38 allocs/op | 0.1x slower |
| **NextP256K** | 53,015 ns/op | 128 B/op | 3 allocs/op | 0.6x slower |
**Analysis:**
- **P256K1 is fastest** (1.8x faster than NextP256K) after comprehensive CPU optimizations
- **54% improvement** from optimizations (63,421 → 29,237 ns/op)
- **47% reduction in allocations** (17 → 9 allocs/op)
- P256K1 is 7.7x faster than Btcec
- Optimizations: precomputed TaggedHash prefixes, eliminated intermediate copies, optimized hash operations
- NextP256K has lowest memory usage (128 B vs 576 B) but P256K1 is significantly faster
### Verification (Schnorr)
Verifying BIP-340 Schnorr signatures (32-byte message + 64-byte signature).
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 138,127 ns/op | 64 B/op | 2 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 177,622 ns/op | 1,120 B/op | 18 allocs/op | 0.8x slower |
| **NextP256K** | 44,776 ns/op | 96 B/op | 2 allocs/op | **3.1x faster** |
**Analysis:**
- NextP256K is dramatically fastest (3.1x faster), showcasing CGO advantage for verification
- **P256K1 is fastest pure Go implementation** (22% faster than Btcec) after comprehensive optimizations
- **8% improvement** from CPU optimizations (149,511 → 138,127 ns/op)
- **78% reduction in allocations** (9 → 2 allocs/op), **89% reduction in memory** (576 → 64 B/op)
- **Total improvement:** 26% faster than original (186,054 → 138,127 ns/op)
- Optimizations: 6-bit windowed multiplication (increased from 5-bit), precomputed TaggedHash, eliminated intermediate copies
- P256K1 now has minimal memory footprint (64 B vs 96 B for NextP256K)
### ECDH (Shared Secret Generation)
Generating shared secret using Elliptic Curve Diffie-Hellman.
| Implementation | Time per op | Memory | Allocations | Speedup vs P256K1 |
|----------------|-------------|--------|-------------|-------------------|
| **P256K1Signer** | 103,345 ns/op | 241 B/op | 6 allocs/op | 1.0x (baseline) |
| **BtcecSigner** | 129,392 ns/op | 832 B/op | 13 allocs/op | 0.8x slower |
| **NextP256K** | 125,835 ns/op | 160 B/op | 3 allocs/op | 0.8x slower |
**Analysis:**
- **P256K1 is fastest** (1.2x faster than NextP256K) after optimizing with windowed multiplication
- **5% improvement** from CPU optimizations (109,068 → 103,345 ns/op)
- **Total improvement:** 37% faster than original (163,356 → 103,345 ns/op)
- Optimizations: 6-bit windowed multiplication (increased from 5-bit), optimized field operations
- P256K1 has lowest memory usage (241 B vs 832 B for Btcec)
---
## Performance Analysis
### Overall Winner: Mixed (P256K1 wins 3/4 operations, NextP256K wins 1/4 operations)
After comprehensive CPU optimizations:
- **P256K1Signer** wins in 3 out of 4 operations:
- **Pubkey Derivation:** Fastest (14% faster than Btcec) - **6% improvement**
- **Signing:** Fastest (1.8x faster than NextP256K) - **54% improvement!**
- **ECDH:** Fastest (1.2x faster than NextP256K) - **5% improvement**
- **NextP256K** wins in 1 operation:
- **Verification:** Fastest (3.1x faster than P256K1, CGO advantage) - but P256K1 is 8% faster than before
### Best Pure Go: P256K1Signer
For pure Go implementations:
- **P256K1** wins for key derivation (14% faster than Btcec) - **6% improvement**
- **P256K1** wins for signing (7.7x faster than Btcec) - **54% improvement!**
- **P256K1** wins for verification (22% faster than Btcec) - **fastest pure Go!** (**8% improvement**)
- **P256K1** wins for ECDH (1.25x faster than Btcec) - **fastest pure Go!** (**5% improvement**)
### Memory Efficiency
| Implementation | Avg Memory per Operation | Notes |
|----------------|-------------------------|-------|
| **P256K1Signer** | ~270 B avg | Low memory footprint, significantly reduced after optimizations |
| **NextP256K** | ~300 KB avg | Very efficient, minimal allocations (except pubkey derivation overhead) |
| **BtcecSigner** | ~1.1 KB avg | Higher allocations, but acceptable |
**Note:** NextP256K shows high memory in pubkey derivation (983 KB) due to one-time CGO initialization overhead, but this is amortized across operations.
**Memory Improvements:**
- **Sign:** 1,152 → 576 B/op (50% reduction)
- **Verify:** 576 → 64 B/op (89% reduction!)
- **Pubkey Derivation:** Already optimized (256 B/op)
---
## Recommendations
### Use NextP256K (CGO) when:
- Maximum verification performance is critical (3.1x faster than P256K1)
- CGO is acceptable in your build environment
- Low memory footprint is important
- Verification speed is critical (3.1x faster)
### Use P256K1Signer when:
- Pure Go is required (no CGO)
- **Signing performance is critical** (1.8x faster than NextP256K, 7.7x faster than Btcec)
- **Pubkey derivation, verification, or ECDH performance is critical** (fastest pure Go for all operations!)
- Lower memory allocations are preferred (64 B for verify, 576 B for sign)
- You want to avoid external C dependencies
- You need the best overall pure Go performance
- **Now competitive with CGO for signing** (faster than NextP256K)
### Use BtcecSigner when:
- Pure Go is required
- You're already using btcec in your project
- Note: P256K1Signer is faster across all operations
---
## Conclusion
The benchmarks demonstrate that:
1. **After comprehensive CPU optimizations**, P256K1Signer achieves:
- **Fastest pubkey derivation** among all implementations (55,091 ns/op) - **6% improvement**
- **Fastest signing** among all implementations (29,237 ns/op) - **54% improvement!** (63,421 → 29,237 ns/op)
- **Fastest ECDH** among all implementations (103,345 ns/op) - **5% improvement** (109,068 → 103,345 ns/op)
- **Fastest pure Go verification** (138,127 ns/op) - **8% improvement** (149,511 → 138,127 ns/op)
- **Now faster than NextP256K for signing** (1.8x faster!)
2. **CPU optimization results (Nov 2025):**
- Precomputed TaggedHash prefixes: 28% faster (310 → 230 ns/op)
- Increased window size from 5-bit to 6-bit: fewer iterations (~43 vs ~52 windows)
- Eliminated unnecessary copies in field/group operations
- Optimized memory allocations: 78% reduction in verify (9 → 2 allocs/op), 47% reduction in sign (17 → 9 allocs/op)
- **Sign: 54% faster** (63,421 → 29,237 ns/op)
- **Verify: 8% faster** (149,511 → 138,127 ns/op), **89% less memory** (576 → 64 B/op)
- **Pubkey Derivation: 6% faster** (58,383 → 55,091 ns/op)
- **ECDH: 5% faster** (109,068 → 103,345 ns/op)
3. **CGO implementations (NextP256K) still provide advantages** for verification (3.1x faster) but P256K1 is now faster for signing
4. **Pure Go implementations are highly competitive**, with P256K1Signer leading in 3 out of 4 operations (pubkey derivation, signing, ECDH)
5. **Memory efficiency** significantly improved, with P256K1Signer maintaining very low memory usage:
- Verify: 64 B/op (89% reduction!)
- Sign: 576 B/op (50% reduction)
- Pubkey Derivation: 256 B/op
- ECDH: 241 B/op
The choice between implementations depends on your specific requirements:
- **Maximum verification performance:** Use NextP256K (CGO) - 3.1x faster for verification
- **Maximum signing performance:** Use P256K1Signer (Pure Go) - 1.8x faster than NextP256K, 7.7x faster than Btcec!
- **Best pure Go performance:** Use P256K1Signer - fastest pure Go for all operations, now competitive with CGO for signing
- **Best overall performance:** Use P256K1Signer - wins 3 out of 4 operations, fastest overall for signing
- **Pure Go alternative:** Use BtcecSigner (but P256K1Signer is significantly faster across all operations)
---
## Running the Benchmarks
To reproduce these benchmarks:
```bash
# Run all benchmarks
CGO_ENABLED=1 go test -tags=cgo ./bench -bench=. -benchmem
# Run specific operation
CGO_ENABLED=1 go test -tags=cgo ./bench -bench=BenchmarkSign
# Run specific implementation
CGO_ENABLED=1 go test -tags=cgo ./bench -bench=Benchmark.*_P256K1
```
**Note:** All benchmarks require CGO to be enabled (`CGO_ENABLED=1`) and the `cgo` build tag.

View File

@@ -14,8 +14,8 @@ import (
// (pure Go port from Bitcoin Core secp256k1)
var (
benchSeckey []byte
benchMsghash []byte
benchSeckey []byte
benchMsghash []byte
compBenchSignerP256K1 *signer.P256K1Signer
compBenchSignerP256K12 *signer.P256K1Signer
compBenchSigP256K1 []byte
@@ -100,7 +100,6 @@ func BenchmarkPubkeyDerivation(b *testing.B) {
}
}
// BenchmarkSign benchmarks Schnorr signing
func BenchmarkSign(b *testing.B) {
if benchSeckey == nil {
@@ -119,7 +118,6 @@ func BenchmarkSign(b *testing.B) {
}
}
// BenchmarkVerify benchmarks Schnorr verification
func BenchmarkVerify(b *testing.B) {
if benchSeckey == nil {
@@ -146,7 +144,6 @@ func BenchmarkVerify(b *testing.B) {
}
}
// BenchmarkECDH benchmarks ECDH shared secret generation
func BenchmarkECDH(b *testing.B) {
if benchSeckey == nil {
@@ -164,5 +161,3 @@ func BenchmarkECDH(b *testing.B) {
}
}
}

19
btcec-signer/go.mod Normal file
View File

@@ -0,0 +1,19 @@
module p256k1.mleku.dev/signer
go 1.25.0
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.6
next.orly.dev v1.0.3
p256k1.mleku.dev v1.0.0
)
require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
golang.org/x/sys v0.37.0 // indirect
)

8
go.mod
View File

@@ -3,19 +3,11 @@ module p256k1.mleku.dev
go 1.25.0
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.6
github.com/minio/sha256-simd v1.0.1
next.orly.dev v1.0.3
)
require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/templexxx/cpu v0.1.1 // indirect
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b // indirect
golang.org/x/sys v0.37.0 // indirect
lol.mleku.dev v1.0.5 // indirect
)

17
go.sum
View File

@@ -1,25 +1,8 @@
github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E=
github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3Wb1+pWBaWv/BlHK0ZYIu/KaL6eHg=
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
lol.mleku.dev v1.0.5 h1:irwfwz+Scv74G/2OXmv05YFKOzUNOVZ735EAkYgjgM8=
lol.mleku.dev v1.0.5/go.mod h1:JlsqP0CZDLKRyd85XGcy79+ydSRqmFkrPzYFMYxQ+zs=
next.orly.dev v1.0.3 h1:PF1mhQa9s6CksqJ9hCkczBlZXp5DAlZK9Ej3katNijg=
next.orly.dev v1.0.3/go.mod h1:/C14fkucnvjsJzj17tzmF5GeW4n0nQw+YkepakUFREc=

17
signer/btcec/go.mod Normal file
View File

@@ -0,0 +1,17 @@
module p256k1.mleku.dev/signer/btcec
go 1.25.0
require (
github.com/btcsuite/btcd/btcec/v2 v2.3.6
next.orly.dev v1.0.3
)
require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
golang.org/x/sys v0.37.0 // indirect
)

View File

@@ -1,191 +0,0 @@
//go:build cgo
// +build cgo
package signer
import (
"errors"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
)
// BtcecSigner implements the I interface using btcec (pure Go implementation)
type BtcecSigner struct {
privKey *btcec.PrivateKey
pubKey *btcec.PublicKey
xonlyPub []byte // Cached x-only public key
hasSecret bool
}
// NewBtcecSigner creates a new BtcecSigner instance
func NewBtcecSigner() *BtcecSigner {
return &BtcecSigner{
hasSecret: false,
}
}
// Generate creates a fresh new key pair from system entropy, and ensures it is even (so ECDH works)
func (s *BtcecSigner) Generate() error {
privKey, err := btcec.NewPrivateKey()
if err != nil {
return err
}
pubKey := privKey.PubKey()
xonlyPub := schnorr.SerializePubKey(pubKey)
// Ensure even Y coordinate for ECDH compatibility
// If the Y coordinate is odd, negate the private key
pubBytes := pubKey.SerializeCompressed()
if pubBytes[0] == 0x03 { // Odd Y coordinate
// Negate the private key
scalar := privKey.Key
scalar.Negate()
privKey = &btcec.PrivateKey{Key: scalar}
pubKey = privKey.PubKey()
xonlyPub = schnorr.SerializePubKey(pubKey)
}
s.privKey = privKey
s.pubKey = pubKey
s.xonlyPub = xonlyPub
s.hasSecret = true
return nil
}
// InitSec initialises the secret (signing) key from the raw bytes, and also derives the public key
func (s *BtcecSigner) InitSec(sec []byte) error {
if len(sec) != 32 {
return errors.New("secret key must be 32 bytes")
}
privKey, pubKey := btcec.PrivKeyFromBytes(sec)
xonlyPub := schnorr.SerializePubKey(pubKey)
// Ensure even Y coordinate for ECDH compatibility
pubBytes := pubKey.SerializeCompressed()
if pubBytes[0] == 0x03 { // Odd Y coordinate
// Negate the private key
scalar := privKey.Key
scalar.Negate()
privKey = &btcec.PrivateKey{Key: scalar}
pubKey = privKey.PubKey()
xonlyPub = schnorr.SerializePubKey(pubKey)
}
s.privKey = privKey
s.pubKey = pubKey
s.xonlyPub = xonlyPub
s.hasSecret = true
return nil
}
// InitPub initializes the public (verification) key from raw bytes, this is expected to be an x-only 32 byte pubkey
func (s *BtcecSigner) InitPub(pub []byte) error {
if len(pub) != 32 {
return errors.New("public key must be 32 bytes")
}
pubKey, err := schnorr.ParsePubKey(pub)
if err != nil {
return err
}
s.pubKey = pubKey
s.xonlyPub = pub
s.privKey = nil
s.hasSecret = false
return nil
}
// Sec returns the secret key bytes
func (s *BtcecSigner) Sec() []byte {
if !s.hasSecret || s.privKey == nil {
return nil
}
return s.privKey.Serialize()
}
// Pub returns the public key bytes (x-only schnorr pubkey)
func (s *BtcecSigner) Pub() []byte {
if s.xonlyPub == nil {
return nil
}
return s.xonlyPub
}
// Sign creates a signature using the stored secret key
func (s *BtcecSigner) Sign(msg []byte) (sig []byte, err error) {
if !s.hasSecret || s.privKey == nil {
return nil, errors.New("no secret key available for signing")
}
if len(msg) != 32 {
return nil, errors.New("message must be 32 bytes")
}
signature, err := schnorr.Sign(s.privKey, msg)
if err != nil {
return nil, err
}
return signature.Serialize(), nil
}
// Verify checks a message hash and signature match the stored public key
func (s *BtcecSigner) Verify(msg, sig []byte) (valid bool, err error) {
if s.pubKey == nil {
return false, errors.New("no public key available for verification")
}
if len(msg) != 32 {
return false, errors.New("message must be 32 bytes")
}
if len(sig) != 64 {
return false, errors.New("signature must be 64 bytes")
}
signature, err := schnorr.ParseSignature(sig)
if err != nil {
return false, err
}
valid = signature.Verify(msg, s.pubKey)
return valid, nil
}
// Zero wipes the secret key to prevent memory leaks
func (s *BtcecSigner) Zero() {
if s.privKey != nil {
s.privKey.Zero()
s.privKey = nil
}
s.hasSecret = false
s.pubKey = nil
s.xonlyPub = nil
}
// ECDH returns a shared secret derived using Elliptic Curve Diffie-Hellman on the I secret and provided pubkey
func (s *BtcecSigner) ECDH(pub []byte) (secret []byte, err error) {
if !s.hasSecret || s.privKey == nil {
return nil, errors.New("no secret key available for ECDH")
}
if len(pub) != 32 {
return nil, errors.New("public key must be 32 bytes")
}
// Parse x-only pubkey
pubKey, err := schnorr.ParsePubKey(pub)
if err != nil {
return nil, err
}
secret = btcec.GenerateSharedSecret(s.privKey, pubKey)
return secret, nil
}