use core::{
convert::TryFrom,
fmt::Display,
ops::{Add, Div, Mul, Sub},
};
use curve25519_dalek::{
constants,
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
};
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::{
asymmetric_crypto::DhKeyPair, bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore,
CryptoCoreError, KeyTrait,
};
pub const X25519_PRIVATE_KEY_LENGTH: usize = 32;
pub const X25519_PUBLIC_KEY_LENGTH: usize = 32;
#[derive(Hash, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(try_from = "&[u8]", into = "[u8; 32]")]
pub struct X25519PrivateKey(Scalar);
impl X25519PrivateKey {
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl KeyTrait<X25519_PRIVATE_KEY_LENGTH> for X25519PrivateKey {
#[inline]
fn new<R: CryptoRngCore>(rng: &mut R) -> Self {
let mut bytes = [0; 64];
rng.fill_bytes(&mut bytes);
Self(Scalar::from_bytes_mod_order_wide(&bytes))
}
#[inline]
fn to_bytes(&self) -> [u8; Self::LENGTH] {
self.0.to_bytes()
}
#[inline]
fn try_from_bytes(bytes: &[u8]) -> Result<Self, CryptoCoreError> {
Self::try_from(bytes)
}
}
impl Serializable for X25519PrivateKey {
type Error = CryptoCoreError;
fn length(&self) -> usize {
Self::LENGTH
}
fn write(&self, ser: &mut crate::bytes_ser_de::Serializer) -> Result<usize, Self::Error> {
ser.write_array(self.as_bytes())
}
fn read(de: &mut crate::bytes_ser_de::Deserializer) -> Result<Self, Self::Error> {
Self::try_from(de.read_array::<{ Self::LENGTH }>()?)
}
}
impl TryFrom<[u8; Self::LENGTH]> for X25519PrivateKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(bytes: [u8; Self::LENGTH]) -> Result<Self, Self::Error> {
let scalar = Scalar::from_canonical_bytes(bytes).ok_or_else(|| {
Self::Error::ConversionError(
"Given bytes do not represent a canonical Scalar!".to_string(),
)
})?;
Ok(Self(scalar))
}
}
impl TryFrom<&[u8]> for X25519PrivateKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let bytes = <[u8; Self::LENGTH]>::try_from(bytes)
.map_err(|e| Self::Error::ConversionError(e.to_string()))?;
Self::try_from(bytes)
}
}
impl From<X25519PrivateKey> for [u8; X25519_PRIVATE_KEY_LENGTH] {
#[inline]
fn from(key: X25519PrivateKey) -> Self {
key.to_bytes()
}
}
impl TryFrom<&str> for X25519PrivateKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
let bytes = hex::decode(value)?;
Self::try_from(bytes.as_slice())
}
}
impl Display for X25519PrivateKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex::encode(self.0.as_bytes()))
}
}
impl<'a> Add<&'a X25519PrivateKey> for &X25519PrivateKey {
type Output = X25519PrivateKey;
#[inline]
fn add(self, rhs: &X25519PrivateKey) -> Self::Output {
X25519PrivateKey(self.0 + rhs.0)
}
}
impl<'a> Sub<&'a X25519PrivateKey> for &X25519PrivateKey {
type Output = X25519PrivateKey;
#[inline]
fn sub(self, rhs: &X25519PrivateKey) -> Self::Output {
X25519PrivateKey(self.0 - rhs.0)
}
}
impl<'a> Mul<&'a X25519PrivateKey> for &X25519PrivateKey {
type Output = X25519PrivateKey;
#[inline]
fn mul(self, rhs: &X25519PrivateKey) -> Self::Output {
X25519PrivateKey(self.0 * rhs.0)
}
}
impl<'a> Div<&'a X25519PrivateKey> for &X25519PrivateKey {
type Output = X25519PrivateKey;
#[inline]
fn div(self, rhs: &X25519PrivateKey) -> Self::Output {
#[allow(clippy::suspicious_arithmetic_impl)]
X25519PrivateKey(self.0 * rhs.0.invert())
}
}
impl Zeroize for X25519PrivateKey {
#[inline]
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl Drop for X25519PrivateKey {
#[inline]
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for X25519PrivateKey {}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(try_from = "&[u8]", into = "[u8; 32]")]
pub struct X25519PublicKey(RistrettoPoint);
impl KeyTrait<X25519_PUBLIC_KEY_LENGTH> for X25519PublicKey {
#[inline]
fn new<R: CryptoRngCore>(rng: &mut R) -> Self {
let mut uniform_bytes = [0u8; 64];
rng.fill_bytes(&mut uniform_bytes);
Self(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
}
#[inline]
fn to_bytes(&self) -> [u8; Self::LENGTH] {
self.0.compress().to_bytes()
}
#[inline]
fn try_from_bytes(bytes: &[u8]) -> Result<Self, CryptoCoreError> {
Self::try_from(bytes)
}
}
impl Serializable for X25519PublicKey {
type Error = CryptoCoreError;
fn length(&self) -> usize {
Self::LENGTH
}
fn write(&self, ser: &mut crate::bytes_ser_de::Serializer) -> Result<usize, Self::Error> {
ser.write_array(&self.to_bytes())
}
fn read(de: &mut crate::bytes_ser_de::Deserializer) -> Result<Self, Self::Error> {
Self::try_from(de.read_array::<{ Self::LENGTH }>()?)
}
}
impl From<X25519PrivateKey> for X25519PublicKey {
#[inline]
fn from(private_key: X25519PrivateKey) -> Self {
Self(&private_key.0 * &constants::RISTRETTO_BASEPOINT_TABLE)
}
}
impl From<&X25519PrivateKey> for X25519PublicKey {
#[inline]
fn from(private_key: &X25519PrivateKey) -> Self {
Self(&private_key.0 * &constants::RISTRETTO_BASEPOINT_TABLE)
}
}
impl TryFrom<[u8; Self::LENGTH]> for X25519PublicKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(bytes: [u8; Self::LENGTH]) -> Result<Self, Self::Error> {
Ok(Self(CompressedRistretto(bytes).decompress().ok_or_else(
|| {
CryptoCoreError::ConversionError(
"Cannot decompress given bytes into a valid curve point!".to_string(),
)
},
)?))
}
}
impl TryFrom<&[u8]> for X25519PublicKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let bytes = <[u8; Self::LENGTH]>::try_from(bytes)
.map_err(|e| Self::Error::ConversionError(e.to_string()))?;
Self::try_from(bytes)
}
}
impl From<X25519PublicKey> for [u8; X25519_PUBLIC_KEY_LENGTH] {
#[inline]
fn from(key: X25519PublicKey) -> Self {
key.to_bytes()
}
}
impl TryFrom<&str> for X25519PublicKey {
type Error = CryptoCoreError;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
let bytes = hex::decode(value)?;
Self::try_from(bytes.as_slice())
}
}
impl Display for X25519PublicKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex::encode(self.0.compress().to_bytes()))
}
}
impl<'a> Sub<&'a X25519PublicKey> for &X25519PublicKey {
type Output = X25519PublicKey;
#[inline]
fn sub(self, rhs: &X25519PublicKey) -> Self::Output {
X25519PublicKey(self.0 - rhs.0)
}
}
impl<'a> Add<&'a X25519PublicKey> for &X25519PublicKey {
type Output = X25519PublicKey;
#[inline]
fn add(self, rhs: &X25519PublicKey) -> Self::Output {
X25519PublicKey(self.0 + rhs.0)
}
}
impl<'a> Mul<&'a X25519PrivateKey> for &X25519PublicKey {
type Output = X25519PublicKey;
#[inline]
fn mul(self, rhs: &X25519PrivateKey) -> Self::Output {
X25519PublicKey(self.0 * rhs.0)
}
}
impl Zeroize for X25519PublicKey {
#[inline]
fn zeroize(&mut self) {
self.0.zeroize()
}
}
impl Drop for X25519PublicKey {
#[inline]
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for X25519PublicKey {}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct X25519KeyPair {
pk: X25519PublicKey,
sk: X25519PrivateKey,
}
impl DhKeyPair<X25519_PUBLIC_KEY_LENGTH, X25519_PRIVATE_KEY_LENGTH> for X25519KeyPair {
type PrivateKey = X25519PrivateKey;
type PublicKey = X25519PublicKey;
#[inline]
fn new<R: CryptoRngCore>(rng: &mut R) -> Self {
let sk = X25519PrivateKey::new(rng);
let pk = X25519PublicKey::from(&sk);
Self { pk, sk }
}
#[inline]
fn public_key(&self) -> &Self::PublicKey {
&self.pk
}
#[inline]
fn private_key(&self) -> &Self::PrivateKey {
&self.sk
}
}
impl Zeroize for X25519KeyPair {
#[inline]
fn zeroize(&mut self) {
self.pk.zeroize();
self.sk.zeroize();
}
}
impl Drop for X25519KeyPair {
#[inline]
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for X25519KeyPair {}
#[cfg(test)]
mod test {
use crate::{
asymmetric_crypto::curve25519::{
DhKeyPair, TryFrom, X25519KeyPair, X25519PrivateKey, X25519PublicKey,
X25519_PRIVATE_KEY_LENGTH, X25519_PUBLIC_KEY_LENGTH,
},
reexport::rand_core::SeedableRng,
CsRng, KeyTrait,
};
#[test]
fn test_private_key_serialization() {
let mut rng = CsRng::from_entropy();
let sk = X25519PrivateKey::new(&mut rng);
let bytes: [u8; X25519_PRIVATE_KEY_LENGTH] = sk.to_bytes();
let recovered = X25519PrivateKey::try_from(bytes).unwrap();
assert_eq!(sk, recovered);
}
#[test]
fn test_public_key_serialization() {
let mut rng = CsRng::from_entropy();
let pk = X25519PublicKey::new(&mut rng);
let bytes: [u8; X25519_PUBLIC_KEY_LENGTH] = pk.to_bytes();
let recovered = super::X25519PublicKey::try_from(bytes).unwrap();
assert_eq!(pk, recovered);
}
#[test]
fn test_dh_key_pair() {
let mut rng = CsRng::from_entropy();
let kp1 = X25519KeyPair::new(&mut rng);
let kp2 = X25519KeyPair::new(&mut rng);
assert_ne!(kp1, kp2);
assert_eq!(
kp1.public_key() * kp2.private_key(),
kp2.public_key() * kp1.private_key()
);
}
}