pub struct HybridSigningKey {
pub ed25519_sk: SigningKey,
pub ml_dsa_sk: Box<SigningKey<MlDsa65>>,
}Expand description
Hybrid signing key — Ed25519 + ML-DSA-65 secret material.
On drop, both halves are zeroed: ed25519_dalek::SigningKey carries
its own Drop impl (via the zeroize feature enabled in Cargo.toml),
and ml_dsa::SigningKey<P> implements ZeroizeOnDrop natively. The
pre-Phase-5.1 unsafe ptr::write_volatile wrapper in crypto::keys
is no longer needed and was deleted.
ml_dsa_sk is Box-ed because SigningKey<MlDsa65> internally
embeds the ~4 KiB ExpandedSigningKey by value; placing the whole
thing on the stack at every construction would overflow default
tokio test threads (the from_seed call alone constructs a large
temporary). On the heap it’s a single pointer-sized field at our
level; Box<T: ZeroizeOnDrop> correctly forwards Drop to T.
Fields§
§ed25519_sk: SigningKey§ml_dsa_sk: Box<SigningKey<MlDsa65>>Implementations§
Source§impl HybridSigningKey
impl HybridSigningKey
Sourcepub fn generate() -> (Self, HybridVerifyingKey)
pub fn generate() -> (Self, HybridVerifyingKey)
Generate a fresh hybrid keypair using the OS RNG.
Equivalent to Self::generate_with_provider(&crate::crypto::rng::OsRng).
Preserved as the call-compatible default so the rest of the crate
(and downstream callers) does not change shape.
Sourcepub fn generate_with_provider<R: RngProvider + ?Sized>(
provider: &R,
) -> (Self, HybridVerifyingKey)
pub fn generate_with_provider<R: RngProvider + ?Sized>( provider: &R, ) -> (Self, HybridVerifyingKey)
Generate a fresh hybrid keypair using an arbitrary
RngProvider.
Phase 3.8 demonstration of the RngProvider indirection: this is
the canonical “small, well-bounded crypto entry point that needs
randomness” call site. Embedders that need to drive key generation
from a hardware TRNG (on no_std) or a FIPS-approved DRBG plug in
here without disturbing the default surface.
Internally, 32 bytes are drawn from the provider for each algorithm:
- Ed25519: the 32 bytes are the seed for
SigningKey::from_bytes. - ML-DSA-65: the 32 bytes are the FIPS 204 § Algorithm 1 seed
xipassed toSigningKey::<MlDsa65>::new(&seed)(== KeyInit /from_seed).
Sourcepub fn pairwise_consistency_check(
&self,
verifying_key: &HybridVerifyingKey,
) -> Result<(), HybridSignError>
pub fn pairwise_consistency_check( &self, verifying_key: &HybridVerifyingKey, ) -> Result<(), HybridSignError>
FIPS 140-3 §7.10 pairwise-consistency test for a freshly-generated
long-term hybrid signing identity: sign a fixed message with this
secret key and verify it against verifying_key. Returns Err iff the
keypair cannot validate its own signature — i.e. the RNG or a signing
primitive was faulted and the key must not be used.
Call this only at persisted / long-lived-identity generation sites
(CLI keygen, server identity load-or-create, PhantomListener::bind).
It is deliberately not run inside generate /
generate_with_provider: those mint the
client’s ephemeral per-handshake signing key, and a sign+verify there
would add ~40% to every client handshake. The requirement targets
long-term keypairs, not per-connection ephemerals.
Sourcepub fn sign(&self, message: &[u8]) -> HybridSignature
pub fn sign(&self, message: &[u8]) -> HybridSignature
Sign with both algorithms. Both signatures are returned in the
HybridSignature; verification on the peer side requires both to
be valid.
pub fn verifying_key(&self) -> HybridVerifyingKey
Sourcepub fn to_bytes(&self) -> Vec<u8> ⓘ
pub fn to_bytes(&self) -> Vec<u8> ⓘ
Serialize the signing key as (ed25519_seed[32] || ml_dsa_seed[32]).
ML-DSA-65 is fully derivable from its 32-byte seed (per FIPS 204
KeyGen algorithm) so we store the compact form rather than the
4032-byte expanded representation.
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, HybridSignError>
pub fn from_bytes(bytes: &[u8]) -> Result<Self, HybridSignError>
Restore from to_bytes output.