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 2, 2024
1 parent 5c33bd5 commit d304bb8
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 2 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ members = [

[profile.dev]
opt-level = 2

[patch.crates-io]
# https://github.com/RustCrypto/formats/pull/1541
const-oid = { git = "https://github.com/baloo/formats.git", branch = "baloo/const-oid/fips203-204-205" }
2 changes: 2 additions & 0 deletions 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, features = ["pem"] }
const-oid = { version = "0.10.0-rc.0", features = ["db"] }

[dev-dependencies]
hex-literal = "0.4.1"
Expand Down
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
37 changes: 37 additions & 0 deletions slh-dsa/src/signing_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ 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::{self, asn1::OctetStringRef},
EncodePrivateKey,
};
use typenum::{Unsigned, U, U16, U24, U32};

// NewTypes for ensuring hash argument order correctness
Expand Down Expand Up @@ -216,6 +220,39 @@ 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)
}
}

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<M> SigningKeyLen for Sha2L1<U16, M> {
type SkLen = U<{ 4 * 16 }>;
}
Expand Down
21 changes: 21 additions & 0 deletions slh-dsa/src/verifying_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::Sha2L35;
use crate::Shake;
use ::signature::{Error, Verifier};
use hybrid_array::{Array, ArraySize};
use pkcs8::{der, EncodePublicKey};
use typenum::{Unsigned, U, U16, U24, U32};

/// A trait specifying the length of a serialized verifying key for a given parameter set
Expand Down Expand Up @@ -147,6 +148,26 @@ impl<P: ParameterSet> Verifier<Signature<P>> for VerifyingKey<P> {
}
}

impl<P> EncodePublicKey for VerifyingKey<P>
where
P: ParameterSet,
{
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<M> VerifyingKeyLen for Sha2L1<U16, M> {
type VkLen = U<32>;
}
Expand Down
37 changes: 37 additions & 0 deletions slh-dsa/tests/pkcs8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 d304bb8

Please sign in to comment.