use wolfcrypt_rs::{
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, wc_FreeRng, wc_InitRng, 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) -> SharedSecret {
let mut rng = WC_RNG::zeroed();
let rc = unsafe { wc_InitRng(&mut rng) };
assert_eq!(rc, 0, "wc_InitRng failed (entropy source unavailable)");
let rc = unsafe { wc_curve25519_set_rng(&mut self.key, &mut rng) };
assert_eq!(rc, 0, "wc_curve25519_set_rng failed (null RNG)");
let mut peer_key = wc_curve25519_key::zeroed();
let rc = unsafe { wc_curve25519_init(&mut peer_key) };
assert_eq!(rc, 0, "wc_curve25519_init (peer) failed (OOM)");
let rc = unsafe {
wc_curve25519_import_public_ex(
peer_public.bytes.as_ptr(),
KEY_LEN as u32,
&mut peer_key,
EC25519_LITTLE_ENDIAN,
)
};
assert_eq!(rc, 0, "wc_curve25519_import_public_ex failed (invalid public key)");
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,
)
};
assert_eq!(rc, 0, "wc_curve25519_shared_secret_ex failed (invalid key or buffer)");
unsafe {
wc_curve25519_free(&mut peer_key);
wc_FreeRng(&mut rng);
}
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) -> X448SharedSecret {
let mut peer_key = wc_curve448_key::zeroed();
let rc = unsafe { wc_curve448_init(&mut peer_key) };
assert_eq!(rc, 0, "wc_curve448_init (peer) failed (OOM)");
let rc = unsafe {
wc_curve448_import_public_ex(
peer_public.bytes.as_ptr(),
X448_KEY_LEN as u32,
&mut peer_key,
EC448_LITTLE_ENDIAN,
)
};
assert_eq!(rc, 0, "wc_curve448_import_public_ex failed (invalid public key)");
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,
)
};
assert_eq!(rc, 0, "wc_curve448_shared_secret_ex failed (invalid key or buffer)");
unsafe {
wc_curve448_free(&mut peer_key);
}
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(all(wolfssl_openssl_extra, wolfssl_ecc))]
mod nist_ecdh {
use core::ffi::{c_int, c_void};
use core::marker::PhantomData;
use core::ptr;
use alloc::vec;
use alloc::vec::Vec;
use zeroize::ZeroizeOnDrop;
use crate::error::WolfCryptError;
use wolfcrypt_rs::{
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_POINT_free, EC_POINT_new, EC_POINT_oct2point, EC_POINT_point2oct,
ECDH_compute_key,
NID_X9_62_prime256v1,
EC_GROUP, EC_KEY,
point_conversion_form_t,
};
#[cfg(wolfssl_ecc_p384)]
use wolfcrypt_rs::NID_secp384r1;
#[cfg(wolfssl_ecc_p521)]
use wolfcrypt_rs::NID_secp521r1;
mod sealed {
pub trait Sealed {}
}
pub trait NistCurve: sealed::Sealed + 'static {
const NID: c_int;
const FIELD_SIZE: usize;
const POINT_SIZE: usize;
}
pub struct NistP256;
impl sealed::Sealed for NistP256 {}
impl NistCurve for NistP256 {
const NID: c_int = NID_X9_62_prime256v1;
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 NID: c_int = NID_secp384r1;
const FIELD_SIZE: usize = 48;
const POINT_SIZE: usize = 97; }
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);
}
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> {
ec_key: *mut EC_KEY,
group: *mut EC_GROUP,
_curve: PhantomData<C>,
}
impl<C: NistCurve> NistEcdhSecret<C> {
#[cfg(feature = "rand")]
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 ec_key = unsafe { EC_KEY_new() };
if ec_key.is_null() {
unsafe { EC_GROUP_free(group) };
return Err(WolfCryptError::ALLOC_FAILED);
}
let rc = unsafe { EC_KEY_set_group(ec_key, group) };
if rc != 1 {
unsafe {
EC_KEY_free(ec_key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_set_group" });
}
let rc = unsafe { EC_KEY_generate_key(ec_key) };
if rc != 1 {
unsafe {
EC_KEY_free(ec_key);
EC_GROUP_free(group);
}
return Err(WolfCryptError::Ffi { code: rc, func: "EC_KEY_generate_key" });
}
Ok(Self {
ec_key,
group,
_curve: PhantomData,
})
}
pub fn public_key(&self) -> NistEcdhPublicKey<C> {
let point = unsafe { EC_KEY_get0_public_key(self.ec_key) };
assert!(!point.is_null(), "EC_KEY_get0_public_key returned null");
let mut buf = vec![0u8; C::POINT_SIZE];
let n = unsafe {
EC_POINT_point2oct(
self.group,
point,
point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED,
buf.as_mut_ptr(),
buf.len(),
ptr::null_mut(),
)
};
assert_eq!(
n, C::POINT_SIZE,
"EC_POINT_point2oct returned unexpected size: {n}"
);
NistEcdhPublicKey {
bytes: buf,
_curve: PhantomData,
}
}
pub fn diffie_hellman(
self,
peer_public: &NistEcdhPublicKey<C>,
) -> NistEcdhSharedSecret<C> {
let peer_point = unsafe { EC_POINT_new(self.group) };
assert!(!peer_point.is_null(), "EC_POINT_new failed");
let rc = unsafe {
EC_POINT_oct2point(
self.group,
peer_point,
peer_public.bytes.as_ptr(),
peer_public.bytes.len(),
ptr::null_mut(),
)
};
assert_eq!(rc, 1, "EC_POINT_oct2point failed (invalid point encoding)");
let mut out = vec![0u8; C::FIELD_SIZE];
let rc = unsafe {
ECDH_compute_key(
out.as_mut_ptr() as *mut c_void,
C::FIELD_SIZE,
peer_point,
self.ec_key,
ptr::null_mut(),
)
};
assert_eq!(
rc as usize, C::FIELD_SIZE,
"ECDH_compute_key failed (invalid key or point)"
);
unsafe { EC_POINT_free(peer_point) };
NistEcdhSharedSecret {
bytes: out,
_curve: PhantomData,
}
}
}
impl<C: NistCurve> Drop for NistEcdhSecret<C> {
fn drop(&mut self) {
unsafe {
EC_KEY_free(self.ec_key);
EC_GROUP_free(self.group);
}
}
}
unsafe impl<C: NistCurve> Send for NistEcdhSecret<C> {}
#[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 NID: c_int = NID_secp521r1;
const FIELD_SIZE: usize = 66;
const POINT_SIZE: usize = 133; }
pub type P256EcdhSecret = NistEcdhSecret<NistP256>;
#[cfg(wolfssl_ecc_p384)]
pub type P384EcdhSecret = NistEcdhSecret<NistP384>;
#[cfg(wolfssl_ecc_p521)]
pub type P521EcdhSecret = NistEcdhSecret<NistP521>;
}
#[cfg(all(wolfssl_openssl_extra, wolfssl_ecc))]
pub use nist_ecdh::{
NistCurve, NistEcdhPublicKey, NistEcdhSecret, NistEcdhSharedSecret,
NistP256, P256EcdhSecret,
};
#[cfg(all(wolfssl_openssl_extra, wolfssl_ecc, wolfssl_sha384))]
pub use nist_ecdh::{NistP384, P384EcdhSecret};
#[cfg(all(wolfssl_openssl_extra, wolfssl_ecc, wolfssl_sha512))]
pub use nist_ecdh::{NistP521, P521EcdhSecret};