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