Skip to main content

cosmwasm_crypto/
ed25519.rs

1use ed25519_zebra::{batch, Signature, VerificationKey};
2use rand_core::CryptoRngCore;
3
4use crate::errors::{CryptoError, CryptoResult};
5
6/// Length of a serialized public key
7pub const EDDSA_PUBKEY_LEN: usize = 32;
8
9/// EdDSA ed25519 implementation.
10///
11/// This function verifies messages against a signature, with the public key of the signer,
12/// using the ed25519 elliptic curve digital signature parametrization / algorithm.
13///
14/// The maximum currently supported message length is 4096 bytes.
15/// The signature and public key are in [Tendermint](https://github.com/tendermint/tendermint/blob/v0.32.x/docs/spec/blockchain/encoding.md#public-key-cryptography)
16/// format:
17/// - signature: raw ED25519 signature (64 bytes).
18/// - public key: raw ED25519 public key (32 bytes).
19pub fn ed25519_verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> CryptoResult<bool> {
20    // Validation
21    let signature = read_signature(signature)?;
22    let pubkey = read_pubkey(public_key)?;
23
24    // Verification
25    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
33/// Performs batch Ed25519 signature verification.
34///
35/// Batch verification asks whether all signatures in some set are valid, rather than asking whether
36/// each of them is valid. This allows sharing computations among all signature verifications,
37/// performing less work overall, at the cost of higher latency (the entire batch must complete),
38/// complexity of caller code (which must assemble a batch of signatures across work-items),
39/// and loss of the ability to easily pinpoint failing signatures.
40///
41/// This batch verification implementation is adaptive, in the sense that it detects multiple
42/// signatures created with the same verification key, and automatically coalesces terms
43/// in the final verification equation.
44///
45/// In the limiting case where all signatures in the batch are made with the same verification key,
46/// coalesced batch verification runs twice as fast as ordinary batch verification.
47///
48/// Three Variants are supported in the input for convenience:
49///  - Equal number of messages, signatures, and public keys: Standard, generic functionality.
50///  - One message, and an equal number of signatures and public keys: Multiple digital signature
51///    (multisig) verification of a single message.
52///  - One public key, and an equal number of messages and signatures: Verification of multiple
53///    messages, all signed with the same private key.
54///
55/// Any other variants of input vectors result in an error.
56///
57/// Notes:
58///  - The "one-message, with zero signatures and zero public keys" case, is considered the empty case.
59///  - The "one-public key, with zero messages and zero signatures" case, is considered the empty case.
60///  - The empty case (no messages, no signatures and no public keys) returns true.
61pub 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    // Structural checks
71    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 { // We're good to go
78    } else if messages_len == 1 && signatures_len == public_keys_len {
79        // Replicate message, for multisig
80        messages = messages.repeat(signatures_len);
81    } else if public_keys_len == 1 && messages_len == signatures_len {
82        // Replicate pubkey
83        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        // Validation
100        let signature = read_signature(signature)?;
101        let pubkey = read_pubkey(public_key)?;
102
103        // Enqueuing
104        batch.queue((pubkey.into(), signature.into(), message));
105    }
106
107    // Batch verification
108    match batch.verify(rng) {
109        Ok(()) => Ok(true),
110        Err(_) => Ok(false),
111    }
112}
113
114/// Error raised when signature is not 64 bytes long
115struct 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
127/// Error raised when pubkey is not 32 bytes long
128struct 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    // For generic signature verification
149    const MSG: &str = "Hello World!";
150
151    // Cosmos ed25519 signature verification
152    // TEST 1 from https://tools.ietf.org/html/rfc8032#section-7.1
153    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    // Test data from https://tools.ietf.org/html/rfc8032#section-7.1
161    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        // Open the file in read-only mode with buffer.
179        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        // Signing
189        let secret_key = SigningKey::new(OsRng);
190        let signature = secret_key.sign(message);
191
192        let public_key = VerificationKey::from(&secret_key);
193
194        // Serialization. Types can be converted to raw byte arrays with From/Into
195        let signature_bytes: [u8; 64] = signature.into();
196        let public_key_bytes: [u8; 32] = public_key.into();
197
198        // Verification
199        assert!(ed25519_verify(message, &signature_bytes, &public_key_bytes).unwrap());
200
201        // Wrong message fails
202        let bad_message = [message, b"\0"].concat();
203        assert!(!ed25519_verify(&bad_message, &signature_bytes, &public_key_bytes).unwrap());
204
205        // Other pubkey fails
206        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            // ed25519_verify() works
258            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        // ed25519_batch_verify() works
289        assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
290    }
291
292    // structural tests
293    #[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        // ed25519_batch_verify() works for empty msgs / sigs / pubkeys
300        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        // Check the whole set passes
327        assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
328
329        // Remove one message
330        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        // Restore messages
342        messages.push(msg);
343
344        // Remove one signature
345        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        // Restore signatures
357        signatures.push(sig);
358
359        // Remove one public key
360        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        // Restore public keys
372        public_keys.push(pubkey);
373
374        // Add one message
375        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        // Restore messages
387        messages.pop();
388
389        // Add one signature
390        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        // Restore signatures
401        signatures.pop();
402
403        // Add one public keys
404        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        // Check the whole set passes
439        assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
440
441        // Just one message
442        messages.truncate(1);
443
444        // Check (in passing) this fails verification
445        assert!(!ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
446
447        // Remove one sig
448        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        // Restore signatures
460        signatures.push(sig);
461
462        // Remove one public key
463        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        // Restore public keys
475        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        // Check the whole set passes
502        assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
503
504        // Just one public key
505        public_keys.truncate(1);
506
507        // Check (in passing) this fails verification
508        assert!(!ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
509
510        // Remove one sig
511        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        // Restore signatures
523        signatures.push(sig);
524
525        // Remove one msg
526        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        // Restore messages
538        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        // Zero sigs / pubkeys
547        let signatures: Vec<&[u8]> = vec![];
548        let public_keys: Vec<&[u8]> = vec![];
549
550        // Just one message
551        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        // ed25519_batch_verify() works for empty sigs / pubkeys
558        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        // Zero msgs / sigs
566        let messages: Vec<&[u8]> = vec![];
567        let signatures: Vec<&[u8]> = vec![];
568        let mut public_keys: Vec<Vec<u8>> = vec![];
569
570        // Just one public key
571        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        // ed25519_batch_verify() works for empty msgs / sigs
578        assert!(ed25519_batch_verify(&mut OsRng, &messages, &signatures, &public_keys).unwrap());
579    }
580}