nx_u256_mul.nx
buildroot/runtime/nx_u256_mul.nx
about
nx_u256_mul.nx -- 256 x 256 -> 512-bit unsigned multiplication.
Phase 0b §I.3 piece 1b of the ECDSA-P256 arc. Extends the
nx_u256 limb primitives (commit 6ff89dd8) with the schoolbook
8 x 8 limb multiplication that produces a 16-limb (512-bit)
wide product. This is the foundation for both:
- p256_field_mul (composes wide_mul + NIST P-256 Solinas
reduction; queued for piece 1c)
- p256_scalar_mul mod n (composes wide_mul + Barrett or
trial-subtract reduction mod the group order; queued)
Split rationale: wide multiplication is a primitive in its own
right. Splitting it from reduce-mod-p means scalar arithmetic
(which needs reduce-mod-n, NOT reduce-mod-p) gets to reuse the
same wide_mul. Per Cardinal 9 (single-responsibility), this
primitive does ONE thing -- big-int multiply -- and the two
reduction primitives compose on top.
Algorithm: schoolbook with carry propagation.
For each i in 0..8:
carry = 0
For each j in 0..8:
s = out[i+j] + a[i] * b[j] + carry
out[i+j] = low_32(s)
carry = high_32(s)
out[i+8] = carry
Each (a[i] * b[j] + out[i+j] + carry) intermediate fits in
u64 (max = (2^32 - 1)^2 + 2 * (2^32 - 1) = 2^64 - 1), so the
expression is computed in a single i64 register without
128-bit emulation. Sign-extension concerns are handled by
AND-mask after right-shift -- arithmetic shift of a negative-
looking i64 still produces the correct high 32 bits when
masked with 0xFFFFFFFF.
Wide-output layout: out_16[0] = LSB of product, out_16[15] = MSB.
Same little-endian limb order as the 8-limb inputs.
Aliasing: out_16 MUST NOT overlap a or b (the algorithm reads
dependencies 2 imports · 5 importers
imports: nx_syscalls.nxnx_u256.nx
imported by: nx_p256_field_mul.nxnx_p256_modn.nxnx_p256_solinas_difftest.nxnx_u256_mul4_fuzz.nxnx_u256_mul_test.nx
structs
| none |
consts
| 71 | const NX_U256_WIDE_LIMBS: i64 = 16 |
| 72 | const NX_U256_WIDE_BYTES: i64 = 64 |
functions
| 77 | func u256_wide_alloc() -> *i64 called by 9: p256_field_mulp256_field_mul_slowmainp256_modn_mulp256_modn_mul_mont_dt_oracle_reduce+3 calls 1: nx_scratch |
| 85 | func u256_mul_wide(out_16: *i64, a: *i64, b: *i64) -> i64 |
| 113 | func u64_lt(a: i64, b: i64) -> i64 |
| 127 | func u256_mul_wide_4x64(out_16: *i64, a: *i64, b: *i64) -> i64 |
| 132 | func u256_wide_cmp(a_16: *i64, b_16: *i64) -> i64 |
| 146 | func u256_wide_fits_in_256(a_16: *i64) -> i64 called by 1: main |
| 160 | func u256_wide_copy_low(out_8: *i64, src_16: *i64) -> i64 |
| 170 | func main() -> i64 |