use alloc::{string::ToString, vec::Vec};
use hkdf::{Hkdf, hmac::SimpleHmac};
use k256::{AffinePoint, elliptic_curve::sec1::ToEncodedPoint, sha2::Sha256};
use rand::{CryptoRng, RngCore};
use crate::{
dsa::ecdsa_k256_keccak::{KeyExchangeKey, PUBLIC_KEY_BYTES, PublicKey},
ecdh::KeyAgreementScheme,
utils::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing},
},
};
pub struct SharedSecret {
pub(crate) inner: k256::ecdh::SharedSecret,
}
impl SharedSecret {
pub(crate) fn new(inner: k256::ecdh::SharedSecret) -> SharedSecret {
Self { inner }
}
pub fn extract(&self, salt: Option<&[u8]>) -> Hkdf<Sha256, SimpleHmac<Sha256>> {
self.inner.extract(salt)
}
}
impl AsRef<[u8]> for SharedSecret {
fn as_ref(&self) -> &[u8] {
self.inner.raw_secret_bytes()
}
}
impl Zeroize for SharedSecret {
fn zeroize(&mut self) {
let bytes = self.inner.raw_secret_bytes();
for byte in
unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len()) }
{
unsafe {
core::ptr::write_volatile(byte, 0u8);
}
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
}
impl ZeroizeOnDrop for SharedSecret {}
pub struct EphemeralSecretKey {
inner: k256::ecdh::EphemeralSecret,
}
impl EphemeralSecretKey {
#[cfg(feature = "std")]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let mut rng = rand::rng();
Self::with_rng(&mut rng)
}
pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
use k256::elliptic_curve::rand_core::SeedableRng;
let mut seed = Zeroizing::new([0_u8; 32]);
RngCore::fill_bytes(rng, &mut *seed);
let mut rng = rand_hc::Hc128Rng::from_seed(*seed);
let sk_e = k256::ecdh::EphemeralSecret::random(&mut rng);
Self { inner: sk_e }
}
pub fn public_key(&self) -> EphemeralPublicKey {
let pk = self.inner.public_key();
EphemeralPublicKey { inner: pk }
}
pub fn diffie_hellman(&self, pk_other: PublicKey) -> SharedSecret {
let shared_secret_inner = self.inner.diffie_hellman(&pk_other.inner.into());
SharedSecret { inner: shared_secret_inner }
}
}
impl ZeroizeOnDrop for EphemeralSecretKey {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EphemeralPublicKey {
pub(crate) inner: k256::PublicKey,
}
impl EphemeralPublicKey {
pub fn as_affine(&self) -> &AffinePoint {
self.inner.as_affine()
}
}
impl Serializable for EphemeralPublicKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let encoded = self.inner.to_encoded_point(true);
target.write_bytes(encoded.as_bytes());
}
}
impl Deserializable for EphemeralPublicKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
let inner = k256::PublicKey::from_sec1_bytes(&bytes)
.map_err(|_| DeserializationError::InvalidValue("Invalid public key".to_string()))?;
Ok(Self { inner })
}
}
pub struct K256;
impl KeyAgreementScheme for K256 {
type EphemeralSecretKey = EphemeralSecretKey;
type EphemeralPublicKey = EphemeralPublicKey;
type SecretKey = KeyExchangeKey;
type PublicKey = PublicKey;
type SharedSecret = SharedSecret;
fn generate_ephemeral_keypair<R: CryptoRng + RngCore>(
rng: &mut R,
) -> (Self::EphemeralSecretKey, Self::EphemeralPublicKey) {
let sk = EphemeralSecretKey::with_rng(rng);
let pk = sk.public_key();
(sk, pk)
}
fn exchange_ephemeral_static(
ephemeral_sk: Self::EphemeralSecretKey,
static_pk: &Self::PublicKey,
) -> Result<Self::SharedSecret, super::KeyAgreementError> {
Ok(ephemeral_sk.diffie_hellman(static_pk.clone()))
}
fn exchange_static_ephemeral(
static_sk: &Self::SecretKey,
ephemeral_pk: &Self::EphemeralPublicKey,
) -> Result<Self::SharedSecret, super::KeyAgreementError> {
Ok(static_sk.get_shared_secret(ephemeral_pk.clone()))
}
fn extract_key_material(
shared_secret: &Self::SharedSecret,
length: usize,
info: &[u8],
) -> Result<Vec<u8>, super::KeyAgreementError> {
let hkdf = shared_secret.extract(None);
let mut buf = vec![0_u8; length];
hkdf.expand(info, &mut buf)
.map_err(|_| super::KeyAgreementError::HkdfExpansionFailed)?;
Ok(buf)
}
}
#[cfg(test)]
mod test {
use super::{EphemeralPublicKey, EphemeralSecretKey};
use crate::{
dsa::ecdsa_k256_keccak::KeyExchangeKey,
rand::test_utils::seeded_rng,
utils::{Deserializable, Serializable},
};
#[test]
fn key_agreement() {
let mut rng = seeded_rng([0u8; 32]);
let sk = KeyExchangeKey::with_rng(&mut rng);
let pk = sk.public_key();
let sk_e = EphemeralSecretKey::with_rng(&mut rng);
let pk_e = sk_e.public_key();
let shared_secret_key_1 = sk_e.diffie_hellman(pk);
let shared_secret_key_2 = sk.get_shared_secret(pk_e);
assert_eq!(
shared_secret_key_1.inner.raw_secret_bytes(),
shared_secret_key_2.inner.raw_secret_bytes()
);
}
#[test]
fn test_serialization_round_trip() {
let mut rng = seeded_rng([1u8; 32]);
let sk_e = EphemeralSecretKey::with_rng(&mut rng);
let pk_e = sk_e.public_key();
let pk_e_bytes = pk_e.to_bytes();
let pk_e_serialized = EphemeralPublicKey::read_from_bytes(&pk_e_bytes)
.expect("failed to desrialize ephemeral public key");
assert_eq!(pk_e_serialized, pk_e);
}
}