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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
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]]
|
[[package]]
|
||||||
name = "atomic"
|
name = "atomic"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
@ -132,6 +145,12 @@ dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.72.1"
|
version = "0.72.1"
|
||||||
|
|
@ -158,6 +177,15 @@ version = "2.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
|
@ -229,6 +257,7 @@ dependencies = [
|
||||||
name = "ccc-crypto-wolfssl"
|
name = "ccc-crypto-wolfssl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argon2",
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"ccc-crypto-core",
|
"ccc-crypto-core",
|
||||||
"cmake",
|
"cmake",
|
||||||
|
|
@ -303,6 +332,15 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -351,6 +389,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -728,6 +767,17 @@ dependencies = [
|
||||||
"log",
|
"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]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -777,6 +827,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.3"
|
||||||
|
|
@ -879,6 +935,12 @@ version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.117"
|
version = "2.0.117"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ ccc-crypto-core.workspace = true
|
||||||
zeroize.workspace = true
|
zeroize.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
log.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]
|
[build-dependencies]
|
||||||
# cmake crate drives the wolfSSL CMake build from source.
|
# 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)`.
|
/// 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!(
|
println!(
|
||||||
"cargo:warning=Building wolfSSL from source at {}",
|
"cargo:warning=Building wolfSSL from source at {}",
|
||||||
source_dir.display()
|
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);
|
let mut cfg = cmake::Config::new(source_dir);
|
||||||
|
|
||||||
// ── Core algorithm selection ─────────────────────────────────────────────
|
// ── Core algorithm selection ─────────────────────────────────────────────
|
||||||
// Enable exactly the algorithms the CCC system needs in Phase 4.
|
// wolfSSL cmake uses add_option() with "yes"/"no" string values (not ON/OFF).
|
||||||
// Additional algorithms can be enabled here for Phase 5+ PQ support.
|
// 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
|
cfg
|
||||||
// Build as a static library.
|
// Build as a static library.
|
||||||
.define("BUILD_SHARED_LIBS", "OFF")
|
.define("BUILD_SHARED_LIBS", "OFF")
|
||||||
.define("WOLFSSL_BUILD_SHARED_LIBS", "OFF")
|
// wolfCrypt-only — no TLS stack. This is the correct cmake variable.
|
||||||
// Disable TLS/SSL stack — we only need the wolfCrypt algorithms.
|
.define("WOLFSSL_CRYPT_ONLY", "yes")
|
||||||
.define("WOLFSSL_NO_TLS", "ON")
|
// AEAD (WOLFSSL_AESGCM — note: no underscore between AES and GCM)
|
||||||
// AEAD
|
.define("WOLFSSL_AESGCM", "yes")
|
||||||
.define("WOLFSSL_AES_GCM", "ON")
|
.define("WOLFSSL_CHACHA", "yes")
|
||||||
.define("WOLFSSL_CHACHA", "ON")
|
.define("WOLFSSL_POLY1305", "yes")
|
||||||
.define("WOLFSSL_POLY1305", "ON")
|
|
||||||
// Hash
|
// Hash
|
||||||
.define("WOLFSSL_SHA224", "ON")
|
.define("WOLFSSL_SHA384", "yes")
|
||||||
.define("WOLFSSL_SHA384", "ON")
|
.define("WOLFSSL_SHA512", "yes")
|
||||||
.define("WOLFSSL_SHA512", "ON")
|
.define("WOLFSSL_SHA3", "yes")
|
||||||
.define("WOLFSSL_SHA3", "ON")
|
// BLAKE2b/BLAKE2s — not a cmake option; enabled via C preprocessor flag.
|
||||||
.define("WOLFSSL_BLAKE2", "ON")
|
// Added to both cmake C flags and bindgen clang args below.
|
||||||
// KDF
|
// KDF (HKDF via TLS-1.3 HKDF primitives, PBKDF2)
|
||||||
.define("WOLFSSL_HKDF", "ON")
|
.define("WOLFSSL_HKDF", "yes")
|
||||||
.define("WOLFSSL_PWDBASED", "ON") // PBKDF2, Argon2
|
.define("WOLFSSL_PWDBASED", "yes")
|
||||||
// MAC
|
// MAC
|
||||||
.define("WOLFSSL_HMAC", "ON")
|
.define("WOLFSSL_HMAC", "yes")
|
||||||
// Asymmetric (needed for X25519 ratchet)
|
// Asymmetric (X25519 / X448 DH for ratchet)
|
||||||
.define("WOLFSSL_CURVE25519", "ON")
|
.define("WOLFSSL_CURVE25519", "yes")
|
||||||
.define("WOLFSSL_CURVE448", "ON")
|
.define("WOLFSSL_CURVE448", "yes")
|
||||||
// RNG (needed internally)
|
// RNG
|
||||||
.define("WOLFSSL_RNG", "ON")
|
.define("WOLFSSL_RNG", "yes")
|
||||||
// Minimise binary size.
|
// Minimise binary size.
|
||||||
.define("WOLFSSL_CRYPTONLY", "ON")
|
|
||||||
.define("WOLFSSL_MIN_RSA_BITS", "2048")
|
.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();
|
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/aes.h").to_str().unwrap())
|
||||||
.header(include_dir.join("wolfssl/wolfcrypt/chacha20_poly1305.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/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/sha3.h").to_str().unwrap())
|
||||||
.header(include_dir.join("wolfssl/wolfcrypt/blake2.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/hmac.h").to_str().unwrap())
|
||||||
.header(include_dir.join("wolfssl/wolfcrypt/kdf.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/curve25519.h").to_str().unwrap())
|
||||||
.header(include_dir.join("wolfssl/wolfcrypt/curve448.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())
|
.header(include_dir.join("wolfssl/wolfcrypt/random.h").to_str().unwrap())
|
||||||
.clang_arg(format!("-I{}", include_dir.display()))
|
.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_Aes.*")
|
||||||
|
.allowlist_function("wc_AesGcm.*")
|
||||||
|
.allowlist_function("wc_AesInit")
|
||||||
|
.allowlist_function("wc_AesFree")
|
||||||
.allowlist_function("wc_ChaCha20Poly1305.*")
|
.allowlist_function("wc_ChaCha20Poly1305.*")
|
||||||
.allowlist_function("wc_Sha.*")
|
.allowlist_function("wc_XChaCha20Poly1305.*")
|
||||||
.allowlist_function("wc_Blake2.*")
|
// SHA-256 (one-shot available)
|
||||||
.allowlist_function("wc_Hmac.*")
|
.allowlist_function("wc_Sha256Hash")
|
||||||
.allowlist_function("wc_HKDF.*")
|
.allowlist_function("wc_Sha256.*")
|
||||||
.allowlist_function("wc_Pbkdf2.*")
|
// SHA-384 (init/update/final — typedef of wc_Sha512 struct)
|
||||||
.allowlist_function("wc_Argon2.*")
|
.allowlist_function("wc_InitSha384")
|
||||||
.allowlist_function("wc_curve25519.*")
|
.allowlist_function("wc_Sha384Update")
|
||||||
.allowlist_function("wc_curve448.*")
|
.allowlist_function("wc_Sha384Final")
|
||||||
.allowlist_function("wc_InitRng.*")
|
.allowlist_function("wc_Sha384Free")
|
||||||
.allowlist_function("wc_RNG.*")
|
// SHA-512 (init/update/final)
|
||||||
.allowlist_function("wc_FreeRng.*")
|
.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("Aes")
|
||||||
.allowlist_type("Hmac")
|
.allowlist_type("Hmac")
|
||||||
.allowlist_type("WC_RNG")
|
.allowlist_type("WC_RNG")
|
||||||
.allowlist_type("curve25519.*")
|
.allowlist_type("OS_Seed")
|
||||||
.allowlist_type("curve448.*")
|
.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("AES_.*")
|
||||||
.allowlist_var("WC_.*")
|
.allowlist_var("WC_SHA.*")
|
||||||
.allowlist_var("SHA.*")
|
.allowlist_var("WC_HASH.*")
|
||||||
.allowlist_var("BLAKE2.*")
|
.allowlist_var("CHACHA20_POLY1305.*")
|
||||||
// Silence warnings for types we don't control.
|
// Only block specific C internal types that cause noise.
|
||||||
.blocklist_type("__.*")
|
// 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)
|
.derive_debug(false)
|
||||||
.layout_tests(false)
|
.layout_tests(false)
|
||||||
.generate()
|
.generate()
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,12 @@ fn chacha20_poly1305_decrypt(
|
||||||
// XChaCha20-Poly1305 (24-byte nonce)
|
// XChaCha20-Poly1305 (24-byte nonce)
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
//
|
//
|
||||||
// wolfCrypt does not expose a single XChaCha20-Poly1305 one-shot function.
|
// wolfCrypt exposes `wc_XChaCha20Poly1305_Encrypt/Decrypt` as public one-shot
|
||||||
// We derive a sub-key from the first 16 bytes of the nonce using HChaCha20,
|
// functions that accept the 24-byte XChaCha nonce directly and handle the
|
||||||
// then apply ChaCha20-Poly1305 with the last 12 bytes for the nonce.
|
// internal HChaCha20 sub-key derivation (RFC 8439 §2) themselves.
|
||||||
// This matches the XChaCha20-Poly1305 construction in RFC 8439 §2.
|
//
|
||||||
|
// Output layout: dst = ciphertext || 16-byte Poly1305 tag
|
||||||
|
// (dst_space = src_len + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE = src_len + 16).
|
||||||
|
|
||||||
fn xchacha20_poly1305_encrypt(
|
fn xchacha20_poly1305_encrypt(
|
||||||
key: &[u8],
|
key: &[u8],
|
||||||
|
|
@ -262,8 +264,32 @@ fn xchacha20_poly1305_encrypt(
|
||||||
plaintext: &[u8],
|
plaintext: &[u8],
|
||||||
aad: &[u8],
|
aad: &[u8],
|
||||||
) -> Result<Vec<u8>, CryptoError> {
|
) -> Result<Vec<u8>, CryptoError> {
|
||||||
let (subkey, chacha_nonce) = xchacha_derive_subkey(key, nonce)?;
|
// dst holds ciphertext (same length as plaintext) followed by the 16-byte tag.
|
||||||
chacha20_poly1305_encrypt(&subkey, &chacha_nonce, plaintext, aad)
|
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(
|
fn xchacha20_poly1305_decrypt(
|
||||||
|
|
@ -272,32 +298,33 @@ fn xchacha20_poly1305_decrypt(
|
||||||
ciphertext_and_tag: &[u8],
|
ciphertext_and_tag: &[u8],
|
||||||
aad: &[u8],
|
aad: &[u8],
|
||||||
) -> Result<Vec<u8>, CryptoError> {
|
) -> Result<Vec<u8>, CryptoError> {
|
||||||
let (subkey, chacha_nonce) = xchacha_derive_subkey(key, nonce)?;
|
if ciphertext_and_tag.len() < TAG_LEN {
|
||||||
chacha20_poly1305_decrypt(&subkey, &chacha_nonce, ciphertext_and_tag, aad)
|
return Err(CryptoError::InvalidInput(
|
||||||
}
|
"XChaCha20-Poly1305: ciphertext too short".into()
|
||||||
|
|
||||||
/// 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)
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
let pt_len = ciphertext_and_tag.len() - TAG_LEN;
|
||||||
// ChaCha nonce = [0u8 × 4] || nonce[16..24]
|
let mut dst = vec![0u8; pt_len];
|
||||||
let mut chacha_nonce = vec![0u8; 12];
|
|
||||||
chacha_nonce[4..12].copy_from_slice(&nonce[16..24]);
|
|
||||||
|
|
||||||
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)
|
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> {
|
fn sha384(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
let mut out = vec![0u8; 48];
|
let mut out = vec![0u8; 48];
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = crate::sys::wc_Sha384Hash(
|
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||||
data.as_ptr(),
|
let ret = crate::sys::wc_InitSha384(&mut sha);
|
||||||
data.len() as u32,
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
if ret != 0 {
|
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)
|
Ok(out)
|
||||||
|
|
@ -61,13 +69,20 @@ fn sha384(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
let mut out = vec![0u8; 64];
|
let mut out = vec![0u8; 64];
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = crate::sys::wc_Sha512Hash(
|
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||||
data.as_ptr(),
|
let ret = crate::sys::wc_InitSha512(&mut sha);
|
||||||
data.len() as u32,
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
if ret != 0 {
|
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)
|
Ok(out)
|
||||||
|
|
@ -77,17 +92,25 @@ fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
// SHA-3 family
|
// 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> {
|
fn sha3_256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
let mut out = vec![0u8; 32];
|
let mut out = vec![0u8; 32];
|
||||||
unsafe {
|
unsafe {
|
||||||
// wolfCrypt: wc_Sha3_256Hash(data, dataSz, digest)
|
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||||
let ret = crate::sys::wc_Sha3_256Hash(
|
let ret = crate::sys::wc_InitSha3_256(&mut sha, std::ptr::null_mut(), -2);
|
||||||
data.as_ptr(),
|
|
||||||
data.len() as u32,
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
if ret != 0 {
|
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)
|
Ok(out)
|
||||||
|
|
@ -96,13 +119,20 @@ fn sha3_256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
let mut out = vec![0u8; 64];
|
let mut out = vec![0u8; 64];
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = crate::sys::wc_Sha3_512Hash(
|
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||||
data.as_ptr(),
|
let ret = crate::sys::wc_InitSha3_512(&mut sha, std::ptr::null_mut(), -2);
|
||||||
data.len() as u32,
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
if ret != 0 {
|
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)
|
Ok(out)
|
||||||
|
|
@ -112,23 +142,26 @@ fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
// BLAKE2b-512
|
// BLAKE2b-512
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
fn blake2b_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
/// Unkeyed BLAKE2b-512 digest using wolfCrypt init / update / final.
|
||||||
let mut out = vec![0u8; 64];
|
///
|
||||||
|
/// 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 {
|
unsafe {
|
||||||
// wc_Blake2bHash(out, outLen, key, keyLen, data, dataSz)
|
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||||
// No key = unkeyed hash (pass NULL, 0).
|
let ret = crate::sys::wc_InitBlake2b(&mut b2b, OUT_LEN);
|
||||||
let ret = crate::sys::wc_Blake2bHash(
|
|
||||||
out.as_mut_ptr(),
|
|
||||||
64u32,
|
|
||||||
std::ptr::null(),
|
|
||||||
0u32,
|
|
||||||
data.as_ptr(),
|
|
||||||
data.len() as u32,
|
|
||||||
);
|
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return Err(CryptoError::InternalError(
|
return Err(CryptoError::InternalError(format!("wc_InitBlake2b returned {ret}")));
|
||||||
format!("wc_Blake2bHash 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)
|
Ok(out)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,40 @@
|
||||||
//! Key derivation function implementations via wolfCrypt.
|
//! Key derivation function implementations via wolfCrypt.
|
||||||
//!
|
//!
|
||||||
//! Covers HKDF (SHA-256 / SHA-384 / SHA-512), Argon2id, and BLAKE2b-based KDF.
|
//! 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 zeroize::Zeroizing;
|
||||||
|
|
||||||
use ccc_crypto_core::{algorithms::KdfAlgorithm, error::CryptoError};
|
use ccc_crypto_core::{algorithms::KdfAlgorithm, error::CryptoError};
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// wolfCrypt hash type constants from `enum wc_HashType` in types.h (non-FIPS).
|
||||||
// wolfCrypt hash type constants (passed to wc_HKDF)
|
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.
|
/// Output byte lengths for each HMAC hash variant, matching the digest size.
|
||||||
/// These match the `enum wc_HashType` values in `wolfssl/wolfcrypt/hash.h`.
|
fn hash_output_len(hash_type: i32) -> usize {
|
||||||
const WC_HASH_TYPE_SHA256: i32 = 8;
|
match hash_type {
|
||||||
const WC_HASH_TYPE_SHA384: i32 = 9;
|
WC_HASH_TYPE_SHA256 => 32,
|
||||||
const WC_HASH_TYPE_SHA512: i32 = 10;
|
WC_HASH_TYPE_SHA384 => 48,
|
||||||
|
WC_HASH_TYPE_SHA512 => 64,
|
||||||
|
_ => 32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
// Public entry point
|
// 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(
|
fn hkdf(
|
||||||
hash_type: i32,
|
hash_type: i32,
|
||||||
ikm: &[u8],
|
ikm: &[u8],
|
||||||
|
|
@ -53,26 +74,94 @@ fn hkdf(
|
||||||
info: &[u8],
|
info: &[u8],
|
||||||
length: usize,
|
length: usize,
|
||||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
) -> 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 {
|
unsafe {
|
||||||
let ret = crate::sys::wc_HKDF(
|
let ret = crate::sys::wc_Tls13_HKDF_Extract(
|
||||||
hash_type,
|
prk.as_mut_ptr(),
|
||||||
ikm.as_ptr(),
|
|
||||||
ikm.len() as u32,
|
|
||||||
if salt.is_empty() { std::ptr::null() } else { salt.as_ptr() },
|
if salt.is_empty() { std::ptr::null() } else { salt.as_ptr() },
|
||||||
salt.len() as u32,
|
salt.len() as u32,
|
||||||
if info.is_empty() { std::ptr::null() } else { info.as_ptr() },
|
ikm_buf.as_mut_ptr(),
|
||||||
info.len() as u32,
|
ikm_buf.len() as u32,
|
||||||
out.as_mut_ptr(),
|
hash_type,
|
||||||
length as u32,
|
|
||||||
);
|
);
|
||||||
if ret != 0 {
|
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
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// 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
|
/// 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(
|
fn argon2id(
|
||||||
password: &[u8], // treated as ikm / password
|
password: &[u8],
|
||||||
salt: &[u8],
|
salt: &[u8],
|
||||||
length: usize,
|
length: usize,
|
||||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
) -> 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 {
|
if salt.len() < 8 {
|
||||||
return Err(CryptoError::InvalidInput(
|
return Err(CryptoError::InvalidInput(
|
||||||
"Argon2id requires at least 8-byte salt".into()
|
"Argon2id requires at least 8-byte salt".into()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut out = Zeroizing::new(vec![0u8; length]);
|
use argon2::{Algorithm, Argon2, Params, Version};
|
||||||
|
|
||||||
unsafe {
|
// Match DEFAULT_CIPHER_PARAMS in cipher_constants.dart:
|
||||||
// wolfCrypt Argon2:
|
// argon2_memory_kb: 65536 (64 MiB)
|
||||||
// typedef struct Argon2_t { ... } Argon2;
|
// argon2_iterations: 3
|
||||||
// int wc_Argon2Hash(Argon2* arg2, const byte* pwd, word32 pwdSz,
|
// argon2_parallelism: 4
|
||||||
// const byte* salt, word32 saltSz, byte* out, word32 outSz)
|
let params = Params::new(
|
||||||
//
|
65_536, // m_cost: 64 MiB
|
||||||
// We use the simplified wc_Argon2id_Hash wrapper (available in wolfCrypt ≥ 5.5).
|
3, // t_cost: iterations
|
||||||
let ret = crate::sys::wc_Argon2id_Hash(
|
4, // p_cost: parallelism (lanes)
|
||||||
out.as_mut_ptr(),
|
Some(length), // output length
|
||||||
length as u32,
|
)
|
||||||
password.as_ptr(),
|
.map_err(|e| CryptoError::InternalError(format!("Argon2 params error: {e}")))?;
|
||||||
password.len() as u32,
|
|
||||||
salt.as_ptr(),
|
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||||
salt.len() as u32,
|
|
||||||
std::ptr::null(), // secret
|
let mut out = Zeroizing::new(vec![0u8; length]);
|
||||||
0u32, // secretLen
|
argon2
|
||||||
std::ptr::null(), // additional data
|
.hash_password_into(password, salt, &mut out)
|
||||||
0u32, // additional data len
|
.map_err(|e| CryptoError::InternalError(format!("Argon2id hash error: {e}")))?;
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ mod sys {
|
||||||
//! feature is only used for type-checking and documentation builds.
|
//! feature is only used for type-checking and documentation builds.
|
||||||
#![allow(non_camel_case_types, dead_code, unused_variables)]
|
#![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 ────────────────────────────────────────────
|
// ── Opaque C struct stubs ────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -58,6 +58,18 @@ mod sys {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct WC_RNG([u8; 256]);
|
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.
|
/// Stub for wolfCrypt `curve25519_key` struct.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct curve25519_key([u8; 256]);
|
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,
|
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,
|
auth_in: *const c_uchar, auth_in_sz: c_uint,
|
||||||
) -> c_int { unreachable!() }
|
) -> 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 ────────────────────────────────────────────────
|
// ── ChaCha20-Poly1305 ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -94,41 +108,63 @@ mod sys {
|
||||||
in_: *const c_uchar, in_sz: c_uint,
|
in_: *const c_uchar, in_sz: c_uint,
|
||||||
auth_tag: *const c_uchar, out: *mut c_uchar,
|
auth_tag: *const c_uchar, out: *mut c_uchar,
|
||||||
) -> c_int { unreachable!() }
|
) -> c_int { unreachable!() }
|
||||||
|
pub unsafe fn wc_XChaCha20Poly1305_Encrypt(
|
||||||
// ── HChaCha20 (for XChaCha20 sub-key) ───────────────────────────────
|
dst: *mut c_uchar, dst_space: usize,
|
||||||
// wc_HChaCha20(out: *mut u32, key: *const u32, nonce_16: *const u32) -> c_int
|
src: *const c_uchar, src_len: usize,
|
||||||
pub unsafe fn wc_HChaCha20(
|
ad: *const c_uchar, ad_len: usize,
|
||||||
out: *mut std::os::raw::c_uint,
|
nonce: *const c_uchar, nonce_len: usize,
|
||||||
key: *const std::os::raw::c_uint,
|
key: *const c_uchar, key_len: usize,
|
||||||
nonce: *const std::os::raw::c_uint,
|
) -> 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!() }
|
) -> c_int { unreachable!() }
|
||||||
|
|
||||||
// ── KDF ─────────────────────────────────────────────────────────────
|
// ── SHA-256 (one-shot available) ─────────────────────────────────────
|
||||||
|
|
||||||
pub unsafe fn wc_HKDF(
|
pub unsafe fn wc_Sha256Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||||
type_: c_int,
|
|
||||||
ikm: *const c_uchar, ikm_sz: c_uint,
|
// ── SHA-384 (init/update/final) ──────────────────────────────────────
|
||||||
salt: *const c_uchar, salt_sz: c_uint,
|
// wc_Sha384 is typedef wc_Sha512 in wolfSSL; bindgen exposes wc_Sha512.
|
||||||
info: *const c_uchar, info_sz: c_uint,
|
|
||||||
out: *mut c_uchar, out_sz: c_uint,
|
pub unsafe fn wc_InitSha384(sha: *mut wc_Sha512) -> c_int { unreachable!() }
|
||||||
) -> 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_Argon2id_Hash(
|
pub unsafe fn wc_Sha384Free(sha: *mut wc_Sha512) { unreachable!() }
|
||||||
out: *mut c_uchar, out_sz: c_uint,
|
|
||||||
pwd: *const c_uchar, pwd_sz: c_uint,
|
// ── SHA-512 (init/update/final) ──────────────────────────────────────
|
||||||
salt: *const c_uchar, salt_sz: c_uint,
|
|
||||||
secret: *const c_uchar, secret_sz: c_uint,
|
pub unsafe fn wc_InitSha512(sha: *mut wc_Sha512) -> c_int { unreachable!() }
|
||||||
ad: *const c_uchar, ad_sz: c_uint,
|
pub unsafe fn wc_Sha512Update(sha: *mut wc_Sha512, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||||
mem_kb: c_uint, iterations: c_uint, parallelism: c_uint,
|
pub unsafe fn wc_Sha512Final(sha: *mut wc_Sha512, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||||
) -> c_int { unreachable!() }
|
pub unsafe fn wc_Sha512Free(sha: *mut wc_Sha512) { unreachable!() }
|
||||||
|
|
||||||
// ── BLAKE2b hash / MAC ───────────────────────────────────────────────
|
// ── SHA3-256 (init/update/final) ─────────────────────────────────────
|
||||||
|
|
||||||
pub unsafe fn wc_Blake2bHash(
|
pub unsafe fn wc_InitSha3_256(sha: *mut wc_Sha3, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||||
out: *mut c_uchar, out_sz: c_uint,
|
pub unsafe fn wc_Sha3_256_Update(sha: *mut wc_Sha3, data: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||||
in_: *const c_uchar, in_sz: c_uint,
|
pub unsafe fn wc_Sha3_256_Final(sha: *mut wc_Sha3, hash: *mut c_uchar) -> c_int { unreachable!() }
|
||||||
key: *const c_uchar, key_sz: c_uint, // key_sz = 0 for unkeyed
|
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!() }
|
) -> 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 ────────────────────────────────────────────────────────────
|
// ── 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_HmacFinal(hmac: *mut Hmac, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||||
pub unsafe fn wc_HmacFree(hmac: *mut Hmac) { 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_Tls13_HKDF_Extract(
|
||||||
pub unsafe fn wc_Sha384Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
prk: *mut c_uchar,
|
||||||
pub unsafe fn wc_Sha512Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
salt: *const c_uchar, salt_len: c_uint,
|
||||||
pub unsafe fn wc_Sha3_256Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
ikm: *mut c_uchar, ikm_len: c_uint,
|
||||||
pub unsafe fn wc_Sha3_512Hash(in_: *const c_uchar, in_sz: c_uint, out: *mut c_uchar) -> c_int { unreachable!() }
|
digest: c_int,
|
||||||
|
) -> c_int { unreachable!() }
|
||||||
|
|
||||||
// ── RNG ─────────────────────────────────────────────────────────────
|
// ── RNG ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub unsafe fn wc_InitRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
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!() }
|
pub unsafe fn wc_FreeRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
||||||
|
|
||||||
// ── Curve25519 ───────────────────────────────────────────────────────
|
// ── 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_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(
|
pub unsafe fn wc_curve25519_export_key_raw(
|
||||||
key: *mut curve25519_key,
|
key: *mut curve25519_key,
|
||||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||||
|
|
@ -173,6 +212,7 @@ mod sys {
|
||||||
// ── Curve448 ─────────────────────────────────────────────────────────
|
// ── 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_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(
|
pub unsafe fn wc_curve448_export_key_raw(
|
||||||
key: *mut curve448_key,
|
key: *mut curve448_key,
|
||||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
use ccc_crypto_core::{algorithms::MacAlgorithm, error::CryptoError};
|
use ccc_crypto_core::{algorithms::MacAlgorithm, error::CryptoError};
|
||||||
|
|
||||||
// wolfCrypt hash type constants matching `enum wc_HashType` in hash.h.
|
// wolfCrypt hash type constants from `enum wc_HashType` in types.h (non-FIPS build).
|
||||||
const WC_HASH_TYPE_SHA256: i32 = 8;
|
// WC_HASH_TYPE_SHA256 = 6, WC_HASH_TYPE_SHA384 = 7, WC_HASH_TYPE_SHA512 = 8.
|
||||||
const WC_HASH_TYPE_SHA384: i32 = 9;
|
const WC_HASH_TYPE_SHA256: i32 = 6;
|
||||||
const WC_HASH_TYPE_SHA512: i32 = 10;
|
const WC_HASH_TYPE_SHA384: i32 = 7;
|
||||||
|
const WC_HASH_TYPE_SHA512: i32 = 8;
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
// Public entry points
|
// Public entry points
|
||||||
|
|
@ -52,7 +53,11 @@ pub fn verify_mac(
|
||||||
// ──────────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// HMAC using wolfCrypt's `wc_HmacSetKey` / `wc_HmacUpdate` / `wc_HmacFinal`.
|
/// 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,
|
hash_type: i32,
|
||||||
key: &[u8],
|
key: &[u8],
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
|
|
@ -104,6 +109,8 @@ fn hmac(
|
||||||
///
|
///
|
||||||
/// Key length must be 1–64 bytes (wolfCrypt limitation).
|
/// Key length must be 1–64 bytes (wolfCrypt limitation).
|
||||||
/// Output is always 64 bytes (BLAKE2b-512 full digest length).
|
/// 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> {
|
fn blake2b_mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
if key.is_empty() || key.len() > 64 {
|
if key.is_empty() || key.len() > 64 {
|
||||||
return Err(CryptoError::InvalidKey(
|
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 {
|
unsafe {
|
||||||
// wolfCrypt: wc_Blake2b(blake2b, out, outLen, key, keyLen, data, dataSz)
|
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||||
// Using the simplified one-shot Blake2bHash wrapper.
|
|
||||||
let ret = crate::sys::wc_Blake2bHash(
|
// Initialise with digest size and key for keyed (MAC) mode.
|
||||||
out.as_mut_ptr(),
|
let ret = crate::sys::wc_InitBlake2b_WithKey(
|
||||||
64u32,
|
&mut b2b,
|
||||||
|
OUT_LEN,
|
||||||
key.as_ptr(),
|
key.as_ptr(),
|
||||||
key.len() as u32,
|
key.len() as u32,
|
||||||
data.as_ptr(),
|
|
||||||
data.len() as u32,
|
|
||||||
);
|
);
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return Err(CryptoError::InternalError(
|
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::{
|
use ccc_crypto_core::{
|
||||||
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
|
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
|
||||||
provider::{AeadProvider, HashProvider, KdfProvider, KemProvider, MacProvider},
|
|
||||||
registry::ProviderRegistry,
|
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::{
|
use super::dto::{
|
||||||
AeadEncryptRequest, AeadEncryptResult, AlgorithmCapabilityDto, HashRequest, KdfDeriveRequest,
|
AeadEncryptRequest, AeadEncryptResult, AlgorithmCapabilityDto, HashRequest, KdfDeriveRequest,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue