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