diff --git a/.github/workflows/aes-gcm.yml b/.github/workflows/aes-gcm.yml index 4a0c7880..727d8c71 100644 --- a/.github/workflows/aes-gcm.yml +++ b/.github/workflows/aes-gcm.yml @@ -3,6 +3,7 @@ name: aes-gcm on: pull_request: paths: + - ".github/workflows/aes-gcm.yml" - "aes-gcm/**" - "Cargo.*" push: @@ -22,7 +23,7 @@ jobs: strategy: matrix: rust: - - 1.51.0 # MSRV + - 1.56.0 # MSRV - stable target: - armv7a-none-eabi @@ -45,7 +46,7 @@ jobs: include: # 32-bit Linux - target: i686-unknown-linux-gnu - rust: 1.51.0 # MSRV + rust: 1.56.0 # MSRV deps: sudo apt update && sudo apt install gcc-multilib - target: i686-unknown-linux-gnu rust: stable @@ -53,7 +54,7 @@ jobs: # 64-bit Linux - target: x86_64-unknown-linux-gnu - rust: 1.51.0 # MSRV + rust: 1.56.0 # MSRV - target: x86_64-unknown-linux-gnu rust: stable steps: diff --git a/Cargo.lock b/Cargo.lock index 67452aeb..2f4a6ce0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,14 +27,25 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "aes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" +dependencies = [ + "cfg-if", + "cipher 0.4.3", + "cpufeatures", +] + [[package]] name = "aes-gcm" version = "0.9.4" dependencies = [ "aead", - "aes", - "cipher 0.3.0", - "ctr", + "aes 0.8.1", + "cipher 0.4.3", + "ctr 0.9.1", "ghash", "hex-literal 0.3.4", "subtle", @@ -46,9 +57,9 @@ name = "aes-gcm-siv" version = "0.10.3" dependencies = [ "aead", - "aes", + "aes 0.7.5", "cipher 0.3.0", - "ctr", + "ctr 0.8.0", "polyval", "subtle", "zeroize", @@ -59,12 +70,12 @@ name = "aes-siv" version = "0.6.2" dependencies = [ "aead", - "aes", + "aes 0.7.5", "blobby", "cipher 0.3.0", "cmac", "crypto-mac", - "ctr", + "ctr 0.8.0", "dbl", "hex-literal 0.3.4", "pmac", @@ -88,9 +99,9 @@ name = "ccm" version = "0.4.4" dependencies = [ "aead", - "aes", + "aes 0.7.5", "cipher 0.3.0", - "ctr", + "ctr 0.8.0", "hex-literal 0.2.2", "subtle", ] @@ -192,6 +203,15 @@ dependencies = [ "cipher 0.3.0", ] +[[package]] +name = "ctr" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" +dependencies = [ + "cipher 0.4.3", +] + [[package]] name = "dbl" version = "0.3.2" @@ -206,7 +226,7 @@ name = "deoxys" version = "0.0.2" dependencies = [ "aead", - "aes", + "aes 0.7.5", "hex-literal 0.3.4", "subtle", "zeroize", @@ -217,10 +237,10 @@ name = "eax" version = "0.4.1" dependencies = [ "aead", - "aes", + "aes 0.7.5", "cipher 0.3.0", "cmac", - "ctr", + "ctr 0.8.0", "subtle", ] diff --git a/aes-gcm/Cargo.toml b/aes-gcm/Cargo.toml index 10df5520..910627af 100644 --- a/aes-gcm/Cargo.toml +++ b/aes-gcm/Cargo.toml @@ -17,9 +17,9 @@ categories = ["cryptography", "no-std"] [dependencies] aead = { version = "0.4", default-features = false } -aes = { version = "0.7.5", optional = true } -cipher = "0.3" -ctr = "0.8" +aes = { version = "0.8", optional = true } +cipher = "0.4" +ctr = "0.9" ghash = { version = "0.4.2", default-features = false } subtle = { version = "2", default-features = false } zeroize = { version = "1", optional = true, default-features = false } @@ -32,8 +32,8 @@ hex-literal = "0.3" default = ["aes", "alloc"] std = ["aead/std", "alloc"] alloc = ["aead/alloc"] -armv8 = ["aes/armv8", "ghash/armv8"] # nightly-only -force-soft = ["aes/force-soft", "ghash/force-soft"] +armv8 = ["ghash/armv8"] # nightly-only +force-soft = ["ghash/force-soft"] heapless = ["aead/heapless"] stream = ["aead/stream"] diff --git a/aes-gcm/README.md b/aes-gcm/README.md index 19f92ac9..fc9ccb70 100644 --- a/aes-gcm/README.md +++ b/aes-gcm/README.md @@ -49,7 +49,7 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/aes-gcm/badge.svg [docs-link]: https://docs.rs/aes-gcm/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.49+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260038-AEADs [downloads-image]: https://img.shields.io/crates/d/aes-gcm.svg diff --git a/aes-gcm/src/lib.rs b/aes-gcm/src/lib.rs index 8d7aaaa1..88df4f8a 100644 --- a/aes-gcm/src/lib.rs +++ b/aes-gcm/src/lib.rs @@ -35,7 +35,7 @@ //! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` //! use aes_gcm::aead::{Aead, NewAead}; //! -//! let key = Key::from_slice(b"an example very very secret key."); +//! let key = Key::::from_slice(b"an example very very secret key."); //! let cipher = Aes256Gcm::new(key); //! //! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message @@ -64,14 +64,13 @@ //! which can then be passed as the `buffer` parameter to the in-place encrypt //! and decrypt methods: //! -//! ``` -//! # #[cfg(feature = "heapless")] -//! # { +#![cfg_attr(feature = "heapless", doc = " ```")] +#![cfg_attr(not(feature = "heapless"), doc = " ```ignore")] //! use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` //! use aes_gcm::aead::{AeadInPlace, NewAead}; //! use aes_gcm::aead::heapless::Vec; //! -//! let key = Key::from_slice(b"an example very very secret key."); +//! let key = Key::::from_slice(b"an example very very secret key."); //! let cipher = Aes256Gcm::new(key); //! //! let nonce = Nonce::from_slice(b"unique nonce"); // 96-bits; unique per message @@ -88,7 +87,6 @@ //! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext //! cipher.decrypt_in_place(nonce, b"", &mut buffer).expect("decryption failure!"); //! assert_eq!(&buffer, b"plaintext message"); -//! # } //! ``` //! //! [1]: https://en.wikipedia.org/wiki/Authenticated_encryption @@ -106,18 +104,17 @@ #![warn(missing_docs, rust_2018_idioms)] pub use aead::{self, AeadCore, AeadInPlace, Error, NewAead}; +pub use cipher::Key; #[cfg(feature = "aes")] pub use aes; use cipher::{ consts::{U0, U16}, - generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, - Block, BlockCipher, BlockCipherKey, BlockEncrypt, FromBlockCipher, NewBlockCipher, - StreamCipher, StreamCipherSeek, + generic_array::{ArrayLength, GenericArray}, + BlockCipher, BlockEncrypt, BlockSizeUser, InnerIvInit, KeyInit, KeySizeUser, StreamCipherCore, }; use core::marker::PhantomData; -use ctr::Ctr32BE; use ghash::{ universal_hash::{NewUniversalHash, UniversalHash}, GHash, @@ -138,9 +135,6 @@ pub const P_MAX: u64 = 1 << 36; /// Maximum length of ciphertext pub const C_MAX: u64 = (1 << 36) + 16; -/// AES-GCM keys -pub type Key = GenericArray; - /// AES-GCM nonces pub type Nonce = GenericArray; @@ -157,6 +151,12 @@ pub type Aes128Gcm = AesGcm; #[cfg_attr(docsrs, doc(cfg(feature = "aes")))] pub type Aes256Gcm = AesGcm; +/// AES block. +type Block = GenericArray; + +/// Counter mode with a 32-bit big endian counter. +type Ctr32BE = ctr::CtrCore; + /// AES-GCM: generic over an underlying AES implementation and nonce size. /// /// This type is generic to support substituting alternative AES implementations @@ -171,12 +171,7 @@ pub type Aes256Gcm = AesGcm; /// /// If in doubt, use the built-in [`Aes128Gcm`] and [`Aes256Gcm`] type aliases. #[derive(Clone)] -pub struct AesGcm -where - Aes: BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, - NonceSize: ArrayLength, -{ +pub struct AesGcm { /// Encryption cipher cipher: Aes, @@ -187,24 +182,27 @@ where nonce_size: PhantomData, } +impl KeySizeUser for AesGcm +where + Aes: KeyInit, +{ + type KeySize = Aes::KeySize; +} + impl NewAead for AesGcm where - Aes: NewBlockCipher + BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, - NonceSize: ArrayLength, + Aes: BlockSizeUser + BlockEncrypt + KeyInit, { type KeySize = Aes::KeySize; - fn new(key: &BlockCipherKey) -> Self { + fn new(key: &Key) -> Self { Aes::new(key).into() } } impl From for AesGcm where - Aes: NewBlockCipher + BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, - NonceSize: ArrayLength, + Aes: BlockSizeUser + BlockEncrypt, { fn from(cipher: Aes) -> Self { let mut ghash_key = ghash::Key::default(); @@ -225,8 +223,6 @@ where impl AeadCore for AesGcm where - Aes: NewBlockCipher + BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, NonceSize: ArrayLength, { type NonceSize = NonceSize; @@ -236,13 +232,12 @@ where impl AeadInPlace for AesGcm where - Aes: NewBlockCipher + BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, + Aes: BlockCipher + BlockSizeUser + BlockEncrypt, NonceSize: ArrayLength, { fn encrypt_in_place_detached( &self, - nonce: &Nonce, + nonce: &Nonce, associated_data: &[u8], buffer: &mut [u8], ) -> Result { @@ -250,22 +245,17 @@ where return Err(Error); } + let (ctr, mask) = self.init_ctr(nonce); + // TODO(tarcieri): interleave encryption with GHASH // See: - let mut ctr = self.init_ctr(nonce); - ctr.seek(Aes::BlockSize::to_usize()); - ctr.apply_keystream(buffer); - - let mut tag = self.compute_tag(associated_data, buffer); - ctr.seek(0); - ctr.apply_keystream(tag.as_mut_slice()); - - Ok(tag) + ctr.apply_keystream_partial(buffer.into()); + Ok(self.compute_tag(mask, associated_data, buffer)) } fn decrypt_in_place_detached( &self, - nonce: &Nonce, + nonce: &Nonce, associated_data: &[u8], buffer: &mut [u8], tag: &Tag, @@ -274,15 +264,15 @@ where return Err(Error); } + let (ctr, mask) = self.init_ctr(nonce); + // TODO(tarcieri): interleave encryption with GHASH // See: - let mut expected_tag = self.compute_tag(associated_data, buffer); - let mut ctr = self.init_ctr(nonce); - ctr.apply_keystream(expected_tag.as_mut_slice()); + let expected_tag = self.compute_tag(mask, associated_data, buffer); + ctr.apply_keystream_partial(buffer.into()); use subtle::ConstantTimeEq; if expected_tag.ct_eq(tag).unwrap_u8() == 1 { - ctr.apply_keystream(buffer); Ok(()) } else { Err(Error) @@ -292,8 +282,7 @@ where impl AesGcm where - Aes: NewBlockCipher + BlockCipher + BlockEncrypt, - Aes::ParBlocks: ArrayLength>, + Aes: BlockCipher + BlockSizeUser + BlockEncrypt, NonceSize: ArrayLength, { /// Initialize counter mode. @@ -305,7 +294,7 @@ where /// > If len(IV)=96, then J0 = IV || 0{31} || 1. /// > If len(IV) ≠ 96, then let s = 128 ⎡len(IV)/128⎤-len(IV), and /// > J0=GHASH(IV||0s+64||[len(IV)]64). - fn init_ctr(&self, nonce: &Nonce) -> Ctr32BE<&Aes> { + fn init_ctr(&self, nonce: &Nonce) -> (Ctr32BE<&Aes>, Block) { let j0 = if NonceSize::to_usize() == 12 { let mut block = ghash::Block::default(); block[..12].copy_from_slice(nonce); @@ -322,11 +311,14 @@ where ghash.finalize().into_bytes() }; - Ctr32BE::from_block_cipher(&self.cipher, &j0) + let mut ctr = Ctr32BE::inner_iv_init(&self.cipher, &j0); + let mut tag_mask = Block::default(); + ctr.write_keystream_block(&mut tag_mask); + (ctr, tag_mask) } /// Authenticate the given plaintext and associated data using GHASH - fn compute_tag(&self, associated_data: &[u8], buffer: &[u8]) -> Tag { + fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Tag { let mut ghash = self.ghash.clone(); ghash.update_padded(associated_data); ghash.update_padded(buffer); @@ -338,6 +330,12 @@ where block[..8].copy_from_slice(&associated_data_bits.to_be_bytes()); block[8..].copy_from_slice(&buffer_bits.to_be_bytes()); ghash.update(&block); - ghash.finalize().into_bytes() + + let mut tag = ghash.finalize().into_bytes(); + for (a, b) in tag.as_mut_slice().iter_mut().zip(mask.as_slice()) { + *a ^= *b; + } + + tag } } diff --git a/aes-gcm/tests/aes128gcm.rs b/aes-gcm/tests/aes128gcm.rs index a8e0cc86..3762d0be 100644 --- a/aes-gcm/tests/aes128gcm.rs +++ b/aes-gcm/tests/aes128gcm.rs @@ -1,14 +1,12 @@ //! AES-128-GCM tests -#[macro_use] -extern crate hex_literal; - #[macro_use] mod common; use self::common::TestVector; use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead, Payload}; use aes_gcm::Aes128Gcm; +use hex_literal::hex; /// NIST CAVS vectors /// diff --git a/aes-gcm/tests/aes256gcm.rs b/aes-gcm/tests/aes256gcm.rs index 87840671..e6456caa 100644 --- a/aes-gcm/tests/aes256gcm.rs +++ b/aes-gcm/tests/aes256gcm.rs @@ -1,14 +1,12 @@ //! AES-256-GCM tests -#[macro_use] -extern crate hex_literal; - #[macro_use] mod common; use self::common::TestVector; use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead, Payload}; use aes_gcm::Aes256Gcm; +use hex_literal::hex; /// NIST CAVS vectors /// diff --git a/aes-gcm/tests/other_ivlen.rs b/aes-gcm/tests/other_ivlen.rs index 72fdd26d..550d4d28 100644 --- a/aes-gcm/tests/other_ivlen.rs +++ b/aes-gcm/tests/other_ivlen.rs @@ -1,19 +1,15 @@ //! Tests for AES-GCM when used with non-96-bit IVs. //! -//! Vectors taken from NIST CAVS vectors' `gcmEncryptExtIV128.rsp` file -/// +//! Vectors taken from NIST CAVS vectors' `gcmEncryptExtIV128.rsp` file: +//! -#[macro_use] -extern crate hex_literal; - -use aes_gcm::{ - aead::{ - generic_array::{typenum, GenericArray}, - Aead, NewAead, - }, - aes::Aes128, - AesGcm, +use aead::{ + generic_array::{typenum, GenericArray}, + Aead, NewAead, }; +use aes::Aes128; +use aes_gcm::AesGcm; +use hex_literal::hex; /// Based on the following `gcmEncryptExtIV128.rsp` test vector: ///