ml-dsa: implement initial pkcs8/spki traits (#891)

This commit is contained in:
Arthur Gautier
2025-01-25 09:30:30 -08:00
committed by GitHub
parent ba7f5e43b3
commit f7e7312d94
11 changed files with 390 additions and 2 deletions
Generated
+2
View File
@@ -683,11 +683,13 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
name = "ml-dsa"
version = "0.1.0-pre.0"
dependencies = [
"const-oid",
"criterion",
"hex",
"hex-literal",
"hybrid-array",
"num-traits",
"pkcs8",
"rand",
"rand_core",
"serde",
+7 -1
View File
@@ -15,9 +15,11 @@ categories = ["cryptography"]
keywords = ["crypto", "signature"]
[features]
default = ["rand_core"]
default = ["rand_core", "alloc", "pkcs8"]
zeroize = ["dep:zeroize", "hybrid-array/zeroize"]
rand_core = ["dep:rand_core", "signature/rand_core"]
alloc = ["pkcs8?/alloc"]
pkcs8 = ["dep:const-oid", "dep:pkcs8"]
[dependencies]
hybrid-array = { version = "0.2.3", features = ["extra-sizes"] }
@@ -27,10 +29,14 @@ sha3 = "0.10.8"
signature = "2.3.0-pre.4"
zeroize = { version = "1.8.1", optional = true, default-features = false }
const-oid = { version = "0.10.0-rc.1", features = ["db"], optional = true }
pkcs8 = { version = "=0.11.0-rc.1", default-features = false, optional = true }
[dev-dependencies]
criterion = "0.5.1"
hex = { version = "0.4.3", features = ["serde"] }
hex-literal = "0.4.1"
pkcs8 = { version = "=0.11.0-rc.1", features = ["pem"] }
rand = "0.8.5"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.132"
+191 -1
View File
@@ -56,6 +56,26 @@ use rand_core::CryptoRngCore;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "pkcs8")]
use {
const_oid::db::fips204,
pkcs8::{
der::{self, AnyRef},
spki::{
self, AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier,
SubjectPublicKeyInfoRef,
},
AlgorithmIdentifierRef, PrivateKeyInfoRef,
},
};
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use pkcs8::{
der::asn1::{BitString, BitStringRef},
spki::{SignatureBitStringEncoding, SubjectPublicKeyInfo},
EncodePublicKey,
};
use crate::algebra::{AlgebraExt, Elem, NttMatrix, NttVector, Truncate, Vector};
use crate::crypto::H;
use crate::hint::Hint;
@@ -124,6 +144,24 @@ impl<P: MlDsaParams> signature::SignatureEncoding for Signature<P> {
type Repr = EncodedSignature<P>;
}
#[cfg(feature = "alloc")]
impl<P: MlDsaParams> SignatureBitStringEncoding for Signature<P> {
fn to_bitstring(&self) -> der::Result<BitString> {
BitString::new(0, self.encode().to_vec())
}
}
#[cfg(feature = "pkcs8")]
impl<P> AssociatedAlgorithmIdentifier for Signature<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = P::ALGORITHM_IDENTIFIER;
}
// This method takes a slice of slices so that we can accommodate the varying calculations (direct
// for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
// having to allocate memory for components.
@@ -156,6 +194,46 @@ impl<P: MlDsaParams> signature::KeypairRef for KeyPair<P> {
type VerifyingKey = VerifyingKey<P>;
}
#[cfg(feature = "pkcs8")]
impl<P> TryFrom<PrivateKeyInfoRef<'_>> for KeyPair<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Error = pkcs8::Error;
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
match private_key_info.algorithm {
alg if alg == P::ALGORITHM_IDENTIFIER => {}
other => return Err(spki::Error::OidUnknown { oid: other.oid }.into()),
};
let seed = Array::try_from(private_key_info.private_key.as_bytes())
.map_err(|_| pkcs8::Error::KeyMalformed)?;
Ok(P::key_gen_internal(&seed))
}
}
/// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and
/// only supports signing with an empty context string.
impl<P: MlDsaParams> signature::Signer<Signature<P>> for KeyPair<P> {
fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
self.signing_key.sign_deterministic(msg, &[])
}
}
#[cfg(feature = "pkcs8")]
impl<P> SignatureAlgorithmIdentifier for KeyPair<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<P>::ALGORITHM_IDENTIFIER;
}
/// An ML-DSA signing key
#[derive(Clone, PartialEq)]
pub struct SigningKey<P: MlDsaParams> {
@@ -384,8 +462,35 @@ impl<P: MlDsaParams> signature::RandomizedSigner<Signature<P>> for SigningKey<P>
}
}
#[cfg(feature = "pkcs8")]
impl<P> SignatureAlgorithmIdentifier for SigningKey<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<P>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "pkcs8")]
impl<P> TryFrom<PrivateKeyInfoRef<'_>> for SigningKey<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Error = pkcs8::Error;
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
let keypair = KeyPair::try_from(private_key_info)?;
Ok(keypair.signing_key)
}
}
/// An ML-DSA verification key
#[derive(Clone, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct VerifyingKey<P: ParameterSet> {
rho: B32,
t1: Vector<P::K>,
@@ -488,6 +593,61 @@ impl<P: MlDsaParams> signature::Verifier<Signature<P>> for VerifyingKey<P> {
}
}
#[cfg(feature = "pkcs8")]
impl<P> SignatureAlgorithmIdentifier for VerifyingKey<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<P>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "alloc")]
impl<P> EncodePublicKey for VerifyingKey<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
fn to_public_key_der(&self) -> spki::Result<der::Document> {
let public_key = self.encode();
let subject_public_key = BitStringRef::new(0, &public_key)?;
SubjectPublicKeyInfo {
algorithm: P::ALGORITHM_IDENTIFIER,
subject_public_key,
}
.try_into()
}
}
#[cfg(feature = "pkcs8")]
impl<P> TryFrom<SubjectPublicKeyInfoRef<'_>> for VerifyingKey<P>
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Error = spki::Error;
fn try_from(spki: SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
match spki.algorithm {
alg if alg == P::ALGORITHM_IDENTIFIER => {}
other => return Err(spki::Error::OidUnknown { oid: other.oid }),
};
Ok(Self::decode(
&EncodedVerifyingKey::<P>::try_from(
spki.subject_public_key
.as_bytes()
.ok_or_else(|| der::Tag::BitString.value_error())?,
)
.map_err(|_| pkcs8::Error::KeyMalformed)?,
))
}
}
/// `MlDsa44` is the parameter set for security category 2.
#[derive(Default, Clone, Debug, PartialEq)]
pub struct MlDsa44;
@@ -505,6 +665,16 @@ impl ParameterSet for MlDsa44 {
const TAU: usize = 39;
}
#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for MlDsa44 {
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
oid: fips204::ID_ML_DSA_44,
parameters: None,
};
}
/// `MlDsa65` is the parameter set for security category 3.
#[derive(Default, Clone, Debug, PartialEq)]
pub struct MlDsa65;
@@ -522,6 +692,16 @@ impl ParameterSet for MlDsa65 {
const TAU: usize = 49;
}
#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for MlDsa65 {
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
oid: fips204::ID_ML_DSA_65,
parameters: None,
};
}
/// `MlKem87` is the parameter set for security category 5.
#[derive(Default, Clone, Debug, PartialEq)]
pub struct MlDsa87;
@@ -539,6 +719,16 @@ impl ParameterSet for MlDsa87 {
const TAU: usize = 60;
}
#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for MlDsa87 {
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
oid: fips204::ID_ML_DSA_87,
parameters: None,
};
}
/// A parameter set that knows how to generate key pairs
pub trait KeyGen: MlDsaParams {
/// The type that is returned by key generation
+4
View File
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
HB0eHw==
-----END PRIVATE KEY-----
+30
View File
@@ -0,0 +1,30 @@
-----BEGIN PUBLIC KEY-----
MIIFMjALBglghkgBZQMEAxEDggUhANeytHJUquDbReeTDUqY0sl9jxOX0Xidr6Fw
JLMW6b7JT8mUbULxm3mnQTu6oz5xSctC7VEVaTrAQfrLmIretf4OHYYxGEmVtZLD
l9IpTi4U+QqkFLo4JomaxD9MzKy8JumoMrlRGNXLQzy++WYLABOOCBf2HnYsonTD
atVU6yKqwRYuSrAay6HjjE79j4C2WzM9D3LlXf5xzpweu5iJ58VhBsD9c4A6Kuz+
r97XqjyyztpU0SvYzTanjPl1lDtHq9JeiArEUuV0LtHo0agq+oblkMdYwVrk0oQN
kryhpQkPQElll/yn2LlRPxob2m6VCqqY3kZ1B9Sk9aTwWZIWWCw1cvYu2okFqzWB
ZwxKAnd6M+DKcpX9j0/20aCjp2g9ZfX19/xg2gI+gmxfkhRMAvfRuhB1mHVT6pNn
/NdtmQt/qZzUWv24g21D5Fn1GH3wWEeXCaAepoNZNfpwRgmQzT3BukAbqUurHd5B
rGerMxncrKBgSNTE7vJ+4TqcF9BTj0MPLWQtwkFWYN54h32NirxyUjl4wELkKF9D
GYRsRBJiQpdoRMEOVWuiFbWnGeWdDGsqltOYWQcf3MLN51JKe+2uVOhbMY6FTo/i
svPt+slxkSgnCq/R5QRMOk/a/Z/zH5B4S46ORZYUSg2vWGUR09mWK56pWvGXtOX8
YPKx7RXeOlvvX4m9x52RBR2bKBbnT6VFMe/cHL501EiFf0drzVjyHAtlOzt2pOB2
plWaMCcYVVzGP3SFmqurkl8COGHKjND3utsocfZ9VTJtdFETWtRfShumkRj7ssij
DuyTku8/l3Bmya3VxxDMZHsVFNIX2VjHAXw+kP0gwE5nS5BIbpNwoxoAHTL0c5ee
SQZ0nn5Hf6C3RQj4pfI3gxK4PCW9OIygsP/3R4uvQrcWZ+2qyXxGsSlkPlhuWwVa
DCEZRtTzbmdb7Vhg+gQqMV2YJhZNapI3w1pfv0lUkKW9TfJIuVxKrneEtgVnMWas
QkW1tLCCoJ6TI+YvIHjFt2eDRG3v1zatOjcC1JsImESQCmGDM5e8RBmzDXqXoLOH
wZEUdMTUG1PjKpd6y28Op122W7OeWecB52lX3vby1EVZwxp3EitSBOO1whnxaIsU
7QvAuAGz5ugtzUPpwOn0F0TNmBW9G8iCDYuxI/BPrNGxtoXdWisbjbvz7ZM2cPCV
oYC08ZLQixC4+rvfzCskUY4y7qCl4MkEyoRHgAg/OwzS0Li2r2e8NVuUlAJdx7Cn
j6gOOi2/61EyiFHWB4GY6Uk2Ua54fsAlH5Irow6fUd9iptcnhM890gU5MXbfoySl
Er2Ulwo23TSlFKhnkfDrNvAUWwmrZGUbSgMTsplhGiocSIkWJ1mHaKMRQGC6RENI
bfUVIqHOiLMJhcIW+ObtF43VZ7MEoNTK+6iCooNC8XqaomrljbYwCD0sNY/fVmw/
XWKkKFZ7yeqM6VyqDzVHSwv6jzOaJQq0388gg76O77wQVeGP4VNw7ssmBWbYP/Br
IRquxDyim1TM0A+IFaJGXvC0ZRXMfkHzEk8J7/9zkwmrWLKaFFmgC85QOOk4yWeP
cusOTuX9quZtn4Vz/Jf8QrSVn0v4th14Qz6GsDNdbpGRxNi/SHs5BcEIz9asJLDO
t9y3z1H4TQ7Wh7lerrHFM8BvDZcCPZKnCCWDe1m6bLfU5WsKh8IDhiro8xW6WSXo
7e+meTaaIgJ2YVHxapZfn4Hs52zAcLVYaeTbl4TPBcgwsyQsgxI=
-----END PUBLIC KEY-----
+4
View File
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MDICAQAwCwYJYIZIAWUDBAMSBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
HB0eHw==
-----END PRIVATE KEY-----
+44
View File
@@ -0,0 +1,44 @@
-----BEGIN PUBLIC KEY-----
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
HgLy6ERP
-----END PUBLIC KEY-----
+4
View File
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MDICAQAwCwYJYIZIAWUDBAMTBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
HB0eHw==
-----END PRIVATE KEY-----
+57
View File
@@ -0,0 +1,57 @@
-----BEGIN PUBLIC KEY-----
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
5VvgYiIvi6kKl0BzMmsxISrs4KNKYA==
-----END PUBLIC KEY-----
+1
View File
@@ -0,0 +1 @@
Examples found in this directory can be found in https://github.com/lamps-wg/dilithium-certificates/tree/main/examples
+46
View File
@@ -0,0 +1,46 @@
#![cfg(all(feature = "pkcs8", feature = "alloc"))]
use core::ops::Deref;
use ml_dsa::{KeyPair, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SigningKey, VerifyingKey};
use pkcs8::{
der::{pem::LineEnding, AnyRef},
spki::AssociatedAlgorithmIdentifier,
DecodePrivateKey, DecodePublicKey, EncodePublicKey,
};
use signature::Keypair;
#[test]
fn private_key_serialization() {
fn test_roundtrip<P>(private_bytes: &str, public_bytes: &str)
where
P: MlDsaParams,
P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
let sk = SigningKey::<P>::from_pkcs8_pem(private_bytes).expect("parse private key");
let kp = KeyPair::<P>::from_pkcs8_pem(private_bytes).expect("parse private key");
assert!(sk == kp.signing_key);
let pk = VerifyingKey::<P>::from_public_key_pem(public_bytes).expect("parse public key");
assert_eq!(
pk.to_public_key_pem(LineEnding::LF)
.expect("serialize public key")
.deref(),
public_bytes
);
assert_eq!(kp.verifying_key(), pk);
}
test_roundtrip::<MlDsa44>(
include_str!("examples/ML-DSA-44.priv"),
include_str!("examples/ML-DSA-44.pub"),
);
test_roundtrip::<MlDsa65>(
include_str!("examples/ML-DSA-65.priv"),
include_str!("examples/ML-DSA-65.pub"),
);
test_roundtrip::<MlDsa87>(
include_str!("examples/ML-DSA-87.priv"),
include_str!("examples/ML-DSA-87.pub"),
);
}