1use crate::pem;
26use base64::Engine;
27use base64::engine::general_purpose::STANDARD as BASE64;
28use der::asn1::OctetStringRef;
29use der::{Decode, Encode};
30use ed25519_dalek::pkcs8::{DecodePublicKey, EncodePublicKey};
31use ed25519_dalek::{Signer, Verifier};
32use pkcs8::PrivateKeyInfoRef;
33use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
34use sha2::Digest;
35use spki::der::AnyRef;
36use spki::der::asn1::BitStringRef;
37use spki::{ObjectIdentifier, SubjectPublicKeyInfo};
38use zeroize::Zeroizing;
39
40pub const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112");
42
43pub const SECRET_KEY_SIZE: usize = 32;
45
46pub const PUBLIC_KEY_SIZE: usize = 32;
48
49pub const SIGNATURE_SIZE: usize = 64;
51
52pub const FINGERPRINT_SIZE: usize = 32;
54
55#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
57pub enum Error {
58 #[error("pem: {0}")]
61 Pem(#[from] pem::Error),
62 #[error("invalid PEM tag {0}")]
66 UnexpectedPemTag(String),
67 #[error("not an Ed25519 key")]
71 UnexpectedAlgorithm,
72 #[error("malformed key: {0}")]
77 MalformedKey(String),
78 #[error("trailing data in key encoding")]
83 TrailingData,
84 #[error("signature verification failed")]
87 InvalidSignature,
88}
89
90#[derive(Clone)]
92pub struct SecretKey {
93 inner: ed25519_dalek::SigningKey,
94}
95
96impl SecretKey {
97 pub fn generate() -> SecretKey {
99 let mut seed = Zeroizing::new([0u8; SECRET_KEY_SIZE]);
100 getrandom::fill(seed.as_mut()).unwrap();
101
102 Self::from_bytes(&seed)
103 }
104
105 pub fn from_bytes(bin: &[u8; SECRET_KEY_SIZE]) -> Self {
107 let key = Zeroizing::new(ed25519_dalek::SecretKey::from(*bin));
108 let sig = ed25519_dalek::SigningKey::from(&*key);
109 Self { inner: sig }
110 }
111
112 pub fn from_der(der: &[u8]) -> Result<Self, Error> {
114 let info =
115 PrivateKeyInfoRef::try_from(der).map_err(|err| Error::MalformedKey(err.to_string()))?;
116
117 let length = info
119 .encoded_len()
120 .map_err(|err| Error::MalformedKey(err.to_string()))?;
121 if length.try_into() != Ok(der.len()) {
122 return Err(Error::TrailingData);
123 }
124 if info.algorithm.oid != OID {
126 return Err(Error::UnexpectedAlgorithm);
127 }
128 if info.public_key.is_some() {
132 return Err(Error::MalformedKey("unsupported PKCS#8 version".into()));
133 }
134 if info.algorithm.parameters.is_some() {
136 return Err(Error::MalformedKey(
137 "unexpected algorithm parameters".into(),
138 ));
139 }
140 let seed = <&OctetStringRef>::from_der(info.private_key.as_bytes())
142 .map_err(|err| Error::MalformedKey(err.to_string()))?;
143 let seed: Zeroizing<[u8; SECRET_KEY_SIZE]> = Zeroizing::new(
144 seed.as_bytes()
145 .try_into()
146 .map_err(|_| Error::MalformedKey("seed not 32 bytes".into()))?,
147 );
148 Ok(Self::from_bytes(&seed))
149 }
150
151 pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
153 let (kind, data) = pem::decode(pem_str.as_bytes())?;
154 if kind != "PRIVATE KEY" {
155 return Err(Error::UnexpectedPemTag(kind));
156 }
157 Self::from_der(&data)
158 }
159
160 pub fn to_bytes(&self) -> Zeroizing<[u8; SECRET_KEY_SIZE]> {
162 Zeroizing::new(self.inner.to_bytes())
163 }
164
165 pub fn to_der(&self) -> Zeroizing<Vec<u8>> {
167 let seed = self.to_bytes();
169 let inner = Zeroizing::new(
170 OctetStringRef::new(seed.as_slice())
171 .unwrap()
172 .to_der()
173 .unwrap(),
174 );
175
176 let alg = pkcs8::AlgorithmIdentifierRef {
177 oid: OID,
178 parameters: None::<AnyRef>,
179 };
180 let info = PrivateKeyInfoRef {
181 algorithm: alg,
182 private_key: OctetStringRef::new(&inner).unwrap(),
183 public_key: None,
184 };
185 Zeroizing::new(info.to_der().unwrap())
186 }
187
188 pub fn to_pem(&self) -> Zeroizing<String> {
190 Zeroizing::new(pem::encode("PRIVATE KEY", &self.to_der()))
191 }
192
193 pub fn public_key(&self) -> PublicKey {
195 PublicKey {
196 inner: self.inner.verifying_key(),
197 }
198 }
199
200 pub fn fingerprint(&self) -> Fingerprint {
203 self.public_key().fingerprint()
204 }
205
206 pub fn sign(&self, message: &[u8]) -> Signature {
208 Signature(self.inner.sign(message).to_bytes())
209 }
210}
211
212#[derive(Debug, Clone)]
214pub struct PublicKey {
215 inner: ed25519_dalek::VerifyingKey,
216}
217
218impl PublicKey {
219 pub fn from_bytes(bin: &[u8; PUBLIC_KEY_SIZE]) -> Result<Self, Error> {
221 let inner = ed25519_dalek::VerifyingKey::from_bytes(bin)
222 .map_err(|err| Error::MalformedKey(err.to_string()))?;
223 Ok(Self { inner })
224 }
225
226 pub fn from_der(der: &[u8]) -> Result<Self, Error> {
228 let info: SubjectPublicKeyInfo<AnyRef, BitStringRef> = SubjectPublicKeyInfo::from_der(der)
230 .map_err(|err| Error::MalformedKey(err.to_string()))?;
231 let length = info
232 .encoded_len()
233 .map_err(|err| Error::MalformedKey(err.to_string()))?;
234 if length.try_into() != Ok(der.len()) {
235 return Err(Error::TrailingData);
236 }
237 if info.algorithm.oid != OID {
239 return Err(Error::UnexpectedAlgorithm);
240 }
241 if info.algorithm.parameters.is_some() {
243 return Err(Error::MalformedKey(
244 "unexpected algorithm parameters".into(),
245 ));
246 }
247 let inner = ed25519_dalek::VerifyingKey::from_public_key_der(der)
248 .map_err(|err| Error::MalformedKey(err.to_string()))?;
249 Ok(Self { inner })
250 }
251
252 pub fn from_pem(pem_str: &str) -> Result<Self, Error> {
254 let (kind, data) = pem::decode(pem_str.as_bytes())?;
255 if kind != "PUBLIC KEY" {
256 return Err(Error::UnexpectedPemTag(kind));
257 }
258 Self::from_der(&data)
259 }
260
261 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
263 self.inner.to_bytes()
264 }
265
266 pub fn to_der(&self) -> Vec<u8> {
268 self.inner.to_public_key_der().unwrap().as_bytes().to_vec()
269 }
270
271 pub fn to_pem(&self) -> String {
273 pem::encode("PUBLIC KEY", &self.to_der())
274 }
275
276 pub fn fingerprint(&self) -> Fingerprint {
279 let mut hasher = sha2::Sha256::new();
280 hasher.update(self.to_bytes());
281 Fingerprint(hasher.finalize().into())
282 }
283
284 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), Error> {
286 let sig = ed25519_dalek::Signature::from_bytes(&signature.to_bytes());
287 self.inner
288 .verify(message, &sig)
289 .map_err(|_| Error::InvalidSignature)
290 }
291}
292
293impl Serialize for PublicKey {
294 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
295 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
296 }
297}
298
299impl<'de> Deserialize<'de> for PublicKey {
300 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
301 let s = String::deserialize(deserializer)?;
302 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
303 let arr: [u8; PUBLIC_KEY_SIZE] = bytes
304 .try_into()
305 .map_err(|_| de::Error::custom("invalid public key length"))?;
306 PublicKey::from_bytes(&arr).map_err(de::Error::custom)
307 }
308}
309
310#[cfg(feature = "cbor")]
311impl crate::cbor::Encode for PublicKey {
312 fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
313 self.to_bytes().encode_cbor_to(buf)
314 }
315}
316
317#[cfg(feature = "cbor")]
318impl crate::cbor::Decode for PublicKey {
319 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
320 let bytes = <[u8; PUBLIC_KEY_SIZE]>::decode_cbor(data)?;
321 Self::from_bytes(&bytes).map_err(|e| crate::cbor::Error::DecodeFailed(e.to_string()))
322 }
323
324 fn decode_cbor_notrail(
325 decoder: &mut crate::cbor::Decoder<'_>,
326 ) -> Result<Self, crate::cbor::Error> {
327 let bytes = decoder.decode_bytes_fixed::<PUBLIC_KEY_SIZE>()?;
328 Self::from_bytes(&bytes).map_err(|e| crate::cbor::Error::DecodeFailed(e.to_string()))
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub struct Signature([u8; SIGNATURE_SIZE]);
335
336impl Signature {
337 pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Self {
339 Self(*bytes)
340 }
341
342 pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
344 self.0
345 }
346}
347
348impl Serialize for Signature {
349 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
350 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
351 }
352}
353
354impl<'de> Deserialize<'de> for Signature {
355 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
356 let s = String::deserialize(deserializer)?;
357 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
358 let arr: [u8; SIGNATURE_SIZE] = bytes
359 .try_into()
360 .map_err(|_| de::Error::custom("invalid signature length"))?;
361 Ok(Signature::from_bytes(&arr))
362 }
363}
364
365#[cfg(feature = "cbor")]
366impl crate::cbor::Encode for Signature {
367 fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
368 self.to_bytes().encode_cbor_to(buf)
369 }
370}
371
372#[cfg(feature = "cbor")]
373impl crate::cbor::Decode for Signature {
374 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
375 let bytes = <[u8; SIGNATURE_SIZE]>::decode_cbor(data)?;
376 Ok(Self::from_bytes(&bytes))
377 }
378
379 fn decode_cbor_notrail(
380 decoder: &mut crate::cbor::Decoder<'_>,
381 ) -> Result<Self, crate::cbor::Error> {
382 let bytes = decoder.decode_bytes_fixed::<SIGNATURE_SIZE>()?;
383 Ok(Self::from_bytes(&bytes))
384 }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389pub struct Fingerprint([u8; FINGERPRINT_SIZE]);
390
391impl Fingerprint {
392 pub fn from_bytes(bytes: &[u8; FINGERPRINT_SIZE]) -> Self {
394 Self(*bytes)
395 }
396
397 pub fn to_bytes(&self) -> [u8; FINGERPRINT_SIZE] {
399 self.0
400 }
401}
402
403impl Serialize for Fingerprint {
404 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
405 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
406 }
407}
408
409impl<'de> Deserialize<'de> for Fingerprint {
410 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
411 let s = String::deserialize(deserializer)?;
412 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
413 let arr: [u8; FINGERPRINT_SIZE] = bytes
414 .try_into()
415 .map_err(|_| de::Error::custom("invalid fingerprint length"))?;
416 Ok(Fingerprint::from_bytes(&arr))
417 }
418}
419
420#[cfg(feature = "cbor")]
421impl crate::cbor::Encode for Fingerprint {
422 fn encode_cbor_to(&self, buf: &mut Vec<u8>) -> Result<(), crate::cbor::Error> {
423 self.to_bytes().encode_cbor_to(buf)
424 }
425}
426
427#[cfg(feature = "cbor")]
428impl crate::cbor::Decode for Fingerprint {
429 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
430 let bytes = <[u8; FINGERPRINT_SIZE]>::decode_cbor(data)?;
431 Ok(Self::from_bytes(&bytes))
432 }
433
434 fn decode_cbor_notrail(
435 decoder: &mut crate::cbor::Decoder<'_>,
436 ) -> Result<Self, crate::cbor::Error> {
437 let bytes = decoder.decode_bytes_fixed::<FINGERPRINT_SIZE>()?;
438 Ok(Self::from_bytes(&bytes))
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 mod ietf_vectors {
449 pub const SECKEY_SEED: &str =
450 "d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842";
451
452 pub const SECKEY_PEM: &str = "\
453-----BEGIN PRIVATE KEY-----
454MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
455-----END PRIVATE KEY-----";
456
457 pub const SECKEY_V2_PEM: &str = "\
458-----BEGIN PRIVATE KEY-----
459MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
460oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
461Z9w7lshQhqowtrbLDFw4rXAxZuE=
462-----END PRIVATE KEY-----";
463
464 pub const PUBKEY_PEM: &str = "\
465-----BEGIN PUBLIC KEY-----
466MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
467-----END PUBLIC KEY-----";
468 }
469
470 #[test]
474 fn test_ietf_vectors() {
475 let key = SecretKey::from_pem(ietf_vectors::SECKEY_PEM).unwrap();
477 assert_eq!(hex::encode(*key.to_bytes()), ietf_vectors::SECKEY_SEED);
478 assert_eq!(key.to_pem().trim(), ietf_vectors::SECKEY_PEM.trim());
479
480 let (_, der) = pem::decode(ietf_vectors::SECKEY_PEM.as_bytes()).unwrap();
482 assert_eq!(*key.to_der(), *der);
483
484 assert_eq!(
486 key.public_key().to_pem().trim(),
487 ietf_vectors::PUBKEY_PEM.trim()
488 );
489
490 let key = PublicKey::from_pem(ietf_vectors::PUBKEY_PEM).unwrap();
492 assert_eq!(key.to_pem().trim(), ietf_vectors::PUBKEY_PEM.trim());
493
494 let (_, der) = pem::decode(ietf_vectors::PUBKEY_PEM.as_bytes()).unwrap();
496 assert_eq!(key.to_der(), *der);
497 }
498
499 #[test]
503 fn test_ietf_v2_rejected() {
504 assert!(SecretKey::from_pem(ietf_vectors::SECKEY_V2_PEM).is_err());
505 }
506
507 #[test]
511 fn test_sign_verify() {
512 let secret = SecretKey::generate();
514 let public = secret.public_key();
515
516 struct TestCase<'a> {
518 message: &'a [u8],
519 }
520 let tests = [TestCase {
521 message: b"message to authenticate",
522 }];
523
524 for tt in &tests {
525 let signature = secret.sign(tt.message);
527
528 public
530 .verify(tt.message, &signature)
531 .unwrap_or_else(|e| panic!("failed to verify message: {}", e));
532 }
533 }
534
535 #[test]
538 fn test_publickey_der_rejects_params() {
539 let key = SecretKey::from_bytes(&[7; 32]).public_key();
541 let der = key.to_der();
542
543 let mut info: SubjectPublicKeyInfo<AnyRef, BitStringRef> =
544 SubjectPublicKeyInfo::from_der(&der).unwrap();
545 info.algorithm.parameters = Some(AnyRef::NULL);
546 let der_bad = info.to_der().unwrap();
547
548 let err = PublicKey::from_der(&der_bad);
549 assert!(matches!(err, Err(Error::MalformedKey(_))));
550 }
551
552 #[test]
555 fn test_secretkey_der_rejects_params() {
556 let der = SecretKey::from_bytes(&[7; 32]).to_der().to_vec();
558 let long = der[1] == 0x82;
559 let algid_pos = if long { 7 } else { 5 };
560 let algid_len = der[algid_pos + 1] as usize;
561 let oid_pos = algid_pos + 2;
562 let oid_len = 2 + der[oid_pos + 1] as usize;
563 let after_oid = oid_pos + oid_len;
564
565 let mut bad = der.clone();
566 bad.splice(after_oid..after_oid, [0x05, 0x00]);
567 bad[algid_pos + 1] = (algid_len + 2) as u8;
568 if long {
569 let grown = (((der[2] as usize) << 8) | der[3] as usize) + 2;
570 bad[2] = (grown >> 8) as u8;
571 bad[3] = (grown & 0xff) as u8;
572 } else {
573 bad[1] = (der[1] as usize + 2) as u8;
574 }
575
576 let err = SecretKey::from_der(&bad);
577 assert!(matches!(err, Err(Error::MalformedKey(_))));
578 }
579}