feat: fix constants and types in ScalarN, KeyCodec, partial Glv fix (WIP — still broken)
Progress on the LongArray(4) migration: - ScalarN: constants converted to 4×64-bit, loop bounds fixed - KeyCodec: B constant fixed - Glv: constants partially converted but regex left residual old values - Point: types fixed, GX/GY constants converted - Secp256k1: parameter types updated Still needs: manual cleanup of Glv constants, ScalarN reduceWide internals, test hex() helpers, test constant arrays, wNAF bit manipulation for 64-bit limbs. https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
This commit is contained in:
@@ -51,7 +51,8 @@ internal object Glv {
|
|||||||
/** β: cube root of unity mod p. The endomorphism is φ(x,y) = (β·x, y). */
|
/** β: cube root of unity mod p. The endomorphism is φ(x,y) = (β·x, y). */
|
||||||
val BETA =
|
val BETA =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x719501EE.toInt(),
|
-4523465429756870162L, -7138124642204153451L, 7954561588662645993L, 8856726876819556112L,
|
||||||
|
),
|
||||||
0xC1396C28.toInt(),
|
0xC1396C28.toInt(),
|
||||||
0x12F58995.toInt(),
|
0x12F58995.toInt(),
|
||||||
0x9CF04975.toInt(),
|
0x9CF04975.toInt(),
|
||||||
@@ -65,8 +66,8 @@ internal object Glv {
|
|||||||
|
|
||||||
/** Result of splitting a 256-bit scalar into two ~128-bit halves via GLV. */
|
/** Result of splitting a 256-bit scalar into two ~128-bit halves via GLV. */
|
||||||
data class Split(
|
data class Split(
|
||||||
val k1: IntArray,
|
val k1: LongArray,
|
||||||
val k2: IntArray,
|
val k2: LongArray,
|
||||||
val negK1: Boolean,
|
val negK1: Boolean,
|
||||||
val negK2: Boolean,
|
val negK2: Boolean,
|
||||||
)
|
)
|
||||||
@@ -76,7 +77,7 @@ internal object Glv {
|
|||||||
* with |k₁|, |k₂| ≈ 128 bits. Both are made positive for wNAF encoding;
|
* with |k₁|, |k₂| ≈ 128 bits. Both are made positive for wNAF encoding;
|
||||||
* the negation flags indicate whether the corresponding point should be negated.
|
* the negation flags indicate whether the corresponding point should be negated.
|
||||||
*/
|
*/
|
||||||
fun splitScalar(k: IntArray): Split {
|
fun splitScalar(k: LongArray): Split {
|
||||||
val c1 = mulShift384(k, G1)
|
val c1 = mulShift384(k, G1)
|
||||||
val c2 = mulShift384(k, G2)
|
val c2 = mulShift384(k, G2)
|
||||||
val r2 = ScalarN.add(ScalarN.mul(c1, MINUS_B1), ScalarN.mul(c2, MINUS_B2))
|
val r2 = ScalarN.add(ScalarN.mul(c1, MINUS_B1), ScalarN.mul(c2, MINUS_B2))
|
||||||
@@ -101,10 +102,10 @@ internal object Glv {
|
|||||||
* bits — a fix for a bug where carries past bit 255 were silently dropped.
|
* bits — a fix for a bug where carries past bit 255 were silently dropped.
|
||||||
*/
|
*/
|
||||||
fun wnaf(
|
fun wnaf(
|
||||||
scalar: IntArray,
|
scalar: LongArray,
|
||||||
w: Int,
|
w: Int,
|
||||||
maxBits: Int,
|
maxBits: Int,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val totalBits = maxBits + w
|
val totalBits = maxBits + w
|
||||||
val sLimbs = maxOf((totalBits + 31) / 32, scalar.size)
|
val sLimbs = maxOf((totalBits + 31) / 32, scalar.size)
|
||||||
val result = IntArray(totalBits)
|
val result = IntArray(totalBits)
|
||||||
@@ -112,7 +113,7 @@ internal object Glv {
|
|||||||
scalar.copyInto(s)
|
scalar.copyInto(s)
|
||||||
var bit = 0
|
var bit = 0
|
||||||
while (bit < totalBits) {
|
while (bit < totalBits) {
|
||||||
if (s[bit / 32] ushr (bit % 32) and 1 == 0) {
|
if (s[bit / 64] ushr (bit % 64) and 1 == 0) {
|
||||||
bit++
|
bit++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -131,48 +132,50 @@ internal object Glv {
|
|||||||
|
|
||||||
/** Multiply two 256-bit numbers, return the result shifted right by 384 bits (rounded). */
|
/** Multiply two 256-bit numbers, return the result shifted right by 384 bits (rounded). */
|
||||||
private fun mulShift384(
|
private fun mulShift384(
|
||||||
k: IntArray,
|
k: LongArray,
|
||||||
g: IntArray,
|
g: LongArray,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val wide = LongArray(8)
|
val wide = LongArray(8)
|
||||||
U256.mulWide(wide, k, g)
|
U256.mulWide(wide, k, g)
|
||||||
val result = LongArray(4)
|
val result = LongArray(4)
|
||||||
for (i in 0 until 4) result[i] = wide[i + 12]
|
for (i in 0 until 2) result[i] = wide[i + 6]
|
||||||
if (wide[11] < 0) { // Round based on bit 383
|
if (wide[5] < 0) { // Round based on bit 383
|
||||||
var c = 1L
|
var c = 1L
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
c += (result[i].toLong() and 0xFFFFFFFFL)
|
val s = result[i] + c
|
||||||
result[i] = c.toInt()
|
val ov = if (s.toULong() < result[i].toULong()) 1L else 0L
|
||||||
c = c ushr 32
|
result[i] = s
|
||||||
|
c = ov
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBitsVar(
|
private fun getBitsVar(
|
||||||
s: IntArray,
|
s: LongArray,
|
||||||
bitPos: Int,
|
bitPos: Int,
|
||||||
count: Int,
|
count: Int,
|
||||||
): Int {
|
): Int {
|
||||||
if (count == 0) return 0
|
if (count == 0) return 0
|
||||||
val limb = bitPos / 32
|
val limb = bitPos / 64
|
||||||
val shift = bitPos % 32
|
val shift = bitPos % 32
|
||||||
var r = (s[limb] ushr shift)
|
var r = (s[limb] ushr shift)
|
||||||
if (shift + count > 32 && limb + 1 < s.size) r = r or (s[limb + 1] shl (32 - shift))
|
if (shift + count > 64 && limb + 1 < s.size) r = r or (s[limb + 1] shl (64 - shift))
|
||||||
return r and ((1 shl count) - 1)
|
return (r and ((1L shl count) - 1L)).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addBitTo(
|
private fun addBitTo(
|
||||||
s: IntArray,
|
s: LongArray,
|
||||||
bitPos: Int,
|
bitPos: Int,
|
||||||
) {
|
) {
|
||||||
val limb = bitPos / 32
|
val limb = bitPos / 64
|
||||||
if (limb >= s.size) return
|
if (limb >= s.size) return
|
||||||
var carry = (1L shl (bitPos % 32))
|
var carry = (1L shl (bitPos % 64))
|
||||||
for (i in limb until s.size) {
|
for (i in limb until s.size) {
|
||||||
carry += (s[i].toLong() and 0xFFFFFFFFL)
|
val sum = s[i] + carry
|
||||||
s[i] = carry.toInt()
|
val ov = if (sum.toULong() < s[i].toULong()) 1L else 0L
|
||||||
carry = carry ushr 32
|
s[i] = sum
|
||||||
|
carry = ov
|
||||||
if (carry == 0L) break
|
if (carry == 0L) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +186,8 @@ internal object Glv {
|
|||||||
/** -λ mod n */
|
/** -λ mod n */
|
||||||
private val MINUS_LAMBDA =
|
private val MINUS_LAMBDA =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0xB51283CF.toInt(),
|
-2247357714951666737L, -6304834983940376126L, 6546514211138018212L, -6008836872998760673L,
|
||||||
|
),
|
||||||
0xE0CFC810.toInt(),
|
0xE0CFC810.toInt(),
|
||||||
0x8EC739C2.toInt(),
|
0x8EC739C2.toInt(),
|
||||||
0xA880B9FC.toInt(),
|
0xA880B9FC.toInt(),
|
||||||
@@ -196,7 +200,8 @@ internal object Glv {
|
|||||||
/** Babai rounding constant g1 = round(2^384 · |b2| / n) */
|
/** Babai rounding constant g1 = round(2^384 · |b2| / n) */
|
||||||
private val G1 =
|
private val G1 =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x45DBB031.toInt(),
|
-1687969588364726223L, 4443515802769476223L, -1698823648040391915L, 3496713202691238861L,
|
||||||
|
),
|
||||||
0xE893209A.toInt(),
|
0xE893209A.toInt(),
|
||||||
0x71E8CA7F.toInt(),
|
0x71E8CA7F.toInt(),
|
||||||
0x3DAA8A14.toInt(),
|
0x3DAA8A14.toInt(),
|
||||||
@@ -209,7 +214,8 @@ internal object Glv {
|
|||||||
/** Babai rounding constant g2 = round(2^384 · |b1| / n) */
|
/** Babai rounding constant g2 = round(2^384 · |b1| / n) */
|
||||||
private val G2 =
|
private val G2 =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x8AC47F71.toInt(),
|
1545214808910233457L, 2455034284347819718L, 8022177200260244676L, -1998614352016537560L,
|
||||||
|
),
|
||||||
0x1571B4AE.toInt(),
|
0x1571B4AE.toInt(),
|
||||||
0x9DF506C6.toInt(),
|
0x9DF506C6.toInt(),
|
||||||
0x221208AC.toInt(),
|
0x221208AC.toInt(),
|
||||||
@@ -222,7 +228,8 @@ internal object Glv {
|
|||||||
/** -b1 mod n (lattice basis vector) */
|
/** -b1 mod n (lattice basis vector) */
|
||||||
private val MINUS_B1 =
|
private val MINUS_B1 =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x0ABFE4C3.toInt(),
|
8022177200260244675L, -1998614352016537560L, 0L, 0L,
|
||||||
|
),
|
||||||
0x6F547FA9.toInt(),
|
0x6F547FA9.toInt(),
|
||||||
0x010E8828.toInt(),
|
0x010E8828.toInt(),
|
||||||
0xE4437ED6.toInt(),
|
0xE4437ED6.toInt(),
|
||||||
@@ -235,7 +242,8 @@ internal object Glv {
|
|||||||
/** -b2 mod n (lattice basis vector) */
|
/** -b2 mod n (lattice basis vector) */
|
||||||
private val MINUS_B2 =
|
private val MINUS_B2 =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x3DB1562C.toInt(),
|
-2925706260434037204L, -8491525256057179027L, -2L, -1L,
|
||||||
|
),
|
||||||
0xD765CDA8.toInt(),
|
0xD765CDA8.toInt(),
|
||||||
0x0774346D.toInt(),
|
0x0774346D.toInt(),
|
||||||
0x8A280AC5.toInt(),
|
0x8A280AC5.toInt(),
|
||||||
@@ -248,7 +256,8 @@ internal object Glv {
|
|||||||
/** n / 2, used to determine if a half-scalar needs negation */
|
/** n / 2, used to determine if a half-scalar needs negation */
|
||||||
private val N_HALF =
|
private val N_HALF =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x681B20A0.toInt(),
|
-2312264954237214560L, 6725966010171805725L, -1L, 9223372036854775807L,
|
||||||
|
),
|
||||||
0xDFE92F46.toInt(),
|
0xDFE92F46.toInt(),
|
||||||
0x57A4501D.toInt(),
|
0x57A4501D.toInt(),
|
||||||
0x5D576E73.toInt(),
|
0x5D576E73.toInt(),
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ package com.vitorpamplona.quartz.utils.secp256k1
|
|||||||
*/
|
*/
|
||||||
internal object KeyCodec {
|
internal object KeyCodec {
|
||||||
/** Curve constant b = 7 in y² = x³ + 7. */
|
/** Curve constant b = 7 in y² = x³ + 7. */
|
||||||
private val B = longArrayOf(7, 0, 0, 0, 0, 0, 0, 0)
|
private val B = longArrayOf(7L, 0L, 0L, 0L)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lift an x-coordinate to a curve point with even y (BIP-340 convention).
|
* Lift an x-coordinate to a curve point with even y (BIP-340 convention).
|
||||||
* Computes y = √(x³ + 7) mod p. Returns false if x is not a valid coordinate.
|
* Computes y = √(x³ + 7) mod p. Returns false if x is not a valid coordinate.
|
||||||
*/
|
*/
|
||||||
fun liftX(
|
fun liftX(
|
||||||
outX: IntArray,
|
outX: LongArray,
|
||||||
outY: IntArray,
|
outY: LongArray,
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (U256.cmp(x, FieldP.P) >= 0) return false
|
if (U256.cmp(x, FieldP.P) >= 0) return false
|
||||||
val t = LongArray(4)
|
val t = LongArray(4)
|
||||||
@@ -62,7 +62,7 @@ internal object KeyCodec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Check if y-coordinate is even (LSB = 0). */
|
/** Check if y-coordinate is even (LSB = 0). */
|
||||||
fun hasEvenY(y: IntArray): Boolean = y[0] and 1 == 0
|
fun hasEvenY(y: LongArray): Boolean = y[0] and 1 == 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a serialized public key (33 bytes compressed or 65 bytes uncompressed).
|
* Parse a serialized public key (33 bytes compressed or 65 bytes uncompressed).
|
||||||
@@ -71,8 +71,8 @@ internal object KeyCodec {
|
|||||||
*/
|
*/
|
||||||
fun parsePublicKey(
|
fun parsePublicKey(
|
||||||
pubkey: ByteArray,
|
pubkey: ByteArray,
|
||||||
outX: IntArray,
|
outX: LongArray,
|
||||||
outY: IntArray,
|
outY: LongArray,
|
||||||
): Boolean =
|
): Boolean =
|
||||||
when {
|
when {
|
||||||
pubkey.size == 33 && (pubkey[0] == 0x02.toByte() || pubkey[0] == 0x03.toByte()) -> {
|
pubkey.size == 33 && (pubkey[0] == 0x02.toByte() || pubkey[0] == 0x03.toByte()) -> {
|
||||||
@@ -112,8 +112,8 @@ internal object KeyCodec {
|
|||||||
|
|
||||||
/** Serialize as 65-byte uncompressed: 04 || x (32 bytes) || y (32 bytes). */
|
/** Serialize as 65-byte uncompressed: 04 || x (32 bytes) || y (32 bytes). */
|
||||||
fun serializeUncompressed(
|
fun serializeUncompressed(
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
y: IntArray,
|
y: LongArray,
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
val r = ByteArray(65)
|
val r = ByteArray(65)
|
||||||
r[0] = 0x04
|
r[0] = 0x04
|
||||||
@@ -124,8 +124,8 @@ internal object KeyCodec {
|
|||||||
|
|
||||||
/** Serialize as 33-byte compressed: 02/03 || x (32 bytes). */
|
/** Serialize as 33-byte compressed: 02/03 || x (32 bytes). */
|
||||||
fun serializeCompressed(
|
fun serializeCompressed(
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
y: IntArray,
|
y: LongArray,
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
val r = ByteArray(33)
|
val r = ByteArray(33)
|
||||||
r[0] = if (hasEvenY(y)) 0x02 else 0x03
|
r[0] = if (hasEvenY(y)) 0x02 else 0x03
|
||||||
|
|||||||
@@ -71,19 +71,19 @@ package com.vitorpamplona.quartz.utils.secp256k1
|
|||||||
* multiplication, which performs thousands of doublings and additions per operation.
|
* multiplication, which performs thousands of doublings and additions per operation.
|
||||||
*/
|
*/
|
||||||
internal class MutablePoint(
|
internal class MutablePoint(
|
||||||
val x: IntArray = LongArray(4),
|
val x: LongArray = LongArray(4),
|
||||||
val y: IntArray = LongArray(4),
|
val y: LongArray = LongArray(4),
|
||||||
val z: IntArray = LongArray(4),
|
val z: LongArray = LongArray(4),
|
||||||
) {
|
) {
|
||||||
fun isInfinity(): Boolean = U256.isZero(z)
|
fun isInfinity(): Boolean = U256.isZero(z)
|
||||||
|
|
||||||
fun setInfinity() {
|
fun setInfinity() {
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
x[i] = 0
|
x[i] = 0L
|
||||||
z[i] = 0
|
z[i] = 0L
|
||||||
}
|
}
|
||||||
y[0] = 1
|
y[0] = 1L
|
||||||
for (i in 1 until 8) y[i] = 0
|
for (i in 1 until 4) y[i] = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyFrom(other: MutablePoint) {
|
fun copyFrom(other: MutablePoint) {
|
||||||
@@ -93,13 +93,13 @@ internal class MutablePoint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setAffine(
|
fun setAffine(
|
||||||
ax: IntArray,
|
ax: LongArray,
|
||||||
ay: IntArray,
|
ay: LongArray,
|
||||||
) {
|
) {
|
||||||
ax.copyInto(x)
|
ax.copyInto(x)
|
||||||
ay.copyInto(y)
|
ay.copyInto(y)
|
||||||
z[0] = 1
|
z[0] = 1L
|
||||||
for (i in 1 until 8) z[i] = 0
|
for (i in 1 until 4) z[i] = 0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +108,8 @@ internal class MutablePoint(
|
|||||||
* Used for precomputed tables where we want compact storage and mixed addition.
|
* Used for precomputed tables where we want compact storage and mixed addition.
|
||||||
*/
|
*/
|
||||||
internal class AffinePoint(
|
internal class AffinePoint(
|
||||||
val x: IntArray = LongArray(4),
|
val x: LongArray = LongArray(4),
|
||||||
val y: IntArray = LongArray(4),
|
val y: LongArray = LongArray(4),
|
||||||
)
|
)
|
||||||
|
|
||||||
internal object ECPoint {
|
internal object ECPoint {
|
||||||
@@ -117,7 +117,8 @@ internal object ECPoint {
|
|||||||
|
|
||||||
val GX =
|
val GX =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x16F81798.toInt(),
|
6481385041966929816L, 188021827762530521L, 6170039885052185351L, 8772561819708210092L,
|
||||||
|
),
|
||||||
0x59F2815B.toInt(),
|
0x59F2815B.toInt(),
|
||||||
0x2DCE28D9.toInt(),
|
0x2DCE28D9.toInt(),
|
||||||
0x029BFCDB.toInt(),
|
0x029BFCDB.toInt(),
|
||||||
@@ -128,7 +129,8 @@ internal object ECPoint {
|
|||||||
)
|
)
|
||||||
val GY =
|
val GY =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0xFB10D4B8.toInt(),
|
-7185545363635252040L, -209500633525038055L, 6747795201694173352L, 5204712524664259685L,
|
||||||
|
),
|
||||||
0x9C47D08F.toInt(),
|
0x9C47D08F.toInt(),
|
||||||
0xA6855419.toInt(),
|
0xA6855419.toInt(),
|
||||||
0xFD17B448.toInt(),
|
0xFD17B448.toInt(),
|
||||||
@@ -139,7 +141,7 @@ internal object ECPoint {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** Curve constant b = 7 in y² = x³ + 7. */
|
/** Curve constant b = 7 in y² = x³ + 7. */
|
||||||
private val B = longArrayOf(7, 0, 0, 0, 0, 0, 0, 0)
|
private val B = longArrayOf(7L, 0L, 0L, 0L)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wNAF window width for the G-side of scalar multiplication.
|
* wNAF window width for the G-side of scalar multiplication.
|
||||||
@@ -329,8 +331,8 @@ internal object ECPoint {
|
|||||||
fun addMixed(
|
fun addMixed(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
p: MutablePoint,
|
p: MutablePoint,
|
||||||
qx: IntArray,
|
qx: LongArray,
|
||||||
qy: IntArray,
|
qy: LongArray,
|
||||||
) {
|
) {
|
||||||
if (p.isInfinity()) {
|
if (p.isInfinity()) {
|
||||||
out.setAffine(qx, qy)
|
out.setAffine(qx, qy)
|
||||||
@@ -450,7 +452,7 @@ internal object ECPoint {
|
|||||||
fun mul(
|
fun mul(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
p: MutablePoint,
|
p: MutablePoint,
|
||||||
scalar: IntArray,
|
scalar: LongArray,
|
||||||
) {
|
) {
|
||||||
if (U256.isZero(scalar) || p.isInfinity()) {
|
if (U256.isZero(scalar) || p.isInfinity()) {
|
||||||
out.setInfinity()
|
out.setInfinity()
|
||||||
@@ -513,7 +515,7 @@ internal object ECPoint {
|
|||||||
*/
|
*/
|
||||||
fun mulG(
|
fun mulG(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
scalar: IntArray,
|
scalar: LongArray,
|
||||||
) {
|
) {
|
||||||
if (U256.isZero(scalar)) {
|
if (U256.isZero(scalar)) {
|
||||||
out.setInfinity()
|
out.setInfinity()
|
||||||
@@ -559,9 +561,9 @@ internal object ECPoint {
|
|||||||
*/
|
*/
|
||||||
fun mulDoubleG(
|
fun mulDoubleG(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
s: IntArray,
|
s: LongArray,
|
||||||
p: MutablePoint,
|
p: MutablePoint,
|
||||||
e: IntArray,
|
e: LongArray,
|
||||||
) {
|
) {
|
||||||
val wP = 5 // Window for P-side (table built per-call, keep small)
|
val wP = 5 // Window for P-side (table built per-call, keep small)
|
||||||
val pTableSize = 1 shl (wP - 2) // 8 entries for P
|
val pTableSize = 1 shl (wP - 2) // 8 entries for P
|
||||||
@@ -629,8 +631,8 @@ internal object ECPoint {
|
|||||||
private fun addWnafMixed(
|
private fun addWnafMixed(
|
||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
tmp: MutablePoint,
|
tmp: MutablePoint,
|
||||||
negY: IntArray,
|
negY: LongArray,
|
||||||
wnafDigits: IntArray,
|
wnafDigits: LongArray,
|
||||||
bitIndex: Int,
|
bitIndex: Int,
|
||||||
table: Array<AffinePoint>,
|
table: Array<AffinePoint>,
|
||||||
glvNeg: Boolean,
|
glvNeg: Boolean,
|
||||||
@@ -654,7 +656,7 @@ internal object ECPoint {
|
|||||||
out: MutablePoint,
|
out: MutablePoint,
|
||||||
tmp: MutablePoint,
|
tmp: MutablePoint,
|
||||||
negScratch: MutablePoint,
|
negScratch: MutablePoint,
|
||||||
wnafDigits: IntArray,
|
wnafDigits: LongArray,
|
||||||
bitIndex: Int,
|
bitIndex: Int,
|
||||||
table: Array<MutablePoint>,
|
table: Array<MutablePoint>,
|
||||||
glvNeg: Boolean,
|
glvNeg: Boolean,
|
||||||
@@ -685,8 +687,8 @@ internal object ECPoint {
|
|||||||
*/
|
*/
|
||||||
fun toAffine(
|
fun toAffine(
|
||||||
p: MutablePoint,
|
p: MutablePoint,
|
||||||
outX: IntArray,
|
outX: LongArray,
|
||||||
outY: IntArray,
|
outY: LongArray,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (p.isInfinity()) return false
|
if (p.isInfinity()) return false
|
||||||
val zInv = LongArray(4)
|
val zInv = LongArray(4)
|
||||||
@@ -703,26 +705,26 @@ internal object ECPoint {
|
|||||||
// ==================== Key Encoding (delegates to KeyCodec) ====================
|
// ==================== Key Encoding (delegates to KeyCodec) ====================
|
||||||
|
|
||||||
fun liftX(
|
fun liftX(
|
||||||
outX: IntArray,
|
outX: LongArray,
|
||||||
outY: IntArray,
|
outY: LongArray,
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
) = KeyCodec.liftX(outX, outY, x)
|
) = KeyCodec.liftX(outX, outY, x)
|
||||||
|
|
||||||
fun hasEvenY(y: IntArray) = KeyCodec.hasEvenY(y)
|
fun hasEvenY(y: LongArray) = KeyCodec.hasEvenY(y)
|
||||||
|
|
||||||
fun parsePublicKey(
|
fun parsePublicKey(
|
||||||
pubkey: ByteArray,
|
pubkey: ByteArray,
|
||||||
outX: IntArray,
|
outX: LongArray,
|
||||||
outY: IntArray,
|
outY: LongArray,
|
||||||
) = KeyCodec.parsePublicKey(pubkey, outX, outY)
|
) = KeyCodec.parsePublicKey(pubkey, outX, outY)
|
||||||
|
|
||||||
fun serializeUncompressed(
|
fun serializeUncompressed(
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
y: IntArray,
|
y: LongArray,
|
||||||
) = KeyCodec.serializeUncompressed(x, y)
|
) = KeyCodec.serializeUncompressed(x, y)
|
||||||
|
|
||||||
fun serializeCompressed(
|
fun serializeCompressed(
|
||||||
x: IntArray,
|
x: LongArray,
|
||||||
y: IntArray,
|
y: LongArray,
|
||||||
) = KeyCodec.serializeCompressed(x, y)
|
) = KeyCodec.serializeCompressed(x, y)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ package com.vitorpamplona.quartz.utils.secp256k1
|
|||||||
internal object ScalarN {
|
internal object ScalarN {
|
||||||
val N =
|
val N =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0xD0364141.toInt(),
|
-4624529908474429119L, -4994812053365940165L, -2L, -1L,
|
||||||
|
),
|
||||||
0xBFD25E8C.toInt(),
|
0xBFD25E8C.toInt(),
|
||||||
0xAF48A03B.toInt(),
|
0xAF48A03B.toInt(),
|
||||||
0xBAAEDCE6.toInt(),
|
0xBAAEDCE6.toInt(),
|
||||||
@@ -48,7 +49,8 @@ internal object ScalarN {
|
|||||||
/** 2^256 - n: the small constant used for reduction (≈129 bits) */
|
/** 2^256 - n: the small constant used for reduction (≈129 bits) */
|
||||||
private val N_COMPLEMENT =
|
private val N_COMPLEMENT =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0x2FC9BEBF.toInt(),
|
4624529908474429119L, 4994812053365940164L, 1L, 0L,
|
||||||
|
),
|
||||||
0x402DA173.toInt(),
|
0x402DA173.toInt(),
|
||||||
0x50B75FC4.toInt(),
|
0x50B75FC4.toInt(),
|
||||||
0x45512319.toInt(),
|
0x45512319.toInt(),
|
||||||
@@ -61,7 +63,8 @@ internal object ScalarN {
|
|||||||
/** n - 2: exponent for Fermat inversion */
|
/** n - 2: exponent for Fermat inversion */
|
||||||
private val N_MINUS_2 =
|
private val N_MINUS_2 =
|
||||||
longArrayOf(
|
longArrayOf(
|
||||||
0xD036413F.toInt(),
|
-4624529908474429121L, -4994812053365940165L, -2L, -1L,
|
||||||
|
),
|
||||||
0xBFD25E8C.toInt(),
|
0xBFD25E8C.toInt(),
|
||||||
0xAF48A03B.toInt(),
|
0xAF48A03B.toInt(),
|
||||||
0xBAAEDCE6.toInt(),
|
0xBAAEDCE6.toInt(),
|
||||||
@@ -72,10 +75,10 @@ internal object ScalarN {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** Check if 0 < a < n (valid non-zero scalar). */
|
/** Check if 0 < a < n (valid non-zero scalar). */
|
||||||
fun isValid(a: IntArray): Boolean = !U256.isZero(a) && U256.cmp(a, N) < 0
|
fun isValid(a: LongArray): Boolean = !U256.isZero(a) && U256.cmp(a, N) < 0
|
||||||
|
|
||||||
/** If a >= n, return a - n. Otherwise return a unchanged. */
|
/** If a >= n, return a - n. Otherwise return a unchanged. */
|
||||||
fun reduce(a: IntArray): IntArray =
|
fun reduce(a: LongArray): LongArray =
|
||||||
if (U256.cmp(a, N) >= 0) {
|
if (U256.cmp(a, N) >= 0) {
|
||||||
val r = LongArray(4)
|
val r = LongArray(4)
|
||||||
U256.subTo(r, a, N)
|
U256.subTo(r, a, N)
|
||||||
@@ -85,9 +88,9 @@ internal object ScalarN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add(
|
fun add(
|
||||||
a: IntArray,
|
a: LongArray,
|
||||||
b: IntArray,
|
b: LongArray,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val r = LongArray(4)
|
val r = LongArray(4)
|
||||||
val carry = U256.addTo(r, a, b)
|
val carry = U256.addTo(r, a, b)
|
||||||
if (carry != 0) U256.addTo(r, r, N_COMPLEMENT)
|
if (carry != 0) U256.addTo(r, r, N_COMPLEMENT)
|
||||||
@@ -96,9 +99,9 @@ internal object ScalarN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sub(
|
fun sub(
|
||||||
a: IntArray,
|
a: LongArray,
|
||||||
b: IntArray,
|
b: LongArray,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val r = LongArray(4)
|
val r = LongArray(4)
|
||||||
val borrow = U256.subTo(r, a, b)
|
val borrow = U256.subTo(r, a, b)
|
||||||
if (borrow != 0) U256.addTo(r, r, N)
|
if (borrow != 0) U256.addTo(r, r, N)
|
||||||
@@ -106,15 +109,15 @@ internal object ScalarN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mul(
|
fun mul(
|
||||||
a: IntArray,
|
a: LongArray,
|
||||||
b: IntArray,
|
b: LongArray,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val w = LongArray(8)
|
val w = LongArray(8)
|
||||||
U256.mulWide(w, a, b)
|
U256.mulWide(w, a, b)
|
||||||
return reduceWide(w)
|
return reduceWide(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun neg(a: IntArray): IntArray {
|
fun neg(a: LongArray): LongArray {
|
||||||
if (U256.isZero(a)) return LongArray(4)
|
if (U256.isZero(a)) return LongArray(4)
|
||||||
val r = LongArray(4)
|
val r = LongArray(4)
|
||||||
U256.subTo(r, N, a)
|
U256.subTo(r, N, a)
|
||||||
@@ -122,12 +125,12 @@ internal object ScalarN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** a^(-1) mod n via Fermat's little theorem. */
|
/** a^(-1) mod n via Fermat's little theorem. */
|
||||||
fun inv(a: IntArray): IntArray {
|
fun inv(a: LongArray): LongArray {
|
||||||
require(!U256.isZero(a))
|
require(!U256.isZero(a))
|
||||||
return powModN(a, N_MINUS_2)
|
return powModN(a, N_MINUS_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduceSelf(a: IntArray) {
|
private fun reduceSelf(a: LongArray) {
|
||||||
if (U256.cmp(a, N) >= 0) U256.subTo(a, a, N)
|
if (U256.cmp(a, N) >= 0) U256.subTo(a, a, N)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +141,10 @@ internal object ScalarN {
|
|||||||
* Since N_COMPLEMENT is ~129 bits, hi × N_COMPLEMENT is ~385 bits. We repeat the
|
* Since N_COMPLEMENT is ~129 bits, hi × N_COMPLEMENT is ~385 bits. We repeat the
|
||||||
* reduction until the result fits in 256 bits, then do a final conditional subtraction.
|
* reduction until the result fits in 256 bits, then do a final conditional subtraction.
|
||||||
*/
|
*/
|
||||||
private fun reduceWide(w: IntArray): IntArray {
|
private fun reduceWide(w: LongArray): LongArray {
|
||||||
val lo = LongArray(4)
|
val lo = LongArray(4)
|
||||||
val hi = LongArray(4)
|
val hi = LongArray(4)
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
lo[i] = w[i]
|
lo[i] = w[i]
|
||||||
hi[i] = w[i + 8]
|
hi[i] = w[i + 8]
|
||||||
}
|
}
|
||||||
@@ -155,9 +158,9 @@ internal object ScalarN {
|
|||||||
U256.mulWide(hiTimesNC, hi, N_COMPLEMENT)
|
U256.mulWide(hiTimesNC, hi, N_COMPLEMENT)
|
||||||
val sum = LongArray(8)
|
val sum = LongArray(8)
|
||||||
var carry = 0L
|
var carry = 0L
|
||||||
for (i in 0 until 16) {
|
for (i in 0 until 8) {
|
||||||
carry += (hiTimesNC[i].toLong() and 0xFFFFFFFFL) +
|
carry += (hiTimesNC[i]) +
|
||||||
if (i < 8) (lo[i].toLong() and 0xFFFFFFFFL) else 0L
|
if (i < 4) (lo[i]) else 0L
|
||||||
sum[i] = carry.toInt()
|
sum[i] = carry.toInt()
|
||||||
carry = carry ushr 32
|
carry = carry ushr 32
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,7 @@ internal object ScalarN {
|
|||||||
// Round 2 if still > 256 bits
|
// Round 2 if still > 256 bits
|
||||||
val lo2 = LongArray(4)
|
val lo2 = LongArray(4)
|
||||||
val hi2 = LongArray(4)
|
val hi2 = LongArray(4)
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
lo2[i] = sum[i]
|
lo2[i] = sum[i]
|
||||||
hi2[i] = sum[i + 8]
|
hi2[i] = sum[i + 8]
|
||||||
}
|
}
|
||||||
@@ -178,24 +181,24 @@ internal object ScalarN {
|
|||||||
U256.mulWide(hi2NC, hi2, N_COMPLEMENT)
|
U256.mulWide(hi2NC, hi2, N_COMPLEMENT)
|
||||||
var c2 = 0L
|
var c2 = 0L
|
||||||
val result = LongArray(4)
|
val result = LongArray(4)
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
c2 += (lo2[i].toLong() and 0xFFFFFFFFL) + (hi2NC[i].toLong() and 0xFFFFFFFFL)
|
c2 += (lo2[i]) + (hi2NC[i])
|
||||||
result[i] = c2.toInt()
|
result[i] = c2.toInt()
|
||||||
c2 = c2 ushr 32
|
c2 = c2 ushr 32
|
||||||
}
|
}
|
||||||
var overflow = c2
|
var overflow = c2
|
||||||
for (i in 8 until 16) overflow += (hi2NC[i].toLong() and 0xFFFFFFFFL)
|
for (i in 4 until 8) overflow += (hi2NC[i])
|
||||||
if (overflow > 0) {
|
if (overflow > 0) {
|
||||||
val corr = IntArray(9)
|
val corr = LongArray(5)
|
||||||
var cc = 0L
|
var cc = 0L
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
cc += (N_COMPLEMENT[i].toLong() and 0xFFFFFFFFL) * overflow
|
cc += (N_COMPLEMENT[i]) * overflow
|
||||||
corr[i] = cc.toInt()
|
corr[i] = cc.toInt()
|
||||||
cc = cc ushr 32
|
cc = cc ushr 32
|
||||||
}
|
}
|
||||||
var c3 = 0L
|
var c3 = 0L
|
||||||
for (i in 0 until 8) {
|
for (i in 0 until 4) {
|
||||||
c3 += (result[i].toLong() and 0xFFFFFFFFL) + (corr[i].toLong() and 0xFFFFFFFFL)
|
c3 += (result[i]) + (corr[i])
|
||||||
result[i] = c3.toInt()
|
result[i] = c3.toInt()
|
||||||
c3 = c3 ushr 32
|
c3 = c3 ushr 32
|
||||||
}
|
}
|
||||||
@@ -205,9 +208,9 @@ internal object ScalarN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun powModN(
|
private fun powModN(
|
||||||
base: IntArray,
|
base: LongArray,
|
||||||
exp: IntArray,
|
exp: LongArray,
|
||||||
): IntArray {
|
): LongArray {
|
||||||
val result = LongArray(4)
|
val result = LongArray(4)
|
||||||
val b = base.copyOf()
|
val b = base.copyOf()
|
||||||
var highBit = 255
|
var highBit = 255
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ object Secp256k1 {
|
|||||||
*/
|
*/
|
||||||
private fun signSchnorrInternal(
|
private fun signSchnorrInternal(
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
d0: IntArray,
|
d0: LongArray,
|
||||||
pBytes: ByteArray,
|
pBytes: ByteArray,
|
||||||
pubKeyHasEvenY: Boolean,
|
pubKeyHasEvenY: Boolean,
|
||||||
auxrand: ByteArray?,
|
auxrand: ByteArray?,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class FieldPTest {
|
|||||||
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
// ==================== Basic identities ====================
|
// ==================== Basic identities ====================
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
/** Comprehensive tests for GLV endomorphism and wNAF encoding. */
|
/** Comprehensive tests for GLV endomorphism and wNAF encoding. */
|
||||||
class GlvTest {
|
class GlvTest {
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
private fun hex(s: String) =
|
private fun hex(s: String) =
|
||||||
U256.fromBytes(
|
U256.fromBytes(
|
||||||
@@ -210,7 +210,7 @@ class GlvTest {
|
|||||||
// ==================== Helpers ====================
|
// ==================== Helpers ====================
|
||||||
|
|
||||||
/** Reconstruct a scalar from wNAF digits using Horner's method. */
|
/** Reconstruct a scalar from wNAF digits using Horner's method. */
|
||||||
private fun reconstructWnaf(digits: IntArray): IntArray {
|
private fun reconstructWnaf(digits: LongArray): LongArray {
|
||||||
var acc = LongArray(4)
|
var acc = LongArray(4)
|
||||||
for (bit in digits.size - 1 downTo 0) {
|
for (bit in digits.size - 1 downTo 0) {
|
||||||
val doubled = LongArray(4)
|
val doubled = LongArray(4)
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
/** Tests for KeyCodec: key parsing, serialization, liftX, hasEvenY. */
|
/** Tests for KeyCodec: key parsing, serialization, liftX, hasEvenY. */
|
||||||
class KeyCodecTest {
|
class KeyCodecTest {
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
// ==================== liftX ====================
|
// ==================== liftX ====================
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class PointTest {
|
|||||||
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
// ==================== Generator point ====================
|
// ==================== Generator point ====================
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ class ScalarNTest {
|
|||||||
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
// ==================== isValid ====================
|
// ==================== isValid ====================
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class U256Test {
|
|||||||
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
s.chunked(2).map { it.toInt(16).toByte() }.toByteArray(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun toHex(a: IntArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
private fun toHex(a: LongArray) = U256.toBytes(a).joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
// ==================== isZero / cmp ====================
|
// ==================== isZero / cmp ====================
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user