]> git.codecow.com Git - nano25519.git/commitdiff
Revert to scalar carry for multiplication.
authorChris Duncan <chris@codecow.com>
Sun, 4 Oct 2026 06:40:07 +0000 (23:40 -0700)
committerChris Duncan <chris@codecow.com>
Sun, 4 Oct 2026 06:40:07 +0000 (23:40 -0700)
src/assembly/ed25519/fe.ts

index ec0660ed046997ece4f0c6e82a7fa5b71b8814f1..919a414490b211fe5a216e51c51ba38f7cb1bcdf 100644 (file)
@@ -430,65 +430,89 @@ export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void
        h56 = i64x2.add(h56, t3)
        h78 = i64x2.add(h78, t4)
 
-       // sum related accumulators and shuffle into ref10 carry schedule
-       let h04_sum = v128.add<i64>(v128.shuffle<i64>(h01, h45, 0, 2), v128.shuffle<i64>(h90, h34, 1, 3))
-       let h15_sum = v128.add<i64>(v128.shuffle<i64>(h12, h56, 0, 2), v128.shuffle<i64>(h01, h45, 1, 3))
-       let h26_sum = v128.add<i64>(v128.shuffle<i64>(h23, h67, 0, 2), v128.shuffle<i64>(h12, h56, 1, 3))
-       let h37_sum = v128.add<i64>(v128.shuffle<i64>(h34, h78, 0, 2), v128.shuffle<i64>(h23, h67, 1, 3))
-       let h89_sum = v128.add<i64>(v128.shuffle<i64>(h89, h90, 0, 2), v128.shuffle<i64>(h78, h89, 1, 3))
-
-       // Signed i64x2 shifts are emulated on x64, unsigned ones are native. Adding
-       // 2⁶² keeps every sum non-negative (|h| ≤ 2⁶⁰·⁴), and the unsigned shift then
-       // floors in fewer instructions. The bias is removed by subtraction, and the
-       // value to remove is the bias minus the original size: 2⁶²⁻²⁶ = 2³⁶ (or
-       // 2⁶²⁻²⁵ = 2³⁷). After unbiasing, the result is the signed carry.
-       const bias24 = v128.splat<i64>((<i64>1 << 24) + (<i64>1 << 62))
-       const bias25 = v128.splat<i64>((<i64>1 << 25) + (<i64>1 << 62))
-       const unbias25 = v128.splat<i64>(<i64>1 << 37)
-       const unbias26 = v128.splat<i64>(<i64>1 << 36)
-       let carryA: v128
-       let carryB: v128
-
-       carryA = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h04_sum, bias25), 26), unbias26)
-       h04_sum = v128.sub<i64>(h04_sum, v128.shl<i64>(carryA, 26))
-       h15_sum = v128.add<i64>(h15_sum, carryA)
-
-       carryB = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h15_sum, bias24), 25), unbias25)
-       h15_sum = v128.sub<i64>(h15_sum, v128.shl<i64>(carryB, 25))
-       h26_sum = v128.add<i64>(h26_sum, carryB)
-
-       carryA = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h26_sum, bias25), 26), unbias26)
-       h26_sum = v128.sub<i64>(h26_sum, v128.shl<i64>(carryA, 26))
-       h37_sum = v128.add<i64>(h37_sum, carryA)
-
-       carryB = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h37_sum, bias24), 25), unbias25)
-       h37_sum = v128.sub<i64>(h37_sum, v128.shl<i64>(carryB, 25))
-       let h48_sum = v128.add<i64>(v128.shuffle<i64>(h04_sum, h89_sum, 1, 2), carryB)
-
-       carryA = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h48_sum, bias25), 26), unbias26)
-       h48_sum = v128.sub<i64>(h48_sum, v128.shl<i64>(carryA, 26))
-       let h59_sum = v128.add<i64>(v128.shuffle<i64>(h15_sum, h89_sum, 1, 3), carryA)
-
-       carryB = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h59_sum, bias24), 25), unbias25)
-       h59_sum = v128.sub<i64>(h59_sum, v128.shl<i64>(carryB, 25))
-       // wrap requires multiplying carry from h9 by 19
-       let h60_sum = v128.add<i64>(v128.shuffle<i64>(h26_sum, h04_sum, 1, 2), v128.mul<i64>(carryB, i64x2(1, 19)))
-
-       carryA = v128.sub<i64>(v128.shr<u64>(v128.add<i64>(h60_sum, bias25), 26), unbias26)
-       h60_sum = v128.sub<i64>(h60_sum, v128.shl<i64>(carryA, 26))
-       let h71_sum = v128.add<i64>(v128.shuffle<i64>(h37_sum, h15_sum, 1, 2), carryA)
-
-       // assign scalar results to output
-       v128.store_lane<i32>(h_ptr, h60_sum, 2, 0)
-       v128.store_lane<i32>(h_ptr, h71_sum, 2, 4)
-       v128.store_lane<i32>(h_ptr, h26_sum, 0, 8)
-       v128.store_lane<i32>(h_ptr, h37_sum, 0, 12)
-       v128.store_lane<i32>(h_ptr, h48_sum, 0, 16)
-       v128.store_lane<i32>(h_ptr, h59_sum, 0, 20)
-       v128.store_lane<i32>(h_ptr, h60_sum, 0, 24)
-       v128.store_lane<i32>(h_ptr, h71_sum, 0, 28)
-       v128.store_lane<i32>(h_ptr, h48_sum, 2, 32)
-       v128.store_lane<i32>(h_ptr, h59_sum, 2, 36)
+       // extract scalars from vectors
+       let h0: i64 = i64x2.extract_lane(h90, 1) + i64x2.extract_lane(h01, 0)
+       let h1: i64 = i64x2.extract_lane(h01, 1) + i64x2.extract_lane(h12, 0)
+       let h2: i64 = i64x2.extract_lane(h12, 1) + i64x2.extract_lane(h23, 0)
+       let h3: i64 = i64x2.extract_lane(h23, 1) + i64x2.extract_lane(h34, 0)
+       let h4: i64 = i64x2.extract_lane(h34, 1) + i64x2.extract_lane(h45, 0)
+       let h5: i64 = i64x2.extract_lane(h45, 1) + i64x2.extract_lane(h56, 0)
+       let h6: i64 = i64x2.extract_lane(h56, 1) + i64x2.extract_lane(h67, 0)
+       let h7: i64 = i64x2.extract_lane(h67, 1) + i64x2.extract_lane(h78, 0)
+       let h8: i64 = i64x2.extract_lane(h78, 1) + i64x2.extract_lane(h89, 0)
+       let h9: i64 = i64x2.extract_lane(h89, 1) + i64x2.extract_lane(h90, 0)
+
+       // reduce scalars with carry
+       let carry0: i64
+       let carry1: i64
+       let carry2: i64
+       let carry3: i64
+       let carry4: i64
+       let carry5: i64
+       let carry6: i64
+       let carry7: i64
+       let carry8: i64
+       let carry9: i64
+
+       carry0 = (h0 + (1 << 25)) >> 26
+       h0 -= carry0 << 26
+       h1 += carry0
+
+       carry4 = (h4 + (1 << 25)) >> 26
+       h4 -= carry4 << 26
+       h5 += carry4
+
+       carry1 = (h1 + (1 << 24)) >> 25
+       h1 -= carry1 << 25
+       h2 += carry1
+
+       carry5 = (h5 + (1 << 24)) >> 25
+       h5 -= carry5 << 25
+       h6 += carry5
+
+       carry2 = (h2 + (1 << 25)) >> 26
+       h2 -= carry2 << 26
+       h3 += carry2
+
+       carry6 = (h6 + (1 << 25)) >> 26
+       h6 -= carry6 << 26
+       h7 += carry6
+
+       carry3 = (h3 + (1 << 24)) >> 25
+       h3 -= carry3 << 25
+       h4 += carry3
+
+       carry7 = (h7 + (1 << 24)) >> 25
+       h7 -= carry7 << 25
+       h8 += carry7
+
+       carry4 = (h4 + (1 << 25)) >> 26
+       h4 -= carry4 << 26
+       h5 += carry4
+
+       carry8 = (h8 + (1 << 25)) >> 26
+       h8 -= carry8 << 26
+       h9 += carry8
+
+       carry9 = (h9 + (1 << 24)) >> 25
+       h9 -= carry9 << 25
+       h0 += carry9 * 19
+
+       carry0 = (h0 + (1 << 25)) >> 26
+       h0 -= carry0 << 26
+       h1 += carry0
+
+       // assign results to output
+       store<i32>(h_ptr, h0, 0)
+       store<i32>(h_ptr, h1, 4)
+       store<i32>(h_ptr, h2, 8)
+       store<i32>(h_ptr, h3, 12)
+       store<i32>(h_ptr, h4, 16)
+       store<i32>(h_ptr, h5, 20)
+       store<i32>(h_ptr, h6, 24)
+       store<i32>(h_ptr, h7, 28)
+       store<i32>(h_ptr, h8, 32)
+       store<i32>(h_ptr, h9, 36)
 }
 
 /**