From 8cf2f9795a94f5aa7d1ed06eab12164bccde7ab2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Apr 2026 03:36:09 +0000 Subject: [PATCH] fix: scalar_mul second fold carry propagation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the modular reduction in scalar_mul: the second fold step was accumulating hc2[4..7] into a single acc variable without proper limb-by-limb carry propagation. Now uses the same 8-limb sum pattern as the first fold, with a convolution-style third fold for any remaining high bits. GLV decomposition now verified correct for all test scalars. The remaining issue is in the wNAF multiply loop within ecmult (correct GLV split → wNAF encode → point table lookup → accumulate). Likely gej_add_ge aliasing or table build ordering issue. https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY --- quartz/src/main/c/secp256k1/scalar.c | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/quartz/src/main/c/secp256k1/scalar.c b/quartz/src/main/c/secp256k1/scalar.c index 6255244e0..f49f0ec4f 100644 --- a/quartz/src/main/c/secp256k1/scalar.c +++ b/quartz/src/main/c/secp256k1/scalar.c @@ -104,25 +104,31 @@ void scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_ sum[i] = (uint64_t)acc; acc >>= 64; } - /* Second fold if sum > 256 bits */ + /* Second fold: sum[4..7] * NC + sum[0..3] */ if (sum[4] | sum[5] | sum[6] | sum[7]) { uint64_t hc2[8]; mul_wide(hc2, &sum[4], NC); acc = 0; - for (int i = 0; i < 4; i++) { - acc += (uint128_t)sum[i] + hc2[i]; - r->d[i] = (uint64_t)acc; + uint64_t sum2[8]; + for (int i = 0; i < 8; i++) { + acc += (uint128_t)(i < 4 ? sum[i] : 0) + hc2[i]; + sum2[i] = (uint64_t)acc; acc >>= 64; } - /* hc2[4..7] should be negligible; handle any carry */ - for (int i = 4; i < 8; i++) { - acc += hc2[i]; - } - if (acc) { - /* Tiny third fold */ - uint128_t c = (uint128_t)r->d[0] + (uint64_t)acc * NC[0]; - r->d[0] = (uint64_t)c; c >>= 64; - if (c) { r->d[1] += (uint64_t)c; if (r->d[1] < (uint64_t)c) { r->d[2]++; if (!r->d[2]) r->d[3]++; } } + /* Third fold if still > 256 bits (sum2 is at most ~130 bits above 256) */ + if (sum2[4] | sum2[5] | sum2[6] | sum2[7]) { + /* sum2[4..7] is tiny (~2 limbs at most). Use reduce_wide pattern. */ + acc = (uint128_t)sum2[0] + (uint128_t)sum2[4] * NC[0]; + r->d[0] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)sum2[1] + (uint128_t)sum2[4] * NC[1] + (uint128_t)sum2[5] * NC[0]; + r->d[1] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)sum2[2] + (uint128_t)sum2[4] * NC[2] + (uint128_t)sum2[5] * NC[1] + (uint128_t)sum2[6] * NC[0]; + r->d[2] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)sum2[3] + (uint128_t)sum2[5] * NC[2] + (uint128_t)sum2[6] * NC[1] + (uint128_t)sum2[7] * NC[0]; + r->d[3] = (uint64_t)acc; + /* Any remaining carry is negligible — handled by while loop below */ + } else { + r->d[0] = sum2[0]; r->d[1] = sum2[1]; r->d[2] = sum2[2]; r->d[3] = sum2[3]; } } else { r->d[0] = sum[0]; r->d[1] = sum[1]; r->d[2] = sum[2]; r->d[3] = sum[3];