Skip to content

Commit

Permalink
slh-dsa: adds pkcs8 support
Browse files Browse the repository at this point in the history
  • Loading branch information
baloo committed Oct 4, 2024
1 parent c0ab70c commit e072e33
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion slh-dsa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -51,4 +54,4 @@ harness = false

[features]
alloc = []
default = ["alloc"]
default = ["alloc", "pkcs8/alloc"]
7 changes: 7 additions & 0 deletions slh-dsa/src/hashes/sha2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -168,6 +169,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
Expand All @@ -190,6 +192,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
Expand Down Expand Up @@ -328,6 +331,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
Expand All @@ -350,6 +354,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
Expand All @@ -372,6 +377,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
Expand All @@ -394,4 +400,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 changes: 7 additions & 0 deletions slh-dsa/src/hashes/shake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -144,6 +145,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
Expand All @@ -166,6 +168,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
Expand All @@ -188,6 +191,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
Expand All @@ -210,6 +214,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
Expand All @@ -232,6 +237,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
Expand All @@ -254,6 +260,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)]
Expand Down
3 changes: 3 additions & 0 deletions slh-dsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
23 changes: 23 additions & 0 deletions slh-dsa/src/signature_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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()
Expand Down
51 changes: 51 additions & 0 deletions slh-dsa/src/signing_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>);
Expand Down Expand Up @@ -216,6 +226,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 }>;
}
Expand Down
38 changes: 38 additions & 0 deletions slh-dsa/src/verifying_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -147,6 +151,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>;
}
Expand Down
39 changes: 39 additions & 0 deletions slh-dsa/tests/pkcs8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#![cfg(feature = "alloc")]

use hex_literal::hex;
use pkcs8::{DecodePrivateKey, EncodePrivateKey, EncodePublicKey, LineEnding};
use slh_dsa::{Sha2_128s, SigningKey, VerifyingKey};
use std::ops::Deref;

#[test]
fn pkcs8_output() {
let signing = SigningKey::<Sha2_128s>::try_from(&hex!("A0FC7756572F3008F544399C25C9E087C28287AB54ADB1601FCACF85C2995A54404F690CD9A145512F61F2E4DE9292DA71371E754B3C2A79F2471E14608A2E34")[..]).unwrap();

let out = signing.to_pkcs8_pem(LineEnding::LF).unwrap();

assert_eq!(
out.deref(),
r#"-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMUBECg/HdWVy8wCPVEOZwlyeCHwoKHq1StsWAfys+F
wplaVEBPaQzZoUVRL2Hy5N6SktpxNx51SzwqefJHHhRgii40
-----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();

assert_eq!(
out.deref(),
r#"-----BEGIN PUBLIC KEY-----
MDAwCwYJYIZIAWUDBAMUAyEAQE9pDNmhRVEvYfLk3pKS2nE3HnVLPCp58kceFGCK
LjQ=
-----END PUBLIC KEY-----
"#
);
}

0 comments on commit e072e33

Please sign in to comment.