• 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 use core::fmt::Debug;
16 
17 /// Collection of types used to provide an implementation of ed25519, the Edwards-curve Digital
18 /// Signature Algorithm scheme using sha-512 (sha2) and Curve25519
19 pub trait Ed25519Provider {
20     /// The keypair which includes both public and secret halves of an asymmetric key.
21     type KeyPair: KeyPair<PublicKey = Self::PublicKey, Signature = Self::Signature>;
22     /// The ed25519 public key, used when verifying a message
23     type PublicKey: PublicKey<Signature = Self::Signature>;
24     /// The ed25519 signature which is the result of signing a message
25     type Signature: Signature;
26 }
27 
28 /// The length of a ed25519 `Signature`, in bytes.
29 pub const SIGNATURE_LENGTH: usize = 64;
30 
31 /// The length of an ed25519 `KeyPair`, in bytes.
32 pub const KEY_PAIR_LENGTH: usize = 64;
33 
34 /// The length of an ed25519 `PublicKey`, in bytes.
35 pub const KEY_LENGTH: usize = 32;
36 
37 /// The keypair which includes both public and secret halves of an asymmetric key.
38 pub trait KeyPair: Sized {
39     /// The ed25519 public key, used when verifying a message
40     type PublicKey: PublicKey;
41 
42     /// The ed25519 signature returned when signing a message
43     type Signature: Signature;
44 
45     /// Converts the key-pair to an array of bytes consisting
46     /// of the bytes of the private key followed by the bytes
47     /// of the public key. This method should only ever be called
48     /// by code which securely stores private credentials.
to_bytes(&self) -> [u8; KEY_PAIR_LENGTH]49     fn to_bytes(&self) -> [u8; KEY_PAIR_LENGTH];
50 
51     /// Builds this key-pair from an array of bytes in the
52     /// format yielded by `to_bytes`. This method should
53     /// only ever be called by code which securely stores private
54     /// credentials.
from_bytes(bytes: [u8; KEY_PAIR_LENGTH]) -> Result<Self, InvalidBytes> where Self: Sized55     fn from_bytes(bytes: [u8; KEY_PAIR_LENGTH]) -> Result<Self, InvalidBytes>
56     where
57         Self: Sized;
58 
59     /// Sign the given message and return a digital signature
sign(&self, msg: &[u8]) -> Self::Signature60     fn sign(&self, msg: &[u8]) -> Self::Signature;
61 
62     /// Generate an ed25519 keypair from a CSPRNG
63     /// generate is not available in `no-std`
64     #[cfg(feature = "std")]
generate() -> Self65     fn generate() -> Self;
66 
67     /// getter function for the Public Key of the key pair
public(&self) -> Self::PublicKey68     fn public(&self) -> Self::PublicKey;
69 }
70 
71 /// An ed25519 signature
72 pub trait Signature: Sized {
73     /// Create a new signature from a byte slice, and return an error on an invalid signature
74     /// An `Ok` result does not guarantee that the Signature is valid, however it will catch a
75     /// number of invalid signatures relatively inexpensively.
from_bytes(bytes: &[u8]) -> Result<Self, InvalidSignature>76     fn from_bytes(bytes: &[u8]) -> Result<Self, InvalidSignature>;
77 
78     /// Returns a slice of the signature bytes
to_bytes(&self) -> [u8; SIGNATURE_LENGTH]79     fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH];
80 }
81 
82 /// An ed25519 public key
83 pub trait PublicKey {
84     /// the signature type being used by verify
85     type Signature: Signature;
86 
87     /// Builds this public key from an array of bytes in
88     /// the format yielded by `to_bytes`.
from_bytes(bytes: [u8; KEY_LENGTH]) -> Result<Self, InvalidBytes> where Self: Sized89     fn from_bytes(bytes: [u8; KEY_LENGTH]) -> Result<Self, InvalidBytes>
90     where
91         Self: Sized;
92 
93     /// Yields the bytes of the public key
to_bytes(&self) -> [u8; KEY_LENGTH]94     fn to_bytes(&self) -> [u8; KEY_LENGTH];
95 
96     /// Succeeds if the signature was a valid signature created by this Keypair on the prehashed_message.
verify_strict( &self, message: &[u8], signature: &Self::Signature, ) -> Result<(), SignatureError>97     fn verify_strict(
98         &self,
99         message: &[u8],
100         signature: &Self::Signature,
101     ) -> Result<(), SignatureError>;
102 }
103 
104 /// error returned when bad bytes are provided to generate keypair
105 #[derive(Debug)]
106 pub struct InvalidBytes;
107 
108 /// Error returned if the verification on the signature + message fails
109 #[derive(Debug)]
110 pub struct SignatureError;
111 
112 /// Error returned if invalid signature bytes are provided
113 #[derive(Debug)]
114 pub struct InvalidSignature;
115 
116 #[cfg(feature = "testing")]
117 /// Utilities for testing. Implementations can use the test cases and functions provided to test
118 /// their implementation.
119 pub mod testing {
120     extern crate alloc;
121     extern crate std;
122 
123     use crate::ed25519::{Ed25519Provider, KeyPair, PublicKey, Signature};
124     use alloc::borrow::ToOwned;
125     use alloc::string::String;
126     use alloc::vec::Vec;
127     use wycheproof::TestResult;
128 
129     // These are test vectors from the creators of Ed25519: https://ed25519.cr.yp.to/ which are referenced
130     // as the SOT for the test vectors in the RFC: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
131     // The vectors have been formatted into a easily parsable/readable format by libgcrypt which is
132     // also used for test cases in the above RFC:
133     // https://dev.gnupg.org/source/libgcrypt/browse/master/tests/t-ed25519.inp
134     const PATH_TO_RFC_VECTORS_FILE: &str =
135         "crypto/crypto_provider/src/testdata/ecdsa/rfc_test_vectors.txt";
136 
137     /// Runs set of Ed25519 wycheproof test vectors against a provided ed25519 implementation
138     /// Tests vectors from Project Wycheproof: <https://github.com/google/wycheproof>
run_wycheproof_test_vectors<E>() where E: Ed25519Provider,139     pub fn run_wycheproof_test_vectors<E>()
140     where
141         E: Ed25519Provider,
142     {
143         let test_set = wycheproof::eddsa::TestSet::load(wycheproof::eddsa::TestName::Ed25519)
144             .expect("should be able to load test set");
145 
146         for test_group in test_set.test_groups {
147             let key_pair = test_group.key;
148             let public_key = key_pair.pk;
149             let secret_key = key_pair.sk;
150 
151             for test in test_group.tests {
152                 let tc_id = test.tc_id;
153                 let comment = test.comment;
154                 let sig = test.sig;
155                 let msg = test.msg;
156 
157                 let valid = match test.result {
158                     TestResult::Invalid => false,
159                     TestResult::Valid | TestResult::Acceptable => true,
160                 };
161                 let result = run_test::<E>(
162                     public_key.clone(),
163                     secret_key.clone(),
164                     sig.clone(),
165                     msg.clone(),
166                 );
167                 if valid {
168                     if let Err(desc) = result {
169                         panic!(
170                             "\n\
171                          Failed test {}: {}\n\
172                          msg:\t{:?}\n\
173                          sig:\t{:?}\n\
174                          comment:\t{:?}\n",
175                             tc_id, desc, msg, sig, comment,
176                         );
177                     }
178                 } else {
179                     assert!(result.is_err())
180                 }
181             }
182         }
183     }
184 
185     /// Runs the RFC specified test vectors against an Ed25519 implementation
run_rfc_test_vectors<E>() where E: Ed25519Provider,186     pub fn run_rfc_test_vectors<E>()
187     where
188         E: Ed25519Provider,
189     {
190         let file_contents =
191             std::fs::read_to_string(test_helper::get_data_file(PATH_TO_RFC_VECTORS_FILE))
192                 .expect("should be able to read file");
193 
194         let mut split_cases: Vec<&str> = file_contents.as_str().split("\n\n").collect();
195         // remove the comments
196         split_cases.remove(0);
197         for case in split_cases {
198             let test_case: Vec<&str> = case.split('\n').collect();
199 
200             let tc_id = extract_string(test_case[0]);
201             let sk = extract_hex(test_case[1]);
202             let pk = extract_hex(test_case[2]);
203             let msg = extract_hex(test_case[3]);
204             let sig = extract_hex(test_case[4]);
205 
206             let result = run_test::<E>(pk.clone(), sk.clone(), sig.clone(), msg.clone());
207             if let Err(desc) = result {
208                 panic!(
209                     "\n\
210                          Failed test {}: {}\n\
211                          msg:\t{:?}\n\
212                          sig:\t{:?}\n\"",
213                     tc_id, desc, msg, sig,
214                 );
215             }
216         }
217     }
218 
extract_hex(line: &str) -> Vec<u8>219     fn extract_hex(line: &str) -> Vec<u8> {
220         test_helper::string_to_hex(extract_string(line).as_str())
221     }
222 
extract_string(line: &str) -> String223     fn extract_string(line: &str) -> String {
224         line.split(':').collect::<Vec<&str>>()[1].trim().to_owned()
225     }
226 
run_test<E>( pub_key: Vec<u8>, secret_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>, ) -> Result<(), &'static str> where E: Ed25519Provider,227     fn run_test<E>(
228         pub_key: Vec<u8>,
229         secret_key: Vec<u8>,
230         sig: Vec<u8>,
231         msg: Vec<u8>,
232     ) -> Result<(), &'static str>
233     where
234         E: Ed25519Provider,
235     {
236         let kp_bytes: [u8; 64] = [secret_key.as_slice(), pub_key.as_slice()]
237             .concat()
238             .try_into()
239             .map_err(|_| "invalid length keypair")?;
240         let kp = E::KeyPair::from_bytes(kp_bytes)
241             .map_err(|_| "Should be able to create Keypair from bytes")?;
242 
243         let sig_result = kp.sign(msg.as_slice());
244         (sig.as_slice() == sig_result.to_bytes())
245             .then_some(())
246             .ok_or("sig not matching expected")?;
247         let signature = E::Signature::from_bytes(sig.as_slice())
248             .map_err(|_| "unable to parse sign from test case")?;
249 
250         let pub_key = kp.public();
251         pub_key
252             .verify_strict(msg.as_slice(), &signature)
253             .map_err(|_| "verify failed")?;
254 
255         Ok(())
256     }
257 }
258