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