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