FIX: lots of mis-match between what's in the generated bindings and what our code calls with wolfcrypt
This commit is contained in:
parent
3c9320ffa5
commit
3b226d0319
|
|
@ -111,6 +111,19 @@ version = "1.0.102"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.3"
|
||||
|
|
@ -132,6 +145,12 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.72.1"
|
||||
|
|
@ -158,6 +177,15 @@ version = "2.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
|
@ -229,6 +257,7 @@ dependencies = [
|
|||
name = "ccc-crypto-wolfssl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"bindgen",
|
||||
"ccc-crypto-core",
|
||||
"cmake",
|
||||
|
|
@ -303,6 +332,15 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
|
|
@ -351,6 +389,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -728,6 +767,17 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
|
@ -777,6 +827,12 @@ 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 = "regex"
|
||||
version = "1.12.3"
|
||||
|
|
@ -879,6 +935,12 @@ version = "0.4.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ ccc-crypto-core.workspace = true
|
|||
zeroize.workspace = true
|
||||
thiserror.workspace = true
|
||||
log.workspace = true
|
||||
# Argon2id KDF — wolfSSL v5.7.x has no built-in Argon2, so we use RustCrypto.
|
||||
argon2 = { version = "0.5", features = ["zeroize"] }
|
||||
|
||||
[build-dependencies]
|
||||
# cmake crate drives the wolfSSL CMake build from source.
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ fn main() {
|
|||
}
|
||||
|
||||
/// Build wolfSSL from source using CMake. Returns `(include_dir, lib_dir)`.
|
||||
fn build_wolfssl_cmake(source_dir: &PathBuf, out_dir: &PathBuf) -> (PathBuf, PathBuf) {
|
||||
fn build_wolfssl_cmake(source_dir: &PathBuf, _out_dir: &PathBuf) -> (PathBuf, PathBuf) {
|
||||
println!(
|
||||
"cargo:warning=Building wolfSSL from source at {}",
|
||||
source_dir.display()
|
||||
|
|
@ -95,38 +95,55 @@ fn build_wolfssl_cmake(source_dir: &PathBuf, out_dir: &PathBuf) -> (PathBuf, Pat
|
|||
let mut cfg = cmake::Config::new(source_dir);
|
||||
|
||||
// ── Core algorithm selection ─────────────────────────────────────────────
|
||||
// Enable exactly the algorithms the CCC system needs in Phase 4.
|
||||
// Additional algorithms can be enabled here for Phase 5+ PQ support.
|
||||
// wolfSSL cmake uses add_option() with "yes"/"no" string values (not ON/OFF).
|
||||
// Option names must exactly match the `add_option("NAME" ...)` calls in
|
||||
// wolfSSL's CMakeLists.txt — notably WOLFSSL_AESGCM (no underscore between
|
||||
// AES and GCM) and WOLFSSL_CRYPT_ONLY (not WOLFSSL_CRYPTONLY).
|
||||
cfg
|
||||
// Build as a static library.
|
||||
.define("BUILD_SHARED_LIBS", "OFF")
|
||||
.define("WOLFSSL_BUILD_SHARED_LIBS", "OFF")
|
||||
// Disable TLS/SSL stack — we only need the wolfCrypt algorithms.
|
||||
.define("WOLFSSL_NO_TLS", "ON")
|
||||
// AEAD
|
||||
.define("WOLFSSL_AES_GCM", "ON")
|
||||
.define("WOLFSSL_CHACHA", "ON")
|
||||
.define("WOLFSSL_POLY1305", "ON")
|
||||
// wolfCrypt-only — no TLS stack. This is the correct cmake variable.
|
||||
.define("WOLFSSL_CRYPT_ONLY", "yes")
|
||||
// AEAD (WOLFSSL_AESGCM — note: no underscore between AES and GCM)
|
||||
.define("WOLFSSL_AESGCM", "yes")
|
||||
.define("WOLFSSL_CHACHA", "yes")
|
||||
.define("WOLFSSL_POLY1305", "yes")
|
||||
// Hash
|
||||
.define("WOLFSSL_SHA224", "ON")
|
||||
.define("WOLFSSL_SHA384", "ON")
|
||||
.define("WOLFSSL_SHA512", "ON")
|
||||
.define("WOLFSSL_SHA3", "ON")
|
||||
.define("WOLFSSL_BLAKE2", "ON")
|
||||
// KDF
|
||||
.define("WOLFSSL_HKDF", "ON")
|
||||
.define("WOLFSSL_PWDBASED", "ON") // PBKDF2, Argon2
|
||||
.define("WOLFSSL_SHA384", "yes")
|
||||
.define("WOLFSSL_SHA512", "yes")
|
||||
.define("WOLFSSL_SHA3", "yes")
|
||||
// BLAKE2b/BLAKE2s — not a cmake option; enabled via C preprocessor flag.
|
||||
// Added to both cmake C flags and bindgen clang args below.
|
||||
// KDF (HKDF via TLS-1.3 HKDF primitives, PBKDF2)
|
||||
.define("WOLFSSL_HKDF", "yes")
|
||||
.define("WOLFSSL_PWDBASED", "yes")
|
||||
// MAC
|
||||
.define("WOLFSSL_HMAC", "ON")
|
||||
// Asymmetric (needed for X25519 ratchet)
|
||||
.define("WOLFSSL_CURVE25519", "ON")
|
||||
.define("WOLFSSL_CURVE448", "ON")
|
||||
// RNG (needed internally)
|
||||
.define("WOLFSSL_RNG", "ON")
|
||||
.define("WOLFSSL_HMAC", "yes")
|
||||
// Asymmetric (X25519 / X448 DH for ratchet)
|
||||
.define("WOLFSSL_CURVE25519", "yes")
|
||||
.define("WOLFSSL_CURVE448", "yes")
|
||||
// RNG
|
||||
.define("WOLFSSL_RNG", "yes")
|
||||
// Minimise binary size.
|
||||
.define("WOLFSSL_CRYPTONLY", "ON")
|
||||
.define("WOLFSSL_MIN_RSA_BITS", "2048")
|
||||
.define("WOLFSSL_NO_FILESYSTEM", "ON");
|
||||
// BLAKE2b — two steps needed:
|
||||
// 1. cmake option adds blake2b.c to the library source list.
|
||||
// 2. C preprocessor flag enables the code inside blake2b.c.
|
||||
.define("WOLFSSL_BLAKE2", "yes")
|
||||
// Disable building test / benchmark executables.
|
||||
// WOLFSSL_CRYPT_ONLY already disables TLS examples (WOLFSSL_EXAMPLES),
|
||||
// but WOLFSSL_CRYPT_TESTS (test + benchmark binaries) defaults to "yes"
|
||||
// and must be disabled separately. Without this, the linker fails on
|
||||
// missing TLS symbols (_GetCA etc.) referenced from asn.c when it tries
|
||||
// to link the test/benchmark executables.
|
||||
.define("WOLFSSL_CRYPT_TESTS", "no")
|
||||
// BLAKE2b (step 2) and XChaCha20 have no cmake option — pass as C
|
||||
// compiler flags so the code inside the compiled files is enabled.
|
||||
// BLAKE2: wc_InitBlake2b/wc_Blake2bUpdate/wc_Blake2bFinal API.
|
||||
// XCHACHA: wc_XChaCha20Poly1305_Encrypt/Decrypt (24-byte nonce).
|
||||
.cflag("-DHAVE_BLAKE2")
|
||||
.cflag("-DHAVE_BLAKE2B")
|
||||
.cflag("-DHAVE_XCHACHA");
|
||||
|
||||
let install_path = cfg.build();
|
||||
|
||||
|
|
@ -157,40 +174,127 @@ fn generate_bindings(include_dir: &PathBuf, out_dir: &PathBuf) {
|
|||
.header(include_dir.join("wolfssl/wolfcrypt/aes.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/chacha20_poly1305.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/sha256.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/sha512.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/sha512.h").to_str().unwrap()) // also covers sha384
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/sha3.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/blake2.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/hmac.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/kdf.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/pwdbased.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/curve25519.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/curve448.h").to_str().unwrap())
|
||||
.header(include_dir.join("wolfssl/wolfcrypt/random.h").to_str().unwrap())
|
||||
.clang_arg(format!("-I{}", include_dir.display()))
|
||||
// Only generate bindings for the types/functions we whitelist.
|
||||
// Pass all required preprocessor defines explicitly so that bindgen's
|
||||
// libclang sees the same feature flags as the C build, regardless of
|
||||
// whether wolfSSL's options.h is correctly resolved by clang.
|
||||
//
|
||||
// AES-GCM (wc_AesGcmSetKey etc. are behind #ifdef HAVE_AESGCM)
|
||||
.clang_arg("-DHAVE_AESGCM")
|
||||
// ChaCha20 / Poly1305 / XChaCha20
|
||||
.clang_arg("-DHAVE_CHACHA")
|
||||
.clang_arg("-DHAVE_POLY1305")
|
||||
.clang_arg("-DHAVE_XCHACHA")
|
||||
// BLAKE2 is not a cmake option — enable via clang preprocessor flags
|
||||
// so that the headers expose the BLAKE2b/BLAKE2s function prototypes.
|
||||
.clang_arg("-DHAVE_BLAKE2")
|
||||
.clang_arg("-DHAVE_BLAKE2B")
|
||||
// Required so sha512.h exposes SHA384 typedef + functions.
|
||||
.clang_arg("-DWOLFSSL_SHA384")
|
||||
.clang_arg("-DWOLFSSL_SHA512")
|
||||
// SHA3 functions (wc_InitSha3_256, etc.)
|
||||
.clang_arg("-DWOLFSSL_SHA3")
|
||||
// HKDF via TLS-1.3 primitives (wc_Tls13_HKDF_Extract/Expand_Label)
|
||||
.clang_arg("-DHAVE_HKDF")
|
||||
// Curve25519 / Curve448 need HAVE_CURVE25519 / HAVE_CURVE448
|
||||
.clang_arg("-DHAVE_CURVE25519")
|
||||
.clang_arg("-DHAVE_CURVE448")
|
||||
// Only generate bindings for the types/functions we use.
|
||||
.allowlist_function("wc_Aes.*")
|
||||
.allowlist_function("wc_AesGcm.*")
|
||||
.allowlist_function("wc_AesInit")
|
||||
.allowlist_function("wc_AesFree")
|
||||
.allowlist_function("wc_ChaCha20Poly1305.*")
|
||||
.allowlist_function("wc_Sha.*")
|
||||
.allowlist_function("wc_Blake2.*")
|
||||
.allowlist_function("wc_Hmac.*")
|
||||
.allowlist_function("wc_HKDF.*")
|
||||
.allowlist_function("wc_Pbkdf2.*")
|
||||
.allowlist_function("wc_Argon2.*")
|
||||
.allowlist_function("wc_curve25519.*")
|
||||
.allowlist_function("wc_curve448.*")
|
||||
.allowlist_function("wc_InitRng.*")
|
||||
.allowlist_function("wc_RNG.*")
|
||||
.allowlist_function("wc_FreeRng.*")
|
||||
.allowlist_function("wc_XChaCha20Poly1305.*")
|
||||
// SHA-256 (one-shot available)
|
||||
.allowlist_function("wc_Sha256Hash")
|
||||
.allowlist_function("wc_Sha256.*")
|
||||
// SHA-384 (init/update/final — typedef of wc_Sha512 struct)
|
||||
.allowlist_function("wc_InitSha384")
|
||||
.allowlist_function("wc_Sha384Update")
|
||||
.allowlist_function("wc_Sha384Final")
|
||||
.allowlist_function("wc_Sha384Free")
|
||||
// SHA-512 (init/update/final)
|
||||
.allowlist_function("wc_InitSha512")
|
||||
.allowlist_function("wc_Sha512Update")
|
||||
.allowlist_function("wc_Sha512Final")
|
||||
.allowlist_function("wc_Sha512Free")
|
||||
// SHA-3 (init/update/final for 256 and 512)
|
||||
.allowlist_function("wc_InitSha3_256")
|
||||
.allowlist_function("wc_Sha3_256_Update")
|
||||
.allowlist_function("wc_Sha3_256_Final")
|
||||
.allowlist_function("wc_Sha3_256_Free")
|
||||
.allowlist_function("wc_InitSha3_512")
|
||||
.allowlist_function("wc_Sha3_512_Update")
|
||||
.allowlist_function("wc_Sha3_512_Final")
|
||||
.allowlist_function("wc_Sha3_512_Free")
|
||||
// BLAKE2b (init/update/final + keyed variant)
|
||||
.allowlist_function("wc_InitBlake2b")
|
||||
.allowlist_function("wc_InitBlake2b_WithKey")
|
||||
.allowlist_function("wc_Blake2bUpdate")
|
||||
.allowlist_function("wc_Blake2bFinal")
|
||||
// HMAC
|
||||
.allowlist_function("wc_HmacSetKey")
|
||||
.allowlist_function("wc_HmacUpdate")
|
||||
.allowlist_function("wc_HmacFinal")
|
||||
.allowlist_function("wc_HmacFree")
|
||||
// HKDF (TLS-1.3 Extract is RFC 5869 Extract; no generic Expand in wolfSSL)
|
||||
.allowlist_function("wc_Tls13_HKDF_Extract")
|
||||
.allowlist_function("wc_Tls13_HKDF_Extract_ex")
|
||||
// PBKDF2
|
||||
.allowlist_function("wc_PBKDF2")
|
||||
.allowlist_function("wc_PBKDF2_ex")
|
||||
// Curve25519
|
||||
.allowlist_function("wc_curve25519_make_key")
|
||||
.allowlist_function("wc_curve25519_init")
|
||||
.allowlist_function("wc_curve25519_free")
|
||||
.allowlist_function("wc_curve25519_import_private")
|
||||
.allowlist_function("wc_curve25519_import_public")
|
||||
.allowlist_function("wc_curve25519_export_key_raw")
|
||||
.allowlist_function("wc_curve25519_shared_secret_ex")
|
||||
// Curve448
|
||||
.allowlist_function("wc_curve448_make_key")
|
||||
.allowlist_function("wc_curve448_init")
|
||||
.allowlist_function("wc_curve448_free")
|
||||
.allowlist_function("wc_curve448_import_private")
|
||||
.allowlist_function("wc_curve448_import_public")
|
||||
.allowlist_function("wc_curve448_export_key_raw")
|
||||
.allowlist_function("wc_curve448_shared_secret_ex")
|
||||
// RNG
|
||||
.allowlist_function("wc_InitRng")
|
||||
.allowlist_function("wc_RNG_GenerateBlock")
|
||||
.allowlist_function("wc_FreeRng")
|
||||
// Types
|
||||
.allowlist_type("Aes")
|
||||
.allowlist_type("Hmac")
|
||||
.allowlist_type("WC_RNG")
|
||||
.allowlist_type("curve25519.*")
|
||||
.allowlist_type("curve448.*")
|
||||
.allowlist_type("OS_Seed")
|
||||
.allowlist_type("wc_Sha256")
|
||||
.allowlist_type("wc_Sha512") // also used as wc_Sha384 (typedef)
|
||||
.allowlist_type("wc_Sha3")
|
||||
.allowlist_type("Blake2b")
|
||||
.allowlist_type("curve25519_key")
|
||||
.allowlist_type("curve448_key")
|
||||
// Constants we use in Rust
|
||||
.allowlist_var("AES_.*")
|
||||
.allowlist_var("WC_.*")
|
||||
.allowlist_var("SHA.*")
|
||||
.allowlist_var("BLAKE2.*")
|
||||
// Silence warnings for types we don't control.
|
||||
.blocklist_type("__.*")
|
||||
.allowlist_var("WC_SHA.*")
|
||||
.allowlist_var("WC_HASH.*")
|
||||
.allowlist_var("CHACHA20_POLY1305.*")
|
||||
// Only block specific C internal types that cause noise.
|
||||
// Note: do NOT use ".blocklist_type("__.*")" here because that regex
|
||||
// also matches `__BindgenBitfieldUnit`, a Rust helper type that bindgen
|
||||
// emits for C structs with bitfields (Aes, Hmac, etc.).
|
||||
.blocklist_type("__uint128_t")
|
||||
.blocklist_type("__int128_t")
|
||||
.derive_debug(false)
|
||||
.layout_tests(false)
|
||||
.generate()
|
||||
|
|
|
|||
|
|
@ -251,10 +251,12 @@ fn chacha20_poly1305_decrypt(
|
|||
// XChaCha20-Poly1305 (24-byte nonce)
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// wolfCrypt does not expose a single XChaCha20-Poly1305 one-shot function.
|
||||
// We derive a sub-key from the first 16 bytes of the nonce using HChaCha20,
|
||||
// then apply ChaCha20-Poly1305 with the last 12 bytes for the nonce.
|
||||
// This matches the XChaCha20-Poly1305 construction in RFC 8439 §2.
|
||||
// wolfCrypt exposes `wc_XChaCha20Poly1305_Encrypt/Decrypt` as public one-shot
|
||||
// functions that accept the 24-byte XChaCha nonce directly and handle the
|
||||
// internal HChaCha20 sub-key derivation (RFC 8439 §2) themselves.
|
||||
//
|
||||
// Output layout: dst = ciphertext || 16-byte Poly1305 tag
|
||||
// (dst_space = src_len + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE = src_len + 16).
|
||||
|
||||
fn xchacha20_poly1305_encrypt(
|
||||
key: &[u8],
|
||||
|
|
@ -262,8 +264,32 @@ fn xchacha20_poly1305_encrypt(
|
|||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
let (subkey, chacha_nonce) = xchacha_derive_subkey(key, nonce)?;
|
||||
chacha20_poly1305_encrypt(&subkey, &chacha_nonce, plaintext, aad)
|
||||
// dst holds ciphertext (same length as plaintext) followed by the 16-byte tag.
|
||||
let mut dst = vec![0u8; plaintext.len() + TAG_LEN];
|
||||
|
||||
unsafe {
|
||||
// wc_XChaCha20Poly1305_Encrypt(dst, dst_space, src, src_len,
|
||||
// ad, ad_len, nonce, nonce_len, key, key_len)
|
||||
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.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
key.as_ptr(),
|
||||
key.len(),
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_XChaCha20Poly1305_Encrypt returned {}", ret)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
fn xchacha20_poly1305_decrypt(
|
||||
|
|
@ -272,32 +298,33 @@ fn xchacha20_poly1305_decrypt(
|
|||
ciphertext_and_tag: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
let (subkey, chacha_nonce) = xchacha_derive_subkey(key, nonce)?;
|
||||
chacha20_poly1305_decrypt(&subkey, &chacha_nonce, ciphertext_and_tag, aad)
|
||||
}
|
||||
|
||||
/// Derive a 32-byte sub-key and a 12-byte ChaCha nonce from a 32-byte key
|
||||
/// and a 24-byte XChaCha nonce, following RFC 8439 §2.
|
||||
fn xchacha_derive_subkey(key: &[u8], nonce: &[u8]) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
|
||||
debug_assert_eq!(nonce.len(), 24);
|
||||
// HChaCha20 takes key[32] + nonce[0..16] → 32-byte subkey.
|
||||
let mut subkey = vec![0u8; 32];
|
||||
unsafe {
|
||||
// wc_HChaCha20(output, key, nonce_16bytes) — available in wolfCrypt.
|
||||
let ret = crate::sys::wc_HChaCha20(
|
||||
subkey.as_mut_ptr() as *mut u32,
|
||||
key.as_ptr() as *const u32,
|
||||
nonce.as_ptr() as *const u32,
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_HChaCha20 returned {}", ret)
|
||||
if ciphertext_and_tag.len() < TAG_LEN {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"XChaCha20-Poly1305: ciphertext too short".into()
|
||||
));
|
||||
}
|
||||
}
|
||||
// ChaCha nonce = [0u8 × 4] || nonce[16..24]
|
||||
let mut chacha_nonce = vec![0u8; 12];
|
||||
chacha_nonce[4..12].copy_from_slice(&nonce[16..24]);
|
||||
let pt_len = ciphertext_and_tag.len() - TAG_LEN;
|
||||
let mut dst = vec![0u8; pt_len];
|
||||
|
||||
Ok((subkey, chacha_nonce))
|
||||
unsafe {
|
||||
// wc_XChaCha20Poly1305_Decrypt(dst, dst_space, src, src_len,
|
||||
// ad, ad_len, nonce, nonce_len, key, key_len)
|
||||
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.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
key.as_ptr(),
|
||||
key.len(),
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::AuthenticationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,16 +43,24 @@ fn sha256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
/// SHA-384: `wc_Sha384` is `typedef struct wc_Sha512 wc_Sha384` in wolfSSL's sha512.h.
|
||||
fn sha384(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut out = vec![0u8; 48];
|
||||
unsafe {
|
||||
let ret = crate::sys::wc_Sha384Hash(
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
out.as_mut_ptr(),
|
||||
);
|
||||
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha384(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha384Hash returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha384 returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha384Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha384Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha384Update returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha384Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha384Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha384Final returned {ret}")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -61,13 +69,20 @@ fn sha384(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut out = vec![0u8; 64];
|
||||
unsafe {
|
||||
let ret = crate::sys::wc_Sha512Hash(
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
out.as_mut_ptr(),
|
||||
);
|
||||
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha512(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha512Hash returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha512 returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha512Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha512Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha512Update returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha512Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha512Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha512Final returned {ret}")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -77,17 +92,25 @@ fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
// SHA-3 family
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// SHA3-256: wolfCrypt provides only init/update/final (no one-shot).
|
||||
/// `wc_InitSha3_256(sha3, heap, devId)` — INVALID_DEVID = -2.
|
||||
fn sha3_256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut out = vec![0u8; 32];
|
||||
unsafe {
|
||||
// wolfCrypt: wc_Sha3_256Hash(data, dataSz, digest)
|
||||
let ret = crate::sys::wc_Sha3_256Hash(
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
out.as_mut_ptr(),
|
||||
);
|
||||
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha3_256(&mut sha, std::ptr::null_mut(), -2);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_256Hash returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha3_256 returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_256_Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha3_256_Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_256_Update returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_256_Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha3_256_Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_256_Final returned {ret}")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -96,13 +119,20 @@ fn sha3_256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut out = vec![0u8; 64];
|
||||
unsafe {
|
||||
let ret = crate::sys::wc_Sha3_512Hash(
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
out.as_mut_ptr(),
|
||||
);
|
||||
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha3_512(&mut sha, std::ptr::null_mut(), -2);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_512Hash returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha3_512 returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_512_Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha3_512_Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_512_Update returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_512_Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha3_512_Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_512_Final returned {ret}")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -112,23 +142,26 @@ fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
// BLAKE2b-512
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn blake2b_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut out = vec![0u8; 64];
|
||||
/// Unkeyed BLAKE2b-512 digest using wolfCrypt init / update / final.
|
||||
///
|
||||
/// For keyed BLAKE2b-MAC, see [`crate::mac`].
|
||||
pub fn blake2b_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
const OUT_LEN: u32 = 64;
|
||||
let mut out = vec![0u8; OUT_LEN as usize];
|
||||
unsafe {
|
||||
// wc_Blake2bHash(out, outLen, key, keyLen, data, dataSz)
|
||||
// No key = unkeyed hash (pass NULL, 0).
|
||||
let ret = crate::sys::wc_Blake2bHash(
|
||||
out.as_mut_ptr(),
|
||||
64u32,
|
||||
std::ptr::null(),
|
||||
0u32,
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
);
|
||||
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitBlake2b(&mut b2b, OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bHash returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!("wc_InitBlake2b returned {ret}")));
|
||||
}
|
||||
let ret = crate::sys::wc_Blake2bUpdate(&mut b2b, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Blake2bUpdate returned {ret}")));
|
||||
}
|
||||
// requestSz = OUT_LEN → produce the full 64-byte digest.
|
||||
let ret = crate::sys::wc_Blake2bFinal(&mut b2b, out.as_mut_ptr(), OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Blake2bFinal returned {ret}")));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,40 @@
|
|||
//! Key derivation function implementations via wolfCrypt.
|
||||
//!
|
||||
//! Covers HKDF (SHA-256 / SHA-384 / SHA-512), Argon2id, and BLAKE2b-based KDF.
|
||||
//!
|
||||
//! ## HKDF implementation
|
||||
//!
|
||||
//! wolfSSL v5.7.x does not expose a generic `wc_HKDF()` function. The only
|
||||
//! available HKDF primitive is `wc_Tls13_HKDF_Extract` (RFC 5869 Extract step)
|
||||
//! together with the `wc_Hmac*` family. We implement RFC 5869 HKDF in Rust:
|
||||
//!
|
||||
//! - **Extract**: `PRK = HMAC-Hash(salt, IKM)` via `wc_Tls13_HKDF_Extract`.
|
||||
//! - **Expand**: `OKM = T(1)||T(2)||…` where
|
||||
//! `T(i) = HMAC(PRK, T(i-1) || info || i)` computed with wolfSSL HMAC.
|
||||
//!
|
||||
//! ## Argon2id
|
||||
//!
|
||||
//! wolfSSL v5.7.x has no built-in Argon2 implementation, so we delegate to
|
||||
//! the pure-Rust `argon2` crate (which is maintained by RustCrypto).
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use ccc_crypto_core::{algorithms::KdfAlgorithm, error::CryptoError};
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// wolfCrypt hash type constants (passed to wc_HKDF)
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// wolfCrypt hash type constants from `enum wc_HashType` in types.h (non-FIPS).
|
||||
const WC_HASH_TYPE_SHA256: i32 = 6;
|
||||
const WC_HASH_TYPE_SHA384: i32 = 7;
|
||||
const WC_HASH_TYPE_SHA512: i32 = 8;
|
||||
|
||||
/// wolfCrypt `wc_HashType` values for hash algorithms used in HKDF.
|
||||
/// These match the `enum wc_HashType` values in `wolfssl/wolfcrypt/hash.h`.
|
||||
const WC_HASH_TYPE_SHA256: i32 = 8;
|
||||
const WC_HASH_TYPE_SHA384: i32 = 9;
|
||||
const WC_HASH_TYPE_SHA512: i32 = 10;
|
||||
/// Output byte lengths for each HMAC hash variant, matching the digest size.
|
||||
fn hash_output_len(hash_type: i32) -> usize {
|
||||
match hash_type {
|
||||
WC_HASH_TYPE_SHA256 => 32,
|
||||
WC_HASH_TYPE_SHA384 => 48,
|
||||
WC_HASH_TYPE_SHA512 => 64,
|
||||
_ => 32,
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// Public entry point
|
||||
|
|
@ -40,12 +60,13 @@ pub fn derive_key(
|
|||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// HKDF (RFC 5869)
|
||||
// HKDF (RFC 5869) using wolfCrypt primitives
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// HKDF using wolfCrypt's `wc_HKDF()`.
|
||||
/// HKDF: Extract then Expand (RFC 5869).
|
||||
///
|
||||
/// `wc_HKDF(type, ikm, ikmSz, salt, saltSz, info, infoSz, okm, okmSz)`
|
||||
/// Extract uses `wc_Tls13_HKDF_Extract` (wolfSSL's RFC 5869 Extract).
|
||||
/// Expand is implemented in Rust using wolfSSL's HMAC primitives.
|
||||
fn hkdf(
|
||||
hash_type: i32,
|
||||
ikm: &[u8],
|
||||
|
|
@ -53,26 +74,94 @@ fn hkdf(
|
|||
info: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
let mut out = Zeroizing::new(vec![0u8; length]);
|
||||
// ── Extract ──────────────────────────────────────────────────────────────
|
||||
let hash_len = hash_output_len(hash_type);
|
||||
// Use zero-valued salt if none is provided (RFC 5869 §2.2).
|
||||
let default_salt = vec![0u8; hash_len];
|
||||
let effective_salt = if salt.is_empty() { &default_salt } else { salt };
|
||||
|
||||
let prk = hkdf_extract(hash_type, effective_salt, ikm)?;
|
||||
|
||||
// ── Expand ───────────────────────────────────────────────────────────────
|
||||
hkdf_expand(hash_type, &prk, info, length)
|
||||
}
|
||||
|
||||
/// HKDF-Extract: `PRK = HMAC-Hash(salt, IKM)`.
|
||||
///
|
||||
/// Wraps `wc_Tls13_HKDF_Extract` which takes a mutable IKM pointer.
|
||||
fn hkdf_extract(
|
||||
hash_type: i32,
|
||||
salt: &[u8],
|
||||
ikm: &[u8],
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
let hash_len = hash_output_len(hash_type);
|
||||
let mut prk = Zeroizing::new(vec![0u8; hash_len]);
|
||||
// wolfSSL's wc_Tls13_HKDF_Extract takes `byte* ikm` (mutable), so we
|
||||
// make a writable copy.
|
||||
let mut ikm_buf = ikm.to_vec();
|
||||
|
||||
unsafe {
|
||||
let ret = crate::sys::wc_HKDF(
|
||||
hash_type,
|
||||
ikm.as_ptr(),
|
||||
ikm.len() as u32,
|
||||
let ret = crate::sys::wc_Tls13_HKDF_Extract(
|
||||
prk.as_mut_ptr(),
|
||||
if salt.is_empty() { std::ptr::null() } else { salt.as_ptr() },
|
||||
salt.len() as u32,
|
||||
if info.is_empty() { std::ptr::null() } else { info.as_ptr() },
|
||||
info.len() as u32,
|
||||
out.as_mut_ptr(),
|
||||
length as u32,
|
||||
ikm_buf.as_mut_ptr(),
|
||||
ikm_buf.len() as u32,
|
||||
hash_type,
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_HKDF returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Tls13_HKDF_Extract returned {ret}")
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(prk)
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
/// HKDF-Expand: `OKM = T(1) || T(2) || …` truncated to `length` bytes.
|
||||
///
|
||||
/// `T(i) = HMAC(PRK, T(i-1) || info || i)` per RFC 5869 §2.3.
|
||||
/// Uses wolfSSL HMAC via `crate::mac::hmac`.
|
||||
fn hkdf_expand(
|
||||
hash_type: i32,
|
||||
prk: &[u8],
|
||||
info: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
let hash_len = hash_output_len(hash_type);
|
||||
|
||||
// Maximum HKDF output is 255 * HashLen (RFC 5869 §2.3).
|
||||
let max_len = 255 * hash_len;
|
||||
if length > max_len {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
format!("HKDF: requested length {length} exceeds maximum {max_len}")
|
||||
));
|
||||
}
|
||||
|
||||
let mut okm = Zeroizing::new(vec![0u8; length]);
|
||||
let mut t = Vec::new(); // T(i-1); starts empty per spec
|
||||
let mut pos = 0usize;
|
||||
let mut counter = 1u8;
|
||||
|
||||
while pos < length {
|
||||
// Build the HMAC input: T(i-1) || info || counter
|
||||
let mut input = Vec::with_capacity(t.len() + info.len() + 1);
|
||||
input.extend_from_slice(&t);
|
||||
input.extend_from_slice(info);
|
||||
input.push(counter);
|
||||
|
||||
// T(i) = HMAC-Hash(PRK, input)
|
||||
t = crate::mac::hmac(hash_type, prk, &input, hash_len)?;
|
||||
|
||||
let copy_len = (length - pos).min(hash_len);
|
||||
okm[pos..pos + copy_len].copy_from_slice(&t[..copy_len]);
|
||||
pos += copy_len;
|
||||
counter = counter.checked_add(1).ok_or_else(|| {
|
||||
CryptoError::InternalError("HKDF counter overflow".into())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(okm)
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -118,50 +207,44 @@ fn blake2b_kdf(
|
|||
// Argon2id
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Argon2id KDF via wolfCrypt's `wc_Argon2()`.
|
||||
/// Argon2id KDF via the pure-Rust `argon2` crate.
|
||||
///
|
||||
/// wolfSSL v5.7.x has no built-in Argon2 implementation.
|
||||
///
|
||||
/// Uses the memory and iteration parameters from `DEFAULT_CIPHER_PARAMS` in
|
||||
/// `cipher_constants.dart`: 64 MB memory, 4 threads, 3 iterations.
|
||||
/// `cipher_constants.dart`: 64 MiB memory, 3 iterations, 4 lanes.
|
||||
fn argon2id(
|
||||
password: &[u8], // treated as ikm / password
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
// Argon2id requires at least 8 bytes of salt.
|
||||
// Argon2 requires at least 8 bytes of salt.
|
||||
if salt.len() < 8 {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"Argon2id requires at least 8-byte salt".into()
|
||||
));
|
||||
}
|
||||
|
||||
let mut out = Zeroizing::new(vec![0u8; length]);
|
||||
use argon2::{Algorithm, Argon2, Params, Version};
|
||||
|
||||
unsafe {
|
||||
// wolfCrypt Argon2:
|
||||
// typedef struct Argon2_t { ... } Argon2;
|
||||
// int wc_Argon2Hash(Argon2* arg2, const byte* pwd, word32 pwdSz,
|
||||
// const byte* salt, word32 saltSz, byte* out, word32 outSz)
|
||||
//
|
||||
// We use the simplified wc_Argon2id_Hash wrapper (available in wolfCrypt ≥ 5.5).
|
||||
let ret = crate::sys::wc_Argon2id_Hash(
|
||||
out.as_mut_ptr(),
|
||||
length as u32,
|
||||
password.as_ptr(),
|
||||
password.len() as u32,
|
||||
salt.as_ptr(),
|
||||
salt.len() as u32,
|
||||
std::ptr::null(), // secret
|
||||
0u32, // secretLen
|
||||
std::ptr::null(), // additional data
|
||||
0u32, // additional data len
|
||||
64 * 1024, // memory kb (64 MB, matches DEFAULT_CIPHER_PARAMS)
|
||||
3u32, // iterations (matches DEFAULT_CIPHER_PARAMS)
|
||||
4u32, // parallelism (matches DEFAULT_CIPHER_PARAMS: 4 cores)
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Argon2id_Hash returned {}", ret)));
|
||||
}
|
||||
}
|
||||
// Match DEFAULT_CIPHER_PARAMS in cipher_constants.dart:
|
||||
// argon2_memory_kb: 65536 (64 MiB)
|
||||
// argon2_iterations: 3
|
||||
// argon2_parallelism: 4
|
||||
let params = Params::new(
|
||||
65_536, // m_cost: 64 MiB
|
||||
3, // t_cost: iterations
|
||||
4, // p_cost: parallelism (lanes)
|
||||
Some(length), // output length
|
||||
)
|
||||
.map_err(|e| CryptoError::InternalError(format!("Argon2 params error: {e}")))?;
|
||||
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
let mut out = Zeroizing::new(vec![0u8; length]);
|
||||
argon2
|
||||
.hash_password_into(password, salt, &mut out)
|
||||
.map_err(|e| CryptoError::InternalError(format!("Argon2id hash error: {e}")))?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ mod sys {
|
|||
//! feature is only used for type-checking and documentation builds.
|
||||
#![allow(non_camel_case_types, dead_code, unused_variables)]
|
||||
|
||||
use std::os::raw::{c_int, c_uchar, c_uint};
|
||||
use std::os::raw::{c_int, c_uchar, c_uint, c_void};
|
||||
|
||||
// ── Opaque C struct stubs ────────────────────────────────────────────
|
||||
|
||||
|
|
@ -58,6 +58,18 @@ mod sys {
|
|||
#[repr(C)]
|
||||
pub struct WC_RNG([u8; 256]);
|
||||
|
||||
/// Stub for wolfCrypt `wc_Sha512` struct (also used as `wc_Sha384`).
|
||||
#[repr(C)]
|
||||
pub struct wc_Sha512([u8; 512]);
|
||||
|
||||
/// Stub for wolfCrypt `wc_Sha3` struct.
|
||||
#[repr(C)]
|
||||
pub struct wc_Sha3([u8; 512]);
|
||||
|
||||
/// Stub for wolfCrypt `Blake2b` struct.
|
||||
#[repr(C)]
|
||||
pub struct Blake2b([u8; 512]);
|
||||
|
||||
/// Stub for wolfCrypt `curve25519_key` struct.
|
||||
#[repr(C)]
|
||||
pub struct curve25519_key([u8; 256]);
|
||||
|
|
@ -79,6 +91,8 @@ mod sys {
|
|||
iv: *const c_uchar, iv_sz: c_uint, auth_tag: *const c_uchar, auth_tag_sz: c_uint,
|
||||
auth_in: *const c_uchar, auth_in_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_AesInit(aes: *mut Aes, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_AesFree(aes: *mut Aes) { unreachable!() }
|
||||
|
||||
// ── ChaCha20-Poly1305 ────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -94,41 +108,63 @@ mod sys {
|
|||
in_: *const c_uchar, in_sz: c_uint,
|
||||
auth_tag: *const c_uchar, out: *mut c_uchar,
|
||||
) -> c_int { unreachable!() }
|
||||
|
||||
// ── HChaCha20 (for XChaCha20 sub-key) ───────────────────────────────
|
||||
// wc_HChaCha20(out: *mut u32, key: *const u32, nonce_16: *const u32) -> c_int
|
||||
pub unsafe fn wc_HChaCha20(
|
||||
out: *mut std::os::raw::c_uint,
|
||||
key: *const std::os::raw::c_uint,
|
||||
nonce: *const std::os::raw::c_uint,
|
||||
pub unsafe fn wc_XChaCha20Poly1305_Encrypt(
|
||||
dst: *mut c_uchar, dst_space: usize,
|
||||
src: *const c_uchar, src_len: usize,
|
||||
ad: *const c_uchar, ad_len: usize,
|
||||
nonce: *const c_uchar, nonce_len: usize,
|
||||
key: *const c_uchar, key_len: usize,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_XChaCha20Poly1305_Decrypt(
|
||||
dst: *mut c_uchar, dst_space: usize,
|
||||
src: *const c_uchar, src_len: usize,
|
||||
ad: *const c_uchar, ad_len: usize,
|
||||
nonce: *const c_uchar, nonce_len: usize,
|
||||
key: *const c_uchar, key_len: usize,
|
||||
) -> c_int { unreachable!() }
|
||||
|
||||
// ── KDF ─────────────────────────────────────────────────────────────
|
||||
// ── SHA-256 (one-shot available) ─────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_HKDF(
|
||||
type_: c_int,
|
||||
ikm: *const c_uchar, ikm_sz: c_uint,
|
||||
salt: *const c_uchar, salt_sz: c_uint,
|
||||
info: *const c_uchar, info_sz: c_uint,
|
||||
out: *mut c_uchar, out_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
|
||||
pub unsafe fn wc_Argon2id_Hash(
|
||||
out: *mut c_uchar, out_sz: c_uint,
|
||||
pwd: *const c_uchar, pwd_sz: c_uint,
|
||||
salt: *const c_uchar, salt_sz: c_uint,
|
||||
secret: *const c_uchar, secret_sz: c_uint,
|
||||
ad: *const c_uchar, ad_sz: c_uint,
|
||||
mem_kb: c_uint, iterations: c_uint, parallelism: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
|
||||
// ── BLAKE2b hash / MAC ───────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_Blake2bHash(
|
||||
out: *mut c_uchar, out_sz: c_uint,
|
||||
in_: *const c_uchar, in_sz: c_uint,
|
||||
key: *const c_uchar, key_sz: c_uint, // key_sz = 0 for unkeyed
|
||||
pub unsafe fn wc_Sha256Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
|
||||
// ── SHA-384 (init/update/final) ──────────────────────────────────────
|
||||
// wc_Sha384 is typedef wc_Sha512 in wolfSSL; bindgen exposes wc_Sha512.
|
||||
|
||||
pub unsafe fn wc_InitSha384(sha: *mut wc_Sha512) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha384Update(sha: *mut wc_Sha512, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha384Final(sha: *mut wc_Sha512, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha384Free(sha: *mut wc_Sha512) { unreachable!() }
|
||||
|
||||
// ── SHA-512 (init/update/final) ──────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitSha512(sha: *mut wc_Sha512) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha512Update(sha: *mut wc_Sha512, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha512Final(sha: *mut wc_Sha512, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha512Free(sha: *mut wc_Sha512) { unreachable!() }
|
||||
|
||||
// ── SHA3-256 (init/update/final) ─────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitSha3_256(sha: *mut wc_Sha3, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_256_Update(sha: *mut wc_Sha3, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_256_Final(sha: *mut wc_Sha3, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_256_Free(sha: *mut wc_Sha3) { unreachable!() }
|
||||
|
||||
// ── SHA3-512 (init/update/final) ─────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitSha3_512(sha: *mut wc_Sha3, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_512_Update(sha: *mut wc_Sha3, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_512_Final(sha: *mut wc_Sha3, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_512_Free(sha: *mut wc_Sha3) { unreachable!() }
|
||||
|
||||
// ── BLAKE2b (init/update/final) ──────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitBlake2b(b2b: *mut Blake2b, digest_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_InitBlake2b_WithKey(
|
||||
b2b: *mut Blake2b, digest_sz: c_uint,
|
||||
key: *const c_uchar, key_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Blake2bUpdate(b2b: *mut Blake2b, in_: *const c_uchar, in_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Blake2bFinal(b2b: *mut Blake2b, final_: *mut c_uchar, request_sz: c_uint) -> c_int { unreachable!() }
|
||||
|
||||
// ── HMAC ────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -137,22 +173,25 @@ mod sys {
|
|||
pub unsafe fn wc_HmacFinal(hmac: *mut Hmac, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_HmacFree(hmac: *mut Hmac) { unreachable!() }
|
||||
|
||||
// ── One-shot hashes ──────────────────────────────────────────────────
|
||||
// ── HKDF (TLS-1.3 Extract — RFC 5869 Extract step) ──────────────────
|
||||
|
||||
pub unsafe fn wc_Sha256Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha384Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha512Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_256Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Sha3_512Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Tls13_HKDF_Extract(
|
||||
prk: *mut c_uchar,
|
||||
salt: *const c_uchar, salt_len: c_uint,
|
||||
ikm: *mut c_uchar, ikm_len: c_uint,
|
||||
digest: c_int,
|
||||
) -> c_int { unreachable!() }
|
||||
|
||||
// ── RNG ─────────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_RNG_GenerateBlock(rng: *mut WC_RNG, buf: *mut c_uchar, sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_FreeRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
||||
|
||||
// ── Curve25519 ───────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_curve25519_make_key(rng: *mut WC_RNG, key_sz: c_int, key: *mut curve25519_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve25519_init(key: *mut curve25519_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve25519_export_key_raw(
|
||||
key: *mut curve25519_key,
|
||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||
|
|
@ -173,6 +212,7 @@ mod sys {
|
|||
// ── Curve448 ─────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_curve448_make_key(rng: *mut WC_RNG, key_sz: c_int, key: *mut curve448_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve448_init(key: *mut curve448_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve448_export_key_raw(
|
||||
key: *mut curve448_key,
|
||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
use ccc_crypto_core::{algorithms::MacAlgorithm, error::CryptoError};
|
||||
|
||||
// wolfCrypt hash type constants matching `enum wc_HashType` in hash.h.
|
||||
const WC_HASH_TYPE_SHA256: i32 = 8;
|
||||
const WC_HASH_TYPE_SHA384: i32 = 9;
|
||||
const WC_HASH_TYPE_SHA512: i32 = 10;
|
||||
// wolfCrypt hash type constants from `enum wc_HashType` in types.h (non-FIPS build).
|
||||
// WC_HASH_TYPE_SHA256 = 6, WC_HASH_TYPE_SHA384 = 7, WC_HASH_TYPE_SHA512 = 8.
|
||||
const WC_HASH_TYPE_SHA256: i32 = 6;
|
||||
const WC_HASH_TYPE_SHA384: i32 = 7;
|
||||
const WC_HASH_TYPE_SHA512: i32 = 8;
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// Public entry points
|
||||
|
|
@ -52,7 +53,11 @@ pub fn verify_mac(
|
|||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// HMAC using wolfCrypt's `wc_HmacSetKey` / `wc_HmacUpdate` / `wc_HmacFinal`.
|
||||
fn hmac(
|
||||
///
|
||||
/// `hash_type` must be a `WC_HASH_TYPE_*` constant; `out_len` must match the
|
||||
/// hash output size. This function is `pub(crate)` so that the KDF module can
|
||||
/// call it directly for RFC 5869 HKDF-Expand without duplicating HMAC logic.
|
||||
pub(crate) fn hmac(
|
||||
hash_type: i32,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
|
|
@ -104,6 +109,8 @@ fn hmac(
|
|||
///
|
||||
/// Key length must be 1–64 bytes (wolfCrypt limitation).
|
||||
/// Output is always 64 bytes (BLAKE2b-512 full digest length).
|
||||
///
|
||||
/// Uses `wc_InitBlake2b_WithKey` → `wc_Blake2bUpdate` → `wc_Blake2bFinal`.
|
||||
fn blake2b_mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
if key.is_empty() || key.len() > 64 {
|
||||
return Err(CryptoError::InvalidKey(
|
||||
|
|
@ -111,22 +118,36 @@ fn blake2b_mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
));
|
||||
}
|
||||
|
||||
let mut out = vec![0u8; 64];
|
||||
const OUT_LEN: u32 = 64;
|
||||
let mut out = vec![0u8; OUT_LEN as usize];
|
||||
|
||||
unsafe {
|
||||
// wolfCrypt: wc_Blake2b(blake2b, out, outLen, key, keyLen, data, dataSz)
|
||||
// Using the simplified one-shot Blake2bHash wrapper.
|
||||
let ret = crate::sys::wc_Blake2bHash(
|
||||
out.as_mut_ptr(),
|
||||
64u32,
|
||||
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||
|
||||
// Initialise with digest size and key for keyed (MAC) mode.
|
||||
let ret = crate::sys::wc_InitBlake2b_WithKey(
|
||||
&mut b2b,
|
||||
OUT_LEN,
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bHash returned {}", ret)
|
||||
format!("wc_InitBlake2b_WithKey returned {ret}")
|
||||
));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_Blake2bUpdate(&mut b2b, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bUpdate returned {ret}")
|
||||
));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_Blake2bFinal(&mut b2b, out.as_mut_ptr(), OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bFinal returned {ret}")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ use zeroize::Zeroizing;
|
|||
|
||||
use ccc_crypto_core::{
|
||||
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
|
||||
provider::{AeadProvider, HashProvider, KdfProvider, KemProvider, MacProvider},
|
||||
registry::ProviderRegistry,
|
||||
};
|
||||
// Provider traits are imported for use in future dispatch methods.
|
||||
#[allow(unused_imports)]
|
||||
use ccc_crypto_core::provider::{AeadProvider, HashProvider, KdfProvider, KemProvider, MacProvider};
|
||||
|
||||
use super::dto::{
|
||||
AeadEncryptRequest, AeadEncryptResult, AlgorithmCapabilityDto, HashRequest, KdfDeriveRequest,
|
||||
|
|
|
|||
Loading…
Reference in New Issue