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

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions aws-lc-rs/src/cmac/tests/fips.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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
9 changes: 5 additions & 4 deletions aws-lc-rs/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// 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,
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,
RSA,
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, 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;
use std::marker::PhantomData;
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
158 changes: 158 additions & 0 deletions aws-lc-rs/tests/cmac_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

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)];

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