Skip to content

Commit f7e7312

Browse files
authored
ml-dsa: implement initial pkcs8/spki traits (#891)
1 parent ba7f5e4 commit f7e7312

11 files changed

+390
-2
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ml-dsa/Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ categories = ["cryptography"]
1515
keywords = ["crypto", "signature"]
1616

1717
[features]
18-
default = ["rand_core"]
18+
default = ["rand_core", "alloc", "pkcs8"]
1919
zeroize = ["dep:zeroize", "hybrid-array/zeroize"]
2020
rand_core = ["dep:rand_core", "signature/rand_core"]
21+
alloc = ["pkcs8?/alloc"]
22+
pkcs8 = ["dep:const-oid", "dep:pkcs8"]
2123

2224
[dependencies]
2325
hybrid-array = { version = "0.2.3", features = ["extra-sizes"] }
@@ -27,10 +29,14 @@ sha3 = "0.10.8"
2729
signature = "2.3.0-pre.4"
2830
zeroize = { version = "1.8.1", optional = true, default-features = false }
2931

32+
const-oid = { version = "0.10.0-rc.1", features = ["db"], optional = true }
33+
pkcs8 = { version = "=0.11.0-rc.1", default-features = false, optional = true }
34+
3035
[dev-dependencies]
3136
criterion = "0.5.1"
3237
hex = { version = "0.4.3", features = ["serde"] }
3338
hex-literal = "0.4.1"
39+
pkcs8 = { version = "=0.11.0-rc.1", features = ["pem"] }
3440
rand = "0.8.5"
3541
serde = { version = "1.0.215", features = ["derive"] }
3642
serde_json = "1.0.132"

ml-dsa/src/lib.rs

+191-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ use rand_core::CryptoRngCore;
5656
#[cfg(feature = "zeroize")]
5757
use zeroize::{Zeroize, ZeroizeOnDrop};
5858

59+
#[cfg(feature = "pkcs8")]
60+
use {
61+
const_oid::db::fips204,
62+
pkcs8::{
63+
der::{self, AnyRef},
64+
spki::{
65+
self, AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier,
66+
SubjectPublicKeyInfoRef,
67+
},
68+
AlgorithmIdentifierRef, PrivateKeyInfoRef,
69+
},
70+
};
71+
72+
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
73+
use pkcs8::{
74+
der::asn1::{BitString, BitStringRef},
75+
spki::{SignatureBitStringEncoding, SubjectPublicKeyInfo},
76+
EncodePublicKey,
77+
};
78+
5979
use crate::algebra::{AlgebraExt, Elem, NttMatrix, NttVector, Truncate, Vector};
6080
use crate::crypto::H;
6181
use crate::hint::Hint;
@@ -124,6 +144,24 @@ impl<P: MlDsaParams> signature::SignatureEncoding for Signature<P> {
124144
type Repr = EncodedSignature<P>;
125145
}
126146

147+
#[cfg(feature = "alloc")]
148+
impl<P: MlDsaParams> SignatureBitStringEncoding for Signature<P> {
149+
fn to_bitstring(&self) -> der::Result<BitString> {
150+
BitString::new(0, self.encode().to_vec())
151+
}
152+
}
153+
154+
#[cfg(feature = "pkcs8")]
155+
impl<P> AssociatedAlgorithmIdentifier for Signature<P>
156+
where
157+
P: MlDsaParams,
158+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
159+
{
160+
type Params = AnyRef<'static>;
161+
162+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = P::ALGORITHM_IDENTIFIER;
163+
}
164+
127165
// This method takes a slice of slices so that we can accommodate the varying calculations (direct
128166
// for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
129167
// having to allocate memory for components.
@@ -156,6 +194,46 @@ impl<P: MlDsaParams> signature::KeypairRef for KeyPair<P> {
156194
type VerifyingKey = VerifyingKey<P>;
157195
}
158196

197+
#[cfg(feature = "pkcs8")]
198+
impl<P> TryFrom<PrivateKeyInfoRef<'_>> for KeyPair<P>
199+
where
200+
P: MlDsaParams,
201+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
202+
{
203+
type Error = pkcs8::Error;
204+
205+
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
206+
match private_key_info.algorithm {
207+
alg if alg == P::ALGORITHM_IDENTIFIER => {}
208+
other => return Err(spki::Error::OidUnknown { oid: other.oid }.into()),
209+
};
210+
211+
let seed = Array::try_from(private_key_info.private_key.as_bytes())
212+
.map_err(|_| pkcs8::Error::KeyMalformed)?;
213+
Ok(P::key_gen_internal(&seed))
214+
}
215+
}
216+
217+
/// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and
218+
/// only supports signing with an empty context string.
219+
impl<P: MlDsaParams> signature::Signer<Signature<P>> for KeyPair<P> {
220+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
221+
self.signing_key.sign_deterministic(msg, &[])
222+
}
223+
}
224+
225+
#[cfg(feature = "pkcs8")]
226+
impl<P> SignatureAlgorithmIdentifier for KeyPair<P>
227+
where
228+
P: MlDsaParams,
229+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
230+
{
231+
type Params = AnyRef<'static>;
232+
233+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
234+
Signature::<P>::ALGORITHM_IDENTIFIER;
235+
}
236+
159237
/// An ML-DSA signing key
160238
#[derive(Clone, PartialEq)]
161239
pub struct SigningKey<P: MlDsaParams> {
@@ -384,8 +462,35 @@ impl<P: MlDsaParams> signature::RandomizedSigner<Signature<P>> for SigningKey<P>
384462
}
385463
}
386464

465+
#[cfg(feature = "pkcs8")]
466+
impl<P> SignatureAlgorithmIdentifier for SigningKey<P>
467+
where
468+
P: MlDsaParams,
469+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
470+
{
471+
type Params = AnyRef<'static>;
472+
473+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
474+
Signature::<P>::ALGORITHM_IDENTIFIER;
475+
}
476+
477+
#[cfg(feature = "pkcs8")]
478+
impl<P> TryFrom<PrivateKeyInfoRef<'_>> for SigningKey<P>
479+
where
480+
P: MlDsaParams,
481+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
482+
{
483+
type Error = pkcs8::Error;
484+
485+
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
486+
let keypair = KeyPair::try_from(private_key_info)?;
487+
488+
Ok(keypair.signing_key)
489+
}
490+
}
491+
387492
/// An ML-DSA verification key
388-
#[derive(Clone, PartialEq)]
493+
#[derive(Clone, Debug, PartialEq)]
389494
pub struct VerifyingKey<P: ParameterSet> {
390495
rho: B32,
391496
t1: Vector<P::K>,
@@ -488,6 +593,61 @@ impl<P: MlDsaParams> signature::Verifier<Signature<P>> for VerifyingKey<P> {
488593
}
489594
}
490595

596+
#[cfg(feature = "pkcs8")]
597+
impl<P> SignatureAlgorithmIdentifier for VerifyingKey<P>
598+
where
599+
P: MlDsaParams,
600+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
601+
{
602+
type Params = AnyRef<'static>;
603+
604+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
605+
Signature::<P>::ALGORITHM_IDENTIFIER;
606+
}
607+
608+
#[cfg(feature = "alloc")]
609+
impl<P> EncodePublicKey for VerifyingKey<P>
610+
where
611+
P: MlDsaParams,
612+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
613+
{
614+
fn to_public_key_der(&self) -> spki::Result<der::Document> {
615+
let public_key = self.encode();
616+
let subject_public_key = BitStringRef::new(0, &public_key)?;
617+
618+
SubjectPublicKeyInfo {
619+
algorithm: P::ALGORITHM_IDENTIFIER,
620+
subject_public_key,
621+
}
622+
.try_into()
623+
}
624+
}
625+
626+
#[cfg(feature = "pkcs8")]
627+
impl<P> TryFrom<SubjectPublicKeyInfoRef<'_>> for VerifyingKey<P>
628+
where
629+
P: MlDsaParams,
630+
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
631+
{
632+
type Error = spki::Error;
633+
634+
fn try_from(spki: SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
635+
match spki.algorithm {
636+
alg if alg == P::ALGORITHM_IDENTIFIER => {}
637+
other => return Err(spki::Error::OidUnknown { oid: other.oid }),
638+
};
639+
640+
Ok(Self::decode(
641+
&EncodedVerifyingKey::<P>::try_from(
642+
spki.subject_public_key
643+
.as_bytes()
644+
.ok_or_else(|| der::Tag::BitString.value_error())?,
645+
)
646+
.map_err(|_| pkcs8::Error::KeyMalformed)?,
647+
))
648+
}
649+
}
650+
491651
/// `MlDsa44` is the parameter set for security category 2.
492652
#[derive(Default, Clone, Debug, PartialEq)]
493653
pub struct MlDsa44;
@@ -505,6 +665,16 @@ impl ParameterSet for MlDsa44 {
505665
const TAU: usize = 39;
506666
}
507667

668+
#[cfg(feature = "pkcs8")]
669+
impl AssociatedAlgorithmIdentifier for MlDsa44 {
670+
type Params = AnyRef<'static>;
671+
672+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
673+
oid: fips204::ID_ML_DSA_44,
674+
parameters: None,
675+
};
676+
}
677+
508678
/// `MlDsa65` is the parameter set for security category 3.
509679
#[derive(Default, Clone, Debug, PartialEq)]
510680
pub struct MlDsa65;
@@ -522,6 +692,16 @@ impl ParameterSet for MlDsa65 {
522692
const TAU: usize = 49;
523693
}
524694

695+
#[cfg(feature = "pkcs8")]
696+
impl AssociatedAlgorithmIdentifier for MlDsa65 {
697+
type Params = AnyRef<'static>;
698+
699+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
700+
oid: fips204::ID_ML_DSA_65,
701+
parameters: None,
702+
};
703+
}
704+
525705
/// `MlKem87` is the parameter set for security category 5.
526706
#[derive(Default, Clone, Debug, PartialEq)]
527707
pub struct MlDsa87;
@@ -539,6 +719,16 @@ impl ParameterSet for MlDsa87 {
539719
const TAU: usize = 60;
540720
}
541721

722+
#[cfg(feature = "pkcs8")]
723+
impl AssociatedAlgorithmIdentifier for MlDsa87 {
724+
type Params = AnyRef<'static>;
725+
726+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
727+
oid: fips204::ID_ML_DSA_87,
728+
parameters: None,
729+
};
730+
}
731+
542732
/// A parameter set that knows how to generate key pairs
543733
pub trait KeyGen: MlDsaParams {
544734
/// The type that is returned by key generation

ml-dsa/tests/examples/ML-DSA-44.priv

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
3+
HB0eHw==
4+
-----END PRIVATE KEY-----

ml-dsa/tests/examples/ML-DSA-44.pub

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIFMjALBglghkgBZQMEAxEDggUhANeytHJUquDbReeTDUqY0sl9jxOX0Xidr6Fw
3+
JLMW6b7JT8mUbULxm3mnQTu6oz5xSctC7VEVaTrAQfrLmIretf4OHYYxGEmVtZLD
4+
l9IpTi4U+QqkFLo4JomaxD9MzKy8JumoMrlRGNXLQzy++WYLABOOCBf2HnYsonTD
5+
atVU6yKqwRYuSrAay6HjjE79j4C2WzM9D3LlXf5xzpweu5iJ58VhBsD9c4A6Kuz+
6+
r97XqjyyztpU0SvYzTanjPl1lDtHq9JeiArEUuV0LtHo0agq+oblkMdYwVrk0oQN
7+
kryhpQkPQElll/yn2LlRPxob2m6VCqqY3kZ1B9Sk9aTwWZIWWCw1cvYu2okFqzWB
8+
ZwxKAnd6M+DKcpX9j0/20aCjp2g9ZfX19/xg2gI+gmxfkhRMAvfRuhB1mHVT6pNn
9+
/NdtmQt/qZzUWv24g21D5Fn1GH3wWEeXCaAepoNZNfpwRgmQzT3BukAbqUurHd5B
10+
rGerMxncrKBgSNTE7vJ+4TqcF9BTj0MPLWQtwkFWYN54h32NirxyUjl4wELkKF9D
11+
GYRsRBJiQpdoRMEOVWuiFbWnGeWdDGsqltOYWQcf3MLN51JKe+2uVOhbMY6FTo/i
12+
svPt+slxkSgnCq/R5QRMOk/a/Z/zH5B4S46ORZYUSg2vWGUR09mWK56pWvGXtOX8
13+
YPKx7RXeOlvvX4m9x52RBR2bKBbnT6VFMe/cHL501EiFf0drzVjyHAtlOzt2pOB2
14+
plWaMCcYVVzGP3SFmqurkl8COGHKjND3utsocfZ9VTJtdFETWtRfShumkRj7ssij
15+
DuyTku8/l3Bmya3VxxDMZHsVFNIX2VjHAXw+kP0gwE5nS5BIbpNwoxoAHTL0c5ee
16+
SQZ0nn5Hf6C3RQj4pfI3gxK4PCW9OIygsP/3R4uvQrcWZ+2qyXxGsSlkPlhuWwVa
17+
DCEZRtTzbmdb7Vhg+gQqMV2YJhZNapI3w1pfv0lUkKW9TfJIuVxKrneEtgVnMWas
18+
QkW1tLCCoJ6TI+YvIHjFt2eDRG3v1zatOjcC1JsImESQCmGDM5e8RBmzDXqXoLOH
19+
wZEUdMTUG1PjKpd6y28Op122W7OeWecB52lX3vby1EVZwxp3EitSBOO1whnxaIsU
20+
7QvAuAGz5ugtzUPpwOn0F0TNmBW9G8iCDYuxI/BPrNGxtoXdWisbjbvz7ZM2cPCV
21+
oYC08ZLQixC4+rvfzCskUY4y7qCl4MkEyoRHgAg/OwzS0Li2r2e8NVuUlAJdx7Cn
22+
j6gOOi2/61EyiFHWB4GY6Uk2Ua54fsAlH5Irow6fUd9iptcnhM890gU5MXbfoySl
23+
Er2Ulwo23TSlFKhnkfDrNvAUWwmrZGUbSgMTsplhGiocSIkWJ1mHaKMRQGC6RENI
24+
bfUVIqHOiLMJhcIW+ObtF43VZ7MEoNTK+6iCooNC8XqaomrljbYwCD0sNY/fVmw/
25+
XWKkKFZ7yeqM6VyqDzVHSwv6jzOaJQq0388gg76O77wQVeGP4VNw7ssmBWbYP/Br
26+
IRquxDyim1TM0A+IFaJGXvC0ZRXMfkHzEk8J7/9zkwmrWLKaFFmgC85QOOk4yWeP
27+
cusOTuX9quZtn4Vz/Jf8QrSVn0v4th14Qz6GsDNdbpGRxNi/SHs5BcEIz9asJLDO
28+
t9y3z1H4TQ7Wh7lerrHFM8BvDZcCPZKnCCWDe1m6bLfU5WsKh8IDhiro8xW6WSXo
29+
7e+meTaaIgJ2YVHxapZfn4Hs52zAcLVYaeTbl4TPBcgwsyQsgxI=
30+
-----END PUBLIC KEY-----

ml-dsa/tests/examples/ML-DSA-65.priv

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MDICAQAwCwYJYIZIAWUDBAMSBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
3+
HB0eHw==
4+
-----END PRIVATE KEY-----

ml-dsa/tests/examples/ML-DSA-65.pub

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
-----BEGIN PUBLIC KEY-----
2+
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
43+
HgLy6ERP
44+
-----END PUBLIC KEY-----

ml-dsa/tests/examples/ML-DSA-87.priv

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MDICAQAwCwYJYIZIAWUDBAMTBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
3+
HB0eHw==
4+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)