Skip to content
582 changes: 582 additions & 0 deletions aws-lc-rs/src/cmac.rs

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions aws-lc-rs/src/cmac/tests/fips.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#![cfg(debug_assertions)]

use crate::cmac::{sign, verify, Key, AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY};
use crate::fips::{assert_fips_status_indicator, FipsServiceStatus};
use crate::rand::{self, SystemRandom};

const TEST_MESSAGE: &str = "test message";

macro_rules! cmac_api {
($name:ident, $alg:expr, $key_len:expr, $expect:path) => {
#[test]
fn $name() -> Result<(), Box<dyn std::error::Error>> {
let rng = SystemRandom::new();

let key_value: [u8; $key_len] = rand::generate(&rng).unwrap().expose();

let s_key = Key::new($alg, key_value.as_ref()).unwrap();

let tag = assert_fips_status_indicator!(
sign(&s_key, TEST_MESSAGE.as_bytes())?,
$expect
);

let v_key = Key::new($alg, key_value.as_ref()).unwrap();

assert_fips_status_indicator!(
verify(&v_key, TEST_MESSAGE.as_bytes(), tag.as_ref())?,
$expect
);

Ok(())
}
};
}

cmac_api!(aes_128, AES_128, 16, FipsServiceStatus::Approved);
cmac_api!(aes_192, AES_192, 24, FipsServiceStatus::NonApproved);
cmac_api!(aes_256, AES_256, 32, FipsServiceStatus::Approved);
cmac_api!(tdes, TDES_FOR_LEGACY_USE_ONLY, 24, FipsServiceStatus::NonApproved);
1 change: 1 addition & 0 deletions aws-lc-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ extern crate aws_lc_sys as aws_lc;

pub mod aead;
pub mod agreement;
pub mod cmac;
pub mod constant_time;
pub mod digest;
pub mod error;
Expand Down
5 changes: 3 additions & 2 deletions aws-lc-rs/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 OR ISC

use crate::aws_lc::{
BN_free, ECDSA_SIG_free, EC_GROUP_free, EC_KEY_free, EC_POINT_free, EVP_AEAD_CTX_free,
BN_free, CMAC_CTX_free, ECDSA_SIG_free, EC_GROUP_free, EC_KEY_free, EC_POINT_free, EVP_AEAD_CTX_free,
EVP_CIPHER_CTX_free, EVP_PKEY_CTX_free, EVP_PKEY_free, OPENSSL_free, RSA_free, BIGNUM,
ECDSA_SIG, EC_GROUP, EC_KEY, EC_POINT, EVP_AEAD_CTX, EVP_CIPHER_CTX, EVP_PKEY, EVP_PKEY_CTX,
CMAC_CTX, ECDSA_SIG, EC_GROUP, EC_KEY, EC_POINT, EVP_AEAD_CTX, EVP_CIPHER_CTX, EVP_PKEY, EVP_PKEY_CTX,
RSA,
};
use core::ops::Deref;
Expand Down Expand Up @@ -271,6 +271,7 @@ create_pointer!(EVP_PKEY_CTX, EVP_PKEY_CTX_free);
create_pointer!(RSA, RSA_free);
create_pointer!(EVP_AEAD_CTX, EVP_AEAD_CTX_free);
create_pointer!(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free);
create_pointer!(CMAC_CTX, CMAC_CTX_free);

#[cfg(test)]
mod tests {
Expand Down
139 changes: 139 additions & 0 deletions aws-lc-rs/tests/cmac_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use aws_lc_rs::{cmac, test, test_file};

#[test]
fn cavp_cmac_aes128_tests() {
test::run(test_file!("data/cavp_aes128_cmac_tests.txt"), |section, test_case| {
assert_eq!(section, "");

let _count = test_case.consume_usize("Count");
let _klen = test_case.consume_usize("Klen");
let mlen = test_case.consume_usize("Mlen");
let tlen = test_case.consume_usize("Tlen");
let key = test_case.consume_bytes("Key");
let msg = test_case.consume_bytes("Msg");
let mac = test_case.consume_bytes("Mac");
let result = test_case.consume_string("Result");

let input = if mlen == 0 { Vec::new() } else { msg };
let should_pass = result.starts_with('P');

let cmac_key = cmac::Key::new(cmac::AES_128, &key).unwrap();
let signature = cmac::sign(&cmac_key, &input).unwrap();

// Truncate to tlen
let truncated_sig = &signature.as_ref()[..std::cmp::min(signature.as_ref().len(), tlen)];
Copy link
Contributor

Choose a reason for hiding this comment

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

The truncation used here and in the other functions is not needed -- it would actually hide a bug were we to produce too long of a signature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I copied these tests data, and test functions from the aws-lc. The original tests also does a truncation. Without the truncation, the tests start to fail.
https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/cmac/cmac_test.cc#L226-L227

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, we should just trancate it to tlen then -- it should never be longer than the actual tag:

let truncated_sig = &signature.as_ref()[..tlen];


if should_pass {
assert_eq!(truncated_sig, &mac);
} else {
assert_ne!(truncated_sig, &mac);
}

Ok(())
});
}

#[test]
fn cavp_cmac_aes192_tests() {
test::run(test_file!("data/cavp_aes192_cmac_tests.txt"), |section, test_case| {
assert_eq!(section, "");

let _count = test_case.consume_usize("Count");
let _klen = test_case.consume_usize("Klen");
let mlen = test_case.consume_usize("Mlen");
let tlen = test_case.consume_usize("Tlen");
let key = test_case.consume_bytes("Key");
let msg = test_case.consume_bytes("Msg");
let mac = test_case.consume_bytes("Mac");
let result = test_case.consume_string("Result");

let input = if mlen == 0 { Vec::new() } else { msg };
let should_pass = result.starts_with('P');

let cmac_key = cmac::Key::new(cmac::AES_192, &key).unwrap();
let signature = cmac::sign(&cmac_key, &input).unwrap();

// Truncate to tlen
let truncated_sig = &signature.as_ref()[..std::cmp::min(signature.as_ref().len(), tlen)];

if should_pass {
assert_eq!(truncated_sig, &mac);
} else {
assert_ne!(truncated_sig, &mac);
}

Ok(())
});
}

#[test]
fn cavp_cmac_aes256_tests() {
test::run(test_file!("data/cavp_aes256_cmac_tests.txt"), |section, test_case| {
assert_eq!(section, "");

let _count = test_case.consume_usize("Count");
let _klen = test_case.consume_usize("Klen");
let mlen = test_case.consume_usize("Mlen");
let tlen = test_case.consume_usize("Tlen");
let key = test_case.consume_bytes("Key");
let msg = test_case.consume_bytes("Msg");
let mac = test_case.consume_bytes("Mac");
let result = test_case.consume_string("Result");

let input = if mlen == 0 { Vec::new() } else { msg };
let should_pass = result.starts_with('P');

let cmac_key = cmac::Key::new(cmac::AES_256, &key).unwrap();
let signature = cmac::sign(&cmac_key, &input).unwrap();

// Truncate to tlen
let truncated_sig = &signature.as_ref()[..std::cmp::min(signature.as_ref().len(), tlen)];

if should_pass {
assert_eq!(truncated_sig, &mac);
} else {
assert_ne!(truncated_sig, &mac);
}

Ok(())
});
}

#[test]
fn cavp_cmac_3des_tests() {
test::run(test_file!("data/cavp_3des_cmac_tests.txt"), |section, test_case| {
assert_eq!(section, "");

let _count = test_case.consume_usize("Count");
let _klen = test_case.consume_usize("Klen");
let mlen = test_case.consume_usize("Mlen");
let tlen = test_case.consume_usize("Tlen");
let key1 = test_case.consume_bytes("Key1");
let key2 = test_case.consume_bytes("Key2");
let key3 = test_case.consume_bytes("Key3");
let msg = test_case.consume_bytes("Msg");
let mac = test_case.consume_bytes("Mac");
let result = test_case.consume_string("Result");

// Combine 3DES keys
let mut combined_key = key1;
combined_key.extend(key2);
combined_key.extend(key3);

let input = if mlen == 0 { Vec::new() } else { msg };
let should_pass = result.starts_with('P');

let cmac_key = cmac::Key::new(cmac::TDES_FOR_LEGACY_USE_ONLY, &combined_key).unwrap();
let signature = cmac::sign(&cmac_key, &input).unwrap();

// Truncate to tlen
let truncated_sig = &signature.as_ref()[..std::cmp::min(signature.as_ref().len(), tlen)];

if should_pass {
assert_eq!(truncated_sig, &mac);
} else {
assert_ne!(truncated_sig, &mac);
}
Ok(())
});
}
Loading
Loading