Skip to main content

ml_dsa/
pkcs8.rs

1//! PKCS#8 private key encoding support.
2
3use crate::{
4    EncodedVerifyingKey, ExpandedSigningKey, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, Signature,
5    SigningKey, VerifyingKey,
6};
7pub use ::pkcs8::*;
8use ::pkcs8::{
9    der::{
10        AnyRef, Reader, TagNumber,
11        asn1::{ContextSpecific, OctetStringRef},
12    },
13    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
14};
15use const_oid::db::fips204;
16
17#[cfg(feature = "alloc")]
18use pkcs8::{
19    der::{
20        Encode, TagMode,
21        asn1::{BitString, BitStringRef},
22    },
23    spki::SignatureBitStringEncoding,
24};
25
26/// Tag number for the seed value.
27const SEED_TAG_NUMBER: TagNumber = TagNumber(0);
28
29/// ML-KEM seed serialized as ASN.1.
30type SeedString<'a> = ContextSpecific<&'a OctetStringRef>;
31
32impl AssociatedAlgorithmIdentifier for MlDsa44 {
33    type Params = AnyRef<'static>;
34
35    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
36        oid: fips204::ID_ML_DSA_44,
37        parameters: None,
38    };
39}
40
41impl AssociatedAlgorithmIdentifier for MlDsa65 {
42    type Params = AnyRef<'static>;
43
44    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
45        oid: fips204::ID_ML_DSA_65,
46        parameters: None,
47    };
48}
49
50impl AssociatedAlgorithmIdentifier for MlDsa87 {
51    type Params = AnyRef<'static>;
52
53    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
54        oid: fips204::ID_ML_DSA_87,
55        parameters: None,
56    };
57}
58
59impl<P> AssociatedAlgorithmIdentifier for Signature<P>
60where
61    P: MlDsaParams,
62    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
63{
64    type Params = AnyRef<'static>;
65
66    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = P::ALGORITHM_IDENTIFIER;
67}
68
69#[cfg(feature = "alloc")]
70impl<P: MlDsaParams> SignatureBitStringEncoding for Signature<P> {
71    fn to_bitstring(&self) -> der::Result<BitString> {
72        BitString::new(0, self.encode().to_vec())
73    }
74}
75
76impl<P> SignatureAlgorithmIdentifier for SigningKey<P>
77where
78    P: MlDsaParams,
79    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
80{
81    type Params = AnyRef<'static>;
82
83    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
84        Signature::<P>::ALGORITHM_IDENTIFIER;
85}
86
87impl<P> TryFrom<PrivateKeyInfoRef<'_>> for SigningKey<P>
88where
89    P: MlDsaParams,
90    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
91{
92    type Error = Error;
93
94    fn try_from(private_key_info: PrivateKeyInfoRef<'_>) -> Result<Self> {
95        private_key_info
96            .algorithm
97            .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?;
98
99        if private_key_info.algorithm.parameters.is_some() {
100            return Err(Error::ParametersMalformed);
101        }
102
103        let mut reader = der::SliceReader::new(private_key_info.private_key.as_bytes())?;
104        let seed_string =
105            SeedString::decode_implicit(&mut reader, SEED_TAG_NUMBER)?.ok_or(KeyError::Invalid)?;
106        let seed = seed_string
107            .value
108            .as_bytes()
109            .try_into()
110            .map_err(|_| KeyError::Invalid)?;
111        reader.finish()?;
112
113        Ok(SigningKey::from_seed(&seed))
114    }
115}
116
117#[cfg(feature = "alloc")]
118impl<P> EncodePrivateKey for SigningKey<P>
119where
120    P: MlDsaParams,
121    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
122{
123    fn to_pkcs8_der(&self) -> ::pkcs8::Result<SecretDocument> {
124        let seed_der = SeedString {
125            tag_mode: TagMode::Implicit,
126            tag_number: SEED_TAG_NUMBER,
127            value: OctetStringRef::new(self.as_seed())?,
128        }
129        .to_der()?;
130
131        let private_key = OctetStringRef::new(&seed_der)?;
132        let private_key_info = PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, private_key);
133        ::pkcs8::SecretDocument::encode_msg(&private_key_info).map_err(::pkcs8::Error::Asn1)
134    }
135}
136
137impl<P> SignatureAlgorithmIdentifier for ExpandedSigningKey<P>
138where
139    P: MlDsaParams,
140    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
141{
142    type Params = AnyRef<'static>;
143
144    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
145        Signature::<P>::ALGORITHM_IDENTIFIER;
146}
147
148impl<P> TryFrom<PrivateKeyInfoRef<'_>> for ExpandedSigningKey<P>
149where
150    P: MlDsaParams,
151    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
152{
153    type Error = ::pkcs8::Error;
154
155    fn try_from(private_key_info: PrivateKeyInfoRef<'_>) -> Result<Self> {
156        SigningKey::try_from(private_key_info).map(|sk| sk.expanded_key().clone())
157    }
158}
159
160impl<P> SignatureAlgorithmIdentifier for VerifyingKey<P>
161where
162    P: MlDsaParams,
163    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
164{
165    type Params = AnyRef<'static>;
166
167    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
168        Signature::<P>::ALGORITHM_IDENTIFIER;
169}
170
171#[cfg(feature = "alloc")]
172impl<P> EncodePublicKey for VerifyingKey<P>
173where
174    P: MlDsaParams,
175    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
176{
177    fn to_public_key_der(&self) -> spki::Result<Document> {
178        let public_key = self.encode();
179        let subject_public_key = BitStringRef::new(0, &public_key)?;
180
181        SubjectPublicKeyInfo {
182            algorithm: P::ALGORITHM_IDENTIFIER,
183            subject_public_key,
184        }
185        .try_into()
186    }
187}
188
189impl<P> TryFrom<SubjectPublicKeyInfoRef<'_>> for VerifyingKey<P>
190where
191    P: MlDsaParams,
192    P: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
193{
194    type Error = spki::Error;
195
196    fn try_from(spki: SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
197        spki.algorithm
198            .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?;
199
200        if spki.algorithm.parameters.is_some() {
201            return Err(spki::Error::KeyMalformed);
202        }
203
204        let evk = EncodedVerifyingKey::<P>::try_from(
205            spki.subject_public_key
206                .as_bytes()
207                .ok_or_else(|| der::Tag::BitString.value_error().to_error())?,
208        )
209        .map_err(|_| spki::Error::KeyMalformed)?;
210
211        Ok(Self::decode(&evk))
212    }
213}