From 5b7f8478aaa10f6c68fdffc859942970d8929f77 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Apr 2026 20:26:11 +0000 Subject: [PATCH] perf: secKeyVerify operates directly on bytes, avoids IntArray allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compares the 32-byte key against n byte-by-byte (big-endian) instead of decoding into limbs first. Eliminates one IntArray(8) allocation and the fromBytes conversion. The non-zero check ORs all bytes in a single pass. privKeyTweakAdd was also tested with a byte-based approach but reverted because 32 byte-iterations are slower than 8 limb-iterations — the JVM optimizes Long operations better than byte loops. https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg --- .../quartz/utils/secp256k1/Secp256k1.kt | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) 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 2ea764e2c..d217c8aeb 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 @@ -101,12 +101,64 @@ object Secp256k1 { } } - /** Verify that a byte array is a valid secret key (32 bytes, 0 < value < n). */ + /** + * Verify that a byte array is a valid secret key (32 bytes, 0 < value < n). + * Operates directly on bytes without converting to limbs — avoids IntArray allocation. + */ fun secKeyVerify(seckey: ByteArray): Boolean { if (seckey.size != 32) return false - return ScalarN.isValid(U256.fromBytes(seckey)) + // Check not zero (any non-zero byte means non-zero) + var nonZero = 0 + for (b in seckey) nonZero = nonZero or b.toInt() + if (nonZero == 0) return false + // Check < n by comparing big-endian bytes against n + // n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + for (i in 0 until 32) { + val si = seckey[i].toInt() and 0xFF + val ni = N_BYTES[i].toInt() and 0xFF + if (si < ni) return true // definitely less + if (si > ni) return false // definitely greater + } + return false // equal to n → invalid } + // n as big-endian bytes for direct comparison + private val N_BYTES = + byteArrayOf( + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFF.toByte(), + 0xFE.toByte(), + 0xBA.toByte(), + 0xAE.toByte(), + 0xDC.toByte(), + 0xE6.toByte(), + 0xAF.toByte(), + 0x48.toByte(), + 0xA0.toByte(), + 0x3B.toByte(), + 0xBF.toByte(), + 0xD2.toByte(), + 0x5E.toByte(), + 0x8C.toByte(), + 0xD0.toByte(), + 0x36.toByte(), + 0x41.toByte(), + 0x41.toByte(), + ) + // ==================== BIP-340 Schnorr Signatures ==================== /**