c255b3 0.0.3

Schorr signatures based Curve25519 and Blake3
Documentation
use curve25519_dalek::{edwards::CompressedEdwardsY, EdwardsPoint};
use subtle::ConstantTimeEq;

use crate::{Domain, Error, Signature};

#[derive(Clone, Eq)]
/// A c25519 public key to verify signatures.
pub struct PublicKey {
    /// The compressed version of the public key.
    pub(crate) compressed: CompressedEdwardsY,
    /// The uncompressed version of the public key.
    pub(crate) point: EdwardsPoint,
}

/// This is based only on the the compressed point, and is constant time.
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 {
    /// Parse a public key from a byte array.
    ///
    /// This both verifies that it decompresses properly and is not a "weak key" -
    /// a key that will verify for most messages.
    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 })
    }

    /// Render to a byte array for external use.
    pub fn to_bits(&self) -> [u8; 32] {
        *self.compressed.as_bytes()
    }

    /// Verify a signature, in a particular domain, using this public key.
    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(())
    }

    /// Verify a signature, in the default domain, using this public key.
    pub fn verify(&self, sig: &Signature, msg: &[u8]) -> Result<(), Error> {
        self.verify_domain(Domain::default(), sig, msg)
    }
}