Skip to main content

licenz_core/crypto/
ed25519.rs

1//! Ed25519 signature algorithm implementation.
2//!
3//! This module implements the `SignatureAlgorithm` trait for Ed25519,
4//! providing a modern, fast, and secure alternative to RSA.
5//!
6//! Ed25519 advantages:
7//! - Much smaller keys and signatures than RSA
8//! - Faster signing and verification
9//! - No padding schemes or key size choices needed
10//! - Strong security with 128-bit security level
11
12use super::SignatureAlgorithm;
13use crate::error::{LicenseError, Result};
14use ed25519_dalek::{
15    pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey},
16    Signature, Signer, SigningKey, Verifier, VerifyingKey, SECRET_KEY_LENGTH,
17};
18use pem::{encode, parse, Pem};
19use rand::rngs::OsRng;
20
21/// Ed25519 signature algorithm implementation
22pub struct Ed25519Signer;
23
24impl Default for Ed25519Signer {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl Ed25519Signer {
31    /// Create a new Ed25519 signer
32    pub fn new() -> Self {
33        Self
34    }
35
36    /// Parse an Ed25519 private key from PEM format
37    ///
38    /// Supports PKCS#8 format ("PRIVATE KEY") and raw Ed25519 format ("ED25519 PRIVATE KEY")
39    fn parse_private_key(pem_str: &str) -> Result<SigningKey> {
40        // Handle escaped newlines
41        let pem_str = pem_str.replace("\\n", "\n");
42
43        // Try PKCS#8 PEM first (standard format)
44        if let Ok(key) = SigningKey::from_pkcs8_pem(&pem_str) {
45            return Ok(key);
46        }
47
48        // Try raw Ed25519 private key format
49        let pem = parse(&pem_str)
50            .map_err(|e| LicenseError::InvalidKeyFormat(format!("Failed to parse PEM: {}", e)))?;
51
52        match pem.tag() {
53            "ED25519 PRIVATE KEY" => {
54                let key_bytes: [u8; SECRET_KEY_LENGTH] =
55                    pem.contents().try_into().map_err(|_| {
56                        LicenseError::InvalidKeyFormat(format!(
57                            "Invalid Ed25519 private key length: expected {}, got {}",
58                            SECRET_KEY_LENGTH,
59                            pem.contents().len()
60                        ))
61                    })?;
62                Ok(SigningKey::from_bytes(&key_bytes))
63            }
64            "PRIVATE KEY" => {
65                // PKCS#8 DER that the pem crate parsed but ed25519-dalek rejected above;
66                // this shouldn't normally happen, but give a clear error
67                Err(LicenseError::InvalidKeyFormat(
68                    "PKCS#8 private key parsed as PEM but could not be decoded as Ed25519".into(),
69                ))
70            }
71            tag => Err(LicenseError::InvalidKeyFormat(format!(
72                "Unexpected PEM tag for Ed25519 private key: {}",
73                tag
74            ))),
75        }
76    }
77
78    /// Parse an Ed25519 public key from PEM format
79    fn parse_public_key(pem_str: &str) -> Result<VerifyingKey> {
80        // Handle escaped newlines
81        let pem_str = pem_str.replace("\\n", "\n");
82
83        // Try SPKI PEM first (standard format)
84        if let Ok(key) = VerifyingKey::from_public_key_pem(&pem_str) {
85            return Ok(key);
86        }
87
88        // Try raw Ed25519 public key format
89        let pem = parse(&pem_str)
90            .map_err(|e| LicenseError::InvalidKeyFormat(format!("Failed to parse PEM: {}", e)))?;
91
92        match pem.tag() {
93            "ED25519 PUBLIC KEY" => {
94                let key_bytes: [u8; 32] = pem.contents().try_into().map_err(|_| {
95                    LicenseError::InvalidKeyFormat(format!(
96                        "Invalid Ed25519 public key length: expected 32, got {}",
97                        pem.contents().len()
98                    ))
99                })?;
100                VerifyingKey::from_bytes(&key_bytes).map_err(|e| {
101                    LicenseError::InvalidKeyFormat(format!("Invalid Ed25519 public key: {}", e))
102                })
103            }
104            "PUBLIC KEY" => Err(LicenseError::InvalidKeyFormat(
105                "SPKI public key parsed as PEM but could not be decoded as Ed25519".into(),
106            )),
107            tag => Err(LicenseError::InvalidKeyFormat(format!(
108                "Unexpected PEM tag for Ed25519 public key: {}",
109                tag
110            ))),
111        }
112    }
113
114    /// Encode a private key to PKCS#8 PEM format
115    fn encode_private_key_pkcs8(signing_key: &SigningKey) -> Result<String> {
116        let der = signing_key.to_pkcs8_der().map_err(|e| {
117            LicenseError::InvalidKeyFormat(format!("Failed to encode PKCS#8 private key: {}", e))
118        })?;
119        Ok(encode(&Pem::new("PRIVATE KEY", der.as_bytes())))
120    }
121
122    /// Encode a public key to SPKI PEM format
123    fn encode_public_key_spki(verifying_key: &VerifyingKey) -> Result<String> {
124        let der = verifying_key.to_public_key_der().map_err(|e| {
125            LicenseError::InvalidKeyFormat(format!("Failed to encode SPKI public key: {}", e))
126        })?;
127        Ok(encode(&Pem::new("PUBLIC KEY", der.as_bytes())))
128    }
129}
130
131impl SignatureAlgorithm for Ed25519Signer {
132    fn algorithm_id(&self) -> &'static str {
133        super::algorithm_ids::ED25519
134    }
135
136    fn sign(&self, data: &[u8], private_key_pem: &str) -> Result<Vec<u8>> {
137        let signing_key = Self::parse_private_key(private_key_pem)?;
138        let signature: Signature = signing_key.sign(data);
139        Ok(signature.to_bytes().to_vec())
140    }
141
142    fn verify(&self, data: &[u8], signature: &[u8], public_key_pem: &str) -> Result<()> {
143        let verifying_key = Self::parse_public_key(public_key_pem)?;
144
145        let sig_bytes: [u8; 64] = signature.try_into().map_err(|_| {
146            LicenseError::VerificationFailed(format!(
147                "Invalid Ed25519 signature length: expected 64, got {}",
148                signature.len()
149            ))
150        })?;
151
152        let signature = Signature::from_bytes(&sig_bytes);
153
154        verifying_key.verify(data, &signature).map_err(|e| {
155            LicenseError::VerificationFailed(format!(
156                "Ed25519 signature verification failed: {}",
157                e
158            ))
159        })
160    }
161
162    fn generate_keypair(&self) -> Result<(String, String)> {
163        let mut csprng = OsRng;
164        let signing_key = SigningKey::generate(&mut csprng);
165        let verifying_key = signing_key.verifying_key();
166
167        let private_pem = Self::encode_private_key_pkcs8(&signing_key)?;
168        let public_pem = Self::encode_public_key_spki(&verifying_key)?;
169
170        Ok((private_pem, public_pem))
171    }
172
173    fn extract_public_key(&self, private_key_pem: &str) -> Result<String> {
174        let signing_key = Self::parse_private_key(private_key_pem)?;
175        let verifying_key = signing_key.verifying_key();
176        Self::encode_public_key_spki(&verifying_key)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_ed25519_signer_algorithm_id() {
186        let signer = Ed25519Signer::new();
187        assert_eq!(signer.algorithm_id(), "Ed25519");
188    }
189
190    #[test]
191    fn test_ed25519_generate_keypair() {
192        let signer = Ed25519Signer::new();
193        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
194
195        assert!(private_pem.contains("PRIVATE KEY"));
196        assert!(public_pem.contains("PUBLIC KEY"));
197    }
198
199    #[test]
200    fn test_ed25519_sign_and_verify() {
201        let signer = Ed25519Signer::new();
202        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
203
204        let data = b"Hello, World!";
205        let signature = signer.sign(data, &private_pem).unwrap();
206
207        // Ed25519 signatures are always 64 bytes
208        assert_eq!(signature.len(), 64);
209        assert!(signer.verify(data, &signature, &public_pem).is_ok());
210    }
211
212    #[test]
213    fn test_ed25519_verify_wrong_data() {
214        let signer = Ed25519Signer::new();
215        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
216
217        let data = b"Hello, World!";
218        let wrong_data = b"Goodbye, World!";
219        let signature = signer.sign(data, &private_pem).unwrap();
220
221        assert!(signer.verify(wrong_data, &signature, &public_pem).is_err());
222    }
223
224    #[test]
225    fn test_ed25519_verify_wrong_key() {
226        let signer = Ed25519Signer::new();
227        let (private_pem, _) = signer.generate_keypair().unwrap();
228        let (_, other_public_pem) = signer.generate_keypair().unwrap();
229
230        let data = b"Hello, World!";
231        let signature = signer.sign(data, &private_pem).unwrap();
232
233        assert!(signer.verify(data, &signature, &other_public_pem).is_err());
234    }
235
236    #[test]
237    fn test_ed25519_extract_public_key() {
238        let signer = Ed25519Signer::new();
239        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
240
241        let extracted = signer.extract_public_key(&private_pem).unwrap();
242        assert_eq!(extracted, public_pem);
243    }
244
245    #[test]
246    fn test_ed25519_signature_size() {
247        let signer = Ed25519Signer::new();
248        let (private_pem, _) = signer.generate_keypair().unwrap();
249
250        let data = b"Test data of various lengths to ensure consistent signature size";
251        let signature = signer.sign(data, &private_pem).unwrap();
252
253        // Ed25519 signatures are always exactly 64 bytes
254        assert_eq!(signature.len(), 64);
255    }
256
257    #[test]
258    fn test_ed25519_key_round_trip() {
259        let signer = Ed25519Signer::new();
260        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
261
262        // Parse and re-encode to verify round-trip
263        let signing_key = Ed25519Signer::parse_private_key(&private_pem).unwrap();
264        let verifying_key = Ed25519Signer::parse_public_key(&public_pem).unwrap();
265
266        // Verify the keys work after parsing
267        let data = b"Round trip test";
268        let signature: Signature = signing_key.sign(data);
269        assert!(verifying_key.verify(data, &signature).is_ok());
270    }
271
272    #[test]
273    fn test_ed25519_empty_data() {
274        let signer = Ed25519Signer::new();
275        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
276
277        let data = b"";
278        let signature = signer.sign(data, &private_pem).unwrap();
279        assert!(signer.verify(data, &signature, &public_pem).is_ok());
280    }
281
282    #[test]
283    fn test_ed25519_large_data() {
284        let signer = Ed25519Signer::new();
285        let (private_pem, public_pem) = signer.generate_keypair().unwrap();
286
287        let data = vec![0xABu8; 10000];
288        let signature = signer.sign(&data, &private_pem).unwrap();
289        assert!(signer.verify(&data, &signature, &public_pem).is_ok());
290    }
291}