use std::{convert::TryFrom, marker::PhantomData}; use crate::{Error, Randomizer, Scalar, SigType, Signature, SpendAuth}; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedJubJub public key. /// /// This is useful for representing a compressed public key; the /// [`PublicKey`] type in this library holds other decompressed state /// used in signature verification. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PublicKeyBytes { pub(crate) bytes: [u8; 32], pub(crate) _marker: PhantomData, } impl From<[u8; 32]> for PublicKeyBytes { fn from(bytes: [u8; 32]) -> PublicKeyBytes { PublicKeyBytes { bytes, _marker: PhantomData, } } } impl From> for [u8; 32] { fn from(refined: PublicKeyBytes) -> [u8; 32] { refined.bytes } } /// A valid RedJubJub public key. /// /// This type holds decompressed state used in signature verification; if the /// public key may not be used immediately, it is probably better to use /// [`PublicKeyBytes`], which is a refinement type for `[u8; 32]`. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "PublicKeyBytes"))] #[cfg_attr(feature = "serde", serde(into = "PublicKeyBytes"))] #[cfg_attr(feature = "serde", serde(bound = "T: SigType"))] pub struct PublicKey { // XXX-jubjub: this should just be Point pub(crate) point: jubjub::ExtendedPoint, pub(crate) bytes: PublicKeyBytes, } impl From> for PublicKeyBytes { fn from(pk: PublicKey) -> PublicKeyBytes { pk.bytes } } impl From> for [u8; 32] { fn from(pk: PublicKey) -> [u8; 32] { pk.bytes.bytes } } impl TryFrom> for PublicKey { type Error = Error; fn try_from(bytes: PublicKeyBytes) -> Result { // XXX-jubjub: this should not use CtOption // XXX-jubjub: this takes ownership of bytes, while Fr doesn't. let maybe_point = jubjub::AffinePoint::from_bytes(bytes.bytes); if maybe_point.is_some().into() { Ok(PublicKey { point: maybe_point.unwrap().into(), bytes, }) } else { Err(Error::MalformedPublicKey) } } } impl TryFrom<[u8; 32]> for PublicKey { type Error = Error; fn try_from(bytes: [u8; 32]) -> Result { use std::convert::TryInto; PublicKeyBytes::from(bytes).try_into() } } impl PublicKey { /// Randomize this public key with the given `randomizer`. /// /// Randomization is only supported for `SpendAuth` keys. pub fn randomize(&self, randomizer: &Randomizer) -> PublicKey { use crate::private::Sealed; let point = &self.point + &(&SpendAuth::basepoint() * randomizer); let bytes = PublicKeyBytes { bytes: jubjub::AffinePoint::from(&point).to_bytes(), _marker: PhantomData, }; PublicKey { bytes, point } } } impl PublicKey { pub(crate) fn from_secret(s: &Scalar) -> PublicKey { let point = &T::basepoint() * s; let bytes = PublicKeyBytes { bytes: jubjub::AffinePoint::from(&point).to_bytes(), _marker: PhantomData, }; PublicKey { bytes, point } } /// Verify a purported `signature` over `msg` made by this public key. // This is similar to impl signature::Verifier but without boxed errors pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { #![allow(non_snake_case)] use crate::HStar; let r = { // XXX-jubjub: should not use CtOption here // XXX-jubjub: inconsistent ownership in from_bytes let maybe_point = jubjub::AffinePoint::from_bytes(signature.r_bytes); if maybe_point.is_some().into() { jubjub::ExtendedPoint::from(maybe_point.unwrap()) } else { return Err(Error::InvalidSignature); } }; let s = { // XXX-jubjub: should not use CtOption here let maybe_scalar = Scalar::from_bytes(&signature.s_bytes); if maybe_scalar.is_some().into() { maybe_scalar.unwrap() } else { return Err(Error::InvalidSignature); } }; let c = HStar::default() .update(&signature.r_bytes[..]) .update(&self.bytes.bytes[..]) // XXX ugly .update(msg) .finalize(); // XXX rewrite as normal double scalar mul // Verify check is h * ( - s * B + R + c * A) == 0 // h * ( s * B - c * A - R) == 0 let sB = &T::basepoint() * &s; let cA = &self.point * &c; let check = sB - cA - r; if check.is_small_order().into() { Ok(()) } else { Err(Error::InvalidSignature) } } }