code wiki / (root) / nx_aes_cbc.nx

nx_aes_cbc.nx source

↩ module page · 98 lines · 6195 B

1// nx_aes_cbc.nx -- AES-128-CBC DECRYPT (NIST SP 800-38A ยง6.2) over the FIPS-197 block primitive in 2// nx_aes (aes128_decrypt_block + aes128_expand_key). The mode HLS uses for #EXT-X-KEY:METHOD=AES-128: 3// each .ts segment is one CBC stream with PKCS7 padding. CBC decrypt: P_i = InvCipher(C_i) XOR C_{i-1} 4// (C_{-1} = IV). In-place safe (the cipher block is saved before the plaintext overwrites it). 5// GATE (main): the canonical NIST SP 800-38A F.2.2 CBC-AES128 4-block vector (authoritative KAT) + 6// a PKCS7 strip check. Crypto is KAT-proven here BEFORE nx_hls_get composes it. license_tier: ORIGINAL 7// module: nishi-core.crypto.aes_cbc 8import "nx_aes.nx" 9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 10import "nx_syscalls.nx" 11 12// AES-128-CBC decrypt n bytes (n must be a multiple of 16). `sched` = 176-byte expanded key. `iv` = 16 13// bytes. out[0..n) = plaintext (may alias ct). Returns n, or -1 if n is not a multiple of the block size. 14func aes128_cbc_decrypt(ct: *u8, n: i64, sched: *u8, iv: *u8, out: *u8) -> i64 { 15 if n <= 0 { return 0 } 16 if (n % 16) != 0 { return 0 - 1 } 17 let prev: *u8 = sys_mmap(16); var z: i64 = 0; while z < 16 { prev[z] = iv[z]; z = z + 1 } 18 let cur: *u8 = sys_mmap(16); let dec: *u8 = sys_mmap(16) 19 var off: i64 = 0 20 while off < n { 21 var i: i64 = 0; while i < 16 { cur[i] = ct[off + i]; i = i + 1 } // save this cipher block (prev for next) 22 aes128_decrypt_block((ct as i64 + off) as *u8, sched, dec) // dec = InvCipher(C_i) 23 i = 0; while i < 16 { out[off + i] = (dec[i] ^ prev[i]) & 0xff; i = i + 1 } // P_i = dec XOR C_{i-1} 24 i = 0; while i < 16 { prev[i] = cur[i]; i = i + 1 } // C_{i-1} <- C_i 25 off = off + 16 26 } 27 return n 28} 29 30// strip PKCS7 padding (RFC 5652): the last byte is the pad count (1..16). Lenient -- if the trailer is 31// not a valid pad, return n unchanged (never corrupt the payload). Returns the unpadded length. 32func aes128_pkcs7_strip(buf: *u8, n: i64) -> i64 { 33 if n <= 0 { return n } 34 let pad: i64 = buf[n - 1] as i64 35 if pad < 1 { return n } 36 if pad > 16 { return n } 37 if pad > n { return n } 38 return n - pad 39} 40 41// ===================== GATE ===================== 42func cbc_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 43// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 44// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 45// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 46// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 47func cbc_putn(v: i64) -> i64 { nxi_out(v); return 0 } 48func cbc_hexv(c: i64) -> i64 { if c>=48 { if c<=57 { return c-48 } } if c>=97 { if c<=102 { return c-97+10 } } if c>=65 { if c<=70 { return c-65+10 } } return 0 } 49// parse `nbytes` from a hex string (2 chars/byte) into out. 50func cbc_hex2bin(hex: *u8, out: *u8, nbytes: i64) -> i64 { 51 var i: i64 = 0 52 while i < nbytes { let hi: i64 = cbc_hexv(hex[i*2] as i64); let lo: i64 = cbc_hexv(hex[i*2+1] as i64); out[i] = ((hi<<4)|lo) & 0xff; i = i + 1 } 53 return nbytes 54} 55 56func main() -> i64 { 57 var pass: i64 = 0; var tot: i64 = 0 58 cbc_puts("nx_aes_cbc gate (AES-128-CBC decrypt, NIST SP 800-38A F.2.2)\n" as *u8) 59 60 // ---- KAT1: NIST SP 800-38A F.2.2 CBC-AES128.Decrypt, 4 blocks (64 bytes), no padding ---- 61 let keyhex: *u8 = "2b7e151628aed2a6abf7158809cf4f3c\x00" as *u8 62 let ivhex: *u8 = "000102030405060708090a0b0c0d0e0f\x00" as *u8 63 let cthex: *u8 = "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7\x00" as *u8 64 let pthex: *u8 = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710\x00" as *u8 65 66 let key: *u8 = sys_mmap(16); cbc_hex2bin(keyhex, key, 16) 67 let iv: *u8 = sys_mmap(16); cbc_hex2bin(ivhex, iv, 16) 68 let ct: *u8 = sys_mmap(64); cbc_hex2bin(cthex, ct, 64) 69 let pt: *u8 = sys_mmap(64); cbc_hex2bin(pthex, pt, 64) 70 let sched: *u8 = sys_mmap(176); aes128_expand_key(key, sched) 71 let out: *u8 = sys_mmap(64) 72 let rn: i64 = aes128_cbc_decrypt(ct, 64, sched, iv, out) 73 var r1: i64 = 1 74 if rn != 64 { r1 = 0 } 75 if r1 == 1 { var i: i64 = 0; while i < 64 { if (out[i] & 0xff) != (pt[i] & 0xff) { r1 = 0; i = 64 } else { i = i + 1 } } } 76 if r1 == 1 { cbc_puts(" PASS KAT1 4-block CBC decrypt == NIST plaintext\n" as *u8); pass = pass + 1 } else { cbc_puts(" FAIL KAT1 (rn=" as *u8); cbc_putn(rn); cbc_puts(")\n" as *u8) } 77 tot = tot + 1 78 79 // ---- KAT2: in-place decrypt (out aliases ct) yields the same plaintext ---- 80 let ct2: *u8 = sys_mmap(64); cbc_hex2bin(cthex, ct2, 64) 81 aes128_cbc_decrypt(ct2, 64, sched, iv, ct2) 82 var r2: i64 = 1; var j: i64 = 0; while j < 64 { if (ct2[j] & 0xff) != (pt[j] & 0xff) { r2 = 0; j = 64 } else { j = j + 1 } } 83 if r2 == 1 { cbc_puts(" PASS KAT2 in-place decrypt matches\n" as *u8); pass = pass + 1 } else { cbc_puts(" FAIL KAT2 in-place\n" as *u8) } 84 tot = tot + 1 85 86 // ---- KAT3: PKCS7 strip (16 bytes, pad=4 -> 12) + reject-bad (pad=0 -> unchanged) ---- 87 let pb: *u8 = sys_mmap(16); var z: i64 = 0; while z < 12 { pb[z] = 65 as u8; z = z + 1 } pb[12]=4 as u8; pb[13]=4 as u8; pb[14]=4 as u8; pb[15]=4 as u8 88 var r3: i64 = 1 89 if aes128_pkcs7_strip(pb, 16) != 12 { r3 = 0 } 90 let pb2: *u8 = sys_mmap(16); var z2: i64 = 0; while z2 < 16 { pb2[z2] = 65 as u8; z2 = z2 + 1 } pb2[15]=0 as u8 91 if aes128_pkcs7_strip(pb2, 16) != 16 { r3 = 0 } // pad byte 0 is invalid -> unchanged 92 if r3 == 1 { cbc_puts(" PASS KAT3 PKCS7 strip (valid->12, invalid->unchanged)\n" as *u8); pass = pass + 1 } else { cbc_puts(" FAIL KAT3 pkcs7\n" as *u8) } 93 tot = tot + 1 94 95 cbc_puts("---- nx_aes_cbc gate: passed " as *u8); cbc_putn(pass); cbc_puts(" / " as *u8); cbc_putn(tot) 96 if pass == tot { cbc_puts(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 97 cbc_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 98}