nx_aes128_gcm_kat_test.nx source
↩ module page · 137 lines · 5278 B
1// nx_aes128_gcm_kat_test.nx -- NIST SP 800-38D Test Case 2 +
2// round-trip (seal then open) on a synthetic AAD.
3//
4// Test Case 2:
5// K = 00000000000000000000000000000000
6// IV = 000000000000000000000000
7// AAD = (empty)
8// P = 00000000000000000000000000000000 (16-byte all-zero pt)
9// -> C = 0388dace60b6a392f328c2b971b2fe78
10// T = ab6e47d42cec13bdf53a67b21257bddf
11//
12// expect_exit: 0.
13// license_tier: ORIGINAL
14
15import "nx_syscalls.nx"
16import "nx_aes128_gcm.nx"
17
18func main() -> i64 {
19 let key: *u8 = sys_mmap(16)
20 let iv: *u8 = sys_mmap(12)
21 let pt: *u8 = sys_mmap(16)
22 var i: i64 = 0
23 while i < 16 { key[i] = 0; pt[i] = 0; i = i + 1 }
24 i = 0
25 while i < 12 { iv[i] = 0; i = i + 1 }
26 let aad: *u8 = sys_mmap(8) // empty (len=0)
27
28 let ct: *u8 = sys_mmap(16)
29 let tag: *u8 = sys_mmap(16)
30 nx_aes128_gcm_seal(key, iv, aad, 0, pt, 16, ct, tag)
31
32 // Expected C = 0388dace60b6a392f328c2b971b2fe78
33 let exp_ct: *u8 = sys_mmap(16)
34 exp_ct[0]=0x03; exp_ct[1]=0x88; exp_ct[2]=0xda; exp_ct[3]=0xce
35 exp_ct[4]=0x60; exp_ct[5]=0xb6; exp_ct[6]=0xa3; exp_ct[7]=0x92
36 exp_ct[8]=0xf3; exp_ct[9]=0x28; exp_ct[10]=0xc2; exp_ct[11]=0xb9
37 exp_ct[12]=0x71; exp_ct[13]=0xb2; exp_ct[14]=0xfe; exp_ct[15]=0x78
38 i = 0
39 while i < 16 {
40 if (ct[i] & 0xff) != (exp_ct[i] & 0xff) { return 10 + i }
41 i = i + 1
42 }
43
44 // Expected T = ab6e47d42cec13bdf53a67b21257bddf
45 let exp_tag: *u8 = sys_mmap(16)
46 exp_tag[0]=0xab; exp_tag[1]=0x6e; exp_tag[2]=0x47; exp_tag[3]=0xd4
47 exp_tag[4]=0x2c; exp_tag[5]=0xec; exp_tag[6]=0x13; exp_tag[7]=0xbd
48 exp_tag[8]=0xf5; exp_tag[9]=0x3a; exp_tag[10]=0x67; exp_tag[11]=0xb2
49 exp_tag[12]=0x12; exp_tag[13]=0x57; exp_tag[14]=0xbd; exp_tag[15]=0xdf
50 i = 0
51 while i < 16 {
52 if (tag[i] & 0xff) != (exp_tag[i] & 0xff) { return 30 + i }
53 i = i + 1
54 }
55
56 // Round-trip: open(ct, tag) -> pt
57 let pt2: *u8 = sys_mmap(16)
58 let ov: i64 = nx_aes128_gcm_open(key, iv, aad, 0, ct, 16, tag, pt2)
59 if ov != 0 { return 50 }
60 i = 0
61 while i < 16 {
62 if (pt2[i] & 0xff) != (pt[i] & 0xff) { return 60 + i }
63 i = i + 1
64 }
65
66 // Negative: corrupt tag -> open must fail.
67 tag[0] = tag[0] ^ 0x01
68 let nv: i64 = nx_aes128_gcm_open(key, iv, aad, 0, ct, 16, tag, pt2)
69 if nv != (0 - 1) { return 80 }
70
71 // Round-trip with non-empty AAD (synthetic vector self-consistency).
72 tag[0] = tag[0] ^ 0x01 // restore tag (unused below)
73 let aad2: *u8 = sys_mmap(16)
74 var j: i64 = 0
75 while j < 13 { aad2[j] = (0xa0 + j) as u8; j = j + 1 }
76 let pt3: *u8 = sys_mmap(32)
77 j = 0
78 while j < 24 { pt3[j] = (0x40 + j) as u8; j = j + 1 }
79 let ct3: *u8 = sys_mmap(32)
80 let tag3: *u8 = sys_mmap(16)
81 nx_aes128_gcm_seal(key, iv, aad2, 13, pt3, 24, ct3, tag3)
82 let pt3o: *u8 = sys_mmap(32)
83 let rv: i64 = nx_aes128_gcm_open(key, iv, aad2, 13, ct3, 24, tag3, pt3o)
84 if rv != 0 { return 100 }
85 j = 0
86 while j < 24 {
87 if (pt3o[j] & 0xff) != (pt3[j] & 0xff) { return 110 + j }
88 j = j + 1
89 }
90
91 // --- 4-WAY AGGREGATION PATH validation: multi-block seal->open round-trips.
92 // open() re-derives the tag via the UNTOUCHED reference (Horner) GHASH, so a
93 // successful open cross-checks the seal's aggregated ciphertext AND tag bit-for-bit
94 // against the independent reference. Two lengths:
95 // 64 bytes -> exactly one 4-block aggregation group (no leftover, no tail)
96 // 100 bytes -> one group + 2 leftover single blocks + a 4-byte ragged tail
97 // both with a 20-byte (multi-block) AAD. ---
98 let m_aad: *u8 = sys_mmap(20)
99 j = 0
100 while j < 20 { m_aad[j] = (0x80 + j) as u8; j = j + 1 }
101
102 // 64-byte pure-group case
103 let g_pt: *u8 = sys_mmap(64)
104 j = 0
105 while j < 64 { g_pt[j] = (0x11 + j * 5) as u8; j = j + 1 }
106 let g_ct: *u8 = sys_mmap(64)
107 let g_tag: *u8 = sys_mmap(16)
108 nx_aes128_gcm_seal(key, iv, m_aad, 20, g_pt, 64, g_ct, g_tag)
109 let g_out: *u8 = sys_mmap(64)
110 if nx_aes128_gcm_open(key, iv, m_aad, 20, g_ct, 64, g_tag, g_out) != 0 { return 200 }
111 j = 0
112 while j < 64 { if (g_out[j] & 0xff) != (g_pt[j] & 0xff) { return 201 } j = j + 1 }
113
114 // 100-byte group + leftover + tail case
115 let b_pt: *u8 = sys_mmap(100)
116 j = 0
117 while j < 100 { b_pt[j] = (0x2b + j * 7) as u8; j = j + 1 }
118 let b_ct: *u8 = sys_mmap(100)
119 let b_tag: *u8 = sys_mmap(16)
120 nx_aes128_gcm_seal(key, iv, m_aad, 20, b_pt, 100, b_ct, b_tag)
121 let b_out: *u8 = sys_mmap(100)
122 if nx_aes128_gcm_open(key, iv, m_aad, 20, b_ct, 100, b_tag, b_out) != 0 { return 210 }
123 j = 0
124 while j < 100 { if (b_out[j] & 0xff) != (b_pt[j] & 0xff) { return 211 } j = j + 1 }
125
126 // Negative control on the aggregated path: flipping one AAD bit must break auth.
127 m_aad[0] = m_aad[0] ^ 0x01
128 if nx_aes128_gcm_open(key, iv, m_aad, 20, b_ct, 100, b_tag, b_out) != (0 - 1) { return 220 }
129 m_aad[0] = m_aad[0] ^ 0x01
130
131 let msg: *u8 = sys_mmap(32)
132 msg[0]=0x47; msg[1]=0x43; msg[2]=0x4D; msg[3]=0x4B // "GCMK"
133 msg[4]=0x41; msg[5]=0x54; msg[6]=0x3D; msg[7]=0x4F // "AT=O"
134 msg[8]=0x4B; msg[9]=0x0A // "K\n"
135 sys_write(1, msg, 10)
136 return 0
137}