nx_aes_cbc_enc.nx source
↩ module page · 91 lines · 5059 B
1// nx_aes_cbc_enc.nx -- AES-128-CBC ENCRYPT (NIST SP 800-38A §6.2) + PKCS#7 pad, over the FIPS-197 block
2// primitive in nx_aes (aes128_encrypt_block + aes128_expand_key). The sovereign sibling of nx_aes_cbc
3// (which ships DECRYPT only). Needed by the PKCS#12 (.p12) provisioning rung R5b to shroud the client
4// identity key under PBES2 (PBKDF2 + AES-CBC). CBC encrypt: C_i = Cipher(P_i XOR C_{i-1}), C_{-1} = IV.
5// GATE (main): the canonical NIST SP 800-38A F.2.1 CBC-AES128.Encrypt 4-block vector (authoritative KAT).
6// license_tier: ORIGINAL expect_exit: 0 module: nishi-core.crypto.aes_cbc_enc
7import "nx_aes.nx"
8import "nx_syscalls.nx"
9
10// AES-128-CBC encrypt n bytes (n MUST be a multiple of 16). sched = 176-byte expanded key. iv = 16 bytes.
11// out[0..n) = ciphertext (must NOT alias pt). Returns n, or -1 if n is not a block multiple.
12func aes128_cbc_encrypt(pt: *u8, n: i64, sched: *u8, iv: *u8, out: *u8) -> i64 {
13 if n <= 0 { return 0 }
14 if (n % 16) != 0 { return 0 - 1 }
15 let prev: *u8 = sys_mmap(16); var z: i64 = 0; while z < 16 { prev[z] = iv[z]; z = z + 1 }
16 let blk: *u8 = sys_mmap(16)
17 var off: i64 = 0
18 while off < n {
19 var i: i64 = 0; while i < 16 { blk[i] = (pt[off + i] ^ prev[i]) & 0xff; i = i + 1 }
20 aes128_encrypt_block(blk, sched, (out as i64 + off) as *u8) // C_i = Cipher(P_i XOR C_{i-1})
21 i = 0; while i < 16 { prev[i] = out[off + i]; i = i + 1 } // C_{i-1} <- C_i
22 off = off + 16
23 }
24 return n
25}
26
27// PKCS#7 pad src[0..n) -> out (cap >= n + 16). Always adds 1..16 bytes (a full block when already aligned,
28// per RFC 5652 §6.3) so the result is unambiguously strippable. Returns the padded length.
29func aes128_pkcs7_pad(src: *u8, n: i64, out: *u8) -> i64 {
30 let pad: i64 = 16 - (n % 16)
31 var i: i64 = 0
32 while i < n { out[i] = src[i]; i = i + 1 }
33 var j: i64 = 0
34 while j < pad { out[n + j] = pad as u8; j = j + 1 }
35 return n + pad
36}
37
38// ===================== GATE =====================
39func ace_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
40func ace_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 }
41func ace_hex2bin(hex: *u8, out: *u8, nbytes: i64) -> i64 {
42 var i: i64 = 0
43 while i < nbytes { let hi: i64 = ace_hexv(hex[i*2] as i64); let lo: i64 = ace_hexv(hex[i*2+1] as i64); out[i] = ((hi<<4)|lo) & 0xff; i = i + 1 }
44 return nbytes
45}
46func ace_row(name: *u8, ok: i64) -> i64 { if ok == 1 { ace_puts(" PASS " as *u8) } else { ace_puts(" FAIL " as *u8) } ace_puts(name); ace_puts("\n" as *u8); return ok }
47
48func main() -> i64 {
49 ace_puts("nx_aes_cbc_enc gate (AES-128-CBC encrypt, NIST SP 800-38A F.2.1)\n" as *u8)
50 let keyhex: *u8 = "2b7e151628aed2a6abf7158809cf4f3c" as *u8
51 let ivhex: *u8 = "000102030405060708090a0b0c0d0e0f" as *u8
52 let pthex: *u8 = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" as *u8
53 let cthex: *u8 = "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7" as *u8
54
55 let key: *u8 = sys_mmap(16); ace_hex2bin(keyhex, key, 16)
56 let iv: *u8 = sys_mmap(16); ace_hex2bin(ivhex, iv, 16)
57 let pt: *u8 = sys_mmap(64); ace_hex2bin(pthex, pt, 64)
58 let ct: *u8 = sys_mmap(64); ace_hex2bin(cthex, ct, 64)
59 let sched: *u8 = sys_mmap(176); aes128_expand_key(key, sched)
60 let out: *u8 = sys_mmap(64)
61 let rn: i64 = aes128_cbc_encrypt(pt, 64, sched, iv, out)
62 var pass: i64 = 0
63
64 var t1: i64 = 1
65 if rn != 64 { t1 = 0 }
66 if t1 == 1 { var i: i64 = 0; while i < 64 { if (out[i] & 0xff) != (ct[i] & 0xff) { t1 = 0; i = 64 } else { i = i + 1 } } }
67 pass = pass + ace_row("T1 4-block CBC encrypt == NIST F.2.1 ciphertext" as *u8, t1)
68
69 // T2: PKCS#7 pad of a 30-byte payload -> 32 bytes, last 2 bytes == 0x02
70 let raw: *u8 = sys_mmap(48); var r: i64 = 0; while r < 30 { raw[r] = (0x41 + (r % 26)) as u8; r = r + 1 }
71 let padded: *u8 = sys_mmap(48)
72 let pn: i64 = aes128_pkcs7_pad(raw, 30, padded)
73 var t2: i64 = 0
74 if pn == 32 { if (padded[30] & 0xff) == 2 { if (padded[31] & 0xff) == 2 { t2 = 1 } } }
75 pass = pass + ace_row("T2 PKCS#7 pad 30 -> 32 (pad byte 0x02)" as *u8, t2)
76
77 // T3: pad an already-aligned 16-byte payload -> 32 (a full pad block, RFC 5652 unambiguous)
78 let a16: *u8 = sys_mmap(48); var b: i64 = 0; while b < 16 { a16[b] = 0x5a as u8; b = b + 1 }
79 let a32: *u8 = sys_mmap(48)
80 var t3: i64 = 0
81 if aes128_pkcs7_pad(a16, 16, a32) == 32 { if (a32[31] & 0xff) == 16 { t3 = 1 } }
82 pass = pass + ace_row("T3 PKCS#7 pad of aligned input adds a full block (pad 0x10)" as *u8, t3)
83
84 if pass == 3 {
85 ace_puts("NX-AES-CBC-ENC GATE GREEN 3/3 (CBC encrypt KAT'd; ready for PKCS#12 PBES2)\n" as *u8)
86 sys_exit(0)
87 }
88 ace_puts("NX-AES-CBC-ENC GATE RED\n" as *u8)
89 sys_exit(1)
90 return 1
91}