• 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 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