• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 extern crate alloc;
16 extern crate std;
17 
18 use crypto_provider::ed25519::{
19     Ed25519Provider, KeyPairImpl, PublicKeyImpl, RawPrivateKeyPermit, RawSignature, SignatureImpl,
20 };
21 use wycheproof::TestResult;
22 
23 // These are test vectors from the creators of Ed25519: https://ed25519.cr.yp.to/ which are referenced
24 // as the SOT for the test vectors in the RFC: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
25 // The vectors have been formatted into a easily parsable/readable format by libgcrypt which is
26 // also used for test cases in the above RFC:
27 // https://dev.gnupg.org/source/libgcrypt/browse/master/tests/t-ed25519.inp
28 const PATH_TO_RFC_VECTORS_FILE: &str =
29     "crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt";
30 
31 /// Runs set of Ed25519 wycheproof test vectors against a provided ed25519 implementation
32 /// Tests vectors from Project Wycheproof: <https://github.com/google/wycheproof>
run_wycheproof_test_vectors<E>() where E: Ed25519Provider,33 pub fn run_wycheproof_test_vectors<E>()
34 where
35     E: Ed25519Provider,
36 {
37     let test_set = wycheproof::eddsa::TestSet::load(wycheproof::eddsa::TestName::Ed25519)
38         .expect("should be able to load test set");
39 
40     for test_group in test_set.test_groups {
41         let public_key = test_group.key.pk.to_vec();
42         for test in test_group.tests {
43             let tc_id = test.tc_id;
44             let comment = test.comment;
45             let sig = test.sig;
46             let msg = test.msg;
47 
48             let valid = match test.result {
49                 TestResult::Invalid => false,
50                 TestResult::Valid | TestResult::Acceptable => true,
51             };
52             let result = run_wycheproof_test::<E>(public_key.to_vec(), sig.to_vec(), msg.to_vec());
53             if valid {
54                 if let Err(desc) = result {
55                     panic!(
56                         "\n\
57                          Failed test {}: {}\n\
58                          msg:\t{:?}\n\
59                          sig:\t{:?}\n\
60                          comment:\t{:?}\n",
61                         tc_id, desc, msg, sig, comment,
62                     );
63                 }
64             } else {
65                 assert!(result.is_err())
66             }
67         }
68     }
69 }
70 
run_wycheproof_test<E>(pub_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>) -> Result<(), &'static str> where E: Ed25519Provider,71 fn run_wycheproof_test<E>(pub_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>) -> Result<(), &'static str>
72 where
73     E: Ed25519Provider,
74 {
75     let pub_key = E::PublicKey::from_bytes(pub_key.as_slice().try_into().unwrap())
76         .map_err(|_| "Invalid public key bytes")?;
77 
78     let raw_sig: RawSignature =
79         sig.as_slice().try_into().map_err(|_| "Invalid length signature")?;
80     let signature = E::Signature::from_bytes(&raw_sig);
81 
82     pub_key.verify_strict(msg.as_slice(), &signature).map_err(|_| "Signature verification failed")
83 }
84 
85 /// Runs the RFC specified test vectors against an Ed25519 implementation
run_rfc_test_vectors<E>() where E: Ed25519Provider,86 pub fn run_rfc_test_vectors<E>()
87 where
88     E: Ed25519Provider,
89 {
90     let file_contents =
91         std::fs::read_to_string(test_helper::get_data_file(PATH_TO_RFC_VECTORS_FILE))
92             .expect("should be able to read file");
93 
94     let mut split_cases: Vec<&str> = file_contents.as_str().split("\n\n").collect();
95     // remove the comments
96     split_cases.remove(0);
97     for case in split_cases {
98         let test_case: Vec<&str> = case.split('\n').collect();
99 
100         let tc_id = extract_string(test_case[0]);
101         let sk = extract_hex(test_case[1]);
102         let pk = extract_hex(test_case[2]);
103         let msg = extract_hex(test_case[3]);
104         let sig = extract_hex(test_case[4]);
105 
106         let result = run_test::<E>(pk.clone(), sk.clone(), sig.clone(), msg.clone());
107         if let Err(desc) = result {
108             panic!(
109                 "\n\
110                          Failed test {}: {}\n\
111                          msg:\t{:?}\n\
112                          sig:\t{:?}\n\"",
113                 tc_id, desc, msg, sig,
114             );
115         }
116     }
117 }
118 
extract_hex(line: &str) -> Vec<u8>119 fn extract_hex(line: &str) -> Vec<u8> {
120     test_helper::string_to_hex(extract_string(line).as_str())
121 }
122 
extract_string(line: &str) -> String123 fn extract_string(line: &str) -> String {
124     line.split(':').collect::<Vec<&str>>()[1].trim().to_owned()
125 }
126 
run_test<E>( expected_pub_key: Vec<u8>, private_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>, ) -> Result<(), &'static str> where E: Ed25519Provider,127 fn run_test<E>(
128     expected_pub_key: Vec<u8>,
129     private_key: Vec<u8>,
130     sig: Vec<u8>,
131     msg: Vec<u8>,
132 ) -> Result<(), &'static str>
133 where
134     E: Ed25519Provider,
135 {
136     let private_key_bytes: [u8; 32] =
137         private_key.as_slice().try_into().expect("Secret key is the wrong length");
138 
139     // Permits: Test-only code, not a production leak of the private key
140     let permit = RawPrivateKeyPermit::default();
141     let kp = E::KeyPair::from_raw_private_key(&private_key_bytes, &permit);
142 
143     let sig_result = kp.sign(msg.as_slice());
144     (sig.as_slice() == sig_result.to_bytes()).then_some(()).ok_or("sig not matching expected")?;
145     let signature = E::Signature::from_bytes(
146         sig.as_slice().try_into().expect("Test signature should be the correct length"),
147     );
148 
149     let pub_key = kp.public_key();
150     assert_eq!(pub_key.to_bytes().as_slice(), expected_pub_key.as_slice());
151     pub_key.verify_strict(msg.as_slice(), &signature).map_err(|_| "verify failed")?;
152 
153     Ok(())
154 }
155