mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
ml-dsa: rename and deprecate ExpandedSigningKey (#1145)
Renames `EncodedSigningKey` to `ExpandedSigningKey` to contrast it with seeds, the preferred API. Also renames the `decode` and `encode` functions to `from_expanded` and `to_expanded`, similar to the changes made to `ml-kem`'s `DecapsulationKey` in RustCrypto/KEMs#163, which also deprecated these APIs. We don't properly implement validation of such keys which can lead to panics in the event they were improperly or maliciously generated (#1133), a problem avoided by using seeds. For now, this merely documents the panic condition. Most implementers have opted not to provide support for this key format due to these problems (it's also actually more expensive to validate an expanded key than it is to use a seed), and also where seeds are the same size regardless of security level, the expanded keys vary in size.
This commit is contained in:
@@ -9,6 +9,7 @@ pub fn rand<L: ArraySize, R: CryptoRng + ?Sized>(rng: &mut R) -> Array<u8, L> {
|
||||
val
|
||||
}
|
||||
|
||||
#[allow(deprecated)] // TODO(tarcieri): stop using expanded signing keys
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut rng = getrandom::SysRng.unwrap_err();
|
||||
let xi: B32 = rand(&mut rng);
|
||||
@@ -20,7 +21,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let vk = kp.verifying_key();
|
||||
let sig = sk.sign_deterministic(&m, &ctx).unwrap();
|
||||
|
||||
let sk_bytes = sk.encode();
|
||||
let sk_bytes = sk.to_expanded();
|
||||
let vk_bytes = vk.encode();
|
||||
let sig_bytes = sig.encode();
|
||||
|
||||
@@ -28,7 +29,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("keygen", |b| {
|
||||
b.iter(|| {
|
||||
let kp = MlDsa65::from_seed(&xi);
|
||||
let _sk_bytes = kp.signing_key().encode();
|
||||
let _sk_bytes = kp.signing_key().to_expanded();
|
||||
let _vk_bytes = kp.verifying_key().encode();
|
||||
})
|
||||
});
|
||||
@@ -36,7 +37,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Signing
|
||||
c.bench_function("sign", |b| {
|
||||
b.iter(|| {
|
||||
let sk = SigningKey::<MlDsa65>::decode(&sk_bytes);
|
||||
let sk = SigningKey::<MlDsa65>::from_expanded(&sk_bytes);
|
||||
let _sig = sk.sign_deterministic(&m, &ctx);
|
||||
})
|
||||
});
|
||||
|
||||
+60
-43
@@ -78,7 +78,7 @@ use crate::sampling::{expand_a, expand_mask, expand_s, sample_in_ball};
|
||||
use crate::util::B64;
|
||||
use core::fmt;
|
||||
|
||||
pub use crate::param::{EncodedSignature, EncodedSigningKey, EncodedVerifyingKey, MlDsaParams};
|
||||
pub use crate::param::{EncodedSignature, EncodedVerifyingKey, ExpandedSigningKey, MlDsaParams};
|
||||
pub use crate::util::B32;
|
||||
pub use signature::{self, Error};
|
||||
|
||||
@@ -488,28 +488,37 @@ impl<P: MlDsaParams> SigningKey<P> {
|
||||
Ok(self.sign_mu_deterministic(&mu))
|
||||
}
|
||||
|
||||
/// Encode the key in a fixed-size byte array.
|
||||
// Algorithm 24 skEncode
|
||||
pub fn encode(&self) -> EncodedSigningKey<P>
|
||||
where
|
||||
P: MlDsaParams,
|
||||
{
|
||||
let s1_enc = P::encode_s1(&self.s1);
|
||||
let s2_enc = P::encode_s2(&self.s2);
|
||||
let t0_enc = P::encode_t0(&self.t0);
|
||||
P::concat_sk(
|
||||
self.rho.clone(),
|
||||
self.K.clone(),
|
||||
self.tr.clone(),
|
||||
s1_enc,
|
||||
s2_enc,
|
||||
t0_enc,
|
||||
)
|
||||
/// This auxiliary function derives a `VerifyingKey` from a bare
|
||||
/// `SigningKey` (even in the absence of the original seed).
|
||||
///
|
||||
/// This is a utility function that is useful when importing the private key
|
||||
/// from an external source which does not export the seed and does not
|
||||
/// provide the precomputed public key associated with the private key
|
||||
/// itself.
|
||||
///
|
||||
/// `SigningKey` implements `signature::Keypair`: this inherent method is
|
||||
/// retained for convenience, so it is available for callers even when the
|
||||
/// `signature::Keypair` trait is out-of-scope.
|
||||
pub fn verifying_key(&self) -> VerifyingKey<P> {
|
||||
let kp: &dyn signature::Keypair<VerifyingKey = VerifyingKey<P>> = self;
|
||||
kp.verifying_key()
|
||||
}
|
||||
|
||||
/// Decode the key from an appropriately sized byte array.
|
||||
/// DEPRECATED: decode the key from an appropriately sized byte array.
|
||||
///
|
||||
/// Note that this form is deprecated in practice; prefer to use [`SigningKey::from_seed`].
|
||||
///
|
||||
/// <div class="warning">
|
||||
/// <b>Panics</b>
|
||||
///
|
||||
/// This API does not validate expanded signing keys and can potentially panic if keys are
|
||||
/// malformed or maliciously generated.
|
||||
///
|
||||
/// To avoid panics, use [`SigningKey::from_seed`] instead.
|
||||
/// </div>
|
||||
// Algorithm 25 skDecode
|
||||
pub fn decode(enc: &EncodedSigningKey<P>) -> Self
|
||||
#[deprecated(since = "0.1.0", note = "use `SigningKey::from_seed` instead")]
|
||||
pub fn from_expanded(enc: &ExpandedSigningKey<P>) -> Self
|
||||
where
|
||||
P: MlDsaParams,
|
||||
{
|
||||
@@ -525,21 +534,26 @@ impl<P: MlDsaParams> SigningKey<P> {
|
||||
)
|
||||
}
|
||||
|
||||
/// This auxiliary function derives a `VerifyingKey` from a bare
|
||||
/// `SigningKey` (even in the absence of the original seed).
|
||||
/// DEPRECATED: encode the key in a fixed-size byte array.
|
||||
///
|
||||
/// This is a utility function that is useful when importing the private key
|
||||
/// from an external source which does not export the seed and does not
|
||||
/// provide the precomputed public key associated with the private key
|
||||
/// itself.
|
||||
///
|
||||
/// `SigningKey` implements `signature::Keypair`: this inherent method is
|
||||
/// retained for convenience, so it is available for callers even when the
|
||||
/// `signature::Keypair` trait is out-of-scope.
|
||||
pub fn verifying_key(&self) -> VerifyingKey<P> {
|
||||
let kp: &dyn signature::Keypair<VerifyingKey = VerifyingKey<P>> = self;
|
||||
|
||||
kp.verifying_key()
|
||||
/// Note that this form is deprecated in practice; prefer to use [`KeyPair::to_seed`].
|
||||
// Algorithm 24 skEncode
|
||||
#[deprecated(since = "0.1.0", note = "use `KeyPair::to_seed` instead")]
|
||||
pub fn to_expanded(&self) -> ExpandedSigningKey<P>
|
||||
where
|
||||
P: MlDsaParams,
|
||||
{
|
||||
let s1_enc = P::encode_s1(&self.s1);
|
||||
let s2_enc = P::encode_s2(&self.s2);
|
||||
let t0_enc = P::encode_t0(&self.t0);
|
||||
P::concat_sk(
|
||||
self.rho.clone(),
|
||||
self.K.clone(),
|
||||
self.tr.clone(),
|
||||
s1_enc,
|
||||
s2_enc,
|
||||
t0_enc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,16 +980,19 @@ mod test {
|
||||
let vk2 = VerifyingKey::<P>::decode(&vk_bytes);
|
||||
assert!(vk == vk2);
|
||||
|
||||
let sk_bytes = sk.encode();
|
||||
let sk2 = SigningKey::<P>::decode(&sk_bytes);
|
||||
assert!(sk == sk2);
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
let sk_bytes = sk.to_expanded();
|
||||
let sk2 = SigningKey::<P>::from_expanded(&sk_bytes);
|
||||
assert!(sk == sk2);
|
||||
|
||||
let M = b"Hello world";
|
||||
let rnd = Array([0u8; 32]);
|
||||
let sig = sk.sign_internal(&[M], &rnd);
|
||||
let sig_bytes = sig.encode();
|
||||
let sig2 = Signature::<P>::decode(&sig_bytes).unwrap();
|
||||
assert!(sig == sig2);
|
||||
let M = b"Hello world";
|
||||
let rnd = Array([0u8; 32]);
|
||||
let sig = sk.sign_internal(&[M], &rnd);
|
||||
let sig_bytes = sig.encode();
|
||||
let sig2 = Signature::<P>::decode(&sig_bytes).unwrap();
|
||||
assert!(sig == sig2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+5
-5
@@ -137,9 +137,9 @@ pub trait SigningKeyParams: ParameterSet {
|
||||
s1: EncodedS1<Self>,
|
||||
s2: EncodedS2<Self>,
|
||||
t0: EncodedT0<Self>,
|
||||
) -> EncodedSigningKey<Self>;
|
||||
) -> ExpandedSigningKey<Self>;
|
||||
fn split_sk(
|
||||
enc: &EncodedSigningKey<Self>,
|
||||
enc: &ExpandedSigningKey<Self>,
|
||||
) -> (
|
||||
&B32,
|
||||
&B32,
|
||||
@@ -157,7 +157,7 @@ pub(crate) type EncodedT0<P> = Array<u8, <P as SigningKeyParams>::T0Size>;
|
||||
pub(crate) type SigningKeySize<P> = <P as SigningKeyParams>::SigningKeySize;
|
||||
|
||||
/// A signing key encoded as a byte array
|
||||
pub type EncodedSigningKey<P> = Array<u8, SigningKeySize<P>>;
|
||||
pub type ExpandedSigningKey<P> = Array<u8, SigningKeySize<P>>;
|
||||
|
||||
impl<P> SigningKeyParams for P
|
||||
where
|
||||
@@ -250,12 +250,12 @@ where
|
||||
s1: EncodedS1<Self>,
|
||||
s2: EncodedS2<Self>,
|
||||
t0: EncodedT0<Self>,
|
||||
) -> EncodedSigningKey<Self> {
|
||||
) -> ExpandedSigningKey<Self> {
|
||||
rho.concat(K).concat(tr).concat(s1).concat(s2).concat(t0)
|
||||
}
|
||||
|
||||
fn split_sk(
|
||||
enc: &EncodedSigningKey<Self>,
|
||||
enc: &ExpandedSigningKey<Self>,
|
||||
) -> (
|
||||
&B32,
|
||||
&B32,
|
||||
|
||||
@@ -29,19 +29,21 @@ fn verify<P: MlDsaParams>(tc: &acvp::TestCase) {
|
||||
// Import test data into the relevant array structures
|
||||
let seed = Array::try_from(tc.seed.as_slice()).unwrap();
|
||||
let vk_bytes = EncodedVerifyingKey::<P>::try_from(tc.pk.as_slice()).unwrap();
|
||||
let sk_bytes = EncodedSigningKey::<P>::try_from(tc.sk.as_slice()).unwrap();
|
||||
let sk_bytes = ExpandedSigningKey::<P>::try_from(tc.sk.as_slice()).unwrap();
|
||||
|
||||
let kp = P::from_seed(&seed);
|
||||
let sk = kp.signing_key().clone();
|
||||
let vk = kp.verifying_key().clone();
|
||||
|
||||
// Verify correctness via serialization
|
||||
assert_eq!(sk.encode(), sk_bytes);
|
||||
assert_eq!(vk.encode(), vk_bytes);
|
||||
assert!(vk == VerifyingKey::<P>::decode(&vk_bytes));
|
||||
|
||||
// Verify correctness via deserialization
|
||||
assert!(sk == SigningKey::<P>::decode(&sk_bytes));
|
||||
assert!(vk == VerifyingKey::<P>::decode(&vk_bytes));
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert_eq!(sk.to_expanded(), sk_bytes);
|
||||
assert!(sk == SigningKey::<P>::from_expanded(&sk_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
mod acvp {
|
||||
|
||||
@@ -26,8 +26,10 @@ fn acvp_sig_gen() {
|
||||
|
||||
fn verify<P: MlDsaParams>(tc: &acvp::TestCase, deterministic: bool) {
|
||||
// Import the signing key
|
||||
let sk_bytes = EncodedSigningKey::<P>::try_from(tc.sk.as_slice()).unwrap();
|
||||
let sk = SigningKey::<P>::decode(&sk_bytes);
|
||||
let sk_bytes = ExpandedSigningKey::<P>::try_from(tc.sk.as_slice()).unwrap();
|
||||
|
||||
#[allow(deprecated)]
|
||||
let sk = SigningKey::<P>::from_expanded(&sk_bytes);
|
||||
|
||||
// Verify correctness
|
||||
let rnd = if deterministic {
|
||||
|
||||
Reference in New Issue
Block a user