licenz_core/crypto/
ed25519.rs1use 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
21pub struct Ed25519Signer;
23
24impl Default for Ed25519Signer {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl Ed25519Signer {
31 pub fn new() -> Self {
33 Self
34 }
35
36 fn parse_private_key(pem_str: &str) -> Result<SigningKey> {
40 let pem_str = pem_str.replace("\\n", "\n");
42
43 if let Ok(key) = SigningKey::from_pkcs8_pem(&pem_str) {
45 return Ok(key);
46 }
47
48 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 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 fn parse_public_key(pem_str: &str) -> Result<VerifyingKey> {
80 let pem_str = pem_str.replace("\\n", "\n");
82
83 if let Ok(key) = VerifyingKey::from_public_key_pem(&pem_str) {
85 return Ok(key);
86 }
87
88 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 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 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 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 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 let signing_key = Ed25519Signer::parse_private_key(&private_pem).unwrap();
264 let verifying_key = Ed25519Signer::parse_public_key(&public_pem).unwrap();
265
266 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}