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 17 use crate::elliptic_curve::{Curve, EcdhProvider, PublicKey}; 18 use alloc::vec::Vec; 19 use core::fmt::Debug; 20 21 /// Marker type for P256 implementation. This is used by EcdhProvider as its type parameter. 22 #[derive(Debug, PartialEq, Eq)] 23 pub enum P256 {} 24 impl Curve for P256 {} 25 26 /// Trait for a NIST-P256 public key. 27 pub trait P256PublicKey: Sized + PartialEq + Debug { 28 /// The error type associated with this implementation. 29 type Error: Debug; 30 31 /// Creates a public key from the given sec1-encoded bytes, as described in section 2.3.4 of 32 /// the SECG SEC 1 ("Elliptic Curve Cryptography") standard. from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error>33 fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error>; 34 35 /// Serializes this key into sec1-encoded bytes, as described in section 2.3.3 of the SECG SEC 1 36 /// ("Elliptic Curve Cryptography") standard. Note that it is not necessarily true that 37 /// `from_sec1_bytes(bytes)?.to_sec1_bytes() == bytes` because of point compression. (But it is 38 /// always true that `from_sec1_bytes(key.to_sec1_bytes())? == key`). to_sec1_bytes(&self) -> Vec<u8>39 fn to_sec1_bytes(&self) -> Vec<u8>; 40 41 /// Converts this public key's x and y coordinates on the elliptic curve to big endian octet 42 /// strings. to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error>43 fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error>; 44 45 /// Creates a public key from the X and Y coordinates on the elliptic curve. from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error>46 fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error>; 47 } 48 49 impl<P: P256PublicKey> PublicKey<P256> for P { 50 type Error = <Self as P256PublicKey>::Error; 51 from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>52 fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { 53 Self::from_sec1_bytes(bytes) 54 } 55 to_bytes(&self) -> Vec<u8>56 fn to_bytes(&self) -> Vec<u8> { 57 Self::to_sec1_bytes(self) 58 } 59 } 60 61 /// Equivalent to EcdhProvider<P256, PublicKey: P256PublicKey> if associated type bounds are 62 /// supported. 63 pub trait P256EcdhProvider: 64 EcdhProvider<P256, PublicKey = <Self as P256EcdhProvider>::PublicKey> 65 { 66 /// Same as EcdhProvider::PublicKey. 67 type PublicKey: P256PublicKey; 68 } 69 70 impl<E> P256EcdhProvider for E 71 where 72 E: EcdhProvider<P256>, 73 E::PublicKey: P256PublicKey, 74 { 75 type PublicKey = E::PublicKey; 76 } 77 78 /// Utilities for testing. Implementations can use the test cases and functions provided to test 79 /// their implementation. 80 #[cfg(feature = "testing")] 81 pub mod testing { 82 extern crate std; 83 use super::{P256PublicKey, P256}; 84 pub use crate::testing::prelude::*; 85 use crate::{ 86 elliptic_curve::{EcdhProvider, EphemeralSecret, EphemeralSecretForTesting, PublicKey}, 87 testing::TestError, 88 CryptoRng, 89 }; 90 use core::marker::PhantomData; 91 use hex_literal::hex; 92 use rstest_reuse::template; 93 94 /// An ECDH provider that provides associated types for testing purposes. This can be mostly 95 /// considered "aliases" for the otherwise long fully-qualified associated types. 96 pub trait EcdhProviderForP256Test { 97 /// The ECDH Provider that is "wrapped" by this type. 98 type EcdhProvider: EcdhProvider< 99 P256, 100 PublicKey = <Self as EcdhProviderForP256Test>::PublicKey, 101 EphemeralSecret = <Self as EcdhProviderForP256Test>::EphemeralSecret, 102 SharedSecret = <Self as EcdhProviderForP256Test>::SharedSecret, 103 >; 104 /// The public key type. 105 type PublicKey: P256PublicKey; 106 /// The ephemeral secret type. 107 type EphemeralSecret: EphemeralSecretForTesting<P256, Impl = Self::EcdhProvider>; 108 /// The shared secret type. 109 type SharedSecret: Into<[u8; 32]>; 110 } 111 112 impl<E> EcdhProviderForP256Test for E 113 where 114 E: EcdhProvider<P256>, 115 E::PublicKey: P256PublicKey, 116 E::EphemeralSecret: EphemeralSecretForTesting<P256>, 117 { 118 type EcdhProvider = E; 119 type PublicKey = E::PublicKey; 120 type EphemeralSecret = E::EphemeralSecret; 121 type SharedSecret = E::SharedSecret; 122 } 123 124 /// Test for P256PublicKey::to_bytes to_bytes_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)125 pub fn to_bytes_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 126 let sec1_bytes = hex!( 127 "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022 128 8bbe76c6dc1643088107636deff8aa79e8002a157b92" 129 ); 130 let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap(); 131 let sec1_bytes_compressed = 132 hex!("02756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09"); 133 assert_eq!(sec1_bytes_compressed.to_vec(), key.to_bytes()); 134 } 135 136 /// Random test for P256PublicKey::to_bytes to_bytes_random_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)137 pub fn to_bytes_random_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 138 for _ in 1..100 { 139 let public_key_bytes = 140 E::EphemeralSecret::generate_random(&mut <E::EphemeralSecret as EphemeralSecret< 141 P256, 142 >>::Rng::new()) 143 .public_key_bytes(); 144 let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap(); 145 assert_eq!( 146 E::PublicKey::from_bytes(&public_key.to_bytes()).unwrap(), 147 public_key, 148 "from_bytes should return the same key for `{public_key_bytes:?}`", 149 ); 150 } 151 } 152 153 /// Test for P256PublicKey::from_affine_coordinates from_affine_coordinates_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)154 pub fn from_affine_coordinates_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 155 // https://www.secg.org/sec1-v2.pdf, section 2.3.3 156 let x = hex!("756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09"); 157 let y = hex!("f0b6da270d2a58a060228bbe76c6dc1643088107636deff8aa79e8002a157b92"); 158 let sec1 = hex!( 159 "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022 160 8bbe76c6dc1643088107636deff8aa79e8002a157b92" 161 ); 162 let expected_key = E::PublicKey::from_sec1_bytes(&sec1).unwrap(); 163 assert!( 164 E::PublicKey::from_affine_coordinates(&x, &y).unwrap() == expected_key, 165 "Public key does not match" 166 ); 167 } 168 169 /// Test for P256PublicKey::from_affine_coordinates from_affine_coordinates_not_on_curve_test<E: EcdhProviderForP256Test>( _: PhantomData<E>, )170 pub fn from_affine_coordinates_not_on_curve_test<E: EcdhProviderForP256Test>( 171 _: PhantomData<E>, 172 ) { 173 // (Invalid) coordinate from wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 193 174 let x = hex!("0000000000000000000000000000000000000000000000000000000000000000"); 175 let y = hex!("0000000000000000000000000000000000000000000000000000000000000000"); 176 let result = E::PublicKey::from_affine_coordinates(&x, &y); 177 assert!( 178 result.is_err(), 179 "Creating public key from invalid affine coordinate should fail" 180 ); 181 } 182 183 /// Test for P256PublicKey::from_sec1_bytes from_sec1_bytes_not_on_curve_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)184 pub fn from_sec1_bytes_not_on_curve_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 185 // (Invalid) sec1 encoding from wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 193 186 let sec1 = hex!( 187 "04000000000000000000000000000000000000000000000000000000000000000000000000000000000000 188 00000000000000000000000000000000000000000000" 189 ); 190 let result = E::PublicKey::from_sec1_bytes(&sec1); 191 assert!( 192 result.is_err(), 193 "Creating public key from point not on curve should fail" 194 ); 195 } 196 197 /// Test for P256PublicKey::from_sec1_bytes from_sec1_bytes_at_infinity_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)198 pub fn from_sec1_bytes_at_infinity_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 199 // A single [0] byte represents a point at infinity. 200 let sec1 = hex!("00"); 201 let result = E::PublicKey::from_sec1_bytes(&sec1); 202 assert!( 203 result.is_err(), 204 "Creating public key from point at infinity should fail" 205 ); 206 } 207 208 /// Test for P256PublicKey::to_affine_coordinates public_key_to_affine_coordinates_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)209 pub fn public_key_to_affine_coordinates_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 210 // https://www.secg.org/sec1-v2.pdf, section 2.3.3 211 let expected_x = hex!("756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09"); 212 let expected_y = hex!("f0b6da270d2a58a060228bbe76c6dc1643088107636deff8aa79e8002a157b92"); 213 let sec1 = hex!( 214 "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022 215 8bbe76c6dc1643088107636deff8aa79e8002a157b92" 216 ); 217 let public_key = E::PublicKey::from_sec1_bytes(&sec1).unwrap(); 218 let (actual_x, actual_y) = public_key.to_affine_coordinates().unwrap(); 219 assert_eq!(actual_x, expected_x); 220 assert_eq!(actual_y, expected_y); 221 } 222 223 /// Test for P256PublicKey::to_affine_coordinates with compressed point with 0x02 octet prefix. 224 /// Support for compressed points is optional according to the specs, but both openssl and 225 /// rustcrypto implementations support it. public_key_to_affine_coordinates_compressed02_test<E: EcdhProviderForP256Test>( _: PhantomData<E>, )226 pub fn public_key_to_affine_coordinates_compressed02_test<E: EcdhProviderForP256Test>( 227 _: PhantomData<E>, 228 ) { 229 // https://www.secg.org/sec1-v2.pdf, section 2.3.3 230 let expected_x = hex!("21238e877c2400f15f9ea7d4353ac0a63dcb5d13168a96fcfc93bdc66031ed1c"); 231 let expected_y = hex!("fa339bd0886602e91b9d2aa9b43213f660b680b1c97ef09cb1cacdc14e9d85ee"); 232 let sec1 = hex!("0221238e877c2400f15f9ea7d4353ac0a63dcb5d13168a96fcfc93bdc66031ed1c"); 233 let public_key = E::PublicKey::from_sec1_bytes(&sec1).unwrap(); 234 let (actual_x, actual_y) = public_key.to_affine_coordinates().unwrap(); 235 assert_eq!(actual_x, expected_x); 236 assert_eq!(actual_y, expected_y); 237 } 238 239 /// Test for P256PublicKey::to_affine_coordinates with compressed point with 0x03 octet prefix 240 /// Support for compressed points is optional according to the specs, but both openssl and 241 /// rustcrypto implementations support it. public_key_to_affine_coordinates_compressed03_test<E: EcdhProviderForP256Test>( _: PhantomData<E>, )242 pub fn public_key_to_affine_coordinates_compressed03_test<E: EcdhProviderForP256Test>( 243 _: PhantomData<E>, 244 ) { 245 // https://www.secg.org/sec1-v2.pdf, section 2.3.3 246 let expected_x = hex!("f557ef33d52e540e6aa4e6fcbb62a314adcb051cc8a1fefc69d004c282af81ff"); 247 let expected_y = hex!("96cd4c6ed5cbf00bb3184e5cd983c3442160310c8519b4c4d16292be83eec539"); 248 let sec1 = hex!("03f557ef33d52e540e6aa4e6fcbb62a314adcb051cc8a1fefc69d004c282af81ff"); 249 let public_key = E::PublicKey::from_sec1_bytes(&sec1).unwrap(); 250 let (actual_x, actual_y) = public_key.to_affine_coordinates().unwrap(); 251 assert_eq!(actual_x, expected_x); 252 assert_eq!(actual_y, expected_y); 253 } 254 255 /// Test for P256PublicKey::to_affine_coordinates with the top byte being zero public_key_to_affine_coordinates_zero_top_byte_test<E: EcdhProviderForP256Test>( _: PhantomData<E>, )256 pub fn public_key_to_affine_coordinates_zero_top_byte_test<E: EcdhProviderForP256Test>( 257 _: PhantomData<E>, 258 ) { 259 // https://www.secg.org/sec1-v2.pdf, section 2.3.3 260 let expected_x = hex!("00f24fe76679c57bc6c2f025af92e6c0b2058fb15fa41014775987587400ed48"); 261 let expected_y = hex!("e09f6fa9979a60f578a62dca805ad75b9e6b89403f2ebb934869e3697ac590e9"); 262 let sec1 = hex!("0400f24fe76679c57bc6c2f025af92e6c0b2058fb15fa41014775987587400ed48e09f6fa9979a60f578a62dca805ad75b9e6b89403f2ebb934869e3697ac590e9"); 263 let public_key = E::PublicKey::from_sec1_bytes(&sec1).unwrap(); 264 let (actual_x, actual_y) = public_key.to_affine_coordinates().unwrap(); 265 assert_eq!(actual_x, expected_x); 266 assert_eq!(actual_y, expected_y); 267 } 268 269 /// Test for P256 Diffie-Hellman key exchange. p256_ecdh_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)270 pub fn p256_ecdh_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 271 // From wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 1 272 // http://google3/third_party/wycheproof/testvectors/ecdh_secp256r1_ecpoint_test.json;l=22;rcl=375894991 273 // sec1 public key manually extracted from the ASN encoded test data 274 let public_key_sec1 = hex!( 275 "0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f 276 26ac333a93a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf" 277 ); 278 let private = hex!("0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346"); 279 let expected_shared_secret = 280 hex!("53020d908b0219328b658b525f26780e3ae12bcd952bb25a93bc0895e1714285"); 281 let actual_shared_secret = p256_ecdh_test_impl::<E>(&public_key_sec1, &private).unwrap(); 282 assert_eq!(actual_shared_secret.into(), expected_shared_secret); 283 } 284 p256_ecdh_test_impl<E: EcdhProviderForP256Test>( public_key_sec1: &[u8], private: &[u8; 32], ) -> Result<E::SharedSecret, TestError>285 fn p256_ecdh_test_impl<E: EcdhProviderForP256Test>( 286 public_key_sec1: &[u8], 287 private: &[u8; 32], 288 ) -> Result<E::SharedSecret, TestError> { 289 let public_key = E::PublicKey::from_sec1_bytes(public_key_sec1).map_err(TestError::new)?; 290 let ephemeral_secret = E::EphemeralSecret::from_private_components(private, &public_key) 291 .map_err(TestError::new)?; 292 ephemeral_secret 293 .diffie_hellman(&public_key) 294 .map_err(TestError::new) 295 } 296 297 /// Wycheproof test for P256 Diffie-Hellman. wycheproof_p256_test<E: EcdhProviderForP256Test>(_: PhantomData<E>)298 pub fn wycheproof_p256_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { 299 // Test cases from https://github.com/randombit/wycheproof-rs/blob/master/src/data/ecdh_secp256r1_ecpoint_test.json 300 let test_set = 301 wycheproof::ecdh::TestSet::load(wycheproof::ecdh::TestName::EcdhSecp256r1Ecpoint) 302 .unwrap(); 303 for test_group in test_set.test_groups { 304 for test in test_group.tests { 305 if test.private_key.len() != 32 { 306 // Some Wycheproof test cases have private key length that are not 32 bytes, but 307 // the RustCrypto implementation doesn't support that (it always take 32 bytes 308 // from the given RNG when generating a new key). 309 continue; 310 }; 311 std::println!("Testing {}", test.tc_id); 312 let result = p256_ecdh_test_impl::<E>( 313 &test.public_key, 314 &test 315 .private_key 316 .try_into() 317 .expect("Private key should be 32 bytes long"), 318 ); 319 match test.result { 320 wycheproof::TestResult::Valid => { 321 let shared_secret = 322 result.unwrap_or_else(|_| panic!("Test {} should succeed", test.tc_id)); 323 assert_eq!(test.shared_secret, shared_secret.into()); 324 } 325 wycheproof::TestResult::Invalid => { 326 result 327 .err() 328 .unwrap_or_else(|| panic!("Test {} should fail", test.tc_id)); 329 } 330 wycheproof::TestResult::Acceptable => { 331 if let Ok(shared_secret) = result { 332 assert_eq!(test.shared_secret, shared_secret.into()); 333 } 334 // Test passes if `result` is an error because this test is "acceptable" 335 } 336 } 337 } 338 } 339 } 340 341 /// Generates the test cases to validate the P256 implementation. 342 /// For example, to test `MyCryptoProvider`: 343 /// 344 /// ``` 345 /// use crypto_provider::p256::testing::*; 346 /// 347 /// mod tests { 348 /// #[apply(p256_test_cases)] 349 /// fn p256_tests(testcase: CryptoProviderTestCase<MyCryptoProvider> { 350 /// testcase(PhantomData::<MyCryptoProvider>); 351 /// } 352 /// } 353 /// ``` 354 #[template] 355 #[export] 356 #[rstest] 357 #[case::to_bytes(to_bytes_test)] 358 #[case::to_bytes_random(to_bytes_random_test)] 359 #[case::from_sec1_bytes_not_on_curve(from_sec1_bytes_not_on_curve_test)] 360 #[case::from_sec1_bytes_not_on_infinity(from_sec1_bytes_at_infinity_test)] 361 #[case::from_affine_coordinates(from_affine_coordinates_test)] 362 #[case::from_affine_coordinates_not_on_curve(from_affine_coordinates_not_on_curve_test)] 363 #[case::public_key_to_affine_coordinates(public_key_to_affine_coordinates_test)] 364 #[case::public_key_to_affine_coordinates_compressed02( 365 public_key_to_affine_coordinates_compressed02_test 366 )] 367 #[case::public_key_to_affine_coordinates_compressed03( 368 public_key_to_affine_coordinates_compressed03_test 369 )] 370 #[case::public_key_to_affine_coordinates_zero_top_byte( 371 public_key_to_affine_coordinates_zero_top_byte_test 372 )] 373 #[case::p256_ecdh(p256_ecdh_test)] 374 #[case::wycheproof_p256(wycheproof_p256_test)] p256_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>)375 fn p256_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>) {} 376 } 377