Merge pull request #2099 from ZcashFoundation/redpallas-sig-stub
Flesh out redpallas, direct port of redjubjub
This commit is contained in:
parent
c2706f448a
commit
3901dc9adc
|
@ -4427,6 +4427,8 @@ dependencies = [
|
|||
"primitive-types",
|
||||
"proptest",
|
||||
"proptest-derive",
|
||||
"rand 0.8.1",
|
||||
"rand_chacha 0.3.0",
|
||||
"rand_core 0.6.2",
|
||||
"redjubjub",
|
||||
"ripemd160",
|
||||
|
@ -4457,6 +4459,7 @@ dependencies = [
|
|||
"displaydoc",
|
||||
"futures 0.3.14",
|
||||
"futures-util",
|
||||
"halo2",
|
||||
"jubjub",
|
||||
"lazy_static",
|
||||
"metrics",
|
||||
|
|
|
@ -59,12 +59,14 @@ zebra-test = { path = "../zebra-test/", optional = true }
|
|||
bincode = "1"
|
||||
color-eyre = "0.5.11"
|
||||
criterion = { version = "0.3", features = ["html_reports"] }
|
||||
itertools = "0.10.0"
|
||||
spandoc = "0.2"
|
||||
tracing = "0.1.26"
|
||||
|
||||
proptest = "0.10"
|
||||
proptest-derive = "0.3"
|
||||
itertools = "0.10.0"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
zebra-test = { path = "../zebra-test/" }
|
||||
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
// XXX: Extracted from redjubjub for now.
|
||||
// -*- 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>
|
||||
|
||||
use group::GroupEncoding;
|
||||
use halo2::pasta::pallas;
|
||||
|
||||
// pub mod batch;
|
||||
mod constants;
|
||||
mod error;
|
||||
// TODO: full redpallas implementation https://github.com/ZcashFoundation/zebra/issues/2044
|
||||
mod hash;
|
||||
mod signature;
|
||||
mod signing_key;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod verification_key;
|
||||
|
||||
pub use error::Error;
|
||||
pub use hash::HStar;
|
||||
pub use signature::Signature;
|
||||
pub use signing_key::SigningKey;
|
||||
pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
||||
|
||||
/// An element of the Pallas scalar field used for randomization of verification
|
||||
/// and signing keys.
|
||||
pub type Randomizer = pallas::Scalar;
|
||||
|
||||
/// Abstracts over different RedPallas parameter choices, [`Binding`]
|
||||
/// and [`SpendAuth`].
|
||||
///
|
||||
|
@ -25,7 +41,7 @@ pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
|||
/// To handle this, we encode the parameter choice as a genuine type
|
||||
/// parameter.
|
||||
///
|
||||
/// [concretereddsa]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
|
||||
/// [concretereddsa]: https://zips.z.cash/protocol/nu5.pdf#concretereddsa
|
||||
pub trait SigType: private::Sealed {}
|
||||
|
||||
/// A type variable corresponding to Zcash's `BindingSig`.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of redjubjub.
|
||||
// This file is part of redpallas.
|
||||
// Copyright (c) 2019-2021 Zcash Foundation
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
|
@ -10,13 +10,15 @@
|
|||
/// The byte-encoding of the basepoint for `SpendAuthSig` on the [Pallas curve][pallasandvesta].
|
||||
///
|
||||
/// [pallasandvesta]: https://zips.z.cash/protocol/nu5.pdf#pallasandvesta
|
||||
// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes()
|
||||
pub const SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [
|
||||
215, 148, 162, 4, 167, 65, 231, 17, 216, 7, 4, 206, 68, 161, 32, 20, 67, 192, 174, 143, 131,
|
||||
35, 240, 117, 113, 113, 7, 198, 56, 190, 133, 53,
|
||||
99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124,
|
||||
24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183,
|
||||
];
|
||||
|
||||
/// The byte-encoding of the basepoint for `BindingSig` on the Pallas curve.
|
||||
// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes()
|
||||
pub const BINDINGSIG_BASEPOINT_BYTES: [u8; 32] = [
|
||||
48, 181, 242, 170, 173, 50, 86, 48, 188, 221, 219, 206, 77, 103, 101, 109, 5, 253, 28, 194,
|
||||
208, 55, 187, 83, 117, 182, 233, 109, 158, 1, 161, 215,
|
||||
145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, 91,
|
||||
35, 102, 79, 187, 9, 164, 15, 85, 68, 244, 7,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of redpallas.
|
||||
// Copyright (c) 2019-2021 Zcash Foundation
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - Deirdre Connolly <deirdre@zfnd.org>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
use blake2b_simd::{Params, State};
|
||||
use halo2::{arithmetic::FieldExt, pasta::pallas::Scalar};
|
||||
|
||||
/// Provides H^star, the hash-to-scalar function used by RedPallas.
|
||||
pub struct HStar {
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl Default for HStar {
|
||||
fn default() -> Self {
|
||||
let state = Params::new()
|
||||
.hash_length(64)
|
||||
.personal(b"Zcash_RedPallasH")
|
||||
.to_state();
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
impl HStar {
|
||||
/// Add `data` to the hash, and return `Self` for chaining.
|
||||
pub fn update(&mut self, data: impl AsRef<[u8]>) -> &mut Self {
|
||||
self.state.update(data.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
/// Consume `self` to compute the hash output.
|
||||
pub fn finalize(&self) -> Scalar {
|
||||
Scalar::from_bytes_wide(self.state.finalize().as_array())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// -*- mode: rust; -*-
|
||||
//
|
||||
// This file is part of redpallas.
|
||||
// Copyright (c) 2019-2021 Zcash Foundation
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Authors:
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
// - Deirdre Connolly <deirdre@zfnd.org>
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::SigType;
|
||||
|
||||
/// A RedPallas signature.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Signature<T: SigType> {
|
||||
pub(crate) r_bytes: [u8; 32],
|
||||
pub(crate) s_bytes: [u8; 32],
|
||||
pub(crate) _marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: SigType> From<[u8; 64]> for Signature<T> {
|
||||
fn from(bytes: [u8; 64]) -> Signature<T> {
|
||||
let mut r_bytes = [0; 32];
|
||||
r_bytes.copy_from_slice(&bytes[0..32]);
|
||||
let mut s_bytes = [0; 32];
|
||||
s_bytes.copy_from_slice(&bytes[32..64]);
|
||||
Signature {
|
||||
r_bytes,
|
||||
s_bytes,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<Signature<T>> for [u8; 64] {
|
||||
fn from(sig: Signature<T>) -> [u8; 64] {
|
||||
let mut bytes = [0; 64];
|
||||
bytes[0..32].copy_from_slice(&sig.r_bytes[..]);
|
||||
bytes[32..64].copy_from_slice(&sig.s_bytes[..]);
|
||||
bytes
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use halo2::arithmetic::FieldExt;
|
||||
use halo2::pasta::pallas;
|
||||
use group::GroupEncoding;
|
||||
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use super::{Error, SigType, VerificationKey};
|
||||
use super::{Error, SigType, Signature, SpendAuth, VerificationKey};
|
||||
|
||||
/// A RedPallas signing key.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -22,6 +24,12 @@ impl<'a, T: SigType> From<&'a SigningKey<T>> for VerificationKey<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
||||
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
||||
sk.sk.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
|
||||
type Error = Error;
|
||||
|
||||
|
@ -30,10 +38,88 @@ impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
|
|||
|
||||
if maybe_sk.is_some().into() {
|
||||
let sk = maybe_sk.unwrap();
|
||||
let pk = VerificationKey::from(&sk);
|
||||
let pk = VerificationKey::from_scalar(&sk);
|
||||
Ok(SigningKey { sk, pk })
|
||||
} else {
|
||||
Err(Error::MalformedSigningKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
struct SerdeHelper([u8; 32]);
|
||||
|
||||
impl<T: SigType> TryFrom<SerdeHelper> for SigningKey<T> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(helper: SerdeHelper) -> Result<Self, Self::Error> {
|
||||
helper.0.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<SigningKey<T>> for SerdeHelper {
|
||||
fn from(sk: SigningKey<T>) -> Self {
|
||||
Self(sk.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningKey<SpendAuth> {
|
||||
/// Randomize this public key with the given `randomizer`.
|
||||
pub fn randomize(&self, randomizer: &pallas::Scalar) -> SigningKey<SpendAuth> {
|
||||
let sk = self.sk + randomizer;
|
||||
let pk = VerificationKey::from_scalar(&sk);
|
||||
SigningKey { sk, pk }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> SigningKey<T> {
|
||||
/// Generate a new signing key.
|
||||
pub fn new<R: RngCore + CryptoRng>(mut rng: R) -> SigningKey<T> {
|
||||
let sk = {
|
||||
let mut bytes = [0; 64];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
pallas::Scalar::from_bytes_wide(&bytes)
|
||||
};
|
||||
let pk = VerificationKey::from_scalar(&sk);
|
||||
SigningKey { sk, pk }
|
||||
}
|
||||
|
||||
/// Create a signature of type `T` on `msg` using this `SigningKey`.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/nu5.pdf#concretereddsa
|
||||
// Similar to signature::Signer but without boxed errors.
|
||||
pub fn sign<R: RngCore + CryptoRng>(&self, mut rng: R, msg: &[u8]) -> Signature<T> {
|
||||
use super::HStar;
|
||||
|
||||
// RedDSA.GenRandom:() → R RedDSA.Random
|
||||
// Choose a byte sequence uniformly at random of length
|
||||
// (\ell_H + 128)/8 bytes. For RedPallas this is (512 + 128)/8 = 80.
|
||||
let random_bytes = {
|
||||
let mut bytes = [0; 80];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
bytes
|
||||
};
|
||||
|
||||
let nonce = HStar::default()
|
||||
.update(&random_bytes[..])
|
||||
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
||||
.update(msg)
|
||||
.finalize();
|
||||
|
||||
let r_bytes = pallas::Affine::from(T::basepoint() * nonce).to_bytes();
|
||||
|
||||
let c = HStar::default()
|
||||
.update(&r_bytes[..])
|
||||
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
||||
.update(msg)
|
||||
.finalize();
|
||||
|
||||
let s_bytes = (nonce + (c * self.sk)).to_bytes();
|
||||
|
||||
Signature {
|
||||
r_bytes,
|
||||
s_bytes,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
mod basepoints;
|
||||
mod prop;
|
|
@ -0,0 +1,22 @@
|
|||
use group::GroupEncoding;
|
||||
use halo2::pasta::{arithmetic::CurveExt, pallas};
|
||||
|
||||
use super::super::constants;
|
||||
|
||||
#[test]
|
||||
fn orchard_spendauth_basepoint() {
|
||||
assert_eq!(
|
||||
// An instance of _GroupHash^P_
|
||||
pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes(),
|
||||
constants::SPENDAUTHSIG_BASEPOINT_BYTES
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn orchard_binding_basepoint() {
|
||||
assert_eq!(
|
||||
// An instance of _GroupHash^P_
|
||||
pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes(),
|
||||
constants::BINDINGSIG_BASEPOINT_BYTES
|
||||
);
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use halo2::arithmetic::FieldExt;
|
||||
use proptest::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use rand_core::{CryptoRng, RngCore, SeedableRng};
|
||||
|
||||
use super::super::SigningKey;
|
||||
use super::super::*;
|
||||
|
||||
/// A signature test-case, containing signature data and expected validity.
|
||||
#[derive(Clone, Debug)]
|
||||
struct SignatureCase<T: SigType> {
|
||||
msg: Vec<u8>,
|
||||
sig: Signature<T>,
|
||||
pk_bytes: VerificationKeyBytes<T>,
|
||||
is_valid: bool,
|
||||
}
|
||||
|
||||
/// A modification to a test-case.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Tweak {
|
||||
/// No-op, used to check that unchanged cases verify.
|
||||
None,
|
||||
/// Change the message the signature is defined for, invalidating the signature.
|
||||
ChangeMessage,
|
||||
/// Change the public key the signature is defined for, invalidating the signature.
|
||||
ChangePubkey,
|
||||
/* XXX implement this -- needs to regenerate a custom signature because the
|
||||
nonce commitment is fed into the hash, so it has to have torsion at signing
|
||||
time.
|
||||
/// Change the case to have a torsion component in the signature's `r` value.
|
||||
AddTorsion,
|
||||
*/
|
||||
/* XXX implement this -- needs custom handling of field arithmetic.
|
||||
/// Change the signature's `s` scalar to be unreduced (mod L), invalidating the signature.
|
||||
UnreducedScalar,
|
||||
*/
|
||||
}
|
||||
|
||||
impl<T: SigType> SignatureCase<T> {
|
||||
fn new<R: RngCore + CryptoRng>(mut rng: R, msg: Vec<u8>) -> Self {
|
||||
let sk = SigningKey::new(&mut rng);
|
||||
let sig = sk.sign(&mut rng, &msg);
|
||||
let pk_bytes = VerificationKey::from(&sk).into();
|
||||
Self {
|
||||
msg,
|
||||
sig,
|
||||
pk_bytes,
|
||||
is_valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Check that signature verification succeeds or fails, as expected.
|
||||
fn check(&self) -> bool {
|
||||
// The signature data is stored in (refined) byte types, but do a round trip
|
||||
// conversion to raw bytes to exercise those code paths.
|
||||
let sig = {
|
||||
let bytes: [u8; 64] = self.sig.into();
|
||||
Signature::<T>::from(bytes)
|
||||
};
|
||||
let pk_bytes = {
|
||||
let bytes: [u8; 32] = self.pk_bytes.into();
|
||||
VerificationKeyBytes::<T>::from(bytes)
|
||||
};
|
||||
|
||||
// Check that signature validation has the expected result.
|
||||
self.is_valid
|
||||
== VerificationKey::try_from(pk_bytes)
|
||||
.and_then(|pk| pk.verify(&self.msg, &sig))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn apply_tweak(&mut self, tweak: &Tweak) {
|
||||
match tweak {
|
||||
Tweak::None => {}
|
||||
Tweak::ChangeMessage => {
|
||||
// Changing the message makes the signature invalid.
|
||||
self.msg.push(90);
|
||||
self.is_valid = false;
|
||||
}
|
||||
Tweak::ChangePubkey => {
|
||||
// Changing the public key makes the signature invalid.
|
||||
let mut bytes: [u8; 32] = self.pk_bytes.into();
|
||||
let j = (bytes[2] & 31) as usize;
|
||||
bytes[2] ^= 0x23;
|
||||
bytes[2] |= 0x99;
|
||||
bytes[j] ^= bytes[2];
|
||||
self.pk_bytes = bytes.into();
|
||||
self.is_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tweak_strategy() -> impl Strategy<Value = Tweak> {
|
||||
prop_oneof![
|
||||
10 => Just(Tweak::None),
|
||||
1 => Just(Tweak::ChangeMessage),
|
||||
1 => Just(Tweak::ChangePubkey),
|
||||
]
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn tweak_signature(
|
||||
tweaks in prop::collection::vec(tweak_strategy(), (0,5)),
|
||||
rng_seed in prop::array::uniform32(any::<u8>()),
|
||||
) {
|
||||
// Use a deterministic RNG so that test failures can be reproduced.
|
||||
let mut rng = ChaChaRng::from_seed(rng_seed);
|
||||
|
||||
// Create a test case for each signature type.
|
||||
let msg = b"test message for proptests";
|
||||
let mut binding = SignatureCase::<Binding>::new(&mut rng, msg.to_vec());
|
||||
let mut spendauth = SignatureCase::<SpendAuth>::new(&mut rng, msg.to_vec());
|
||||
|
||||
// Apply tweaks to each case.
|
||||
for t in &tweaks {
|
||||
binding.apply_tweak(t);
|
||||
spendauth.apply_tweak(t);
|
||||
}
|
||||
|
||||
assert!(binding.check());
|
||||
assert!(spendauth.check());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn randomization_commutes_with_pubkey_homomorphism(rng_seed in prop::array::uniform32(any::<u8>())) {
|
||||
// Use a deterministic RNG so that test failures can be reproduced.
|
||||
let mut rng = ChaChaRng::from_seed(rng_seed);
|
||||
|
||||
let r = {
|
||||
// XXX-jubjub: better API for this
|
||||
let mut bytes = [0; 64];
|
||||
rng.fill_bytes(&mut bytes[..]);
|
||||
Randomizer::from_bytes_wide(&bytes)
|
||||
};
|
||||
|
||||
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
||||
let pk = VerificationKey::from(&sk);
|
||||
|
||||
let sk_r = sk.randomize(&r);
|
||||
let pk_r = pk.randomize(&r);
|
||||
|
||||
let pk_r_via_sk_rand: [u8; 32] = VerificationKeyBytes::from(VerificationKey::from(&sk_r)).into();
|
||||
let pk_r_via_pk_rand: [u8; 32] = VerificationKeyBytes::from(pk_r).into();
|
||||
|
||||
assert_eq!(pk_r_via_pk_rand, pk_r_via_sk_rand);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
use std::{
|
||||
convert::TryFrom,
|
||||
// hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::{convert::TryFrom, marker::PhantomData};
|
||||
|
||||
use group::{cofactor::CofactorGroup, GroupEncoding};
|
||||
use halo2::pasta::pallas;
|
||||
use halo2::{arithmetic::FieldExt, pasta::pallas};
|
||||
|
||||
use super::{Error, SigType};
|
||||
use super::*;
|
||||
|
||||
/// A refinement type for `[u8; 32]` indicating that the bytes represent
|
||||
/// an encoding of a RedPallas verification key.
|
||||
|
@ -73,18 +69,6 @@ impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> From<&pallas::Scalar> for VerificationKey<T> {
|
||||
fn from(s: &pallas::Scalar) -> VerificationKey<T> {
|
||||
let point = T::basepoint() * s;
|
||||
let bytes = VerificationKeyBytes {
|
||||
bytes: pallas::Affine::from(&point).to_bytes(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
Self { point, bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> TryFrom<VerificationKeyBytes<T>> for VerificationKey<T> {
|
||||
type Error = Error;
|
||||
|
||||
|
@ -115,3 +99,81 @@ impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
|
|||
VerificationKeyBytes::from(bytes).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl VerificationKey<SpendAuth> {
|
||||
/// Randomize this verification key with the given `randomizer`.
|
||||
///
|
||||
/// Randomization is only supported for `SpendAuth` keys.
|
||||
pub fn randomize(&self, randomizer: &Randomizer) -> VerificationKey<SpendAuth> {
|
||||
use super::private::Sealed;
|
||||
let point = self.point + (SpendAuth::basepoint() * randomizer);
|
||||
let bytes = VerificationKeyBytes {
|
||||
bytes: point.to_bytes(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
VerificationKey { point, bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> VerificationKey<T> {
|
||||
pub(crate) fn from_scalar(s: &pallas::Scalar) -> VerificationKey<T> {
|
||||
let point = T::basepoint() * s;
|
||||
let bytes = VerificationKeyBytes {
|
||||
bytes: point.to_bytes(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
VerificationKey { point, bytes }
|
||||
}
|
||||
|
||||
/// Verify a purported `signature` over `msg` made by this verification key.
|
||||
// This is similar to impl signature::Verifier but without boxed errors
|
||||
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
||||
let c = HStar::default()
|
||||
.update(&signature.r_bytes[..])
|
||||
.update(&self.bytes.bytes[..]) // XXX ugly
|
||||
.update(msg)
|
||||
.finalize();
|
||||
self.verify_prehashed(signature, c)
|
||||
}
|
||||
|
||||
/// Verify a purported `signature` with a prehashed challenge.
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn verify_prehashed(
|
||||
&self,
|
||||
signature: &Signature<T>,
|
||||
c: pallas::Scalar,
|
||||
) -> Result<(), Error> {
|
||||
let r = {
|
||||
// XXX-pasta_curves: should not use CtOption here
|
||||
let maybe_point = pallas::Affine::from_bytes(&signature.r_bytes);
|
||||
if maybe_point.is_some().into() {
|
||||
pallas::Point::from(maybe_point.unwrap())
|
||||
} else {
|
||||
return Err(Error::InvalidSignature);
|
||||
}
|
||||
};
|
||||
|
||||
let s = {
|
||||
// XXX-pasta_curves: should not use CtOption here
|
||||
let maybe_scalar = pallas::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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue