redjubjub/src/public_key.rs

160 lines
5.2 KiB
Rust

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<T: SigType> {
pub(crate) bytes: [u8; 32],
pub(crate) _marker: PhantomData<T>,
}
impl<T: SigType> From<[u8; 32]> for PublicKeyBytes<T> {
fn from(bytes: [u8; 32]) -> PublicKeyBytes<T> {
PublicKeyBytes {
bytes,
_marker: PhantomData,
}
}
}
impl<T: SigType> From<PublicKeyBytes<T>> for [u8; 32] {
fn from(refined: PublicKeyBytes<T>) -> [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<T>"))]
#[cfg_attr(feature = "serde", serde(into = "PublicKeyBytes<T>"))]
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
pub struct PublicKey<T: SigType> {
// XXX-jubjub: this should just be Point
pub(crate) point: jubjub::ExtendedPoint,
pub(crate) bytes: PublicKeyBytes<T>,
}
impl<T: SigType> From<PublicKey<T>> for PublicKeyBytes<T> {
fn from(pk: PublicKey<T>) -> PublicKeyBytes<T> {
pk.bytes
}
}
impl<T: SigType> From<PublicKey<T>> for [u8; 32] {
fn from(pk: PublicKey<T>) -> [u8; 32] {
pk.bytes.bytes
}
}
impl<T: SigType> TryFrom<PublicKeyBytes<T>> for PublicKey<T> {
type Error = Error;
fn try_from(bytes: PublicKeyBytes<T>) -> Result<Self, Self::Error> {
// 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<T: SigType> TryFrom<[u8; 32]> for PublicKey<T> {
type Error = Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
use std::convert::TryInto;
PublicKeyBytes::from(bytes).try_into()
}
}
impl PublicKey<SpendAuth> {
/// Randomize this public key with the given `randomizer`.
///
/// Randomization is only supported for `SpendAuth` keys.
pub fn randomize(&self, randomizer: &Randomizer) -> PublicKey<SpendAuth> {
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<T: SigType> PublicKey<T> {
pub(crate) fn from_secret(s: &Scalar) -> PublicKey<T> {
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<T>) -> 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)
}
}
}