mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
slh-dsa: adds pkcs8 support (#867)
This commit is contained in:
Generated
+2
@@ -1162,6 +1162,7 @@ version = "0.2.0-pre"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"cipher",
|
||||
"const-oid",
|
||||
"criterion",
|
||||
"ctr",
|
||||
"digest 0.11.0-pre.9",
|
||||
@@ -1171,6 +1172,7 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
"num-bigint",
|
||||
"paste",
|
||||
"pkcs8",
|
||||
"proptest",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
|
||||
+4
-1
@@ -24,6 +24,8 @@ signature = { version = "2.3.0-pre.4", features = ["rand_core"] }
|
||||
hmac = "=0.13.0-pre.4"
|
||||
sha2 = { version = "=0.11.0-pre.4", default-features = false }
|
||||
digest = "=0.11.0-pre.9"
|
||||
pkcs8 = { version = "=0.11.0-rc.1", default-features = false }
|
||||
const-oid = { version = "0.10.0-rc.1", features = ["db"] }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.4.1"
|
||||
@@ -41,6 +43,7 @@ paste = "1.0.15"
|
||||
rand = "0.8.5"
|
||||
serde_json = "1.0.124"
|
||||
serde = { version = "1.0.207", features = ["derive"] }
|
||||
pkcs8 = { version = "=0.11.0-rc.1", features = ["pem"] }
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
@@ -51,4 +54,4 @@ harness = false
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
default = ["alloc"]
|
||||
default = ["alloc", "pkcs8/alloc"]
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::{
|
||||
xmss::XmssParams, ParameterSet,
|
||||
};
|
||||
use crate::{PkSeed, SkPrf, SkSeed};
|
||||
use const_oid::db::fips205;
|
||||
use digest::{Digest, KeyInit, Mac};
|
||||
use hmac::Hmac;
|
||||
use hybrid_array::{Array, ArraySize};
|
||||
@@ -169,6 +170,7 @@ impl ForsParams for Sha2_128s {
|
||||
}
|
||||
impl ParameterSet for Sha2_128s {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-128s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_128_S;
|
||||
}
|
||||
|
||||
/// SHA2 at L1 security with fast signatures
|
||||
@@ -191,6 +193,7 @@ impl ForsParams for Sha2_128f {
|
||||
}
|
||||
impl ParameterSet for Sha2_128f {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-128f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_128_F;
|
||||
}
|
||||
|
||||
/// Implementation of the component hash functions using SHA2 at Security Category 3 and 5
|
||||
@@ -330,6 +333,7 @@ impl ForsParams for Sha2_192s {
|
||||
}
|
||||
impl ParameterSet for Sha2_192s {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-192s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_192_S;
|
||||
}
|
||||
|
||||
/// SHA2 at L3 security with fast signatures
|
||||
@@ -352,6 +356,7 @@ impl ForsParams for Sha2_192f {
|
||||
}
|
||||
impl ParameterSet for Sha2_192f {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-192f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_192_F;
|
||||
}
|
||||
|
||||
/// SHA2 at L5 security with small signatures
|
||||
@@ -374,6 +379,7 @@ impl ForsParams for Sha2_256s {
|
||||
}
|
||||
impl ParameterSet for Sha2_256s {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-256s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_256_S;
|
||||
}
|
||||
|
||||
/// SHA2 at L5 security with fast signatures
|
||||
@@ -396,4 +402,5 @@ impl ForsParams for Sha2_256f {
|
||||
}
|
||||
impl ParameterSet for Sha2_256f {
|
||||
const NAME: &'static str = "SLH-DSA-SHA2-256f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHA_2_256_F;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::hypertree::HypertreeParams;
|
||||
use crate::wots::WotsParams;
|
||||
use crate::xmss::XmssParams;
|
||||
use crate::{ParameterSet, PkSeed, SkPrf, SkSeed};
|
||||
use const_oid::db::fips205;
|
||||
use digest::{ExtendableOutput, Update};
|
||||
use hybrid_array::typenum::consts::{U16, U30, U32};
|
||||
use hybrid_array::typenum::{U24, U34, U39, U42, U47, U49};
|
||||
@@ -146,6 +147,7 @@ impl ForsParams for Shake128s {
|
||||
}
|
||||
impl ParameterSet for Shake128s {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-128s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_128_S;
|
||||
}
|
||||
|
||||
/// SHAKE256 at L1 security with fast signatures
|
||||
@@ -168,6 +170,7 @@ impl ForsParams for Shake128f {
|
||||
}
|
||||
impl ParameterSet for Shake128f {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-128f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_128_F;
|
||||
}
|
||||
|
||||
/// SHAKE256 at L3 security with small signatures
|
||||
@@ -190,6 +193,7 @@ impl ForsParams for Shake192s {
|
||||
}
|
||||
impl ParameterSet for Shake192s {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-192s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_192_S;
|
||||
}
|
||||
|
||||
/// SHAKE256 at L3 security with fast signatures
|
||||
@@ -212,6 +216,7 @@ impl ForsParams for Shake192f {
|
||||
}
|
||||
impl ParameterSet for Shake192f {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-192f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_192_F;
|
||||
}
|
||||
|
||||
/// SHAKE256 at L5 security with small signatures
|
||||
@@ -234,6 +239,7 @@ impl ForsParams for Shake256s {
|
||||
}
|
||||
impl ParameterSet for Shake256s {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-256s";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_256_S;
|
||||
}
|
||||
|
||||
/// SHAKE256 at L5 security with fast signatures
|
||||
@@ -256,6 +262,7 @@ impl ForsParams for Shake256f {
|
||||
}
|
||||
impl ParameterSet for Shake256f {
|
||||
const NAME: &'static str = "SLH-DSA-SHAKE-256f";
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier = fips205::ID_SLH_DSA_SHAKE_256_F;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -73,6 +73,9 @@ pub trait ParameterSet:
|
||||
{
|
||||
/// Human-readable name for parameter set, matching the FIPS-205 designations
|
||||
const NAME: &'static str;
|
||||
|
||||
/// Associated OID with the Parameter
|
||||
const ALGORITHM_OID: pkcs8::ObjectIdentifier;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -8,8 +8,15 @@ use crate::{fors::ForsSignature, Shake128s};
|
||||
use ::signature::{Error, SignatureEncoding};
|
||||
use hybrid_array::sizes::{U16224, U17088, U29792, U35664, U49856, U7856};
|
||||
use hybrid_array::{Array, ArraySize};
|
||||
use pkcs8::{der::AnyRef, spki::AssociatedAlgorithmIdentifier, AlgorithmIdentifierRef};
|
||||
use typenum::Unsigned;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use pkcs8::{
|
||||
der::{self, asn1::BitString},
|
||||
spki::SignatureBitStringEncoding,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// A parsed SLH-DSA signature for a given parameter set
|
||||
///
|
||||
@@ -95,6 +102,22 @@ impl<P: ParameterSet> SignatureEncoding for Signature<P> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<P: ParameterSet> SignatureBitStringEncoding for Signature<P> {
|
||||
fn to_bitstring(&self) -> der::Result<BitString> {
|
||||
BitString::new(0, self.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ParameterSet> AssociatedAlgorithmIdentifier for Signature<P> {
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
|
||||
oid: P::ALGORITHM_OID,
|
||||
parameters: None,
|
||||
};
|
||||
}
|
||||
|
||||
impl<P: ParameterSet> From<Signature<P>> for Array<u8, P::SigLen> {
|
||||
fn from(sig: Signature<P>) -> Array<u8, P::SigLen> {
|
||||
sig.to_bytes()
|
||||
|
||||
@@ -5,8 +5,18 @@ use crate::verifying_key::VerifyingKey;
|
||||
use crate::{ParameterSet, PkSeed, Sha2L1, Sha2L35, Shake, VerifyingKeyLen};
|
||||
use ::signature::{Error, KeypairRef, RandomizedSigner, Signer};
|
||||
use hybrid_array::{Array, ArraySize};
|
||||
use pkcs8::{
|
||||
der::AnyRef,
|
||||
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
|
||||
};
|
||||
use typenum::{Unsigned, U, U16, U24, U32};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use pkcs8::{
|
||||
der::{self, asn1::OctetStringRef},
|
||||
EncodePrivateKey,
|
||||
};
|
||||
|
||||
// NewTypes for ensuring hash argument order correctness
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct SkSeed<N: ArraySize>(pub(crate) Array<u8, N>);
|
||||
@@ -215,6 +225,47 @@ impl<P: ParameterSet> KeypairRef for SigningKey<P> {
|
||||
type VerifyingKey = VerifyingKey<P>;
|
||||
}
|
||||
|
||||
impl<P> TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SigningKey<P>
|
||||
where
|
||||
P: ParameterSet,
|
||||
{
|
||||
type Error = pkcs8::Error;
|
||||
|
||||
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
|
||||
private_key_info
|
||||
.algorithm
|
||||
.assert_algorithm_oid(P::ALGORITHM_OID)?;
|
||||
|
||||
Self::try_from(private_key_info.private_key.as_bytes())
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<P> EncodePrivateKey for SigningKey<P>
|
||||
where
|
||||
P: ParameterSet,
|
||||
{
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
|
||||
let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
|
||||
oid: P::ALGORITHM_OID,
|
||||
parameters: None,
|
||||
};
|
||||
|
||||
let private_key = self.to_bytes();
|
||||
let pkcs8_key =
|
||||
pkcs8::PrivateKeyInfoRef::new(algorithm_identifier, OctetStringRef::new(&private_key)?);
|
||||
Ok(der::SecretDocument::encode_msg(&pkcs8_key)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ParameterSet> SignatureAlgorithmIdentifier for SigningKey<P> {
|
||||
type Params = AnyRef<'static>;
|
||||
|
||||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
|
||||
Signature::<P>::ALGORITHM_IDENTIFIER;
|
||||
}
|
||||
|
||||
impl<M> SigningKeyLen for Sha2L1<U16, M> {
|
||||
type SkLen = U<{ 4 * 16 }>;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ use crate::Sha2L35;
|
||||
use crate::Shake;
|
||||
use ::signature::{Error, Verifier};
|
||||
use hybrid_array::{Array, ArraySize};
|
||||
use pkcs8::{der, spki};
|
||||
use typenum::{Unsigned, U, U16, U24, U32};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use pkcs8::EncodePublicKey;
|
||||
|
||||
/// A trait specifying the length of a serialized verifying key for a given parameter set
|
||||
pub trait VerifyingKeyLen {
|
||||
/// The length of the serialized verifying key in bytes
|
||||
@@ -150,6 +154,40 @@ impl<P: ParameterSet> Verifier<Signature<P>> for VerifyingKey<P> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<P: ParameterSet> EncodePublicKey for VerifyingKey<P> {
|
||||
fn to_public_key_der(&self) -> pkcs8::spki::Result<der::Document> {
|
||||
let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
|
||||
oid: P::ALGORITHM_OID,
|
||||
parameters: None,
|
||||
};
|
||||
|
||||
let public_key = self.to_bytes();
|
||||
let subject_public_key = der::asn1::BitStringRef::new(0, &public_key)?;
|
||||
|
||||
pkcs8::SubjectPublicKeyInfo {
|
||||
algorithm: algorithm_identifier,
|
||||
subject_public_key,
|
||||
}
|
||||
.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ParameterSet> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'_>> for VerifyingKey<P> {
|
||||
type Error = spki::Error;
|
||||
|
||||
fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
|
||||
spki.algorithm.assert_algorithm_oid(P::ALGORITHM_OID)?;
|
||||
|
||||
Ok(Self::try_from(
|
||||
spki.subject_public_key
|
||||
.as_bytes()
|
||||
.ok_or_else(|| der::Tag::BitString.value_error())?,
|
||||
)
|
||||
.map_err(|_| pkcs8::Error::KeyMalformed)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> VerifyingKeyLen for Sha2L1<U16, M> {
|
||||
type VkLen = U<32>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#![cfg(feature = "alloc")]
|
||||
|
||||
use hex_literal::hex;
|
||||
use pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey, LineEnding};
|
||||
use slh_dsa::{Sha2_128s, SigningKey, VerifyingKey};
|
||||
use std::ops::Deref;
|
||||
|
||||
// Serialization of the SLH-DSA keys is still a draft
|
||||
// The vectors used here are taken from the draft-ietf-lamps-x509-slhdsa
|
||||
// https://github.com/lamps-wg/x509-slhdsa/commit/128c68b6b141e109e3e0ec8f3f47c832a4baaa30
|
||||
#[test]
|
||||
fn pkcs8_output() {
|
||||
let signing = SigningKey::<Sha2_128s>::try_from(&hex!("A2263BCA45860836523160049523D621677FAD90D51EB6067A327E0D1E64A5012B8109EC777CAA4E1F024CCFCF9497D99180509280F4256AF2B07AF80289B494")[..]).unwrap();
|
||||
|
||||
let out = signing.to_pkcs8_pem(LineEnding::LF).unwrap();
|
||||
|
||||
// https://github.com/lamps-wg/x509-slhdsa/blob/main/id-slh-dsa-sha2-128s.priv
|
||||
assert_eq!(
|
||||
out.deref(),
|
||||
r#"-----BEGIN PRIVATE KEY-----
|
||||
MFICAQAwCwYJYIZIAWUDBAMUBECiJjvKRYYINlIxYASVI9YhZ3+tkNUetgZ6Mn4N
|
||||
HmSlASuBCex3fKpOHwJMz8+Ul9mRgFCSgPQlavKwevgCibSU
|
||||
-----END PRIVATE KEY-----
|
||||
"#
|
||||
);
|
||||
|
||||
let parsed = SigningKey::<Sha2_128s>::from_pkcs8_pem(out.deref()).unwrap();
|
||||
|
||||
assert_eq!(parsed, signing);
|
||||
|
||||
let public: VerifyingKey<Sha2_128s> = parsed.as_ref().clone();
|
||||
|
||||
let out = public.to_public_key_pem(LineEnding::LF).unwrap();
|
||||
|
||||
// https://github.com/lamps-wg/x509-slhdsa/blob/main/id-slh-dsa-sha2-128s.pub
|
||||
assert_eq!(
|
||||
out.deref(),
|
||||
r#"-----BEGIN PUBLIC KEY-----
|
||||
MDAwCwYJYIZIAWUDBAMUAyEAK4EJ7Hd8qk4fAkzPz5SX2ZGAUJKA9CVq8rB6+AKJ
|
||||
tJQ=
|
||||
-----END PUBLIC KEY-----
|
||||
"#
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user