Merge pull request #10070 from holtrop-wolfssl/rust-rand_core-aead-cipher

Rust wrapper: add rand_core, aead, cipher trait implementations
This commit is contained in:
David Garske
2026-04-13 10:00:26 -07:00
committed by GitHub
9 changed files with 2533 additions and 9 deletions
+102
View File
@@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"blobby",
"crypto-common 0.1.7",
"generic-array",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -37,6 +48,12 @@ version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "blobby"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec"
[[package]]
name = "cexpr"
version = "0.6.0"
@@ -52,6 +69,16 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "cipher"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea"
dependencies = [
"crypto-common 0.2.1",
"inout",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -63,18 +90,66 @@ dependencies = [
"libloading",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"typenum",
]
[[package]]
name = "crypto-common"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
dependencies = [
"hybrid-array",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hybrid-array"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1"
dependencies = [
"typenum",
]
[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"hybrid-array",
]
[[package]]
name = "itertools"
version = "0.13.0"
@@ -156,6 +231,18 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_core"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
[[package]]
name = "regex"
version = "1.11.2"
@@ -208,12 +295,24 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "windows-link"
version = "0.1.3"
@@ -289,6 +388,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
name = "wolfssl-wolfcrypt"
version = "1.2.0"
dependencies = [
"aead",
"bindgen",
"cipher",
"rand_core 0.10.0",
"regex",
]
+12
View File
@@ -12,6 +12,18 @@ readme = "README.md"
[features]
std = []
rand_core = ["dep:rand_core"]
aead = ["dep:aead"]
cipher = ["dep:cipher"]
[dependencies]
rand_core = { version = "0.10", optional = true, default-features = false }
aead = { version = "0.5", optional = true, default-features = false }
cipher = { version = "0.5", optional = true, default-features = false }
[dev-dependencies]
aead = { version = "0.5", features = ["alloc", "dev"] }
cipher = "0.5"
[build-dependencies]
bindgen = "0.72.1"
+8 -5
View File
@@ -1,16 +1,19 @@
FEATURES := rand_core,aead,cipher
CARGO_FEATURE_FLAGS := --features $(FEATURES)
.PHONY: all
all:
cargo build
cargo clippy
cargo doc
cargo build $(CARGO_FEATURE_FLAGS)
cargo clippy $(CARGO_FEATURE_FLAGS)
cargo doc $(CARGO_FEATURE_FLAGS)
.PHONY: test
test:
cargo test -- --test-threads=1
cargo test $(CARGO_FEATURE_FLAGS) -- --test-threads=1
.PHONY: testfips
testfips:
cargo test --lib --bins --tests -- --test-threads=1
cargo test $(CARGO_FEATURE_FLAGS) --lib --bins --tests -- --test-threads=1
.PHONY: clean
clean:
File diff suppressed because it is too large Load Diff
@@ -155,8 +155,7 @@ impl ChaCha20Poly1305 {
return Err(rc);
}
let wc_ccp = unsafe { wc_ccp.assume_init() };
let chacha20poly1305 = ChaCha20Poly1305 { wc_ccp };
Ok(chacha20poly1305)
Ok(ChaCha20Poly1305 { wc_ccp })
}
/// Update AAD (additional authenticated data).
@@ -244,10 +243,95 @@ impl ChaCha20Poly1305 {
}
}
#[cfg(xchacha20_poly1305)]
pub struct XChaCha20Poly1305 {
// ---------------------------------------------------------------------------
// ChaCha20-Poly1305 aead trait implementations
// ---------------------------------------------------------------------------
/// ChaCha20-Poly1305 AEAD instance holding a key for use with the
/// `aead::KeyInit` and `aead::AeadInPlace` traits.
#[cfg(feature = "aead")]
pub struct ChaCha20Poly1305Aead {
key: [u8; 32],
}
#[cfg(feature = "aead")]
impl aead::KeySizeUser for ChaCha20Poly1305Aead {
type KeySize = aead::generic_array::typenum::U32;
}
#[cfg(feature = "aead")]
impl aead::AeadCore for ChaCha20Poly1305Aead {
type NonceSize = aead::generic_array::typenum::U12;
type TagSize = aead::generic_array::typenum::U16;
type CiphertextOverhead = aead::generic_array::typenum::U0;
}
#[cfg(feature = "aead")]
impl aead::KeyInit for ChaCha20Poly1305Aead {
fn new(key: &aead::Key<Self>) -> Self {
let mut k = [0u8; 32];
k.copy_from_slice(key.as_ref());
ChaCha20Poly1305Aead { key: k }
}
}
#[cfg(feature = "aead")]
impl aead::AeadInPlace for ChaCha20Poly1305Aead {
fn encrypt_in_place_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<aead::Tag<Self>, aead::Error> {
let mut tag = aead::Tag::<Self>::default();
// wc_ChaCha20Poly1305_Encrypt supports in-place (out == in).
let buf_ptr = buffer.as_mut_ptr();
let in_ptr = buf_ptr as *const u8;
let nonce_bytes: &[u8] = nonce;
let tag_bytes: &mut [u8] = &mut tag;
let rc = unsafe {
sys::wc_ChaCha20Poly1305_Encrypt(
self.key.as_ptr(), nonce_bytes.as_ptr(),
associated_data.as_ptr(), associated_data.len() as u32,
in_ptr, buffer.len() as u32,
buf_ptr, tag_bytes.as_mut_ptr(),
)
};
if rc != 0 {
return Err(aead::Error);
}
Ok(tag)
}
fn decrypt_in_place_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &aead::Tag<Self>,
) -> Result<(), aead::Error> {
let buf_ptr = buffer.as_mut_ptr();
let in_ptr = buf_ptr as *const u8;
let nonce_bytes: &[u8] = nonce;
let tag_bytes: &[u8] = tag;
let rc = unsafe {
sys::wc_ChaCha20Poly1305_Decrypt(
self.key.as_ptr(), nonce_bytes.as_ptr(),
associated_data.as_ptr(), associated_data.len() as u32,
in_ptr, buffer.len() as u32,
tag_bytes.as_ptr(), buf_ptr,
)
};
if rc != 0 {
return Err(aead::Error);
}
Ok(())
}
}
#[cfg(xchacha20_poly1305)]
pub struct XChaCha20Poly1305;
#[cfg(xchacha20_poly1305)]
impl XChaCha20Poly1305 {
/// Key size for XChaCha20-Poly1305 stream cipher.
@@ -340,3 +424,111 @@ impl XChaCha20Poly1305 {
Ok(())
}
}
// ---------------------------------------------------------------------------
// XChaCha20-Poly1305 aead trait implementations
// ---------------------------------------------------------------------------
/// XChaCha20-Poly1305 AEAD instance holding a key for use with the
/// `aead::KeyInit` and `aead::AeadInPlace` traits.
#[cfg(all(xchacha20_poly1305, feature = "aead"))]
pub struct XChaCha20Poly1305Aead {
key: [u8; 32],
}
#[cfg(all(xchacha20_poly1305, feature = "aead"))]
impl aead::KeySizeUser for XChaCha20Poly1305Aead {
type KeySize = aead::generic_array::typenum::U32;
}
#[cfg(all(xchacha20_poly1305, feature = "aead"))]
impl aead::AeadCore for XChaCha20Poly1305Aead {
type NonceSize = aead::generic_array::typenum::U24;
type TagSize = aead::generic_array::typenum::U16;
type CiphertextOverhead = aead::generic_array::typenum::U0;
}
#[cfg(all(xchacha20_poly1305, feature = "aead"))]
impl aead::KeyInit for XChaCha20Poly1305Aead {
fn new(key: &aead::Key<Self>) -> Self {
let mut k = [0u8; 32];
k.copy_from_slice(key.as_ref());
XChaCha20Poly1305Aead { key: k }
}
}
#[cfg(all(xchacha20_poly1305, feature = "aead"))]
impl aead::AeadInPlace for XChaCha20Poly1305Aead {
// This function can encrypt a maximum of 4096 bytes.
fn encrypt_in_place_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<aead::Tag<Self>, aead::Error> {
// wc_XChaCha20Poly1305_Encrypt writes ciphertext + 16-byte tag into a
// single output buffer. Use a stack buffer to hold both, then split
// the tag out and copy the ciphertext back over the caller's buffer.
const MAX_INLINE: usize = 4096;
debug_assert!(buffer.len() <= MAX_INLINE, "Maximum of 4096 bytes supported");
if buffer.len() > MAX_INLINE {
return Err(aead::Error);
}
let out_len = buffer.len() + 16;
let mut out_buf = [0u8; MAX_INLINE + 16];
let nonce_bytes: &[u8] = nonce;
let rc = unsafe {
sys::wc_XChaCha20Poly1305_Encrypt(
out_buf.as_mut_ptr(), out_len,
buffer.as_ptr(), buffer.len(),
associated_data.as_ptr(), associated_data.len(),
nonce_bytes.as_ptr(), nonce_bytes.len(),
self.key.as_ptr(), self.key.len(),
)
};
if rc != 0 {
return Err(aead::Error);
}
buffer.copy_from_slice(&out_buf[..buffer.len()]);
let mut tag = aead::Tag::<Self>::default();
let tag_bytes: &mut [u8] = &mut tag;
tag_bytes.copy_from_slice(&out_buf[buffer.len()..out_len]);
Ok(tag)
}
// This function can decrypt a maximum of 4096 bytes.
fn decrypt_in_place_detached(
&self,
nonce: &aead::Nonce<Self>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &aead::Tag<Self>,
) -> Result<(), aead::Error> {
// wc_XChaCha20Poly1305_Decrypt expects the auth tag appended after the
// ciphertext. Build a combined [ciphertext | tag] buffer on the stack.
const MAX_INLINE: usize = 4096;
debug_assert!(buffer.len() <= MAX_INLINE, "Maximum of 4096 bytes supported");
if buffer.len() > MAX_INLINE {
return Err(aead::Error);
}
let mut in_buf = [0u8; MAX_INLINE + 16];
let in_len = buffer.len() + 16;
in_buf[..buffer.len()].copy_from_slice(buffer);
let tag_bytes: &[u8] = tag;
in_buf[buffer.len()..in_len].copy_from_slice(tag_bytes);
let nonce_bytes: &[u8] = nonce;
let rc = unsafe {
sys::wc_XChaCha20Poly1305_Decrypt(
buffer.as_mut_ptr(), buffer.len(),
in_buf.as_ptr(), in_len,
associated_data.as_ptr(), associated_data.len(),
nonce_bytes.as_ptr(), nonce_bytes.len(),
self.key.as_ptr(), self.key.len(),
)
};
if rc != 0 {
return Err(aead::Error);
}
Ok(())
}
}
@@ -380,6 +380,34 @@ impl RNG {
}
}
/// Implement `rand_core::TryRng` for `RNG`, allowing it to be used anywhere
/// a standard Rust RNG is expected.
///
/// `Error` is set to `Infallible` so that the blanket impls for `Rng` and
/// `CryptoRng` apply automatically. wolfSSL RNG failures cause a panic, which
/// is consistent with the infallible contract.
#[cfg(feature = "rand_core")]
impl rand_core::TryRng for RNG {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
rand_core::utils::next_word_via_fill(self)
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
rand_core::utils::next_word_via_fill(self)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.generate_block(dest).expect("RNG failure");
Ok(())
}
}
/// Mark `RNG` as a cryptographically secure random number generator.
#[cfg(feature = "rand_core")]
impl rand_core::TryCryptoRng for RNG {}
impl Drop for RNG {
/// Safely free the underlying wolfSSL RNG context.
///
@@ -863,3 +863,563 @@ fn test_xtsstream_big_msg() {
assert_eq!(plain_out, BIG_MSG);
}
// ---------------------------------------------------------------------------
// AES aead trait implementations
// ---------------------------------------------------------------------------
#[cfg(feature = "aead")]
use aead::{Aead, AeadInPlace, KeyInit, Payload};
/// NIST SP 800-38D, Test Case 2:
/// Key = 00000000000000000000000000000000
/// IV = 000000000000000000000000
/// PT = 00000000000000000000000000000000
/// AAD = (empty)
/// CT = 0388dace60b6a392f328c2b971b2fe78
/// Tag = ab6e47d42cec13bdf53a67b21257bddf
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes128gcm_nist_tc2_encrypt() {
let key = [0u8; 16];
let nonce = [0u8; 12];
let expected_ciphertext = [
0x03u8, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92,
0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78,
];
let expected_tag = [
0xabu8, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd,
0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf,
];
let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
let nonce_arr: aead::Nonce<Aes128Gcm> = nonce.into();
let mut buffer = [0u8; 16];
let tag = cipher
.encrypt_in_place_detached(&nonce_arr, &[], &mut buffer)
.expect("AES-128-GCM encrypt failed");
assert_eq!(buffer, expected_ciphertext);
assert_eq!(&tag[..], &expected_tag);
}
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes128gcm_nist_tc2_decrypt() {
let key = [0u8; 16];
let nonce = [0u8; 12];
let mut ciphertext = [
0x03u8, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92,
0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78,
];
let tag_bytes = [
0xabu8, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd,
0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf,
];
let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
let nonce_arr: aead::Nonce<Aes128Gcm> = nonce.into();
let tag: aead::Tag<Aes128Gcm> = tag_bytes.into();
cipher
.decrypt_in_place_detached(&nonce_arr, &[], &mut ciphertext, &tag)
.expect("AES-128-GCM decrypt failed");
assert_eq!(ciphertext, [0u8; 16]);
}
/// Test AES-128-GCM roundtrip using the `aead::Aead` blanket impl.
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes128gcm_aead_roundtrip() {
let key = [0x42u8; 16];
let nonce_bytes = [0x11u8; 12];
let aad = b"associated data";
let plaintext = b"Hello, AEAD world!";
let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes128Gcm> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("AES-128-GCM Aead::encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("AES-128-GCM Aead::decrypt failed");
assert_eq!(recovered, plaintext);
}
/// Verify that decryption rejects a tampered tag.
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes128gcm_reject_bad_tag() {
let key = [0u8; 16];
let nonce_bytes = [0u8; 12];
let plaintext = b"some plaintext!";
let cipher = Aes128Gcm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes128Gcm> = nonce_bytes.into();
let mut ct = cipher.encrypt(&nonce, plaintext.as_ref()).expect("encrypt failed");
let last = ct.len() - 1;
ct[last] ^= 0xff;
assert!(cipher.decrypt(&nonce, ct.as_slice()).is_err());
}
/// NIST SP 800-38D, Test Case 14 (256-bit key):
/// Key = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308
/// IV = cafebabefacedbaddecaf888
/// PT = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a7
/// 21c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39 (60 B)
/// AAD = feedfacedeadbeeffeedfacedeadbeefabaddad2
/// CT = 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1a
/// a8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0a (60 B)
/// Tag = 76fc6ece0f4e1768cddf8853bb2d551b
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes256gcm_nist_tc14_encrypt() {
let key = [
0xfeu8, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
];
let nonce = [
0xcau8, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
0xde, 0xca, 0xf8, 0x88,
];
let aad = [
0xfeu8, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
0xab, 0xad, 0xda, 0xd2,
];
let plaintext = [
0xd9u8, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
0xba, 0x63, 0x7b, 0x39,
];
let expected_ciphertext = [
0x52u8, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
0xbc, 0xc9, 0xf6, 0x62,
];
let expected_tag = [
0x76u8, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68,
0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b,
];
let cipher = Aes256Gcm::new_from_slice(&key).unwrap();
let nonce_arr: aead::Nonce<Aes256Gcm> = nonce.into();
let mut buffer = plaintext;
let tag = cipher
.encrypt_in_place_detached(&nonce_arr, &aad, &mut buffer)
.expect("AES-256-GCM encrypt failed");
assert_eq!(buffer, expected_ciphertext);
assert_eq!(&tag[..], &expected_tag);
}
/// Roundtrip test for AES-256-GCM using `aead::Aead`.
#[test]
#[cfg(all(feature = "aead", aes_gcm))]
fn test_aes256gcm_aead_roundtrip() {
let key = [0xabu8; 32];
let nonce_bytes = [0xbcu8; 12];
let aad = b"test aad";
let plaintext = b"AES-256-GCM roundtrip test";
let cipher = Aes256Gcm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes256Gcm> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("decrypt failed");
assert_eq!(recovered, plaintext);
}
/// Roundtrip test for AES-128-CCM using `aead::Aead`.
#[test]
#[cfg(all(feature = "aead", aes_ccm))]
fn test_aes128ccm_aead_roundtrip() {
let key = [0x01u8; 16];
let nonce_bytes = [0x02u8; 12];
let aad = b"ccm aad";
let plaintext = b"AES-128-CCM plaintext!";
let cipher = Aes128Ccm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes128Ccm> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("AES-128-CCM encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("AES-128-CCM decrypt failed");
assert_eq!(recovered, plaintext);
}
/// Verify that AES-128-CCM decryption rejects a tampered ciphertext.
#[test]
#[cfg(all(feature = "aead", aes_ccm))]
fn test_aes128ccm_reject_tampered() {
let key = [0x01u8; 16];
let nonce_bytes = [0x02u8; 12];
let plaintext = b"AES-128-CCM tamper test!";
let cipher = Aes128Ccm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes128Ccm> = nonce_bytes.into();
let mut ct = cipher.encrypt(&nonce, plaintext.as_ref()).expect("encrypt failed");
ct[0] ^= 0x01;
assert!(cipher.decrypt(&nonce, ct.as_slice()).is_err());
}
/// Roundtrip test for AES-256-CCM using `aead::Aead`.
#[test]
#[cfg(all(feature = "aead", aes_ccm))]
fn test_aes256ccm_aead_roundtrip() {
let key = [0xddu8; 32];
let nonce_bytes = [0xeeu8; 12];
let aad = b"aes-256-ccm test";
let plaintext = b"AES-256-CCM plaintext data";
let cipher = Aes256Ccm::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<Aes256Ccm> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("AES-256-CCM encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("AES-256-CCM decrypt failed");
assert_eq!(recovered, plaintext);
}
// ---------------------------------------------------------------------------
// AES cipher crate trait tests
// ---------------------------------------------------------------------------
/// Test AES-128-ECB encryption against the known test vector used in the
/// existing `test_ecb_encrypt_decrypt` test.
#[test]
#[cfg(all(feature = "cipher", aes_ecb))]
fn test_aes128_ecb_enc_block_encrypt() {
use cipher::{BlockModeEncrypt, KeyInit};
use wolfssl_wolfcrypt::aes::Aes128EcbEnc;
let key: [u8; 16] = *b"0123456789abcdef";
let plaintext: [u8; 16] = [
0x6e, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
];
let expected: [u8; 16] = [
0xd0, 0xc9, 0xd9, 0xc9, 0x40, 0xe8, 0x97, 0xb6,
0xc8, 0x8c, 0x33, 0x3b, 0xb5, 0x8f, 0x85, 0xd1,
];
let mut enc = Aes128EcbEnc::new_from_slice(&key).expect("key init failed");
let mut block = cipher::Block::<Aes128EcbEnc>::try_from(&plaintext[..]).unwrap();
enc.encrypt_block(&mut block);
assert_eq!(block.as_slice(), &expected);
}
/// Test AES-128-ECB decryption matches the plaintext after encryption.
#[test]
#[cfg(all(feature = "cipher", aes_ecb))]
fn test_aes128_ecb_dec_block_decrypt() {
use cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyInit};
use wolfssl_wolfcrypt::aes::{Aes128EcbDec, Aes128EcbEnc};
let key: [u8; 16] = *b"0123456789abcdef";
let plaintext: [u8; 16] = [
0x6e, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
];
let mut enc = Aes128EcbEnc::new_from_slice(&key).expect("enc init failed");
let mut dec = Aes128EcbDec::new_from_slice(&key).expect("dec init failed");
let mut block = cipher::Block::<Aes128EcbEnc>::try_from(&plaintext[..]).unwrap();
enc.encrypt_block(&mut block);
let mut block2 = cipher::Block::<Aes128EcbDec>::try_from(block.as_slice()).unwrap();
dec.decrypt_block(&mut block2);
assert_eq!(block2.as_slice(), &plaintext);
}
/// Test AES-256-ECB encryption and decryption roundtrip.
#[test]
#[cfg(all(feature = "cipher", aes_ecb))]
fn test_aes256_ecb_roundtrip() {
use cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyInit};
use wolfssl_wolfcrypt::aes::{Aes256EcbDec, Aes256EcbEnc};
let key = [0xabu8; 32];
let plaintext = [0x5cu8; 16];
let mut enc = Aes256EcbEnc::new_from_slice(&key).expect("enc init failed");
let mut dec = Aes256EcbDec::new_from_slice(&key).expect("dec init failed");
let mut block = cipher::Block::<Aes256EcbEnc>::try_from(&plaintext[..]).unwrap();
enc.encrypt_block(&mut block);
assert_ne!(block.as_slice(), &plaintext, "encrypted block should differ from plaintext");
let mut block2 = cipher::Block::<Aes256EcbDec>::try_from(block.as_slice()).unwrap();
dec.decrypt_block(&mut block2);
assert_eq!(block2.as_slice(), &plaintext);
}
/// Test AES-128-CTR `apply_keystream` against the NIST CTR test vector.
#[test]
#[cfg(all(feature = "cipher", aes_ctr))]
fn test_aes128_ctr_apply_keystream() {
use cipher::{KeyIvInit, StreamCipher};
use wolfssl_wolfcrypt::aes::Aes128Ctr;
let key: [u8; 16] = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
];
let iv: [u8; 16] = [
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
let plaintext: [u8; 64] = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
];
let expected_ciphertext: [u8; 64] = [
0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
];
let key_arr = cipher::Key::<Aes128Ctr>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes128Ctr>::try_from(&iv[..]).unwrap();
let mut enc = Aes128Ctr::new(&key_arr, &iv_arr);
let mut data = plaintext;
enc.apply_keystream(&mut data);
assert_eq!(data, expected_ciphertext);
// apply_keystream is self-inverse: applying again must recover plaintext.
let mut dec = Aes128Ctr::new(&key_arr, &iv_arr);
dec.apply_keystream(&mut data);
assert_eq!(data, plaintext);
}
/// Test AES-256-CTR roundtrip via `apply_keystream`.
#[test]
#[cfg(all(feature = "cipher", aes_ctr))]
fn test_aes256_ctr_roundtrip() {
use cipher::{KeyIvInit, StreamCipher};
use wolfssl_wolfcrypt::aes::Aes256Ctr;
let key = [0x01u8; 32];
let iv = [0x02u8; 16];
let plaintext = [0x55u8; 48];
let key_arr = cipher::Key::<Aes256Ctr>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes256Ctr>::try_from(&iv[..]).unwrap();
let mut enc = Aes256Ctr::new(&key_arr, &iv_arr);
let mut data = plaintext;
enc.apply_keystream(&mut data);
assert_ne!(data, plaintext);
let mut dec = Aes256Ctr::new(&key_arr, &iv_arr);
dec.apply_keystream(&mut data);
assert_eq!(data, plaintext);
}
/// Test AES-256-OFB `apply_keystream` against the known OFB test vector.
#[test]
#[cfg(all(feature = "cipher", aes_ofb))]
fn test_aes256_ofb_apply_keystream() {
use cipher::{KeyIvInit, StreamCipher};
use wolfssl_wolfcrypt::aes::Aes256Ofb;
let key: [u8; 32] = [
0xc4, 0xc7, 0xfa, 0xd6, 0x53, 0x5c, 0xb8, 0x71,
0x4a, 0x5c, 0x40, 0x77, 0x9a, 0x8b, 0xa1, 0xd2,
0x53, 0x3e, 0x23, 0xb4, 0xb2, 0x58, 0x73, 0x2a,
0x5b, 0x78, 0x01, 0xf4, 0xe3, 0x71, 0xa7, 0x94,
];
let iv: [u8; 16] = [
0x5e, 0xb9, 0x33, 0x13, 0xb8, 0x71, 0xff, 0x16,
0xb9, 0x8a, 0x9b, 0xcb, 0x43, 0x33, 0x0d, 0x6f,
];
let plaintext: [u8; 48] = [
0x6d, 0x0b, 0xb0, 0x79, 0x63, 0x84, 0x71, 0xe9,
0x39, 0xd4, 0x53, 0x14, 0x86, 0xc1, 0x4c, 0x25,
0x9a, 0xee, 0xc6, 0xf3, 0xc0, 0x0d, 0xfd, 0xd6,
0xc0, 0x50, 0xa8, 0xba, 0xa8, 0x20, 0xdb, 0x71,
0xcc, 0x12, 0x2c, 0x4e, 0x0c, 0x17, 0x15, 0xef,
0x55, 0xf3, 0x99, 0x5a, 0x6b, 0xf0, 0x2a, 0x4c,
];
let expected_ciphertext: [u8; 48] = [
0x0f, 0x54, 0x61, 0x71, 0x59, 0xd0, 0x3f, 0xfc,
0x1b, 0xfa, 0xfb, 0x60, 0x29, 0x30, 0xd7, 0x00,
0xf4, 0xa4, 0xa8, 0xe6, 0xdd, 0x93, 0x94, 0x46,
0x64, 0xd2, 0x19, 0xc4, 0xc5, 0x4d, 0xde, 0x1b,
0x04, 0x53, 0xe1, 0x73, 0xf5, 0x18, 0x74, 0xae,
0xfd, 0x64, 0xa2, 0xe1, 0xe2, 0x76, 0x13, 0xb0,
];
let key_arr = cipher::Key::<Aes256Ofb>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes256Ofb>::try_from(&iv[..]).unwrap();
let mut enc = Aes256Ofb::new(&key_arr, &iv_arr);
let mut data = plaintext;
enc.apply_keystream(&mut data);
assert_eq!(data, expected_ciphertext);
// apply_keystream is self-inverse for OFB (same keystream for enc/dec).
let mut dec = Aes256Ofb::new(&key_arr, &iv_arr);
dec.apply_keystream(&mut data);
assert_eq!(data, plaintext);
}
/// Test AES-128-OFB roundtrip via `apply_keystream`.
#[test]
#[cfg(all(feature = "cipher", aes_ofb))]
fn test_aes128_ofb_roundtrip() {
use cipher::{KeyIvInit, StreamCipher};
use wolfssl_wolfcrypt::aes::Aes128Ofb;
let key = [0xddu8; 16];
let iv = [0xeeu8; 16];
let plaintext = [0x42u8; 32];
let key_arr = cipher::Key::<Aes128Ofb>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes128Ofb>::try_from(&iv[..]).unwrap();
let mut enc = Aes128Ofb::new(&key_arr, &iv_arr);
let mut data = plaintext;
enc.apply_keystream(&mut data);
assert_ne!(data, plaintext);
let mut dec = Aes128Ofb::new(&key_arr, &iv_arr);
dec.apply_keystream(&mut data);
assert_eq!(data, plaintext);
}
/// Test AES-128-CBC encryption against a known vector (same as test_cbc_encrypt_decrypt).
#[test]
#[cfg(all(feature = "cipher", aes_cbc))]
fn test_aes128_cbc_enc_block_mode() {
use cipher::{BlockModeEncrypt, KeyIvInit};
use wolfssl_wolfcrypt::aes::Aes128CbcEnc;
let key: [u8; 16] = *b"0123456789abcdef";
let iv: [u8; 16] = *b"1234567890abcdef";
let plaintext: [u8; 16] = [
0x6e, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
];
let expected: [u8; 16] = [
0x95, 0x94, 0x92, 0x57, 0x5f, 0x42, 0x81, 0x53,
0x2c, 0xcc, 0x9d, 0x46, 0x77, 0xa2, 0x33, 0xcb,
];
let key_arr = cipher::Key::<Aes128CbcEnc>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes128CbcEnc>::try_from(&iv[..]).unwrap();
let mut enc = Aes128CbcEnc::new(&key_arr, &iv_arr);
let mut block = cipher::Block::<Aes128CbcEnc>::try_from(&plaintext[..]).unwrap();
enc.encrypt_block(&mut block);
assert_eq!(block.as_slice(), &expected);
}
/// Test AES-128-CBC decryption roundtrip.
#[test]
#[cfg(all(feature = "cipher", aes_cbc))]
fn test_aes128_cbc_dec_block_mode() {
use cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit};
use wolfssl_wolfcrypt::aes::{Aes128CbcDec, Aes128CbcEnc};
let key: [u8; 16] = *b"0123456789abcdef";
let iv: [u8; 16] = *b"1234567890abcdef";
let plaintext: [u8; 16] = [
0x6e, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
];
let key_arr = cipher::Key::<Aes128CbcEnc>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes128CbcEnc>::try_from(&iv[..]).unwrap();
let mut enc = Aes128CbcEnc::new(&key_arr, &iv_arr);
let mut block = cipher::Block::<Aes128CbcEnc>::try_from(&plaintext[..]).unwrap();
enc.encrypt_block(&mut block);
let key_arr = cipher::Key::<Aes128CbcDec>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes128CbcDec>::try_from(&iv[..]).unwrap();
let mut dec = Aes128CbcDec::new(&key_arr, &iv_arr);
dec.decrypt_block(&mut block);
assert_eq!(block.as_slice(), &plaintext);
}
/// Test AES-256-CBC encryption/decryption roundtrip across multiple blocks.
#[test]
#[cfg(all(feature = "cipher", aes_cbc))]
fn test_aes256_cbc_roundtrip() {
use cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit};
use wolfssl_wolfcrypt::aes::{Aes256CbcDec, Aes256CbcEnc};
let key = [0xabu8; 32];
let iv = [0xcdu8; 16];
let plaintext = [[0x5cu8; 16], [0x3au8; 16], [0x1eu8; 16]];
let key_arr = cipher::Key::<Aes256CbcEnc>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes256CbcEnc>::try_from(&iv[..]).unwrap();
let mut enc = Aes256CbcEnc::new(&key_arr, &iv_arr);
let mut blocks: [cipher::Block<Aes256CbcEnc>; 3] = plaintext
.iter()
.map(|b| cipher::Block::<Aes256CbcEnc>::try_from(b.as_ref()).unwrap())
.collect::<Vec<_>>()
.try_into()
.unwrap();
for block in blocks.iter_mut() {
enc.encrypt_block(block);
}
// Ciphertext must differ from plaintext due to key and IV mixing.
assert!(blocks.iter().zip(plaintext.iter()).any(|(c, p)| c.as_slice() != p));
let key_arr = cipher::Key::<Aes256CbcDec>::try_from(&key[..]).unwrap();
let iv_arr = cipher::Iv::<Aes256CbcDec>::try_from(&iv[..]).unwrap();
let mut dec = Aes256CbcDec::new(&key_arr, &iv_arr);
for block in blocks.iter_mut() {
dec.decrypt_block(block);
}
for (block, expected) in blocks.iter().zip(plaintext.iter()) {
assert_eq!(block.as_slice(), expected);
}
}
@@ -273,3 +273,229 @@ fn test_xchacha20_poly1305() {
XChaCha20Poly1305::decrypt(&key, &iv, &aad, &ciphertext_buffer, &mut plaintext_buffer).expect("Error with decrypt()");
assert_eq!(plaintext_buffer, PLAINTEXT);
}
// ---------------------------------------------------------------------------
// ChaCha20-Poly1305 aead trait implementations
// ---------------------------------------------------------------------------
#[cfg(feature = "aead")]
use aead::{Aead, AeadInPlace, KeyInit, Payload};
/// RFC 8439, Section 2.8.2 test vector.
///
/// Key = 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f
/// IV = 070000004041424344454647
/// AAD = 50515253c0c1c2c3c4c5c6c7
/// PT = 4c61646965732061...
/// Tag = 1ae10b594f09e26a7e902ecbd0600691
#[test]
#[cfg(feature = "aead")]
fn test_chacha20poly1305_rfc8439_encrypt() {
let key = [
0x80u8, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
];
let nonce = [
0x07u8, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
0x44, 0x45, 0x46, 0x47,
];
let aad = [
0x50u8, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7,
];
let mut plaintext = [
0x4cu8, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
0x74, 0x2e,
];
let expected_ciphertext = [
0xd3u8, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
0x61, 0x16,
];
let expected_tag = [
0x1au8, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91,
];
let cipher = ChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce_arr: aead::Nonce<ChaCha20Poly1305Aead> = nonce.into();
let tag = cipher
.encrypt_in_place_detached(&nonce_arr, &aad, &mut plaintext)
.expect("ChaCha20-Poly1305 encrypt failed");
assert_eq!(plaintext, expected_ciphertext);
assert_eq!(&tag[..], &expected_tag);
}
/// Roundtrip test for ChaCha20-Poly1305 using `aead::Aead`.
#[test]
#[cfg(feature = "aead")]
fn test_chacha20poly1305_aead_roundtrip() {
let key = [0x55u8; 32];
let nonce_bytes = [0x66u8; 12];
let aad = b"chacha20 aad";
let plaintext = b"ChaCha20-Poly1305 roundtrip";
let cipher = ChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<ChaCha20Poly1305Aead> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("decrypt failed");
assert_eq!(recovered, plaintext);
}
/// Verify that ChaCha20-Poly1305 rejects a tampered message.
#[test]
#[cfg(feature = "aead")]
fn test_chacha20poly1305_reject_tampered() {
let key = [0x77u8; 32];
let nonce_bytes = [0x88u8; 12];
let plaintext = b"tamper me!";
let cipher = ChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<ChaCha20Poly1305Aead> = nonce_bytes.into();
let mut ct = cipher.encrypt(&nonce, plaintext.as_ref()).expect("encrypt failed");
ct[0] ^= 0x01;
assert!(cipher.decrypt(&nonce, ct.as_slice()).is_err());
}
/// Roundtrip test for XChaCha20-Poly1305 using `aead::Aead`.
#[test]
#[cfg(all(feature = "aead", xchacha20_poly1305))]
fn test_xchacha20poly1305_aead_roundtrip() {
let key = [0xaau8; 32];
let nonce_bytes = [0xbbu8; 24];
let aad = b"xchacha20 aad";
let plaintext = b"XChaCha20-Poly1305 roundtrip";
let cipher = XChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<XChaCha20Poly1305Aead> = nonce_bytes.into();
let ciphertext = cipher
.encrypt(&nonce, Payload { msg: plaintext, aad })
.expect("XChaCha20-Poly1305 encrypt failed");
let recovered = cipher
.decrypt(&nonce, Payload { msg: &ciphertext, aad })
.expect("XChaCha20-Poly1305 decrypt failed");
assert_eq!(recovered, plaintext);
}
/// RFC 8439-based XChaCha20-Poly1305 known-answer test.
#[test]
#[cfg(all(feature = "aead", xchacha20_poly1305))]
fn test_xchacha20poly1305_known_answer() {
let key = [
0x80u8, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
];
let nonce = [
0x40u8, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
];
let aad = [
0x50u8, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7,
];
let mut plaintext = [
0x4cu8, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
0x74, 0x2e,
];
let expected_ciphertext = [
0xbdu8, 0x6d, 0x17, 0x9d, 0x3e, 0x83, 0xd4, 0x3b,
0x95, 0x76, 0x57, 0x94, 0x93, 0xc0, 0xe9, 0x39,
0x57, 0x2a, 0x17, 0x00, 0x25, 0x2b, 0xfa, 0xcc,
0xbe, 0xd2, 0x90, 0x2c, 0x21, 0x39, 0x6c, 0xbb,
0x73, 0x1c, 0x7f, 0x1b, 0x0b, 0x4a, 0xa6, 0x44,
0x0b, 0xf3, 0xa8, 0x2f, 0x4e, 0xda, 0x7e, 0x39,
0xae, 0x64, 0xc6, 0x70, 0x8c, 0x54, 0xc2, 0x16,
0xcb, 0x96, 0xb7, 0x2e, 0x12, 0x13, 0xb4, 0x52,
0x2f, 0x8c, 0x9b, 0xa4, 0x0d, 0xb5, 0xd9, 0x45,
0xb1, 0x1b, 0x69, 0xb9, 0x82, 0xc1, 0xbb, 0x9e,
0x3f, 0x3f, 0xac, 0x2b, 0xc3, 0x69, 0x48, 0x8f,
0x76, 0xb2, 0x38, 0x35, 0x65, 0xd3, 0xff, 0xf9,
0x21, 0xf9, 0x66, 0x4c, 0x97, 0x63, 0x7d, 0xa9,
0x76, 0x88, 0x12, 0xf6, 0x15, 0xc6, 0x8b, 0x13,
0xb5, 0x2e,
];
let expected_tag = [
0xc0u8, 0x87, 0x59, 0x24, 0xc1, 0xc7, 0x98, 0x79,
0x47, 0xde, 0xaf, 0xd8, 0x78, 0x0a, 0xcf, 0x49,
];
let cipher = XChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce_arr: aead::Nonce<XChaCha20Poly1305Aead> = nonce.into();
let tag = cipher
.encrypt_in_place_detached(&nonce_arr, &aad, &mut plaintext)
.expect("XChaCha20-Poly1305 encrypt failed");
assert_eq!(plaintext, expected_ciphertext);
assert_eq!(&tag[..], &expected_tag);
}
/// Verify that XChaCha20-Poly1305 decryption rejects a tampered ciphertext.
#[test]
#[cfg(all(feature = "aead", xchacha20_poly1305))]
fn test_xchacha20poly1305_reject_tampered() {
let key = [0x55u8; 32];
let nonce_bytes = [0x66u8; 24];
let plaintext = b"XChaCha tamper test";
let cipher = XChaCha20Poly1305Aead::new_from_slice(&key).unwrap();
let nonce: aead::Nonce<XChaCha20Poly1305Aead> = nonce_bytes.into();
let mut ct = cipher.encrypt(&nonce, plaintext.as_ref()).expect("encrypt failed");
ct[0] ^= 0x01;
assert!(cipher.decrypt(&nonce, ct.as_slice()).is_err());
}
@@ -97,3 +97,53 @@ fn test_rng_reseed() {
let seed = [1u8, 2, 3, 4];
rng.reseed(&seed).expect("Error with reseed()");
}
#[test]
#[cfg(feature = "rand_core")]
fn test_rng_rand_core_fill_bytes() {
use rand_core::Rng;
let mut rng = RNG::new().expect("Failed to create RNG");
let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
assert_ne!(buf, [0u8; 32]);
}
#[test]
#[cfg(feature = "rand_core")]
fn test_rng_rand_core_try_fill_bytes() {
use rand_core::TryRng;
let mut rng = RNG::new().expect("Failed to create RNG");
let mut buf = [0u8; 32];
rng.try_fill_bytes(&mut buf).expect("Failed to try_fill_bytes");
assert_ne!(buf, [0u8; 32]);
}
#[test]
#[cfg(feature = "rand_core")]
fn test_rng_rand_core_next_u32() {
use rand_core::Rng;
let mut rng = RNG::new().expect("Failed to create RNG");
// Generate several values and verify they aren't all zero
let v: u64 = (0..4).map(|_| rng.next_u32() as u64).sum();
assert_ne!(v, 0);
}
#[test]
#[cfg(feature = "rand_core")]
fn test_rng_rand_core_next_u64() {
use rand_core::Rng;
let mut rng = RNG::new().expect("Failed to create RNG");
// Generate two values and verify they aren't all ones
let v1 = rng.next_u64();
let v2 = rng.next_u64();
assert_ne!(v1 & v2, u64::MAX);
}
#[test]
#[cfg(feature = "rand_core")]
fn test_rng_is_crypto_rng() {
use rand_core::CryptoRng;
fn requires_crypto_rng<R: CryptoRng>(_: &R) {}
let rng = RNG::new().expect("Failed to create RNG");
requires_crypto_rng(&rng);
}