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::RcRng; 16 use core::marker::PhantomData; 17 use crypto_provider::{ 18 elliptic_curve::{EcdhProvider, EphemeralSecret}, 19 p256::{PointCompression, P256}, 20 tinyvec::ArrayVec, 21 }; 22 use p256::{ 23 elliptic_curve, 24 elliptic_curve::{ 25 generic_array::GenericArray, 26 sec1::{FromEncodedPoint, ToEncodedPoint}, 27 }, 28 }; 29 use rand::{RngCore, SeedableRng}; 30 use rand_core::CryptoRng; 31 32 /// Implementation of NIST-P256 using RustCrypto crates. 33 pub struct P256Ecdh<R> { 34 _marker: PhantomData<R>, 35 } 36 37 impl<R: CryptoRng + SeedableRng + RngCore + Send> EcdhProvider<P256> for P256Ecdh<R> { 38 type PublicKey = P256PublicKey; 39 type EphemeralSecret = P256EphemeralSecret<R>; 40 type SharedSecret = [u8; 32]; 41 } 42 43 /// A NIST-P256 public key. 44 #[derive(Debug, PartialEq, Eq)] 45 pub struct P256PublicKey(p256::PublicKey); 46 impl crypto_provider::p256::P256PublicKey for P256PublicKey { 47 type Error = elliptic_curve::Error; 48 from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error>49 fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { 50 p256::PublicKey::from_sec1_bytes(bytes).map(Self) 51 } 52 to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]>53 fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> { 54 let mut bytes = ArrayVec::<[u8; 65]>::new(); 55 bytes.extend_from_slice( 56 self.0.to_encoded_point(point_compression == PointCompression::Compressed).as_bytes(), 57 ); 58 bytes 59 } 60 61 #[allow(clippy::expect_used)] to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error>62 fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> { 63 let p256_key = self.0.to_encoded_point(false); 64 let x: &[u8; 32] = 65 p256_key.x().expect("Generated key should not be on identity point").as_ref(); 66 let y: &[u8; 32] = 67 p256_key.y().expect("Generated key should not be on identity point").as_ref(); 68 Ok((*x, *y)) 69 } from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error>70 fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> { 71 let key_option: Option<p256::PublicKey> = 72 p256::PublicKey::from_encoded_point(&p256::EncodedPoint::from_affine_coordinates( 73 &GenericArray::clone_from_slice(x), 74 &GenericArray::clone_from_slice(y), 75 false, 76 )) 77 .into(); 78 key_option.map(Self).ok_or(elliptic_curve::Error) 79 } 80 } 81 82 /// Ephemeral secrect for use in a P256 Diffie-Hellman 83 pub struct P256EphemeralSecret<R: CryptoRng + SeedableRng + RngCore> { 84 secret: p256::ecdh::EphemeralSecret, 85 _marker: PhantomData<R>, 86 } 87 88 impl<R: CryptoRng + SeedableRng + RngCore + Send> EphemeralSecret<P256> for P256EphemeralSecret<R> { 89 type Impl = P256Ecdh<R>; 90 type Error = sec1::Error; 91 type Rng = RcRng<R>; 92 type EncodedPublicKey = ArrayVec<[u8; 65]>; 93 generate_random(rng: &mut Self::Rng) -> Self94 fn generate_random(rng: &mut Self::Rng) -> Self { 95 Self { 96 secret: p256::ecdh::EphemeralSecret::random(&mut rng.0), 97 _marker: Default::default(), 98 } 99 } 100 public_key_bytes(&self) -> Self::EncodedPublicKey101 fn public_key_bytes(&self) -> Self::EncodedPublicKey { 102 let mut bytes = Self::EncodedPublicKey::new(); 103 bytes.extend_from_slice(self.secret.public_key().to_encoded_point(false).as_bytes()); 104 bytes 105 } 106 diffie_hellman( self, other_pub: &P256PublicKey, ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error>107 fn diffie_hellman( 108 self, 109 other_pub: &P256PublicKey, 110 ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> { 111 let shared_secret = p256::ecdh::EphemeralSecret::diffie_hellman(&self.secret, &other_pub.0); 112 let bytes: <Self::Impl as EcdhProvider<P256>>::SharedSecret = 113 (*shared_secret.raw_secret_bytes()).into(); 114 Ok(bytes) 115 } 116 } 117 118 #[cfg(test)] 119 impl<R: CryptoRng + SeedableRng + RngCore + Send> 120 crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<P256> 121 for P256EphemeralSecret<R> 122 { from_private_components( private_bytes: &[u8; 32], _public_key: &P256PublicKey, ) -> Result<Self, Self::Error>123 fn from_private_components( 124 private_bytes: &[u8; 32], 125 _public_key: &P256PublicKey, 126 ) -> Result<Self, Self::Error> { 127 Ok(Self { 128 secret: p256::ecdh::EphemeralSecret::random(&mut crate::testing::MockCryptoRng { 129 values: private_bytes.iter(), 130 }), 131 _marker: Default::default(), 132 }) 133 } 134 } 135 136 #[cfg(test)] 137 mod tests { 138 use super::P256Ecdh; 139 use core::marker::PhantomData; 140 use crypto_provider_test::p256::*; 141 use rand::rngs::StdRng; 142 143 #[apply(p256_test_cases)] p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>, _name: &str)144 fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>, _name: &str) { 145 testcase(PhantomData::<P256Ecdh<StdRng>>) 146 } 147 } 148