FIX: wc_XChaCha20Poly1305_Init issues with null AAD when length 0, aad.as_prt() always passed now; also added mroe tests
This commit is contained in:
parent
4609e43cc8
commit
cbad181ae6
|
|
@ -343,16 +343,18 @@ fn xchacha20_poly1305_encrypt(
|
|||
unsafe {
|
||||
// wc_XChaCha20Poly1305_Encrypt(dst, dst_space, src, src_len,
|
||||
// ad, ad_len, nonce, nonce_len, key, key_len)
|
||||
//
|
||||
// NOTE: wolfSSL's wc_XChaCha20Poly1305_Init unconditionally rejects
|
||||
// a NULL `ad` pointer (BAD_FUNC_ARG / -173), unlike AES-GCM and
|
||||
// ChaCha20-Poly1305 which allow NULL when ad_len == 0. Always pass
|
||||
// aad.as_ptr() — Rust guarantees a non-null dangling pointer for
|
||||
// empty slices.
|
||||
let ret = crate::sys::wc_XChaCha20Poly1305_Encrypt(
|
||||
dst.as_mut_ptr(),
|
||||
dst.len(),
|
||||
plaintext.as_ptr(),
|
||||
plaintext.len(),
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.as_ptr(),
|
||||
aad.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
|
|
@ -387,16 +389,14 @@ fn xchacha20_poly1305_decrypt(
|
|||
unsafe {
|
||||
// wc_XChaCha20Poly1305_Decrypt(dst, dst_space, src, src_len,
|
||||
// ad, ad_len, nonce, nonce_len, key, key_len)
|
||||
//
|
||||
// NOTE: See encrypt above — wolfSSL rejects NULL ad for XChaCha20.
|
||||
let ret = crate::sys::wc_XChaCha20Poly1305_Decrypt(
|
||||
dst.as_mut_ptr(),
|
||||
dst.len(),
|
||||
ciphertext_and_tag.as_ptr(),
|
||||
ciphertext_and_tag.len(),
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.as_ptr(),
|
||||
aad.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
|
|
@ -410,3 +410,280 @@ fn xchacha20_poly1305_decrypt(
|
|||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// Tests
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ccc_crypto_core::algorithms::AeadAlgorithm;
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Encrypt then decrypt and assert the plaintext round-trips.
|
||||
fn roundtrip(algo: AeadAlgorithm, key: &[u8], nonce: &[u8], pt: &[u8], aad: &[u8]) {
|
||||
let ct = encrypt(algo, key, nonce, pt, aad)
|
||||
.unwrap_or_else(|e| panic!("{} encrypt failed: {e}", algo.name()));
|
||||
|
||||
// ciphertext must be plaintext + 16-byte tag
|
||||
assert_eq!(
|
||||
ct.len(),
|
||||
pt.len() + TAG_LEN,
|
||||
"{}: unexpected ciphertext length",
|
||||
algo.name()
|
||||
);
|
||||
|
||||
let recovered = decrypt(algo, key, nonce, &ct, aad)
|
||||
.unwrap_or_else(|e| panic!("{} decrypt failed: {e}", algo.name()));
|
||||
assert_eq!(recovered, pt, "{}: plaintext mismatch after roundtrip", algo.name());
|
||||
}
|
||||
|
||||
// ── AES-256-GCM ─────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_roundtrip_with_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::AesGcm256,
|
||||
&[0x01; 32],
|
||||
&[0x02; 12],
|
||||
b"aes-gcm payload with associated data",
|
||||
b"some associated data",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_roundtrip_empty_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::AesGcm256,
|
||||
&[0x11; 32],
|
||||
&[0x22; 12],
|
||||
b"aes-gcm payload no aad",
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_roundtrip_empty_plaintext() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::AesGcm256,
|
||||
&[0x31; 32],
|
||||
&[0x32; 12],
|
||||
&[],
|
||||
b"aad only, no plaintext",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_roundtrip_empty_both() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::AesGcm256,
|
||||
&[0x41; 32],
|
||||
&[0x42; 12],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_tampered_tag_fails() {
|
||||
let key = [0x51u8; 32];
|
||||
let nonce = [0x52u8; 12];
|
||||
let pt = b"integrity check";
|
||||
let mut ct = encrypt(AeadAlgorithm::AesGcm256, &key, &nonce, pt, &[]).unwrap();
|
||||
// Flip last byte of the tag
|
||||
let last = ct.len() - 1;
|
||||
ct[last] ^= 0xFF;
|
||||
assert!(decrypt(AeadAlgorithm::AesGcm256, &key, &nonce, &ct, &[]).is_err());
|
||||
}
|
||||
|
||||
// ── ChaCha20-Poly1305 ───────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn chacha20_roundtrip_with_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::ChaCha20Poly1305,
|
||||
&[0x61; 32],
|
||||
&[0x62; 12],
|
||||
b"chacha20 payload with associated data",
|
||||
b"chacha20 aad",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_roundtrip_empty_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::ChaCha20Poly1305,
|
||||
&[0x71; 32],
|
||||
&[0x72; 12],
|
||||
b"chacha20 payload no aad",
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_roundtrip_empty_plaintext() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::ChaCha20Poly1305,
|
||||
&[0x81; 32],
|
||||
&[0x82; 12],
|
||||
&[],
|
||||
b"chacha20 aad only",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_roundtrip_empty_both() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::ChaCha20Poly1305,
|
||||
&[0x91; 32],
|
||||
&[0x92; 12],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_tampered_tag_fails() {
|
||||
let key = [0xA1u8; 32];
|
||||
let nonce = [0xA2u8; 12];
|
||||
let pt = b"integrity check chacha";
|
||||
let mut ct = encrypt(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, pt, &[]).unwrap();
|
||||
let last = ct.len() - 1;
|
||||
ct[last] ^= 0xFF;
|
||||
assert!(decrypt(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce, &ct, &[]).is_err());
|
||||
}
|
||||
|
||||
// ── XChaCha20-Poly1305 ──────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn xchacha20_roundtrip_with_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::XChaCha20Poly1305,
|
||||
&[0xB1; 32],
|
||||
&[0xB2; 24],
|
||||
b"xchacha20 payload with associated data",
|
||||
b"xchacha20 aad",
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression: wolfSSL's `wc_XChaCha20Poly1305_Init` unconditionally
|
||||
/// rejects a NULL `ad` pointer (BAD_FUNC_ARG / -173), even when
|
||||
/// `ad_len == 0`. Must pass a non-null dangling pointer for empty AAD.
|
||||
#[test]
|
||||
fn xchacha20_roundtrip_empty_aad() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::XChaCha20Poly1305,
|
||||
&[0xC1; 32],
|
||||
&[0xC2; 24],
|
||||
b"xchacha20 payload no aad",
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xchacha20_roundtrip_empty_plaintext() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::XChaCha20Poly1305,
|
||||
&[0xD1; 32],
|
||||
&[0xD2; 24],
|
||||
&[],
|
||||
b"xchacha20 aad only",
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression: both AAD and plaintext empty — exercises the NULL-pointer
|
||||
/// code paths simultaneously.
|
||||
#[test]
|
||||
fn xchacha20_roundtrip_empty_both() {
|
||||
roundtrip(
|
||||
AeadAlgorithm::XChaCha20Poly1305,
|
||||
&[0xE1; 32],
|
||||
&[0xE2; 24],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xchacha20_tampered_tag_fails() {
|
||||
let key = [0xF1u8; 32];
|
||||
let nonce = [0xF2u8; 24];
|
||||
let pt = b"integrity check xchacha";
|
||||
let mut ct = encrypt(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, pt, &[]).unwrap();
|
||||
let last = ct.len() - 1;
|
||||
ct[last] ^= 0xFF;
|
||||
assert!(decrypt(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, &ct, &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xchacha20_wrong_aad_fails() {
|
||||
let key = [0xF3u8; 32];
|
||||
let nonce = [0xF4u8; 24];
|
||||
let pt = b"aad mismatch test";
|
||||
let ct = encrypt(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, pt, b"correct").unwrap();
|
||||
assert!(decrypt(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, &ct, b"wrong").is_err());
|
||||
}
|
||||
|
||||
// ── Key / nonce validation ──────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_wrong_key_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::AesGcm256, &[0; 16], &[0; 12], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_wrong_nonce_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::AesGcm256, &[0; 32], &[0; 8], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_wrong_key_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::ChaCha20Poly1305, &[0; 16], &[0; 12], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chacha20_wrong_nonce_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::ChaCha20Poly1305, &[0; 32], &[0; 8], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xchacha20_wrong_key_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::XChaCha20Poly1305, &[0; 16], &[0; 24], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xchacha20_wrong_nonce_len_rejected() {
|
||||
assert!(encrypt(AeadAlgorithm::XChaCha20Poly1305, &[0; 32], &[0; 12], b"x", &[]).is_err());
|
||||
}
|
||||
|
||||
// ── Large payload ───────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn xchacha20_large_payload_roundtrip() {
|
||||
let key = [0xAAu8; 32];
|
||||
let nonce = [0xBBu8; 24];
|
||||
let pt = vec![0x42u8; 64 * 1024]; // 64 KiB — exercises the 16k chunked loop
|
||||
roundtrip(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, &pt, b"large");
|
||||
}
|
||||
|
||||
// ── Cross-algorithm isolation ───────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn ciphertext_not_interchangeable_between_algorithms() {
|
||||
let key = [0xDD; 32];
|
||||
let nonce_12 = [0xEE; 12];
|
||||
let nonce_24 = [0xEE; 24];
|
||||
let pt = b"cross-algo test";
|
||||
|
||||
let ct_aes = encrypt(AeadAlgorithm::AesGcm256, &key, &nonce_12, pt, &[]).unwrap();
|
||||
let ct_chacha = encrypt(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce_12, pt, &[]).unwrap();
|
||||
let ct_xchacha = encrypt(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce_24, pt, &[]).unwrap();
|
||||
|
||||
// Each ciphertext must fail to decrypt under a different algorithm
|
||||
assert!(decrypt(AeadAlgorithm::ChaCha20Poly1305, &key, &nonce_12, &ct_aes, &[]).is_err());
|
||||
assert!(decrypt(AeadAlgorithm::AesGcm256, &key, &nonce_12, &ct_chacha, &[]).is_err());
|
||||
assert!(decrypt(AeadAlgorithm::AesGcm256, &key, &nonce_24, &ct_xchacha, &[]).is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue