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 getrandom::{SysRng, rand_core::UnwrapErr};
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 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}