From 3cb81c84df91e3afecbaa20eff36dd1ce54fa781 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Fri, 3 Apr 2026 11:51:43 -0700 Subject: [PATCH] ml-dsa: make PartialEq for ExpandedSigningKey constant time This depends on https://github.com/RustCrypto/KEMs/pull/285 --- Cargo.lock | 6 ++++-- ml-dsa/Cargo.toml | 2 +- ml-dsa/src/lib.rs | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e4e9cde..33f124df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -715,6 +715,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ + "ctutils", "subtle", "typenum", "zeroize", @@ -868,10 +869,11 @@ dependencies = [ [[package]] name = "module-lattice" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cbdc2ac6ead7d0889a5d96e70b7f11a8812702a100ca7c65c5c71fdf076e182" +checksum = "164eb3faeaecbd14b0b2a917c1b4d0c035097a9c559b0bed85c2cdd032bc8faa" dependencies = [ + "ctutils", "hybrid-array", "num-traits", "zeroize", diff --git a/ml-dsa/Cargo.toml b/ml-dsa/Cargo.toml index 921f1566..b2d755a3 100644 --- a/ml-dsa/Cargo.toml +++ b/ml-dsa/Cargo.toml @@ -34,7 +34,7 @@ pkcs8 = ["dep:const-oid", "dep:pkcs8"] [dependencies] ctutils = { version = "0.4", default-features = false } hybrid-array = { version = "0.4", features = ["extra-sizes"] } -module-lattice = "0.2" +module-lattice = { version = "0.2.1", features = ["ctutils"] } sha3 = { version = "0.11", default-features = false } signature = { version = "3.0.0-rc.10", default-features = false, features = ["digest"] } diff --git a/ml-dsa/src/lib.rs b/ml-dsa/src/lib.rs index 8f553717..3bf476b8 100644 --- a/ml-dsa/src/lib.rs +++ b/ml-dsa/src/lib.rs @@ -58,6 +58,7 @@ use crate::param::{ParameterSet, QMinus1, SamplingSize, SpecQ}; use crate::sampling::{expand_a, expand_mask, expand_s, sample_in_ball}; use core::convert::{TryFrom, TryInto}; use core::fmt; +use ctutils::{Choice, CtEq}; use hybrid_array::{ Array, typenum::{ @@ -187,7 +188,7 @@ impl AsMut for MuBuilder { } /// An ML-DSA signing key initialized through a seed -#[derive(Clone, PartialEq)] +#[derive(Clone)] pub struct SigningKey { /// The signing key of the key pair signing_key: ExpandedSigningKey

, @@ -254,8 +255,22 @@ impl DigestSigner> for SigningKey

{ } } +impl PartialEq for SigningKey

{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl CtEq for SigningKey

{ + fn ct_eq(&self, other: &Self) -> Choice { + self.signing_key + .ct_eq(&other.signing_key) + .and(self.seed.ct_eq(&other.seed)) + } +} + /// An ML-DSA signing key -#[derive(Clone, PartialEq)] +#[derive(Clone)] pub struct ExpandedSigningKey { rho: B32, K: B32, @@ -661,6 +676,24 @@ impl RandomizedDigestSigner> for Expanded } } +impl PartialEq for ExpandedSigningKey

{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl CtEq for ExpandedSigningKey

{ + fn ct_eq(&self, other: &Self) -> Choice { + self.rho + .ct_eq(&other.rho) + .and(self.K.ct_eq(&other.K)) + .and(self.tr.ct_eq(&other.tr)) + .and(self.s1.ct_eq(&other.s1)) + .and(self.s2.ct_eq(&other.s2)) + .and(self.t0.ct_eq(&other.t0)) + } +} + /// An ML-DSA verification key #[derive(Clone, Debug, PartialEq)] pub struct VerifyingKey {