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::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey}; 18 use crypto_provider::x25519::X25519; 19 use rand::RngCore; 20 use rand_core::{CryptoRng, SeedableRng}; 21 22 /// The RustCrypto implementation of X25519 ECDH. 23 pub struct X25519Ecdh<R> { 24 _marker: PhantomData<R>, 25 } 26 27 impl<R: CryptoRng + RngCore + SeedableRng + Send> EcdhProvider<X25519> for X25519Ecdh<R> { 28 type PublicKey = X25519PublicKey; 29 type EphemeralSecret = X25519EphemeralSecret<R>; 30 type SharedSecret = [u8; 32]; 31 } 32 33 /// A X25519 ephemeral secret used for Diffie-Hellman. 34 pub struct X25519EphemeralSecret<R: CryptoRng + RngCore + SeedableRng> { 35 secret: x25519_dalek::EphemeralSecret, 36 marker: PhantomData<R>, 37 } 38 39 impl<R: CryptoRng + RngCore + SeedableRng + Send> EphemeralSecret<X25519> 40 for X25519EphemeralSecret<R> 41 { 42 type Impl = X25519Ecdh<R>; 43 type Error = Error; 44 type Rng = RcRng<R>; 45 type EncodedPublicKey = [u8; 32]; 46 generate_random(rng: &mut Self::Rng) -> Self47 fn generate_random(rng: &mut Self::Rng) -> Self { 48 Self { 49 secret: x25519_dalek::EphemeralSecret::random_from_rng(&mut rng.0), 50 marker: Default::default(), 51 } 52 } 53 public_key_bytes(&self) -> Self::EncodedPublicKey54 fn public_key_bytes(&self) -> Self::EncodedPublicKey { 55 let pubkey: x25519_dalek::PublicKey = (&self.secret).into(); 56 pubkey.to_bytes() 57 } 58 diffie_hellman( self, other_pub: &<X25519Ecdh<R> as EcdhProvider<X25519>>::PublicKey, ) -> Result<<X25519Ecdh<R> as EcdhProvider<X25519>>::SharedSecret, Self::Error>59 fn diffie_hellman( 60 self, 61 other_pub: &<X25519Ecdh<R> as EcdhProvider<X25519>>::PublicKey, 62 ) -> Result<<X25519Ecdh<R> as EcdhProvider<X25519>>::SharedSecret, Self::Error> { 63 Ok(x25519_dalek::EphemeralSecret::diffie_hellman(self.secret, &other_pub.0).to_bytes()) 64 } 65 } 66 67 #[cfg(test)] 68 impl<R: CryptoRng + RngCore + SeedableRng + Send> 69 crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<X25519> 70 for X25519EphemeralSecret<R> 71 { from_private_components( private_bytes: &[u8; 32], _public_key: &X25519PublicKey, ) -> Result<Self, Self::Error>72 fn from_private_components( 73 private_bytes: &[u8; 32], 74 _public_key: &X25519PublicKey, 75 ) -> Result<Self, Self::Error> { 76 Ok(Self { 77 secret: x25519_dalek::EphemeralSecret::random_from_rng(crate::testing::MockCryptoRng { 78 values: private_bytes.iter(), 79 }), 80 marker: Default::default(), 81 }) 82 } 83 } 84 85 /// A X25519 public key. 86 #[derive(Debug, PartialEq, Eq)] 87 pub struct X25519PublicKey(x25519_dalek::PublicKey); 88 89 impl PublicKey<X25519> for X25519PublicKey { 90 type Error = Error; 91 type EncodedPublicKey = [u8; 32]; 92 from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>93 fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { 94 let byte_sized: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?; 95 Ok(Self(byte_sized.into())) 96 } 97 to_bytes(&self) -> Self::EncodedPublicKey98 fn to_bytes(&self) -> Self::EncodedPublicKey { 99 self.0.to_bytes() 100 } 101 } 102 103 /// Error type for the RustCrypto implementation of x25519. 104 #[derive(Debug)] 105 pub enum Error { 106 /// Unexpected size for the given input. 107 WrongSize, 108 } 109 110 #[cfg(test)] 111 mod tests { 112 use super::X25519Ecdh; 113 use core::marker::PhantomData; 114 use crypto_provider_test::x25519::*; 115 use rand::rngs::StdRng; 116 117 #[apply(x25519_test_cases)] x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh<StdRng>>)118 fn x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh<StdRng>>) { 119 testcase(PhantomData::<X25519Ecdh<StdRng>>) 120 } 121 } 122