Skip to content

Commit f1f5e83

Browse files
authored
Merge pull request #124 from dalek-cryptography/errors
Add failure-based error types.
2 parents 9ba2d72 + cdc16f7 commit f1f5e83

File tree

8 files changed

+178
-78
lines changed

8 files changed

+178
-78
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ byteorder = "1.2.1"
2020
serde = "1"
2121
serde_derive = "1"
2222
tiny-keccak = "1.4.1"
23+
failure = "0.1"
2324

2425
[dev-dependencies]
2526
hex = "^0.3"

src/errors.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! Errors related to proving and verifying proofs.
2+
3+
/// Represents an error in proof creation, verification, or parsing.
4+
#[derive(Fail, Clone, Debug, Eq, PartialEq)]
5+
pub enum ProofError {
6+
/// This error occurs when a proof failed to verify.
7+
#[fail(display = "Proof verification failed.")]
8+
VerificationError,
9+
/// This error occurs when the proof encoding is malformed.
10+
#[fail(display = "Proof data could not be parsed.")]
11+
FormatError,
12+
/// This error occurs during proving if the number of blinding
13+
/// factors does not match the number of values.
14+
#[fail(display = "Wrong number of blinding factors supplied.")]
15+
WrongNumBlindingFactors,
16+
/// This error occurs when attempting to create a proof with
17+
/// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\).
18+
#[fail(display = "Invalid bitsize, must have n = 8,16,32,64")]
19+
InvalidBitsize,
20+
/// This error occurs when attempting to create an aggregated
21+
/// proof with non-power-of-two aggregation size.
22+
#[fail(display = "Invalid aggregation size, m must be a power of 2")]
23+
InvalidAggregation,
24+
/// This error results from an internal error during proving.
25+
///
26+
/// The single-party prover is implemented by performing
27+
/// multiparty computation with ourselves. However, because the
28+
/// MPC protocol is not exposed by the single-party API, we
29+
/// consider its errors to be internal errors.
30+
#[fail(display = "Internal error during proof creation: {}", _0)]
31+
ProvingError(MPCError),
32+
}
33+
34+
impl From<MPCError> for ProofError {
35+
fn from(e: MPCError) -> ProofError {
36+
match e {
37+
MPCError::InvalidBitsize => ProofError::InvalidBitsize,
38+
MPCError::InvalidAggregation => ProofError::InvalidAggregation,
39+
_ => ProofError::ProvingError(e),
40+
}
41+
}
42+
}
43+
44+
/// Represents an error during the multiparty computation protocol for
45+
/// proof aggregation.
46+
///
47+
/// This is a separate type from the `ProofError` to allow a layered
48+
/// API: although the MPC protocol is used internally for single-party
49+
/// proving, its API should not expose the complexity of the MPC
50+
/// protocol.
51+
#[derive(Fail, Clone, Debug, Eq, PartialEq)]
52+
pub enum MPCError {
53+
/// This error occurs when the dealer gives a zero challenge,
54+
/// which would annihilate the blinding factors.
55+
#[fail(display = "Dealer gave a malicious challenge value.")]
56+
MaliciousDealer,
57+
/// This error occurs when attempting to create a proof with
58+
/// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\).
59+
#[fail(display = "Invalid bitsize, must have n = 8,16,32,64")]
60+
InvalidBitsize,
61+
/// This error occurs when attempting to create an aggregated
62+
/// proof with non-power-of-two aggregation size.
63+
#[fail(display = "Invalid aggregation size, m must be a power of 2")]
64+
InvalidAggregation,
65+
/// This error occurs when the dealer is given the wrong number of
66+
/// value commitments.
67+
#[fail(display = "Wrong number of value commitments")]
68+
WrongNumValueCommitments,
69+
/// This error occurs when the dealer is given the wrong number of
70+
/// polynomial commitments.
71+
#[fail(display = "Wrong number of value commitments")]
72+
WrongNumPolyCommitments,
73+
/// This error occurs when the dealer is given the wrong number of
74+
/// proof shares.
75+
#[fail(display = "Wrong number of proof shares")]
76+
WrongNumProofShares,
77+
/// This error occurs when one or more parties submit malformed
78+
/// proof shares.
79+
#[fail(
80+
display = "Malformed proof shares from parties {:?}",
81+
bad_shares
82+
)]
83+
MalformedProofShares {
84+
/// A vector with the indexes of the parties whose shares were malformed.
85+
bad_shares: Vec<usize>,
86+
},
87+
}

src/inner_product_proof.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use curve25519_dalek::traits::VartimeMultiscalarMul;
1111

1212
use proof_transcript::ProofTranscript;
1313

14+
use errors::ProofError;
15+
1416
#[derive(Clone, Debug)]
1517
pub struct InnerProductProof {
1618
pub(crate) L_vec: Vec<CompressedRistretto>,
@@ -185,7 +187,7 @@ impl InnerProductProof {
185187
Q: &RistrettoPoint,
186188
G: &[RistrettoPoint],
187189
H: &[RistrettoPoint],
188-
) -> Result<(), &'static str>
190+
) -> Result<(), ProofError>
189191
where
190192
I: IntoIterator,
191193
I::Item: Borrow<Scalar>,
@@ -208,13 +210,13 @@ impl InnerProductProof {
208210
let Ls = self
209211
.L_vec
210212
.iter()
211-
.map(|p| p.decompress().ok_or("InnerProductProof L point is invalid"))
213+
.map(|p| p.decompress().ok_or(ProofError::VerificationError))
212214
.collect::<Result<Vec<_>, _>>()?;
213215

214216
let Rs = self
215217
.R_vec
216218
.iter()
217-
.map(|p| p.decompress().ok_or("InnerProductProof R point is invalid"))
219+
.map(|p| p.decompress().ok_or(ProofError::VerificationError))
218220
.collect::<Result<Vec<_>, _>>()?;
219221

220222
let expect_P = RistrettoPoint::vartime_multiscalar_mul(
@@ -233,7 +235,7 @@ impl InnerProductProof {
233235
if expect_P == *P {
234236
Ok(())
235237
} else {
236-
Err("InnerProductProof is invalid")
238+
Err(ProofError::VerificationError)
237239
}
238240
}
239241

@@ -267,21 +269,21 @@ impl InnerProductProof {
267269
/// * \\(n\\) is larger or equal to 32 (proof is too big),
268270
/// * any of \\(2n\\) points are not valid compressed Ristretto points,
269271
/// * any of 2 scalars are not canonical scalars modulo Ristretto group order.
270-
pub fn from_bytes(slice: &[u8]) -> Result<InnerProductProof, &'static str> {
272+
pub fn from_bytes(slice: &[u8]) -> Result<InnerProductProof, ProofError> {
271273
let b = slice.len();
272274
if b % 32 != 0 {
273-
return Err("InnerProductProof size is not divisible by 32");
275+
return Err(ProofError::FormatError);
274276
}
275277
let num_elements = b / 32;
276278
if num_elements < 2 {
277-
return Err("InnerProductProof must contain at least two 32-byte elements");
279+
return Err(ProofError::FormatError);
278280
}
279281
if (num_elements - 2) % 2 != 0 {
280-
return Err("InnerProductProof must contain even number of points");
282+
return Err(ProofError::FormatError);
281283
}
282284
let lg_n = (num_elements - 2) / 2;
283285
if lg_n >= 32 {
284-
return Err("InnerProductProof contains too many points");
286+
return Err(ProofError::FormatError);
285287
}
286288

287289
use util::read32;
@@ -295,10 +297,9 @@ impl InnerProductProof {
295297
}
296298

297299
let pos = 2 * lg_n * 32;
298-
let a = Scalar::from_canonical_bytes(read32(&slice[pos..]))
299-
.ok_or("InnerProductProof.a is not a canonical scalar")?;
300+
let a = Scalar::from_canonical_bytes(read32(&slice[pos..])).ok_or(ProofError::FormatError)?;
300301
let b = Scalar::from_canonical_bytes(read32(&slice[pos + 32..]))
301-
.ok_or("InnerProductProof.b is not a canonical scalar")?;
302+
.ok_or(ProofError::FormatError)?;
302303

303304
Ok(InnerProductProof { L_vec, R_vec, a, b })
304305
}

src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,39 @@
1111
extern crate byteorder;
1212
extern crate core;
1313
extern crate curve25519_dalek;
14+
#[macro_use]
15+
extern crate failure;
1416
extern crate rand;
1517
extern crate sha2;
1618
extern crate subtle;
1719
extern crate tiny_keccak;
18-
1920
#[macro_use]
2021
extern crate serde_derive;
2122
extern crate serde;
2223

23-
#[cfg(test)]
24-
extern crate test;
25-
2624
#[cfg(test)]
2725
extern crate bincode;
26+
#[cfg(test)]
27+
extern crate test;
2828

2929
mod util;
3030

3131
#[doc(include = "../docs/notes.md")]
3232
mod notes {}
33+
mod errors;
3334
mod generators;
3435
mod inner_product_proof;
3536
mod proof_transcript;
3637
mod range_proof;
3738

39+
pub use errors::ProofError;
3840
pub use generators::{Generators, GeneratorsView, PedersenGenerators};
3941
pub use proof_transcript::ProofTranscript;
4042
pub use range_proof::RangeProof;
4143

4244
#[doc(include = "../docs/aggregation-api.md")]
4345
pub mod aggregation {
46+
pub use errors::MPCError;
4447
pub use range_proof::dealer;
4548
pub use range_proof::messages;
4649
pub use range_proof::party;

src/range_proof/dealer.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rand::{CryptoRng, Rng};
99
use curve25519_dalek::ristretto::RistrettoPoint;
1010
use curve25519_dalek::scalar::Scalar;
1111

12+
use errors::MPCError;
1213
use generators::Generators;
1314
use inner_product_proof;
1415
use proof_transcript::ProofTranscript;
@@ -28,12 +29,12 @@ impl Dealer {
2829
n: usize,
2930
m: usize,
3031
transcript: &'a mut ProofTranscript,
31-
) -> Result<DealerAwaitingValueCommitments<'a, 'b>, &'static str> {
32-
if !n.is_power_of_two() || n > 64 {
33-
return Err("n is not valid: must be a power of 2, and less than or equal to 64");
32+
) -> Result<DealerAwaitingValueCommitments<'a, 'b>, MPCError> {
33+
if !(n == 8 || n == 16 || n == 32 || n == 64) {
34+
return Err(MPCError::InvalidBitsize);
3435
}
3536
if !m.is_power_of_two() {
36-
return Err("m is not valid: must be a power of 2");
37+
return Err(MPCError::InvalidAggregation);
3738
}
3839

3940
// At the end of the protocol, the dealer will attempt to
@@ -81,9 +82,9 @@ impl<'a, 'b> DealerAwaitingValueCommitments<'a, 'b> {
8182
pub fn receive_value_commitments(
8283
self,
8384
value_commitments: Vec<ValueCommitment>,
84-
) -> Result<(DealerAwaitingPolyCommitments<'a, 'b>, ValueChallenge), &'static str> {
85+
) -> Result<(DealerAwaitingPolyCommitments<'a, 'b>, ValueChallenge), MPCError> {
8586
if self.m != value_commitments.len() {
86-
return Err("Length of value commitments doesn't match expected length m");
87+
return Err(MPCError::WrongNumValueCommitments);
8788
}
8889

8990
// Commit each V_j individually
@@ -137,9 +138,9 @@ impl<'a, 'b> DealerAwaitingPolyCommitments<'a, 'b> {
137138
pub fn receive_poly_commitments(
138139
self,
139140
poly_commitments: Vec<PolyCommitment>,
140-
) -> Result<(DealerAwaitingProofShares<'a, 'b>, PolyChallenge), &'static str> {
141+
) -> Result<(DealerAwaitingProofShares<'a, 'b>, PolyChallenge), MPCError> {
141142
if self.m != poly_commitments.len() {
142-
return Err("Length of poly commitments doesn't match expected length m");
143+
return Err(MPCError::WrongNumPolyCommitments);
143144
}
144145

145146
// Commit sums of T_1_j's and T_2_j's
@@ -195,9 +196,9 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> {
195196
/// Used as a helper function by `receive_trusted_shares` (which
196197
/// just hands back the result) and `receive_shares` (which
197198
/// validates the proof shares.
198-
fn assemble_shares(&mut self, proof_shares: &[ProofShare]) -> Result<RangeProof, &'static str> {
199+
fn assemble_shares(&mut self, proof_shares: &[ProofShare]) -> Result<RangeProof, MPCError> {
199200
if self.m != proof_shares.len() {
200-
return Err("Length of proof shares doesn't match expected length m");
201+
return Err(MPCError::WrongNumProofShares);
201202
}
202203

203204
let t_x: Scalar = proof_shares.iter().map(|ps| ps.t_x).sum();
@@ -255,19 +256,17 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> {
255256
mut self,
256257
rng: &mut R,
257258
proof_shares: &[ProofShare],
258-
) -> Result<RangeProof, &'static str> {
259+
) -> Result<RangeProof, MPCError> {
259260
let proof = self.assemble_shares(proof_shares)?;
260261

261262
let V: Vec<_> = self.value_commitments.iter().map(|vc| vc.V_j).collect();
262263

263264
// See comment in `Dealer::new` for why we use `initial_transcript`
264-
if proof
265-
.verify(&V, self.gens, &mut self.initial_transcript, rng, self.n)
266-
.is_ok()
267-
{
265+
let transcript = &mut self.initial_transcript;
266+
if proof.verify(&V, self.gens, transcript, rng, self.n).is_ok() {
268267
Ok(proof)
269268
} else {
270-
// Create a list of bad shares
269+
// Proof verification failed. Now audit the parties:
271270
let mut bad_shares = Vec::new();
272271
for j in 0..self.m {
273272
match proof_shares[j].audit_share(
@@ -279,13 +278,10 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> {
279278
&self.poly_challenge,
280279
) {
281280
Ok(_) => {}
282-
// XXX pass errors upwards
283281
Err(_) => bad_shares.push(j),
284282
}
285283
}
286-
// XXX pass this upwards
287-
println!("bad shares: {:?}", bad_shares);
288-
Err("proof failed to verify")
284+
Err(MPCError::MalformedProofShares { bad_shares })
289285
}
290286
}
291287

@@ -305,7 +301,7 @@ impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> {
305301
pub fn receive_trusted_shares(
306302
mut self,
307303
proof_shares: &[ProofShare],
308-
) -> Result<RangeProof, &'static str> {
304+
) -> Result<RangeProof, MPCError> {
309305
self.assemble_shares(proof_shares)
310306
}
311307
}

src/range_proof/messages.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl ProofShare {
5555
value_challenge: &ValueChallenge,
5656
poly_commitment: &PolyCommitment,
5757
poly_challenge: &PolyChallenge,
58-
) -> Result<(), &'static str> {
58+
) -> Result<(), ()> {
5959
use std::iter;
6060

6161
use curve25519_dalek::traits::{IsIdentity, VartimeMultiscalarMul};
@@ -76,7 +76,7 @@ impl ProofShare {
7676
let y_inv = y.invert(); // y^(-1)
7777

7878
if self.t_x != inner_product(&self.l_vec, &self.r_vec) {
79-
return Err("Inner product of l_vec and r_vec is not equal to t_x");
79+
return Err(());
8080
}
8181

8282
let g = self.l_vec.iter().map(|l_i| minus_z - l_i);
@@ -102,7 +102,7 @@ impl ProofShare {
102102
.chain(gens.share(j).H.iter()),
103103
);
104104
if !P_check.is_identity() {
105-
return Err("P check is not equal to zero");
105+
return Err(());
106106
}
107107

108108
let sum_of_powers_y = util::sum_of_powers(&y, n);
@@ -120,10 +120,11 @@ impl ProofShare {
120120
.chain(iter::once(&gens.pedersen_generators.B))
121121
.chain(iter::once(&gens.pedersen_generators.B_blinding)),
122122
);
123-
if !t_check.is_identity() {
124-
return Err("t check is not equal to zero");
125-
}
126123

127-
Ok(())
124+
if t_check.is_identity() {
125+
Ok(())
126+
} else {
127+
Err(())
128+
}
128129
}
129130
}

0 commit comments

Comments
 (0)