2021-02-25 08:06:54 -08:00
|
|
|
// -*- mode: rust; -*-
|
|
|
|
//
|
|
|
|
// This file is part of redjubjub.
|
|
|
|
// Copyright (c) 2019-2021 Zcash Foundation
|
|
|
|
// See LICENSE for licensing information.
|
|
|
|
//
|
|
|
|
// Authors:
|
|
|
|
// - Deirdre Connolly <deirdre@zfnd.org>
|
|
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
|
2020-07-03 15:23:28 -07:00
|
|
|
use std::{
|
|
|
|
convert::TryFrom,
|
|
|
|
hash::{Hash, Hasher},
|
|
|
|
marker::PhantomData,
|
|
|
|
};
|
2019-12-02 21:58:19 -08:00
|
|
|
|
2021-03-01 06:54:52 -08:00
|
|
|
use jubjub::Scalar;
|
|
|
|
|
|
|
|
use crate::{Error, Randomizer, SigType, Signature, SpendAuth};
|
2019-12-02 21:58:19 -08:00
|
|
|
|
2019-12-03 15:59:24 -08:00
|
|
|
/// A refinement type for `[u8; 32]` indicating that the bytes represent
|
2020-06-25 11:56:29 -07:00
|
|
|
/// an encoding of a RedJubJub verification key.
|
2019-12-03 15:59:24 -08:00
|
|
|
///
|
2020-06-25 11:56:29 -07:00
|
|
|
/// This is useful for representing a compressed verification key; the
|
|
|
|
/// [`VerificationKey`] type in this library holds other decompressed state
|
2019-12-03 15:59:24 -08:00
|
|
|
/// used in signature verification.
|
2019-12-02 21:58:19 -08:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
2019-12-09 11:07:05 -08:00
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
2020-06-25 11:56:29 -07:00
|
|
|
pub struct VerificationKeyBytes<T: SigType> {
|
2019-12-03 15:59:24 -08:00
|
|
|
pub(crate) bytes: [u8; 32],
|
|
|
|
pub(crate) _marker: PhantomData<T>,
|
2019-12-03 12:22:35 -08:00
|
|
|
}
|
2019-12-02 21:58:19 -08:00
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> From<[u8; 32]> for VerificationKeyBytes<T> {
|
|
|
|
fn from(bytes: [u8; 32]) -> VerificationKeyBytes<T> {
|
|
|
|
VerificationKeyBytes {
|
2019-12-03 13:39:26 -08:00
|
|
|
bytes,
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
2019-12-02 21:58:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> From<VerificationKeyBytes<T>> for [u8; 32] {
|
|
|
|
fn from(refined: VerificationKeyBytes<T>) -> [u8; 32] {
|
2019-12-03 12:22:35 -08:00
|
|
|
refined.bytes
|
2019-12-02 21:58:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 15:23:28 -07:00
|
|
|
impl<T: SigType> Hash for VerificationKeyBytes<T> {
|
|
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
|
|
self.bytes.hash(state);
|
|
|
|
self._marker.hash(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
/// A valid RedJubJub verification key.
|
2019-12-04 17:36:01 -08:00
|
|
|
///
|
|
|
|
/// This type holds decompressed state used in signature verification; if the
|
2020-06-25 11:56:29 -07:00
|
|
|
/// verification key may not be used immediately, it is probably better to use
|
|
|
|
/// [`VerificationKeyBytes`], which is a refinement type for `[u8; 32]`.
|
2020-01-17 09:59:41 -08:00
|
|
|
///
|
|
|
|
/// ## Consensus properties
|
|
|
|
///
|
2020-06-25 11:56:29 -07:00
|
|
|
/// The `TryFrom<VerificationKeyBytes>` conversion performs the following Zcash
|
2020-01-17 09:59:41 -08:00
|
|
|
/// consensus rule checks:
|
|
|
|
///
|
2020-06-25 11:56:29 -07:00
|
|
|
/// 1. The check that the bytes are a canonical encoding of a verification key;
|
|
|
|
/// 2. The check that the verification key is not a point of small order.
|
2019-12-02 21:58:19 -08:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2019-12-09 11:22:39 -08:00
|
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
2020-06-25 11:56:29 -07:00
|
|
|
#[cfg_attr(feature = "serde", serde(try_from = "VerificationKeyBytes<T>"))]
|
|
|
|
#[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes<T>"))]
|
2019-12-09 11:22:39 -08:00
|
|
|
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
|
2020-06-25 11:56:29 -07:00
|
|
|
pub struct VerificationKey<T: SigType> {
|
2019-12-03 14:51:38 -08:00
|
|
|
// XXX-jubjub: this should just be Point
|
2019-12-03 15:01:54 -08:00
|
|
|
pub(crate) point: jubjub::ExtendedPoint,
|
2020-06-25 11:56:29 -07:00
|
|
|
pub(crate) bytes: VerificationKeyBytes<T>,
|
2019-12-02 21:58:19 -08:00
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> From<VerificationKey<T>> for VerificationKeyBytes<T> {
|
|
|
|
fn from(pk: VerificationKey<T>) -> VerificationKeyBytes<T> {
|
2019-12-03 15:59:24 -08:00
|
|
|
pk.bytes
|
2019-12-02 21:58:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
|
|
|
|
fn from(pk: VerificationKey<T>) -> [u8; 32] {
|
2019-12-04 17:36:01 -08:00
|
|
|
pk.bytes.bytes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> TryFrom<VerificationKeyBytes<T>> for VerificationKey<T> {
|
2019-12-02 21:58:19 -08:00
|
|
|
type Error = Error;
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
fn try_from(bytes: VerificationKeyBytes<T>) -> Result<Self, Self::Error> {
|
2019-12-03 14:51:38 -08:00
|
|
|
// XXX-jubjub: this should not use CtOption
|
|
|
|
// XXX-jubjub: this takes ownership of bytes, while Fr doesn't.
|
2020-01-17 09:59:41 -08:00
|
|
|
// This checks that the encoding is canonical...
|
2019-12-03 14:51:38 -08:00
|
|
|
let maybe_point = jubjub::AffinePoint::from_bytes(bytes.bytes);
|
|
|
|
if maybe_point.is_some().into() {
|
2020-01-17 09:59:41 -08:00
|
|
|
let point: jubjub::ExtendedPoint = maybe_point.unwrap().into();
|
2020-06-25 11:56:29 -07:00
|
|
|
// This checks that the verification key is not of small order.
|
2020-01-17 09:59:41 -08:00
|
|
|
if <bool>::from(point.is_small_order()) == false {
|
2020-06-25 11:56:29 -07:00
|
|
|
Ok(VerificationKey { point, bytes })
|
2020-01-17 09:59:41 -08:00
|
|
|
} else {
|
2020-06-25 11:56:29 -07:00
|
|
|
Err(Error::MalformedVerificationKey)
|
2020-01-17 09:59:41 -08:00
|
|
|
}
|
2019-12-03 14:51:38 -08:00
|
|
|
} else {
|
2020-06-25 11:56:29 -07:00
|
|
|
Err(Error::MalformedVerificationKey)
|
2019-12-03 14:51:38 -08:00
|
|
|
}
|
2019-12-02 21:58:19 -08:00
|
|
|
}
|
|
|
|
}
|
2019-12-02 22:20:21 -08:00
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
|
2019-12-04 17:36:01 -08:00
|
|
|
type Error = Error;
|
|
|
|
|
|
|
|
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
|
|
|
use std::convert::TryInto;
|
2020-06-25 11:56:29 -07:00
|
|
|
VerificationKeyBytes::from(bytes).try_into()
|
2019-12-04 17:36:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl VerificationKey<SpendAuth> {
|
|
|
|
/// Randomize this verification key with the given `randomizer`.
|
2019-12-04 17:00:55 -08:00
|
|
|
///
|
|
|
|
/// Randomization is only supported for `SpendAuth` keys.
|
2020-06-25 11:56:29 -07:00
|
|
|
pub fn randomize(&self, randomizer: &Randomizer) -> VerificationKey<SpendAuth> {
|
2019-12-04 17:00:55 -08:00
|
|
|
use crate::private::Sealed;
|
|
|
|
let point = &self.point + &(&SpendAuth::basepoint() * randomizer);
|
2020-06-25 11:56:29 -07:00
|
|
|
let bytes = VerificationKeyBytes {
|
2019-12-04 17:00:55 -08:00
|
|
|
bytes: jubjub::AffinePoint::from(&point).to_bytes(),
|
|
|
|
_marker: PhantomData,
|
|
|
|
};
|
2020-06-25 11:56:29 -07:00
|
|
|
VerificationKey { bytes, point }
|
2019-12-04 17:00:55 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
impl<T: SigType> VerificationKey<T> {
|
|
|
|
pub(crate) fn from(s: &Scalar) -> VerificationKey<T> {
|
2019-12-03 18:20:45 -08:00
|
|
|
let point = &T::basepoint() * s;
|
2020-06-25 11:56:29 -07:00
|
|
|
let bytes = VerificationKeyBytes {
|
2019-12-03 18:20:45 -08:00
|
|
|
bytes: jubjub::AffinePoint::from(&point).to_bytes(),
|
|
|
|
_marker: PhantomData,
|
|
|
|
};
|
2020-06-25 11:56:29 -07:00
|
|
|
VerificationKey { bytes, point }
|
2019-12-03 18:20:45 -08:00
|
|
|
}
|
|
|
|
|
2020-06-25 11:56:29 -07:00
|
|
|
/// Verify a purported `signature` over `msg` made by this verification key.
|
2019-12-03 12:22:35 -08:00
|
|
|
// This is similar to impl signature::Verifier but without boxed errors
|
2019-12-04 11:59:31 -08:00
|
|
|
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
2019-12-03 22:32:30 -08:00
|
|
|
use crate::HStar;
|
2020-07-15 12:38:43 -07:00
|
|
|
let c = HStar::default()
|
|
|
|
.update(&signature.r_bytes[..])
|
|
|
|
.update(&self.bytes.bytes[..]) // XXX ugly
|
|
|
|
.update(msg)
|
|
|
|
.finalize();
|
|
|
|
self.verify_prehashed(signature, c)
|
|
|
|
}
|
2019-12-02 22:32:55 -08:00
|
|
|
|
2020-07-15 12:38:43 -07:00
|
|
|
/// Verify a purported `signature` with a prehashed challenge.
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub(crate) fn verify_prehashed(
|
|
|
|
&self,
|
|
|
|
signature: &Signature<T>,
|
|
|
|
c: Scalar,
|
|
|
|
) -> Result<(), Error> {
|
2019-12-03 22:32:30 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2019-12-02 22:20:21 -08:00
|
|
|
}
|
|
|
|
}
|