1use ed25519_zebra::{batch, Signature, VerificationKey};
2use rand_core::CryptoRngCore;
3
4use crate::errors::{CryptoError, CryptoResult};
5
6pub const EDDSA_PUBKEY_LEN: usize = 32;
8
9pub fn ed25519_verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> CryptoResult<bool> {
20 let signature = read_signature(signature)?;
22 let pubkey = read_pubkey(public_key)?;
23
24 match VerificationKey::try_from(pubkey)
26 .and_then(|vk| vk.verify(&Signature::from(signature), message))
27 {
28 Ok(()) => Ok(true),
29 Err(_) => Ok(false),
30 }
31}
32
33pub fn ed25519_batch_verify<R>(
62 rng: &mut R,
63 messages: &[&[u8]],
64 signatures: &[&[u8]],
65 public_keys: &[&[u8]],
66) -> CryptoResult<bool>
67where
68 R: CryptoRngCore,
69{
70 let messages_len = messages.len();
72 let signatures_len = signatures.len();
73 let public_keys_len = public_keys.len();
74
75 let mut messages = messages.to_vec();
76 let mut public_keys = public_keys.to_vec();
77 if messages_len == signatures_len && messages_len == public_keys_len { } else if messages_len == 1 && signatures_len == public_keys_len {
79 messages = messages.repeat(signatures_len);
81 } else if public_keys_len == 1 && messages_len == signatures_len {
82 public_keys = public_keys.repeat(messages_len);
84 } else {
85 return Err(CryptoError::batch_err(
86 "Mismatched / erroneous number of messages / signatures / public keys",
87 ));
88 }
89 debug_assert_eq!(messages.len(), signatures_len);
90 debug_assert_eq!(messages.len(), public_keys.len());
91
92 let mut batch = batch::Verifier::new();
93
94 for ((&message, &signature), &public_key) in messages
95 .iter()
96 .zip(signatures.iter())
97 .zip(public_keys.iter())
98 {
99 let signature = read_signature(signature)?;
101 let pubkey = read_pubkey(public_key)?;
102
103 batch.queue((pubkey.into(), signature.into(), message));
105 }
106
107 match batch.verify(rng) {
109 Ok(()) => Ok(true),
110 Err(_) => Ok(false),
111 }
112}
113
114struct InvalidEd25519SignatureFormat;
116
117impl From<InvalidEd25519SignatureFormat> for CryptoError {
118 fn from(_original: InvalidEd25519SignatureFormat) -> Self {
119 CryptoError::invalid_signature_format()
120 }
121}
122
123fn read_signature(data: &[u8]) -> Result<[u8; 64], InvalidEd25519SignatureFormat> {
124 data.try_into().map_err(|_| InvalidEd25519SignatureFormat)
125}
126
127struct InvalidEd25519PubkeyFormat;
129
130impl From<InvalidEd25519PubkeyFormat> for CryptoError {
131 fn from(_original: InvalidEd25519PubkeyFormat) -> Self {
132 CryptoError::invalid_pubkey_format()
133 }
134}
135
136fn read_pubkey(data: &[u8]) -> Result<[u8; 32], InvalidEd25519PubkeyFormat> {
137 data.try_into().map_err(|_| InvalidEd25519PubkeyFormat)
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use alloc::{string::String, vec, vec::Vec};
144 use ed25519_zebra::SigningKey;
145 use rand_core::OsRng;
146 use serde::Deserialize;
147
148 const MSG: &str = "Hello World!";
150
151 const COSMOS_ED25519_MSG: &str = "";
154 const COSMOS_ED25519_PRIVATE_KEY_HEX: &str =
155 "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60";
156 const COSMOS_ED25519_PUBLIC_KEY_HEX: &str =
157 "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
158 const COSMOS_ED25519_SIGNATURE_HEX: &str = "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b";
159
160 const COSMOS_ED25519_TESTS_JSON: &str = "./testdata/ed25519_tests.json";
162
163 #[derive(Deserialize, Debug)]
164 struct Encoded {
165 #[serde(rename = "privkey")]
166 #[allow(dead_code)]
167 private_key: String,
168 #[serde(rename = "pubkey")]
169 public_key: String,
170 message: String,
171 signature: String,
172 }
173
174 fn read_cosmos_sigs() -> Vec<Encoded> {
175 use std::fs::File;
176 use std::io::BufReader;
177
178 let file = File::open(COSMOS_ED25519_TESTS_JSON).unwrap();
180 let reader = BufReader::new(file);
181
182 serde_json::from_reader(reader).unwrap()
183 }
184
185 #[test]
186 fn test_ed25519_verify() {
187 let message = MSG.as_bytes();
188 let secret_key = SigningKey::new(OsRng);
190 let signature = secret_key.sign(message);
191
192 let public_key = VerificationKey::from(&secret_key);
193
194 let signature_bytes: [u8; 64] = signature.into();
196 let public_key_bytes: [u8; 32] = public_key.into();
197
198 assert!(ed25519_verify(message, &signature_bytes, &public_key_bytes).unwrap());
200
201 let bad_message = [message, b"\0"].concat();
203 assert!(!ed25519_verify(&bad_message, &signature_bytes, &public_key_bytes).unwrap());
204
205 let other_secret_key = SigningKey::new(OsRng);
207 let other_public_key = VerificationKey::from(&other_secret_key);
208 let other_public_key_bytes: [u8; 32] = other_public_key.into();
209 assert!(!ed25519_verify(message, &signature_bytes, &other_public_key_bytes).unwrap());
210 }
211
212 #[test]
213 fn test_cosmos_ed25519_verify() {
214 let secret_key = SigningKey::try_from(
215 hex::decode(COSMOS_ED25519_PRIVATE_KEY_HEX)
216 .unwrap()
217 .as_slice(),
218 )
219 .unwrap();
220 let public_key = VerificationKey::try_from(
221 hex::decode(COSMOS_ED25519_PUBLIC_KEY_HEX)
222 .unwrap()
223 .as_slice(),
224 )
225 .unwrap();
226 let signature = secret_key.sign(COSMOS_ED25519_MSG.as_bytes());
227
228 let signature_bytes: [u8; 64] = signature.into();
229 let public_key_bytes: [u8; 32] = public_key.into();
230
231 assert_eq!(
232 signature_bytes,
233 hex::decode(COSMOS_ED25519_SIGNATURE_HEX)
234 .unwrap()
235 .as_slice()
236 );
237
238 assert!(ed25519_verify(
239 COSMOS_ED25519_MSG.as_bytes(),
240 &signature_bytes,
241 &public_key_bytes
242 )
243 .unwrap());
244 }
245
246 #[test]
247 fn test_cosmos_extra_ed25519_verify() {
248 let codes = read_cosmos_sigs();
249
250 for (i, encoded) in (1..).zip(codes) {
251 let message = hex::decode(&encoded.message).unwrap();
252
253 let signature = hex::decode(&encoded.signature).unwrap();
254
255 let public_key = hex::decode(&encoded.public_key).unwrap();
256
257 assert!(
259 ed25519_verify(&message, &signature, &public_key).unwrap(),
260 "verify() failed (test case {i})"
261 );
262 }
263 }
264
265 #[test]
266 fn test_cosmos_ed25519_batch_verify() {
267 let codes = read_cosmos_sigs();
268
269 let mut messages: Vec<Vec<u8>> = vec![];
270 let mut signatures: Vec<Vec<u8>> = vec![];
271 let mut public_keys: Vec<Vec<u8>> = vec![];
272
273 for encoded in codes {
274 let message = hex::decode(&encoded.message).unwrap();
275 messages.push(message);
276
277 let signature = hex::decode(&encoded.signature).unwrap();
278 signatures.push(signature);
279
280 let public_key = hex::decode(&encoded.public_key).unwrap();
281 public_keys.push(public_key);
282 }
283
284 let messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
285 let signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
286 let public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
287
288 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
290 }
291
292 #[test]
294 fn test_cosmos_ed25519_batch_verify_empty_works() {
295 let messages: Vec<&[u8]> = vec![];
296 let signatures: Vec<&[u8]> = vec![];
297 let public_keys: Vec<&[u8]> = vec![];
298
299 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
301 }
302
303 #[test]
304 fn test_cosmos_ed25519_batch_verify_wrong_number_of_items_errors() {
305 let codes = read_cosmos_sigs();
306
307 let mut messages: Vec<Vec<u8>> = vec![];
308 let mut signatures: Vec<Vec<u8>> = vec![];
309 let mut public_keys: Vec<Vec<u8>> = vec![];
310
311 for encoded in codes {
312 let message = hex::decode(&encoded.message).unwrap();
313 messages.push(message);
314
315 let signature = hex::decode(&encoded.signature).unwrap();
316 signatures.push(signature);
317
318 let public_key = hex::decode(&encoded.public_key).unwrap();
319 public_keys.push(public_key);
320 }
321
322 let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
323 let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
324 let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
325
326 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
328
329 let msg = messages.pop().unwrap();
331
332 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
333 match res.unwrap_err() {
334 CryptoError::BatchErr { msg, .. } => assert_eq!(
335 msg,
336 "Mismatched / erroneous number of messages / signatures / public keys"
337 ),
338 _ => panic!("Wrong error message"),
339 }
340
341 messages.push(msg);
343
344 let sig = signatures.pop().unwrap();
346
347 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
348 match res.unwrap_err() {
349 CryptoError::BatchErr { msg, .. } => assert_eq!(
350 msg,
351 "Mismatched / erroneous number of messages / signatures / public keys"
352 ),
353 _ => panic!("Wrong error message"),
354 }
355
356 signatures.push(sig);
358
359 let pubkey = public_keys.pop().unwrap();
361
362 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
363 match res.unwrap_err() {
364 CryptoError::BatchErr { msg, .. } => assert_eq!(
365 msg,
366 "Mismatched / erroneous number of messages / signatures / public keys"
367 ),
368 _ => panic!("Wrong error message"),
369 }
370
371 public_keys.push(pubkey);
373
374 messages.push(messages[0]);
376
377 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
378 match res.unwrap_err() {
379 CryptoError::BatchErr { msg, .. } => assert_eq!(
380 msg,
381 "Mismatched / erroneous number of messages / signatures / public keys"
382 ),
383 _ => panic!("Wrong error message"),
384 }
385
386 messages.pop();
388
389 signatures.push(signatures[0]);
391 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
392 match res.unwrap_err() {
393 CryptoError::BatchErr { msg, .. } => assert_eq!(
394 msg,
395 "Mismatched / erroneous number of messages / signatures / public keys"
396 ),
397 _ => panic!("Wrong error message"),
398 }
399
400 signatures.pop();
402
403 public_keys.push(public_keys[0]);
405 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
406 match res.unwrap_err() {
407 CryptoError::BatchErr { msg, .. } => assert_eq!(
408 msg,
409 "Mismatched / erroneous number of messages / signatures / public keys"
410 ),
411 _ => panic!("Wrong error message"),
412 }
413 }
414
415 #[test]
416 fn test_cosmos_ed25519_batch_verify_one_msg_different_number_of_sigs_pubkeys_errors() {
417 let codes = read_cosmos_sigs();
418
419 let mut messages: Vec<Vec<u8>> = vec![];
420 let mut signatures: Vec<Vec<u8>> = vec![];
421 let mut public_keys: Vec<Vec<u8>> = vec![];
422
423 for encoded in codes {
424 let message = hex::decode(&encoded.message).unwrap();
425 messages.push(message);
426
427 let signature = hex::decode(&encoded.signature).unwrap();
428 signatures.push(signature);
429
430 let public_key = hex::decode(&encoded.public_key).unwrap();
431 public_keys.push(public_key);
432 }
433
434 let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
435 let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
436 let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
437
438 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
440
441 messages.truncate(1);
443
444 assert!(!ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
446
447 let sig = signatures.pop().unwrap();
449
450 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
451 match res.unwrap_err() {
452 CryptoError::BatchErr { msg, .. } => assert_eq!(
453 msg,
454 "Mismatched / erroneous number of messages / signatures / public keys"
455 ),
456 _ => panic!("Wrong error message"),
457 }
458
459 signatures.push(sig);
461
462 let pubkey = public_keys.pop().unwrap();
464
465 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
466 match res.unwrap_err() {
467 CryptoError::BatchErr { msg, .. } => assert_eq!(
468 msg,
469 "Mismatched / erroneous number of messages / signatures / public keys"
470 ),
471 _ => panic!("Wrong error message"),
472 }
473
474 public_keys.push(pubkey);
476 }
477
478 #[test]
479 fn test_cosmos_ed25519_batch_verify_one_pubkey_different_number_of_msgs_sigs_errors() {
480 let codes = read_cosmos_sigs();
481
482 let mut messages: Vec<Vec<u8>> = vec![];
483 let mut signatures: Vec<Vec<u8>> = vec![];
484 let mut public_keys: Vec<Vec<u8>> = vec![];
485
486 for encoded in codes {
487 let message = hex::decode(&encoded.message).unwrap();
488 messages.push(message);
489
490 let signature = hex::decode(&encoded.signature).unwrap();
491 signatures.push(signature);
492
493 let public_key = hex::decode(&encoded.public_key).unwrap();
494 public_keys.push(public_key);
495 }
496
497 let mut messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
498 let mut signatures: Vec<&[u8]> = signatures.iter().map(|m| m.as_slice()).collect();
499 let mut public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
500
501 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
503
504 public_keys.truncate(1);
506
507 assert!(!ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
509
510 let sig = signatures.pop().unwrap();
512
513 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
514 match res.unwrap_err() {
515 CryptoError::BatchErr { msg, .. } => assert_eq!(
516 msg,
517 "Mismatched / erroneous number of messages / signatures / public keys"
518 ),
519 _ => panic!("Wrong error message"),
520 }
521
522 signatures.push(sig);
524
525 let msg = messages.pop().unwrap();
527
528 let res = ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys);
529 match res.unwrap_err() {
530 CryptoError::BatchErr { msg, .. } => assert_eq!(
531 msg,
532 "Mismatched / erroneous number of messages / signatures / public keys"
533 ),
534 _ => panic!("Wrong error message"),
535 }
536
537 messages.push(msg);
539 }
540
541 #[test]
542 fn test_cosmos_ed25519_batch_verify_one_msg_zero_sigs_pubkeys_works() {
543 let codes = read_cosmos_sigs();
544
545 let mut messages: Vec<Vec<u8>> = vec![];
546 let signatures: Vec<&[u8]> = vec![];
548 let public_keys: Vec<&[u8]> = vec![];
549
550 for encoded in codes[..1].iter() {
552 let message = hex::decode(&encoded.message).unwrap();
553 messages.push(message);
554 }
555 let messages: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
556
557 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
559 }
560
561 #[test]
562 fn test_cosmos_ed25519_batch_verify_one_pubkey_zero_msgs_sigs_works() {
563 let codes = read_cosmos_sigs();
564
565 let messages: Vec<&[u8]> = vec![];
567 let signatures: Vec<&[u8]> = vec![];
568 let mut public_keys: Vec<Vec<u8>> = vec![];
569
570 for encoded in codes[..1].iter() {
572 let public_key = hex::decode(&encoded.public_key).unwrap();
573 public_keys.push(public_key);
574 }
575 let public_keys: Vec<&[u8]> = public_keys.iter().map(|m| m.as_slice()).collect();
576
577 assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
579 }
580}