#[cfg(feature = "rustcrypto-ec")]
use elliptic_curve::{
point::PointCompression,
sec1::{CompressedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint},
AffinePoint, Curve, CurveArithmetic,
};
use rand_core::CryptoRngCore;
use crate::{curves, curves::KeyPair, error::*};
#[cfg(feature = "dalek")]
use curves::{dalek, X25519};
#[cfg(feature = "rustcrypto-ec")]
use curves::{rcec, EllipticCurve};
#[derive(Clone, Debug)]
pub struct BlindedPublicKey<K: KeyPair> {
pub inner: K::PublicKey,
pub blinding_factor: K::PublicKey,
}
#[cfg(feature = "dalek")]
impl Blinded for BlindedPublicKey<X25519> {
type BytesArray = [u8; 64];
fn from_bytes(bytes: &Self::BytesArray) -> Result<Self> {
let inner_bytes: [u8; 32] = bytes[0..32].try_into().map_err(|_| Error::Decoding)?;
let inner = dalek::PublicKey::from(inner_bytes);
let blinding_factor_bytes: [u8; 32] =
bytes[32..64].try_into().map_err(|_| Error::Decoding)?;
let blinding_factor = dalek::PublicKey::from(blinding_factor_bytes);
Ok(Self {
inner,
blinding_factor,
})
}
fn to_bytes(&self) -> Self::BytesArray {
let mut output = [0u8; 64];
output[0..32].copy_from_slice(self.inner.as_bytes());
output[32..64].copy_from_slice(self.blinding_factor.as_bytes());
output
}
}
#[cfg(feature = "rustcrypto-ec")]
impl<C: CurveArithmetic + PointCompression> Blinded for BlindedPublicKey<EllipticCurve<C>>
where
<C as Curve>::FieldBytesSize: ModulusSize,
<C as CurveArithmetic>::AffinePoint: ToEncodedPoint<C> + FromEncodedPoint<C>,
{
type BytesArray = [u8; 66];
fn from_bytes(bytes: &Self::BytesArray) -> Result<Self> {
let inner =
rcec::PublicKey::<C>::from_sec1_bytes(&bytes[0..33]).map_err(|_| Error::Decoding)?;
let blinding_factor =
rcec::PublicKey::<C>::from_sec1_bytes(&bytes[33..66]).map_err(|_| Error::Decoding)?;
Ok(Self {
inner,
blinding_factor,
})
}
fn to_bytes(&self) -> [u8; 66] {
let inner_cp = CompressedPoint::<C>::from(&self.inner);
let blinding_factor_cp = CompressedPoint::<C>::from(&self.blinding_factor);
let mut output = [0u8; 66];
output[0..33].copy_from_slice(inner_cp.as_slice());
output[33..66].copy_from_slice(blinding_factor_cp.as_slice());
output
}
}
pub trait Blind<K: KeyPair> {
fn blind(&self, csprng: &mut impl CryptoRngCore) -> BlindedPublicKey<K>;
}
pub trait Blinded: Sized {
type BytesArray;
fn from_bytes(bytes: &Self::BytesArray) -> Result<Self>;
fn to_bytes(&self) -> Self::BytesArray;
}
impl<'a, K: KeyPair> TryFrom<&'a [u8]> for BlindedPublicKey<K>
where
Self: Blinded,
<Self as Blinded>::BytesArray: TryFrom<&'a [u8]>,
{
type Error = Error;
fn try_from(bytes: &'a [u8]) -> Result<Self> {
Self::from_bytes(
&<Self as Blinded>::BytesArray::try_from(bytes).map_err(|_| Error::Decoding)?,
)
}
}
#[cfg(feature = "dalek")]
pub(crate) fn random_blind_dalek(
public_key: &dalek::PublicKey,
return_blinding_factor: bool,
csprng: &mut impl CryptoRngCore,
) -> (
dalek::PublicKey,
Option<dalek::PublicKey>,
dalek::StaticSecret,
) {
let blinding_factor_secret = dalek::StaticSecret::random_from_rng(csprng);
let blinding_factor = if return_blinding_factor {
Some(dalek::PublicKey::from(&blinding_factor_secret))
} else {
None
};
let blinded_public_key = blind_dalek(public_key, &blinding_factor_secret);
(blinded_public_key, blinding_factor, blinding_factor_secret)
}
#[cfg(feature = "dalek")]
pub(crate) fn blind_dalek(
public_key: &dalek::PublicKey,
blinding_factor_secret: &dalek::StaticSecret,
) -> dalek::PublicKey {
let blinded_shared_secret = blinding_factor_secret.diffie_hellman(public_key);
dalek::PublicKey::from(*blinded_shared_secret.as_bytes())
}
#[cfg(feature = "dalek")]
impl Blind<X25519> for dalek::PublicKey {
fn blind(&self, csprng: &mut impl CryptoRngCore) -> BlindedPublicKey<X25519> {
let (inner, blinding_factor, _) = random_blind_dalek(self, true, csprng);
BlindedPublicKey {
inner,
blinding_factor: blinding_factor.expect("Infallible"),
}
}
}
#[cfg(feature = "rustcrypto-ec")]
fn diffie_hellman_affine<C: CurveArithmetic>(
secret_key: &rcec::SecretKey<C>,
public_key: &rcec::PublicKey<C>,
) -> AffinePoint<C> {
use elliptic_curve::{group::Curve, ProjectivePoint};
let public_point = ProjectivePoint::<C>::from(*public_key.as_affine());
(public_point * secret_key.to_nonzero_scalar().as_ref()).to_affine()
}
#[cfg(feature = "rustcrypto-ec")]
pub(crate) fn random_blind_rcec<C: CurveArithmetic>(
public_key: &rcec::PublicKey<C>,
return_blinding_factor: bool,
csprng: &mut impl CryptoRngCore,
) -> (
rcec::PublicKey<C>,
Option<rcec::PublicKey<C>>,
rcec::SecretKey<C>,
) {
let blinding_factor_secret = rcec::SecretKey::<C>::random(csprng);
let blinding_factor = if return_blinding_factor {
Some(blinding_factor_secret.public_key())
} else {
None
};
let blinded_public_key = blind_rcec(public_key, &blinding_factor_secret);
(blinded_public_key, blinding_factor, blinding_factor_secret)
}
#[cfg(feature = "rustcrypto-ec")]
pub(crate) fn blind_rcec<C: CurveArithmetic>(
public_key: &rcec::PublicKey<C>,
blinding_factor_secret: &rcec::SecretKey<C>,
) -> rcec::PublicKey<C> {
let blinded_shared_secret = diffie_hellman_affine(blinding_factor_secret, public_key);
rcec::PublicKey::<C>::from_affine(blinded_shared_secret)
.expect("Should not be an identity point")
}
#[cfg(feature = "rustcrypto-ec")]
impl<C: CurveArithmetic> Blind<EllipticCurve<C>> for rcec::PublicKey<C> {
fn blind(&self, csprng: &mut impl CryptoRngCore) -> BlindedPublicKey<EllipticCurve<C>> {
let (inner, blinding_factor, _) = random_blind_rcec(self, true, csprng);
BlindedPublicKey {
inner,
blinding_factor: blinding_factor.expect("Infallible"),
}
}
}