use crate::{
traits::{self, CryptoMaterialError, ValidCryptoMaterial, ValidCryptoMaterialStringExt},
x25519,
};
use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay};
use rand::{CryptoRng, RngCore};
use std::convert::{TryFrom, TryInto};
#[cfg(any(test, feature = "fuzzing"))]
use proptest_derive::Arbitrary;
pub use x25519_dalek;
pub const PRIVATE_KEY_SIZE: usize = 32;
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const SHARED_SECRET_SIZE: usize = 32;
#[derive(DeserializeKey, SilentDisplay, SilentDebug, SerializeKey)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Clone))]
pub struct PrivateKey(x25519_dalek::StaticSecret);
#[derive(
Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, SerializeKey, DeserializeKey,
)]
#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
pub struct PublicKey([u8; PUBLIC_KEY_SIZE]);
impl PrivateKey {
pub fn public_key(&self) -> PublicKey {
let public_key: x25519_dalek::PublicKey = (&self.0).into();
PublicKey(public_key.as_bytes().to_owned())
}
pub fn diffie_hellman(&self, remote_public_key: &PublicKey) -> [u8; SHARED_SECRET_SIZE] {
let remote_public_key = x25519_dalek::PublicKey::from(remote_public_key.0);
let shared_secret = self.0.diffie_hellman(&remote_public_key);
shared_secret.as_bytes().to_owned()
}
pub fn from_ed25519_private_bytes(private_slice: &[u8]) -> Result<Self, CryptoMaterialError> {
let ed25519_secretkey = ed25519_dalek::SecretKey::from_bytes(private_slice)
.map_err(|_| CryptoMaterialError::DeserializationError)?;
let expanded_key = ed25519_dalek::ExpandedSecretKey::from(&ed25519_secretkey);
let mut expanded_keypart = [0u8; 32];
expanded_keypart.copy_from_slice(&expanded_key.to_bytes()[..32]);
let potential_x25519 = x25519::PrivateKey::from(expanded_keypart);
if potential_x25519.to_bytes()[..] != expanded_key.to_bytes()[..32] {
Err(CryptoMaterialError::DeserializationError)
} else {
Ok(potential_x25519)
}
}
}
impl PublicKey {
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn from_ed25519_public_bytes(ed25519_bytes: &[u8]) -> Result<Self, CryptoMaterialError> {
if ed25519_bytes.len() != 32 {
return Err(CryptoMaterialError::DeserializationError);
}
let ed_point = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(ed25519_bytes)
.decompress()
.ok_or(CryptoMaterialError::DeserializationError)?;
Ok(x25519::PublicKey::from(ed_point.to_montgomery().to_bytes()))
}
}
impl std::convert::From<[u8; PRIVATE_KEY_SIZE]> for PrivateKey {
fn from(private_key_bytes: [u8; PRIVATE_KEY_SIZE]) -> Self {
Self(x25519_dalek::StaticSecret::from(private_key_bytes))
}
}
impl std::convert::TryFrom<&[u8]> for PrivateKey {
type Error = traits::CryptoMaterialError;
fn try_from(private_key_bytes: &[u8]) -> Result<Self, Self::Error> {
let private_key_bytes: [u8; PRIVATE_KEY_SIZE] = private_key_bytes
.try_into()
.map_err(|_| traits::CryptoMaterialError::DeserializationError)?;
Ok(Self(x25519_dalek::StaticSecret::from(private_key_bytes)))
}
}
impl traits::PrivateKey for PrivateKey {
type PublicKeyMaterial = PublicKey;
}
impl traits::Uniform for PrivateKey {
fn generate<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
Self(x25519_dalek::StaticSecret::new(rng))
}
}
impl traits::ValidCryptoMaterial for PrivateKey {
fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
}
#[cfg(any(test, feature = "fuzzing"))]
impl PartialEq for PrivateKey {
fn eq(&self, other: &Self) -> bool {
self.to_bytes() == other.to_bytes()
}
}
#[cfg(any(test, feature = "fuzzing"))]
impl proptest::arbitrary::Arbitrary for PrivateKey {
type Parameters = ();
type Strategy = proptest::strategy::BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
use proptest::strategy::Strategy as _;
proptest::arbitrary::any::<[u8; 32]>()
.prop_map(PrivateKey::from)
.boxed()
}
}
impl From<&PrivateKey> for PublicKey {
fn from(private_key: &PrivateKey) -> Self {
private_key.public_key()
}
}
impl std::convert::From<[u8; PUBLIC_KEY_SIZE]> for PublicKey {
fn from(public_key_bytes: [u8; PUBLIC_KEY_SIZE]) -> Self {
Self(public_key_bytes)
}
}
impl std::convert::TryFrom<&[u8]> for PublicKey {
type Error = traits::CryptoMaterialError;
fn try_from(public_key_bytes: &[u8]) -> Result<Self, Self::Error> {
let public_key_bytes: [u8; PUBLIC_KEY_SIZE] = public_key_bytes
.try_into()
.map_err(|_| traits::CryptoMaterialError::WrongLengthError)?;
Ok(Self(public_key_bytes))
}
}
impl traits::PublicKey for PublicKey {
type PrivateKeyMaterial = PrivateKey;
}
impl traits::ValidCryptoMaterial for PublicKey {
fn to_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}
impl std::fmt::Display for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
impl std::fmt::Debug for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "x25519::PublicKey({})", self)
}
}
#[cfg(any(test, feature = "fuzzing"))]
use crate::test_utils::{self, KeyPair};
#[cfg(any(test, feature = "fuzzing"))]
use proptest::prelude::*;
#[cfg(any(test, feature = "fuzzing"))]
pub fn keypair_strategy() -> impl Strategy<Value = KeyPair<PrivateKey, PublicKey>> {
test_utils::uniform_keypair_strategy::<PrivateKey, PublicKey>()
}