use crate::error::{Error, Result};
use curve25519_dalek::{EdwardsPoint, MontgomeryPoint, Scalar};
use ring::digest::{SHA512, digest};
use ring::rand::{SecureRandom, SystemRandom, generate};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Debug, Zeroize, ZeroizeOnDrop)]
pub struct XEdDSAPrivateKey {
montgomery_private_key: Scalar,
}
impl From<Scalar> for XEdDSAPrivateKey {
fn from(montgomery_private_key: Scalar) -> Self {
XEdDSAPrivateKey {
montgomery_private_key,
}
}
}
impl XEdDSAPrivateKey {
pub fn generate(rng: &impl SecureRandom) -> Self {
XEdDSAPrivateKey {
montgomery_private_key: Scalar::from_bytes_mod_order(generate(rng).unwrap().expose()),
}
}
pub fn compute_public_key(&self) -> XEdDSAPublicKey {
XEdDSAPublicKey {
montgomery_public_key: MontgomeryPoint::mul_base(&self.montgomery_private_key),
}
}
pub fn agree_ephemeral(&self, peer_public_key: &[u8]) -> Result<Vec<u8>> {
Ok((self.montgomery_private_key
* MontgomeryPoint(
peer_public_key
.as_ref()
.try_into()
.map_err(|_| Error::Failed("Invalid DH public key.".to_string()))?,
))
.as_bytes()
.to_vec())
}
pub fn sign(&self, message: impl AsRef<[u8]>) -> Vec<u8> {
let mut edwards_private_key = self.montgomery_private_key;
let edwards_public_key = EdwardsPoint::mul_base(&edwards_private_key);
let mut edwards_public_key_y = edwards_public_key.compress().to_bytes();
if edwards_public_key_y[31] >= 0x80 {
edwards_private_key = -self.montgomery_private_key;
let edwards_public_key = EdwardsPoint::mul_base(&edwards_private_key);
edwards_public_key_y = edwards_public_key.compress().to_bytes();
}
let mut to_digest = vec![0xFF; 32];
to_digest.extend(edwards_private_key.as_bytes());
to_digest.extend(message.as_ref());
to_digest.extend(generate::<[u8; 64]>(&SystemRandom::new()).unwrap().expose());
let r = Scalar::from_bytes_mod_order_wide(
&digest(&SHA512, &to_digest).as_ref().try_into().unwrap(),
);
let r_ = EdwardsPoint::mul_base(&r);
let mut to_digest = r_.compress().as_bytes().to_vec();
to_digest.extend(edwards_public_key_y);
to_digest.extend(message.as_ref());
let h = Scalar::from_bytes_mod_order_wide(
&digest(&SHA512, &to_digest).as_ref().try_into().unwrap(),
);
let s = r + h * edwards_private_key;
let mut res = r_.compress().as_bytes().to_vec();
res.extend(s.as_bytes());
res
}
}
#[derive(Debug)]
pub struct XEdDSAPublicKey {
montgomery_public_key: MontgomeryPoint,
}
impl XEdDSAPublicKey {
pub fn new(bytes: &[u8]) -> Self {
XEdDSAPublicKey {
montgomery_public_key: MontgomeryPoint(bytes.try_into().unwrap()),
}
}
pub fn verify(&self, message: impl AsRef<[u8]>, signature: &[u8]) -> Result<()> {
if signature.len() != 64 {
return Err(Error::Signature);
}
let edwards_public_key = self
.montgomery_public_key
.to_edwards(0)
.ok_or(Error::Signature)?;
ring::signature::UnparsedPublicKey::new(
&ring::signature::ED25519,
edwards_public_key.compress().as_bytes(),
)
.verify(message.as_ref(), signature)
.map_err(|_| Error::Signature)?;
Ok(())
}
}
impl AsRef<[u8]> for XEdDSAPublicKey {
fn as_ref(&self) -> &[u8] {
self.montgomery_public_key.as_bytes()
}
}