use crate::WolfCryptError;
use wolfcrypt_rs::{
wc_FreeRng, wc_InitRng, wc_curve25519_export_key_raw_ex, wc_curve25519_free,
wc_curve25519_import_private_raw_ex, wc_curve25519_import_public_ex, wc_curve25519_init,
wc_curve25519_key, wc_curve25519_make_key, wc_curve25519_make_pub, wc_curve25519_set_rng,
wc_curve25519_shared_secret_ex, EC25519_LITTLE_ENDIAN, WC_RNG,
};
use zeroize::{Zeroize, ZeroizeOnDrop};
const KEY_LEN: usize = 32;
fn clamp(scalar: &mut [u8; KEY_LEN]) {
scalar[0] &= 248;
scalar[31] &= 127;
scalar[31] |= 64;
}
pub struct X25519StaticSecret {
key: wc_curve25519_key,
}
pub struct X25519PublicKey {
bytes: [u8; KEY_LEN],
}
#[derive(ZeroizeOnDrop)]
pub struct SharedSecret(#[zeroize(drop)] [u8; KEY_LEN]);
impl X25519StaticSecret {
pub fn from_bytes(private: &[u8; KEY_LEN]) -> Self {
let mut key = wc_curve25519_key::zeroed();
let rc = unsafe { wc_curve25519_init(&mut key) };
assert_eq!(rc, 0, "wc_curve25519_init failed (OOM)");
let mut clamped = *private;
clamp(&mut clamped);
let mut pub_bytes = [0u8; KEY_LEN];
let rc = unsafe {
wc_curve25519_make_pub(
KEY_LEN as i32,
pub_bytes.as_mut_ptr(),
KEY_LEN as i32,
clamped.as_ptr(),
)
};
assert_eq!(rc, 0, "wc_curve25519_make_pub failed (invalid private key)");
let rc = unsafe {
wc_curve25519_import_private_raw_ex(
clamped.as_ptr(),
KEY_LEN as u32,
pub_bytes.as_ptr(),
KEY_LEN as u32,
&mut key,
EC25519_LITTLE_ENDIAN,
)
};
assert_eq!(
rc, 0,
"wc_curve25519_import_private_raw_ex failed (invalid key bytes)"
);
clamped.zeroize();
Self { key }
}
#[cfg(feature = "rand")]
pub fn random(rng: &mut crate::rand::WolfRng) -> Self {
let mut key = wc_curve25519_key::zeroed();
let rc = unsafe { wc_curve25519_init(&mut key) };
assert_eq!(rc, 0, "wc_curve25519_init failed (OOM)");
let rc = unsafe { wc_curve25519_make_key(&mut rng.rng, KEY_LEN as i32, &mut key) };
assert_eq!(rc, 0, "wc_curve25519_make_key failed (RNG failure)");
Self { key }
}
pub fn public_key(&self) -> X25519PublicKey {
let mut priv_bytes = [0u8; KEY_LEN];
let mut pub_bytes = [0u8; KEY_LEN];
let mut priv_sz = KEY_LEN as u32;
let mut pub_sz = KEY_LEN as u32;
let rc = unsafe {
wc_curve25519_export_key_raw_ex(
&self.key as *const wc_curve25519_key as *mut wc_curve25519_key,
priv_bytes.as_mut_ptr(),
&mut priv_sz,
pub_bytes.as_mut_ptr(),
&mut pub_sz,
EC25519_LITTLE_ENDIAN,
)
};
assert_eq!(
rc, 0,
"wc_curve25519_export_key_raw_ex failed (buffer too small)"
);
priv_bytes.zeroize();
X25519PublicKey { bytes: pub_bytes }
}
pub fn diffie_hellman(
mut self,
peer_public: &X25519PublicKey,
) -> Result<SharedSecret, WolfCryptError> {
let mut rng = WC_RNG::zeroed();
let rc = unsafe { wc_InitRng(&mut rng) };
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_InitRng",
});
}
let rc = unsafe { wc_curve25519_set_rng(&mut self.key, &mut rng) };
if rc != 0 {
unsafe { wc_FreeRng(&mut rng) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve25519_set_rng",
});
}
let mut peer_key = wc_curve25519_key::zeroed();
let rc = unsafe { wc_curve25519_init(&mut peer_key) };
if rc != 0 {
unsafe { wc_FreeRng(&mut rng) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve25519_init",
});
}
let rc = unsafe {
wc_curve25519_import_public_ex(
peer_public.bytes.as_ptr(),
KEY_LEN as u32,
&mut peer_key,
EC25519_LITTLE_ENDIAN,
)
};
if rc != 0 {
unsafe {
wc_curve25519_free(&mut peer_key);
wc_FreeRng(&mut rng);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve25519_import_public_ex",
});
}
let mut out = [0u8; KEY_LEN];
let mut out_len = KEY_LEN as u32;
let rc = unsafe {
wc_curve25519_shared_secret_ex(
&mut self.key,
&mut peer_key,
out.as_mut_ptr(),
&mut out_len,
EC25519_LITTLE_ENDIAN,
)
};
unsafe {
wc_curve25519_free(&mut peer_key);
wc_FreeRng(&mut rng);
}
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve25519_shared_secret_ex",
});
}
Ok(SharedSecret(out))
}
}
impl Drop for X25519StaticSecret {
fn drop(&mut self) {
unsafe { wc_curve25519_free(&mut self.key) };
}
}
unsafe impl Send for X25519StaticSecret {}
impl X25519PublicKey {
pub fn from_bytes(bytes: &[u8; KEY_LEN]) -> Self {
Self { bytes: *bytes }
}
pub fn as_bytes(&self) -> &[u8; KEY_LEN] {
&self.bytes
}
}
impl From<[u8; KEY_LEN]> for X25519PublicKey {
fn from(bytes: [u8; KEY_LEN]) -> Self {
Self { bytes }
}
}
unsafe impl Send for X25519PublicKey {}
unsafe impl Sync for X25519PublicKey {}
impl SharedSecret {
pub fn as_bytes(&self) -> &[u8; KEY_LEN] {
&self.0
}
}
#[cfg(wolfssl_curve448)]
use wolfcrypt_rs::{
wc_curve448_export_key_raw_ex, wc_curve448_free, wc_curve448_import_private_raw_ex,
wc_curve448_import_public_ex, wc_curve448_init, wc_curve448_key, wc_curve448_make_key,
wc_curve448_make_pub, wc_curve448_shared_secret_ex, EC448_LITTLE_ENDIAN,
};
#[cfg(wolfssl_curve448)]
const X448_KEY_LEN: usize = 56;
#[cfg(wolfssl_curve448)]
pub struct X448StaticSecret {
key: wc_curve448_key,
}
#[cfg(wolfssl_curve448)]
pub struct X448PublicKey {
bytes: [u8; X448_KEY_LEN],
}
#[cfg(wolfssl_curve448)]
#[derive(ZeroizeOnDrop)]
pub struct X448SharedSecret(#[zeroize(drop)] [u8; X448_KEY_LEN]);
#[cfg(wolfssl_curve448)]
impl X448StaticSecret {
pub fn from_bytes(private: &[u8; X448_KEY_LEN]) -> Self {
let mut key = wc_curve448_key::zeroed();
let rc = unsafe { wc_curve448_init(&mut key) };
assert_eq!(rc, 0, "wc_curve448_init failed (OOM)");
let mut clamped = *private;
clamped[0] &= 252;
clamped[55] |= 128;
let mut pub_bytes = [0u8; X448_KEY_LEN];
let rc = unsafe {
wc_curve448_make_pub(
X448_KEY_LEN as i32,
pub_bytes.as_mut_ptr(),
X448_KEY_LEN as i32,
clamped.as_ptr(),
)
};
assert_eq!(rc, 0, "wc_curve448_make_pub failed (invalid private key)");
let rc = unsafe {
wc_curve448_import_private_raw_ex(
clamped.as_ptr(),
X448_KEY_LEN as u32,
pub_bytes.as_ptr(),
X448_KEY_LEN as u32,
&mut key,
EC448_LITTLE_ENDIAN,
)
};
assert_eq!(
rc, 0,
"wc_curve448_import_private_raw_ex failed (invalid key bytes)"
);
clamped.zeroize();
Self { key }
}
#[cfg(feature = "rand")]
pub fn random(rng: &mut crate::rand::WolfRng) -> Self {
let mut key = wc_curve448_key::zeroed();
let rc = unsafe { wc_curve448_init(&mut key) };
assert_eq!(rc, 0, "wc_curve448_init failed (OOM)");
let rc = unsafe { wc_curve448_make_key(&mut rng.rng, X448_KEY_LEN as i32, &mut key) };
assert_eq!(rc, 0, "wc_curve448_make_key failed (RNG failure)");
Self { key }
}
pub fn public_key(&self) -> X448PublicKey {
let mut priv_bytes = [0u8; X448_KEY_LEN];
let mut pub_bytes = [0u8; X448_KEY_LEN];
let mut priv_sz = X448_KEY_LEN as u32;
let mut pub_sz = X448_KEY_LEN as u32;
let rc = unsafe {
wc_curve448_export_key_raw_ex(
&self.key as *const wc_curve448_key as *mut wc_curve448_key,
priv_bytes.as_mut_ptr(),
&mut priv_sz,
pub_bytes.as_mut_ptr(),
&mut pub_sz,
EC448_LITTLE_ENDIAN,
)
};
assert_eq!(
rc, 0,
"wc_curve448_export_key_raw_ex failed (buffer too small)"
);
priv_bytes.zeroize();
X448PublicKey { bytes: pub_bytes }
}
pub fn diffie_hellman(
mut self,
peer_public: &X448PublicKey,
) -> Result<X448SharedSecret, WolfCryptError> {
let mut peer_key = wc_curve448_key::zeroed();
let rc = unsafe { wc_curve448_init(&mut peer_key) };
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve448_init",
});
}
let rc = unsafe {
wc_curve448_import_public_ex(
peer_public.bytes.as_ptr(),
X448_KEY_LEN as u32,
&mut peer_key,
EC448_LITTLE_ENDIAN,
)
};
if rc != 0 {
unsafe { wc_curve448_free(&mut peer_key) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve448_import_public_ex",
});
}
let mut out = [0u8; X448_KEY_LEN];
let mut out_len = X448_KEY_LEN as u32;
let rc = unsafe {
wc_curve448_shared_secret_ex(
&mut self.key,
&mut peer_key,
out.as_mut_ptr(),
&mut out_len,
EC448_LITTLE_ENDIAN,
)
};
unsafe { wc_curve448_free(&mut peer_key) };
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_curve448_shared_secret_ex",
});
}
Ok(X448SharedSecret(out))
}
}
#[cfg(wolfssl_curve448)]
impl Drop for X448StaticSecret {
fn drop(&mut self) {
unsafe { wc_curve448_free(&mut self.key) };
}
}
#[cfg(wolfssl_curve448)]
unsafe impl Send for X448StaticSecret {}
#[cfg(wolfssl_curve448)]
impl X448PublicKey {
pub fn from_bytes(bytes: &[u8; X448_KEY_LEN]) -> Self {
Self { bytes: *bytes }
}
pub fn as_bytes(&self) -> &[u8; X448_KEY_LEN] {
&self.bytes
}
}
#[cfg(wolfssl_curve448)]
impl From<[u8; X448_KEY_LEN]> for X448PublicKey {
fn from(bytes: [u8; X448_KEY_LEN]) -> Self {
Self { bytes }
}
}
#[cfg(wolfssl_curve448)]
unsafe impl Send for X448PublicKey {}
#[cfg(wolfssl_curve448)]
unsafe impl Sync for X448PublicKey {}
#[cfg(wolfssl_curve448)]
impl X448SharedSecret {
pub fn as_bytes(&self) -> &[u8; X448_KEY_LEN] {
&self.0
}
}
#[cfg(wolfssl_ecc)]
pub(crate) mod nist_ecdh_native {
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::cell::UnsafeCell;
use core::ffi::c_int;
use core::marker::PhantomData;
use core::ptr;
use zeroize::ZeroizeOnDrop;
use crate::error::WolfCryptError;
use wolfcrypt_rs::{
wc_FreeRng, wc_InitRng, wc_ecc_check_key, wc_ecc_export_x963, wc_ecc_import_private_key_ex,
wc_ecc_import_x963, wc_ecc_key, wc_ecc_key_free, wc_ecc_key_new, wc_ecc_make_key_ex,
wc_ecc_set_rng, wc_ecc_shared_secret, ECC_SECP256R1, WC_RNG,
};
#[cfg(wolfssl_ecc_p384)]
use wolfcrypt_rs::ECC_SECP384R1;
#[cfg(wolfssl_ecc_p521)]
use wolfcrypt_rs::ECC_SECP521R1;
mod sealed {
pub trait Sealed {}
}
pub trait NistCurve: sealed::Sealed + 'static {
const CURVE_ID: c_int;
const FIELD_SIZE: usize;
const POINT_SIZE: usize;
}
pub struct NistP256;
impl sealed::Sealed for NistP256 {}
impl NistCurve for NistP256 {
const CURVE_ID: c_int = ECC_SECP256R1;
const FIELD_SIZE: usize = 32;
const POINT_SIZE: usize = 65; }
#[cfg(wolfssl_ecc_p384)]
pub struct NistP384;
#[cfg(wolfssl_ecc_p384)]
impl sealed::Sealed for NistP384 {}
#[cfg(wolfssl_ecc_p384)]
impl NistCurve for NistP384 {
const CURVE_ID: c_int = ECC_SECP384R1;
const FIELD_SIZE: usize = 48;
const POINT_SIZE: usize = 97; }
#[cfg(wolfssl_ecc_p521)]
pub struct NistP521;
#[cfg(wolfssl_ecc_p521)]
impl sealed::Sealed for NistP521 {}
#[cfg(wolfssl_ecc_p521)]
impl NistCurve for NistP521 {
const CURVE_ID: c_int = ECC_SECP521R1;
const FIELD_SIZE: usize = 66;
const POINT_SIZE: usize = 133; }
pub struct NistEcdhPublicKey<C: NistCurve> {
bytes: Vec<u8>,
_curve: PhantomData<C>,
}
impl<C: NistCurve> NistEcdhPublicKey<C> {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, WolfCryptError> {
if bytes.len() != C::POINT_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
if bytes[0] != 0x04 {
return Err(WolfCryptError::INVALID_INPUT);
}
let tmp = unsafe { wc_ecc_key_new(ptr::null_mut()) };
if tmp.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { wc_ecc_import_x963(bytes.as_ptr(), bytes.len() as u32, tmp) };
if rc != 0 {
unsafe { wc_ecc_key_free(tmp) };
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_import_x963",
});
}
let rc = unsafe { wc_ecc_check_key(tmp) };
unsafe { wc_ecc_key_free(tmp) };
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_check_key",
});
}
Ok(Self {
bytes: bytes.to_vec(),
_curve: PhantomData,
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
unsafe impl<C: NistCurve> Send for NistEcdhPublicKey<C> {}
unsafe impl<C: NistCurve> Sync for NistEcdhPublicKey<C> {}
#[derive(ZeroizeOnDrop)]
pub struct NistEcdhSharedSecret<C: NistCurve> {
#[zeroize(drop)]
bytes: Vec<u8>,
_curve: PhantomData<C>,
}
impl<C: NistCurve> NistEcdhSharedSecret<C> {
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
pub struct NistEcdhSecret<C: NistCurve> {
key: UnsafeCell<*mut wc_ecc_key>,
has_public: bool,
_curve: PhantomData<C>,
}
unsafe impl<C: NistCurve> Send for NistEcdhSecret<C> {}
impl<C: NistCurve> Drop for NistEcdhSecret<C> {
fn drop(&mut self) {
let key = *self.key.get_mut();
if !key.is_null() {
unsafe {
wc_ecc_key_free(key);
}
}
}
}
impl<C: NistCurve> NistEcdhSecret<C> {
pub fn generate() -> Result<Self, WolfCryptError> {
let key = unsafe { wc_ecc_key_new(ptr::null_mut()) };
if key.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let mut rng = WC_RNG::zeroed();
let rc = unsafe { wc_InitRng(&mut rng) };
if rc != 0 {
unsafe {
wc_ecc_key_free(key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_InitRng",
});
}
let rc =
unsafe { wc_ecc_make_key_ex(&mut rng, C::FIELD_SIZE as i32, key, C::CURVE_ID) };
unsafe {
wc_FreeRng(&mut rng);
}
if rc != 0 {
unsafe {
wc_ecc_key_free(key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_make_key_ex",
});
}
Ok(Self {
key: UnsafeCell::new(key),
has_public: true,
_curve: PhantomData,
})
}
pub fn from_private_scalar(scalar: &[u8]) -> Result<Self, WolfCryptError> {
if scalar.len() != C::FIELD_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
let key = unsafe { wc_ecc_key_new(ptr::null_mut()) };
if key.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
wc_ecc_import_private_key_ex(
scalar.as_ptr(),
scalar.len() as u32,
ptr::null(),
0,
key,
C::CURVE_ID,
)
};
if rc != 0 {
unsafe {
wc_ecc_key_free(key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_import_private_key_ex",
});
}
Ok(Self {
key: UnsafeCell::new(key),
has_public: false,
_curve: PhantomData,
})
}
pub fn public_key(&self) -> Result<NistEcdhPublicKey<C>, WolfCryptError> {
if !self.has_public {
return Err(WolfCryptError::INVALID_INPUT);
}
let key = unsafe { *self.key.get() };
let mut buf = vec![0u8; C::POINT_SIZE];
let mut sz = buf.len() as u32;
let rc = unsafe { wc_ecc_export_x963(key, buf.as_mut_ptr(), &mut sz) };
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_export_x963",
});
}
if sz as usize != C::POINT_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
NistEcdhPublicKey::from_bytes(&buf)
}
pub fn diffie_hellman(
self,
peer_public: &NistEcdhPublicKey<C>,
) -> Result<NistEcdhSharedSecret<C>, WolfCryptError> {
let peer_key = unsafe { wc_ecc_key_new(ptr::null_mut()) };
if peer_key.is_null() {
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe {
wc_ecc_import_x963(
peer_public.bytes.as_ptr(),
peer_public.bytes.len() as u32,
peer_key,
)
};
if rc != 0 {
unsafe {
wc_ecc_key_free(peer_key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_import_x963",
});
}
let rc = unsafe { wc_ecc_check_key(peer_key) };
if rc != 0 {
unsafe {
wc_ecc_key_free(peer_key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_check_key",
});
}
let priv_key = unsafe { *self.key.get() };
let mut rng = WC_RNG::zeroed();
let rc = unsafe { wc_InitRng(&mut rng) };
if rc != 0 {
unsafe {
wc_ecc_key_free(peer_key);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_InitRng",
});
}
let rc = unsafe { wc_ecc_set_rng(priv_key, &mut rng) };
if rc != 0 {
unsafe {
wc_ecc_key_free(peer_key);
wc_FreeRng(&mut rng);
}
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_set_rng",
});
}
let mut out = vec![0u8; C::FIELD_SIZE];
let mut out_len = out.len() as u32;
let rc =
unsafe { wc_ecc_shared_secret(priv_key, peer_key, out.as_mut_ptr(), &mut out_len) };
unsafe {
wc_ecc_key_free(peer_key);
wc_FreeRng(&mut rng);
}
if rc != 0 {
return Err(WolfCryptError::Ffi {
code: rc,
func: "wc_ecc_shared_secret",
});
}
if out_len as usize != C::FIELD_SIZE {
return Err(WolfCryptError::INVALID_INPUT);
}
Ok(NistEcdhSharedSecret {
bytes: out,
_curve: PhantomData,
})
}
}
pub type P256EcdhSecret = NistEcdhSecret<NistP256>;
#[cfg(wolfssl_ecc_p384)]
pub type P384EcdhSecret = NistEcdhSecret<NistP384>;
#[cfg(wolfssl_ecc_p521)]
pub type P521EcdhSecret = NistEcdhSecret<NistP521>;
}
#[cfg(wolfssl_ecc)]
pub use nist_ecdh_native::{
NistCurve, NistEcdhPublicKey, NistEcdhSecret, NistEcdhSharedSecret, NistP256, P256EcdhSecret,
};
#[cfg(all(wolfssl_ecc, wolfssl_ecc_p384))]
pub use nist_ecdh_native::{NistP384, P384EcdhSecret};
#[cfg(all(wolfssl_ecc, wolfssl_ecc_p521))]
pub use nist_ecdh_native::{NistP521, P521EcdhSecret};