nx_kyber_poly_tomont_wasm.nx source
↩ module page · 61 lines · 1961 B
1// nx_kyber_poly_tomont_wasm.nx -- Convert polynomial to Montgomery form.
2//
3// After polyvec_basemul_acc_montgomery (= nx_kyber_poly_basemul_acc),
4// each coefficient is in the "x * R^{-1} mod q" form because every
5// fqmul multiplied by R^{-1}. poly_tomont multiplies by R^2 mod q
6// (= 1353) and then applies montgomery_reduce, which has the effect of
7// multiplying the polynomial by R -- canceling the R^{-1} factor and
8// restoring CANONICAL representation.
9//
10// Per PQClean's poly_tomont():
11// const int16_t f = (1ULL << 32) % KYBER_Q; // = 1353
12// for each coef c: c = montgomery_reduce(c * f);
13//
14// Used immediately after basemul_acc inside K-PKE keygen/encrypt to put
15// the accumulated polynomial back into canonical form so it can be added
16// to canonical-form e/e1/e2/mu polynomials without mixing representations.
17//
18// API:
19// nx_kyber_poly_tomont(poly) -> i64
20//
21// license_tier: INDEPENDENT_REDERIVE
22// genealogy_id: international-research-sources/nist/fips_203
23// lineage_id: nishi_kyber_poly_tomont_wasm_q1
24// safe_shift_audit: no 64-bit rotations in this module
25
26const KYBER_Q: i64 = 3329
27const KYBER_QINV: i64 = 62209
28const KYBER_N: i64 = 256
29const KYBER_F: i64 = 1353 // (2^32) mod q
30
31func _pload(p: *u8, i: i64) -> i64 {
32 let lo: i64 = p[i * 2]
33 let hi: i64 = p[i * 2 + 1]
34 let raw: i64 = lo | (hi << 8)
35 if raw >= 32768 { return raw - 65536 }
36 return raw
37}
38
39func _pstore(p: *u8, i: i64, v: i64) -> i64 {
40 var vv: i64 = v
41 if vv < 0 { vv = vv + 65536 }
42 p[i * 2] = vv & 0xff
43 p[i * 2 + 1] = (vv >> 8) & 0xff
44 return 0
45}
46
47func _mont(a: i64) -> i64 {
48 var u: i64 = (a * KYBER_QINV) & 0xffff
49 if u >= 32768 { u = u - 65536 }
50 return (a - u * KYBER_Q) >> 16
51}
52
53func nx_kyber_poly_tomont(poly: *u8) -> i64 {
54 var i: i64 = 0
55 while i < KYBER_N {
56 let c: i64 = _pload(poly, i)
57 _pstore(poly, i, _mont(c * KYBER_F))
58 i = i + 1
59 }
60 return 0
61}