blob: 98a6cd4070fe82f5244e5fbf0cfcadf42fd6cdf4 [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.
use crate::RcRng;
use core::marker::PhantomData;
use crypto_provider::{
elliptic_curve::{EcdhProvider, EphemeralSecret},
p256::{PointCompression, P256},
tinyvec::ArrayVec,
};
use p256::{
elliptic_curve,
elliptic_curve::{
generic_array::GenericArray,
sec1::{FromEncodedPoint, ToEncodedPoint},
},
};
use rand::{RngCore, SeedableRng};
use rand_core::CryptoRng;
/// Implementation of NIST-P256 using RustCrypto crates.
pub struct P256Ecdh<R> {
_marker: PhantomData<R>,
}
impl<R: CryptoRng + SeedableRng + RngCore + Send> EcdhProvider<P256> for P256Ecdh<R> {
type PublicKey = P256PublicKey;
type EphemeralSecret = P256EphemeralSecret<R>;
type SharedSecret = [u8; 32];
}
/// A NIST-P256 public key.
#[derive(Debug, PartialEq, Eq)]
pub struct P256PublicKey(p256::PublicKey);
impl crypto_provider::p256::P256PublicKey for P256PublicKey {
type Error = elliptic_curve::Error;
fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
p256::PublicKey::from_sec1_bytes(bytes).map(Self)
}
fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
let mut bytes = ArrayVec::<[u8; 65]>::new();
bytes.extend_from_slice(
self.0.to_encoded_point(point_compression == PointCompression::Compressed).as_bytes(),
);
bytes
}
#[allow(clippy::expect_used)]
fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> {
let p256_key = self.0.to_encoded_point(false);
let x: &[u8; 32] =
p256_key.x().expect("Generated key should not be on identity point").as_ref();
let y: &[u8; 32] =
p256_key.y().expect("Generated key should not be on identity point").as_ref();
Ok((*x, *y))
}
fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> {
let key_option: Option<p256::PublicKey> =
p256::PublicKey::from_encoded_point(&p256::EncodedPoint::from_affine_coordinates(
&GenericArray::clone_from_slice(x),
&GenericArray::clone_from_slice(y),
false,
))
.into();
key_option.map(Self).ok_or(elliptic_curve::Error)
}
}
/// Ephemeral secrect for use in a P256 Diffie-Hellman
pub struct P256EphemeralSecret<R: CryptoRng + SeedableRng + RngCore> {
secret: p256::ecdh::EphemeralSecret,
_marker: PhantomData<R>,
}
impl<R: CryptoRng + SeedableRng + RngCore + Send> EphemeralSecret<P256> for P256EphemeralSecret<R> {
type Impl = P256Ecdh<R>;
type Error = sec1::Error;
type Rng = RcRng<R>;
type EncodedPublicKey = ArrayVec<[u8; 65]>;
fn generate_random(rng: &mut Self::Rng) -> Self {
Self {
secret: p256::ecdh::EphemeralSecret::random(&mut rng.0),
_marker: Default::default(),
}
}
fn public_key_bytes(&self) -> Self::EncodedPublicKey {
let mut bytes = Self::EncodedPublicKey::new();
bytes.extend_from_slice(self.secret.public_key().to_encoded_point(false).as_bytes());
bytes
}
fn diffie_hellman(
self,
other_pub: &P256PublicKey,
) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> {
let shared_secret = p256::ecdh::EphemeralSecret::diffie_hellman(&self.secret, &other_pub.0);
let bytes: <Self::Impl as EcdhProvider<P256>>::SharedSecret =
(*shared_secret.raw_secret_bytes()).into();
Ok(bytes)
}
}
#[cfg(test)]
impl<R: CryptoRng + SeedableRng + RngCore + Send>
crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<P256>
for P256EphemeralSecret<R>
{
fn from_private_components(
private_bytes: &[u8; 32],
_public_key: &P256PublicKey,
) -> Result<Self, Self::Error> {
Ok(Self {
secret: p256::ecdh::EphemeralSecret::random(&mut crate::testing::MockCryptoRng {
values: private_bytes.iter(),
}),
_marker: Default::default(),
})
}
}
#[cfg(test)]
mod tests {
use super::P256Ecdh;
use core::marker::PhantomData;
use crypto_provider_test::p256::*;
use rand::rngs::StdRng;
#[apply(p256_test_cases)]
fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>, _name: &str) {
testcase(PhantomData::<P256Ecdh<StdRng>>)
}
}