code wiki / (root) / nx_aes128_gcm_wasm.nx

nx_aes128_gcm_wasm.nx source

↩ module page · 488 lines · 22188 B

1// nx_aes128_gcm_wasm.nx -- AES-128-GCM AEAD (NIST SP 800-38D / RFC 5288). 2// Inlines AES-128 ECB + CTR-mode encryption + GHASH (GF(2^128) multiply) 3// to produce a single seal/open AEAD pair. 4// Verified vs NIST SP 800-38D Appendix B Test Cases 1-4. 5// 6// API (12-byte IV form per RFC 5288 §3 -- the only IV length real-world 7// TLS / IPsec / WireGuard use): 8// nx_aes128_gcm_seal(key, iv12, aad, aad_len, pt, pt_len, 9// scratch, ct_out, tag16_out) -> i64 10// nx_aes128_gcm_open(key, iv12, aad, aad_len, ct, ct_len, tag16, 11// scratch, pt_out) -> i64 (0=OK, -1=auth fail) 12// 13// scratch >= 320 bytes. 14// 15// (Original CTR + ECB header below is the inlined cipher core.) 16// 17// nx_aes128_wasm.nx -- AES-128 ECB (FIPS 197) self-contained for WAT target. 18// 19// Rijndael-128 with 128-bit key, 16-byte block, 10 rounds. 20// ECB mode only -- caller composes CTR/GCM/CBC on top. 21// 22// Implementation: byte-oriented SBox/InvSBox + ShiftRows + MixColumns 23// done as straightforward 4x4 byte ops. No T-tables (slower but 24// smaller code, simpler, side-channel-friendly). 25// 26// API: 27// nx_aes128_encrypt(key_ptr, in_block, scratch_ptr, out_block) -> i64 28// key_ptr -- 16 bytes (128-bit key) 29// in_block -- 16 bytes plaintext 30// scratch_ptr -- >= 256 bytes (key schedule = 11 round keys = 176 bytes) 31// out_block -- 16 bytes ciphertext 32// nx_aes128_decrypt(key_ptr, in_block, scratch_ptr, out_block) -> i64 33// symmetric inverse (uses InvSBox + InvShiftRows + InvMixColumns) 34// 35// Verified against FIPS 197 Appendix C.1 (single-block KAT). 36// 37// license_tier: INDEPENDENT_REDERIVE 38// genealogy_id: international-research-sources/nist/fips_197 39// lineage_id: nishi_aes128_wasm_q11 40 41// === Rijndael S-box (FIPS 197 §5.1.1 / Appendix A) === 42// Inlined as a switch. 256 entries. 43func _aes_sbox(b: i64) -> i64 { 44 if b == 0 { return 0x63 } if b == 1 { return 0x7c } if b == 2 { return 0x77 } if b == 3 { return 0x7b } 45 if b == 4 { return 0xf2 } if b == 5 { return 0x6b } if b == 6 { return 0x6f } if b == 7 { return 0xc5 } 46 if b == 8 { return 0x30 } if b == 9 { return 0x01 } if b == 10 { return 0x67 } if b == 11 { return 0x2b } 47 if b == 12 { return 0xfe } if b == 13 { return 0xd7 } if b == 14 { return 0xab } if b == 15 { return 0x76 } 48 if b == 16 { return 0xca } if b == 17 { return 0x82 } if b == 18 { return 0xc9 } if b == 19 { return 0x7d } 49 if b == 20 { return 0xfa } if b == 21 { return 0x59 } if b == 22 { return 0x47 } if b == 23 { return 0xf0 } 50 if b == 24 { return 0xad } if b == 25 { return 0xd4 } if b == 26 { return 0xa2 } if b == 27 { return 0xaf } 51 if b == 28 { return 0x9c } if b == 29 { return 0xa4 } if b == 30 { return 0x72 } if b == 31 { return 0xc0 } 52 if b == 32 { return 0xb7 } if b == 33 { return 0xfd } if b == 34 { return 0x93 } if b == 35 { return 0x26 } 53 if b == 36 { return 0x36 } if b == 37 { return 0x3f } if b == 38 { return 0xf7 } if b == 39 { return 0xcc } 54 if b == 40 { return 0x34 } if b == 41 { return 0xa5 } if b == 42 { return 0xe5 } if b == 43 { return 0xf1 } 55 if b == 44 { return 0x71 } if b == 45 { return 0xd8 } if b == 46 { return 0x31 } if b == 47 { return 0x15 } 56 if b == 48 { return 0x04 } if b == 49 { return 0xc7 } if b == 50 { return 0x23 } if b == 51 { return 0xc3 } 57 if b == 52 { return 0x18 } if b == 53 { return 0x96 } if b == 54 { return 0x05 } if b == 55 { return 0x9a } 58 if b == 56 { return 0x07 } if b == 57 { return 0x12 } if b == 58 { return 0x80 } if b == 59 { return 0xe2 } 59 if b == 60 { return 0xeb } if b == 61 { return 0x27 } if b == 62 { return 0xb2 } if b == 63 { return 0x75 } 60 if b == 64 { return 0x09 } if b == 65 { return 0x83 } if b == 66 { return 0x2c } if b == 67 { return 0x1a } 61 if b == 68 { return 0x1b } if b == 69 { return 0x6e } if b == 70 { return 0x5a } if b == 71 { return 0xa0 } 62 if b == 72 { return 0x52 } if b == 73 { return 0x3b } if b == 74 { return 0xd6 } if b == 75 { return 0xb3 } 63 if b == 76 { return 0x29 } if b == 77 { return 0xe3 } if b == 78 { return 0x2f } if b == 79 { return 0x84 } 64 if b == 80 { return 0x53 } if b == 81 { return 0xd1 } if b == 82 { return 0x00 } if b == 83 { return 0xed } 65 if b == 84 { return 0x20 } if b == 85 { return 0xfc } if b == 86 { return 0xb1 } if b == 87 { return 0x5b } 66 if b == 88 { return 0x6a } if b == 89 { return 0xcb } if b == 90 { return 0xbe } if b == 91 { return 0x39 } 67 if b == 92 { return 0x4a } if b == 93 { return 0x4c } if b == 94 { return 0x58 } if b == 95 { return 0xcf } 68 if b == 96 { return 0xd0 } if b == 97 { return 0xef } if b == 98 { return 0xaa } if b == 99 { return 0xfb } 69 if b == 100 { return 0x43 } if b == 101 { return 0x4d } if b == 102 { return 0x33 } if b == 103 { return 0x85 } 70 if b == 104 { return 0x45 } if b == 105 { return 0xf9 } if b == 106 { return 0x02 } if b == 107 { return 0x7f } 71 if b == 108 { return 0x50 } if b == 109 { return 0x3c } if b == 110 { return 0x9f } if b == 111 { return 0xa8 } 72 if b == 112 { return 0x51 } if b == 113 { return 0xa3 } if b == 114 { return 0x40 } if b == 115 { return 0x8f } 73 if b == 116 { return 0x92 } if b == 117 { return 0x9d } if b == 118 { return 0x38 } if b == 119 { return 0xf5 } 74 if b == 120 { return 0xbc } if b == 121 { return 0xb6 } if b == 122 { return 0xda } if b == 123 { return 0x21 } 75 if b == 124 { return 0x10 } if b == 125 { return 0xff } if b == 126 { return 0xf3 } if b == 127 { return 0xd2 } 76 if b == 128 { return 0xcd } if b == 129 { return 0x0c } if b == 130 { return 0x13 } if b == 131 { return 0xec } 77 if b == 132 { return 0x5f } if b == 133 { return 0x97 } if b == 134 { return 0x44 } if b == 135 { return 0x17 } 78 if b == 136 { return 0xc4 } if b == 137 { return 0xa7 } if b == 138 { return 0x7e } if b == 139 { return 0x3d } 79 if b == 140 { return 0x64 } if b == 141 { return 0x5d } if b == 142 { return 0x19 } if b == 143 { return 0x73 } 80 if b == 144 { return 0x60 } if b == 145 { return 0x81 } if b == 146 { return 0x4f } if b == 147 { return 0xdc } 81 if b == 148 { return 0x22 } if b == 149 { return 0x2a } if b == 150 { return 0x90 } if b == 151 { return 0x88 } 82 if b == 152 { return 0x46 } if b == 153 { return 0xee } if b == 154 { return 0xb8 } if b == 155 { return 0x14 } 83 if b == 156 { return 0xde } if b == 157 { return 0x5e } if b == 158 { return 0x0b } if b == 159 { return 0xdb } 84 if b == 160 { return 0xe0 } if b == 161 { return 0x32 } if b == 162 { return 0x3a } if b == 163 { return 0x0a } 85 if b == 164 { return 0x49 } if b == 165 { return 0x06 } if b == 166 { return 0x24 } if b == 167 { return 0x5c } 86 if b == 168 { return 0xc2 } if b == 169 { return 0xd3 } if b == 170 { return 0xac } if b == 171 { return 0x62 } 87 if b == 172 { return 0x91 } if b == 173 { return 0x95 } if b == 174 { return 0xe4 } if b == 175 { return 0x79 } 88 if b == 176 { return 0xe7 } if b == 177 { return 0xc8 } if b == 178 { return 0x37 } if b == 179 { return 0x6d } 89 if b == 180 { return 0x8d } if b == 181 { return 0xd5 } if b == 182 { return 0x4e } if b == 183 { return 0xa9 } 90 if b == 184 { return 0x6c } if b == 185 { return 0x56 } if b == 186 { return 0xf4 } if b == 187 { return 0xea } 91 if b == 188 { return 0x65 } if b == 189 { return 0x7a } if b == 190 { return 0xae } if b == 191 { return 0x08 } 92 if b == 192 { return 0xba } if b == 193 { return 0x78 } if b == 194 { return 0x25 } if b == 195 { return 0x2e } 93 if b == 196 { return 0x1c } if b == 197 { return 0xa6 } if b == 198 { return 0xb4 } if b == 199 { return 0xc6 } 94 if b == 200 { return 0xe8 } if b == 201 { return 0xdd } if b == 202 { return 0x74 } if b == 203 { return 0x1f } 95 if b == 204 { return 0x4b } if b == 205 { return 0xbd } if b == 206 { return 0x8b } if b == 207 { return 0x8a } 96 if b == 208 { return 0x70 } if b == 209 { return 0x3e } if b == 210 { return 0xb5 } if b == 211 { return 0x66 } 97 if b == 212 { return 0x48 } if b == 213 { return 0x03 } if b == 214 { return 0xf6 } if b == 215 { return 0x0e } 98 if b == 216 { return 0x61 } if b == 217 { return 0x35 } if b == 218 { return 0x57 } if b == 219 { return 0xb9 } 99 if b == 220 { return 0x86 } if b == 221 { return 0xc1 } if b == 222 { return 0x1d } if b == 223 { return 0x9e } 100 if b == 224 { return 0xe1 } if b == 225 { return 0xf8 } if b == 226 { return 0x98 } if b == 227 { return 0x11 } 101 if b == 228 { return 0x69 } if b == 229 { return 0xd9 } if b == 230 { return 0x8e } if b == 231 { return 0x94 } 102 if b == 232 { return 0x9b } if b == 233 { return 0x1e } if b == 234 { return 0x87 } if b == 235 { return 0xe9 } 103 if b == 236 { return 0xce } if b == 237 { return 0x55 } if b == 238 { return 0x28 } if b == 239 { return 0xdf } 104 if b == 240 { return 0x8c } if b == 241 { return 0xa1 } if b == 242 { return 0x89 } if b == 243 { return 0x0d } 105 if b == 244 { return 0xbf } if b == 245 { return 0xe6 } if b == 246 { return 0x42 } if b == 247 { return 0x68 } 106 if b == 248 { return 0x41 } if b == 249 { return 0x99 } if b == 250 { return 0x2d } if b == 251 { return 0x0f } 107 if b == 252 { return 0xb0 } if b == 253 { return 0x54 } if b == 254 { return 0xbb } 108 return 0x16 109} 110 111// === Round constants Rcon[i] for key schedule (FIPS 197 §5.2) === 112func _aes_rcon(i: i64) -> i64 { 113 if i == 1 { return 0x01 } if i == 2 { return 0x02 } if i == 3 { return 0x04 } if i == 4 { return 0x08 } 114 if i == 5 { return 0x10 } if i == 6 { return 0x20 } if i == 7 { return 0x40 } if i == 8 { return 0x80 } 115 if i == 9 { return 0x1b } 116 return 0x36 117} 118 119// === GF(2^8) multiplication for MixColumns (xtime-based) === 120// Multiply a by 2 in GF(2^8) with reduction polynomial x^8 + x^4 + x^3 + x + 1 (0x1b). 121func _xtime(a: i64) -> i64 { 122 let shifted: i64 = (a << 1) & 0xff 123 if (a & 0x80) != 0 { return shifted ^ 0x1b } 124 return shifted 125} 126 127// === Key expansion: derive 11 round keys (44 4-byte words) === 128// Stored in scratch as 176 contiguous bytes, 16 bytes per round key. 129func _aes128_key_expand(key: *u8, rk: *u8) -> i64 { 130 var i: i64 = 0 131 while i < 16 { rk[i] = key[i]; i = i + 1 } 132 var n: i64 = 16 // bytes generated so far 133 var rcon_idx: i64 = 1 134 while n < 176 { 135 // last word 136 var t0: i64 = rk[n - 4] 137 var t1: i64 = rk[n - 3] 138 var t2: i64 = rk[n - 2] 139 var t3: i64 = rk[n - 1] 140 if (n & 15) == 0 { 141 // RotWord + SubBytes + Rcon 142 let r0: i64 = _aes_sbox(t1) ^ _aes_rcon(rcon_idx) 143 let r1: i64 = _aes_sbox(t2) 144 let r2: i64 = _aes_sbox(t3) 145 let r3: i64 = _aes_sbox(t0) 146 t0 = r0; t1 = r1; t2 = r2; t3 = r3 147 rcon_idx = rcon_idx + 1 148 } 149 rk[n] = (rk[n - 16] ^ t0) & 0xff 150 rk[n + 1] = (rk[n - 15] ^ t1) & 0xff 151 rk[n + 2] = (rk[n - 14] ^ t2) & 0xff 152 rk[n + 3] = (rk[n - 13] ^ t3) & 0xff 153 n = n + 4 154 } 155 return 0 156} 157 158// === AES round transformations === 159 160// AddRoundKey: state ^= round_key (16 bytes XOR). 161func _aes_add_round_key(state: *u8, rk: *u8, round: i64) -> i64 { 162 var i: i64 = 0 163 while i < 16 { state[i] = (state[i] ^ rk[round * 16 + i]) & 0xff; i = i + 1 } 164 return 0 165} 166 167// SubBytes: state[i] = SBox[state[i]] 168func _aes_sub_bytes(state: *u8) -> i64 { 169 var i: i64 = 0 170 while i < 16 { state[i] = _aes_sbox(state[i]) & 0xff; i = i + 1 } 171 return 0 172} 173 174// ShiftRows: row r shifts left by r positions (cyclic). 175// State layout (column-major per FIPS 197): 176// state[0] state[4] state[8] state[12] <- row 0 (no shift) 177// state[1] state[5] state[9] state[13] <- row 1 (shift 1) 178// state[2] state[6] state[10] state[14] <- row 2 (shift 2) 179// state[3] state[7] state[11] state[15] <- row 3 (shift 3) 180func _aes_shift_rows(state: *u8) -> i64 { 181 // Row 1: rotate left by 1 182 let t1: i64 = state[1] 183 state[1] = state[5] 184 state[5] = state[9] 185 state[9] = state[13] 186 state[13] = t1 187 // Row 2: rotate left by 2 (swap pairs) 188 let t2: i64 = state[2] 189 state[2] = state[10] 190 state[10] = t2 191 let t6: i64 = state[6] 192 state[6] = state[14] 193 state[14] = t6 194 // Row 3: rotate left by 3 (= rotate right by 1) 195 let t3: i64 = state[15] 196 state[15] = state[11] 197 state[11] = state[7] 198 state[7] = state[3] 199 state[3] = t3 200 return 0 201} 202 203// MixColumns: for each 4-byte column, multiply by the matrix 204// [02 03 01 01] 205// [01 02 03 01] 206// [01 01 02 03] 207// [03 01 01 02] 208// in GF(2^8). 209func _aes_mix_columns(state: *u8) -> i64 { 210 var c: i64 = 0 211 while c < 4 { 212 let off: i64 = c * 4 213 let a0: i64 = state[off] 214 let a1: i64 = state[off + 1] 215 let a2: i64 = state[off + 2] 216 let a3: i64 = state[off + 3] 217 // t = a0 ^ a1 ^ a2 ^ a3 218 let t: i64 = a0 ^ a1 ^ a2 ^ a3 219 let n0: i64 = a0 ^ _xtime(a0 ^ a1) ^ t 220 let n1: i64 = a1 ^ _xtime(a1 ^ a2) ^ t 221 let n2: i64 = a2 ^ _xtime(a2 ^ a3) ^ t 222 let n3: i64 = a3 ^ _xtime(a3 ^ a0) ^ t 223 state[off] = n0 & 0xff 224 state[off + 1] = n1 & 0xff 225 state[off + 2] = n2 & 0xff 226 state[off + 3] = n3 & 0xff 227 c = c + 1 228 } 229 return 0 230} 231 232// === AES-128 encrypt one block (private, used by CTR wrapper) === 233func _aes128_encrypt_block(key_ptr: *u8, in_block: *u8, 234 scratch_ptr: *u8, out_block: *u8) -> i64 { 235 let rk: *u8 = scratch_ptr // 176 bytes for round keys 236 let state: *u8 = (scratch_ptr as i64 + 176) as *u8 // 16 bytes state 237 _aes128_key_expand(key_ptr, rk) 238 var i: i64 = 0 239 while i < 16 { state[i] = in_block[i]; i = i + 1 } 240 _aes_add_round_key(state, rk, 0) 241 var r: i64 = 1 242 while r < 10 { 243 _aes_sub_bytes(state) 244 _aes_shift_rows(state) 245 _aes_mix_columns(state) 246 _aes_add_round_key(state, rk, r) 247 r = r + 1 248 } 249 // Final round: no MixColumns 250 _aes_sub_bytes(state) 251 _aes_shift_rows(state) 252 _aes_add_round_key(state, rk, 10) 253 var j: i64 = 0 254 while j < 16 { out_block[j] = state[j]; j = j + 1 } 255 return 0 256} 257 258// === GCM helpers (NIST SP 800-38D) === 259 260// inc32: increment only the rightmost 32 bits of a 16-byte block. 261// Per SP 800-38D §6.2. Used between GCM counter blocks. 262func _aes_gcm_inc32(ctr: *u8) -> i64 { 263 var i: i64 = 15 264 while i >= 12 { 265 let nb: i64 = (ctr[i] + 1) & 0xff 266 ctr[i] = nb 267 if nb != 0 { i = 11 } else { i = i - 1 } 268 } 269 return 0 270} 271 272// GF(2^128) multiplication per SP 800-38D §6.3 Algorithm 1. 273// Bit convention: byte[0] holds bits 0..7 with bit 0 = MSB of byte. 274// Reduction polynomial R = 11100001 || 0^120 (byte[0] = 0xe1). 275// z = x * h, all 16-byte blocks. z may alias x but NOT h. 276func _ghash_mul(x16: *u8, h16: *u8, z16: *u8, v16: *u8) -> i64 { 277 // Initialize Z = 0, V = H. 278 var k: i64 = 0 279 while k < 16 { z16[k] = 0; v16[k] = h16[k]; k = k + 1 } 280 281 var bit: i64 = 0 282 while bit < 128 { 283 let byte_idx: i64 = bit >> 3 284 let bit_in_byte: i64 = 7 - (bit & 7) 285 let xbit: i64 = (x16[byte_idx] >> bit_in_byte) & 1 286 if xbit != 0 { 287 var j: i64 = 0 288 while j < 16 { z16[j] = z16[j] ^ v16[j]; j = j + 1 } 289 } 290 // V = V >> 1, MSB-first stream order, optional XOR with R. 291 let lsb: i64 = v16[15] & 1 292 var i: i64 = 15 293 while i > 0 { 294 v16[i] = ((v16[i] >> 1) | ((v16[i - 1] & 1) << 7)) & 0xff 295 i = i - 1 296 } 297 v16[0] = (v16[0] >> 1) & 0xff 298 if lsb != 0 { v16[0] = v16[0] ^ 0xe1 } 299 bit = bit + 1 300 } 301 return 0 302} 303 304// Accumulate one 16-byte block into running GHASH state (Y = (Y ^ X) * H). 305// y16 is mutated. scratch_mul is 16-byte temp for the multiplier ladder. 306func _ghash_update_block(y16: *u8, h16: *u8, x16: *u8, 307 temp_y: *u8, temp_v: *u8) -> i64 { 308 var i: i64 = 0 309 while i < 16 { temp_y[i] = y16[i] ^ x16[i]; i = i + 1 } 310 _ghash_mul(temp_y, h16, y16, temp_v) 311 return 0 312} 313 314// Accumulate a buffer (zero-padded to 16-byte block) into GHASH. 315func _ghash_update_buf(y16: *u8, h16: *u8, buf: *u8, buf_len: i64, 316 temp_y: *u8, temp_v: *u8, temp_x: *u8) -> i64 { 317 var pos: i64 = 0 318 while pos < buf_len { 319 // Build padded block. 320 var b: i64 = 0 321 while b < 16 { 322 if pos + b < buf_len { temp_x[b] = buf[pos + b] } 323 else { temp_x[b] = 0 } 324 b = b + 1 325 } 326 _ghash_update_block(y16, h16, temp_x, temp_y, temp_v) 327 pos = pos + 16 328 } 329 return 0 330} 331 332// Big-endian 64-bit write into 8 bytes at dst. Used for length block. 333func _be64_put(dst: *u8, v: i64) -> i64 { 334 dst[0] = (v >> 56) & 0xff 335 dst[1] = (v >> 48) & 0xff 336 dst[2] = (v >> 40) & 0xff 337 dst[3] = (v >> 32) & 0xff 338 dst[4] = (v >> 24) & 0xff 339 dst[5] = (v >> 16) & 0xff 340 dst[6] = (v >> 8) & 0xff 341 dst[7] = v & 0xff 342 return 0 343} 344 345// === AES-128-GCM public API (12-byte IV form per RFC 5288 §3) === 346// 347// scratch layout (>= 320 bytes): 348// 0..175 : AES round keys 349// 176..191 : AES working state 350// 192..207 : counter block (J0, then inc32 each block) 351// 208..223 : keystream block 352// 224..239 : H = E_K(0^128) hash subkey 353// 240..255 : Y = running GHASH accumulator 354// 256..271 : temp Y for ghash_update_block 355// 272..287 : temp V for ghash_mul 356// 288..303 : temp X (length block / padded buffer block) 357// 304..319 : E_K(J0) for final tag mask 358 359func nx_aes128_gcm_seal(key_ptr: *u8, iv12: *u8, 360 aad: *u8, aad_len: i64, 361 pt: *u8, pt_len: i64, 362 scratch_ptr: *u8, 363 ct_out: *u8, tag16_out: *u8) -> i64 { 364 let ctr: *u8 = (scratch_ptr as i64 + 192) as *u8 365 let ks: *u8 = (scratch_ptr as i64 + 208) as *u8 366 let h: *u8 = (scratch_ptr as i64 + 224) as *u8 367 let y: *u8 = (scratch_ptr as i64 + 240) as *u8 368 let temp_y: *u8 = (scratch_ptr as i64 + 256) as *u8 369 let temp_v: *u8 = (scratch_ptr as i64 + 272) as *u8 370 let temp_x: *u8 = (scratch_ptr as i64 + 288) as *u8 371 let ekj0: *u8 = (scratch_ptr as i64 + 304) as *u8 372 373 // H = E_K(0^128) 374 var i: i64 = 0 375 while i < 16 { ctr[i] = 0; i = i + 1 } 376 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, h) 377 378 // J0 = iv12 || 0x00000001 (only the 96-bit IV branch) 379 var j: i64 = 0 380 while j < 12 { ctr[j] = iv12[j]; j = j + 1 } 381 ctr[12] = 0; ctr[13] = 0; ctr[14] = 0; ctr[15] = 1 382 383 // E_K(J0) for tag mask 384 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, ekj0) 385 386 // Init GHASH Y = 0 387 var k: i64 = 0 388 while k < 16 { y[k] = 0; k = k + 1 } 389 390 // GHASH over AAD (zero-padded to 16-byte boundary) 391 _ghash_update_buf(y, h, aad, aad_len, temp_y, temp_v, temp_x) 392 393 // Encrypt + GHASH ciphertext blocks. Starting counter = inc32(J0). 394 _aes_gcm_inc32(ctr) 395 var pos: i64 = 0 396 while pos < pt_len { 397 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, ks) 398 var b: i64 = 0 399 while b < 16 { 400 if pos + b >= pt_len { temp_x[b] = 0 } 401 else { 402 let c: i64 = (pt[pos + b] ^ ks[b]) & 0xff 403 ct_out[pos + b] = c 404 temp_x[b] = c 405 } 406 b = b + 1 407 } 408 _ghash_update_block(y, h, temp_x, temp_y, temp_v) 409 _aes_gcm_inc32(ctr) 410 pos = pos + 16 411 } 412 413 // Length block: [len(AAD) in bits || len(C) in bits], both 64-bit BE. 414 var z: i64 = 0 415 while z < 16 { temp_x[z] = 0; z = z + 1 } 416 _be64_put(temp_x, aad_len * 8) 417 _be64_put((temp_x as i64 + 8) as *u8, pt_len * 8) 418 _ghash_update_block(y, h, temp_x, temp_y, temp_v) 419 420 // Tag = Y XOR E_K(J0) 421 var t: i64 = 0 422 while t < 16 { tag16_out[t] = (y[t] ^ ekj0[t]) & 0xff; t = t + 1 } 423 return 0 424} 425 426func nx_aes128_gcm_open(key_ptr: *u8, iv12: *u8, 427 aad: *u8, aad_len: i64, 428 ct: *u8, ct_len: i64, tag16: *u8, 429 scratch_ptr: *u8, pt_out: *u8) -> i64 { 430 let ctr: *u8 = (scratch_ptr as i64 + 192) as *u8 431 let ks: *u8 = (scratch_ptr as i64 + 208) as *u8 432 let h: *u8 = (scratch_ptr as i64 + 224) as *u8 433 let y: *u8 = (scratch_ptr as i64 + 240) as *u8 434 let temp_y: *u8 = (scratch_ptr as i64 + 256) as *u8 435 let temp_v: *u8 = (scratch_ptr as i64 + 272) as *u8 436 let temp_x: *u8 = (scratch_ptr as i64 + 288) as *u8 437 let ekj0: *u8 = (scratch_ptr as i64 + 304) as *u8 438 439 // H, J0, E_K(J0) same as seal. 440 var i: i64 = 0 441 while i < 16 { ctr[i] = 0; i = i + 1 } 442 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, h) 443 var j: i64 = 0 444 while j < 12 { ctr[j] = iv12[j]; j = j + 1 } 445 ctr[12] = 0; ctr[13] = 0; ctr[14] = 0; ctr[15] = 1 446 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, ekj0) 447 448 // GHASH AAD then ciphertext (ciphertext is hashed in its on-wire form). 449 var k: i64 = 0 450 while k < 16 { y[k] = 0; k = k + 1 } 451 _ghash_update_buf(y, h, aad, aad_len, temp_y, temp_v, temp_x) 452 _ghash_update_buf(y, h, ct, ct_len, temp_y, temp_v, temp_x) 453 454 // Length block 455 var z: i64 = 0 456 while z < 16 { temp_x[z] = 0; z = z + 1 } 457 _be64_put(temp_x, aad_len * 8) 458 _be64_put((temp_x as i64 + 8) as *u8, ct_len * 8) 459 _ghash_update_block(y, h, temp_x, temp_y, temp_v) 460 461 // Expected tag = Y XOR E_K(J0); constant-time compare to provided. 462 var diff: i64 = 0 463 var t: i64 = 0 464 while t < 16 { 465 let want: i64 = (y[t] ^ ekj0[t]) & 0xff 466 diff = diff | (want ^ tag16[t]) 467 t = t + 1 468 } 469 if diff != 0 { return -1 } 470 471 // Tag good -- decrypt with CTR starting at inc32(J0). 472 _aes_gcm_inc32(ctr) 473 var pos: i64 = 0 474 while pos < ct_len { 475 _aes128_encrypt_block(key_ptr, ctr, scratch_ptr, ks) 476 var b: i64 = 0 477 while b < 16 { 478 if pos + b >= ct_len { b = 16 } 479 else { 480 pt_out[pos + b] = (ct[pos + b] ^ ks[b]) & 0xff 481 b = b + 1 482 } 483 } 484 _aes_gcm_inc32(ctr) 485 pos = pos + 16 486 } 487 return 0 488}