Skip to content

Commit ca266d5

Browse files
committed
Introduce SpendAuth: SigType and Binding: SigType traits
The prior `SpendAuth` and `Binding` enums have been renamed to `sapling::{SpendAuth, Binding}`. These might subsequently be removed from the crate entirely (moving into a wrapping `redjubjub` crate). The code assumes that scalar and point representations are [u8; 32], which will be the case for all curves we instantiate RedDSA with for Zcash.
1 parent eff4f3d commit ca266d5

19 files changed

+465
-351
lines changed

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ features = ["nightly"]
2626
blake2b_simd = "0.5"
2727
byteorder = "1.4"
2828
digest = "0.9"
29+
group = "0.11"
2930
jubjub = "0.8"
3031
rand_core = "0.6"
3132
serde = { version = "1", optional = true, features = ["derive"] }

Diff for: README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
A minimal [RedDSA][reddsa] implementation for use in Zcash.
22

3-
Two parameterizations of RedJubjub are used in Zcash, one for
3+
Two specializations of RedDSA are used in Zcash: RedJubjub and
4+
RedPallas. For each of these, two parameterizations are used, one for
45
`BindingSig` and one for `SpendAuthSig`. This library distinguishes
56
these in the type system, using the [sealed] `SigType` trait as a
67
type-level enum.
78

89
In addition to the `Signature`, `SigningKey`, `VerificationKey` types,
910
the library also provides `VerificationKeyBytes`, a [refinement] of a
10-
`[u8; 32]` indicating that bytes represent an encoding of a RedJubjub
11+
`[u8; 32]` indicating that bytes represent an encoding of a RedDSA
1112
verification key. This allows the `VerificationKey` type to cache
1213
verification checks related to the verification key encoding.
14+
For all specializations of RedDSA used in Zcash, encodings of signing
15+
and verification keys are 32 bytes.
1316

1417
## Examples
1518

@@ -24,15 +27,15 @@ use reddsa::*;
2427
let msg = b"Hello!";
2528

2629
// Generate a secret key and sign the message
27-
let sk = SigningKey::<Binding>::new(thread_rng());
30+
let sk = SigningKey::<sapling::Binding>::new(thread_rng());
2831
let sig = sk.sign(thread_rng(), msg);
2932

3033
// Types can be converted to raw byte arrays using From/Into
3134
let sig_bytes: [u8; 64] = sig.into();
3235
let pk_bytes: [u8; 32] = VerificationKey::from(&sk).into();
3336

3437
// Deserialize and verify the signature.
35-
let sig: Signature<Binding> = sig_bytes.into();
38+
let sig: Signature<sapling::Binding> = sig_bytes.into();
3639
assert!(
3740
VerificationKey::try_from(pk_bytes)
3841
.and_then(|pk| pk.verify(msg, &sig))

Diff for: benches/bench.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ use std::convert::TryFrom;
66

77
enum Item {
88
SpendAuth {
9-
vk_bytes: VerificationKeyBytes<SpendAuth>,
10-
sig: Signature<SpendAuth>,
9+
vk_bytes: VerificationKeyBytes<sapling::SpendAuth>,
10+
sig: Signature<sapling::SpendAuth>,
1111
},
1212
Binding {
13-
vk_bytes: VerificationKeyBytes<Binding>,
14-
sig: Signature<Binding>,
13+
vk_bytes: VerificationKeyBytes<sapling::Binding>,
14+
sig: Signature<sapling::Binding>,
1515
},
1616
}
1717

@@ -21,13 +21,13 @@ fn sigs_with_distinct_keys() -> impl Iterator<Item = Item> {
2121
let msg = b"Bench";
2222
match rng.gen::<u8>() % 2 {
2323
0 => {
24-
let sk = SigningKey::<SpendAuth>::new(thread_rng());
24+
let sk = SigningKey::<sapling::SpendAuth>::new(thread_rng());
2525
let vk_bytes = VerificationKey::from(&sk).into();
2626
let sig = sk.sign(thread_rng(), &msg[..]);
2727
Item::SpendAuth { vk_bytes, sig }
2828
}
2929
1 => {
30-
let sk = SigningKey::<Binding>::new(thread_rng());
30+
let sk = SigningKey::<sapling::Binding>::new(thread_rng());
3131
let vk_bytes = VerificationKey::from(&sk).into();
3232
let sig = sk.sign(thread_rng(), &msg[..]);
3333
Item::Binding { vk_bytes, sig }
@@ -76,10 +76,10 @@ fn bench_batch_verify(c: &mut Criterion) {
7676
let msg = b"Bench";
7777
match item {
7878
Item::SpendAuth { vk_bytes, sig } => {
79-
batch.queue((*vk_bytes, *sig, msg));
79+
batch.queue(batch::Item::from_spendauth(*vk_bytes, *sig, msg));
8080
}
8181
Item::Binding { vk_bytes, sig } => {
82-
batch.queue((*vk_bytes, *sig, msg));
82+
batch.queue(batch::Item::from_binding(*vk_bytes, *sig, msg));
8383
}
8484
}
8585
}

Diff for: src/batch.rs

+60-57
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// - Deirdre Connolly <[email protected]>
99
// - Henry de Valence <[email protected]>
1010

11-
//! Performs batch RedJubjub signature verification.
11+
//! Performs batch RedDSA signature verification.
1212
//!
1313
//! Batch verification asks whether *all* signatures in some set are valid,
1414
//! rather than asking whether *each* of them is valid. This allows sharing
@@ -20,10 +20,14 @@
2020
2121
use std::convert::TryFrom;
2222

23-
use jubjub::*;
23+
use group::{
24+
cofactor::CofactorGroup,
25+
ff::{Field, PrimeField},
26+
GroupEncoding,
27+
};
2428
use rand_core::{CryptoRng, RngCore};
2529

26-
use crate::{private::Sealed, scalar_mul::VartimeMultiscalarMul, *};
30+
use crate::{private::SealedScalar, scalar_mul::VartimeMultiscalarMul, *};
2731

2832
// Shim to generate a random 128bit value in a [u64; 4], without
2933
// importing `rand`.
@@ -35,16 +39,16 @@ fn gen_128_bits<R: RngCore + CryptoRng>(mut rng: R) -> [u64; 4] {
3539
}
3640

3741
#[derive(Clone, Debug)]
38-
enum Inner {
42+
enum Inner<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
3943
SpendAuth {
40-
vk_bytes: VerificationKeyBytes<SpendAuth>,
41-
sig: Signature<SpendAuth>,
42-
c: Scalar,
44+
vk_bytes: VerificationKeyBytes<S>,
45+
sig: Signature<S>,
46+
c: S::Scalar,
4347
},
4448
Binding {
45-
vk_bytes: VerificationKeyBytes<Binding>,
46-
sig: Signature<Binding>,
47-
c: Scalar,
49+
vk_bytes: VerificationKeyBytes<B>,
50+
sig: Signature<B>,
51+
c: B::Scalar,
4852
},
4953
}
5054

@@ -54,26 +58,19 @@ enum Inner {
5458
/// lifetime of the message. This is useful when using the batch verification API
5559
/// in an async context.
5660
#[derive(Clone, Debug)]
57-
pub struct Item {
58-
inner: Inner,
61+
pub struct Item<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
62+
inner: Inner<S, B>,
5963
}
6064

61-
impl<'msg, M: AsRef<[u8]>>
62-
From<(
63-
VerificationKeyBytes<SpendAuth>,
64-
Signature<SpendAuth>,
65-
&'msg M,
66-
)> for Item
67-
{
68-
fn from(
69-
(vk_bytes, sig, msg): (
70-
VerificationKeyBytes<SpendAuth>,
71-
Signature<SpendAuth>,
72-
&'msg M,
73-
),
65+
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Item<S, B> {
66+
/// Create a batch item from a `SpendAuth` signature.
67+
pub fn from_spendauth<'msg, M: AsRef<[u8]>>(
68+
vk_bytes: VerificationKeyBytes<S>,
69+
sig: Signature<S>,
70+
msg: &'msg M,
7471
) -> Self {
7572
// Compute c now to avoid dependency on the msg lifetime.
76-
let c = HStar::default()
73+
let c = HStar::<S>::default()
7774
.update(&sig.r_bytes[..])
7875
.update(&vk_bytes.bytes[..])
7976
.update(msg)
@@ -82,16 +79,15 @@ impl<'msg, M: AsRef<[u8]>>
8279
inner: Inner::SpendAuth { vk_bytes, sig, c },
8380
}
8481
}
85-
}
8682

87-
impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M)>
88-
for Item
89-
{
90-
fn from(
91-
(vk_bytes, sig, msg): (VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M),
83+
/// Create a batch item from a `Binding` signature.
84+
pub fn from_binding<'msg, M: AsRef<[u8]>>(
85+
vk_bytes: VerificationKeyBytes<B>,
86+
sig: Signature<B>,
87+
msg: &'msg M,
9288
) -> Self {
9389
// Compute c now to avoid dependency on the msg lifetime.
94-
let c = HStar::default()
90+
let c = HStar::<B>::default()
9591
.update(&sig.r_bytes[..])
9692
.update(&vk_bytes.bytes[..])
9793
.update(msg)
@@ -100,9 +96,7 @@ impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes<Binding>, Signature<Bindin
10096
inner: Inner::Binding { vk_bytes, sig, c },
10197
}
10298
}
103-
}
10499

105-
impl Item {
106100
/// Perform non-batched verification of this `Item`.
107101
///
108102
/// This is useful (in combination with `Item::clone`) for implementing fallback
@@ -113,31 +107,36 @@ impl Item {
113107
#[allow(non_snake_case)]
114108
pub fn verify_single(self) -> Result<(), Error> {
115109
match self.inner {
116-
Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
117-
.and_then(|vk| vk.verify_prehashed(&sig, c)),
110+
Inner::Binding { vk_bytes, sig, c } => {
111+
VerificationKey::<B>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
112+
}
118113
Inner::SpendAuth { vk_bytes, sig, c } => {
119-
VerificationKey::<SpendAuth>::try_from(vk_bytes)
120-
.and_then(|vk| vk.verify_prehashed(&sig, c))
114+
VerificationKey::<S>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
121115
}
122116
}
123117
}
124118
}
125119

126-
#[derive(Default)]
127120
/// A batch verification context.
128-
pub struct Verifier {
121+
pub struct Verifier<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
129122
/// Signature data queued for verification.
130-
signatures: Vec<Item>,
123+
signatures: Vec<Item<S, B>>,
124+
}
125+
126+
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Default for Verifier<S, B> {
127+
fn default() -> Self {
128+
Verifier { signatures: vec![] }
129+
}
131130
}
132131

133-
impl Verifier {
132+
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Verifier<S, B> {
134133
/// Construct a new batch verifier.
135-
pub fn new() -> Verifier {
134+
pub fn new() -> Verifier<S, B> {
136135
Verifier::default()
137136
}
138137

139138
/// Queue an Item for verification.
140-
pub fn queue<I: Into<Item>>(&mut self, item: I) {
139+
pub fn queue<I: Into<Item<S, B>>>(&mut self, item: I) {
141140
self.signatures.push(item.into());
142141
}
143142

@@ -163,7 +162,7 @@ impl Verifier {
163162
/// - h_G is the cofactor of the group;
164163
/// - P_G is the generator of the subgroup;
165164
///
166-
/// Since RedJubjub uses different subgroups for different types
165+
/// Since RedDSA uses different subgroups for different types
167166
/// of signatures, SpendAuth's and Binding's, we need to have yet
168167
/// another point and associated scalar accumulator for all the
169168
/// signatures of each type in our batch, but we can still
@@ -185,8 +184,8 @@ impl Verifier {
185184
let mut VKs = Vec::with_capacity(n);
186185
let mut R_coeffs = Vec::with_capacity(self.signatures.len());
187186
let mut Rs = Vec::with_capacity(self.signatures.len());
188-
let mut P_spendauth_coeff = Scalar::zero();
189-
let mut P_binding_coeff = Scalar::zero();
187+
let mut P_spendauth_coeff = S::Scalar::zero();
188+
let mut P_binding_coeff = B::Scalar::zero();
190189

191190
for item in self.signatures.iter() {
192191
let (s_bytes, r_bytes, c) = match item.inner {
@@ -196,7 +195,9 @@ impl Verifier {
196195

197196
let s = {
198197
// XXX-jubjub: should not use CtOption here
199-
let maybe_scalar = Scalar::from_bytes(&s_bytes);
198+
let mut repr = <S::Scalar as PrimeField>::Repr::default();
199+
repr.as_mut().copy_from_slice(&s_bytes);
200+
let maybe_scalar = S::Scalar::from_repr(repr);
200201
if maybe_scalar.is_some().into() {
201202
maybe_scalar.unwrap()
202203
} else {
@@ -207,24 +208,26 @@ impl Verifier {
207208
let R = {
208209
// XXX-jubjub: should not use CtOption here
209210
// XXX-jubjub: inconsistent ownership in from_bytes
210-
let maybe_point = AffinePoint::from_bytes(r_bytes);
211+
let mut repr = <S::Point as GroupEncoding>::Repr::default();
212+
repr.as_mut().copy_from_slice(&r_bytes);
213+
let maybe_point = S::Point::from_bytes(&repr);
211214
if maybe_point.is_some().into() {
212-
jubjub::ExtendedPoint::from(maybe_point.unwrap())
215+
maybe_point.unwrap()
213216
} else {
214217
return Err(Error::InvalidSignature);
215218
}
216219
};
217220

218221
let VK = match item.inner {
219222
Inner::SpendAuth { vk_bytes, .. } => {
220-
VerificationKey::<SpendAuth>::try_from(vk_bytes.bytes)?.point
223+
VerificationKey::<S>::try_from(vk_bytes.bytes)?.point
221224
}
222225
Inner::Binding { vk_bytes, .. } => {
223-
VerificationKey::<Binding>::try_from(vk_bytes.bytes)?.point
226+
VerificationKey::<B>::try_from(vk_bytes.bytes)?.point
224227
}
225228
};
226229

227-
let z = Scalar::from_raw(gen_128_bits(&mut rng));
230+
let z = S::Scalar::from_raw(gen_128_bits(&mut rng));
228231

229232
let P_coeff = z * s;
230233
match item.inner {
@@ -239,7 +242,7 @@ impl Verifier {
239242
R_coeffs.push(z);
240243
Rs.push(R);
241244

242-
VK_coeffs.push(Scalar::zero() + (z * c));
245+
VK_coeffs.push(S::Scalar::zero() + (z * c));
243246
VKs.push(VK);
244247
}
245248

@@ -250,10 +253,10 @@ impl Verifier {
250253
.chain(VK_coeffs.iter())
251254
.chain(R_coeffs.iter());
252255

253-
let basepoints = [SpendAuth::basepoint(), Binding::basepoint()];
256+
let basepoints = [S::basepoint(), B::basepoint()];
254257
let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
255258

256-
let check = ExtendedPoint::vartime_multiscalar_mul(scalars, points);
259+
let check = S::Point::vartime_multiscalar_mul(scalars, points);
257260

258261
if check.is_small_order().into() {
259262
Ok(())

0 commit comments

Comments
 (0)