diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Point.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Point.kt index b1280e62f..d5a3ebfa5 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Point.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Point.kt @@ -36,31 +36,32 @@ package com.vitorpamplona.quartz.utils.secp256k1 // // The "point at infinity" (identity element) is represented by Z = 0. // -// PRECOMPUTED GENERATOR TABLE -// =========================== -// The generator point G is used for public key creation and signature operations. -// We precompute a table of [1G, 2G, 3G, ..., 16G] in affine form (stored as AffinePoint) -// at first use. This table is lazily initialized and cached for the lifetime of the process. -// Affine storage enables "mixed addition" (Jacobian + Affine), which is cheaper than -// adding two Jacobian points because the second point's Z=1 eliminates several multiplications. -// -// POINT DOUBLING FORMULA -// ====================== +// POINT DOUBLING +// ============== // We use the 3M+4S formula from libsecp256k1 that computes L = (3/2)·X² using a cheap // field halving operation instead of a full multiplication. On the secp256k1 curve (a=0), // this is the most efficient known doubling formula. // -// SCALAR MULTIPLICATION -// ===================== -// For general scalar multiplication (mul, mulG), we use a 4-bit windowed method. -// For signature verification (mulDoubleG), we use Shamir's trick combined with: +// SCALAR MULTIPLICATION STRATEGIES +// ================================ +// Three methods are used depending on the context: // -// - GLV Endomorphism (Glv.kt): Splits each 256-bit scalar into two ~128-bit halves, -// halving the number of doublings from 256 to ~130. -// - wNAF-5 Encoding (Glv.kt): Encodes each half-scalar so non-zero digits are sparse -// (separated by ≥4 zeros), reducing point additions. -// - Mixed Addition: G-side additions use the precomputed affine table (8M+3S per add), -// while P-side uses full Jacobian (11M+5S, avoiding expensive table inversions). +// 1. mulG (Generator multiplication): Comb method (Hamburg 2012). +// Arranges scalar bits into a 4×66 matrix, processes 4 rows with 11 table lookups +// each. Only 3 doublings total. Uses a precomputed 704-entry affine table (~45KB). +// Cost: ~43 mixed additions + 3 doublings (vs ~130 dbl for GLV+wNAF). +// Used by: pubkeyCreate, signSchnorr. +// +// 2. mul (Arbitrary point multiplication): GLV endomorphism + wNAF-5 (Glv.kt). +// Splits the 256-bit scalar into two ~128-bit halves via the secp256k1 endomorphism, +// then processes both with wNAF encoding in a single pass of ~130 shared doublings. +// Used by: pubKeyTweakMul (ECDH). +// +// 3. mulDoubleG (Verification: s·G + e·P): Strauss/Shamir trick with GLV + wNAF. +// Splits both scalars via GLV into 4 half-scalar streams. G-side uses a precomputed +// 64-entry affine wNAF-8 table; P-side builds a Jacobian wNAF-5 table per call. +// All 4 streams share ~130 doublings in a single pass. +// Used by: verifySchnorr. // ===================================================================================== /** diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Secp256k1.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Secp256k1.kt index b1419a9ca..7fec2625a 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Secp256k1.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/Secp256k1.kt @@ -36,10 +36,13 @@ import com.vitorpamplona.quartz.utils.sha256.sha256 * - [privKeyTweakAdd]: BIP-32 key derivation (NIP-06) * - [pubKeyTweakMul]: ECDH shared secrets (NIP-04, NIP-44) * - * Performance: ~2,100 verify/s on JVM (~13× slower than the native C library). + * Performance on JVM (vs native C/JNI secp256k1): + * verify ~3,700 ops/s (~8×), sign ~14K ops/s (~2.3×), pubkeyCreate ~18K ops/s (~3.6×), + * compress ~7M ops/s (2× FASTER), secKeyVerify ~6M ops/s (FASTER). * The gap is primarily due to JVM's lack of 128-bit integer types (forcing 8×32-bit - * limbs instead of C's 5×52-bit) and the absence of the GLV endomorphism optimization - * that halves the number of EC point doublings. See Point.kt for optimization notes. + * limbs with 64 inner products per field multiply, vs C's 5×52-bit with 25). + * All algorithmic optimizations from libsecp256k1 are implemented: GLV endomorphism, + * wNAF encoding, Shamir's trick, comb method, and optimized addition chains. */ object Secp256k1 { // ==================== Cached BIP-340 tag hash prefixes ==================== diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/U256.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/U256.kt index 15f29f063..88f6baafa 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/U256.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/U256.kt @@ -21,15 +21,18 @@ package com.vitorpamplona.quartz.utils.secp256k1 // ===================================================================================== -// 256-BIT ARITHMETIC AND MODULAR FIELD OPERATIONS FOR secp256k1 +// 256-BIT UNSIGNED INTEGER ARITHMETIC FOR secp256k1 // ===================================================================================== // -// This file implements the foundational math needed for elliptic curve cryptography on -// the secp256k1 curve (used by Bitcoin and Nostr). It provides: +// This file implements the raw 256-bit unsigned integer arithmetic that underlies all +// secp256k1 operations. It is the foundation of the secp256k1 package, which consists of: // -// - U256: Raw 256-bit unsigned integer arithmetic (add, subtract, multiply, compare) -// - FieldP: Arithmetic modulo p (the field prime), used for point coordinates -// - ScalarN: Arithmetic modulo n (the group order), used for private keys and signatures +// - U256.kt (this file): Raw 256-bit arithmetic (add, subtract, multiply, compare) +// - FieldP.kt: Modular arithmetic mod p (field prime), for point coordinates +// - ScalarN.kt: Modular arithmetic mod n (group order), for keys and signatures +// - Glv.kt: GLV endomorphism and wNAF scalar encoding +// - Point.kt: EC point operations, scalar multiplication (comb, Strauss, GLV) +// - Secp256k1.kt: Public API (sign, verify, key operations) // // REPRESENTATION // ==============