use crate::{cpu, ec, error, rand};
use untrusted;
pub use crate::ec::{
curve25519::x25519::X25519,
suite_b::ecdh::{ECDH_P256, ECDH_P384},
};
pub struct Algorithm {
pub(crate) curve: &'static ec::Curve,
pub(crate) ecdh: fn(
out: &mut [u8],
private_key: &ec::Seed,
peer_public_key: untrusted::Input,
) -> Result<(), error::Unspecified>,
}
derive_debug_via_field!(Algorithm, curve);
impl Eq for Algorithm {}
impl PartialEq for Algorithm {
fn eq(&self, other: &Algorithm) -> bool {
self.curve.id == other.curve.id
}
}
pub struct EphemeralPrivateKey {
private_key: ec::Seed,
alg: &'static Algorithm,
}
impl EphemeralPrivateKey {
pub fn generate(
alg: &'static Algorithm,
rng: &dyn rand::SecureRandom,
) -> Result<Self, error::Unspecified> {
let cpu_features = cpu::features();
let private_key = ec::Seed::generate(&alg.curve, rng, cpu_features)?;
Ok(Self { private_key, alg })
}
#[inline(always)]
pub fn compute_public_key(&self) -> Result<PublicKey, error::Unspecified> {
self.private_key.compute_public_key().map(PublicKey)
}
#[cfg(test)]
pub fn bytes(&self) -> &[u8] {
self.private_key.bytes_less_safe()
}
}
#[derive(Clone)]
pub struct PublicKey(ec::PublicKey);
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
derive_debug_self_as_ref_hex_bytes!(PublicKey);
pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
algorithm: &'static Algorithm,
bytes: B,
}
impl<B: Copy> Copy for UnparsedPublicKey<B> where B: AsRef<[u8]> {}
impl<B: Clone> Clone for UnparsedPublicKey<B>
where
B: AsRef<[u8]>,
{
fn clone(&self) -> Self {
Self {
algorithm: self.algorithm,
bytes: self.bytes.clone(),
}
}
}
impl<B: core::fmt::Debug> core::fmt::Debug for UnparsedPublicKey<B>
where
B: AsRef<[u8]>,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.debug_struct("UnparsedPublicKey")
.field("algorithm", &self.algorithm)
.field("bytes", &self.bytes)
.finish()
}
}
impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
Self { algorithm, bytes }
}
#[inline]
pub fn algorithm(&self) -> &'static Algorithm {
self.algorithm
}
#[inline]
pub fn bytes(&self) -> &B {
&self.bytes
}
}
#[inline]
pub fn agree_ephemeral<B: AsRef<[u8]>, F, R, E>(
my_private_key: EphemeralPrivateKey,
peer_public_key: &UnparsedPublicKey<B>,
error_value: E,
kdf: F,
) -> Result<R, E>
where
F: FnOnce(&[u8]) -> Result<R, E>,
{
let peer_public_key = UnparsedPublicKey {
algorithm: peer_public_key.algorithm,
bytes: peer_public_key.bytes.as_ref(),
};
agree_ephemeral_(my_private_key, peer_public_key, error_value, kdf)
}
fn agree_ephemeral_<F, R, E>(
my_private_key: EphemeralPrivateKey,
peer_public_key: UnparsedPublicKey<&[u8]>,
error_value: E,
kdf: F,
) -> Result<R, E>
where
F: FnOnce(&[u8]) -> Result<R, E>,
{
if peer_public_key.algorithm != my_private_key.alg {
return Err(error_value);
}
let alg = &my_private_key.alg;
let mut shared_key = [0u8; ec::ELEM_MAX_BYTES];
let shared_key = &mut shared_key[..alg.curve.elem_scalar_seed_len];
(alg.ecdh)(
shared_key,
&my_private_key.private_key,
untrusted::Input::from(peer_public_key.bytes),
)
.map_err(|_| error_value)?;
kdf(shared_key)
}