Compare commits
No commits in common. "3c3fd39ab862fa1086d2391c9f74f195555ebc02" and "919d4bcc95161ee8e9aa83ad9a3474f407faa9bc" have entirely different histories.
3c3fd39ab8
...
919d4bcc95
|
|
@ -1,5 +1,5 @@
|
||||||
|
# https://taskfile.dev
|
||||||
version: "3"
|
version: "3"
|
||||||
silent: true # This makes all tasks silent by default
|
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
APPLE_TARGETS: "aarch64-apple-ios aarch64-apple-darwin x86_64-apple-darwin"
|
APPLE_TARGETS: "aarch64-apple-ios aarch64-apple-darwin x86_64-apple-darwin"
|
||||||
|
|
@ -201,22 +201,6 @@ tasks:
|
||||||
- task: release
|
- task: release
|
||||||
- task: build:apple
|
- task: build:apple
|
||||||
|
|
||||||
# ── Publish ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
release:
|
|
||||||
desc: "Push trunk and sync lum_ccc_fplugin Cargo.lock to the new commit"
|
|
||||||
summary: |
|
|
||||||
Runs in order:
|
|
||||||
1. git push gitea trunk (lum_ccc_rust)
|
|
||||||
2. dev-sync.sh --update (lum_ccc_fplugin) — updates Cargo.lock + clears build cache
|
|
||||||
Set FPLUGIN_DIR env var to override the default fplugin path.
|
|
||||||
vars:
|
|
||||||
FPLUGIN_DIR:
|
|
||||||
sh: echo "${FPLUGIN_DIR:-/Volumes/LUM/source/letusmsg_proj/app/lum_ccc_fplugin}"
|
|
||||||
cmds:
|
|
||||||
- git push gitea trunk
|
|
||||||
- "{{.FPLUGIN_DIR}}/scripts/dev-sync.sh --update"
|
|
||||||
|
|
||||||
# ── Utility ─────────────────────────────────────────────────────────────
|
# ── Utility ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
@ -13,11 +13,6 @@
|
||||||
//! * `WOLFSSL_INSTALL_DIR` – If set, skip the CMake build and link against
|
//! * `WOLFSSL_INSTALL_DIR` – If set, skip the CMake build and link against
|
||||||
//! a pre-installed wolfSSL at this path. The path must contain
|
//! a pre-installed wolfSSL at this path. The path must contain
|
||||||
//! `include/wolfssl/` and `lib/libwolfssl.a`.
|
//! `include/wolfssl/` and `lib/libwolfssl.a`.
|
||||||
//! * `ANDROID_NDK_ROOT` (or `ANDROID_NDK_HOME`) – Required when targeting
|
|
||||||
//! Android. Set to the NDK root directory so that CMake can locate the
|
|
||||||
//! Android toolchain. Cargokit/Gradle sets this automatically; when
|
|
||||||
//! invoking `cargo` directly make sure the variable is exported in your
|
|
||||||
//! shell session.
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
|
|
@ -104,31 +99,6 @@ fn build_wolfssl_cmake(source_dir: &Path, _out_dir: &Path) -> (PathBuf, PathBuf)
|
||||||
|
|
||||||
let mut cfg = cmake::Config::new(source_dir);
|
let mut cfg = cmake::Config::new(source_dir);
|
||||||
|
|
||||||
// ── Android NDK ───────────────────────────────────────────────────────────
|
|
||||||
// When cross-compiling for Android the cmake crate already sets
|
|
||||||
// CMAKE_SYSTEM_NAME=Android and the clang compiler paths. The only
|
|
||||||
// missing piece that Android-Determine.cmake checks first is
|
|
||||||
// CMAKE_ANDROID_NDK — once it is set all other NDK detection succeeds.
|
|
||||||
//
|
|
||||||
// We read it from ANDROID_NDK_ROOT (standard SDK Manager layout) or the
|
|
||||||
// legacy ANDROID_NDK_HOME. Cargokit/Gradle sets ANDROID_NDK_ROOT, and the
|
|
||||||
// same variable must be visible in the shell when using cargo directly.
|
|
||||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
|
||||||
if target_os == "android" {
|
|
||||||
let ndk_root = env::var("ANDROID_NDK_ROOT")
|
|
||||||
.or_else(|_| env::var("ANDROID_NDK_HOME"))
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
panic!(
|
|
||||||
"\n\nAndroid target requires ANDROID_NDK_ROOT (or ANDROID_NDK_HOME) \
|
|
||||||
to be set.\nSet it to the NDK directory, e.g.:\n \
|
|
||||||
export ANDROID_NDK_ROOT=$HOME/apps/android-sdk/ndk/28.x.x\n"
|
|
||||||
)
|
|
||||||
});
|
|
||||||
cfg.define("CMAKE_ANDROID_NDK", &ndk_root);
|
|
||||||
println!("cargo:rerun-if-env-changed=ANDROID_NDK_ROOT");
|
|
||||||
println!("cargo:rerun-if-env-changed=ANDROID_NDK_HOME");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Core algorithm selection ─────────────────────────────────────────────
|
// ── Core algorithm selection ─────────────────────────────────────────────
|
||||||
// wolfSSL cmake uses add_option() with "yes"/"no" string values (not ON/OFF).
|
// wolfSSL cmake uses add_option() with "yes"/"no" string values (not ON/OFF).
|
||||||
// Option names must exactly match the `add_option("NAME" ...)` calls in
|
// Option names must exactly match the `add_option("NAME" ...)` calls in
|
||||||
|
|
|
||||||
|
|
@ -343,18 +343,16 @@ fn xchacha20_poly1305_encrypt(
|
||||||
unsafe {
|
unsafe {
|
||||||
// wc_XChaCha20Poly1305_Encrypt(dst, dst_space, src, src_len,
|
// wc_XChaCha20Poly1305_Encrypt(dst, dst_space, src, src_len,
|
||||||
// ad, ad_len, nonce, nonce_len, key, key_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(
|
let ret = crate::sys::wc_XChaCha20Poly1305_Encrypt(
|
||||||
dst.as_mut_ptr(),
|
dst.as_mut_ptr(),
|
||||||
dst.len(),
|
dst.len(),
|
||||||
plaintext.as_ptr(),
|
plaintext.as_ptr(),
|
||||||
plaintext.len(),
|
plaintext.len(),
|
||||||
aad.as_ptr(),
|
if aad.is_empty() {
|
||||||
|
std::ptr::null()
|
||||||
|
} else {
|
||||||
|
aad.as_ptr()
|
||||||
|
},
|
||||||
aad.len(),
|
aad.len(),
|
||||||
nonce.as_ptr(),
|
nonce.as_ptr(),
|
||||||
nonce.len(),
|
nonce.len(),
|
||||||
|
|
@ -389,14 +387,16 @@ fn xchacha20_poly1305_decrypt(
|
||||||
unsafe {
|
unsafe {
|
||||||
// wc_XChaCha20Poly1305_Decrypt(dst, dst_space, src, src_len,
|
// wc_XChaCha20Poly1305_Decrypt(dst, dst_space, src, src_len,
|
||||||
// ad, ad_len, nonce, nonce_len, key, key_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(
|
let ret = crate::sys::wc_XChaCha20Poly1305_Decrypt(
|
||||||
dst.as_mut_ptr(),
|
dst.as_mut_ptr(),
|
||||||
dst.len(),
|
dst.len(),
|
||||||
ciphertext_and_tag.as_ptr(),
|
ciphertext_and_tag.as_ptr(),
|
||||||
ciphertext_and_tag.len(),
|
ciphertext_and_tag.len(),
|
||||||
aad.as_ptr(),
|
if aad.is_empty() {
|
||||||
|
std::ptr::null()
|
||||||
|
} else {
|
||||||
|
aad.as_ptr()
|
||||||
|
},
|
||||||
aad.len(),
|
aad.len(),
|
||||||
nonce.as_ptr(),
|
nonce.as_ptr(),
|
||||||
nonce.len(),
|
nonce.len(),
|
||||||
|
|
@ -410,280 +410,3 @@ fn xchacha20_poly1305_decrypt(
|
||||||
|
|
||||||
Ok(dst)
|
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