/ test-server / ghash_test.rs
ghash_test.rs
1 // Standalone GHASH test - corrected implementation 2 // Run with: rustc ghash_test.rs -o ghash_test && ./ghash_test 3 4 use std::vec::Vec; 5 6 // ============= GHASH implementation (corrected for GCM bit ordering) ============= 7 8 /// GHASH implementation for GCM 9 /// GCM uses a "reflected" bit ordering where MSB of byte 0 is x^0 10 struct Ghash { 11 h: [u8; 16], 12 state: [u8; 16], 13 } 14 15 impl Ghash { 16 fn new(h: &[u8; 16]) -> Self { 17 Ghash { 18 h: *h, 19 state: [0u8; 16], 20 } 21 } 22 23 fn update(&mut self, block: &[u8; 16]) { 24 for i in 0..16 { 25 self.state[i] ^= block[i]; 26 } 27 self.state = Self::gf_mul(&self.state, &self.h); 28 } 29 30 /// GF(2^128) multiplication in GCM's reflected representation 31 fn gf_mul(x: &[u8; 16], y: &[u8; 16]) -> [u8; 16] { 32 let mut z = [0u8; 16]; 33 let mut v = *y; 34 35 for i in 0..128 { 36 let byte_idx = i / 8; 37 let bit_idx = 7 - (i % 8); 38 39 if (x[byte_idx] >> bit_idx) & 1 == 1 { 40 for j in 0..16 { 41 z[j] ^= v[j]; 42 } 43 } 44 45 let lsb = v[15] & 1; 46 47 // Right shift V by 1 bit 48 let mut carry = 0u8; 49 for j in 0..16 { 50 let new_carry = v[j] & 1; 51 v[j] = (v[j] >> 1) | (carry << 7); 52 carry = new_carry; 53 } 54 55 if lsb == 1 { 56 v[0] ^= 0xe1; 57 } 58 } 59 60 z 61 } 62 63 fn finalize(self) -> [u8; 16] { 64 self.state 65 } 66 } 67 68 // ============= Simple AES for testing ============= 69 70 struct Aes128 { 71 round_keys: [[u8; 16]; 11], 72 } 73 74 impl Aes128 { 75 fn new(key: &[u8; 16]) -> Self { 76 let mut round_keys = [[0u8; 16]; 11]; 77 round_keys[0].copy_from_slice(key); 78 79 let mut temp = [0u8; 4]; 80 for i in 1..11 { 81 temp.copy_from_slice(&round_keys[i-1][12..16]); 82 83 let t = temp[0]; 84 temp[0] = temp[1]; 85 temp[1] = temp[2]; 86 temp[2] = temp[3]; 87 temp[3] = t; 88 89 for b in temp.iter_mut() { 90 *b = SBOX[*b as usize]; 91 } 92 93 temp[0] ^= RCON[i]; 94 95 for j in 0..4 { 96 round_keys[i][j] = round_keys[i-1][j] ^ temp[j]; 97 } 98 for j in 1..4 { 99 for k in 0..4 { 100 round_keys[i][j*4 + k] = round_keys[i-1][j*4 + k] ^ round_keys[i][(j-1)*4 + k]; 101 } 102 } 103 } 104 105 Self { round_keys } 106 } 107 108 fn encrypt_block(&self, block: &mut [u8; 16]) { 109 for i in 0..16 { 110 block[i] ^= self.round_keys[0][i]; 111 } 112 113 for round in 1..10 { 114 for b in block.iter_mut() { 115 *b = SBOX[*b as usize]; 116 } 117 118 let temp = *block; 119 block[1] = temp[5]; 120 block[5] = temp[9]; 121 block[9] = temp[13]; 122 block[13] = temp[1]; 123 block[2] = temp[10]; 124 block[6] = temp[14]; 125 block[10] = temp[2]; 126 block[14] = temp[6]; 127 block[3] = temp[15]; 128 block[7] = temp[3]; 129 block[11] = temp[7]; 130 block[15] = temp[11]; 131 132 for i in 0..4 { 133 let col = i * 4; 134 let a = block[col]; 135 let b = block[col + 1]; 136 let c = block[col + 2]; 137 let d = block[col + 3]; 138 139 block[col] = gmul(a, 2) ^ gmul(b, 3) ^ c ^ d; 140 block[col + 1] = a ^ gmul(b, 2) ^ gmul(c, 3) ^ d; 141 block[col + 2] = a ^ b ^ gmul(c, 2) ^ gmul(d, 3); 142 block[col + 3] = gmul(a, 3) ^ b ^ c ^ gmul(d, 2); 143 } 144 145 for i in 0..16 { 146 block[i] ^= self.round_keys[round][i]; 147 } 148 } 149 150 for b in block.iter_mut() { 151 *b = SBOX[*b as usize]; 152 } 153 154 let temp = *block; 155 block[1] = temp[5]; 156 block[5] = temp[9]; 157 block[9] = temp[13]; 158 block[13] = temp[1]; 159 block[2] = temp[10]; 160 block[6] = temp[14]; 161 block[10] = temp[2]; 162 block[14] = temp[6]; 163 block[3] = temp[15]; 164 block[7] = temp[3]; 165 block[11] = temp[7]; 166 block[15] = temp[11]; 167 168 for i in 0..16 { 169 block[i] ^= self.round_keys[10][i]; 170 } 171 } 172 } 173 174 fn gmul(a: u8, b: u8) -> u8 { 175 let mut p = 0u8; 176 let mut a = a; 177 let mut b = b; 178 for _ in 0..8 { 179 if b & 1 != 0 { 180 p ^= a; 181 } 182 let hi_bit_set = a & 0x80 != 0; 183 a <<= 1; 184 if hi_bit_set { 185 a ^= 0x1b; 186 } 187 b >>= 1; 188 } 189 p 190 } 191 192 static SBOX: [u8; 256] = [ 193 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 194 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 195 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 196 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 197 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 198 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 199 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 200 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 201 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 202 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 203 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 204 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 205 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 206 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 207 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 208 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, 209 ]; 210 211 static RCON: [u8; 11] = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; 212 213 // ============= AES-GCM ============= 214 215 struct AesGcm { 216 cipher: Aes128, 217 h: [u8; 16], 218 } 219 220 impl AesGcm { 221 fn new(key: &[u8; 16]) -> Self { 222 let cipher = Aes128::new(key); 223 let mut h = [0u8; 16]; 224 cipher.encrypt_block(&mut h); 225 AesGcm { cipher, h } 226 } 227 228 fn encrypt(&self, nonce: &[u8; 12], aad: &[u8], plaintext: &[u8]) -> Vec<u8> { 229 let mut ghash = Ghash::new(&self.h); 230 231 // Process AAD 232 self.ghash_update_padded(&mut ghash, aad); 233 234 // Encrypt plaintext 235 let mut ciphertext = Vec::with_capacity(plaintext.len() + 16); 236 let mut ctr = 2u32; 237 238 for chunk in plaintext.chunks(16) { 239 let mut counter_block = [0u8; 16]; 240 counter_block[0..12].copy_from_slice(nonce); 241 counter_block[12..16].copy_from_slice(&ctr.to_be_bytes()); 242 243 self.cipher.encrypt_block(&mut counter_block); 244 245 let mut ct_block = [0u8; 16]; 246 for (i, &b) in chunk.iter().enumerate() { 247 ct_block[i] = counter_block[i] ^ b; 248 } 249 ciphertext.extend_from_slice(&ct_block[..chunk.len()]); 250 ctr += 1; 251 } 252 253 // Process ciphertext through GHASH 254 self.ghash_update_padded(&mut ghash, &ciphertext); 255 256 // Append lengths block 257 let aad_bits = (aad.len() as u64) * 8; 258 let ct_bits = (ciphertext.len() as u64) * 8; 259 let mut len_block = [0u8; 16]; 260 len_block[0..8].copy_from_slice(&aad_bits.to_be_bytes()); 261 len_block[8..16].copy_from_slice(&ct_bits.to_be_bytes()); 262 ghash.update(&len_block); 263 264 // Generate tag 265 let mut tag_mask = [0u8; 16]; 266 tag_mask[0..12].copy_from_slice(nonce); 267 tag_mask[12..16].copy_from_slice(&1u32.to_be_bytes()); 268 self.cipher.encrypt_block(&mut tag_mask); 269 270 let ghash_result = ghash.finalize(); 271 let mut tag = [0u8; 16]; 272 for i in 0..16 { 273 tag[i] = ghash_result[i] ^ tag_mask[i]; 274 } 275 276 ciphertext.extend_from_slice(&tag); 277 ciphertext 278 } 279 280 fn ghash_update_padded(&self, ghash: &mut Ghash, data: &[u8]) { 281 for chunk in data.chunks(16) { 282 let mut block = [0u8; 16]; 283 block[..chunk.len()].copy_from_slice(chunk); 284 ghash.update(&block); 285 } 286 } 287 } 288 289 // ============= Tests ============= 290 291 fn hex_decode(s: &str) -> Vec<u8> { 292 (0..s.len()) 293 .step_by(2) 294 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()) 295 .collect() 296 } 297 298 fn hex_encode(data: &[u8]) -> String { 299 data.iter().map(|b| format!("{:02x}", b)).collect() 300 } 301 302 fn main() { 303 println!("=== GHASH/AES-GCM Test Suite (Corrected) ===\n"); 304 305 let mut passed = 0; 306 let mut failed = 0; 307 308 // Test 1: Empty plaintext, empty AAD 309 print!("TC1: Empty PT, Empty AAD... "); 310 { 311 let key = [0u8; 16]; 312 let nonce = [0u8; 12]; 313 let gcm = AesGcm::new(&key); 314 let result = gcm.encrypt(&nonce, &[], &[]); 315 let expected_tag = hex_decode("58e2fccefa7e3061367f1d57a4e7455a"); 316 317 if &result[..] == &expected_tag[..] { 318 println!("PASS"); 319 passed += 1; 320 } else { 321 println!("FAIL"); 322 println!(" Got: {}", hex_encode(&result)); 323 println!(" Expected: {}", hex_encode(&expected_tag)); 324 failed += 1; 325 } 326 } 327 328 // Test 2: 16-byte plaintext, empty AAD 329 print!("TC2: 16B PT, Empty AAD... "); 330 { 331 let key = [0u8; 16]; 332 let nonce = [0u8; 12]; 333 let plaintext = [0u8; 16]; 334 let gcm = AesGcm::new(&key); 335 let result = gcm.encrypt(&nonce, &[], &plaintext); 336 337 let expected_ct = hex_decode("0388dace60b6a392f328c2b971b2fe78"); 338 let expected_tag = hex_decode("ab6e47d42cec13bdf53a67b21257bddf"); 339 340 let ct_ok = &result[..16] == &expected_ct[..]; 341 let tag_ok = &result[16..] == &expected_tag[..]; 342 343 if ct_ok && tag_ok { 344 println!("PASS"); 345 passed += 1; 346 } else { 347 println!("FAIL"); 348 if !ct_ok { 349 println!(" CT Got: {}", hex_encode(&result[..16])); 350 println!(" CT Expected: {}", hex_encode(&expected_ct)); 351 } 352 if !tag_ok { 353 println!(" Tag Got: {}", hex_encode(&result[16..])); 354 println!(" Tag Expected: {}", hex_encode(&expected_tag)); 355 } 356 failed += 1; 357 } 358 } 359 360 // Test 3: 64-byte plaintext, empty AAD 361 print!("TC3: 64B PT, Empty AAD... "); 362 { 363 let key_bytes = hex_decode("feffe9928665731c6d6a8f9467308308"); 364 let mut key = [0u8; 16]; 365 key.copy_from_slice(&key_bytes); 366 367 let nonce_bytes = hex_decode("cafebabefacedbaddecaf888"); 368 let mut nonce = [0u8; 12]; 369 nonce.copy_from_slice(&nonce_bytes); 370 371 let plaintext = hex_decode( 372 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255" 373 ); 374 375 let gcm = AesGcm::new(&key); 376 let result = gcm.encrypt(&nonce, &[], &plaintext); 377 378 let expected_ct = hex_decode( 379 "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985" 380 ); 381 let expected_tag = hex_decode("4d5c2af327cd64a62cf35abd2ba6fab4"); 382 383 let ct_ok = &result[..64] == &expected_ct[..]; 384 let tag_ok = &result[64..] == &expected_tag[..]; 385 386 if ct_ok && tag_ok { 387 println!("PASS"); 388 passed += 1; 389 } else { 390 println!("FAIL"); 391 if !ct_ok { 392 println!(" CT Got: {}", hex_encode(&result[..64])); 393 println!(" CT Expected: {}", hex_encode(&expected_ct)); 394 } 395 if !tag_ok { 396 println!(" Tag Got: {}", hex_encode(&result[64..])); 397 println!(" Tag Expected: {}", hex_encode(&expected_tag)); 398 } 399 failed += 1; 400 } 401 } 402 403 // Test 4: 60-byte plaintext, 20-byte AAD 404 print!("TC4: 60B PT, 20B AAD... "); 405 { 406 let key_bytes = hex_decode("feffe9928665731c6d6a8f9467308308"); 407 let mut key = [0u8; 16]; 408 key.copy_from_slice(&key_bytes); 409 410 let nonce_bytes = hex_decode("cafebabefacedbaddecaf888"); 411 let mut nonce = [0u8; 12]; 412 nonce.copy_from_slice(&nonce_bytes); 413 414 let aad = hex_decode("feedfacedeadbeeffeedfacedeadbeefabaddad2"); 415 let plaintext = hex_decode( 416 "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39" 417 ); 418 419 let gcm = AesGcm::new(&key); 420 let result = gcm.encrypt(&nonce, &aad, &plaintext); 421 422 let expected_ct = hex_decode( 423 "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091" 424 ); 425 let expected_tag = hex_decode("5bc94fbc3221a5db94fae95ae7121a47"); 426 427 let ct_ok = &result[..60] == &expected_ct[..]; 428 let tag_ok = &result[60..] == &expected_tag[..]; 429 430 if ct_ok && tag_ok { 431 println!("PASS"); 432 passed += 1; 433 } else { 434 println!("FAIL"); 435 if !ct_ok { 436 println!(" CT Got: {}", hex_encode(&result[..60])); 437 println!(" CT Expected: {}", hex_encode(&expected_ct)); 438 } 439 if !tag_ok { 440 println!(" Tag Got: {}", hex_encode(&result[60..])); 441 println!(" Tag Expected: {}", hex_encode(&expected_tag)); 442 } 443 failed += 1; 444 } 445 } 446 447 // Test 5: QUIC-like 1157 bytes 448 print!("TC5: 1157B PT, 26B AAD (QUIC-like)... "); 449 { 450 let key_bytes = hex_decode("1f369613dd76d5467730efcbe3b1a22d"); 451 let mut key = [0u8; 16]; 452 key.copy_from_slice(&key_bytes); 453 454 let nonce_bytes = hex_decode("fa044b2f42a3fd3b46fb255c"); 455 let mut nonce = [0u8; 12]; 456 nonce.copy_from_slice(&nonce_bytes); 457 458 let aad = hex_decode("c3000000018394c8f03e51570800000000000000000000000000"); 459 460 let mut plaintext = vec![0u8; 1157]; 461 for (i, byte) in plaintext.iter_mut().enumerate() { 462 *byte = (i & 0xff) as u8; 463 } 464 465 let gcm = AesGcm::new(&key); 466 let result = gcm.encrypt(&nonce, &aad, &plaintext); 467 468 // From Go reference 469 let expected_ct_start = hex_decode("40b513d99e79a520cbaf459eca8579cc"); 470 let expected_tag = hex_decode("58acb131f857b7192188751449aa04bb"); 471 472 let ct_start_ok = &result[..16] == &expected_ct_start[..]; 473 let tag_ok = &result[1157..] == &expected_tag[..]; 474 475 if ct_start_ok && tag_ok { 476 println!("PASS"); 477 passed += 1; 478 } else { 479 println!("FAIL"); 480 if !ct_start_ok { 481 println!(" CT[0:16] Got: {}", hex_encode(&result[..16])); 482 println!(" CT[0:16] Expected: {}", hex_encode(&expected_ct_start)); 483 } 484 if !tag_ok { 485 println!(" Tag Got: {}", hex_encode(&result[1157..])); 486 println!(" Tag Expected: {}", hex_encode(&expected_tag)); 487 } 488 failed += 1; 489 } 490 } 491 492 println!("\n=== Results: {} passed, {} failed ===", passed, failed); 493 494 if failed > 0 { 495 std::process::exit(1); 496 } 497 }