1#![cfg(feature = "pkcs8")]
11
12pub use ::pkcs8::{DecodePrivateKey, DecodePublicKey, spki::AssociatedAlgorithmIdentifier};
13pub use const_oid::AssociatedOid;
14
15#[cfg(feature = "alloc")]
16pub use ::pkcs8::{EncodePrivateKey, EncodePublicKey};
17
18use crate::{
19 MlKem512Params, MlKem768Params, MlKem1024Params,
20 kem::{DecapsulationKey, EncapsulationKey},
21 param::{EncapsulationKeySize, KemParams},
22 pke::EncryptionKey,
23};
24use ::pkcs8::{
25 der::{
26 AnyRef, Reader, SliceReader, TagNumber,
27 asn1::{ContextSpecific, OctetStringRef},
28 },
29 spki,
30};
31use hybrid_array::Array;
32
33#[cfg(feature = "alloc")]
34use {
35 crate::EncodedSizeUser,
36 ::pkcs8::der::{Encode, TagMode, asn1::BitStringRef},
37};
38
39const SEED_TAG_NUMBER: TagNumber = TagNumber(0);
41
42type SeedString<'a> = ContextSpecific<&'a OctetStringRef>;
44
45impl AssociatedOid for MlKem512Params {
46 const OID: ::pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_512;
47}
48
49impl AssociatedOid for MlKem768Params {
50 const OID: ::pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_768;
51}
52
53impl AssociatedOid for MlKem1024Params {
54 const OID: ::pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_1024;
55}
56
57impl AssociatedAlgorithmIdentifier for MlKem512Params {
58 type Params = ::pkcs8::der::AnyRef<'static>;
59
60 const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> =
61 spki::AlgorithmIdentifier {
62 oid: Self::OID,
63 parameters: None,
64 };
65}
66
67impl AssociatedAlgorithmIdentifier for MlKem768Params {
68 type Params = ::pkcs8::der::AnyRef<'static>;
69
70 const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> =
71 spki::AlgorithmIdentifier {
72 oid: Self::OID,
73 parameters: None,
74 };
75}
76
77impl AssociatedAlgorithmIdentifier for MlKem1024Params {
78 type Params = ::pkcs8::der::AnyRef<'static>;
79
80 const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> =
81 spki::AlgorithmIdentifier {
82 oid: Self::OID,
83 parameters: None,
84 };
85}
86
87impl<P> AssociatedAlgorithmIdentifier for EncapsulationKey<P>
88where
89 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
90{
91 type Params = P::Params;
92
93 const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> = P::ALGORITHM_IDENTIFIER;
94}
95
96#[cfg(feature = "alloc")]
97impl<P> pkcs8::EncodePublicKey for EncapsulationKey<P>
98where
99 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
100{
101 fn to_public_key_der(&self) -> spki::Result<pkcs8::Document> {
104 let public_key = self.as_bytes();
105 let subject_public_key = BitStringRef::new(0, &public_key)?;
106
107 ::pkcs8::SubjectPublicKeyInfo {
108 algorithm: P::ALGORITHM_IDENTIFIER,
109 subject_public_key,
110 }
111 .try_into()
112 }
113}
114
115impl<P> TryFrom<::pkcs8::SubjectPublicKeyInfoRef<'_>> for EncapsulationKey<P>
116where
117 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
118{
119 type Error = spki::Error;
120
121 fn try_from(spki: ::pkcs8::SubjectPublicKeyInfoRef<'_>) -> Result<Self, Self::Error> {
124 if spki.algorithm.oid != P::ALGORITHM_IDENTIFIER.oid {
125 return Err(spki::Error::OidUnknown {
126 oid: P::ALGORITHM_IDENTIFIER.oid,
127 });
128 }
129
130 let bitstring_of_encapsulation_key = spki.subject_public_key;
131 let enc_key = match bitstring_of_encapsulation_key.as_bytes() {
132 Some(bytes) => {
133 let arr: Array<u8, EncapsulationKeySize<P>> = match bytes.try_into() {
134 Ok(array) => array,
135 Err(_) => return Err(spki::Error::KeyMalformed),
136 };
137 EncryptionKey::from_bytes(&arr)
138 }
139 None => return Err(spki::Error::KeyMalformed),
140 };
141
142 Ok(Self::new(enc_key))
143 }
144}
145
146impl<P> AssociatedAlgorithmIdentifier for DecapsulationKey<P>
147where
148 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
149{
150 type Params = P::Params;
151
152 const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> = P::ALGORITHM_IDENTIFIER;
153}
154
155#[cfg(feature = "alloc")]
156impl<P> pkcs8::EncodePrivateKey for DecapsulationKey<P>
157where
158 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
159{
160 fn to_pkcs8_der(&self) -> ::pkcs8::Result<pkcs8::SecretDocument> {
163 let seed = self.to_seed().ok_or(pkcs8::Error::KeyMalformed)?;
164
165 let seed_der = SeedString {
166 tag_mode: TagMode::Implicit,
167 tag_number: SEED_TAG_NUMBER,
168 value: OctetStringRef::new(&seed)?,
169 }
170 .to_der()?;
171
172 let private_key = OctetStringRef::new(&seed_der)?;
173 let private_key_info = pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, private_key);
174 pkcs8::SecretDocument::encode_msg(&private_key_info).map_err(pkcs8::Error::Asn1)
175 }
176}
177
178impl<P> TryFrom<::pkcs8::PrivateKeyInfoRef<'_>> for DecapsulationKey<P>
179where
180 P: KemParams + AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
181{
182 type Error = ::pkcs8::Error;
183
184 fn try_from(private_key_info_ref: ::pkcs8::PrivateKeyInfoRef<'_>) -> Result<Self, Self::Error> {
187 private_key_info_ref
188 .algorithm
189 .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?;
190
191 let mut reader = SliceReader::new(private_key_info_ref.private_key.as_bytes())?;
192 let seed_string = SeedString::decode_implicit(&mut reader, SEED_TAG_NUMBER)?
193 .ok_or(pkcs8::Error::KeyMalformed)?;
194 let seed = seed_string
195 .value
196 .as_bytes()
197 .try_into()
198 .map_err(|_| pkcs8::Error::KeyMalformed)?;
199 reader.finish()?;
200
201 Ok(Self::from_seed(seed))
202 }
203}