use crate::common::*;
use core::fmt::Debug;
use curve25519_dalek::{
constants,
edwards::{CompressedEdwardsY, EdwardsPoint},
};
use sha2::{Digest, Sha512};
use super::{constants::*, errors::*, proof::*, secret::*};
#[derive(Copy, Clone, Default, Eq, PartialEq)]
pub struct PublicKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
impl Debug for PublicKey {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "PublicKey({:?}), {:?})", self.0, self.1)
}
}
impl Serial for PublicKey {
#[inline]
fn serial<B: Buffer>(&self, x: &mut B) {
x.write_all(&self.0.to_bytes())
.expect("Writing to buffer should succeed.")
}
}
impl Deserial for PublicKey {
#[inline]
fn deserial<R: ReadBytesExt>(source: &mut R) -> ParseResult<Self> {
let mut buf = [0u8; PUBLIC_KEY_LENGTH];
source.read_exact(&mut buf)?;
let compressed = CompressedEdwardsY(buf);
let point = compressed
.decompress()
.ok_or(ProofError(InternalError::PointDecompression))?;
if !point.is_small_order() {
Ok(PublicKey(compressed, point))
} else {
Err(ProofError(InternalError::Verify).into())
}
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl From<&SecretKey> for PublicKey {
fn from(secret_key: &SecretKey) -> PublicKey {
let expanded = ExpandedSecretKey::from(secret_key);
(&expanded).into()
}
}
impl From<&ExpandedSecretKey> for PublicKey {
fn from(expanded_secret_key: &ExpandedSecretKey) -> PublicKey {
let key = expanded_secret_key.key;
let point = &key * constants::ED25519_BASEPOINT_TABLE;
let compressed = point.compress();
PublicKey(compressed, point)
}
}
impl PublicKey {
#[inline]
pub fn as_bytes(&self) -> &'_ [u8; PUBLIC_KEY_LENGTH] {
&(self.0).0
}
pub fn hash_to_curve(&self, message: &[u8]) -> Option<EdwardsPoint> {
let mut p_candidate_bytes = [0u8; 32];
let mut h: Sha512 = Sha512::new();
h.update(SUITE_STRING);
h.update(ONE_STRING);
h.update(self.as_bytes()); h.update(message); for ctr in 0..=u8::MAX {
let mut attempt_h = h.clone();
attempt_h.update(ctr.to_le_bytes()); attempt_h.update(ZERO_STRING);
let hash = attempt_h.finalize();
p_candidate_bytes.copy_from_slice(&hash[..32]);
let p_candidate = CompressedEdwardsY(p_candidate_bytes);
if let Some(ed_point) = p_candidate.decompress() {
if !ed_point.is_small_order() {
return Some(ed_point.mul_by_cofactor());
}
}
}
None
}
pub fn verify_key(&self) -> bool {
!self.1.is_small_order()
}
#[allow(clippy::many_single_char_names)]
pub fn verify(&self, pi: &Proof, message: &[u8]) -> bool {
if let Some(h) = self.hash_to_curve(message) {
let Proof(gamma, c, s) = pi;
let b_to_s = s * constants::ED25519_BASEPOINT_TABLE; let y_to_c = c * self.1; let u = b_to_s - y_to_c;
let h_to_s = s * h; let gamma_to_c = c * gamma; let v = h_to_s - gamma_to_c;
let derivable_c =
hash_points(&[h.compress(), gamma.compress(), u.compress(), v.compress()]);
*c == derivable_c
} else {
false
}
}
}