#![allow(missing_docs)]
#[derive(Debug, PartialEq)]
pub enum HandshakeError {
Serialize(tls_codec::Error),
Deserialize(tls_codec::Error),
CryptoError,
InitiatorState,
ResponderState,
TransportState,
OutputBufferShort,
PayloadTooLong,
ChannelError,
UnsupportedCiphersuite,
RateLimit,
Storage,
OtherError,
IdentifierMismatch,
InvalidMessage,
InvalidDHSecret,
}
impl From<libcrux_ed25519::Error> for HandshakeError {
fn from(_value: libcrux_ed25519::Error) -> Self {
Self::CryptoError
}
}
impl From<libcrux_ml_dsa::SigningError> for HandshakeError {
fn from(_value: libcrux_ml_dsa::SigningError) -> Self {
Self::CryptoError
}
}
impl From<libcrux_ml_dsa::VerificationError> for HandshakeError {
fn from(_value: libcrux_ml_dsa::VerificationError) -> Self {
Self::CryptoError
}
}
impl From<AEADError> for HandshakeError {
fn from(value: AEADError) -> Self {
match value {
AEADError::CryptoError => HandshakeError::CryptoError,
AEADError::Serialize(error) => HandshakeError::Serialize(error),
AEADError::Deserialize(error) => HandshakeError::Deserialize(error),
AEADError::KeyExpired => unreachable!("Attempt to re-use an expired key during handshake. This indicates a fatal bug in the handshake protocol, please submit a bug report at https://github.com/cryspen/libcrux/."), }
}
}
use std::ops::Deref;
use tls_codec::{TlsDeserialize, TlsSerialize, TlsSerializeBytes, TlsSize, VLByteSlice, VLBytes};
use transcript::Transcript;
use crate::{
aead::{AEADError, AEADKeyNonce, AeadType},
handshake::{
ciphersuite::{types::PQSharedSecret, CiphersuiteName},
dhkem::{DHPrivateKey, DHPublicKey, DHSharedSecret},
types::{Authenticator, ProvideAuthenticator, Signature, SignatureVerificationKey},
},
};
pub(crate) mod dhkem;
pub(crate) mod initiator;
pub(crate) mod responder;
pub(crate) mod transcript;
pub(crate) mod builder;
pub(crate) mod ciphersuite;
pub(crate) struct ToTransportState {
pub(crate) tx2: Transcript,
pub(crate) k2: AEADKeyNonce,
pub(crate) initiator_authenticator: Option<Authenticator>,
pub(crate) pq: bool,
}
#[derive(TlsDeserialize, TlsSize)]
pub struct HandshakeMessage {
pk: DHPublicKey,
ciphertext: Vec<u8>,
tag: [u8; 16],
aad: Vec<u8>,
ciphersuite: CiphersuiteName,
}
#[derive(TlsDeserialize, TlsSize)]
#[repr(u8)]
pub(crate) enum AuthMessage {
Dh(DHPublicKey),
Sig {
vk: Box<SignatureVerificationKey>,
signature: Box<Signature>,
},
}
impl From<&AuthMessage> for Authenticator {
fn from(value: &AuthMessage) -> Self {
match value {
AuthMessage::Dh(dhpublic_key) => dhpublic_key.authenticator(),
AuthMessage::Sig { vk, signature: _ } => vk.deref().authenticator(),
}
}
}
#[derive(TlsDeserialize, TlsSize)]
pub(crate) struct InnerMessage {
ciphertext: VLBytes,
tag: [u8; 16],
aad: VLBytes,
pq_encapsulation: VLBytes,
auth: AuthMessage,
}
#[derive(TlsSerialize, TlsSize)]
#[repr(u8)]
pub(crate) enum AuthMessageOut<'a> {
Dh(&'a DHPublicKey),
Sig {
vk: &'a SignatureVerificationKey,
signature: &'a Signature,
},
}
#[derive(TlsSerialize, TlsSize)]
pub(crate) struct InnerMessageOut<'a> {
ciphertext: VLByteSlice<'a>,
tag: [u8; 16], aad: VLByteSlice<'a>,
pq_encapsulation: VLByteSlice<'a>,
auth: AuthMessageOut<'a>,
}
#[derive(TlsSerialize, TlsSize)]
pub(crate) struct HandshakeMessageOut<'a> {
pk: &'a DHPublicKey,
ciphertext: VLByteSlice<'a>,
tag: [u8; 16], aad: VLByteSlice<'a>,
ciphersuite: CiphersuiteName,
}
pub(crate) fn write_output(payload: &[u8], out: &mut [u8]) -> Result<usize, HandshakeError> {
let payload_len = payload.len();
if out.len() < payload_len {
return Err(HandshakeError::OutputBufferShort);
}
out[..payload_len].copy_from_slice(payload);
Ok(payload_len)
}
pub(super) fn derive_k0(
peer_pk: &DHPublicKey,
own_pk: &DHPublicKey,
own_sk: &DHPrivateKey,
ctx: &[u8],
is_responder: bool,
aead_type: AeadType,
) -> Result<(Transcript, AEADKeyNonce), HandshakeError> {
#[derive(TlsSerializeBytes, TlsSize)]
struct K0Ikm<'a> {
g_xs: &'a DHSharedSecret,
}
let tx0 = if is_responder {
transcript::tx0(ctx, own_pk, peer_pk)?
} else {
transcript::tx0(ctx, peer_pk, own_pk)?
};
let ikm = K0Ikm {
g_xs: &DHSharedSecret::derive(own_sk, peer_pk)?,
};
Ok((tx0, AEADKeyNonce::new(&ikm, &tx0, aead_type)?))
}
pub(super) fn derive_k1_dh(
k0: &AEADKeyNonce,
own_longterm_key: &DHPrivateKey,
peer_longterm_pk: &DHPublicKey,
pq_shared_secret: Option<PQSharedSecret>,
tx1: &Transcript,
aead_type: AeadType,
) -> Result<AEADKeyNonce, HandshakeError> {
#[derive(TlsSerializeBytes, TlsSize)]
struct K1IkmDh<'a> {
k0: &'a AEADKeyNonce,
ecdh_shared_secret: &'a DHSharedSecret,
pq_shared_secret: Option<PQSharedSecret>,
}
let ecdh_shared_secret = DHSharedSecret::derive(own_longterm_key, peer_longterm_pk)?;
let ikm = K1IkmDh {
k0,
ecdh_shared_secret: &ecdh_shared_secret,
pq_shared_secret,
};
Ok(AEADKeyNonce::new(&ikm, &tx1, aead_type)?)
}
pub(super) fn derive_k1_sig(
k0: &AEADKeyNonce,
pq_shared_secret: Option<PQSharedSecret>,
tx1: &Transcript,
signature: &Signature,
aead_type: AeadType,
) -> Result<AEADKeyNonce, HandshakeError> {
#[derive(TlsSerializeBytes, TlsSize)]
struct K1IkmSig<'a> {
k0: &'a AEADKeyNonce,
pq_shared_secret: Option<PQSharedSecret>,
}
#[derive(TlsSerializeBytes, TlsSize)]
struct K1InfoSig<'a> {
tx1: &'a Transcript,
signature_vec: Vec<u8>,
}
let signature_vec = match signature {
Signature::Ed25519(sig) => sig.to_vec(),
Signature::MlDsa65(mldsasignature) => mldsasignature.deref().as_ref().to_vec(),
};
Ok(AEADKeyNonce::new(
&K1IkmSig {
k0,
pq_shared_secret,
},
&K1InfoSig { tx1, signature_vec },
aead_type,
)?)
}
#[derive(TlsSerializeBytes, TlsSize)]
struct K2IkmQuery<'a> {
k0: &'a AEADKeyNonce,
g_xs: &'a DHSharedSecret,
g_xy: &'a DHSharedSecret,
}
#[derive(TlsSerializeBytes, TlsSize)]
struct K2IkmRegistrationDh<'a, 'b> {
k1: &'a AEADKeyNonce,
g_cy: &'b DHSharedSecret,
g_xy: &'b DHSharedSecret,
}
#[derive(TlsSerializeBytes, TlsSize)]
struct K2IkmRegistrationSig<'a, 'b> {
k1: &'a AEADKeyNonce,
g_xy: &'b DHSharedSecret,
}
pub mod builders {
#[derive(Debug, PartialEq)]
pub enum BuilderError {
CiphersuiteBuilderState,
PrincipalBuilderState,
UnsupportedCiphersuite,
}
#[doc(inline)]
pub use crate::handshake::builder::PrincipalBuilder;
#[doc(inline)]
pub use crate::handshake::ciphersuite::builder::CiphersuiteBuilder;
#[doc(inline)]
pub use crate::handshake::ciphersuite::initiator::InitiatorCiphersuite;
#[doc(inline)]
pub use crate::handshake::ciphersuite::responder::ResponderCiphersuite;
}
pub mod types {
#[doc(inline)]
pub use crate::handshake::ciphersuite::initiator::SigningKeyPair;
#[doc(inline)]
pub use crate::handshake::ciphersuite::types::*;
#[doc(inline)]
pub use crate::handshake::dhkem::{DHKeyPair, DHPrivateKey, DHPublicKey};
}
pub mod ciphersuites {
#[doc(inline)]
pub use crate::handshake::ciphersuite::CiphersuiteName;
}
#[doc(inline)]
pub use initiator::query::QueryInitiator;
#[doc(inline)]
pub use initiator::registration::RegistrationInitiator;
#[doc(inline)]
pub use responder::Responder;