use core::alloc::Layout;
#[cfg(feature = "internal-software-crypto-ecdh")]
pub use software::*;
use wasefire_error::Error;
use crate::Support;
pub trait Api<const N: usize>: Support<bool> + Send {
const PRIVATE: Layout;
const PUBLIC: Layout;
const SHARED: Layout;
fn generate(private: &mut [u8]) -> Result<(), Error>;
fn public(private: &[u8], public: &mut [u8]) -> Result<(), Error>;
fn shared(private: &[u8], public: &[u8], shared: &mut [u8]) -> Result<(), Error>;
fn drop_private(private: &mut [u8]) -> Result<(), Error>;
fn drop_shared(shared: &mut [u8]) -> Result<(), Error>;
fn export_public(public: &[u8], x: &mut [u8; N], y: &mut [u8; N]) -> Result<(), Error>;
fn import_public(x: &[u8; N], y: &[u8; N], public: &mut [u8]) -> Result<(), Error>;
fn export_shared(shared: &[u8], x: &mut [u8; N]) -> Result<(), Error>;
}
#[cfg(feature = "internal-software-crypto-ecdh")]
mod software {
use core::marker::PhantomData;
use elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::subtle::CtOption;
use elliptic_curve::zeroize::Zeroize;
use elliptic_curve::{
AffinePoint, CurveArithmetic, FieldBytes, FieldBytesSize, ProjectivePoint, Scalar,
ScalarPrimitive, SecretKey,
};
use signature::rand_core::CryptoRngCore;
use wasefire_error::Code;
use super::*;
use crate::Supported;
use crate::crypto::WithError;
pub struct Software<C, R, const N: usize> {
curve: PhantomData<C>,
rng: PhantomData<R>,
}
impl<C, R, const N: usize> Supported for Software<C, R, N> {}
impl<C, R, const N: usize> Api<N> for Software<C, R, N>
where
C: CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: FromEncodedPoint<C>,
FieldBytesSize<C>: ModulusSize,
R: Default + CryptoRngCore + WithError + Send,
{
const PRIVATE: Layout = unsafe { Layout::from_size_align_unchecked(N, 1) };
const PUBLIC: Layout = unsafe { Layout::from_size_align_unchecked(2 * N, 1) };
const SHARED: Layout = unsafe { Layout::from_size_align_unchecked(N, 1) };
fn generate(private: &mut [u8]) -> Result<(), Error> {
if private.len() != N {
return Err(Error::user(Code::InvalidLength));
}
let mut rng = R::default();
let key = R::with_error(|| SecretKey::<C>::random(&mut rng))?;
private.copy_from_slice(&key.to_bytes());
Ok(())
}
fn public(private: &[u8], public: &mut [u8]) -> Result<(), Error> {
if private.len() != N || public.len() != 2 * N {
return Err(Error::user(Code::InvalidLength));
}
let key = SecretKey::<C>::from_bytes(FieldBytes::<C>::from_slice(private))
.map_err(|_| Error::user(Code::InvalidArgument))?;
let key = key.public_key().as_affine().to_encoded_point(false);
let (x, y) = public.split_at_mut(N);
x.copy_from_slice(key.x().ok_or(Error::user(0))?);
y.copy_from_slice(key.y().ok_or(Error::user(0))?);
Ok(())
}
fn shared(private: &[u8], public: &[u8], shared: &mut [u8]) -> Result<(), Error> {
if private.len() != N || public.len() != 2 * N || shared.len() != N {
return Err(Error::user(Code::InvalidLength));
}
let private = unwrap_ct(ScalarPrimitive::<C>::from_bytes(private.into()))?;
let private = Scalar::<C>::from(private);
let (x, y) = public.split_at(N);
let public = EncodedPoint::<C>::from_affine_coordinates(x.into(), y.into(), false);
let public = unwrap_ct(ProjectivePoint::<C>::from_encoded_point(&public))?;
let secret: AffinePoint<C> = (public * private).into();
let secret = secret.to_encoded_point(false);
shared.copy_from_slice(secret.x().ok_or(Error::user(0))?);
Ok(())
}
fn drop_private(private: &mut [u8]) -> Result<(), Error> {
if private.len() != N {
return Err(Error::user(Code::InvalidLength));
}
private.zeroize();
Ok(())
}
fn drop_shared(shared: &mut [u8]) -> Result<(), Error> {
if shared.len() != N {
return Err(Error::user(Code::InvalidLength));
}
shared.zeroize();
Ok(())
}
fn export_public(public: &[u8], x: &mut [u8; N], y: &mut [u8; N]) -> Result<(), Error> {
if public.len() != 2 * N {
return Err(Error::user(Code::InvalidLength));
}
let (pub_x, pub_y) = public.split_at(N);
x.copy_from_slice(pub_x);
y.copy_from_slice(pub_y);
Ok(())
}
fn import_public(x: &[u8; N], y: &[u8; N], public: &mut [u8]) -> Result<(), Error> {
if public.len() != 2 * N {
return Err(Error::user(Code::InvalidLength));
}
let (pub_x, pub_y) = public.split_at_mut(N);
pub_x.copy_from_slice(x);
pub_y.copy_from_slice(y);
Ok(())
}
fn export_shared(shared: &[u8], x: &mut [u8; N]) -> Result<(), Error> {
if shared.len() != N {
return Err(Error::user(Code::InvalidLength));
}
x.copy_from_slice(shared);
Ok(())
}
}
fn unwrap_ct<T>(x: CtOption<T>) -> Result<T, Error> {
Option::<T>::from(x).ok_or(Error::user(0))
}
}