1use ed25519_dalek::{Signature, Signer as EdSigner, SigningKey, Verifier, VerifyingKey};
5use getrandom::{SysRng, rand_core::UnwrapErr};
6
7use crate::{Signer, SignerError};
8
9pub struct Ed25519Signer {
11 signing_key: SigningKey,
12 cached_public_key: [u8; 32],
13}
14
15impl Ed25519Signer {
16 pub fn generate() -> Result<Self, SignerError> {
17 let mut rng = UnwrapErr(SysRng);
18 let signing_key = SigningKey::generate(&mut rng);
19 let cached_public_key = signing_key.verifying_key().to_bytes();
20 Ok(Self {
21 signing_key,
22 cached_public_key,
23 })
24 }
25
26 pub fn from_pem(pem: &str) -> Result<Self, SignerError> {
27 use crate::pem_loader::{PemKind, classify_pem};
28
29 match classify_pem(pem) {
30 PemKind::Pkcs8 => Self::from_pkcs8_pem(pem),
31 PemKind::OpenSsh => Self::from_openssh_pem(pem),
32 PemKind::Ed25519HexSeed => {
33 let bytes = hex::decode(pem.trim()).map_err(|e| SignerError::Pem(e.to_string()))?;
34 Self::from_seed(&bytes)
35 }
36 PemKind::Ed25519Base64Seed => {
37 use base64::Engine;
38 let bytes = base64::engine::general_purpose::STANDARD
39 .decode(pem.trim())
40 .map_err(|e| SignerError::Pem(e.to_string()))?;
41 if bytes.len() == 64 {
42 Self::from_seed(&bytes[..32])
43 } else {
44 Self::from_seed(&bytes)
45 }
46 }
47 _ => Err(SignerError::UnknownKeyFormat),
48 }
49 }
50
51 pub fn from_seed(seed: &[u8]) -> Result<Self, SignerError> {
52 let seed_bytes: [u8; 32] = seed
53 .try_into()
54 .map_err(|_| SignerError::InvalidKey("seed must be 32 bytes".to_string()))?;
55 let signing_key = SigningKey::from_bytes(&seed_bytes);
56 let cached_public_key = signing_key.verifying_key().to_bytes();
57 Ok(Self {
58 signing_key,
59 cached_public_key,
60 })
61 }
62
63 fn from_pkcs8_pem(pem: &str) -> Result<Self, SignerError> {
64 use pkcs8::DecodePrivateKey;
65
66 let signing_key = SigningKey::from_pkcs8_pem(pem.trim())?;
67 let cached_public_key = signing_key.verifying_key().to_bytes();
68 Ok(Self {
69 signing_key,
70 cached_public_key,
71 })
72 }
73
74 fn from_openssh_pem(_pem: &str) -> Result<Self, SignerError> {
75 Err(SignerError::Pem(
76 "OpenSSH Ed25519 private keys are not yet supported".to_string(),
77 ))
78 }
79
80 pub fn to_pem(&self) -> Result<String, SignerError> {
81 use pkcs8::EncodePrivateKey;
82
83 self.signing_key
84 .to_pkcs8_pem(pkcs8::LineEnding::LF)
85 .map(|pem| pem.to_string())
86 .map_err(|e| SignerError::Pkcs8(e.to_string()))
87 }
88
89 pub fn verify_with_public_key(
90 data: &[u8],
91 public_key: &[u8],
92 signature: &[u8],
93 ) -> Result<(), SignerError> {
94 let verifying_key = VerifyingKey::from_bytes(public_key.try_into().map_err(|_| {
95 SignerError::InvalidPublicKey("public key must be 32 bytes".to_string())
96 })?)?;
97 let signature = Signature::from_slice(signature)
98 .map_err(|_| SignerError::InvalidSignature("signature must be 64 bytes".to_string()))?;
99 verifying_key
100 .verify(data, &signature)
101 .map_err(|_| SignerError::VerificationFailed)
102 }
103}
104
105impl Signer for Ed25519Signer {
106 fn algorithm(&self) -> &'static str {
107 "ed25519"
108 }
109
110 fn public_key(&self) -> &[u8] {
111 &self.cached_public_key
112 }
113
114 fn sign(&self, data: &[u8]) -> Result<Vec<u8>, SignerError> {
115 let signature = self.signing_key.sign(data);
116 Ok(signature.to_bytes().to_vec())
117 }
118
119 fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), SignerError> {
120 Self::verify_with_public_key(data, self.public_key(), signature)
121 }
122}