code wiki / (root) / nx_u256_mul.nx

nx_u256_mul.nx

buildroot/runtime/nx_u256_mul.nx

6484 B172 linesdepth 3pulls 3 transitivereach 763 importersview sourcekind tooltopic u256
docsdependenciesstructsconstsfunctions

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

nx_syscalls.nx nx_u256.nx nx_u256_mul.nx nx_p256_field_mul.nx nx_p256_modn.nx nx_p256_solinas_difftest.nx nx_u256_mul4_fuzz.nx nx_u256_mul_test.nx

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

71const NX_U256_WIDE_LIMBS: i64 = 16
72const NX_U256_WIDE_BYTES: i64 = 64

functions

77func u256_wide_alloc() -> *i64
85func u256_mul_wide(out_16: *i64, a: *i64, b: *i64) -> i64
113func u64_lt(a: i64, b: i64) -> i64
127func u256_mul_wide_4x64(out_16: *i64, a: *i64, b: *i64) -> i64
called by 1: main calls 1: u256_mul_wide
132func u256_wide_cmp(a_16: *i64, b_16: *i64) -> i64
146func u256_wide_fits_in_256(a_16: *i64) -> i64
called by 1: main
160func u256_wide_copy_low(out_8: *i64, src_16: *i64) -> i64
170func main() -> i64