Skip to main content

crypto/
ed25519.rs

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