diff --git a/ciphers/src/digital/block_ciphers/ascon.rs b/ciphers/src/digital/block_ciphers/ascon.rs index 63a8e9ff..04ff92a1 100644 --- a/ciphers/src/digital/block_ciphers/ascon.rs +++ b/ciphers/src/digital/block_ciphers/ascon.rs @@ -1,12 +1,5 @@ -use crate::{ - digital::block_ciphers::block_cipher::{BCMode, BlockCipher}, - errors::CipherError, - Cipher, -}; -use utils::{ - byte_formatting::{u64s_to_bytes_be, ByteFormat}, - padding::bit_padding, -}; +use crate::{digital::block_ciphers::block_cipher::BCMode, errors::CipherError, Cipher}; +use utils::{byte_formatting::ByteFormat, padding::bit_padding}; fn bytes_to_u64_be(bytes: &[u8]) -> Vec { assert!( @@ -24,6 +17,21 @@ fn bytes_to_u64_be(bytes: &[u8]) -> Vec { out } +fn padded_bytes_to_u64_be(bytes: &[u8]) -> u64 { + if bytes.len() > 8 { + panic!("input block was too large") + } else if bytes.len() == 8 { + u64::from_be_bytes(bytes.try_into().unwrap()) + } else { + let mut word_bytes: [u8; 8] = [0; 8]; + for (word_byte, input_byte) in word_bytes.iter_mut().zip(bytes.iter()) { + *word_byte = *input_byte; + } + word_bytes[bytes.len()] = 0x80; + u64::from_be_bytes(word_bytes) + } +} + const C: [u64; 12] = [ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b, ]; @@ -34,7 +42,7 @@ const ROTS: [(u32, u32); 5] = [(19, 28), (61, 39), (1, 6), (10, 17), (7, 41)]; pub struct AsconState { state: [u64; 5], _k: u8, // not used in this implementation - r: u8, + _r: u8, a: u8, b: u8, } @@ -60,7 +68,7 @@ impl AsconState { let mut out = Self { state: [0x80400c0600000000, key[0], key[1], nonce[0], nonce[1]], _k: 128, - r: 64, + _r: 64, a: 12, b: 6, }; @@ -75,7 +83,7 @@ impl AsconState { let mut out = Self { state: [0x80800c0800000000, key[0], key[1], nonce[0], nonce[1]], _k: 128, - r: 128, + _r: 128, a: 12, b: 8, }; @@ -132,32 +140,6 @@ impl AsconState { self[i] ^= self[i].rotate_right(ROTS[i].0) ^ self[i].rotate_right(ROTS[i].1); } } - - // pub fn absorb(&mut self, message: &[u8]) { - // assert!( - // message.len() % self.r as usize == 0, - // "message length in bytes must be a multiple of {}", - // self.r - // ); - // let words = bytes_to_u64_be(message); - - // for word in words { - // self[0] ^= word; - // self.rounds_a(); - // } - // } - - // pub fn squeeze(&mut self, hash_len: usize) -> Vec { - // let mut output = Vec::with_capacity(hash_len); - - // while output.len() < hash_len { - // output.extend_from_slice(&self[0].to_be_bytes()); - // self.rounds_a(); - // } - - // output.truncate(hash_len); - // output - // } } pub struct Ascon128 { @@ -165,8 +147,6 @@ pub struct Ascon128 { pub output_format: ByteFormat, pub mode: BCMode, // pub padding: BCPadding, // only bit padding is allowed - pub iv: u64, - pub state: AsconState, pub associated_data: Vec, pub subkeys: [u64; 2], pub nonce: [u64; 2], @@ -178,9 +158,6 @@ impl Default for Ascon128 { input_format: ByteFormat::Hex, output_format: ByteFormat::Hex, mode: Default::default(), - iv: 0, - - state: Default::default(), associated_data: Default::default(), subkeys: Default::default(), nonce: Default::default(), @@ -221,60 +198,70 @@ impl Ascon128 { self.nonce(key); self } -} - -// impl BlockCipher<8> for Ascon128 { -// fn encrypt_block(&self, bytes: &mut [u8]) { -// let mut block = -// u64::from_be_bytes(bytes.try_into().expect("invalid bytes for block encrypt")); - -// u64s_to_bytes_be(bytes, &[block]); -// } - -// fn decrypt_block(&self, bytes: &mut [u8]) { -// let mut block = -// u64::from_be_bytes(bytes.try_into().expect("invalid bytes for block encrypt")); - -// u64s_to_bytes_be(bytes, &[block]); -// } -// } - -impl Cipher for Ascon128 { - fn encrypt(&self, text: &str) -> Result { - let mut bytes = self - .input_format - .text_to_bytes(text) - .map_err(|_| CipherError::input("byte format error"))?; - let last_block_len = bytes.len() % 8; - bit_padding(&mut bytes, 8).map_err(|e| CipherError::General(e.to_string()))?; - let mut ad = self.associated_data.clone(); - bit_padding(&mut ad, 8).map_err(|e| CipherError::General(e.to_string()))?; + pub fn with_ad(mut self, ad: &[u8]) -> Self { + self.associated_data = ad.to_owned(); + self + } + pub fn encrypt_bytes(&self, bytes: &[u8]) -> Vec { let mut state = AsconState::ascon_128(self.subkeys, self.nonce); - // Absorb associated data - for chunk in bytes_to_u64_be(&ad) { - state[0] ^= chunk; - state.rounds_b(); + // Absorb associated data if it is provided + if !self.associated_data.is_empty() { + for chunk in self.associated_data.chunks(8) { + println!("{:016x?}", padded_bytes_to_u64_be(chunk)); + state[0] ^= padded_bytes_to_u64_be(chunk); + state.rounds_b(); + } } - state[5] ^= 1; + // Flip the last bit, this is described as domain separation + state[4] ^= 1; - // Encrypt the plaintext except the last block + // Encrypt the plaintext treating the last block specially let mut ctext = Vec::new(); - let ptext = bytes_to_u64_be(&bytes); - for chunk in ptext.iter().take(ptext.len() - 1) { - state[0] ^= chunk; - ctext.extend(state[0].to_be_bytes()); - state.rounds_b(); + let chunks = bytes.chunks(8); + let n_chunks = chunks.len(); + + if n_chunks == 0 { + state[0] ^= padded_bytes_to_u64_be(&[]); + } else { + for chunk in chunks.clone().take(n_chunks - 1) { + state[0] ^= padded_bytes_to_u64_be(chunk); + ctext.extend(state[0].to_be_bytes()); + state.rounds_b(); + } + + // Encrypt the last block then truncate it to the length of the input + let last_chunk = chunks.last().expect("there is always at least one block"); + let last_chunk_len = last_chunk.len(); + state[0] ^= padded_bytes_to_u64_be(last_chunk); + ctext.extend_from_slice(&state[0].to_be_bytes()[0..last_chunk_len]); } - // Encrypt the last block then truncate it to the length of the input - state[0] ^= ptext.last().expect("there is alwways at least one chunk"); - ctext.extend_from_slice(&state[0].to_be_bytes()[0..last_block_len]); // Finalize and create the authentication tag + state[1] ^= self.subkeys[0]; + state[2] ^= self.subkeys[1]; + state.rounds_a(); + ctext.extend((state[3] ^ self.subkeys[0]).to_be_bytes()); + ctext.extend((state[4] ^ self.subkeys[1]).to_be_bytes()); + + ctext + } + + // pub fn decrypt_bytes(&self, bytes: &[u8]) {} +} + +impl Cipher for Ascon128 { + fn encrypt(&self, text: &str) -> Result { + let bytes = self + .input_format + .text_to_bytes(text) + .map_err(|_| CipherError::input("byte format error"))?; - todo!() + Ok(self + .output_format + .byte_slice_to_text(&self.encrypt_bytes(&bytes))) } fn decrypt(&self, text: &str) -> Result { @@ -282,22 +269,23 @@ impl Cipher for Ascon128 { .input_format .text_to_bytes(text) .map_err(|_| CipherError::input("byte format error"))?; - let last_block_len = bytes.len() % 8; - bit_padding(&mut bytes, 8).map_err(|e| CipherError::General(e.to_string()))?; - - let mut ad = self.associated_data.clone(); - bit_padding(&mut ad, 8).map_err(|e| CipherError::General(e.to_string()))?; let mut state = AsconState::ascon_128(self.subkeys, self.nonce); - // Absorb associated data - for chunk in bytes_to_u64_be(&ad) { - state[0] ^= chunk; - state.rounds_b(); + // Absorb associated data if it is provided + if !self.associated_data.is_empty() { + for chunk in self.associated_data.chunks(8) { + state[0] ^= padded_bytes_to_u64_be(chunk); + state.rounds_b(); + } } - state[5] ^= 1; + // Flip the last bit, this is described as domain separation + state[4] ^= 1; // Decrypt the plaintext except the last block + let last_block_len = bytes.len() % 8; + bit_padding(&mut bytes, 8).map_err(|e| CipherError::General(e.to_string()))?; + let mut ptext = Vec::new(); let ctext = bytes_to_u64_be(&bytes); for chunk in ctext.iter().take(ctext.len() - 1) { @@ -306,10 +294,198 @@ impl Cipher for Ascon128 { state.rounds_b(); } // Decrypt the last block then truncate it to the length of the input - state[0] ^= ctext.last().expect("there is alwways at least one chunk"); + state[0] ^= ctext.last().expect("there is always at least one chunk"); ptext.extend_from_slice(&state[0].to_be_bytes()[0..last_block_len]); - // Finalize and check the authentication tag - todo!() + // Finalize, check, and remove the authentication tag + let (message, tag) = ptext.split_at(ptext.len() - 16); + state[1] ^= self.subkeys[0]; + state[2] ^= self.subkeys[1]; + state.rounds_a(); + + let x = (state[3] ^ self.subkeys[0]).to_be_bytes(); + let y = (state[4] ^ self.subkeys[1]).to_be_bytes(); + println!("{:02x?} {:02x?}", x, y); + println!("{:02x?}", tag); + // TODO + + // Output as text + Ok(self.output_format.byte_slice_to_text(message)) + } +} + +#[cfg(test)] +mod ascon_tests { + + use super::*; + + #[test] + fn ascon128_encrypt_0_0() { + let cipher = Ascon128::default() + .with_key([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]) + .with_nonce([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]); + let ptext = ""; + let ctext = cipher.encrypt(ptext).unwrap(); + assert_eq!("e355159f292911f794cb1432a0103a8a", ctext); } + + #[test] + fn ascon128_encrypt_2_0() { + let cipher = Ascon128::default() + .with_key([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]) + .with_nonce([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]); + let ptext = "0001"; + let ctext = cipher.encrypt(ptext).unwrap(); + assert_eq!("bc82d5bde868f7494f57d81e06facbf70ce1", ctext); + } + + #[test] + fn ascon128_encrypt_7_0() { + let cipher = Ascon128::default() + .with_key([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]) + .with_nonce([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]); + let ptext = "00010203040506"; + let ctext = cipher.encrypt(ptext).unwrap(); + assert_eq!("bc820dbdf7a463ce9985966c40bc56a9c5180e23f7086c", ctext); + } + + #[test] + fn ascon128_encrypt_8_0() { + let cipher = Ascon128::default() + .with_key([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]) + .with_nonce([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]); + let ptext = "0001020304050607"; + let ctext = cipher.encrypt(ptext).unwrap(); + assert_eq!("bc820dbdf7a4631c01a8807a44254b42ac6bb490da1e000a", ctext); + } + + #[test] + fn ascon128_encrypt_12_0() { + let cipher = Ascon128::default() + .with_key([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]) + .with_nonce([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]); + let ptext = "000102030405060708090A0B"; + let ctext = cipher.encrypt(ptext).unwrap(); + assert_eq!( + "bc820dbdf7a4631c5b29884a7d1c07dc8d0d5ed48e64d7dcb25c325f", + ctext + ); + } + + // #[test] + // fn ascon128_decrypt_0_0() { + // let cipher = Ascon128::default() + // .with_key([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_nonce([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]); + // let ctext = "e355159f292911f794cb1432a0103a8a"; + // let ptext = cipher.decrypt(ctext).unwrap(); + // assert_eq!("", ptext); + // } + + // #[test] + // fn ascon128_encrypt_0_1() { + // let cipher = Ascon128::default() + // .with_key([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_nonce([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_ad(&[0x00]); + // let ptext = ""; + // let ctext = cipher.encrypt(ptext).unwrap(); + // assert_eq!("944df887cd4901614c5dedbc42fc0da0", ctext); + // } + + // #[test] + // fn ascon128_decrypt_2_0() { + // let cipher = Ascon128::default() + // .with_key([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_nonce([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]); + // let ctext = "bc82d5bde868f7494f57d81e06facbf70ce1"; + // let ptext = cipher.decrypt(ctext).unwrap(); + // assert_eq!("0001", ptext); + // } + + // #[test] + // fn ascon128_encrypt_2_2() { + // let cipher = Ascon128::default() + // .with_key([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_nonce([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_ad(&[0x00, 0x01]); + // let ptext = "0001"; + // let ctext = cipher.encrypt(ptext).unwrap(); + // assert_eq!("6e9f373c0b74264c1ce4d705d995915fcccd", ctext); + // } + + // #[test] + // fn ascon128_encrypt_64_64() { + // let cipher = Ascon128::default() + // .with_key([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_nonce([ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, + // ]) + // .with_ad(&[ + // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + // 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + // 0x1C, 0x1D, 0x1E, 0x1F, + // ]); + // let ptext = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; + // let ctext = cipher.encrypt(ptext).unwrap(); + // assert_eq!("b96c78651b6246b0c3b1a5d373b0d5168dca4a96734cf0ddf5f92f8d15e30270279bf6a6cc3f2fc9350b915c292bdb8d", ctext); + // } } diff --git a/ciphers/src/ids/cipher_descriptions.json b/ciphers/src/ids/cipher_descriptions.json index 7fc29a4e..cbf7fa50 100644 --- a/ciphers/src/ids/cipher_descriptions.json +++ b/ciphers/src/ids/cipher_descriptions.json @@ -34,14 +34,6 @@ "SP-network" ] }, - "AMSCO": { - "Description": "The AMSCO cipher is a variation of the columnar transposition cipher. Rather than place one character in each cell of the grid AMSCO alternates between one character and two characters. While this is a more complex process the occasional placement of characters together in the correct order leaks information to an attacker.", - "Authors": "A. M. Scott", - "Publication": null, - "Traits": [ - "Transposition" - ] - }, "Affine": { "Description": "The affine cipher is a slight improvement to the Caesar cipher. Rather than a simple shift an affine transformation, both a multiplication and an addition, is applied to the letters. Each letter is assigned a value based on its position then the first key value is added to it, then that number is multiplied by the second key value, and finally the value is divided by the length of the alphabet and the remainder is taken. That number corresponds to some letter in the alphabet. While a simple improvement on the Caesar cipher this method was not used widely by the ancients because not every multiplicative key is reversible unless the length of the alphabet is prime. The Atbash Code, which effectively reverses the alphabet can be produced as an affine cipher.", "Authors": null, @@ -58,6 +50,14 @@ "Substitution" ] }, + "AMSCO": { + "Description": "The AMSCO cipher is a variation of the columnar transposition cipher. Rather than place one character in each cell of the grid AMSCO alternates between one character and two characters. While this is a more complex process the occasional placement of characters together in the correct order leaks information to an attacker.", + "Authors": "A. M. Scott", + "Publication": null, + "Traits": [ + "Transposition" + ] + }, "Ascon": { "Description": "Ascon-128 and Ascon-128a are block ciphers derived from the Ascon sponge function. This sponge function is similar to Keccak, though with a smaller state and simpler round. Ascon is also used to create the Ascon-Hash and Ascon-XOF hash functions.", "Authors": "Dobraunig, Eichlseder, Mendel, and Schläffer", diff --git a/hashers/src/ids/hasher_descriptions.json b/hashers/src/ids/hasher_descriptions.json index 3820bfa0..5aabe5b2 100644 --- a/hashers/src/ids/hasher_descriptions.json +++ b/hashers/src/ids/hasher_descriptions.json @@ -22,7 +22,7 @@ "Traits": null }, "BLAKE2": { - "Description": "The BLAKE2 family of hash functions are closely related to the original BLAKE family of hash functions which were finalists for the SHA-3 competition. To improve speed the BLAKE2 hashers have fewer compression rounds and pad the input in a simpler way. They are designed to work as drop in replacements to broken hash functions such as SHA-1 and MD5, offering both superior security and supere speed.", + "Description": "The BLAKE2 family of hash functions are closely related to the original BLAKE family of hash functions. To improve speed the BLAKE2 hashers have fewer compression rounds and pad the input in a simpler way. They are designed to work as drop in replacements to broken hash functions such as SHA-1 and MD5, offering both superior security and supere speed.", "Authors": "Aumasson, Neves, Wilcox-O'Hearn, and Winnerlein", "Publication": "2012", "Hash Length": [