Skip to content

Conversation

@jschneider-bensch
Copy link
Collaborator

@jschneider-bensch jschneider-bensch commented Nov 19, 2025

This PR adds

  • signature based authentication for the initiator
    • to enable this, I've added support for (de-)serialization of key and signature structs using tls_codec to ML-DSA
    • the initiator authenticator, be it a long-term DH public key or signature verification key can be retrieved from the Responder once the first initiator message was processed for out-of-band verification. I've added a function Responder::abort_handshake to reset the responder if this verification fails.
  • AES-GCM 128 support for the AEAD
  • Documentation on the different protocol modes in README.md
  • I've added a way to export secrets for a given context from the Session. This will export a secret derived as HKDF-SHA256(K_session, context || "PSQ secret export") where context can be provided by the application.

TODO:

  • Update changelog

@jschneider-bensch jschneider-bensch requested a review from a team as a code owner November 19, 2025 14:45
@jschneider-bensch jschneider-bensch linked an issue Nov 19, 2025 that may be closed by this pull request
2 tasks
@jschneider-bensch jschneider-bensch requested review from franziskuskiefer and karthikbhargavan and removed request for keks November 19, 2025 14:46
@jschneider-bensch jschneider-bensch requested a review from a team as a code owner November 25, 2025 16:05
@jschneider-bensch jschneider-bensch marked this pull request as draft November 27, 2025 09:54
@franziskuskiefer franziskuskiefer linked an issue Dec 1, 2025 that may be closed by this pull request
@jschneider-bensch jschneider-bensch linked an issue Dec 1, 2025 that may be closed by this pull request
@jschneider-bensch jschneider-bensch marked this pull request as ready for review December 1, 2025 16:06
@jschneider-bensch
Copy link
Collaborator Author

Looks like the macOS ASAN nightly has gone a bit stale. Attempting to unpin that with #1258.

Copy link
Member

@franziskuskiefer franziskuskiefer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't go too deep yet. But this looks good in general.

There are a bunch of public items without docs. But also no warnings. So they may not be exposed.

}

/// Provide a principal's long-term ML-DSA signing key.
pub fn longterm_mldsa_signing_key(mut self, signing_key: &'a MLDSA65SigningKey) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have ecdh, mlkem, mldsa, ed25519. The ecdh is the only generic one. Is that on purpose?

pq_shared_secret,
};

// XXX: This makes clippy unhappy, but is a lifetime error for feature `classic-mceliece` if we return directlyr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disable the lint here then.

Suggested change
// XXX: This makes clippy unhappy, but is a lifetime error for feature `classic-mceliece` if we return directlyr
// XXX: This makes clippy unhappy, but is a lifetime error for feature `classic-mceliece` if we return directly

pub fn deserialize(
bytes: &[u8],
initiator_ecdh_pk: &DHPublicKey,
initiator_authenticator: &Authenticator,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Into directly here doesn't work. But the into on the outside is a little ugly. You could add an authenticator function to the DHKeyPair that does it.

))
}
}
#[cfg(not(feature = "classic-mceliece"))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to duplicate all of these matches? Couldn't you just add the additional bits with the feature flag?

| CiphersuiteName::X25519_MLKEM768_X25519_CHACHA20POLY1305_HKDFSHA256
| CiphersuiteName::X25519_NONE_X25519_AESGCM128_HKDFSHA256
| CiphersuiteName::X25519_MLKEM768_X25519_AESGCM128_HKDFSHA256 => {
Authenticator::Dh(self.initiator_ecdh_keys.pk.clone())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could just be that authenticator function on the dh key pair.

let pq_encapsulation_deserialized =
working_ciphersuite.deserialize_encapsulation(pq_encapsulation.as_ref())?;

let tx1 = tx1_dh(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not move the match into the tx1 and derive_k1 functions? Then everything else appears to be the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PSQ] Client-authenticating handshake [PSQ] Implement AES ciphersuites [PSQ] Document v2

2 participants