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}