use core::alloc::Layout;
#[cfg(feature = "internal-software-crypto-ecdsa")]
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 WRAPPED: usize;
fn generate(private: &mut [u8]) -> Result<(), Error>;
fn public(private: &[u8], public: &mut [u8]) -> Result<(), Error>;
fn sign(
private: &[u8], digest: &[u8; N], r: &mut [u8; N], s: &mut [u8; N],
) -> Result<(), Error>;
fn verify(public: &[u8], digest: &[u8; N], r: &[u8; N], s: &[u8; N]) -> Result<bool, Error>;
fn drop_private(private: &mut [u8]) -> Result<(), Error>;
fn export_private(private: &[u8], wrapped: &mut [u8]) -> Result<(), Error>;
fn import_private(wrapped: &[u8], private: &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>;
}
#[cfg(feature = "internal-software-crypto-ecdsa")]
mod software {
use core::marker::PhantomData;
use crypto_common::BlockSizeUser;
use crypto_common::generic_array::ArrayLength;
use ecdsa::hazmat::{SignPrimitive, VerifyPrimitive};
use ecdsa::{EncodedPoint, PrimeCurve, Signature, SignatureSize, SigningKey, VerifyingKey};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::zeroize::Zeroize;
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytes, FieldBytesSize, Scalar};
use signature::digest::{Digest, FixedOutput, FixedOutputReset};
use signature::hazmat::PrehashVerifier;
use signature::rand_core::CryptoRngCore;
use wasefire_error::Code;
use super::*;
use crate::Supported;
use crate::crypto::WithError;
pub struct Software<C, D, R, const N: usize> {
curve: PhantomData<C>,
digest: PhantomData<D>,
rng: PhantomData<R>,
}
impl<C, D, R, const N: usize> Supported for Software<C, D, R, N> {}
impl<C, D, R, const N: usize> Api<N> for Software<C, D, R, N>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
FieldBytesSize<C>: ModulusSize,
D: Support<bool> + Send,
D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldBytesSize<C>> + FixedOutputReset,
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 WRAPPED: usize = N;
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(|| SigningKey::<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 = SigningKey::<C>::from_bytes(FieldBytes::<C>::from_slice(private))
.map_err(|_| Error::user(Code::InvalidArgument))?;
let key = key.verifying_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 sign(
private: &[u8], digest: &[u8; N], r: &mut [u8; N], s: &mut [u8; N],
) -> Result<(), Error> {
if private.len() != N {
return Err(Error::user(Code::InvalidLength));
}
let key = SigningKey::<C>::from_bytes(FieldBytes::<C>::from_slice(private))
.map_err(|_| Error::user(Code::InvalidArgument))?;
let (sig, _) = key
.as_nonzero_scalar()
.try_sign_prehashed_rfc6979::<D>(FieldBytes::<C>::from_slice(digest), &[])
.map_err(|_| Error::world(0))?;
r.copy_from_slice(FieldBytes::<C>::from(sig.r()).as_slice());
s.copy_from_slice(FieldBytes::<C>::from(sig.s()).as_slice());
Ok(())
}
fn verify(
public: &[u8], digest: &[u8; N], r: &[u8; N], s: &[u8; N],
) -> Result<bool, Error> {
if public.len() != 2 * N {
return Err(Error::user(Code::InvalidLength));
}
let (x, y) = public.split_at(N);
let key = EncodedPoint::<C>::from_affine_coordinates(x.into(), y.into(), false);
let key = VerifyingKey::<C>::from_encoded_point(&key).map_err(|_| Error::user(0))?;
let r = FieldBytes::<C>::from_slice(r).clone();
let s = FieldBytes::<C>::from_slice(s).clone();
let sig = Signature::from_scalars(r, s).map_err(|_| Error::user(0))?;
Ok(key.verify_prehash(digest, &sig).is_ok())
}
fn drop_private(private: &mut [u8]) -> Result<(), Error> {
if private.len() != N {
return Err(Error::user(Code::InvalidLength));
}
private.zeroize();
Ok(())
}
fn export_private(private: &[u8], wrapped: &mut [u8]) -> Result<(), Error> {
if private.len() != N || wrapped.len() != N {
return Err(Error::user(Code::InvalidLength));
}
wrapped.copy_from_slice(private);
Ok(())
}
fn import_private(wrapped: &[u8], private: &mut [u8]) -> Result<(), Error> {
if wrapped.len() != N || private.len() != N {
return Err(Error::user(Code::InvalidLength));
}
private.copy_from_slice(wrapped);
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(())
}
}
}