nx_u4096.nx source
↩ module page · 210 lines · 5872 B
1// nx_u4096.nx -- 4096-bit unsigned big-int (128 × 32-bit limbs).
2//
3// Mirrors nx_u2048 / nx_u384 with limb count 128. Needed for RSA-4096
4// signature verification against modern Web PKI roots: ISRG Root X1
5// (Let's Encrypt's root) is RSA-4096; without this primitive the LE
6// cert chain cannot validate, blocking L11 step 2d -> 2e of the
7// nishifamily ACME arc.
8//
9// Layout: 128 i64 slots, low 32 bits used per limb. Limb 0 LSB.
10// Byte serialization: 512-byte BE (DER) layout.
11//
12// Function names use the `u4096_` prefix to avoid the silent-
13// shadowing bug class fixed 2026-05-20 in nx_sha256/nx_sha512.
14//
15// license_tier: INDEPENDENT_REDERIVE
16// genealogy_id: international-research-sources/ietf/rfc_8017 + nist/fips_186_5
17// lineage_id: nishi_u4096_q1
18
19// nx_safety_envelope:
20// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
21// sil_target: SIL1
22// evidence: [u4096-bigint-foundation-for-rsa-4096-le-root]
23// verdict: NOT_YET_EVALUATED
24
25import "nx_syscalls.nx"
26
27const NX_U4096_LIMBS: i64 = 128
28const NX_U4096_BYTES: i64 = 512
29const NX_U4096_LIMB_BITS: i64 = 32
30const NX_U4096_LIMB_MASK: i64 = 0xFFFFFFFF
31
32func u4096_alloc() -> *i64 {
33 let b: *u8 = sys_mmap(NX_U4096_LIMBS * 8)
34 let p: *i64 = b as *i64
35 var i: i64 = 0
36 while i < NX_U4096_LIMBS {
37 p[i] = 0
38 i = i + 1
39 }
40 return p
41}
42
43// Free a buffer obtained from u4096_alloc. Each alloc is its own
44// sys_mmap (page-rounded); the matching munmap reclaims it so callers
45// that allocate scratch per modexp/mont-mul (RSA-4096 verify) do not
46// leak ~1KB per call -- the leak that made a forked apm probe's
47// cert-chain validation slow and a sequential research_fetch OOM ~#23.
48func u4096_free(p: *i64) -> i64 {
49 if (p as i64) == 0 { return 0 }
50 sys_munmap(p as *u8, NX_U4096_LIMBS * 8)
51 return 0
52}
53
54func u4096_zero(out: *i64) -> i64 {
55 var i: i64 = 0
56 while i < NX_U4096_LIMBS {
57 out[i] = 0
58 i = i + 1
59 }
60 return 0
61}
62
63func u4096_one(out: *i64) -> i64 {
64 u4096_zero(out)
65 out[0] = 1
66 return 0
67}
68
69func u4096_copy(out: *i64, src: *i64) -> i64 {
70 var i: i64 = 0
71 while i < NX_U4096_LIMBS {
72 out[i] = src[i]
73 i = i + 1
74 }
75 return 0
76}
77
78// Load 512 BE bytes into LE 128-limb layout.
79func u4096_load_be(out: *i64, bytes: *u8) -> i64 {
80 var i: i64 = 0
81 while i < NX_U4096_LIMBS {
82 let off: i64 = i * 4
83 let limb_idx: i64 = NX_U4096_LIMBS - 1 - i
84 let b0: i64 = bytes[off] & 0xff
85 let b1: i64 = bytes[off + 1] & 0xff
86 let b2: i64 = bytes[off + 2] & 0xff
87 let b3: i64 = bytes[off + 3] & 0xff
88 out[limb_idx] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
89 i = i + 1
90 }
91 return 0
92}
93
94func u4096_store_be(bytes: *u8, src: *i64) -> i64 {
95 var i: i64 = 0
96 while i < NX_U4096_LIMBS {
97 let off: i64 = i * 4
98 let limb_idx: i64 = NX_U4096_LIMBS - 1 - i
99 let limb: i64 = src[limb_idx]
100 bytes[off] = ((limb >> 24) & 0xff) as u8
101 bytes[off + 1] = ((limb >> 16) & 0xff) as u8
102 bytes[off + 2] = ((limb >> 8) & 0xff) as u8
103 bytes[off + 3] = (limb & 0xff) as u8
104 i = i + 1
105 }
106 return 0
107}
108
109func u4096_add_with_carry(out: *i64, a: *i64, b: *i64) -> i64 {
110 var i: i64 = 0
111 var carry: i64 = 0
112 while i < NX_U4096_LIMBS {
113 let s: i64 = (a[i] & NX_U4096_LIMB_MASK) + (b[i] & NX_U4096_LIMB_MASK) + carry
114 out[i] = s & NX_U4096_LIMB_MASK
115 carry = (s >> NX_U4096_LIMB_BITS) & 1
116 i = i + 1
117 }
118 return carry
119}
120
121func u4096_sub_with_borrow(out: *i64, a: *i64, b: *i64) -> i64 {
122 var i: i64 = 0
123 var borrow: i64 = 0
124 while i < NX_U4096_LIMBS {
125 let d: i64 = (a[i] & NX_U4096_LIMB_MASK) - (b[i] & NX_U4096_LIMB_MASK) - borrow
126 if d < 0 {
127 out[i] = (d + (1 << NX_U4096_LIMB_BITS)) & NX_U4096_LIMB_MASK
128 borrow = 1
129 } else {
130 out[i] = d & NX_U4096_LIMB_MASK
131 borrow = 0
132 }
133 i = i + 1
134 }
135 return borrow
136}
137
138func u4096_cmp(a: *i64, b: *i64) -> i64 {
139 var i: i64 = NX_U4096_LIMBS - 1
140 while i >= 0 {
141 let av: i64 = a[i] & NX_U4096_LIMB_MASK
142 let bv: i64 = b[i] & NX_U4096_LIMB_MASK
143 if av < bv { return 0 - 1 }
144 if av > bv { return 1 }
145 i = i - 1
146 }
147 return 0
148}
149
150func u4096_is_zero(a: *i64) -> i64 {
151 var i: i64 = 0
152 var acc: i64 = 0
153 while i < NX_U4096_LIMBS {
154 acc = acc | (a[i] & NX_U4096_LIMB_MASK)
155 i = i + 1
156 }
157 if acc == 0 { return 1 }
158 return 0
159}
160
161func u4096_eq(a: *i64, b: *i64) -> i64 {
162 var i: i64 = 0
163 var diff: i64 = 0
164 while i < NX_U4096_LIMBS {
165 diff = diff | ((a[i] ^ b[i]) & NX_U4096_LIMB_MASK)
166 i = i + 1
167 }
168 if diff == 0 { return 1 }
169 return 0
170}
171
172// Left-shift by one bit (in place; carries propagate up).
173// Returns carry-out (the bit that fell off the top).
174func u4096_shl1(out: *i64) -> i64 {
175 var i: i64 = 0
176 var carry: i64 = 0
177 while i < NX_U4096_LIMBS {
178 let v: i64 = out[i] & NX_U4096_LIMB_MASK
179 let new_carry: i64 = (v >> 31) & 1
180 out[i] = ((v << 1) | carry) & NX_U4096_LIMB_MASK
181 carry = new_carry
182 i = i + 1
183 }
184 return carry
185}
186
187// Right-shift by one bit (in place).
188func u4096_shr1(out: *i64) -> i64 {
189 var i: i64 = NX_U4096_LIMBS - 1
190 var carry: i64 = 0
191 while i >= 0 {
192 let v: i64 = out[i] & NX_U4096_LIMB_MASK
193 let new_carry: i64 = v & 1
194 out[i] = ((v >> 1) | (carry << 31)) & NX_U4096_LIMB_MASK
195 carry = new_carry
196 i = i - 1
197 }
198 return 0
199}
200
201// Get bit i (0..4095) where bit 0 is LSB.
202func u4096_get_bit(a: *i64, bit: i64) -> i64 {
203 let limb: i64 = bit / 32
204 let pos: i64 = bit % 32
205 return (a[limb] >> pos) & 1
206}
207
208func main() -> i64 {
209 return 0
210}