• 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 alloc::vec;
18 use bssl_crypto::ecdh::{PrivateKey, PublicKey};
19 use core::fmt::{Debug, Formatter};
20 use crypto_provider::{
21     elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey as _},
22     p256::{PointCompression, P256},
23     tinyvec::ArrayVec,
24 };
25 
26 /// Implementation of NIST-P256 using bssl-crypto crate.
27 pub struct P256Ecdh;
28 
29 impl EcdhProvider<P256> for P256Ecdh {
30     type PublicKey = P256PublicKey;
31     type EphemeralSecret = P256EphemeralSecret;
32     type SharedSecret = [u8; 32];
33 }
34 
35 /// A NIST-P256 public key.
36 pub struct P256PublicKey(PublicKey<bssl_crypto::ec::P256>);
37 
38 impl PartialEq for P256PublicKey {
eq(&self, other: &Self) -> bool39     fn eq(&self, other: &Self) -> bool {
40         self.0.to_x962_uncompressed().as_ref() == other.0.to_x962_uncompressed().as_ref()
41     }
42 }
43 
44 impl Debug for P256PublicKey {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result45     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
46         write!(f, "X9.62 bytes: {:?}", self.0.to_x962_uncompressed().as_ref())
47     }
48 }
49 
50 impl crypto_provider::p256::P256PublicKey for P256PublicKey {
51     type Error = Error;
52 
from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error>53     fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
54         PublicKey::from_x962_uncompressed(bytes).map(P256PublicKey).ok_or(Error::ConversionFailed)
55     }
56 
to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]>57     fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
58         match point_compression {
59             PointCompression::Compressed => unimplemented!(),
60             PointCompression::Uncompressed => {
61                 let mut bytes = ArrayVec::<[u8; 65]>::new();
62                 bytes.extend_from_slice(self.0.to_x962_uncompressed().as_ref());
63                 bytes
64             }
65         }
66     }
67 
68     #[allow(clippy::expect_used, clippy::indexing_slicing)]
to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error>69     fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> {
70         let raw_key = self.0.to_x962_uncompressed();
71         let x = raw_key.as_ref()[1..33].try_into().map_err(|_| Error::ConversionFailed)?;
72         let y = raw_key.as_ref()[33..65].try_into().map_err(|_| Error::ConversionFailed)?;
73         Ok((x, y))
74     }
from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error>75     fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> {
76         let mut point = vec![0x04];
77         point.extend_from_slice(x);
78         point.extend_from_slice(y);
79         PublicKey::<bssl_crypto::ec::P256>::from_x962_uncompressed(&point)
80             .map(Self)
81             .ok_or(Error::ConversionFailed)
82     }
83 }
84 
85 /// Ephemeral secret for use in a P256 Diffie-Hellman exchange.
86 pub struct P256EphemeralSecret {
87     secret: PrivateKey<bssl_crypto::ec::P256>,
88 }
89 
90 impl EphemeralSecret<P256> for P256EphemeralSecret {
91     type Impl = P256Ecdh;
92     type Error = Error;
93     type Rng = ();
94     type EncodedPublicKey =
95         <P256PublicKey as crypto_provider::elliptic_curve::PublicKey<P256>>::EncodedPublicKey;
96 
generate_random(_rng: &mut Self::Rng) -> Self97     fn generate_random(_rng: &mut Self::Rng) -> Self {
98         Self { secret: PrivateKey::generate() }
99     }
100 
public_key_bytes(&self) -> Self::EncodedPublicKey101     fn public_key_bytes(&self) -> Self::EncodedPublicKey {
102         P256PublicKey(self.secret.to_public_key()).to_bytes()
103     }
104 
diffie_hellman( self, other_pub: &P256PublicKey, ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error>105     fn diffie_hellman(
106         self,
107         other_pub: &P256PublicKey,
108     ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> {
109         let shared_secret = self.secret.compute_shared_key(&other_pub.0);
110         shared_secret.try_into().map_err(|_| Error::DiffieHellmanFailed)
111     }
112 }
113 
114 /// Error type for ECDH operations.
115 #[derive(Debug)]
116 pub enum Error {
117     /// Failed when trying to convert between representations.
118     ConversionFailed,
119     /// The Diffie-Hellman key exchange failed.
120     DiffieHellmanFailed,
121 }
122 
123 #[cfg(test)]
124 impl crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<P256> for P256EphemeralSecret {
125     #[allow(clippy::unwrap_used)]
from_private_components( private_bytes: &[u8; 32], _public_key: &P256PublicKey, ) -> Result<Self, Self::Error>126     fn from_private_components(
127         private_bytes: &[u8; 32],
128         _public_key: &P256PublicKey,
129     ) -> Result<Self, Self::Error> {
130         Ok(Self { secret: PrivateKey::from_big_endian(private_bytes).unwrap() })
131     }
132 }
133 
134 #[cfg(test)]
135 mod tests {
136     use super::P256Ecdh;
137     use core::marker::PhantomData;
138     use crypto_provider_test::p256::*;
139 
140     #[apply(p256_test_cases)]
p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, name: &str)141     fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, name: &str) {
142         if name.contains("compressed") {
143             return; // EC point compression not supported by bssl-crypto yet
144         }
145         testcase(PhantomData::<P256Ecdh>)
146     }
147 }
148