Skip to content

Commit e7c698a

Browse files
authored
ml-dsa: support for encoding PKCS#8 private keys (#892)
1 parent f7e7312 commit e7c698a

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

ml-dsa/src/lib.rs

+33-7
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818
//!
1919
//! ```
2020
//! use ml_dsa::{MlDsa65, KeyGen};
21-
//! use signature::{Signer, Verifier};
21+
//! use signature::{Keypair, Signer, Verifier};
2222
//!
2323
//! let mut rng = rand::thread_rng();
2424
//! let kp = MlDsa65::key_gen(&mut rng);
2525
//!
2626
//! let msg = b"Hello world";
27-
//! let sig = kp.signing_key.sign(msg);
27+
//! let sig = kp.signing_key().sign(msg);
2828
//!
29-
//! assert!(kp.verifying_key.verify(msg, &sig).is_ok());
29+
//! assert!(kp.verifying_key().verify(msg, &sig).is_ok());
3030
//! ```
3131
3232
mod algebra;
@@ -71,9 +71,9 @@ use {
7171

7272
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
7373
use pkcs8::{
74-
der::asn1::{BitString, BitStringRef},
74+
der::asn1::{BitString, BitStringRef, OctetStringRef},
7575
spki::{SignatureBitStringEncoding, SubjectPublicKeyInfo},
76-
EncodePublicKey,
76+
EncodePrivateKey, EncodePublicKey,
7777
};
7878

7979
use crate::algebra::{AlgebraExt, Elem, NttMatrix, NttVector, Truncate, Vector};
@@ -178,10 +178,20 @@ fn message_representative(tr: &[u8], Mp: &[&[u8]]) -> B64 {
178178
/// An ML-DSA key pair
179179
pub struct KeyPair<P: MlDsaParams> {
180180
/// The signing key of the key pair
181-
pub signing_key: SigningKey<P>,
181+
signing_key: SigningKey<P>,
182182

183183
/// The verifying key of the key pair
184-
pub verifying_key: VerifyingKey<P>,
184+
verifying_key: VerifyingKey<P>,
185+
186+
/// The seed this signing key was derived from
187+
seed: B32,
188+
}
189+
190+
impl<P: MlDsaParams> KeyPair<P> {
191+
/// The signing key of the key pair
192+
pub fn signing_key(&self) -> &SigningKey<P> {
193+
&self.signing_key
194+
}
185195
}
186196

187197
impl<P: MlDsaParams> AsRef<VerifyingKey<P>> for KeyPair<P> {
@@ -234,6 +244,21 @@ where
234244
Signature::<P>::ALGORITHM_IDENTIFIER;
235245
}
236246

247+
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
248+
impl<P> EncodePrivateKey for KeyPair<P>
249+
where
250+
P: MlDsaParams,
251+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
252+
{
253+
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
254+
let pkcs8_key = pkcs8::PrivateKeyInfoRef::new(
255+
P::ALGORITHM_IDENTIFIER,
256+
OctetStringRef::new(&self.seed)?,
257+
);
258+
Ok(der::SecretDocument::encode_msg(&pkcs8_key)?)
259+
}
260+
}
261+
237262
/// An ML-DSA signing key
238263
#[derive(Clone, PartialEq)]
239264
pub struct SigningKey<P: MlDsaParams> {
@@ -793,6 +818,7 @@ where
793818
KeyPair {
794819
signing_key,
795820
verifying_key,
821+
seed: xi.clone(),
796822
}
797823
}
798824
}

ml-dsa/tests/key-gen.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use ml_dsa::*;
22

33
use hybrid_array::Array;
4+
use signature::Keypair;
45
use std::{fs::read_to_string, path::PathBuf};
56

67
#[test]
@@ -32,8 +33,8 @@ fn verify<P: MlDsaParams>(tc: &acvp::TestCase) {
3233
let sk_bytes = EncodedSigningKey::<P>::try_from(tc.sk.as_slice()).unwrap();
3334

3435
let kp = P::key_gen_internal(&seed);
35-
let sk = kp.signing_key;
36-
let vk = kp.verifying_key;
36+
let sk = kp.signing_key().clone();
37+
let vk = kp.verifying_key().clone();
3738

3839
// Verify correctness via serialization
3940
assert_eq!(sk.encode(), sk_bytes);

ml-dsa/tests/pkcs8.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ml_dsa::{KeyPair, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SigningKey, Verify
55
use pkcs8::{
66
der::{pem::LineEnding, AnyRef},
77
spki::AssociatedAlgorithmIdentifier,
8-
DecodePrivateKey, DecodePublicKey, EncodePublicKey,
8+
DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey,
99
};
1010
use signature::Keypair;
1111

@@ -18,7 +18,13 @@ fn private_key_serialization() {
1818
{
1919
let sk = SigningKey::<P>::from_pkcs8_pem(private_bytes).expect("parse private key");
2020
let kp = KeyPair::<P>::from_pkcs8_pem(private_bytes).expect("parse private key");
21-
assert!(sk == kp.signing_key);
21+
assert!(sk == *kp.signing_key());
22+
assert_eq!(
23+
kp.to_pkcs8_pem(LineEnding::LF)
24+
.expect("serialize private seed")
25+
.deref(),
26+
private_bytes
27+
);
2228

2329
let pk = VerifyingKey::<P>::from_public_key_pem(public_bytes).expect("parse public key");
2430
assert_eq!(

0 commit comments

Comments
 (0)