code wiki / (root) / nx_pkcs12_kdf.nx

nx_pkcs12_kdf.nx source

↩ module page · 143 lines · 6936 B

1// nx_pkcs12_kdf.nx -- the PKCS#12 password-based KDF (RFC 7292 Appendix B.2) over SHA-256. 2// 3// The KDF PKCS#12 uses for BOTH its PBE encryption keys/IVs (id=1 key, id=2 IV) AND its integrity MAC key 4// (id=3). Needed by the .p12 provisioning rung R5b so a browser/OS (Windows cert store, Chrome/Edge/Firefox) 5// accepts the imported client identity -- the MAC + key derivation MUST be byte-exact or the import is 6// rejected. SHA-256 variant: u=32 (hash out), v=64 (hash block). Password is a BMPString (UTF-16BE + a 7// two-byte NUL terminator) per RFC 7292 App B.1. Composes sha256 (one-shot digest). 8// 9// GATE (main): KAT vs reference values computed independently (RFC 7292 B.2 reference impl); the FINAL .p12 10// is then validated end-to-end by `openssl pkcs12` (the authoritative oracle, dev-time only -- not shipped). 11// license_tier: ORIGINAL expect_exit: 0 module: nishi-core.crypto.pkcs12_kdf 12import "nx_syscalls.nx" 13import "sha256.nx" 14const P12_MAGIC_2048: i64 = 2048 15 16const P12_U: i64 = 32 // SHA-256 output bytes 17const P12_V: i64 = 64 // SHA-256 block bytes 18 19// dst[0..64) = (dst + b + 1) mod 2^512, big-endian, in place (the RFC 7292 B.2 I_j update step). 20func p12_bnadd_v(dst: *u8, b: *u8) -> i64 { 21 var carry: i64 = 1 // the "+1" 22 var i: i64 = P12_V - 1 23 while i >= 0 { 24 let s: i64 = (dst[i] as i64) + (b[i] as i64) + carry 25 dst[i] = (s & 0xff) as u8 26 carry = (s >> 8) & 0xff // s <= 511 -> carry is 0 or 1 27 i = i - 1 28 } 29 return 0 // carry-out discarded = mod 2^512 30} 31 32// out[0..L) = data repeated to fill L = ceil(dlen/v)*v bytes. Returns L (0 if dlen==0). 33func p12_fill(data: *u8, dlen: i64, v: i64, out: *u8) -> i64 { 34 if dlen <= 0 { return 0 } 35 let blocks: i64 = (dlen + v - 1) / v 36 let L: i64 = blocks * v 37 var i: i64 = 0 38 while i < L { out[i] = data[i % dlen]; i = i + 1 } 39 return L 40} 41 42// PKCS#12 KDF (RFC 7292 B.2). pw_bmp/pw_n = the BMPString password (UTF-16BE + 2-byte NUL). idb = 1(key)/ 43// 2(iv)/3(mac). Writes n bytes to out. Returns n. 44func nx_pkcs12_kdf(pw_bmp: *u8, pw_n: i64, salt: *u8, salt_n: i64, iter: i64, idb: i64, n: i64, out: *u8) -> i64 { 45 let d: *u8 = sys_mmap(P12_V) 46 var di: i64 = 0 47 while di < P12_V { d[di] = idb as u8; di = di + 1 } 48 let sbuf: *u8 = sys_mmap(P12_V * 4); let slen: i64 = p12_fill(salt, salt_n, P12_V, sbuf) 49 let pbuf: *u8 = sys_mmap(P12_V * 8); let plen: i64 = p12_fill(pw_bmp, pw_n, P12_V, pbuf) 50 let ilen: i64 = slen + plen 51 let ibuf: *u8 = sys_mmap(ilen + P12_V) 52 var k: i64 = 0 53 while k < slen { ibuf[k] = sbuf[k]; k = k + 1 } 54 var j: i64 = 0 55 while j < plen { ibuf[slen + j] = pbuf[j]; j = j + 1 } 56 57 let a: *u8 = sys_mmap(P12_U) 58 let a2: *u8 = sys_mmap(P12_U) 59 let dibuf: *u8 = sys_mmap(P12_V + ilen) 60 let b: *u8 = sys_mmap(P12_V) 61 var produced: i64 = 0 62 while produced < n { 63 // A = SHA256^iter(D || I) 64 var z: i64 = 0 65 while z < P12_V { dibuf[z] = d[z]; z = z + 1 } 66 z = 0 67 while z < ilen { dibuf[P12_V + z] = ibuf[z]; z = z + 1 } 68 sha256_digest(dibuf, P12_V + ilen, a) 69 var r: i64 = 1 70 while r < iter { 71 sha256_digest(a, P12_U, a2) 72 var c: i64 = 0 73 while c < P12_U { a[c] = a2[c]; c = c + 1 } 74 r = r + 1 75 } 76 // B = A repeated to v bytes 77 var bk: i64 = 0 78 while bk < P12_V { b[bk] = a[bk % P12_U]; bk = bk + 1 } 79 // I_j = (I_j + B + 1) mod 2^(8v) for each v-byte block 80 var blk: i64 = 0 81 while blk < ilen { p12_bnadd_v((ibuf as i64 + blk) as *u8, b); blk = blk + P12_V } 82 // append A (u bytes) 83 var t: i64 = 0 84 while t < P12_U { if produced + t < n { out[produced + t] = a[t] } t = t + 1 } 85 produced = produced + P12_U 86 } 87 return n 88} 89 90// Encode an ASCII password as a BMPString (UTF-16BE) + 2-byte NUL terminator. Returns byte length = 2*(n+1). 91func nx_pkcs12_bmp_password(ascii: *u8, n: i64, out: *u8) -> i64 { 92 var i: i64 = 0 93 while i < n { out[i*2] = 0 as u8; out[i*2+1] = ascii[i]; i = i + 1 } 94 out[n*2] = 0 as u8; out[n*2+1] = 0 as u8 95 return (n + 1) * 2 96} 97 98// ===================== GATE ===================== 99func kd_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 100func kd_hexv(c: i64) -> i64 { if c>=48 { if c<=57 { return c-48 } } if c>=97 { if c<=102 { return c-97+10 } } return 0 } 101func kd_hex2bin(hex: *u8, out: *u8, nb: i64) -> i64 { var i: i64 = 0; while i < nb { out[i] = ((kd_hexv(hex[i*2] as i64)<<4)|kd_hexv(hex[i*2+1] as i64)) & 0xff; i = i + 1 } return nb } 102func kd_eq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { if (a[i] & 0xff) != (b[i] & 0xff) { return 0 } i = i + 1 } return 1 } 103func kd_row(name: *u8, ok: i64) -> i64 { if ok == 1 { kd_puts(" PASS " as *u8) } else { kd_puts(" FAIL " as *u8) } kd_puts(name); kd_puts("\n" as *u8); return ok } 104 105func main() -> i64 { 106 kd_puts("nx_pkcs12_kdf gate (RFC 7292 B.2; SHA-256; KAT vs reference: pw=nishi salt=0102..08 iter=2048)\n" as *u8) 107 let bmp: *u8 = sys_mmap(32) 108 let bmp_n: i64 = nx_pkcs12_bmp_password("nishi" as *u8, 5, bmp) 109 let salt: *u8 = sys_mmap(8) 110 var s: i64 = 0 111 while s < 8 { salt[s] = (s + 1) as u8; s = s + 1 } 112 113 var pass: i64 = 0 114 115 // T0: BMPString encoding == 006e00690073006800690000 116 let bmp_exp: *u8 = sys_mmap(16); kd_hex2bin("006e00690073006800690000" as *u8, bmp_exp, 12) 117 var t0: i64 = 0 118 if bmp_n == 12 { if kd_eq(bmp, bmp_exp, 12) == 1 { t0 = 1 } } 119 pass = pass + kd_row("T0 BMPString(\"nishi\") == 006e00690073006800690000" as *u8, t0) 120 121 // T1: id=1 (key, 16 bytes) 122 let o1: *u8 = sys_mmap(16); nx_pkcs12_kdf(bmp, bmp_n, salt, 8, P12_MAGIC_2048, 1, 16, o1) 123 let e1: *u8 = sys_mmap(16); kd_hex2bin("fb74da57cf9d2de7210b62bbad28d0f5" as *u8, e1, 16) 124 pass = pass + kd_row("T1 id=1 key == fb74da57cf9d2de7210b62bbad28d0f5" as *u8, kd_eq(o1, e1, 16)) 125 126 // T2: id=2 (IV, 16 bytes) 127 let o2: *u8 = sys_mmap(16); nx_pkcs12_kdf(bmp, bmp_n, salt, 8, P12_MAGIC_2048, 2, 16, o2) 128 let e2: *u8 = sys_mmap(16); kd_hex2bin("de305d4a6d6cda195bd6a9aab84e290d" as *u8, e2, 16) 129 pass = pass + kd_row("T2 id=2 iv == de305d4a6d6cda195bd6a9aab84e290d" as *u8, kd_eq(o2, e2, 16)) 130 131 // T3: id=3 (MAC, 32 bytes) 132 let o3: *u8 = sys_mmap(32); nx_pkcs12_kdf(bmp, bmp_n, salt, 8, P12_MAGIC_2048, 3, 32, o3) 133 let e3: *u8 = sys_mmap(32); kd_hex2bin("44ea89384afc486e1f27b8adb2c15c9a1fff70f77931f57bfbd7bafe06440781" as *u8, e3, 32) 134 pass = pass + kd_row("T3 id=3 mac == 44ea89384afc486e...06440781" as *u8, kd_eq(o3, e3, 32)) 135 136 if pass == 4 { 137 kd_puts("NX-PKCS12-KDF GATE GREEN 4/4 (RFC 7292 B.2 KAT'd; ready for the .p12 MAC + PBES2)\n" as *u8) 138 sys_exit(0) 139 } 140 kd_puts("NX-PKCS12-KDF GATE RED\n" as *u8) 141 sys_exit(1) 142 return 1 143}