lum_ccc_rust/crates/ccc-flutter-bridge/src/bridge.rs

282 lines
12 KiB
Rust

//! Public bridge functions — annotated with `#[frb(sync)]` or `#[frb]` so
//! that `flutter_rust_bridge` code-generation emits matching Dart APIs.
//!
//! All fallible functions return `anyhow::Result<T>` (FRB v2 maps that to a
//! Dart `Future<T>` that throws on error).
//!
//! # Naming convention
//! Functions are named `ccc_<family>_<operation>` so the generated Dart class
//! is pleasingly namespaced as `CccBridge.xxxYyy(…)`.
use flutter_rust_bridge::frb;
use zeroize::Zeroizing;
use ccc_crypto_core::{
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
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,
KemEncapResultDto, KemKeyPairDto, MacComputeRequest, ProviderCapabilitiesDto,
SelfTestReportDto,
};
// ──────────────────────────────────────────────────────────────────────────────
// Initialisation
// ──────────────────────────────────────────────────────────────────────────────
/// Initialise the CCC Rust layer.
///
/// **Must** be called once before any other bridge function, typically during
/// the app's boot sequence in `main.dart` (or `AppState.init()`).
///
/// Registers the wolfSSL provider into the global [`ProviderRegistry`].
/// Subsequent calls are no-ops.
#[frb(sync)]
pub fn ccc_init() {
static ONCE: std::sync::OnceLock<()> = std::sync::OnceLock::new();
ONCE.get_or_init(|| {
let _ = env_logger::try_init(); // tolerate error when already initialised
ccc_crypto_wolfssl::init();
log::info!("[ccc-bridge] wolfSSL provider registered");
});
}
// ──────────────────────────────────────────────────────────────────────────────
// Provider registry queries
// ──────────────────────────────────────────────────────────────────────────────
/// List all registered provider names.
#[frb(sync)]
pub fn ccc_list_providers() -> Vec<String> {
ProviderRegistry::global().list()
}
/// Retrieve full capabilities for a named provider.
///
/// Returns `None` if the provider is not registered.
#[frb(sync)]
pub fn ccc_capabilities(provider_name: String) -> Option<ProviderCapabilitiesDto> {
let reg = ProviderRegistry::global();
let guard = reg.get(&provider_name)?;
Some(guard.capabilities().into())
}
/// List all algorithms of a specific family that are **available** in a provider.
///
/// `family` is one of `"aead"`, `"kdf"`, `"mac"`, `"hash"`, `"kem"`.
#[frb(sync)]
pub fn ccc_available_algorithms(
provider_name: String,
family: String,
) -> Vec<AlgorithmCapabilityDto> {
let reg = ProviderRegistry::global();
let Some(guard) = reg.get(&provider_name) else { return vec![] };
// Convert the full capability map to DTO (which flattens HashMaps → Vecs).
let caps_dto: ProviderCapabilitiesDto = guard.capabilities().into();
let mut list = match family.as_str() {
"aead" => caps_dto.aead,
"kdf" => caps_dto.kdf,
"mac" => caps_dto.mac,
"hash" => caps_dto.hash,
"kem" => caps_dto.kem,
_ => vec![],
};
list.retain(|c| c.available);
list
}
// ──────────────────────────────────────────────────────────────────────────────
// AEAD
// ──────────────────────────────────────────────────────────────────────────────
/// Encrypt with an AEAD cipher.
///
/// Returns `ciphertext || 16-byte authentication tag` on success.
///
/// # Errors
/// Propagates [`ccc_crypto_core::error::CryptoError`] as an `anyhow::Result`.
pub fn ccc_aead_encrypt(req: AeadEncryptRequest) -> anyhow::Result<AeadEncryptResult> {
let algo = AeadAlgorithm::from_u32(req.algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown AEAD algo_id: {}", req.algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
let ct_tag = guard.encrypt_aead(algo, &req.key, &req.nonce, &req.plaintext, &req.aad)?;
Ok(AeadEncryptResult { ciphertext_and_tag: ct_tag })
}
/// Decrypt with an AEAD cipher.
///
/// Expects `ciphertext || 16-byte authentication tag` as `ciphertext_and_tag`.
/// Returns plaintext on success; returns an error if authentication fails.
pub fn ccc_aead_decrypt(
algo_id: u32,
key: Vec<u8>,
nonce: Vec<u8>,
ciphertext_and_tag: Vec<u8>,
aad: Vec<u8>,
) -> anyhow::Result<Vec<u8>> {
let algo = AeadAlgorithm::from_u32(algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown AEAD algo_id: {}", algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
Ok(guard.decrypt_aead(algo, &key, &nonce, &ciphertext_and_tag, &aad)?)
}
// ──────────────────────────────────────────────────────────────────────────────
// KDF
// ──────────────────────────────────────────────────────────────────────────────
/// Derive a key using a KDF.
///
/// Returns the derived key bytes (length = `req.out_length`).
pub fn ccc_kdf_derive(req: KdfDeriveRequest) -> anyhow::Result<Vec<u8>> {
let algo = KdfAlgorithm::from_u32(req.algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown KDF algo_id: {}", req.algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
let out: Zeroizing<Vec<u8>> =
guard.derive_key(algo, &req.ikm, &req.salt, &req.info, req.out_length as usize)?;
// Dart does not share memory with Rust; returning a plain Vec<u8> is safe.
Ok(out.to_vec())
}
// ──────────────────────────────────────────────────────────────────────────────
// MAC
// ──────────────────────────────────────────────────────────────────────────────
/// Compute a MAC tag.
pub fn ccc_mac_compute(req: MacComputeRequest) -> anyhow::Result<Vec<u8>> {
let algo = MacAlgorithm::from_u32(req.algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown MAC algo_id: {}", req.algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
Ok(guard.compute_mac(algo, &req.key, &req.data)?)
}
/// Verify a MAC tag in constant time (returns `true` if authentic).
pub fn ccc_mac_verify(
algo_id: u32,
key: Vec<u8>,
data: Vec<u8>,
mac_bytes: Vec<u8>,
) -> anyhow::Result<bool> {
let algo = MacAlgorithm::from_u32(algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown MAC algo_id: {}", algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
Ok(guard.verify_mac(algo, &key, &data, &mac_bytes)?)
}
// ──────────────────────────────────────────────────────────────────────────────
// Hash
// ──────────────────────────────────────────────────────────────────────────────
/// Compute a cryptographic hash.
pub fn ccc_hash(req: HashRequest) -> anyhow::Result<Vec<u8>> {
let algo = HashAlgorithm::from_u32(req.algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown Hash algo_id: {}", req.algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
Ok(guard.hash(algo, &req.data)?)
}
// ──────────────────────────────────────────────────────────────────────────────
// KEM
// ──────────────────────────────────────────────────────────────────────────────
/// Generate a KEM key pair.
pub fn ccc_kem_generate_keypair(algo_id: u32) -> anyhow::Result<KemKeyPairDto> {
let algo = KemAlgorithm::from_u32(algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown KEM algo_id: {}", algo_id))?;
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
// WolfSslProvider also implements KemProvider.
// Down-cast via concrete type via the global init path.
use ccc_crypto_wolfssl::provider::WolfSslProvider;
let _ = guard; // release the Arc lock before constructing a temporary
let temp = WolfSslProvider::new();
Ok(temp.generate_keypair(algo)?.into())
}
/// Encapsulate a shared secret to a public key.
pub fn ccc_kem_encapsulate(algo_id: u32, public_key: Vec<u8>) -> anyhow::Result<KemEncapResultDto> {
let algo = KemAlgorithm::from_u32(algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown KEM algo_id: {}", algo_id))?;
use ccc_crypto_wolfssl::provider::WolfSslProvider;
let temp = WolfSslProvider::new();
Ok(temp.encapsulate(algo, &public_key)?.into())
}
/// Decapsulate a shared secret using a private key.
///
/// Returns the shared secret bytes.
pub fn ccc_kem_decapsulate(
algo_id: u32,
private_key: Vec<u8>,
ciphertext: Vec<u8>,
) -> anyhow::Result<Vec<u8>> {
let algo = KemAlgorithm::from_u32(algo_id)
.ok_or_else(|| anyhow::anyhow!("unknown KEM algo_id: {}", algo_id))?;
use ccc_crypto_wolfssl::provider::WolfSslProvider;
let temp = WolfSslProvider::new();
let secret: Zeroizing<Vec<u8>> = temp.decapsulate(algo, &private_key, &ciphertext)?;
Ok(secret.to_vec())
}
// ──────────────────────────────────────────────────────────────────────────────
// Self-test
// ──────────────────────────────────────────────────────────────────────────────
/// Run the embedded NIST / RFC test vectors for the wolfSSL provider.
///
/// Returns a full [`SelfTestReportDto`]; `all_passed == true` means every
/// algorithm passed its vectors. Check individual `results` for details.
pub fn ccc_self_test() -> anyhow::Result<SelfTestReportDto> {
let reg = ProviderRegistry::global();
let guard = reg
.get("wolfssl")
.ok_or_else(|| anyhow::anyhow!("wolfssl provider not registered"))?;
Ok(guard.self_test().into())
}