use core::cell::UnsafeCell;
use core::ffi::{c_int, c_uint, c_void};
use core::marker::PhantomData;
use alloc::vec;
use alloc::vec::Vec;
use generic_array::GenericArray;
use crate::error::{len_as_c_int, WolfCryptError};
use wolfcrypt_rs::{
BN_bn2bin, BN_bin2bn, BN_free, BN_num_bytes,
EC_GROUP_free, EC_GROUP_new_by_curve_name,
EC_KEY_free, EC_KEY_generate_key, EC_KEY_get0_public_key, EC_KEY_new,
EC_KEY_set_group, EC_KEY_set_private_key, EC_KEY_set_public_key,
EC_POINT_free, EC_POINT_mul, EC_POINT_new, EC_POINT_oct2point, EC_POINT_point2oct,
ECDSA_SIG_free, ECDSA_SIG_get0, ECDSA_SIG_new, ECDSA_SIG_set0,
ECDSA_do_sign, ECDSA_do_verify,
EVP_DigestFinal, EVP_DigestInit_ex, EVP_DigestUpdate,
EVP_MD, EVP_MD_CTX_free, EVP_MD_CTX_new,
EVP_sha256,
NID_X9_62_prime256v1,
BIGNUM, EC_GROUP, EC_KEY,
point_conversion_form_t,
};
#[cfg(wolfssl_ecc_p384)]
use wolfcrypt_rs::{EVP_sha384, NID_secp384r1};
#[cfg(wolfssl_ecc_p521)]
use wolfcrypt_rs::{EVP_sha512, NID_secp521r1};
mod sealed {
pub trait Sealed {}
}
pub trait EcdsaCurve: sealed::Sealed + 'static {
const NID: c_int;
const FIELD_SIZE: usize;
const SIG_SIZE: usize;
type SigSize: generic_array::ArrayLength<u8>;
const UNCOMPRESSED_POINT_SIZE: usize;
const HASH_LEN: usize;
fn evp_md() -> *const EVP_MD;
}
pub struct P256;
impl sealed::Sealed for P256 {}
impl EcdsaCurve for P256 {
const NID: c_int = NID_X9_62_prime256v1;
const FIELD_SIZE: usize = 32;
const SIG_SIZE: usize = 64;
type SigSize = typenum::U64;
const UNCOMPRESSED_POINT_SIZE: usize = 65;
const HASH_LEN: usize = 32;
fn evp_md() -> *const EVP_MD {
unsafe { EVP_sha256() }
}
}
#[cfg(wolfssl_ecc_p384)]
pub struct P384;
#[cfg(wolfssl_ecc_p384)]
impl sealed::Sealed for P384 {}
#[cfg(wolfssl_ecc_p384)]
impl EcdsaCurve for P384 {
const NID: c_int = NID_secp384r1;
const FIELD_SIZE: usize = 48;
const SIG_SIZE: usize = 96;
type SigSize = typenum::U96;
const UNCOMPRESSED_POINT_SIZE: usize = 97;
const HASH_LEN: usize = 48;
fn evp_md() -> *const EVP_MD {
unsafe { EVP_sha384() }
}
}
#[cfg(wolfssl_ecc_p521)]
pub struct P521;
#[cfg(wolfssl_ecc_p521)]
impl sealed::Sealed for P521 {}
#[cfg(wolfssl_ecc_p521)]
impl EcdsaCurve for P521 {
const NID: c_int = NID_secp521r1;
const FIELD_SIZE: usize = 66; const SIG_SIZE: usize = 132; type SigSize = typenum::U132;
const UNCOMPRESSED_POINT_SIZE: usize = 133; const HASH_LEN: usize = 64; fn evp_md() -> *const EVP_MD {
unsafe { EVP_sha512() }
}
}
pub struct EcdsaSignature<C: EcdsaCurve> {
bytes: GenericArray<u8, C::SigSize>,
_curve: PhantomData<C>,
}
impl<C: EcdsaCurve> EcdsaSignature<C> {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, WolfCryptError> {
if bytes.len() != C::SIG_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
Ok(Self {
bytes: GenericArray::clone_from_slice(bytes),
_curve: PhantomData,
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn r_bytes(&self) -> &[u8] {
&self.bytes[..C::FIELD_SIZE]
}
pub fn s_bytes(&self) -> &[u8] {
&self.bytes[C::FIELD_SIZE..]
}
}
impl<C: EcdsaCurve> Clone for EcdsaSignature<C> {
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
_curve: PhantomData,
}
}
}
impl<C: EcdsaCurve> PartialEq for EcdsaSignature<C> {
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes
}
}
impl<C: EcdsaCurve> Eq for EcdsaSignature<C> {}
impl<C: EcdsaCurve> core::fmt::Debug for EcdsaSignature<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EcdsaSignature")
.field("len", &self.bytes.len())
.finish()
}
}
impl<C: EcdsaCurve> signature_trait::SignatureEncoding for EcdsaSignature<C> {
type Repr = Vec<u8>;
}
impl<C: EcdsaCurve> TryFrom<&[u8]> for EcdsaSignature<C> {
type Error = signature_trait::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_bytes(bytes).map_err(|_| signature_trait::Error::new())
}
}
impl<C: EcdsaCurve> From<EcdsaSignature<C>> for Vec<u8> {
fn from(sig: EcdsaSignature<C>) -> Vec<u8> {
sig.bytes.to_vec()
}
}
impl<C: EcdsaCurve> AsRef<[u8]> for EcdsaSignature<C> {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
fn hash_message<C: EcdsaCurve>(msg: &[u8]) -> Result<Vec<u8>, WolfCryptError> {
let ctx = unsafe { EVP_MD_CTX_new() };
if ctx.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
EVP_DigestInit_ex(ctx, C::evp_md(), core::ptr::null_mut())
};
if rc != 1 {
unsafe { EVP_MD_CTX_free(ctx) };
return Err(WolfCryptError::Ffi { code: rc, func: "EVP_DigestInit_ex" });
}
let rc = unsafe {
EVP_DigestUpdate(ctx, msg.as_ptr() as *const c_void, msg.len())
};
if rc != 1 {
unsafe { EVP_MD_CTX_free(ctx) };
return Err(WolfCryptError::Ffi { code: rc, func: "EVP_DigestUpdate" });
}
let mut hash = vec![0u8; C::HASH_LEN];
let mut hash_len: c_uint = 0;
let rc = unsafe { EVP_DigestFinal(ctx, hash.as_mut_ptr(), &mut hash_len) };
unsafe { EVP_MD_CTX_free(ctx) };
if rc != 1 {
return Err(WolfCryptError::Ffi { code: rc, func: "EVP_DigestFinal" });
}
Ok(hash)
}
unsafe fn bn_to_fixed_bytes(bn: *const BIGNUM, field_size: usize) -> Vec<u8> {
let num_bytes = unsafe { BN_num_bytes(bn) } as usize;
let mut buf = vec![0u8; field_size];
if num_bytes > 0 {
let offset = field_size.saturating_sub(num_bytes);
unsafe { BN_bn2bin(bn, buf[offset..].as_mut_ptr()) };
}
buf
}
pub struct EcdsaSigningKey<C: EcdsaCurve> {
key: UnsafeCell<*mut EC_KEY>,
group: *mut EC_GROUP,
_curve: PhantomData<C>,
}
unsafe impl<C: EcdsaCurve> Send for EcdsaSigningKey<C> {}
impl<C: EcdsaCurve> EcdsaSigningKey<C> {
pub fn generate() -> Result<Self, WolfCryptError> {
let group = unsafe { EC_GROUP_new_by_curve_name(C::NID) };
if group.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let key = unsafe { EC_KEY_new() };
if key.is_null() {
unsafe { EC_GROUP_free(group) };
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_group(key, group) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_group" });
}
let rc = unsafe { EC_KEY_generate_key(key) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_generate_key" });
}
Ok(Self {
key: UnsafeCell::new(key),
group,
_curve: PhantomData,
})
}
pub fn verifying_key(&self) -> Result<EcdsaVerifyingKey<C>, WolfCryptError> {
let key = unsafe { *self.key.get() };
let point = unsafe { EC_KEY_get0_public_key(key) };
if point.is_null() {
return Err(WolfCryptError::Ffi { code: 0, func: "EC_KEY_get0_public_key" });
}
let mut buf = vec![0u8; C::UNCOMPRESSED_POINT_SIZE];
let written = unsafe {
EC_POINT_point2oct(
self.group as *const _,
point,
point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED,
buf.as_mut_ptr(),
buf.len(),
core::ptr::null_mut(),
)
};
if written != C::UNCOMPRESSED_POINT_SIZE {
return Err(WolfCryptError::Ffi { code: written as i32, func: "EC_POINT_point2oct" });
}
EcdsaVerifyingKey::from_uncompressed_point(&buf)
}
pub fn from_private_key_and_public_point(
priv_bytes: &[u8],
pub_bytes: &[u8],
) -> Result<Self, WolfCryptError> {
if pub_bytes.len() != C::UNCOMPRESSED_POINT_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
if pub_bytes[0] != 0x04 {
return Err(WolfCryptError::INVALID_INPUT);
}
let group = unsafe { EC_GROUP_new_by_curve_name(C::NID) };
if group.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let key = unsafe { EC_KEY_new() };
if key.is_null() {
unsafe { EC_GROUP_free(group) };
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_group(key, group) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_group" });
}
let priv_bn = unsafe {
BN_bin2bn(
priv_bytes.as_ptr(),
len_as_c_int(priv_bytes.len()),
core::ptr::null_mut(),
)
};
if priv_bn.is_null() {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_private_key(key, priv_bn) };
unsafe { BN_free(priv_bn) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_private_key" });
}
let point = unsafe { EC_POINT_new(group) };
if point.is_null() {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
EC_POINT_oct2point(
group,
point,
pub_bytes.as_ptr(),
pub_bytes.len(),
core::ptr::null_mut(),
)
};
if rc != 1 {
unsafe {
EC_POINT_free(point);
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_POINT_oct2point" });
}
let rc = unsafe { EC_KEY_set_public_key(key, point) };
unsafe { EC_POINT_free(point) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_public_key" });
}
Ok(Self {
key: UnsafeCell::new(key),
group,
_curve: PhantomData,
})
}
pub fn from_private_key_bytes(priv_bytes: &[u8]) -> Result<Self, WolfCryptError> {
let group = unsafe { EC_GROUP_new_by_curve_name(C::NID) };
if group.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let key = unsafe { EC_KEY_new() };
if key.is_null() {
unsafe { EC_GROUP_free(group) };
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_group(key, group) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_group" });
}
let priv_bn = unsafe {
BN_bin2bn(
priv_bytes.as_ptr(),
len_as_c_int(priv_bytes.len()),
core::ptr::null_mut(),
)
};
if priv_bn.is_null() {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_private_key(key, priv_bn) };
if rc != 1 {
unsafe {
BN_free(priv_bn);
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_private_key" });
}
let point = unsafe { EC_POINT_new(group) };
if point.is_null() {
unsafe {
BN_free(priv_bn);
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
EC_POINT_mul(
group,
point,
priv_bn,
core::ptr::null(),
core::ptr::null(),
core::ptr::null_mut(),
)
};
unsafe { BN_free(priv_bn) };
if rc != 1 {
unsafe {
EC_POINT_free(point);
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_POINT_mul" });
}
let rc = unsafe { EC_KEY_set_public_key(key, point) };
unsafe { EC_POINT_free(point) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_public_key" });
}
Ok(Self {
key: UnsafeCell::new(key),
group,
_curve: PhantomData,
})
}
pub fn sign_prehash(
&self,
hash: &[u8],
) -> Result<EcdsaSignature<C>, WolfCryptError> {
if hash.len() != C::HASH_LEN {
return Err(WolfCryptError::INVALID_INPUT);
}
let key = unsafe { *self.key.get() };
let sig = unsafe {
ECDSA_do_sign(hash.as_ptr(), len_as_c_int(hash.len()), key)
};
if sig.is_null() {
return Err(WolfCryptError::Ffi { code: 0, func: "ECDSA_do_sign" });
}
let mut r_ptr: *const BIGNUM = core::ptr::null();
let mut s_ptr: *const BIGNUM = core::ptr::null();
unsafe { ECDSA_SIG_get0(sig, &mut r_ptr, &mut s_ptr) };
let r_bytes = unsafe { bn_to_fixed_bytes(r_ptr, C::FIELD_SIZE) };
let s_bytes = unsafe { bn_to_fixed_bytes(s_ptr, C::FIELD_SIZE) };
unsafe { ECDSA_SIG_free(sig) };
let mut combined = GenericArray::<u8, C::SigSize>::default();
combined[..C::FIELD_SIZE].copy_from_slice(&r_bytes);
combined[C::FIELD_SIZE..].copy_from_slice(&s_bytes);
Ok(EcdsaSignature {
bytes: combined,
_curve: PhantomData,
})
}
}
impl<C: EcdsaCurve> Drop for EcdsaSigningKey<C> {
fn drop(&mut self) {
unsafe {
EC_KEY_free(*self.key.get_mut());
EC_GROUP_free(self.group);
}
}
}
impl<C: EcdsaCurve> signature_trait::Signer<EcdsaSignature<C>> for EcdsaSigningKey<C> {
fn try_sign(
&self,
msg: &[u8],
) -> Result<EcdsaSignature<C>, signature_trait::Error> {
let hash = hash_message::<C>(msg).map_err(|_| signature_trait::Error::new())?;
let key = unsafe { *self.key.get() };
let sig = unsafe {
ECDSA_do_sign(hash.as_ptr(), len_as_c_int(hash.len()), key)
};
if sig.is_null() {
return Err(signature_trait::Error::new());
}
let mut r_ptr: *const BIGNUM = core::ptr::null();
let mut s_ptr: *const BIGNUM = core::ptr::null();
unsafe { ECDSA_SIG_get0(sig, &mut r_ptr, &mut s_ptr) };
let r_bytes = unsafe { bn_to_fixed_bytes(r_ptr, C::FIELD_SIZE) };
let s_bytes = unsafe { bn_to_fixed_bytes(s_ptr, C::FIELD_SIZE) };
unsafe { ECDSA_SIG_free(sig) };
let mut combined = GenericArray::<u8, C::SigSize>::default();
combined[..C::FIELD_SIZE].copy_from_slice(&r_bytes);
combined[C::FIELD_SIZE..].copy_from_slice(&s_bytes);
Ok(EcdsaSignature {
bytes: combined,
_curve: PhantomData,
})
}
}
pub struct EcdsaVerifyingKey<C: EcdsaCurve> {
key: UnsafeCell<*mut EC_KEY>,
group: *mut EC_GROUP,
pub_bytes: Vec<u8>,
_curve: PhantomData<C>,
}
unsafe impl<C: EcdsaCurve> Send for EcdsaVerifyingKey<C> {}
impl<C: EcdsaCurve> EcdsaVerifyingKey<C> {
pub fn from_uncompressed_point(bytes: &[u8]) -> Result<Self, WolfCryptError> {
if bytes.len() != C::UNCOMPRESSED_POINT_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
if bytes[0] != 0x04 {
return Err(WolfCryptError::INVALID_INPUT);
}
let group = unsafe { EC_GROUP_new_by_curve_name(C::NID) };
if group.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let key = unsafe { EC_KEY_new() };
if key.is_null() {
unsafe { EC_GROUP_free(group) };
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_group(key, group) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_group" });
}
let point = unsafe { EC_POINT_new(group) };
if point.is_null() {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
EC_POINT_oct2point(
group,
point,
bytes.as_ptr(),
bytes.len(),
core::ptr::null_mut(),
)
};
if rc != 1 {
unsafe {
EC_POINT_free(point);
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_POINT_oct2point" });
}
let rc = unsafe { EC_KEY_set_public_key(key, point) };
unsafe { EC_POINT_free(point) };
if rc != 1 {
unsafe {
EC_KEY_free(key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_public_key" });
}
Ok(Self {
key: UnsafeCell::new(key),
group,
pub_bytes: bytes.to_vec(),
_curve: PhantomData,
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.pub_bytes
}
}
impl<C: EcdsaCurve> Drop for EcdsaVerifyingKey<C> {
fn drop(&mut self) {
unsafe {
EC_KEY_free(*self.key.get_mut());
EC_GROUP_free(self.group);
}
}
}
impl<C: EcdsaCurve> signature_trait::Verifier<EcdsaSignature<C>> for EcdsaVerifyingKey<C> {
fn verify(
&self,
msg: &[u8],
signature: &EcdsaSignature<C>,
) -> Result<(), signature_trait::Error> {
let hash = hash_message::<C>(msg).map_err(|_| signature_trait::Error::new())?;
let key = unsafe { *self.key.get() };
let r_bytes = signature.r_bytes();
let s_bytes = signature.s_bytes();
let r_bn = unsafe {
BN_bin2bn(r_bytes.as_ptr(), len_as_c_int(r_bytes.len()), core::ptr::null_mut())
};
if r_bn.is_null() {
return Err(signature_trait::Error::new());
}
let s_bn = unsafe {
BN_bin2bn(s_bytes.as_ptr(), len_as_c_int(s_bytes.len()), core::ptr::null_mut())
};
if s_bn.is_null() {
unsafe { BN_free(r_bn) };
return Err(signature_trait::Error::new());
}
let sig = unsafe { ECDSA_SIG_new() };
if sig.is_null() {
unsafe {
BN_free(r_bn);
BN_free(s_bn);
}
return Err(signature_trait::Error::new());
}
let rc = unsafe { ECDSA_SIG_set0(sig, r_bn, s_bn) };
if rc != 1 {
unsafe {
BN_free(r_bn);
BN_free(s_bn);
ECDSA_SIG_free(sig);
}
return Err(signature_trait::Error::new());
}
let result = unsafe {
ECDSA_do_verify(hash.as_ptr(), len_as_c_int(hash.len()), sig, key)
};
unsafe { ECDSA_SIG_free(sig) };
if result == 1 {
Ok(())
} else {
Err(signature_trait::Error::new())
}
}
}
pub type P256SigningKey = EcdsaSigningKey<P256>;
pub type P256VerifyingKey = EcdsaVerifyingKey<P256>;
pub type P256Signature = EcdsaSignature<P256>;
#[cfg(wolfssl_ecc_p384)]
pub type P384SigningKey = EcdsaSigningKey<P384>;
#[cfg(wolfssl_ecc_p384)]
pub type P384VerifyingKey = EcdsaVerifyingKey<P384>;
#[cfg(wolfssl_ecc_p384)]
pub type P384Signature = EcdsaSignature<P384>;
#[cfg(wolfssl_ecc_p521)]
pub type P521SigningKey = EcdsaSigningKey<P521>;
#[cfg(wolfssl_ecc_p521)]
pub type P521VerifyingKey = EcdsaVerifyingKey<P521>;
#[cfg(wolfssl_ecc_p521)]
pub type P521Signature = EcdsaSignature<P521>;