Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion make_test_vectors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TEST_VECTORS=test-vectors

cd reference-implementation

for kind in header aes-ctr-hmac sframe;
for kind in header aes-ctr-hmac aes256-ctr-hmac sframe;
do
cargo run --example test_vectors md ${kind} >../test-vectors/${kind}.md
done
Expand Down
151 changes: 150 additions & 1 deletion reference-implementation/examples/test_vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,150 @@ ct: {ct:4}
}
}

mod aes256_ctr_hmac {
use super::Hex;
use aead::{Aead, Key, KeyInit, KeySizeUser, Nonce, Payload};
use aes::Aes256;
use cipher::{
consts::{U10, U32, U4, U8},
ArrayLength,
};
use hex_literal::hex;
use serde::{Deserialize, Serialize};
use sframe_reference::{aes_ctr_hmac::*, cipher::CipherSuite};
use sha2::Sha512;

#[derive(Serialize, Deserialize)]
pub struct TestVector {
cipher_suite: u16,
key: Hex,
enc_key: Hex,
auth_key: Hex,
nonce: Hex,
aad: Hex,
pt: Hex,
ct: Hex,
}

impl TestVector {
fn new<C, D, T>() -> Self
where
C: Cipher + KeySizeUser<KeySize = U32>,
D: Digest,
T: ArrayLength<u8>,
AesCtrHmac<C, D, T>: KeySizeUser + KeyInit,
{
let cipher_suite = match T::to_usize() {
10 => CipherSuite::AES_256_CTR_HMAC_SHA_512_80,
8 => CipherSuite::AES_256_CTR_HMAC_SHA_512_64,
4 => CipherSuite::AES_256_CTR_HMAC_SHA_512_32,
_ => unreachable!(),
};

let key = hex!("000102030405060708090a0b0c0d0e0f"
"101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f");
let key = Key::<AesCtrHmac<C, D, T>>::clone_from_slice(&key);
let nonce: Nonce<AesCtrHmac<C, D, T>> = hex!("101112131415161718191a1b").into();
let aad = b"IETF SFrame WG";
let pt = b"draft-ietf-sframe-enc";

let cipher = AesCtrHmac::<C, D, T>::new(&key);
let ct = cipher.encrypt(&nonce, Payload { msg: pt, aad }).unwrap();

Self {
cipher_suite: cipher_suite.0,
key: Hex::from(key),
enc_key: Hex::from(cipher.enc_key),
auth_key: Hex::from(cipher.auth_key),
nonce: Hex::from(nonce),
aad: Hex::from(aad),
pt: Hex::from(pt),
ct: Hex::from(ct),
}
}

pub fn make_all() -> Vec<Self> {
vec![
Self::new::<Aes256, Sha512, U10>(),
Self::new::<Aes256, Sha512, U8>(),
Self::new::<Aes256, Sha512, U4>(),
]
}

fn verify_one<C, D, T>(&self) -> bool
where
C: Cipher + KeySizeUser<KeySize = U32>,
D: Digest,
T: ArrayLength<u8>,
AesCtrHmac<C, D, T>: KeySizeUser + KeyInit,
{
let key = Key::<AesCtrHmac<C, D, T>>::from_slice(&self.key);
let nonce = Nonce::<AesCtrHmac<C, D, T>>::from_slice(&self.nonce);

let cipher = AesCtrHmac::<C, D, T>::new(&key);

let payload = Payload {
msg: &self.pt,
aad: &self.aad,
};
let encrypted = cipher.encrypt(&nonce, payload).unwrap();
let encrypt_pass = self.ct == encrypted;

let payload = Payload {
msg: &self.ct,
aad: &self.aad,
};
let decrypted = cipher.decrypt(&nonce, payload).unwrap();
let decrypt_pass = self.pt == decrypted;

encrypt_pass && decrypt_pass
}

pub fn verify(&self) -> bool {
match CipherSuite(self.cipher_suite) {
CipherSuite::AES_256_CTR_HMAC_SHA_512_80 => {
self.verify_one::<Aes256, Sha512, U10>()
}
CipherSuite::AES_256_CTR_HMAC_SHA_512_64 => self.verify_one::<Aes256, Sha512, U8>(),
CipherSuite::AES_256_CTR_HMAC_SHA_512_32 => self.verify_one::<Aes256, Sha512, U4>(),
_ => unreachable!(),
}
}
}

impl super::ToMarkdown for TestVector {
fn to_markdown(&self) -> String {
let TestVector {
cipher_suite,
key,
enc_key,
auth_key,
nonce,
aad,
pt,
ct,
} = self;

format!(
"~~~ test-vectors
cipher_suite: 0x{cipher_suite:04x}
key: {key:5}
enc_key: {enc_key:9}
auth_key: {auth_key:10}
nonce: {nonce:7}
aad: {aad:5}
pt: {pt:4}
ct: {ct:4}
~~~"
)
}
}
}

mod sframe {
use super::Hex;
use hex_literal::hex;
Expand Down Expand Up @@ -393,6 +537,7 @@ impl<T: AsRef<[u8]>> PartialEq<T> for Hex {
enum TestVectorType {
Header,
AesCtrHmac,
Aes256CtrHmac,
Sframe,
}

Expand All @@ -404,6 +549,7 @@ trait ToMarkdown {
struct TestVectors {
header: Vec<header::TestVector>,
aes_ctr_hmac: Vec<aes_ctr_hmac::TestVector>,
aes256_ctr_hmac: Vec<aes256_ctr_hmac::TestVector>,
sframe: Vec<sframe::TestVector>,
}

Expand All @@ -412,6 +558,7 @@ impl TestVectors {
Self {
header: header::TestVector::make_all(),
aes_ctr_hmac: aes_ctr_hmac::TestVector::make_all(),
aes256_ctr_hmac: aes256_ctr_hmac::TestVector::make_all(),
sframe: sframe::TestVector::make_all(),
}
}
Expand All @@ -420,8 +567,9 @@ impl TestVectors {
let header = self.header.iter().map(|tv| tv.verify());
let aes_ctr_hmac = self.aes_ctr_hmac.iter().map(|tv| tv.verify());
let sframe = self.sframe.iter().map(|tv| tv.verify());
let aes256_ctr_hmac = self.aes256_ctr_hmac.iter().map(|tv| tv.verify());

header.chain(aes_ctr_hmac).chain(sframe).all(|x| x)
header.chain(aes_ctr_hmac).chain(aes256_ctr_hmac).chain(sframe).all(|x| x)
}

fn print_md_all<T: ToMarkdown>(vecs: &[T]) {
Expand All @@ -434,6 +582,7 @@ impl TestVectors {
match vec_type {
TestVectorType::Header => Self::print_md_all(&self.header),
TestVectorType::AesCtrHmac => Self::print_md_all(&self.aes_ctr_hmac),
TestVectorType::Aes256CtrHmac => Self::print_md_all(&self.aes256_ctr_hmac),
TestVectorType::Sframe => Self::print_md_all(&self.sframe),
}
}
Expand Down
73 changes: 72 additions & 1 deletion reference-implementation/src/aes_ctr_hmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ where

#[cfg(test)]
#[generic_tests::define]
mod test {
mod test_aes128 {
use super::*;
use aead::Aead;
use aes::Aes128;
Expand Down Expand Up @@ -233,3 +233,74 @@ mod test {
#[instantiate_tests(<Aes128, Sha256, U4>)]
mod aes_128_ctr_sha_256_32 {}
}

#[cfg(test)]
#[generic_tests::define]
mod test_aes256 {
Comment on lines +237 to +239

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be in a different module.

use super::*;
use aead::Aead;
use aes::Aes256;
use cipher::consts::{U10, U4, U8};
use hex_literal::hex;
use sha2::Sha512;

#[test]
fn round_trip<C, D, T>()
where
C: Cipher + KeySizeUser<KeySize = U32>,
D: Digest,
T: ArrayLength<u8>,
AesCtrHmac<C, D, T>: KeySizeUser + KeyInit,
{
let key = hex!("000102030405060708090a0b0c0d0e0f"
"101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f");
let key = Key::<AesCtrHmac<C, D, T>>::clone_from_slice(&key);
let nonce: Nonce<AesCtrHmac<C, D, T>> = hex!("101112131415161718191a1b").into();
let msg = b"Never gonna give you up";
let aad = b"Never gonna let you down";

let cipher = AesCtrHmac::<C, D, T>::new(&key);

// Verify that an encrypt/decrypt round-trip works
let encrypt_payload = Payload { msg, aad };
let encrypted = cipher.encrypt(&nonce, encrypt_payload).unwrap();
assert_eq!(encrypted.len(), msg.len() + T::to_usize());

let decrypt_payload = Payload {
msg: &encrypted,
aad,
};
let decrypted = cipher.decrypt(&nonce, decrypt_payload).unwrap();
assert_eq!(&decrypted, msg);

// Verify that trying to decrypt with different AAD fails
let different_aad = b"Never gonna run around and hurt you";
let different_aad_payload = Payload {
msg: &encrypted,
aad: different_aad,
};
assert!(cipher.decrypt(&nonce, different_aad_payload).is_err());

// Verify that trying to decrypt with corrupted ciphertext fails
let mut different_msg = encrypted.clone();
different_msg[0] ^= 0xff;
let different_msg_payload = Payload {
msg: &different_msg,
aad,
};
assert!(cipher.decrypt(&nonce, different_msg_payload).is_err());
}

#[instantiate_tests(<Aes256, Sha512, U10>)]
mod aes_256_ctr_sha_512_80 {}

#[instantiate_tests(<Aes256, Sha512, U8>)]
mod aes_256_ctr_sha_256_64 {}

#[instantiate_tests(<Aes256, Sha512, U4>)]
mod aes_256_ctr_sha_512_32 {}
}
28 changes: 26 additions & 2 deletions reference-implementation/src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::header::{Header, KeyId};
use crate::{Error, Result};

use aead::{AeadCore, Key, KeyInit, KeySizeUser, Nonce, Payload};
use aes::Aes128;
use aes::{Aes128, Aes256};
use aes_gcm::{Aes128Gcm, Aes256Gcm};
use cipher::consts::{U10, U4, U8};
use cipher::Unsigned;
Expand Down Expand Up @@ -32,15 +32,27 @@ impl CipherSuite {

/// AES-256-GCM, with a full 16-byte tag
pub const AES_256_GCM_SHA_512: CipherSuite = CipherSuite(0x0005);

/// AES-256-CTR with HMAC-SHA-512, with a 10-byte tag
pub const AES_256_CTR_HMAC_SHA_512_80: CipherSuite = CipherSuite(0x0006);

/// AES-256-CTR with HMAC-SHA-512, with an 8-byte tag
pub const AES_256_CTR_HMAC_SHA_512_64: CipherSuite = CipherSuite(0x0007);

/// AES-256-CTR with HMAC-SHA-512, with an 4-byte tag
pub const AES_256_CTR_HMAC_SHA_512_32: CipherSuite = CipherSuite(0x0008);
}

/// A list of all available ciphersuites
pub const ALL_CIPHER_SUITES: [CipherSuite; 5] = [
pub const ALL_CIPHER_SUITES: [CipherSuite; 8] = [
CipherSuite::AES_128_CTR_HMAC_SHA_256_80,
CipherSuite::AES_128_CTR_HMAC_SHA_256_64,
CipherSuite::AES_128_CTR_HMAC_SHA_256_32,
CipherSuite::AES_128_GCM_SHA_256,
CipherSuite::AES_256_GCM_SHA_512,
CipherSuite::AES_256_CTR_HMAC_SHA_512_80,
CipherSuite::AES_256_CTR_HMAC_SHA_512_64,
CipherSuite::AES_256_CTR_HMAC_SHA_512_32,
];

/// A convenience trait summarizing all of the salient aspects of an AEAD cipher.
Expand Down Expand Up @@ -246,6 +258,9 @@ type Aes128CtrHmacSha256_64 = CipherImpl<AesCtrHmac<Aes128, Sha256, U8>>;
type Aes128CtrHmacSha256_32 = CipherImpl<AesCtrHmac<Aes128, Sha256, U4>>;
type Aes128GcmSha256 = CipherImpl<Aes128Gcm>;
type Aes256GcmSha512 = CipherImpl<Aes256Gcm>;
type Aes256CtrHmacSha512_80 = CipherImpl<AesCtrHmac<Aes256, Sha512, U10>>;
type Aes256CtrHmacSha512_64 = CipherImpl<AesCtrHmac<Aes256, Sha512, U8>>;
type Aes256CtrHmacSha512_32 = CipherImpl<AesCtrHmac<Aes256, Sha512, U4>>;

/// Construct a new cipher for the specified ciphersuite. The key and salt for the cipher are
/// derived from the `base_key` and `kid`.
Expand All @@ -266,6 +281,15 @@ pub fn new_cipher(cipher_suite: CipherSuite, kid: KeyId, base_key: &[u8]) -> Box
CipherSuite::AES_256_GCM_SHA_512 => {
Box::new(Aes256GcmSha512::new::<Sha512>(cipher_suite, kid, base_key))
}
CipherSuite::AES_256_CTR_HMAC_SHA_512_80 => Box::new(
Aes256CtrHmacSha512_80::new::<Sha512>(cipher_suite, kid, base_key),
),
CipherSuite::AES_256_CTR_HMAC_SHA_512_64 => Box::new(
Aes256CtrHmacSha512_64::new::<Sha512>(cipher_suite, kid, base_key),
),
CipherSuite::AES_256_CTR_HMAC_SHA_512_32 => Box::new(
Aes256CtrHmacSha512_32::new::<Sha512>(cipher_suite, kid, base_key),
),
_ => unreachable!(),
}
}
Expand Down
Loading