Skip to content

Commit

Permalink
xaes: initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioBenitez committed Jun 29, 2024
1 parent ad109f3 commit 90897dd
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 4 deletions.
6 changes: 6 additions & 0 deletions aes-gcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,17 @@
//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
//! [`aead::arrayvec::ArrayVec`]).
#[cfg(feature = "aes")]
mod xaes;

pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};

#[cfg(feature = "aes")]
pub use aes;

#[cfg(feature = "aes")]
pub use xaes::XaesGcm256;

use cipher::{
array::{Array, ArraySize},
consts::{U0, U16},
Expand Down
127 changes: 127 additions & 0 deletions aes-gcm/src/xaes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use core::ops::{Div, Mul};

use aead::{array::Array, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
use cipher::{consts::U2, BlockCipherEncrypt, BlockSizeUser};

use crate::{Aes256, Aes256Gcm, Nonce, Tag};

/// XAES-256-GCM
#[derive(Clone)]
pub struct XaesGcm256 {
aes: Aes256,
k1: Block,
}

type KeySize = <Aes256Gcm as KeySizeUser>::KeySize;
type NonceSize = <<Aes256Gcm as AeadCore>::NonceSize as Mul<U2>>::Output;
type TagSize = <Aes256Gcm as AeadCore>::TagSize;
type CiphertextOverhead = <Aes256Gcm as AeadCore>::CiphertextOverhead;
type Block = Array<u8, <Aes256 as BlockSizeUser>::BlockSize>;

/// Maximum length of plaintext.
pub const P_MAX: u64 = 1 << 36;

/// Maximum length of associated data.
// pub const A_MAX: u64 = 1 << 61;
pub const A_MAX: u64 = 1 << 36;

/// Maximum length of ciphertext.
pub const C_MAX: u64 = (1 << 36) + 16;

impl AeadCore for XaesGcm256 {
type NonceSize = NonceSize;
type TagSize = TagSize;
type CiphertextOverhead = CiphertextOverhead;
}

impl KeySizeUser for XaesGcm256 {
type KeySize = KeySize;
}

impl KeyInit for XaesGcm256 {
// Implements step 1 and 2 of the spec.
fn new(key: &Key<Aes256>) -> Self {
let aes = Aes256::new(key);

// L = AES-256ₖ(0¹²⁸)
let mut k1 = Block::default();
aes.encrypt_block(&mut k1);

// If MSB₁(L) = 0 then K1 = L << 1 Else K1 = (L << 1) ⊕ 0¹²⁰10000111
let mut msb = 0;
for i in (0..k1.len()).rev() {
let new_msb = k1[i] >> 7;
k1[i] = (k1[i] << 1) | msb;
msb = new_msb;
}

let b = k1.len() - 1;
k1[b] ^= msb * 0b10000111;

Self { aes, k1 }
}
}

impl AeadInPlace for XaesGcm256 {
fn encrypt_in_place_detached(
&self,
nonce: &Nonce<NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag<TagSize>, Error> {
if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}

let (n1, n) = nonce.split_ref::<<NonceSize as Div<U2>>::Output>();
let k = self.derive_key(n1);
Aes256Gcm::new(&k).encrypt_in_place_detached(n, associated_data, buffer)
}

fn decrypt_in_place_detached(
&self,
nonce: &Nonce<NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag<TagSize>,
) -> Result<(), Error> {
if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}

let (n1, n) = nonce.split_ref::<<NonceSize as Div<U2>>::Output>();
let k = self.derive_key(n1);
Aes256Gcm::new(&k).decrypt_in_place_detached(n, associated_data, buffer, tag)
}
}

impl XaesGcm256 {
// Implements steps 3 - 5 of the spec.
fn derive_key(&self, n1: &Nonce<<NonceSize as Div<U2>>::Output>) -> Key<Aes256Gcm> {
// M1 = 0x00 || 0x01 || X || 0x00 || N[:12]
let mut m1 = Block::default();
m1[..4].copy_from_slice(&[0, 1, b'X', 0]);
m1[4..].copy_from_slice(n1);

// M2 = 0x00 || 0x02 || X || 0x00 || N[:12]
let mut m2 = Block::default();
m2[..4].copy_from_slice(&[0, 2, b'X', 0]);
m2[4..].copy_from_slice(n1);

// Kₘ = AES-256ₖ(M1 ⊕ K1)
// Kₙ = AES-256ₖ(M2 ⊕ K1)
// Kₓ = Kₘ || Kₙ = AES-256ₖ(M1 ⊕ K1) || AES-256ₖ(M2 ⊕ K1)
let mut key: Key<Aes256Gcm> = Array::default();
let (mut km, mut kn) = key.split_ref_mut::<<KeySize as Div<U2>>::Output>();
for i in 0..km.len() {
km[i] = m1[i] ^ self.k1[i];
}
for i in 0..kn.len() {
kn[i] = m2[i] ^ self.k1[i];
}

self.aes.encrypt_block(&mut km);
self.aes.encrypt_block(&mut kn);
key
}
}
11 changes: 7 additions & 4 deletions aes-gcm/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#[derive(Debug)]
pub struct TestVector<K: 'static> {
pub key: &'static K,
pub nonce: &'static [u8; 12],
pub nonce: &'static [u8],
pub aad: &'static [u8],
pub plaintext: &'static [u8],
pub ciphertext: &'static [u8],
Expand All @@ -27,8 +27,11 @@ macro_rules! tests {
let cipher = <$aead>::new(key);
let ciphertext = cipher.encrypt(nonce, payload).unwrap();
let (ct, tag) = ciphertext.split_at(ciphertext.len() - 16);
assert_eq!(vector.ciphertext, ct);
assert_eq!(vector.tag, tag);
assert_eq!(
vector.ciphertext, ct,
"ciphertext mismatch (expected != actual)"
);
assert_eq!(vector.tag, tag, "tag mismatch (expected != actual)");
}
}

Expand All @@ -48,7 +51,7 @@ macro_rules! tests {
let cipher = <$aead>::new(key);
let plaintext = cipher.decrypt(nonce, payload).unwrap();

assert_eq!(vector.plaintext, plaintext.as_slice());
assert_eq!(vector.plaintext, plaintext.as_slice(), "plaintext mismatch");
}
}

Expand Down
35 changes: 35 additions & 0 deletions aes-gcm/tests/xaes256gcm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! XAES-256-GCM test vectors
#![cfg(all(feature = "aes", feature = "alloc"))]

#[macro_use]
mod common;

use aes_gcm::aead::{array::Array, Aead, AeadInPlace, KeyInit, Payload};
use aes_gcm::XaesGcm256;
use common::TestVector;
use hex_literal::hex;

/// C2SP XAES-256-GCM test vectors
///
/// <https://github.com/C2SP/C2SP/blob/main/XAES-256-GCM.md>
const TEST_VECTORS: &[TestVector<[u8; 32]>] = &[
TestVector {
key: &hex!("0101010101010101010101010101010101010101010101010101010101010101"),
nonce: b"ABCDEFGHIJKLMNOPQRSTUVWX",
plaintext: b"XAES-256-GCM",
aad: b"",
ciphertext: &hex!("ce546ef63c9cc60765923609"),
tag: &hex!("b33a9a1974e96e52daf2fcf7075e2271"),
},
TestVector {
key: &hex!("0303030303030303030303030303030303030303030303030303030303030303"),
nonce: b"ABCDEFGHIJKLMNOPQRSTUVWX",
plaintext: b"XAES-256-GCM",
aad: b"c2sp.org/XAES-256-GCM",
ciphertext: &hex!("986ec1832593df5443a17943"),
tag: &hex!("7fd083bf3fdb41abd740a21f71eb769d"),
},
];

tests!(XaesGcm256, TEST_VECTORS);

0 comments on commit 90897dd

Please sign in to comment.