// Blapi backend for RecordProtection: calls freebl AES-GCM and ChaCha20-Poly1305 // primitives directly, bypassing the PKCS#11 session layer. // // NOTE: this bypasses softoken's FIPS power-on self-test gate. Intentional for // neqo (non-FIPS) in exchange for lower per-packet overhead.
// Compile-time C-type conversions for the constants used in every freebl call. #[expect(
clippy::cast_possible_truncation,
reason = "NONCE_LEN = 12 and TAG_LEN = 16 both fit in u32"
)] const NONCE_LEN_C: c_uint = NONCE_LEN as c_uint; #[expect(
clippy::cast_possible_truncation,
reason = "NONCE_LEN = 12 and TAG_LEN = 16 both fit in u32"
)] const TAG_LEN_C: c_uint = TAG_LEN as c_uint; // On Windows c_ulong is u32; on other platforms it is u64, so // cast_possible_truncation only fires on Windows. #[cfg_attr(
target_os = "windows",
expect(
clippy::cast_possible_truncation,
reason = "NONCE_LEN = 12 fits in u32"
)
)] const NONCE_LEN_UL: c_ulong = NONCE_LEN as c_ulong; #[cfg_attr(
target_os = "windows",
expect(
clippy::cast_possible_truncation,
reason = "TAG_LEN * 8 = 128 fits in u32"
)
)] const TAG_BITS_UL: c_ulong = (TAG_LEN * 8) as c_ulong; #[expect(
clippy::cast_possible_truncation,
reason = "CK_GCM_MESSAGE_PARAMS is a small fixed struct"
)] const GCM_PARAMS_LEN_C: c_uint = size_of::<freebl::CK_GCM_MESSAGE_PARAMS>() as c_uint;
enum RecordCipher { // AES-GCM bakes direction into the context at creation time via the // `encrypt` parameter to `AES_CreateContext`.
Aes(AesCtx), // ChaCha20-Poly1305 bakes direction into the function pointer at // construction time; the context itself is direction-agnostic.
ChaCha(ChaCha20Ctx, ChaChaOpFn),
}
/// Dispatch an AEAD operation to the appropriate freebl primitive. /// /// # Safety /// /// `output`, `tag`, and `input` must be valid for `output_max`, `TAG_LEN`, /// and `input_len` bytes respectively. `output` and `input` may overlap /// (in-place); `tag` must not overlap the `output` region. #[expect(
clippy::too_many_arguments,
reason = "Thin wrapper over two 10-argument C functions."
)] unsafefn aead_op(
cipher: &RecordCipher,
nonce: &[u8; NONCE_LEN],
aad: &[u8],
output: *mut u8,
output_max: c_uint,
tag: *mut u8,
input: *const u8,
input_len: c_uint,
) -> Res<usize> { letmut out_len: c_uint = 0; let aad_len = c_uint::try_from(aad.len())?; match cipher {
RecordCipher::Aes(ctx) => { letmut params = freebl::CK_GCM_MESSAGE_PARAMS {
pIv: nonce.as_ptr().cast_mut(), // NSS only reads pIv for CKG_NO_GENERATE
ulIvLen: NONCE_LEN_UL,
ulIvFixedBits: 0,
ivGenerator: 0,
pTag: tag,
ulTagBits: TAG_BITS_UL,
};
secstatus_to_res(unsafe {
freebl::AES_AEAD(
**ctx,
output,
&raw mut out_len,
output_max,
input,
input_len,
(&raw mut params).cast(),
GCM_PARAMS_LEN_C,
aad.as_ptr(),
aad_len,
)
})?;
}
RecordCipher::ChaCha(ctx, f) => {
secstatus_to_res(unsafe {
f(
**ctx,
output,
&raw mut out_len,
output_max,
input,
input_len,
nonce.as_ptr(),
NONCE_LEN_C,
aad.as_ptr(),
aad_len,
tag,
)
})?;
}
}
Ok(usize::try_from(out_len)?)
}
// blapi holds nonce_base outside NSS-managed memory, so zero it on drop; // the PKCS#11 backend relies on the SymKey lifecycle for this instead. #[derive(ZeroizeOnDrop)] pubstruct RecordProtection { #[zeroize(skip)]
cipher: RecordCipher,
nonce_base: [u8; NONCE_LEN],
}
impl RecordProtection { /// Create a new AEAD instance for the given direction. /// /// # Errors /// /// Returns `Error` when the underlying crypto operations fail. pubfn new(
version: Version,
cipher: Cipher,
secret: &SymKey,
prefix: &str,
mode: Mode,
) -> Res<Self> { // Moves into RecordProtection (ZeroizeOnDrop), no Zeroizing needed. let nonce_base: [u8; NONCE_LEN] =
expand_label_buf(version, cipher, secret, &format!("{prefix}iv"))?; let key_label = format!("{prefix}key");
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.