use curve25519_dalek::{edwards::CompressedEdwardsY, EdwardsPoint};
use subtle::ConstantTimeEq;
use crate::{Domain, Error, Signature};
#[derive(Clone, Eq)]
pub struct PublicKey {
pub(crate) compressed: CompressedEdwardsY,
pub(crate) point: EdwardsPoint,
}
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl core::fmt::LowerHex for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for x in self.compressed.to_bytes() {
write!(f, "{:02x}", x)?;
}
Ok(())
}
}
impl core::fmt::Debug for PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PublicKey {:x}", *self)
}
}
impl ConstantTimeEq for PublicKey {
fn ct_eq(&self, other: &Self) -> subtle::Choice {
self.compressed.ct_eq(&other.compressed)
}
}
impl PublicKey {
pub fn parse_bits(value: &[u8; 32]) -> Result<PublicKey, Error> {
let compressed = CompressedEdwardsY(*value);
let point = compressed.decompress().ok_or(Error::FailedDecompression)?;
if point.is_small_order() {
return Err(Error::WeakPublicKey);
}
Ok(PublicKey { compressed, point })
}
pub fn to_bits(&self) -> [u8; 32] {
*self.compressed.as_bytes()
}
pub fn verify_domain(&self, domain: Domain, sig: &Signature, msg: &[u8]) -> Result<(), Error> {
let r_v = EdwardsPoint::vartime_double_scalar_mul_basepoint(&sig.e, &self.point, &sig.s);
let e_v = domain.hash_to_scalar(&r_v, msg);
if e_v != sig.e {
return Err(Error::SignatureMismatch);
}
Ok(())
}
pub fn verify(&self, sig: &Signature, msg: &[u8]) -> Result<(), Error> {
self.verify_domain(Domain::default(), sig, msg)
}
}