Path: blob/main/foundations/03-galois-fields-aes/rust/src/lib.rs
483 views
unlisted
//! # Module 03: Galois Fields and AES — Exercises1//!2//! All GF(256) arithmetic uses the AES irreducible polynomial:3//! `x⁸ + x⁴ + x³ + x + 1` (0x11B in hex).4//!5//! ## Progression6//! 1. `gf256_add` — Guided (it's XOR)7//! 2. `gf256_mul` — Scaffolded (Russian peasant + reduction)8//! 3. `gf256_inv` — Scaffolded (hint: use extended Euclidean or Fermat)9//! 4. `aes_sbox` — Scaffolded (compose inverse + affine)10//! 5. `aes_mix_column` — Independent1112/// The AES irreducible polynomial: x⁸ + x⁴ + x³ + x + 1.13const AES_POLY: u16 = 0x11B;1415/// Add two elements in GF(256). This is just XOR.16///17/// # Example18/// ```19/// # use galois_fields_aes::gf256_add;20/// assert_eq!(gf256_add(0x57, 0x83), 0xD4);21/// ```22pub fn gf256_add(a: u8, b: u8) -> u8 {23// In GF(2^n), addition is XOR. That's it!24todo!("XOR a and b")25}2627/// Multiply two elements in GF(256) using the AES irreducible polynomial.28///29/// Use the "Russian peasant" (shift-and-XOR) algorithm:30/// - Shift `a` left by 1 bit each iteration (multiply by x)31/// - If the shift overflows 8 bits, reduce by XORing with AES_POLY32/// - If the corresponding bit of `b` is set, XOR `a` into the result33pub fn gf256_mul(a: u8, b: u8) -> u8 {34// Hint: work with u16 to catch overflow past 8 bits.35// For each bit of b (from bit 0 to bit 7):36// if bit is set, result ^= a37// shift a left by 138// if a overflows 8 bits (bit 8 set), reduce: a ^= AES_POLY39todo!("GF(256) multiplication with polynomial reduction")40}4142/// Compute the multiplicative inverse of `a` in GF(256).43///44/// Returns 0 if `a == 0` (by convention for AES S-box).45///46/// Hint: By Fermat's little theorem in GF(2^8), a^(254) = a^(-1).47/// Alternatively, implement the extended Euclidean algorithm for polynomials.48pub fn gf256_inv(a: u8) -> u8 {49todo!("Multiplicative inverse in GF(256)")50}5152/// Compute the AES S-box value for a single byte.53///54/// S-box(a) = affine_transform(gf256_inv(a))55///56/// The affine transform over GF(2) is:57/// b[i] = inv[i] ^ inv[(i+4)%8] ^ inv[(i+5)%8] ^ inv[(i+6)%8] ^ inv[(i+7)%8] ^ c[i]58/// where c = 0x63.59pub fn aes_sbox(byte: u8) -> u8 {60todo!("AES S-box: inverse then affine transform")61}6263/// Apply the AES MixColumns transformation to a single column.64///65/// Multiplies the column vector by the fixed matrix in GF(256):66/// ```text67/// | 2 3 1 1 | | c0 |68/// | 1 2 3 1 | × | c1 |69/// | 1 1 2 3 | | c2 |70/// | 3 1 1 2 | | c3 |71/// ```72pub fn aes_mix_column(col: [u8; 4]) -> [u8; 4] {73todo!("MixColumns matrix multiplication in GF(256)")74}7576#[cfg(test)]77mod tests {78use super::*;7980#[test]81#[ignore]82fn test_gf256_add() {83assert_eq!(gf256_add(0x57, 0x83), 0xD4);84assert_eq!(gf256_add(0xFF, 0xFF), 0x00); // self-inverse85}8687#[test]88#[ignore]89fn test_gf256_mul_basic() {90assert_eq!(gf256_mul(0x57, 0x83), 0xC1);91}9293#[test]94#[ignore]95fn test_gf256_mul_identity() {96assert_eq!(gf256_mul(0x53, 0x01), 0x53); // multiply by 197assert_eq!(gf256_mul(0x00, 0x53), 0x00); // multiply by 098}99100#[test]101#[ignore]102fn test_gf256_inv() {103// inv(0x53) × 0x53 should equal 1104let inv = gf256_inv(0x53);105assert_eq!(gf256_mul(inv, 0x53), 0x01);106assert_eq!(gf256_inv(0x00), 0x00); // special case107}108109#[test]110#[ignore]111fn test_aes_sbox_known_values() {112assert_eq!(aes_sbox(0x00), 0x63);113assert_eq!(aes_sbox(0x01), 0x7C);114assert_eq!(aes_sbox(0x53), 0xED);115}116117#[test]118#[ignore]119fn test_aes_mix_column() {120// FIPS 197 test vector121let input = [0xDB, 0x13, 0x53, 0x45];122let expected = [0x8E, 0x4D, 0xA1, 0xBC];123assert_eq!(aes_mix_column(input), expected);124}125}126127128