1pub use pkcs8::{
18 DecodePrivateKey, DecodePublicKey, Error, KeyError, ObjectIdentifier, PrivateKeyInfoRef,
19 Result, spki,
20};
21
22#[cfg(feature = "alloc")]
23pub use pkcs8::{EncodePrivateKey, spki::EncodePublicKey};
24
25#[cfg(feature = "alloc")]
26pub use pkcs8::der::{
27 Document, SecretDocument,
28 asn1::{BitStringRef, OctetStringRef},
29};
30
31#[cfg(feature = "zeroize")]
32use zeroize::Zeroize;
33
34use core::fmt;
35
36pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.113");
41
42pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifierRef<'static> = pkcs8::AlgorithmIdentifierRef {
44 oid: ALGORITHM_OID,
45 parameters: None,
46};
47
48pub struct KeypairBytes {
65 pub secret_key: [u8; Self::BYTE_SIZE / 2],
71
72 pub public_key: Option<PublicKeyBytes>,
76}
77
78impl KeypairBytes {
79 const BYTE_SIZE: usize = 114;
81
82 #[must_use]
84 #[allow(clippy::missing_panics_doc, reason = "MSRV TODO")]
85 pub fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Self {
86 let (sk, pk) = bytes.split_at(Self::BYTE_SIZE / 2);
88
89 Self {
90 secret_key: sk.try_into().expect("secret key size error"),
91 public_key: Some(PublicKeyBytes(
92 pk.try_into().expect("public key size error"),
93 )),
94 }
95 }
96
97 #[must_use]
103 pub fn to_bytes(&self) -> Option<[u8; Self::BYTE_SIZE]> {
104 if let Some(public_key) = &self.public_key {
105 let mut result = [0u8; Self::BYTE_SIZE];
106 let (sk, pk) = result.split_at_mut(Self::BYTE_SIZE / 2);
107 sk.copy_from_slice(&self.secret_key);
108 pk.copy_from_slice(public_key.as_ref());
109 Some(result)
110 } else {
111 None
112 }
113 }
114}
115
116impl Drop for KeypairBytes {
117 fn drop(&mut self) {
118 #[cfg(feature = "zeroize")]
119 self.secret_key.zeroize();
120 }
121}
122
123#[cfg(feature = "alloc")]
124impl EncodePrivateKey for KeypairBytes {
125 fn to_pkcs8_der(&self) -> Result<SecretDocument> {
126 let mut private_key = [0u8; 2 + (Self::BYTE_SIZE / 2)];
128 private_key[0] = 0x04;
129 private_key[1] = 0x39;
130 private_key[2..].copy_from_slice(&self.secret_key);
131
132 let private_key_info = PrivateKeyInfoRef {
133 algorithm: ALGORITHM_ID,
134 private_key: OctetStringRef::new(&private_key)?,
135 public_key: self
136 .public_key
137 .as_ref()
138 .map(|pk| BitStringRef::new(0, &pk.0))
139 .transpose()?,
140 };
141 let result = SecretDocument::encode_msg(&private_key_info)?;
142
143 #[cfg(feature = "zeroize")]
144 private_key.zeroize();
145
146 Ok(result)
147 }
148}
149
150impl TryFrom<PrivateKeyInfoRef<'_>> for KeypairBytes {
151 type Error = Error;
152
153 fn try_from(private_key: PrivateKeyInfoRef<'_>) -> Result<Self> {
154 private_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
155
156 if private_key.algorithm.parameters.is_some() {
157 return Err(Error::ParametersMalformed);
158 }
159
160 let secret_key = match private_key.private_key.as_bytes() {
169 [0x04, 0x39, rest @ ..] => rest.try_into().map_err(|_| KeyError::Invalid),
170 _ => Err(KeyError::Invalid),
171 }?;
172
173 let public_key = private_key
174 .public_key
175 .and_then(|bs| bs.as_bytes())
176 .map(|bytes| bytes.try_into().map_err(|_| KeyError::Invalid))
177 .transpose()?
178 .map(PublicKeyBytes);
179
180 Ok(Self {
181 secret_key,
182 public_key,
183 })
184 }
185}
186
187impl fmt::Debug for KeypairBytes {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 f.debug_struct("KeypairBytes")
190 .field("public_key", &self.public_key)
191 .finish_non_exhaustive()
192 }
193}
194
195#[derive(Clone, Copy, Eq, Hash, PartialEq)]
212pub struct PublicKeyBytes(pub [u8; Self::BYTE_SIZE]);
213
214impl PublicKeyBytes {
215 const BYTE_SIZE: usize = 57;
217
218 #[must_use]
220 pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] {
221 self.0
222 }
223}
224
225impl AsRef<[u8; Self::BYTE_SIZE]> for PublicKeyBytes {
226 fn as_ref(&self) -> &[u8; Self::BYTE_SIZE] {
227 &self.0
228 }
229}
230
231#[cfg(feature = "alloc")]
232impl EncodePublicKey for PublicKeyBytes {
233 fn to_public_key_der(&self) -> spki::Result<Document> {
234 pkcs8::SubjectPublicKeyInfoRef {
235 algorithm: ALGORITHM_ID,
236 subject_public_key: BitStringRef::new(0, &self.0)?,
237 }
238 .try_into()
239 }
240}
241
242impl TryFrom<spki::SubjectPublicKeyInfoRef<'_>> for PublicKeyBytes {
243 type Error = spki::Error;
244
245 fn try_from(spki: spki::SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
246 spki.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
247
248 if spki.algorithm.parameters.is_some() {
249 return Err(spki::Error::KeyMalformed);
250 }
251
252 spki.subject_public_key
253 .as_bytes()
254 .ok_or(spki::Error::KeyMalformed)?
255 .try_into()
256 .map(Self)
257 .map_err(|_| spki::Error::KeyMalformed)
258 }
259}
260
261impl fmt::Debug for PublicKeyBytes {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 f.write_str("PublicKeyBytes(")?;
264
265 for &byte in self.as_ref() {
266 write!(f, "{byte:02X}")?;
267 }
268
269 f.write_str(")")
270 }
271}
272
273#[cfg(feature = "pem")]
274#[cfg(test)]
275mod tests {
276 use super::{KeypairBytes, PublicKeyBytes};
277 use hex_literal::hex;
278
279 const SECRET_KEY_BYTES: [u8; 57] = hex!(
280 "8A57471AA375074DC7D75EA2252E9933BB15C107E4F9A2F9CFEA6C418BEBB0774D1ABB671B58B96EFF95F35D63F2418422A59C7EAE3E00D70F"
281 );
282
283 const PUBLIC_KEY_BYTES: [u8; 57] = hex!(
284 "f27f9809412035541b681c69fbe69b9d25a6af506d914ecef7d973fca04ccd33a8b96a0868211382ca08fe06b72e8c0cb3297f3a9d6bc02380"
285 );
286
287 #[test]
288 fn to_bytes() {
289 let valid_keypair = KeypairBytes {
290 secret_key: SECRET_KEY_BYTES,
291 public_key: Some(PublicKeyBytes(PUBLIC_KEY_BYTES)),
292 };
293
294 assert_eq!(
295 valid_keypair.to_bytes().expect("to_bytes"),
296 hex!(
297 "8A57471AA375074DC7D75EA2252E9933BB15C107E4F9A2F9CFEA6C418BEBB0774D1ABB671B58B96EFF95F35D63F2418422A59C7EAE3E00D70Ff27f9809412035541b681c69fbe69b9d25a6af506d914ecef7d973fca04ccd33a8b96a0868211382ca08fe06b72e8c0cb3297f3a9d6bc02380"
298 )
299 );
300
301 let invalid_keypair = KeypairBytes {
302 secret_key: SECRET_KEY_BYTES,
303 public_key: None,
304 };
305
306 assert_eq!(invalid_keypair.to_bytes(), None);
307 }
308}