// Bindings to NSS's freebl AEAD primitives, bypassing the PKCS#11 session // layer. // // Functions are accessed through FREEBL_GetVector() rather than as direct // symbol references, because on some platforms (e.g. FreeBSD) freebl only // exports FREEBL_GetVector.
use std::{
os::raw::{c_int, c_uchar, c_uint, c_ulong, c_void},
sync::OnceLock,
};
// GCM message-level params (PKCS#11 v3, pkcs11t.h). // For CKG_NO_GENERATE (ivGenerator = 0), pIv/ulIvLen supply the full nonce. // For encrypt, pTag is the output tag buffer; for decrypt, pTag is the input // tag to verify. ulTagBits = TAG_LEN * 8 = 128. // // NSS's pkcs11t.h pulls in pkcs11p.h which applies `#pragma pack(push, // cryptoki, 1)` on all platforms. On LP64 all six fields are 8 bytes wide so // packing is a no-op (sizeof = 48). On Windows LLP64 (CK_ULONG = 4 bytes) // packing removes the natural 4-byte padding before `pTag` (sizeof = 32). #[repr(C, packed)] #[derive(Copy, Clone)] #[expect(non_snake_case, reason = "PKCS#11 naming conventions.")] pubstruct CK_GCM_MESSAGE_PARAMS { pub pIv: *mut c_uchar, pub ulIvLen: c_ulong, pub ulIvFixedBits: c_ulong, pub ivGenerator: c_ulong, // CK_GENERATOR_FUNCTION; 0 = CKG_NO_GENERATE pub pTag: *mut c_uchar, pub ulTagBits: c_ulong,
}
// LP64: pointer == c_ulong == 8 bytes, no padding even packed → size = 6 × 8. // Windows LLP64: packed size = 32, which != 6 × 4 = 24, so skip the check. #[cfg(not(target_os = "windows"))] const _: () = assert!(size_of::<CK_GCM_MESSAGE_PARAMS>() == 6 * size_of::<c_ulong>());
// Encrypt and decrypt share this signature; direction is baked in at // construction time by storing either pointer. pubtype ChaChaOpFn = unsafeextern"C"fn(
*const ChaCha20Poly1305Context,
*mut c_uchar,
*mut c_uint,
c_uint,
*const c_uchar,
c_uint,
*const c_uchar,
c_uint,
*const c_uchar,
c_uint,
*mut c_uchar,
) -> c_int;
// Full FREEBLVectorStr layout generated from lib/freebl/loader.h. // AESContext and ChaCha20Poly1305Context are blocklisted so the definitions // above are used. See the regeneration instructions in vector.rs. #[expect(
clippy::type_complexity,
dead_code,
non_camel_case_types,
non_snake_case,
reason = "generated code in vector.rs"
)] mod generated { usesuper::{AESContext, ChaCha20Poly1305Context}; // PLArenaPool is NSPR-internal; only forward-declared in freebl headers. // KyberParams is an enum defined in kyber.h; underlying type is c_uint. // Both appear in FREEBLVectorStr fields we don't extract. #[repr(C)] pubstruct PLArenaPool {
_unused: [u8; 0],
} pubtype KyberParams = ::core::ffi::c_uint;
include!("vector.rs");
} use generated::FREEBLVectorStr;
fn freebl() -> &'static FreeblFns { static FREEBL: OnceLock<FreeblFns> = OnceLock::new();
FREEBL.get_or_init(|| { let ptr = unsafe { FREEBL_GetVector() };
assert!(!ptr.is_null(), "FREEBL_GetVector() returned null"); let v = unsafe { &*ptr }; // p_AES_AEAD is at the highest offset among the fields we extract; // check the vector is at least large enough to contain it. let min =
core::mem::offset_of!(FREEBLVectorStr, p_AES_AEAD) + size_of::<Option<AesAeadFn>>();
assert!(
usize::from(v.length) >= min, "freebl vector too short (length {}, need {min})",
v.length,
);
FreeblFns {
aes_create: v.p_AES_CreateContext.expect("freebl: AES_CreateContext"),
aes_destroy: v.p_AES_DestroyContext.expect("freebl: AES_DestroyContext"),
aes_encrypt: v.p_AES_Encrypt.expect("freebl: AES_Encrypt"),
aes_aead: v.p_AES_AEAD.expect("freebl: AES_AEAD"),
chacha_create: v
.p_ChaCha20Poly1305_CreateContext
.expect("freebl: ChaCha20Poly1305_CreateContext"),
chacha_destroy: v
.p_ChaCha20Poly1305_DestroyContext
.expect("freebl: ChaCha20Poly1305_DestroyContext"),
chacha_encrypt: v
.p_ChaCha20Poly1305_Encrypt
.expect("freebl: ChaCha20Poly1305_Encrypt"),
chacha_decrypt: v
.p_ChaCha20Poly1305_Decrypt
.expect("freebl: ChaCha20Poly1305_Decrypt"),
chacha_xor: v.p_ChaCha20_Xor.expect("freebl: ChaCha20_Xor"),
}
})
}
/// Create an `AesCtx` for the given AES `mode` and `encrypt` direction. /// /// `key` supplies both the key bytes and the key length via its slice length. /// The IV is always `null` — ECB needs none, and GCM supplies the IV /// per-operation via the params struct passed to `AES_AEAD`. #[expect(
clippy::redundant_pub_crate,
reason = "pub(crate) signals intent; the module is also pub(crate) which Clippy treats as equivalent"
)] pub(crate) fn aes_context(key: &[u8], mode: c_int, encrypt: bool) -> Res<AesCtx> {
debug_assert!(
key.len() == 16 || key.len() == 32, "AES key must be 16 or 32 bytes, got {}",
key.len()
);
AesCtx::from_ptr(unsafe {
AES_CreateContext(
key.as_ptr(),
std::ptr::null(),
mode,
c_int::from(encrypt),
c_uint::try_from(key.len())?,
AES_BLOCK_SIZE,
)
})
}
/// Returns the encrypt or decrypt function pointer for ChaCha20-Poly1305. /// The caller stores this at construction time to bake in the direction. pubfn chacha20_poly1305_op(mode: Mode) -> ChaChaOpFn { let f = freebl(); match mode {
Mode::Encrypt => f.chacha_encrypt,
Mode::Decrypt => f.chacha_decrypt,
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.