1#[cfg(feature = "software-crypto")]
2use coset::iana;
3#[cfg(feature = "software-crypto")]
4use ed25519_dalek::{Signature, Signer as DalekSigner, SigningKey, Verifier, VerifyingKey};
5
6#[cfg(feature = "software-crypto")]
7use crate::crypto::traits::{SignatureVerifier, Signer};
8#[cfg(feature = "software-crypto")]
9use crate::error::{CryptoError, CryptoResult};
10
11#[cfg(feature = "software-crypto")]
13pub struct Ed25519Verifier {
14 public_key: VerifyingKey,
15}
16
17const ED25519_WEAK_KEY_ZEROS: [u8; 32] = [0u8; 32];
19
20const ED25519_SMALL_ORDER_POINTS: [[u8; 32]; 8] = [
23 [
25 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 0x00, 0x00,
28 ],
29 [
31 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
33 0xff, 0x7f,
34 ],
35 [
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 0x00, 0x80,
40 ],
41 [
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00,
45 ],
46 [
48 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
49 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
50 0x03, 0x7a,
51 ],
52 [
53 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
54 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
55 0x03, 0xfa,
56 ],
57 [
58 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
59 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
60 0xfc, 0x05,
61 ],
62 [
63 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
64 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
65 0xfc, 0x85,
66 ],
67];
68
69#[cfg(feature = "software-crypto")]
70impl Ed25519Verifier {
71 pub fn from_bytes(bytes: &[u8]) -> CryptoResult<Self> {
75 let bytes: [u8; 32] = bytes.try_into().map_err(|_| {
76 CryptoError::InvalidKeyFormat("Ed25519 public key must be 32 bytes".to_string())
77 })?;
78
79 if bytes == ED25519_WEAK_KEY_ZEROS {
81 return Err(CryptoError::InvalidKeyFormat(
82 "weak key rejected: all-zeros public key".to_string(),
83 ));
84 }
85
86 for weak_point in &ED25519_SMALL_ORDER_POINTS {
88 if bytes == *weak_point {
89 return Err(CryptoError::InvalidKeyFormat(
90 "weak key rejected: small-order point".to_string(),
91 ));
92 }
93 }
94
95 let public_key = VerifyingKey::from_bytes(&bytes)
96 .map_err(|e| CryptoError::InvalidKeyFormat(e.to_string()))?;
97
98 Ok(Self { public_key })
99 }
100
101 pub fn from_pem(pem: &str) -> CryptoResult<Self> {
108 use ed25519_dalek::pkcs8::DecodePublicKey;
109
110 let pem = pem.trim();
111
112 if pem.contains("BEGIN PUBLIC KEY") {
114 let public_key = VerifyingKey::from_public_key_pem(pem)
115 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid SPKI PEM: {}", e)))?;
116
117 let bytes = public_key.to_bytes();
119
120 if bytes == ED25519_WEAK_KEY_ZEROS {
121 return Err(CryptoError::InvalidKeyFormat(
122 "weak key rejected: all-zeros public key".to_string(),
123 ));
124 }
125
126 for weak_point in &ED25519_SMALL_ORDER_POINTS {
127 if bytes == *weak_point {
128 return Err(CryptoError::InvalidKeyFormat(
129 "weak key rejected: small-order point".to_string(),
130 ));
131 }
132 }
133
134 return Ok(Self { public_key });
135 }
136
137 use base64::Engine;
139 let bytes = base64::engine::general_purpose::STANDARD
140 .decode(pem)
141 .map_err(|e| CryptoError::InvalidKeyFormat(format!("Invalid base64: {}", e)))?;
142
143 Self::from_bytes(&bytes)
144 }
145}
146
147#[cfg(feature = "software-crypto")]
148impl SignatureVerifier for Ed25519Verifier {
149 fn verify(
150 &self,
151 algorithm: iana::Algorithm,
152 _key_id: Option<&[u8]>,
153 data: &[u8],
154 signature: &[u8],
155 ) -> CryptoResult<()> {
156 if algorithm != iana::Algorithm::EdDSA {
158 return Err(CryptoError::UnsupportedAlgorithm(format!(
159 "{:?}",
160 algorithm
161 )));
162 }
163
164 let signature: [u8; 64] = signature
165 .try_into()
166 .map_err(|_| CryptoError::VerificationFailed)?;
167
168 let signature = Signature::from_bytes(&signature);
169
170 self.public_key
171 .verify(data, &signature)
172 .map_err(|_| CryptoError::VerificationFailed)
173 }
174}
175
176#[cfg(feature = "software-crypto")]
182pub struct Ed25519Signer {
183 signing_key: SigningKey,
184}
185
186#[cfg(feature = "software-crypto")]
187impl Ed25519Signer {
188 pub fn from_bytes(bytes: &[u8]) -> CryptoResult<Self> {
190 let bytes: [u8; 32] = bytes.try_into().map_err(|_| {
191 CryptoError::InvalidKeyFormat("Ed25519 private key must be 32 bytes".to_string())
192 })?;
193
194 let signing_key = SigningKey::from_bytes(&bytes);
195 Ok(Self { signing_key })
196 }
197
198 pub fn generate() -> Self {
200 use rand::rngs::OsRng;
201 let signing_key = SigningKey::generate(&mut OsRng);
202 Self { signing_key }
203 }
204
205 pub fn verifying_key(&self) -> Ed25519Verifier {
207 Ed25519Verifier {
208 public_key: self.signing_key.verifying_key(),
209 }
210 }
211
212 pub fn public_key_bytes(&self) -> [u8; 32] {
214 self.signing_key.verifying_key().to_bytes()
215 }
216}
217
218#[cfg(feature = "software-crypto")]
219impl Signer for Ed25519Signer {
220 fn sign(
221 &self,
222 algorithm: iana::Algorithm,
223 _key_id: Option<&[u8]>,
224 data: &[u8],
225 ) -> CryptoResult<Vec<u8>> {
226 if algorithm != iana::Algorithm::EdDSA {
227 return Err(CryptoError::UnsupportedAlgorithm(format!(
228 "{:?}",
229 algorithm
230 )));
231 }
232
233 let signature = self.signing_key.sign(data);
234 Ok(signature.to_bytes().to_vec())
235 }
236}
237
238#[cfg(all(test, feature = "software-crypto"))]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_ed25519_sign_verify() {
244 let signer = Ed25519Signer::generate();
245 let verifier = signer.verifying_key();
246
247 let data = b"test message to sign";
248 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
249
250 assert!(verifier
251 .verify(iana::Algorithm::EdDSA, None, data, &signature)
252 .is_ok());
253 }
254
255 #[test]
256 fn test_ed25519_verify_wrong_data() {
257 let signer = Ed25519Signer::generate();
258 let verifier = signer.verifying_key();
259
260 let data = b"original message";
261 let wrong_data = b"tampered message";
262 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
263
264 assert!(verifier
265 .verify(iana::Algorithm::EdDSA, None, wrong_data, &signature)
266 .is_err());
267 }
268
269 #[test]
270 fn test_ed25519_wrong_algorithm() {
271 let signer = Ed25519Signer::generate();
272 let verifier = signer.verifying_key();
273
274 let data = b"test message";
275 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
276
277 let result = verifier.verify(iana::Algorithm::ES256, None, data, &signature);
279 assert!(matches!(result, Err(CryptoError::UnsupportedAlgorithm(_))));
280 }
281
282 #[test]
283 fn test_ed25519_from_bytes() {
284 let signer = Ed25519Signer::generate();
285 let public_bytes = signer.public_key_bytes();
286
287 let verifier = Ed25519Verifier::from_bytes(&public_bytes).unwrap();
288
289 let data = b"test data";
290 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
291
292 assert!(verifier
293 .verify(iana::Algorithm::EdDSA, None, data, &signature)
294 .is_ok());
295 }
296
297 #[test]
298 fn test_ed25519_invalid_key_length() {
299 let result = Ed25519Verifier::from_bytes(&[0u8; 16]);
300 assert!(matches!(result, Err(CryptoError::InvalidKeyFormat(_))));
301 }
302
303 #[test]
304 fn test_ed25519_from_pem_spki() {
305 use ed25519_dalek::pkcs8::{spki::der::pem::LineEnding, EncodePublicKey};
306
307 let signer = Ed25519Signer::generate();
308 let pem = signer
309 .signing_key
310 .verifying_key()
311 .to_public_key_pem(LineEnding::LF)
312 .expect("PEM encoding should not fail");
313
314 assert!(pem.contains("-----BEGIN PUBLIC KEY-----"));
316 assert!(pem.contains("-----END PUBLIC KEY-----"));
317
318 let verifier = Ed25519Verifier::from_pem(&pem).unwrap();
320
321 let data = b"test data for PEM verification";
322 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
323
324 assert!(verifier
325 .verify(iana::Algorithm::EdDSA, None, data, &signature)
326 .is_ok());
327 }
328
329 #[test]
330 fn test_ed25519_from_pem_invalid() {
331 let result = Ed25519Verifier::from_pem("not a valid PEM");
333 assert!(result.is_err());
334
335 let invalid_spki =
337 "-----BEGIN PUBLIC KEY-----\nnotvalidbase64!!!\n-----END PUBLIC KEY-----";
338 let result = Ed25519Verifier::from_pem(invalid_spki);
339 assert!(result.is_err());
340 }
341
342 #[test]
343 fn test_ed25519_from_pem_raw_base64() {
344 let signer = Ed25519Signer::generate();
346 let public_bytes = signer.public_key_bytes();
347
348 use base64::Engine;
349 let raw_base64 = base64::engine::general_purpose::STANDARD.encode(public_bytes);
350
351 let verifier = Ed25519Verifier::from_pem(&raw_base64).unwrap();
352
353 let data = b"test data";
354 let signature = signer.sign(iana::Algorithm::EdDSA, None, data).unwrap();
355
356 assert!(verifier
357 .verify(iana::Algorithm::EdDSA, None, data, &signature)
358 .is_ok());
359 }
360
361 #[test]
362 fn test_ed25519_rejects_all_zeros_key() {
363 let zero_key = [0u8; 32];
364 let result = Ed25519Verifier::from_bytes(&zero_key);
365
366 assert!(result.is_err());
367 match result {
368 Err(CryptoError::InvalidKeyFormat(msg)) => {
369 assert!(
370 msg.contains("weak key"),
371 "Error should mention weak key: {}",
372 msg
373 );
374 }
375 _ => panic!("Expected InvalidKeyFormat error"),
376 }
377 }
378
379 #[test]
380 fn test_ed25519_rejects_small_order_points() {
381 let identity = [
383 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385 0x00, 0x00, 0x00, 0x00,
386 ];
387
388 let result = Ed25519Verifier::from_bytes(&identity);
389 assert!(result.is_err());
390 match result {
391 Err(CryptoError::InvalidKeyFormat(msg)) => {
392 assert!(
393 msg.contains("small-order"),
394 "Error should mention small-order: {}",
395 msg
396 );
397 }
398 _ => panic!("Expected InvalidKeyFormat error for identity point"),
399 }
400
401 let order_2 = [
403 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
404 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
405 0xff, 0xff, 0xff, 0x7f,
406 ];
407
408 let result = Ed25519Verifier::from_bytes(&order_2);
409 assert!(result.is_err());
410 }
411}