diff --git a/.github/workflows/ascon.yml b/.github/workflows/ascon.yml index fdba916..475d2a1 100644 --- a/.github/workflows/ascon.yml +++ b/.github/workflows/ascon.yml @@ -21,7 +21,7 @@ jobs: set-msrv: uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master with: - msrv: 1.41.0 + msrv: 1.59.0 benches: runs-on: ubuntu-latest diff --git a/ascon/README.md b/ascon/README.md index 4b0908f..5348294 100644 --- a/ascon/README.md +++ b/ascon/README.md @@ -28,7 +28,7 @@ portfolio of the [CAESAR competition]. ## Minimum Supported Rust Version -This crate requires **Rust 1.41** at a minimum. +This crate requires **Rust 1.59** at a minimum. We may change the MSRV in the future, but it will be accompanied by a minor version bump. @@ -57,7 +57,7 @@ dual licensed as above, without any additional terms or conditions. [build-image]: https://github.com/RustCrypto/sponges/actions/workflows/ascon.yml/badge.svg [build-link]: https://github.com/RustCrypto/sponges/actions/workflows/ascon.yml [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.59+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/369879-sponges diff --git a/ascon/src/aead.rs b/ascon/src/aead.rs index e847139..3f1ce58 100644 --- a/ascon/src/aead.rs +++ b/ascon/src/aead.rs @@ -1,8 +1,17 @@ //! Authenticated Encryption with Associated Data. -use crate::{finalization, initialization, permutation, B, KEY_LEN, RATE, S_SIZE}; +use crate::{Ascon, KEY_LEN, RATE, S_SIZE}; use alloc::{vec, vec::Vec}; +/// Ascon(a,b) `b`-parameter. +const B: usize = 8; + +/// Tag length. +const TAG_LEN: usize = 16; + +/// Authentication tag. +type Tag = [u8; TAG_LEN]; + /// Decryption errors. #[derive(Debug)] pub enum DecryptFail { @@ -14,54 +23,54 @@ pub enum DecryptFail { } /// AEAD encryption. -pub fn encrypt(key: &[u8], iv: &[u8], message: &[u8], aad: &[u8]) -> (Vec, [u8; KEY_LEN]) { +pub fn encrypt(key: &[u8], iv: &[u8], message: &[u8], aad: &[u8]) -> (Vec, Tag) { let s = aad.len() / RATE + 1; let t = message.len() / RATE + 1; let l = message.len() % RATE; - let mut ss = [0; S_SIZE]; let mut aa = vec![0; s * RATE]; let mut mm = vec![0; t * RATE]; let mut output = vec![0; message.len()]; - let mut tag = [0; KEY_LEN]; // pad aad aa[..aad.len()].copy_from_slice(aad); aa[aad.len()] = 0x80; + // pad message mm[..message.len()].copy_from_slice(message); mm[message.len()] = 0x80; // init - initialization(&mut ss, key, iv); + let mut ss = Ascon::new(key, iv); // aad if !aad.is_empty() { process_aad(&mut ss, &aa, s); } - ss[S_SIZE - 1] ^= 1; + + ss.state[S_SIZE - 1] ^= 1; // plaintext for i in 0..(t - 1) { for j in 0..RATE { - ss[j] ^= mm[i * RATE + j]; + ss.state[j] ^= mm[i * RATE + j]; } - output[(i * RATE)..(i * RATE + RATE)].copy_from_slice(&ss[..RATE]); - permutation(&mut ss, 12 - B, B); - } - for j in 0..RATE { - ss[j] ^= mm[(t - 1) * RATE + j]; - } - for j in 0..l { - output[(t - 1) * RATE + j] = ss[j]; + output[(i * RATE)..(i * RATE + RATE)].copy_from_slice(&ss.state[..RATE]); + ss.permutation(12 - B, B); } - // finalization - finalization(&mut ss, key); + for j in 0..RATE { + ss.state[j] ^= mm[(t - 1) * RATE + j]; + } + + for j in 0..l { + output[(t - 1) * RATE + j] = ss.state[j]; + } // tag - tag.copy_from_slice(&ss[S_SIZE - KEY_LEN..]); + let mut tag = Tag::default(); + tag.copy_from_slice(&ss.finalize(key)[S_SIZE - KEY_LEN..]); (output, tag) } @@ -82,7 +91,6 @@ pub fn decrypt( let t = ciphertext.len() / RATE + 1; let l = ciphertext.len() % RATE; - let mut ss = [0; S_SIZE]; let mut aa = vec![0; s * RATE]; let mut mm = vec![0; t * RATE]; @@ -91,34 +99,38 @@ pub fn decrypt( aa[aad.len()] = 0x80; // init - initialization(&mut ss, key, iv); + let mut ss = Ascon::new(key, iv); // aad if !aad.is_empty() { process_aad(&mut ss, &aa, s); } - ss[S_SIZE - 1] ^= 1; + + ss.state[S_SIZE - 1] ^= 1; // ciphertext for i in 0..(t - 1) { for j in 0..RATE { - mm[i * RATE + j] = ss[j] ^ ciphertext[i * RATE + j]; + mm[i * RATE + j] = ss.state[j] ^ ciphertext[i * RATE + j]; } - ss[..RATE].copy_from_slice(&ciphertext[(i * RATE)..(i * RATE + RATE)]); - permutation(&mut ss, 12 - B, B); + ss.state[..RATE].copy_from_slice(&ciphertext[(i * RATE)..(i * RATE + RATE)]); + ss.permutation(12 - B, B); } + for j in 0..l { - mm[(t - 1) * RATE + j] = ss[j] ^ ciphertext[(t - 1) * RATE + j]; + mm[(t - 1) * RATE + j] = ss.state[j] ^ ciphertext[(t - 1) * RATE + j]; } + for j in 0..l { - ss[j] = ciphertext[(t - 1) * RATE + j]; + ss.state[j] = ciphertext[(t - 1) * RATE + j]; } - ss[l] ^= 0x80; + + ss.state[l] ^= 0x80; // finalization - finalization(&mut ss, key); + let expected_tag = ss.finalize(key); - if ct_eq(&ss[S_SIZE - KEY_LEN..], tag) { + if ct_eq(&expected_tag[S_SIZE - KEY_LEN..], tag) { Ok(mm[..ciphertext.len()].into()) } else { Err(DecryptFail::AuthenticationFail) @@ -138,12 +150,13 @@ fn ct_eq(a: &[u8], b: &[u8]) -> bool { } } -fn process_aad(ss: &mut [u8], aa: &[u8], s: usize) { +fn process_aad(ss: &mut Ascon, aa: &[u8], s: usize) { for i in 0..s { for j in 0..RATE { - ss[j] ^= aa[i * RATE + j]; + ss.state[j] ^= aa[i * RATE + j]; } - permutation(ss, 12 - B, B); + + ss.permutation(12 - B, B); } } diff --git a/ascon/src/lib.rs b/ascon/src/lib.rs index 4833db7..b5a922d 100644 --- a/ascon/src/lib.rs +++ b/ascon/src/lib.rs @@ -46,20 +46,71 @@ use core::convert::TryInto; /// Key length. const KEY_LEN: usize = 16; +/// State size in bits. +const S_BITS: usize = 320; + /// State size. -const S_SIZE: usize = 320 / 8; +const S_SIZE: usize = S_BITS / 8; /// Rate: Sᵣ. const RATE: usize = 128 / 8; -/// Ascon(a,b) a-parameter. -const A: usize = 12; +/// Ascon(a,b) permutation. +pub struct Ascon { + state: [u8; S_SIZE], +} -/// Ascon(a,b) b-parameter. -const B: usize = 8; +impl Ascon { + /// Initialize Ascon permutation. + // TODO(tarcieri): validate length of key and nonce + pub fn new(key: &[u8], nonce: &[u8]) -> Self { + let mut state = [0; S_SIZE]; + state[0] = KEY_LEN as u8 * 8; + state[1] = RATE as u8 * 8; + state[2] = A as u8; + state[3] = B as u8; + + let mut pos = S_SIZE - 2 * KEY_LEN; + state[pos..pos + key.len()].copy_from_slice(key); + pos += KEY_LEN; + state[pos..pos + nonce.len()].copy_from_slice(nonce); + + permutation(&mut state, 12 - A, A); + + for (i, &b) in key.iter().enumerate() { + state[pos + i] ^= b; + } + + Self { state } + } + + /// Perform Ascon permutation on internal state. + pub fn permutation(&mut self, start: usize, rounds: usize) { + permutation(&mut self.state, start, rounds); + } + + /// Finalize Ascon permutation. + #[inline(always)] + #[must_use] + pub fn finalize(self, key: &[u8]) -> [u8; S_SIZE] { + let mut s = self.state; + + for (i, &b) in key.iter().enumerate() { + s[RATE + i] ^= b; + } + + permutation(&mut s, 12 - A, A); + + for (i, &b) in key.iter().enumerate() { + s[S_SIZE - KEY_LEN + i] ^= b; + } + + s + } +} /// Ascon permutation. -pub fn permutation(s: &mut [u8], start: usize, rounds: usize) { +fn permutation(s: &mut [u8], start: usize, rounds: usize) { let mut x = [0; 5]; let mut t = [0; 5]; @@ -110,33 +161,3 @@ pub fn permutation(s: &mut [u8], start: usize, rounds: usize) { s[(i * 8)..((i + 1) * 8)].copy_from_slice(&b.to_be_bytes()) } } - -/// Initialize Ascon permutation. -pub fn initialization(s: &mut [u8], key: &[u8], nonce: &[u8]) { - s[0] = KEY_LEN as u8 * 8; - s[1] = RATE as u8 * 8; - s[2] = A as u8; - s[3] = B as u8; - - let mut pos = S_SIZE - 2 * KEY_LEN; - s[pos..pos + key.len()].copy_from_slice(key); - pos += KEY_LEN; - s[pos..pos + nonce.len()].copy_from_slice(nonce); - - permutation(s, 12 - A, A); - - for (i, &b) in key.iter().enumerate() { - s[pos + i] ^= b; - } -} - -/// Finalize Ascon permutation. -pub fn finalization(s: &mut [u8], key: &[u8]) { - for (i, &b) in key.iter().enumerate() { - s[RATE + i] ^= b; - } - permutation(s, 12 - A, A); - for (i, &b) in key.iter().enumerate() { - s[S_SIZE - KEY_LEN + i] ^= b; - } -}