From 8091bb33ed1a5a95073e6f1958fc5f5e51fb6b77 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Sun, 28 Apr 2024 14:43:12 +0200 Subject: [PATCH] reverse key schedule test --- src/aes.rs | 194 ++------------------------------------------------ src/main.rs | 44 +++++++----- src/square.rs | 179 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 206 deletions(-) create mode 100644 src/square.rs diff --git a/src/aes.rs b/src/aes.rs index 41569bb..81c495a 100644 --- a/src/aes.rs +++ b/src/aes.rs @@ -139,7 +139,7 @@ static INVERSE_SBOX: [[u8; 16]; 16] = [ ]; // for key schedule -static RC: [u8; 11] = [ +pub static RC: [u8; 11] = [ 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, ]; @@ -153,7 +153,7 @@ where a } -fn xor(bits1: &[u8; 4], bits2: &[u8; 4]) -> [u8; 4] { +pub fn xor(bits1: &[u8; 4], bits2: &[u8; 4]) -> [u8; 4] { let mut result = [0u8; 4]; for i in 0..4 { result[i] = bits1[i] ^ bits2[i]; @@ -161,7 +161,7 @@ fn xor(bits1: &[u8; 4], bits2: &[u8; 4]) -> [u8; 4] { result } -fn substitute_word(bytes: &[u8; 4]) -> [u8; 4] { +pub fn substitute_word(bytes: &[u8; 4]) -> [u8; 4] { let mut result = [0u8; 4]; for i in 0..4 { result[i] = substitute(bytes[i], true); @@ -184,7 +184,7 @@ fn substitute_state(state: &mut [[u8; 4]; 4]) { } } -fn inverse_substitute_state(state: &mut [[u8; 4]; 4]) { +pub fn inverse_substitute_state(state: &mut [[u8; 4]; 4]) { for row in state.iter_mut().take(4) { for item in row.iter_mut().take(4) { *item = substitute(*item, false); @@ -202,7 +202,7 @@ pub fn substitute(byte: u8, encryption: bool) -> u8 { } } -fn shift_word(bytes: &[u8; 4]) -> [u8; 4] { +pub fn shift_word(bytes: &[u8; 4]) -> [u8; 4] { let mut result = [0u8; 4]; for i in 0..4 { result[i] = bytes[(i + 1) % 4]; @@ -443,172 +443,6 @@ impl Aes { expanded_key } - - // pub fn reverse_key_schedule(round_key: &mut [u8; 16], aes_round: usize) -> [[u8; 4]; 4] { - // let mut round_keys = [[0u8; 4]; 4]; // Array to store the round keys - // let n = 4; - // - // // Extract the original key from the provided round key - // let mut original_key = [[0u8; 4]; 4]; - // for i in 0..16 { - // original_key[i / 4][i % 4] = round_key[i]; - // } - // - // // Reverse the key expansion process for the first 4 rounds - // for i in (0..16).rev() { - // if i % n == 0 { - // // Reverse the addition of the round constant - // let mut rcon = [0u8; 4]; - // rcon[0] = RC[i / n]; - // round_keys[i / n] = xor( - // &original_key[i / n], - // &inverse_shift_word(&inverse_substitute_word(&xor(&original_key[i / n], &rcon))), - // ); - // } else if i >= n { - // // Reverse the XOR operation - // round_keys[i / n] = xor(&original_key[i / n], &original_key[i / n - 1]); - // } - // } - // - // // Update the provided round key array with the round keys for the specified AES rounds - // for i in 0..16 { - // round_key[i] = round_keys[aes_round / n][i % n]; - // } - // - // round_keys - // } - pub fn reverse_key_schedule(round_key: &mut [u8; 16], aes_round: usize) -> [u8; 16] { - let mut rcon = [0u8; 4]; - // let n = 4; - // let indexes = [1,2,3,4]; - - for i in (0..aes_round).rev() { - rcon[0] = RC[i + 1]; - let a2: [u8; 4] = round_key[0..4].try_into().unwrap(); - let b2: [u8; 4] = round_key[4..8].try_into().unwrap(); - let c2: [u8; 4] = round_key[8..12].try_into().unwrap(); - let d2: [u8; 4] = round_key[12..16].try_into().unwrap(); - - let d1 = xor(&d2, &c2); - let c1 = xor(&c2, &b2); - let b1 = xor(&b2, &a2); - let a1 = xor(&xor(&a2, &shift_word(&substitute_word(&d1))), &rcon); - - round_key[0..4].copy_from_slice(&a1); - round_key[4..8].copy_from_slice(&b1); - round_key[8..12].copy_from_slice(&c1); - round_key[12..16].copy_from_slice(&d1); - } - *round_key - } - - pub fn aes_reduced(&key: &[u8; 16], &block: &[u8; 16]) -> [u8; 16] { - let nturn = 4; - let aescipher = Self::new(&key, &nturn); - aescipher.encrypt_block(&block) - } - pub fn aes_reduced_gen_texts(&key: &[u8; 16], &const_byte: &u8) -> [[u8; 16]; 256] { - let mut cleartext: [u8; 16]; - let mut ciphertexts: [[u8; 16]; 256] = [[0; 16]; 256]; - for i in 0..=255 { - cleartext = [ - i, const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, - const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, - const_byte, const_byte, - ]; - ciphertexts[i as usize] = Aes::aes_reduced(&key, &cleartext); - } - ciphertexts - } - pub fn decrypt_block_reduced_1_step(&self, block: &[u8; 16]) -> [u8; 16] { - let mut result = [0u8; 16]; - - let mut state = [[0u8; 4]; 4]; - for i in 0..16 { - state[i % 4][i / 4] = block[i]; - } - add_round_key(&mut state, &clone_into_array(&self.expanded_key[16..20])); - inverse_shift_rows(&mut state); - inverse_substitute_state(&mut state); - - for i in 0..4 { - for j in 0..4 { - result[4 * j + i] = state[i][j] - } - } - - result - } - pub fn guessroundkey(&texts: &[[u8; 16]; 256]) -> [Vec; 16] { - let mut key: [Vec; 16] = Default::default(); - let mut s: u8; - let mut matrix: [[u8; 4]; 4] = [[0; 4]; 4]; - let mut array: [u8; 16] = [0; 16]; - for j in 0..16 { - for i in 0..=255 { - s = 0x00; - for ciphertext in texts { - for i in 0..4 { - for j in 0..4 { - matrix[j][i] = ciphertext[j * 4 + i]; - } - } - // aes::inverse_shift_rows(&mut matrix); - for i in 0..4 { - for j in 0..4 { - array[j * 4 + i] = matrix[j][i]; - } - } - s ^= substitute(array[j] ^ i, false); - } - if s == 0x00 { - println!("found: {:02x} for byte {}", i, j); - key[j].push(i); - } - } - println!(); - } - key - } - - pub fn findroundkey(&key: &[u8; 16]) -> [u8; 16] { - let mut found_key: [u8; 16] = [0x00; 16]; - let mut const_byte: u8 = 0x00; - let mut ciphertexts: [[u8; 16]; 256] = Self::aes_reduced_gen_texts(&key, &const_byte); - let mut key_guesses: [Vec; 16] = Self::guessroundkey(&ciphertexts); - let mut more_key_guesses: [Vec; 16]; - let mut result: [Vec; 16] = Default::default(); - while key_guesses.iter().any(|vec| vec.len() > 1) { - const_byte += 1_u8; - ciphertexts = Self::aes_reduced_gen_texts(&key, &const_byte); - more_key_guesses = Self::guessroundkey(&ciphertexts); - for ((vec1, vec2), result_vec) in more_key_guesses - .iter() - .zip(key_guesses.iter()) - .zip(result.iter_mut()) - { - let common_elements: Vec = vec1 - .iter() - .filter(|&byte| vec2.contains(byte)) - .copied() - .collect(); - - *result_vec = common_elements; - } - for (source, target) in result.iter().zip(key_guesses.iter_mut()) { - *target = source.clone(); - } - } - for (index, vec) in key_guesses.iter().enumerate() { - if let Some(&first_byte) = vec.first() { - found_key[index] = first_byte; - } - } - found_key - } - // pub fn findkey(&key: &[u8; 16]) -> [u8; 16] { - // - // } } #[cfg(test)] @@ -704,24 +538,6 @@ mod tests { assert_eq!(cleartext, expected_cleartext); } #[test] - fn findroundkey_test() { - let key: [u8; 16] = [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, - 0x4f, 0x3c, - ]; - let nturn = 4; // doesn't matter for this test - let aescipher = Aes::new(&key, &nturn); - let expected: [u8; 16] = aescipher.expanded_key[16..20] // 5th key (1st is the pre_whitenning, 2nd, 3rd and 4th the 3 previous round key) - .iter() - .flat_map(|subarray| subarray.iter()) - .copied() - .collect::>() - .try_into() - .unwrap(); - let found_key: [u8; 16] = Aes::findroundkey(&key); - assert_eq!(found_key, expected); - } - #[test] fn inverse_shift_word_test() { let word: [u8; 4] = [0x01, 0x34, 0x76, 0x49]; let inverse_word: [u8; 4] = shift_word(&word); diff --git a/src/main.rs b/src/main.rs index fc2b18d..7f409e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,44 @@ mod aes; -use aes::Aes; +mod square; +use crate::aes::Aes; + fn main() { let key: [u8; 16] = [ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, ]; - let mut found_key: [u8; 16] = Aes::findroundkey(&key); + let found_key: [u8; 16] = Aes::findroundkey(&key); for &byte in &found_key { print!("{:02x}", byte); } println!(); - // should be - // ef44a541 - // a8525b7f - // b671253b - // db0bad00 - let found_key_origininal = Aes::reverse_key_schedule(&mut found_key, 4); + let found_key_origininal = Aes::reverse_key_schedule(&found_key, 4); for &byte in &found_key_origininal { print!("{:02x}", byte); } println!(); - // 247e156b 22aed2a6 a7f71588 01cf4f3c - // 2b7e1516, 28aed2a6, abf71588, 09cf4f3c, EXPECTED - // 2b7e1574 27aed2a6 a4f71588 00cf4f3c - // 2b7e1516 28aed2a6 abf71588 09cf4f3c + let nturn = 4; + let aescipher = Aes::new(&key, &nturn); + let mut output = [[0u8; 16]; 11]; + for i in 0..11 { + for j in 0..4 { + output[i][4 * j..4 * (j + 1)].copy_from_slice(&aescipher.expanded_key[4 * i + j]); + } + } + for &key in &output { + for &byte in &key { + print!("{:02x}", byte); + } + println!(); + } - // 2b7e1516, 28aed2a6, abf71588, 09cf4f3c, - // a0fafe17, 88542cb1, 23a33939, 2a6c7605, - // f2c295f2, 7a96b943, 5935807a, 7359f67f, - // 3d80477d, 4716fe3e, 1e237e44, 6d7a883b, - // ef44a541, a8525b7f, b671253b, db0bad00 + println!("test"); + for i in 0..11 { + for &byte in &Aes::reverse_key_schedule(&output[i], i) { + print!("{:02x}", byte); + } + println!(); + println!(); + } } diff --git a/src/square.rs b/src/square.rs new file mode 100644 index 0000000..7787acb --- /dev/null +++ b/src/square.rs @@ -0,0 +1,179 @@ +use crate::aes::*; + +impl Aes { + pub fn reverse_key_schedule(original_round_key: &[u8; 16], aes_round: usize) -> [u8; 16] { + let mut rcon = [0u8; 4]; + let mut round_key: [u8; 16] = [0u8; 16]; + round_key.copy_from_slice(original_round_key); + + for i in (0..aes_round).rev() { + // println!("{}", i); + rcon[0] = RC[i + 1]; + // println!("{:02x}", rcon[0]); + let a2: [u8; 4] = round_key[0..4].try_into().unwrap(); + let b2: [u8; 4] = round_key[4..8].try_into().unwrap(); + let c2: [u8; 4] = round_key[8..12].try_into().unwrap(); + let d2: [u8; 4] = round_key[12..16].try_into().unwrap(); + + let d1 = xor(&d2, &c2); + let c1 = xor(&c2, &b2); + let b1 = xor(&b2, &a2); + let a1 = xor(&xor(&a2, &shift_word(&substitute_word(&d1))), &rcon); + + round_key[0..4].copy_from_slice(&a1); + round_key[4..8].copy_from_slice(&b1); + round_key[8..12].copy_from_slice(&c1); + round_key[12..16].copy_from_slice(&d1); + } + round_key + } + + pub fn aes_reduced(&key: &[u8; 16], &block: &[u8; 16]) -> [u8; 16] { + let nturn = 4; + let aescipher = Self::new(&key, &nturn); + aescipher.encrypt_block(&block) + } + pub fn aes_reduced_gen_texts(&key: &[u8; 16], &const_byte: &u8) -> [[u8; 16]; 256] { + let mut cleartext: [u8; 16]; + let mut ciphertexts: [[u8; 16]; 256] = [[0; 16]; 256]; + for i in 0..=255 { + cleartext = [ + i, const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, + const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, const_byte, + const_byte, const_byte, + ]; + ciphertexts[i as usize] = Aes::aes_reduced(&key, &cleartext); + } + ciphertexts + } + pub fn decrypt_block_reduced_1_step(&self, block: &[u8; 16]) -> [u8; 16] { + let mut result = [0u8; 16]; + + let mut state = [[0u8; 4]; 4]; + for i in 0..16 { + state[i % 4][i / 4] = block[i]; + } + add_round_key(&mut state, &clone_into_array(&self.expanded_key[16..20])); + inverse_shift_rows(&mut state); + inverse_substitute_state(&mut state); + + for i in 0..4 { + for j in 0..4 { + result[4 * j + i] = state[i][j] + } + } + + result + } + pub fn guessroundkey(&texts: &[[u8; 16]; 256]) -> [Vec; 16] { + let mut key: [Vec; 16] = Default::default(); + let mut s: u8; + let mut matrix: [[u8; 4]; 4] = [[0; 4]; 4]; + let mut array: [u8; 16] = [0; 16]; + for j in 0..16 { + for i in 0..=255 { + s = 0x00; + for ciphertext in texts { + for i in 0..4 { + for j in 0..4 { + matrix[j][i] = ciphertext[j * 4 + i]; + } + } + for i in 0..4 { + for j in 0..4 { + array[j * 4 + i] = matrix[j][i]; + } + } + s ^= substitute(array[j] ^ i, false); + } + if s == 0x00 { + println!("found: {:02x} for byte {}", i, j); + key[j].push(i); + } + } + println!(); + } + key + } + + pub fn findroundkey(&key: &[u8; 16]) -> [u8; 16] { + let mut found_key: [u8; 16] = [0x00; 16]; + let mut const_byte: u8 = 0x00; + let mut ciphertexts: [[u8; 16]; 256] = Self::aes_reduced_gen_texts(&key, &const_byte); + let mut key_guesses: [Vec; 16] = Self::guessroundkey(&ciphertexts); + let mut more_key_guesses: [Vec; 16]; + let mut result: [Vec; 16] = Default::default(); + while key_guesses.iter().any(|vec| vec.len() > 1) { + const_byte += 1_u8; + ciphertexts = Self::aes_reduced_gen_texts(&key, &const_byte); + more_key_guesses = Self::guessroundkey(&ciphertexts); + for ((vec1, vec2), result_vec) in more_key_guesses + .iter() + .zip(key_guesses.iter()) + .zip(result.iter_mut()) + { + let common_elements: Vec = vec1 + .iter() + .filter(|&byte| vec2.contains(byte)) + .copied() + .collect(); + + *result_vec = common_elements; + } + for (source, target) in result.iter().zip(key_guesses.iter_mut()) { + *target = source.clone(); + } + } + for (index, vec) in key_guesses.iter().enumerate() { + if let Some(&first_byte) = vec.first() { + found_key[index] = first_byte; + } + } + found_key + } + // pub fn findkey(&key: &[u8; 16]) -> [u8; 16] { + // + // } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn findroundkey_test() { + let key: [u8; 16] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, + 0x4f, 0x3c, + ]; + let nturn = 4; // doesn't matter for this test + let aescipher = Aes::new(&key, &nturn); + let expected: [u8; 16] = aescipher.expanded_key[16..20] // 5th key (1st is the pre_whitenning, 2nd, 3rd and 4th the 3 previous round key) + .iter() + .flat_map(|subarray| subarray.iter()) + .copied() + .collect::>() + .try_into() + .unwrap(); + let found_key: [u8; 16] = Aes::findroundkey(&key); + assert_eq!(found_key, expected); + } + #[test] + fn reverse_key_schedule_test() { + let key: [u8; 16] = [ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, + 0x4f, 0x3c, + ]; + let nturn = 4; // doesn't matter for this test + let aescipher = Aes::new(&key, &nturn); + let mut round_keys: [[u8; 16]; 11] = [[0u8; 16]; 11]; + for i in 0..11 { + for j in 0..4 { + round_keys[i][4 * j..4 * (j + 1)] + .copy_from_slice(&aescipher.expanded_key[4 * i + j]); + } + } + for i in 0..11 { + assert_eq!(Aes::reverse_key_schedule(&round_keys[i], i), key); + } + } +}