code wiki / (root) / nx_u4096.nx

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}