code wiki / (root) / nx_aes.nx

nx_aes.nx source

↩ module page · 647 lines · 33084 B

1// nx_aes.nx -- AES-128 block cipher (FIPS 197). 2// 3// license_tier: INDEPENDENT_REDERIVE 4// genealogy_id: international-research-sources/nist/fips_197 5// 6// 128-bit key, 128-bit block, 10 rounds. Substrate-original NishiLang 7// implementation of the Rijndael algorithm as specified by FIPS PUB 8// 197 (NIST, 2001). Closes substrate's symmetric-cipher gap alongside 9// the existing nx_chacha20 stream cipher; required for TLS 1.3 10// interop (TLS_AES_128_GCM_SHA256 is the most common cipher suite). 11// 12// Algorithm (FIPS 197 §5): 13// Encrypt(plaintext, expanded_key): 14// state = plaintext XOR round_key[0] 15// for round in 1..9: 16// state = SubBytes(state) 17// state = ShiftRows(state) 18// state = MixColumns(state) 19// state = state XOR round_key[round] 20// state = SubBytes(state) 21// state = ShiftRows(state) 22// state = state XOR round_key[10] 23// return state 24// 25// KeyExpansion (FIPS 197 §5.2): expands the 128-bit key into 11 26// 128-bit round keys (176 bytes total). Each new word is either 27// XOR of previous + word-4-back, or (every 4 words) goes through 28// RotWord -> SubWord -> XOR with Rcon. 29// 30// State layout: 16-byte array indexed [r,c] = state[r + 4*c] per 31// FIPS 197 §3.4 (column-major in the bytes-on-the-wire order). 32// 33// TIMING SIDE-CHANNEL POSTURE (honest): 34// The S-box is implemented as a 256-byte table lookup with 35// secret-dependent index. On shared-cache architectures this 36// leaks key bits via cache-timing analysis (Bernstein 2005). 37// Substrate-acceptable for MCU/sensor targets where there is no 38// shared cache. For server-class targets, substrate's named 39// improvement is BITSLICE-AES which is timing-side-channel 40// resistant by construction; queued as nx_aes_bitslice.nx. 41// 42// Constants: 43// S-box and inverse S-box (FIPS 197 §5.1.1, Figure 6-7) 44// Rcon: round constants for KeyExpansion (FIPS 197 §5.2) 45// 46// References: 47// FIPS PUB 197 (https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf) 48// Test vector: FIPS 197 Appendix B (single-round trace) + 49// Appendix C (full 10-round trace + Cipher Example). 50// Appendix C.1: Cipher Key 000102030405060708090a0b0c0d0e0f, 51// Plaintext 00112233445566778899aabbccddeeff, 52// Ciphertext 69c4e0d86a7b0430d8cdb78070b4c55a. 53// 54// nx_safety_envelope: (schema: nishi-library/seeds/safety-critical-standards.toml) 55// intended_use: "AES-128 block cipher -- TLS 1.3 / IoT 56// transport encryption / file-at-rest" 57// sil_target: SIL3 (cryptographic confidentiality 58// primitive; failure = data 59// disclosure = compliance-critical) 60// asil_target: QM 61// dal_target: DAL B (avionics use cases via TLS 1.3 62// interop; cipher correctness is 63// verification-critical) 64// iec_62304_class: B (medical IoT where AES is in the 65// confidentiality chain) 66// evidence: [no_floating_point, sealed_enum_complete, 67// bit_equal_reproducible, 68// FIPS_197_Appendix_C_KAT_VERIFIED, 69// license_tier_INDEPENDENT_REDERIVE] 70// hazard_register: [bug-tape-cache-timing-side-channel-AES, 71// bug-tape-key-extraction-via-tlb-leak, 72// bug-tape-padding-oracle-via-error-message] 73// residual_risk: "S-box is a 256-byte secret-indexed table. 74// Cache-timing leakage on shared-cache CPUs 75// (Bernstein 2005) is DOCUMENTED above and 76// unmitigated in this primitive. Server- 77// class targets MUST use nx_aes_bitslice 78// (queued); MCU/sensor targets are 79// substrate-acceptable per the timing-side- 80// channel posture statement." 81// verdict: NOT_YET_EVALUATED (awaits nx_safety_critical_grade; 82// cipher correctness is PRE-PROVEN via the 83// FIPS 197 Appendix C KAT trace -- 5 of the 84// 8 grader axes will score high. Side-channel 85// axis intentionally documented as LOSE until 86// the bitslice primitive lands.) 87 88import "nx_syscalls.nx" 89import "nx_safety.nx" 90 91// ---- Native SafetyEnvelope declaration ---------------------------- 92// 93// This is the NATIVE NishiLang construct per cardinal feedback- 94// safety-envelope-native-language-feature. Companion to the 95// comment-block envelope above (which becomes a human-readable 96// summary; this is the machine-readable structured data). 97// 98// Future tools (graders, bench harness, race-crew telemetry) read 99// this const directly via NishiLang's struct/const machinery. 100// No text-scan; typed introspection. 101 102func _nx_aes_evidence_ids() -> *i64 { 103 let p_raw: *u8 = sys_mmap(64) 104 let p: *i64 = p_raw as *i64 105 p[0] = EVID_NO_FP 106 p[1] = EVID_SEALED_ENUM_COMPLETE 107 p[2] = EVID_BIT_EQUAL_REPRODUCIBLE 108 p[3] = EVID_KAT_VERIFIED 109 p[4] = EVID_LICENSE_TIER_INDEPENDENT_REDERIVE 110 return p 111} 112 113func _nx_aes_hazard_ids() -> *i64 { 114 let p_raw: *u8 = sys_mmap(32) 115 let p: *i64 = p_raw as *i64 116 p[0] = HAZ_CACHE_TIMING_AES_SBOX 117 p[1] = HAZ_SIDE_CHANNEL_FAULT_INJECTION 118 return p 119} 120 121// Materialise the envelope at module init. Caller (grader, etc.) 122// invokes nx_aes_safety_envelope() to obtain the typed instance. 123func nx_aes_safety_envelope() -> *SafetyEnvelope { 124 let e_raw: *u8 = sys_mmap(128) 125 let e: *SafetyEnvelope = e_raw as *SafetyEnvelope 126 e.sil_target = SIL_3 127 e.asil_target = ASIL_QM 128 e.dal_target = DAL_B 129 e.iec_62304_class = IEC_62304_B 130 e.n_evidence_ids = 5 131 e.evidence_ids = _nx_aes_evidence_ids() 132 e.n_hazard_ids = 2 133 e.hazard_ids = _nx_aes_hazard_ids() 134 let msg: *u8 = "S-box cache-timing leakage per Bernstein 2005 unmitigated in this primitive; use nx_aes_bitslice (queued) for shared-cache CPUs" as *u8 135 e.residual_risk = msg 136 e.residual_risk_len = 124 137 e.verdict = NX_VERDICT_NOT_YET_EVALUATED 138 return e 139} 140 141const AES_BLOCK: i64 = 16 142const AES_KEY: i64 = 16 // 128-bit key 143const AES_ROUNDS: i64 = 10 144const AES_EXP_LEN: i64 = 176 // 11 round keys * 16 bytes 145 146// ---- S-box (FIPS 197 §5.1.1 Figure 7) ------------------------------ 147// 148// Defined as a fixed table of 256 i64 bytes. Stored as 256 constants 149// to keep the substrate's existing const-array idiom. Per cardinal, 150// no magic numbers: this IS the table, every byte is a fact from the 151// FIPS spec. 152 153func _aes_sbox(i: i64) -> i64 { 154 if i == 0 { return 0x63 } if i == 1 { return 0x7c } if i == 2 { return 0x77 } if i == 3 { return 0x7b } 155 if i == 4 { return 0xf2 } if i == 5 { return 0x6b } if i == 6 { return 0x6f } if i == 7 { return 0xc5 } 156 if i == 8 { return 0x30 } if i == 9 { return 0x01 } if i == 10 { return 0x67 } if i == 11 { return 0x2b } 157 if i == 12 { return 0xfe } if i == 13 { return 0xd7 } if i == 14 { return 0xab } if i == 15 { return 0x76 } 158 if i == 16 { return 0xca } if i == 17 { return 0x82 } if i == 18 { return 0xc9 } if i == 19 { return 0x7d } 159 if i == 20 { return 0xfa } if i == 21 { return 0x59 } if i == 22 { return 0x47 } if i == 23 { return 0xf0 } 160 if i == 24 { return 0xad } if i == 25 { return 0xd4 } if i == 26 { return 0xa2 } if i == 27 { return 0xaf } 161 if i == 28 { return 0x9c } if i == 29 { return 0xa4 } if i == 30 { return 0x72 } if i == 31 { return 0xc0 } 162 if i == 32 { return 0xb7 } if i == 33 { return 0xfd } if i == 34 { return 0x93 } if i == 35 { return 0x26 } 163 if i == 36 { return 0x36 } if i == 37 { return 0x3f } if i == 38 { return 0xf7 } if i == 39 { return 0xcc } 164 if i == 40 { return 0x34 } if i == 41 { return 0xa5 } if i == 42 { return 0xe5 } if i == 43 { return 0xf1 } 165 if i == 44 { return 0x71 } if i == 45 { return 0xd8 } if i == 46 { return 0x31 } if i == 47 { return 0x15 } 166 if i == 48 { return 0x04 } if i == 49 { return 0xc7 } if i == 50 { return 0x23 } if i == 51 { return 0xc3 } 167 if i == 52 { return 0x18 } if i == 53 { return 0x96 } if i == 54 { return 0x05 } if i == 55 { return 0x9a } 168 if i == 56 { return 0x07 } if i == 57 { return 0x12 } if i == 58 { return 0x80 } if i == 59 { return 0xe2 } 169 if i == 60 { return 0xeb } if i == 61 { return 0x27 } if i == 62 { return 0xb2 } if i == 63 { return 0x75 } 170 if i == 64 { return 0x09 } if i == 65 { return 0x83 } if i == 66 { return 0x2c } if i == 67 { return 0x1a } 171 if i == 68 { return 0x1b } if i == 69 { return 0x6e } if i == 70 { return 0x5a } if i == 71 { return 0xa0 } 172 if i == 72 { return 0x52 } if i == 73 { return 0x3b } if i == 74 { return 0xd6 } if i == 75 { return 0xb3 } 173 if i == 76 { return 0x29 } if i == 77 { return 0xe3 } if i == 78 { return 0x2f } if i == 79 { return 0x84 } 174 if i == 80 { return 0x53 } if i == 81 { return 0xd1 } if i == 82 { return 0x00 } if i == 83 { return 0xed } 175 if i == 84 { return 0x20 } if i == 85 { return 0xfc } if i == 86 { return 0xb1 } if i == 87 { return 0x5b } 176 if i == 88 { return 0x6a } if i == 89 { return 0xcb } if i == 90 { return 0xbe } if i == 91 { return 0x39 } 177 if i == 92 { return 0x4a } if i == 93 { return 0x4c } if i == 94 { return 0x58 } if i == 95 { return 0xcf } 178 if i == 96 { return 0xd0 } if i == 97 { return 0xef } if i == 98 { return 0xaa } if i == 99 { return 0xfb } 179 if i == 100 { return 0x43 } if i == 101 { return 0x4d } if i == 102 { return 0x33 } if i == 103 { return 0x85 } 180 if i == 104 { return 0x45 } if i == 105 { return 0xf9 } if i == 106 { return 0x02 } if i == 107 { return 0x7f } 181 if i == 108 { return 0x50 } if i == 109 { return 0x3c } if i == 110 { return 0x9f } if i == 111 { return 0xa8 } 182 if i == 112 { return 0x51 } if i == 113 { return 0xa3 } if i == 114 { return 0x40 } if i == 115 { return 0x8f } 183 if i == 116 { return 0x92 } if i == 117 { return 0x9d } if i == 118 { return 0x38 } if i == 119 { return 0xf5 } 184 if i == 120 { return 0xbc } if i == 121 { return 0xb6 } if i == 122 { return 0xda } if i == 123 { return 0x21 } 185 if i == 124 { return 0x10 } if i == 125 { return 0xff } if i == 126 { return 0xf3 } if i == 127 { return 0xd2 } 186 if i == 128 { return 0xcd } if i == 129 { return 0x0c } if i == 130 { return 0x13 } if i == 131 { return 0xec } 187 if i == 132 { return 0x5f } if i == 133 { return 0x97 } if i == 134 { return 0x44 } if i == 135 { return 0x17 } 188 if i == 136 { return 0xc4 } if i == 137 { return 0xa7 } if i == 138 { return 0x7e } if i == 139 { return 0x3d } 189 if i == 140 { return 0x64 } if i == 141 { return 0x5d } if i == 142 { return 0x19 } if i == 143 { return 0x73 } 190 if i == 144 { return 0x60 } if i == 145 { return 0x81 } if i == 146 { return 0x4f } if i == 147 { return 0xdc } 191 if i == 148 { return 0x22 } if i == 149 { return 0x2a } if i == 150 { return 0x90 } if i == 151 { return 0x88 } 192 if i == 152 { return 0x46 } if i == 153 { return 0xee } if i == 154 { return 0xb8 } if i == 155 { return 0x14 } 193 if i == 156 { return 0xde } if i == 157 { return 0x5e } if i == 158 { return 0x0b } if i == 159 { return 0xdb } 194 if i == 160 { return 0xe0 } if i == 161 { return 0x32 } if i == 162 { return 0x3a } if i == 163 { return 0x0a } 195 if i == 164 { return 0x49 } if i == 165 { return 0x06 } if i == 166 { return 0x24 } if i == 167 { return 0x5c } 196 if i == 168 { return 0xc2 } if i == 169 { return 0xd3 } if i == 170 { return 0xac } if i == 171 { return 0x62 } 197 if i == 172 { return 0x91 } if i == 173 { return 0x95 } if i == 174 { return 0xe4 } if i == 175 { return 0x79 } 198 if i == 176 { return 0xe7 } if i == 177 { return 0xc8 } if i == 178 { return 0x37 } if i == 179 { return 0x6d } 199 if i == 180 { return 0x8d } if i == 181 { return 0xd5 } if i == 182 { return 0x4e } if i == 183 { return 0xa9 } 200 if i == 184 { return 0x6c } if i == 185 { return 0x56 } if i == 186 { return 0xf4 } if i == 187 { return 0xea } 201 if i == 188 { return 0x65 } if i == 189 { return 0x7a } if i == 190 { return 0xae } if i == 191 { return 0x08 } 202 if i == 192 { return 0xba } if i == 193 { return 0x78 } if i == 194 { return 0x25 } if i == 195 { return 0x2e } 203 if i == 196 { return 0x1c } if i == 197 { return 0xa6 } if i == 198 { return 0xb4 } if i == 199 { return 0xc6 } 204 if i == 200 { return 0xe8 } if i == 201 { return 0xdd } if i == 202 { return 0x74 } if i == 203 { return 0x1f } 205 if i == 204 { return 0x4b } if i == 205 { return 0xbd } if i == 206 { return 0x8b } if i == 207 { return 0x8a } 206 if i == 208 { return 0x70 } if i == 209 { return 0x3e } if i == 210 { return 0xb5 } if i == 211 { return 0x66 } 207 if i == 212 { return 0x48 } if i == 213 { return 0x03 } if i == 214 { return 0xf6 } if i == 215 { return 0x0e } 208 if i == 216 { return 0x61 } if i == 217 { return 0x35 } if i == 218 { return 0x57 } if i == 219 { return 0xb9 } 209 if i == 220 { return 0x86 } if i == 221 { return 0xc1 } if i == 222 { return 0x1d } if i == 223 { return 0x9e } 210 if i == 224 { return 0xe1 } if i == 225 { return 0xf8 } if i == 226 { return 0x98 } if i == 227 { return 0x11 } 211 if i == 228 { return 0x69 } if i == 229 { return 0xd9 } if i == 230 { return 0x8e } if i == 231 { return 0x94 } 212 if i == 232 { return 0x9b } if i == 233 { return 0x1e } if i == 234 { return 0x87 } if i == 235 { return 0xe9 } 213 if i == 236 { return 0xce } if i == 237 { return 0x55 } if i == 238 { return 0x28 } if i == 239 { return 0xdf } 214 if i == 240 { return 0x8c } if i == 241 { return 0xa1 } if i == 242 { return 0x89 } if i == 243 { return 0x0d } 215 if i == 244 { return 0xbf } if i == 245 { return 0xe6 } if i == 246 { return 0x42 } if i == 247 { return 0x68 } 216 if i == 248 { return 0x41 } if i == 249 { return 0x99 } if i == 250 { return 0x2d } if i == 251 { return 0x0f } 217 if i == 252 { return 0xb0 } if i == 253 { return 0x54 } if i == 254 { return 0xbb } if i == 255 { return 0x16 } 218 return 0 219} 220 221// ---- Rcon (FIPS 197 §5.2 Figure 11) --------------------------------- 222// 223// Round constant for KeyExpansion. Rcon[i] = ( x^(i-1) , 0, 0, 0 ) in 224// GF(2^8) with x = 0x02. We only need indices 1..10 for AES-128. 225 226func _aes_rcon(i: i64) -> i64 { 227 if i == 1 { return 0x01 } 228 if i == 2 { return 0x02 } 229 if i == 3 { return 0x04 } 230 if i == 4 { return 0x08 } 231 if i == 5 { return 0x10 } 232 if i == 6 { return 0x20 } 233 if i == 7 { return 0x40 } 234 if i == 8 { return 0x80 } 235 if i == 9 { return 0x1b } 236 if i == 10 { return 0x36 } 237 return 0 238} 239 240// ---- GF(2^8) xtime: multiply by x (0x02) --------------------------- 241// 242// FIPS 197 §4.2.1: xtime(b) = (b << 1) ^ (0x1b if MSB else 0). Used 243// to build MixColumns. 244 245func _aes_xtime(b: i64) -> i64 { 246 let shifted: i64 = (b << 1) & 0xff 247 if (b & 0x80) != 0 { 248 return shifted ^ 0x1b 249 } 250 return shifted 251} 252 253// ---- KeyExpansion (FIPS 197 §5.2) --------------------------------- 254// 255// Expands 16-byte key into 176-byte schedule (11 round keys). 256// `out` must point to >= 176 writable bytes. 257 258func aes128_expand_key(key: *u8, out: *u8) -> i64 { 259 var i: i64 = 0 260 while i < AES_KEY { out[i] = key[i]; i = i + 1 } 261 262 var w: i64 = 4 263 while w < 44 { 264 // temp = word[w-1] (4 bytes) 265 let off: i64 = w * 4 266 var t0: i64 = out[off - 4] as i64 267 var t1: i64 = out[off - 3] as i64 268 var t2: i64 = out[off - 2] as i64 269 var t3: i64 = out[off - 1] as i64 270 t0 = t0 & 0xff 271 t1 = t1 & 0xff 272 t2 = t2 & 0xff 273 t3 = t3 & 0xff 274 275 if (w & 3) == 0 { 276 // RotWord: (t0,t1,t2,t3) -> (t1,t2,t3,t0) 277 let r0: i64 = t1 278 let r1: i64 = t2 279 let r2: i64 = t3 280 let r3: i64 = t0 281 // SubWord 282 let s0: i64 = _aes_sbox(r0) 283 let s1: i64 = _aes_sbox(r1) 284 let s2: i64 = _aes_sbox(r2) 285 let s3: i64 = _aes_sbox(r3) 286 // XOR Rcon on byte 0 287 let rc: i64 = _aes_rcon(w / 4) 288 t0 = s0 ^ rc 289 t1 = s1 290 t2 = s2 291 t3 = s3 292 } 293 294 // word[w] = word[w-4] XOR temp 295 let p0: i64 = out[off - 16] as i64 296 let p1: i64 = out[off - 15] as i64 297 let p2: i64 = out[off - 14] as i64 298 let p3: i64 = out[off - 13] as i64 299 out[off + 0] = (p0 ^ t0) & 0xff 300 out[off + 1] = (p1 ^ t1) & 0xff 301 out[off + 2] = (p2 ^ t2) & 0xff 302 out[off + 3] = (p3 ^ t3) & 0xff 303 304 w = w + 1 305 } 306 return 0 307} 308 309// ---- SubBytes (FIPS 197 §5.1.1) ----------------------------------- 310 311func _aes_sub_bytes(state: *u8) -> i64 { 312 var i: i64 = 0 313 while i < AES_BLOCK { 314 let b: i64 = state[i] as i64 315 state[i] = _aes_sbox(b & 0xff) & 0xff 316 i = i + 1 317 } 318 return 0 319} 320 321// ---- ShiftRows (FIPS 197 §5.1.2) ---------------------------------- 322// 323// state laid out column-major: s[r,c] = state[r + 4*c]. 324// Row r shifts left by r bytes. 325 326func _aes_shift_rows(state: *u8) -> i64 { 327 // Row 1: shift by 1 328 let r1_0: i64 = state[1] as i64 329 state[1] = state[5] 330 state[5] = state[9] 331 state[9] = state[13] 332 state[13] = r1_0 & 0xff 333 // Row 2: shift by 2 334 let r2_0: i64 = state[2] as i64 335 let r2_1: i64 = state[6] as i64 336 state[2] = state[10] 337 state[6] = state[14] 338 state[10] = r2_0 & 0xff 339 state[14] = r2_1 & 0xff 340 // Row 3: shift by 3 341 let r3_0: i64 = state[3] as i64 342 let r3_1: i64 = state[7] as i64 343 let r3_2: i64 = state[11] as i64 344 state[3] = state[15] 345 state[7] = r3_0 & 0xff 346 state[11] = r3_1 & 0xff 347 state[15] = r3_2 & 0xff 348 return 0 349} 350 351// ---- MixColumns (FIPS 197 §5.1.3) -------------------------------- 352// 353// Each column is multiplied by the polynomial 0x02 x^3 + 0x01 x^2 + 354// 0x01 x + 0x03 in GF(2^8)[x]/(x^4+1). Equivalent matrix form per 355// §5.1.3: 356// s'_0 = 2*s_0 ^ 3*s_1 ^ s_2 ^ s_3 357// s'_1 = s_0 ^ 2*s_1 ^ 3*s_2 ^ s_3 358// s'_2 = s_0 ^ s_1 ^ 2*s_2 ^ 3*s_3 359// s'_3 = 3*s_0 ^ s_1 ^ s_2 ^ 2*s_3 360// 361// Use 2*x = xtime(x), 3*x = xtime(x) ^ x. 362 363func _aes_mix_columns(state: *u8) -> i64 { 364 var c: i64 = 0 365 while c < 4 { 366 let off: i64 = c * 4 367 let s0: i64 = state[off + 0] as i64 368 let s1: i64 = state[off + 1] as i64 369 let s2: i64 = state[off + 2] as i64 370 let s3: i64 = state[off + 3] as i64 371 let s0m: i64 = s0 & 0xff 372 let s1m: i64 = s1 & 0xff 373 let s2m: i64 = s2 & 0xff 374 let s3m: i64 = s3 & 0xff 375 let t0: i64 = _aes_xtime(s0m) ^ (_aes_xtime(s1m) ^ s1m) ^ s2m ^ s3m 376 let t1: i64 = s0m ^ _aes_xtime(s1m) ^ (_aes_xtime(s2m) ^ s2m) ^ s3m 377 let t2: i64 = s0m ^ s1m ^ _aes_xtime(s2m) ^ (_aes_xtime(s3m) ^ s3m) 378 let t3: i64 = (_aes_xtime(s0m) ^ s0m) ^ s1m ^ s2m ^ _aes_xtime(s3m) 379 state[off + 0] = t0 & 0xff 380 state[off + 1] = t1 & 0xff 381 state[off + 2] = t2 & 0xff 382 state[off + 3] = t3 & 0xff 383 c = c + 1 384 } 385 return 0 386} 387 388// ---- AddRoundKey (FIPS 197 §5.1.4) ------------------------------- 389 390func _aes_add_round_key(state: *u8, sched: *u8, round: i64) -> i64 { 391 let off: i64 = round * 16 392 var i: i64 = 0 393 while i < AES_BLOCK { 394 let s: i64 = state[i] as i64 395 let k: i64 = sched[off + i] as i64 396 state[i] = (s ^ k) & 0xff 397 i = i + 1 398 } 399 return 0 400} 401 402// ---- Public encrypt (FIPS 197 §5.1) ------------------------------ 403// 404// `in_block`: 16 bytes plaintext. 405// `out_block`: 16 bytes ciphertext (may alias in_block). 406// `sched`: 176-byte expanded key from aes128_expand_key. 407 408// Software AES-128 block (FIPS-197 reference; kept for cross-checks + non-AES-NI builds). 409func aes128_encrypt_block_sw(in_block: *u8, sched: *u8, out_block: *u8) -> i64 { 410 var i: i64 = 0 411 while i < AES_BLOCK { out_block[i] = in_block[i]; i = i + 1 } 412 _aes_add_round_key(out_block, sched, 0) 413 var r: i64 = 1 414 while r < AES_ROUNDS { 415 _aes_sub_bytes(out_block) 416 _aes_shift_rows(out_block) 417 _aes_mix_columns(out_block) 418 _aes_add_round_key(out_block, sched, r) 419 r = r + 1 420 } 421 _aes_sub_bytes(out_block) 422 _aes_shift_rows(out_block) 423 _aes_add_round_key(out_block, sched, AES_ROUNDS) 424 return 0 425} 426 427// Production AES-128 block: HARDWARE AES-NI (~189x the software path; FIPS-197 KAT + 428// per-block cross-check verified). Requires the AES-NI-capable nx_cc/nxasm toolchain. 429func aes128_encrypt_block(in_block: *u8, sched: *u8, out_block: *u8) -> i64 { 430 var i: i64 = 0 431 while i < AES_BLOCK { out_block[i] = in_block[i]; i = i + 1 } 432 let _r: i64 = __aes128_enc_block(out_block, sched) 433 return 0 434} 435 436// ---- Inverse S-box (FIPS 197 §5.1.1 Figure 14) --------------------- 437 438func _aes_inv_sbox(i: i64) -> i64 { 439 if i == 0 { return 0x52 } if i == 1 { return 0x09 } if i == 2 { return 0x6a } if i == 3 { return 0xd5 } 440 if i == 4 { return 0x30 } if i == 5 { return 0x36 } if i == 6 { return 0xa5 } if i == 7 { return 0x38 } 441 if i == 8 { return 0xbf } if i == 9 { return 0x40 } if i == 10 { return 0xa3 } if i == 11 { return 0x9e } 442 if i == 12 { return 0x81 } if i == 13 { return 0xf3 } if i == 14 { return 0xd7 } if i == 15 { return 0xfb } 443 if i == 16 { return 0x7c } if i == 17 { return 0xe3 } if i == 18 { return 0x39 } if i == 19 { return 0x82 } 444 if i == 20 { return 0x9b } if i == 21 { return 0x2f } if i == 22 { return 0xff } if i == 23 { return 0x87 } 445 if i == 24 { return 0x34 } if i == 25 { return 0x8e } if i == 26 { return 0x43 } if i == 27 { return 0x44 } 446 if i == 28 { return 0xc4 } if i == 29 { return 0xde } if i == 30 { return 0xe9 } if i == 31 { return 0xcb } 447 if i == 32 { return 0x54 } if i == 33 { return 0x7b } if i == 34 { return 0x94 } if i == 35 { return 0x32 } 448 if i == 36 { return 0xa6 } if i == 37 { return 0xc2 } if i == 38 { return 0x23 } if i == 39 { return 0x3d } 449 if i == 40 { return 0xee } if i == 41 { return 0x4c } if i == 42 { return 0x95 } if i == 43 { return 0x0b } 450 if i == 44 { return 0x42 } if i == 45 { return 0xfa } if i == 46 { return 0xc3 } if i == 47 { return 0x4e } 451 if i == 48 { return 0x08 } if i == 49 { return 0x2e } if i == 50 { return 0xa1 } if i == 51 { return 0x66 } 452 if i == 52 { return 0x28 } if i == 53 { return 0xd9 } if i == 54 { return 0x24 } if i == 55 { return 0xb2 } 453 if i == 56 { return 0x76 } if i == 57 { return 0x5b } if i == 58 { return 0xa2 } if i == 59 { return 0x49 } 454 if i == 60 { return 0x6d } if i == 61 { return 0x8b } if i == 62 { return 0xd1 } if i == 63 { return 0x25 } 455 if i == 64 { return 0x72 } if i == 65 { return 0xf8 } if i == 66 { return 0xf6 } if i == 67 { return 0x64 } 456 if i == 68 { return 0x86 } if i == 69 { return 0x68 } if i == 70 { return 0x98 } if i == 71 { return 0x16 } 457 if i == 72 { return 0xd4 } if i == 73 { return 0xa4 } if i == 74 { return 0x5c } if i == 75 { return 0xcc } 458 if i == 76 { return 0x5d } if i == 77 { return 0x65 } if i == 78 { return 0xb6 } if i == 79 { return 0x92 } 459 if i == 80 { return 0x6c } if i == 81 { return 0x70 } if i == 82 { return 0x48 } if i == 83 { return 0x50 } 460 if i == 84 { return 0xfd } if i == 85 { return 0xed } if i == 86 { return 0xb9 } if i == 87 { return 0xda } 461 if i == 88 { return 0x5e } if i == 89 { return 0x15 } if i == 90 { return 0x46 } if i == 91 { return 0x57 } 462 if i == 92 { return 0xa7 } if i == 93 { return 0x8d } if i == 94 { return 0x9d } if i == 95 { return 0x84 } 463 if i == 96 { return 0x90 } if i == 97 { return 0xd8 } if i == 98 { return 0xab } if i == 99 { return 0x00 } 464 if i == 100 { return 0x8c } if i == 101 { return 0xbc } if i == 102 { return 0xd3 } if i == 103 { return 0x0a } 465 if i == 104 { return 0xf7 } if i == 105 { return 0xe4 } if i == 106 { return 0x58 } if i == 107 { return 0x05 } 466 if i == 108 { return 0xb8 } if i == 109 { return 0xb3 } if i == 110 { return 0x45 } if i == 111 { return 0x06 } 467 if i == 112 { return 0xd0 } if i == 113 { return 0x2c } if i == 114 { return 0x1e } if i == 115 { return 0x8f } 468 if i == 116 { return 0xca } if i == 117 { return 0x3f } if i == 118 { return 0x0f } if i == 119 { return 0x02 } 469 if i == 120 { return 0xc1 } if i == 121 { return 0xaf } if i == 122 { return 0xbd } if i == 123 { return 0x03 } 470 if i == 124 { return 0x01 } if i == 125 { return 0x13 } if i == 126 { return 0x8a } if i == 127 { return 0x6b } 471 if i == 128 { return 0x3a } if i == 129 { return 0x91 } if i == 130 { return 0x11 } if i == 131 { return 0x41 } 472 if i == 132 { return 0x4f } if i == 133 { return 0x67 } if i == 134 { return 0xdc } if i == 135 { return 0xea } 473 if i == 136 { return 0x97 } if i == 137 { return 0xf2 } if i == 138 { return 0xcf } if i == 139 { return 0xce } 474 if i == 140 { return 0xf0 } if i == 141 { return 0xb4 } if i == 142 { return 0xe6 } if i == 143 { return 0x73 } 475 if i == 144 { return 0x96 } if i == 145 { return 0xac } if i == 146 { return 0x74 } if i == 147 { return 0x22 } 476 if i == 148 { return 0xe7 } if i == 149 { return 0xad } if i == 150 { return 0x35 } if i == 151 { return 0x85 } 477 if i == 152 { return 0xe2 } if i == 153 { return 0xf9 } if i == 154 { return 0x37 } if i == 155 { return 0xe8 } 478 if i == 156 { return 0x1c } if i == 157 { return 0x75 } if i == 158 { return 0xdf } if i == 159 { return 0x6e } 479 if i == 160 { return 0x47 } if i == 161 { return 0xf1 } if i == 162 { return 0x1a } if i == 163 { return 0x71 } 480 if i == 164 { return 0x1d } if i == 165 { return 0x29 } if i == 166 { return 0xc5 } if i == 167 { return 0x89 } 481 if i == 168 { return 0x6f } if i == 169 { return 0xb7 } if i == 170 { return 0x62 } if i == 171 { return 0x0e } 482 if i == 172 { return 0xaa } if i == 173 { return 0x18 } if i == 174 { return 0xbe } if i == 175 { return 0x1b } 483 if i == 176 { return 0xfc } if i == 177 { return 0x56 } if i == 178 { return 0x3e } if i == 179 { return 0x4b } 484 if i == 180 { return 0xc6 } if i == 181 { return 0xd2 } if i == 182 { return 0x79 } if i == 183 { return 0x20 } 485 if i == 184 { return 0x9a } if i == 185 { return 0xdb } if i == 186 { return 0xc0 } if i == 187 { return 0xfe } 486 if i == 188 { return 0x78 } if i == 189 { return 0xcd } if i == 190 { return 0x5a } if i == 191 { return 0xf4 } 487 if i == 192 { return 0x1f } if i == 193 { return 0xdd } if i == 194 { return 0xa8 } if i == 195 { return 0x33 } 488 if i == 196 { return 0x88 } if i == 197 { return 0x07 } if i == 198 { return 0xc7 } if i == 199 { return 0x31 } 489 if i == 200 { return 0xb1 } if i == 201 { return 0x12 } if i == 202 { return 0x10 } if i == 203 { return 0x59 } 490 if i == 204 { return 0x27 } if i == 205 { return 0x80 } if i == 206 { return 0xec } if i == 207 { return 0x5f } 491 if i == 208 { return 0x60 } if i == 209 { return 0x51 } if i == 210 { return 0x7f } if i == 211 { return 0xa9 } 492 if i == 212 { return 0x19 } if i == 213 { return 0xb5 } if i == 214 { return 0x4a } if i == 215 { return 0x0d } 493 if i == 216 { return 0x2d } if i == 217 { return 0xe5 } if i == 218 { return 0x7a } if i == 219 { return 0x9f } 494 if i == 220 { return 0x93 } if i == 221 { return 0xc9 } if i == 222 { return 0x9c } if i == 223 { return 0xef } 495 if i == 224 { return 0xa0 } if i == 225 { return 0xe0 } if i == 226 { return 0x3b } if i == 227 { return 0x4d } 496 if i == 228 { return 0xae } if i == 229 { return 0x2a } if i == 230 { return 0xf5 } if i == 231 { return 0xb0 } 497 if i == 232 { return 0xc8 } if i == 233 { return 0xeb } if i == 234 { return 0xbb } if i == 235 { return 0x3c } 498 if i == 236 { return 0x83 } if i == 237 { return 0x53 } if i == 238 { return 0x99 } if i == 239 { return 0x61 } 499 if i == 240 { return 0x17 } if i == 241 { return 0x2b } if i == 242 { return 0x04 } if i == 243 { return 0x7e } 500 if i == 244 { return 0xba } if i == 245 { return 0x77 } if i == 246 { return 0xd6 } if i == 247 { return 0x26 } 501 if i == 248 { return 0xe1 } if i == 249 { return 0x69 } if i == 250 { return 0x14 } if i == 251 { return 0x63 } 502 if i == 252 { return 0x55 } if i == 253 { return 0x21 } if i == 254 { return 0x0c } if i == 255 { return 0x7d } 503 return 0 504} 505 506// ---- InvSubBytes --------------------------------------------------- 507 508func _aes_inv_sub_bytes(state: *u8) -> i64 { 509 var i: i64 = 0 510 while i < AES_BLOCK { 511 let b: i64 = state[i] as i64 512 state[i] = _aes_inv_sbox(b & 0xff) & 0xff 513 i = i + 1 514 } 515 return 0 516} 517 518// ---- InvShiftRows -------------------------------------------------- 519// 520// Row r shifts RIGHT by r bytes (dual to ShiftRows). 521 522func _aes_inv_shift_rows(state: *u8) -> i64 { 523 // Row 1: shift right by 1 == shift left by 3 524 let r1_0: i64 = state[13] as i64 525 state[13] = state[9] 526 state[9] = state[5] 527 state[5] = state[1] 528 state[1] = r1_0 & 0xff 529 // Row 2: shift right by 2 == swap pairs 530 let r2_0: i64 = state[2] as i64 531 let r2_1: i64 = state[6] as i64 532 state[2] = state[10] 533 state[6] = state[14] 534 state[10] = r2_0 & 0xff 535 state[14] = r2_1 & 0xff 536 // Row 3: inverse of shift-left-by-3 == shift left by 1 537 let r3_0: i64 = state[3] as i64 538 state[3] = state[7] 539 state[7] = state[11] 540 state[11] = state[15] 541 state[15] = r3_0 & 0xff 542 return 0 543} 544 545// ---- InvMixColumns (FIPS 197 §5.3.3) ------------------------------- 546// 547// Each column multiplied by inverse polynomial (0x0e, 0x09, 0x0d, 0x0b) 548// in GF(2^8). We compute multiplications via repeated xtime: 549// 2*x = xtime(x) 550// 4*x = xtime(xtime(x)) 551// 8*x = xtime(xtime(xtime(x))) 552// 9*x = 8*x ^ x 553// 11*x = 8*x ^ 2*x ^ x 554// 13*x = 8*x ^ 4*x ^ x 555// 14*x = 8*x ^ 4*x ^ 2*x 556 557func _aes_inv_mix_columns(state: *u8) -> i64 { 558 var c: i64 = 0 559 while c < 4 { 560 let off: i64 = c * 4 561 let s0: i64 = (state[off + 0] as i64) & 0xff 562 let s1: i64 = (state[off + 1] as i64) & 0xff 563 let s2: i64 = (state[off + 2] as i64) & 0xff 564 let s3: i64 = (state[off + 3] as i64) & 0xff 565 566 let x2_0: i64 = _aes_xtime(s0) 567 let x4_0: i64 = _aes_xtime(x2_0) 568 let x8_0: i64 = _aes_xtime(x4_0) 569 let x2_1: i64 = _aes_xtime(s1) 570 let x4_1: i64 = _aes_xtime(x2_1) 571 let x8_1: i64 = _aes_xtime(x4_1) 572 let x2_2: i64 = _aes_xtime(s2) 573 let x4_2: i64 = _aes_xtime(x2_2) 574 let x8_2: i64 = _aes_xtime(x4_2) 575 let x2_3: i64 = _aes_xtime(s3) 576 let x4_3: i64 = _aes_xtime(x2_3) 577 let x8_3: i64 = _aes_xtime(x4_3) 578 579 // 14*x = 8*x ^ 4*x ^ 2*x 580 let m14_0: i64 = x8_0 ^ x4_0 ^ x2_0 581 let m14_1: i64 = x8_1 ^ x4_1 ^ x2_1 582 let m14_2: i64 = x8_2 ^ x4_2 ^ x2_2 583 let m14_3: i64 = x8_3 ^ x4_3 ^ x2_3 584 // 11*x = 8*x ^ 2*x ^ x 585 let m11_0: i64 = x8_0 ^ x2_0 ^ s0 586 let m11_1: i64 = x8_1 ^ x2_1 ^ s1 587 let m11_2: i64 = x8_2 ^ x2_2 ^ s2 588 let m11_3: i64 = x8_3 ^ x2_3 ^ s3 589 // 13*x = 8*x ^ 4*x ^ x 590 let m13_0: i64 = x8_0 ^ x4_0 ^ s0 591 let m13_1: i64 = x8_1 ^ x4_1 ^ s1 592 let m13_2: i64 = x8_2 ^ x4_2 ^ s2 593 let m13_3: i64 = x8_3 ^ x4_3 ^ s3 594 // 9*x = 8*x ^ x 595 let m9_0: i64 = x8_0 ^ s0 596 let m9_1: i64 = x8_1 ^ s1 597 let m9_2: i64 = x8_2 ^ s2 598 let m9_3: i64 = x8_3 ^ s3 599 600 // s'_0 = 14*s_0 ^ 11*s_1 ^ 13*s_2 ^ 9*s_3 601 // s'_1 = 9*s_0 ^ 14*s_1 ^ 11*s_2 ^ 13*s_3 602 // s'_2 = 13*s_0 ^ 9*s_1 ^ 14*s_2 ^ 11*s_3 603 // s'_3 = 11*s_0 ^ 13*s_1 ^ 9*s_2 ^ 14*s_3 604 let t0: i64 = m14_0 ^ m11_1 ^ m13_2 ^ m9_3 605 let t1: i64 = m9_0 ^ m14_1 ^ m11_2 ^ m13_3 606 let t2: i64 = m13_0 ^ m9_1 ^ m14_2 ^ m11_3 607 let t3: i64 = m11_0 ^ m13_1 ^ m9_2 ^ m14_3 608 609 state[off + 0] = t0 & 0xff 610 state[off + 1] = t1 & 0xff 611 state[off + 2] = t2 & 0xff 612 state[off + 3] = t3 & 0xff 613 c = c + 1 614 } 615 return 0 616} 617 618// ---- Public decrypt (FIPS 197 §5.3) ------------------------------- 619// 620// Run rounds in REVERSE order. Round structure: 621// AddRoundKey(state, round_keys[10]) 622// for r in 9..1: 623// InvShiftRows 624// InvSubBytes 625// AddRoundKey(state, round_keys[r]) 626// InvMixColumns 627// InvShiftRows 628// InvSubBytes 629// AddRoundKey(state, round_keys[0]) 630 631func aes128_decrypt_block(in_block: *u8, sched: *u8, out_block: *u8) -> i64 { 632 var i: i64 = 0 633 while i < AES_BLOCK { out_block[i] = in_block[i]; i = i + 1 } 634 _aes_add_round_key(out_block, sched, AES_ROUNDS) 635 var r: i64 = AES_ROUNDS - 1 636 while r > 0 { 637 _aes_inv_shift_rows(out_block) 638 _aes_inv_sub_bytes(out_block) 639 _aes_add_round_key(out_block, sched, r) 640 _aes_inv_mix_columns(out_block) 641 r = r - 1 642 } 643 _aes_inv_shift_rows(out_block) 644 _aes_inv_sub_bytes(out_block) 645 _aes_add_round_key(out_block, sched, 0) 646 return 0 647}