blob: 794d780980840ba1718b5e6ada73fb18bc6f8c25 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate alloc;
use crate::RcRng;
use alloc::vec::Vec;
use core::marker::PhantomData;
use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
use crypto_provider::x25519::X25519;
use rand::RngCore;
use rand_core::{CryptoRng, SeedableRng};
/// The RustCrypto implementation of X25519 ECDH.
pub struct X25519Ecdh<R> {
_marker: PhantomData<R>,
}
impl<R: CryptoRng + RngCore + SeedableRng + Send> EcdhProvider<X25519> for X25519Ecdh<R> {
type PublicKey = X25519PublicKey;
type EphemeralSecret = X25519EphemeralSecret<R>;
type SharedSecret = [u8; 32];
}
/// A X25519 ephemeral secret used for Diffie-Hellman.
pub struct X25519EphemeralSecret<R: CryptoRng + RngCore + SeedableRng> {
secret: x25519_dalek::EphemeralSecret,
marker: PhantomData<R>,
}
impl<R: CryptoRng + RngCore + SeedableRng + Send> EphemeralSecret<X25519>
for X25519EphemeralSecret<R>
{
type Impl = X25519Ecdh<R>;
type Error = Error;
type Rng = RcRng<R>;
fn generate_random(rng: &mut Self::Rng) -> Self {
Self {
secret: x25519_dalek::EphemeralSecret::random_from_rng(&mut rng.0),
marker: Default::default(),
}
}
fn public_key_bytes(&self) -> Vec<u8> {
let pubkey: x25519_dalek::PublicKey = (&self.secret).into();
pubkey.to_bytes().into()
}
fn diffie_hellman(
self,
other_pub: &<X25519Ecdh<R> as EcdhProvider<X25519>>::PublicKey,
) -> Result<<X25519Ecdh<R> as EcdhProvider<X25519>>::SharedSecret, Self::Error> {
Ok(x25519_dalek::EphemeralSecret::diffie_hellman(self.secret, &other_pub.0).to_bytes())
}
}
#[cfg(test)]
impl<R: CryptoRng + RngCore + SeedableRng + Send>
crypto_provider::elliptic_curve::EphemeralSecretForTesting<X25519>
for X25519EphemeralSecret<R>
{
fn from_private_components(
private_bytes: &[u8; 32],
_public_key: &X25519PublicKey,
) -> Result<Self, Self::Error> {
Ok(Self {
secret: x25519_dalek::EphemeralSecret::random_from_rng(
&mut crate::testing::MockCryptoRng {
values: private_bytes.iter(),
},
),
marker: Default::default(),
})
}
}
/// A X25519 public key.
#[derive(Debug, PartialEq, Eq)]
pub struct X25519PublicKey(x25519_dalek::PublicKey);
impl PublicKey<X25519> for X25519PublicKey {
type Error = Error;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
let byte_sized: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?;
Ok(Self(byte_sized.into()))
}
fn to_bytes(&self) -> Vec<u8> {
self.0.as_bytes().to_vec()
}
}
/// Error type for the RustCrypto implementation of x25519.
#[derive(Debug)]
pub enum Error {
/// Unexpected size for the given input.
WrongSize,
}
#[cfg(test)]
mod tests {
use super::X25519Ecdh;
use core::marker::PhantomData;
use crypto_provider::x25519::testing::*;
use rand::rngs::StdRng;
#[apply(x25519_test_cases)]
fn x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh<StdRng>>) {
testcase(PhantomData::<X25519Ecdh<StdRng>>)
}
}