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
31use core::fmt;
32
33#[cfg(feature = "pem")]
34use core::str;
35
36#[cfg(feature = "zeroize")]
37use zeroize::Zeroize;
38
39#[cfg(feature = "zerocopy")]
40use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
41
42pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112");
47
48pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifierRef<'static> = pkcs8::AlgorithmIdentifierRef {
50 oid: ALGORITHM_OID,
51 parameters: None,
52};
53
54pub struct KeypairBytes {
71 pub secret_key: [u8; Self::BYTE_SIZE / 2],
77
78 pub public_key: Option<PublicKeyBytes>,
82}
83
84impl KeypairBytes {
85 const BYTE_SIZE: usize = 64;
87
88 #[must_use]
90 #[allow(clippy::missing_panics_doc, reason = "MSRV TODO")]
91 pub fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Self {
92 let (sk, pk) = bytes.split_at(Self::BYTE_SIZE / 2);
94
95 Self {
96 secret_key: sk.try_into().expect("secret key size error"),
97 public_key: Some(PublicKeyBytes(
98 pk.try_into().expect("public key size error"),
99 )),
100 }
101 }
102
103 #[must_use]
109 pub fn to_bytes(&self) -> Option<[u8; Self::BYTE_SIZE]> {
110 if let Some(public_key) = &self.public_key {
111 let mut result = [0u8; Self::BYTE_SIZE];
112 let (sk, pk) = result.split_at_mut(Self::BYTE_SIZE / 2);
113 sk.copy_from_slice(&self.secret_key);
114 pk.copy_from_slice(public_key.as_ref());
115 Some(result)
116 } else {
117 None
118 }
119 }
120}
121
122impl Drop for KeypairBytes {
123 fn drop(&mut self) {
124 #[cfg(feature = "zeroize")]
125 self.secret_key.zeroize();
126 }
127}
128
129#[cfg(feature = "alloc")]
130impl EncodePrivateKey for KeypairBytes {
131 fn to_pkcs8_der(&self) -> Result<SecretDocument> {
132 let mut private_key = [0u8; 2 + (Self::BYTE_SIZE / 2)];
134 private_key[0] = 0x04;
135 private_key[1] = 0x20;
136 private_key[2..].copy_from_slice(&self.secret_key);
137
138 let private_key_info = PrivateKeyInfoRef {
139 algorithm: ALGORITHM_ID,
140 private_key: OctetStringRef::new(&private_key)?,
141 public_key: self
142 .public_key
143 .as_ref()
144 .map(|pk| BitStringRef::new(0, &pk.0))
145 .transpose()?,
146 };
147
148 let result = SecretDocument::encode_msg(&private_key_info)?;
149
150 #[cfg(feature = "zeroize")]
151 private_key.zeroize();
152
153 Ok(result)
154 }
155}
156
157impl TryFrom<PrivateKeyInfoRef<'_>> for KeypairBytes {
158 type Error = Error;
159
160 fn try_from(private_key: PrivateKeyInfoRef<'_>) -> Result<Self> {
161 private_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
162
163 if private_key.algorithm.parameters.is_some() {
164 return Err(Error::ParametersMalformed);
165 }
166
167 let secret_key = match private_key.private_key.as_bytes() {
176 [0x04, 0x20, rest @ ..] => rest.try_into().map_err(|_| KeyError::Invalid),
177 _ => Err(KeyError::Invalid),
178 }?;
179
180 let public_key = private_key
181 .public_key
182 .and_then(|bs| bs.as_bytes())
183 .map(|bytes| bytes.try_into().map_err(|_| KeyError::Invalid))
184 .transpose()?
185 .map(PublicKeyBytes);
186
187 Ok(Self {
188 secret_key,
189 public_key,
190 })
191 }
192}
193
194impl TryFrom<&[u8]> for KeypairBytes {
195 type Error = Error;
196
197 fn try_from(der_bytes: &[u8]) -> Result<Self> {
198 Self::from_pkcs8_der(der_bytes)
199 }
200}
201
202impl fmt::Debug for KeypairBytes {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 f.debug_struct("KeypairBytes")
205 .field("public_key", &self.public_key)
206 .finish_non_exhaustive()
207 }
208}
209
210#[cfg(feature = "pem")]
211impl str::FromStr for KeypairBytes {
212 type Err = Error;
213
214 fn from_str(pem: &str) -> Result<Self> {
215 Self::from_pkcs8_pem(pem)
216 }
217}
218
219#[derive(Clone, Copy, Eq, Hash, PartialEq)]
236#[cfg_attr(
237 feature = "zerocopy",
238 derive(IntoBytes, FromBytes, Unaligned, KnownLayout, Immutable,)
239)]
240#[repr(transparent)]
241pub struct PublicKeyBytes(pub [u8; Self::BYTE_SIZE]);
242
243impl PublicKeyBytes {
244 const BYTE_SIZE: usize = 32;
246
247 #[must_use]
249 pub fn to_bytes(&self) -> [u8; Self::BYTE_SIZE] {
250 self.0
251 }
252}
253
254impl AsRef<[u8; Self::BYTE_SIZE]> for PublicKeyBytes {
255 fn as_ref(&self) -> &[u8; Self::BYTE_SIZE] {
256 &self.0
257 }
258}
259
260#[cfg(feature = "alloc")]
261impl EncodePublicKey for PublicKeyBytes {
262 fn to_public_key_der(&self) -> spki::Result<Document> {
263 pkcs8::SubjectPublicKeyInfoRef {
264 algorithm: ALGORITHM_ID,
265 subject_public_key: BitStringRef::new(0, &self.0)?,
266 }
267 .try_into()
268 }
269}
270
271impl TryFrom<spki::SubjectPublicKeyInfoRef<'_>> for PublicKeyBytes {
272 type Error = spki::Error;
273
274 fn try_from(spki: spki::SubjectPublicKeyInfoRef<'_>) -> spki::Result<Self> {
275 spki.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
276
277 if spki.algorithm.parameters.is_some() {
278 return Err(spki::Error::KeyMalformed);
279 }
280
281 spki.subject_public_key
282 .as_bytes()
283 .ok_or(spki::Error::KeyMalformed)?
284 .try_into()
285 .map(Self)
286 .map_err(|_| spki::Error::KeyMalformed)
287 }
288}
289
290impl TryFrom<&[u8]> for PublicKeyBytes {
291 type Error = spki::Error;
292
293 fn try_from(der_bytes: &[u8]) -> spki::Result<Self> {
294 Self::from_public_key_der(der_bytes)
295 }
296}
297
298impl TryFrom<KeypairBytes> for PublicKeyBytes {
299 type Error = spki::Error;
300
301 fn try_from(keypair: KeypairBytes) -> spki::Result<PublicKeyBytes> {
302 PublicKeyBytes::try_from(&keypair)
303 }
304}
305
306impl TryFrom<&KeypairBytes> for PublicKeyBytes {
307 type Error = spki::Error;
308
309 fn try_from(keypair: &KeypairBytes) -> spki::Result<PublicKeyBytes> {
310 keypair.public_key.ok_or(spki::Error::KeyMalformed)
311 }
312}
313
314impl fmt::Debug for PublicKeyBytes {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 f.write_str("PublicKeyBytes(")?;
317
318 for &byte in self.as_ref() {
319 write!(f, "{byte:02X}")?;
320 }
321
322 f.write_str(")")
323 }
324}
325
326#[cfg(feature = "pem")]
327impl str::FromStr for PublicKeyBytes {
328 type Err = spki::Error;
329
330 fn from_str(pem: &str) -> spki::Result<Self> {
331 Self::from_public_key_pem(pem)
332 }
333}
334
335#[cfg(feature = "pem")]
336impl fmt::Display for PublicKeyBytes {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 f.write_str(
339 &self
340 .to_public_key_pem(Default::default())
341 .expect("PEM serialization error"),
342 )
343 }
344}
345
346#[cfg(feature = "pem")]
347#[cfg(test)]
348mod tests {
349 use super::{KeypairBytes, PublicKeyBytes};
350 use hex_literal::hex;
351
352 const SECRET_KEY_BYTES: [u8; 32] =
353 hex!("D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842");
354
355 const PUBLIC_KEY_BYTES: [u8; 32] =
356 hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1");
357
358 #[test]
359 fn to_bytes() {
360 let valid_keypair = KeypairBytes {
361 secret_key: SECRET_KEY_BYTES,
362 public_key: Some(PublicKeyBytes(PUBLIC_KEY_BYTES)),
363 };
364
365 assert_eq!(
366 valid_keypair.to_bytes().expect("to_bytes"),
367 hex!(
368 "D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F4475584219BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1"
369 )
370 );
371
372 let invalid_keypair = KeypairBytes {
373 secret_key: SECRET_KEY_BYTES,
374 public_key: None,
375 };
376
377 assert_eq!(invalid_keypair.to_bytes(), None);
378 }
379}