code wiki / _hdl_build / nx_u2048_millerrabin.nx

nx_u2048_millerrabin.nx source

↩ module page · 249 lines · 11421 B

1// nx_u2048_millerrabin.nx -- F103e RUNG 5: probabilistic primality over u2048. 2// 3// The gate for RSA KEYGEN, which is the gate for signing, which is the gate for enrolling our own 4// Secure Boot key (debt 1786237435: real EDK2 with MS keys refuses our unsigned .efi; the SAME 5// secboot firmware in SETUP mode runs it, so the blocker is the enrolled key set). 6// Unblocked by nx_rsa2048_mod_exp_big: Miller-Rabin needs a^d mod n with d ~1024 bits, and the 7// estate's incumbent modexp takes the exponent as an i64 -- it structurally could not do this. 8// 9// ★NO RECALLED CONSTANTS. Every fixture's primality is established IN-TEST by independent i64 trial 10// division; the u2048 Miller-Rabin path is then required to agree. Two methods, one answer. An RSA 11// modulus recited from memory would be a fabricated constant, which is exactly what the estate's 12// rules forbid -- so the key gets GENERATED (next rung), never quoted. 13// 14// ★THE ANTI-VACUITY TOOTH IS THE CARMICHAEL FAMILY. 561 = 3*11*17, 1105 = 5*13*17, 1729 = 7*13*19 -- 15// built here AS PRODUCTS, so their compositeness is proven by construction rather than remembered. 16// They satisfy the FERMAT test for every coprime base, so an implementation that computed 17// a^(n-1) mod n instead of doing the square-root descent passes every other test in this file and 18// fails only these. ★★★★★★A WEAKER ALGORITHM WEARING THE RIGHT NAME PASSES EVERY TEST EXCEPT THE 19// ONE THAT WAS DESIGNED TO SEPARATE THEM. 20// 21// ★AND ONE TOOTH PROVES THE BASE LOOP MATTERS: 23*89 = 2047 is a strong pseudoprime to base 2, so 22// MR with bases {2} must report PROBABLY-PRIME (wrong, and expected) while {2,3} reports COMPOSITE. 23// Without this, a single-base implementation would look perfect. 24// 25// Usage: nx_u2048_millerrabin selftest 26// Exit: 0 GREEN | 1 RED. Log -> knowledge/status/nishi_os.log, verdict= LAST. 27// license_tier: ORIGINAL 28import "nx_syscalls.nx" 29import "nx_u2048.nx" 30import "nx_u2048_mul.nx" 31import "nx_rsa2048_mod.nx" 32import "nx_rsa2048_mod_exp.nx" 33import "nx_rsa2048_mod_exp_big.nx" 34const MR_MAGIC_2048: i64 = 2048 35const MR_MAGIC_1105: i64 = 1105 36const MR_MAGIC_1729: i64 = 1729 37const MR_MAGIC_2465: i64 = 2465 38const MR_MAGIC_2821: i64 = 2821 39const MR_MAGIC_2147483647: i64 = 2147483647 40const MR_MAGIC_65521: i64 = 65521 41const MR_MAGIC_65519: i64 = 65519 42 43const MR_SWEEP_HI: i64 = 512 // EXHAUSTIVE over [2, MR_SWEEP_HI]; traps beyond are named 44 45func mr_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 46func mr_fp(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 } 47func mr_fn(fd: i64, v: i64) -> i64 { 48 let bb: *u8 = sys_mmap(28); var m: i64 = v 49 if m < 0 { m = 0 - m } 50 let t: *u8 = sys_mmap(28); var k: i64 = 0 51 if m == 0 { t[0] = 48 as u8; k = 1 } 52 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 53 var i: i64 = 0 54 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 55 sys_write(fd, bb, k); return 0 56} 57 58func mr_set_i64(x: *i64, v: i64) -> i64 { 59 let b: *u8 = sys_mmap(256) 60 var i: i64 = 0 61 while i < 256 { b[i] = 0 as u8; i = i + 1 } 62 var k: i64 = 0 63 while k < 8 { b[255 - k] = ((v >> (k * 8)) & 0xff) as u8; k = k + 1 } 64 u2048_load_be(x, b) 65 return 0 66} 67 68// THE INDEPENDENT ORACLE: plain i64 trial division. Different arithmetic, different code path. 69func mr_i64_is_prime(v: i64) -> i64 { 70 if v < 2 { return 0 } 71 var i: i64 = 2 72 while (i * i) <= v { 73 if (v % i) == 0 { return 0 } 74 i = i + 1 75 } 76 return 1 77} 78 79// Miller-Rabin. Returns 1 = probably prime, 0 = COMPOSITE (definite). 80// bases are small i64 values; nbases of them. Deterministic given the base list, which is what makes 81// the teeth reproducible -- a random-base version wraps this once keygen needs it. 82func u2048_is_probable_prime(n: *i64, bases: *i64, nbases: i64) -> i64 { 83 let two: *i64 = u2048_alloc() 84 mr_set_i64(two, 2) 85 if u2048_cmp(n, two) < 0 { return 0 } // 0 and 1 are not prime 86 if u2048_cmp(n, two) == 0 { return 1 } // 2 is 87 if u2048_get_bit(n, 0) == 0 { return 0 } // any other even number is composite 88 89 let one: *i64 = u2048_alloc() 90 u2048_one(one) 91 let n1: *i64 = u2048_alloc() 92 u2048_sub_with_borrow(n1, n, one) // n1 = n - 1 93 94 // n-1 = d * 2^r -- r = count of trailing zero bits, found by INDEX not by clobbering a cursor 95 var r: i64 = 0 96 var scanning: i64 = 1 97 while scanning == 1 { 98 if u2048_get_bit(n1, r) == 1 { scanning = 0 } else { r = r + 1 } 99 if r >= MR_MAGIC_2048 { scanning = 0 } 100 } 101 let d: *i64 = u2048_alloc() 102 u2048_copy(d, n1) 103 var s: i64 = 0 104 while s < r { u2048_shr1(d); s = s + 1 } 105 106 let a: *i64 = u2048_alloc() 107 let x: *i64 = u2048_alloc() 108 let tmp: *i64 = u2048_alloc() 109 var bi: i64 = 0 110 while bi < nbases { 111 mr_set_i64(a, bases[bi]) 112 // require 2 <= a <= n-2; if the base is too big for this n, it carries no information 113 if u2048_cmp(a, n1) < 0 { 114 if rsa2048_mod_exp_big(x, a, d, n) != 1 { return 0 } 115 var witness: i64 = 1 // assume this base witnesses compositeness 116 if u2048_eq(x, one) == 1 { witness = 0 } 117 if u2048_eq(x, n1) == 1 { witness = 0 } 118 var k: i64 = 1 119 while k < r { 120 if witness == 1 { 121 rsa2048_mul_mod(tmp, x, x, n) 122 u2048_copy(x, tmp) 123 if u2048_eq(x, n1) == 1 { witness = 0 } 124 } 125 k = k + 1 126 } 127 if witness == 1 { return 0 } // definitely composite 128 } 129 bi = bi + 1 130 } 131 return 1 132} 133 134// ================================================================================================= 135func mr_selftest() -> i64 { 136 var pass: i64 = 0 137 var teeth: i64 = 0 138 let n: *i64 = u2048_alloc() 139 let bases: *i64 = sys_mmap(64) as *i64 140 bases[0] = 2; bases[1] = 3; bases[2] = 5; bases[3] = 7; bases[4] = 11 141 142 // T1 EXHAUSTIVE over [2, MR_SWEEP_HI] against independent i64 trial division. Not a sample -- 143 // the interval is stated and every value in it is checked. 144 teeth = teeth + 1 145 var mismatches: i64 = 0 146 var first_bad: i64 = 0 - 1 147 var v: i64 = 2 148 while v <= MR_SWEEP_HI { 149 mr_set_i64(n, v) 150 let got: i64 = u2048_is_probable_prime(n, bases, 5) 151 let want: i64 = mr_i64_is_prime(v) 152 if got != want { 153 mismatches = mismatches + 1 154 if first_bad < 0 { first_bad = v } 155 } 156 v = v + 1 157 } 158 if mismatches == 0 { pass = pass + 1 159 mr_p("MR-T1 exhaustive [2," as *u8); mr_fn(1, MR_SWEEP_HI) 160 mr_p("] agrees with i64 trial division GREEN\n" as *u8) } 161 else { mr_p("MR-T1 RED mismatches=" as *u8); mr_fn(1, mismatches) 162 mr_p(" first=" as *u8); mr_fn(1, first_bad); mr_p("\n" as *u8) } 163 164 // T2 ANTI-VACUITY: Carmichael numbers, CONSTRUCTED as products so compositeness is proven here. 165 // A Fermat test passes all of these; only the square-root descent catches them. 166 teeth = teeth + 1 167 let cf: *i64 = sys_mmap(128) as *i64 168 cf[0] = 3; cf[1] = 11; cf[2] = 17 // 561 169 cf[3] = 5; cf[4] = 13; cf[5] = 17 // MR_MAGIC_1105 170 cf[6] = 7; cf[7] = 13; cf[8] = 19 // MR_MAGIC_1729 171 cf[9] = 5; cf[10] = 17; cf[11] = 29 // MR_MAGIC_2465 172 cf[12] = 7; cf[13] = 13; cf[14] = 31 // MR_MAGIC_2821 173 var ci: i64 = 0 174 var carm_ok: i64 = 1 175 var carm_checked: i64 = 0 176 while ci < 5 { 177 let prod: i64 = cf[ci * 3] * cf[ci * 3 + 1] * cf[ci * 3 + 2] 178 if mr_i64_is_prime(prod) != 0 { carm_ok = 0 } // fixture sanity: a product IS composite 179 mr_set_i64(n, prod) 180 if u2048_is_probable_prime(n, bases, 5) != 0 { carm_ok = 0 } 181 carm_checked = carm_checked + 1 182 ci = ci + 1 183 } 184 if carm_ok == 1 { if carm_checked == 5 { pass = pass + 1 185 mr_p("MR-T2 carmichael-5of5-rejected (a Fermat test would pass all) GREEN\n" as *u8) } 186 else { mr_p("MR-T2 RED [VACUOUS: checked " as *u8); mr_fn(1, carm_checked); mr_p("]\n" as *u8) } } 187 else { mr_p("MR-T2 RED\n" as *u8) } 188 189 // T3 THE BASE LOOP MUST MATTER: 23*89 = 2047 is a strong pseudoprime to base 2. 190 // {2} alone must say PROBABLY-PRIME (wrong, and that is the point); {2,3} must say COMPOSITE. 191 teeth = teeth + 1 192 let sp: i64 = 23 * 89 193 mr_set_i64(n, sp) 194 let b1: *i64 = sys_mmap(32) as *i64 195 b1[0] = 2; b1[1] = 3 196 let one_base: i64 = u2048_is_probable_prime(n, b1, 1) 197 let two_base: i64 = u2048_is_probable_prime(n, b1, 2) 198 if mr_i64_is_prime(sp) == 0 { // fixture is genuinely composite 199 if one_base == 1 { if two_base == 0 { pass = pass + 1 200 mr_p("MR-T3 base-2-fooled-by-2047, base-3-catches-it GREEN\n" as *u8) } 201 else { mr_p("MR-T3 RED two-base did not catch it\n" as *u8) } } 202 else { mr_p("MR-T3 RED [single-base did NOT report probable-prime, so this fixture proves nothing about the base loop]\n" as *u8) } 203 } else { mr_p("MR-T3 RED [VACUOUS: fixture not composite]\n" as *u8) } 204 205 // T4 a large prime the sweep cannot reach: 2^31-1, verified in-test by i64 trial division. 206 teeth = teeth + 1 207 let big: i64 = MR_MAGIC_2147483647 208 if mr_i64_is_prime(big) == 1 { 209 mr_set_i64(n, big) 210 if u2048_is_probable_prime(n, bases, 5) == 1 { pass = pass + 1 211 mr_p("MR-T4 large-prime-2^31-1 GREEN\n" as *u8) } 212 else { mr_p("MR-T4 RED said composite\n" as *u8) } 213 } else { mr_p("MR-T4 RED [fixture failed its own trial division]\n" as *u8) } 214 215 // T5 a large COMPOSITE of two primes -- the RSA shape. Built as a product. 216 teeth = teeth + 1 217 let semi: i64 = MR_MAGIC_65521 * MR_MAGIC_65519 218 mr_set_i64(n, semi) 219 if u2048_is_probable_prime(n, bases, 5) == 0 { pass = pass + 1 220 mr_p("MR-T5 semiprime-rejected GREEN\n" as *u8) } 221 else { mr_p("MR-T5 RED\n" as *u8) } 222 223 // T6 boundaries: 0, 1 not prime; 2 prime; an even number composite. 224 teeth = teeth + 1 225 var bok: i64 = 1 226 mr_set_i64(n, 0); if u2048_is_probable_prime(n, bases, 5) != 0 { bok = 0 } 227 mr_set_i64(n, 1); if u2048_is_probable_prime(n, bases, 5) != 0 { bok = 0 } 228 mr_set_i64(n, 2); if u2048_is_probable_prime(n, bases, 5) != 1 { bok = 0 } 229 mr_set_i64(n, 4); if u2048_is_probable_prime(n, bases, 5) != 0 { bok = 0 } 230 if bok == 1 { pass = pass + 1; mr_p("MR-T6 boundaries-0-1-2-4 GREEN\n" as *u8) } 231 else { mr_p("MR-T6 RED\n" as *u8) } 232 233 mr_p("MR-SELFTEST " as *u8); mr_fn(1, pass); mr_p("/" as *u8); mr_fn(1, teeth); mr_p("\n" as *u8) 234 let lf: i64 = sys_openat_append("knowledge/status/nishi_os.log" as *u8, 0x1a4) 235 if lf >= 0 { 236 mr_fp(lf, "MILLERRABIN selftest teeth=" as *u8); mr_fn(lf, pass) 237 mr_fp(lf, "of" as *u8); mr_fn(lf, teeth) 238 mr_fp(lf, " sweep=exhaustive-2-to-" as *u8); mr_fn(lf, MR_SWEEP_HI) 239 mr_fp(lf, " oracle=i64-trial-division verdict=" as *u8) 240 if pass == teeth { mr_fp(lf, "GREEN\n" as *u8) } else { mr_fp(lf, "RED\n" as *u8) } 241 sys_close(lf) 242 } 243 if pass == teeth { sys_exit(0); return 0 } 244 sys_exit(1); return 1 245} 246 247func main(argc: i64, argv: *i64) -> i64 { 248 return mr_selftest() 249}