zkSNARK: Enforce that new output notes have unique `rho` to prevent faerie gold attack.
This commit is contained in:
parent
e52f40e839
commit
6b010d9bfd
|
@ -20,16 +20,24 @@ private:
|
||||||
|
|
||||||
// Aux inputs
|
// Aux inputs
|
||||||
pb_variable<FieldT> ZERO;
|
pb_variable<FieldT> ZERO;
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> zk_phi;
|
||||||
|
|
||||||
// Input note gadgets
|
// Input note gadgets
|
||||||
boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
|
boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
|
||||||
boost::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_hmac_authentication;
|
boost::array<std::shared_ptr<PRF_pk_gadget<FieldT>>, NumInputs> zk_hmac_authentication;
|
||||||
|
|
||||||
|
// Output note gadgets
|
||||||
|
boost::array<std::shared_ptr<output_note_gadget<FieldT>>, NumOutputs> zk_output_notes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// PRF_pk only has a 1-bit domain separation "nonce"
|
// PRF_pk only has a 1-bit domain separation "nonce"
|
||||||
// for different hmacs.
|
// for different macs.
|
||||||
BOOST_STATIC_ASSERT(NumInputs <= 2);
|
BOOST_STATIC_ASSERT(NumInputs <= 2);
|
||||||
|
|
||||||
|
// PRF_rho only has a 1-bit domain separation "nonce"
|
||||||
|
// for different output `rho`.
|
||||||
|
BOOST_STATIC_ASSERT(NumOutputs <= 2);
|
||||||
|
|
||||||
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
|
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
|
||||||
// Verification
|
// Verification
|
||||||
{
|
{
|
||||||
|
@ -77,6 +85,8 @@ public:
|
||||||
// to be one automatically for us, and is known as `ONE`.
|
// to be one automatically for us, and is known as `ONE`.
|
||||||
ZERO.allocate(pb);
|
ZERO.allocate(pb);
|
||||||
|
|
||||||
|
zk_phi.reset(new digest_variable<FieldT>(pb, 252, ""));
|
||||||
|
|
||||||
for (size_t i = 0; i < NumInputs; i++) {
|
for (size_t i = 0; i < NumInputs; i++) {
|
||||||
// Input note gadget for commitments, hmacs, nullifiers,
|
// Input note gadget for commitments, hmacs, nullifiers,
|
||||||
// and spend authority.
|
// and spend authority.
|
||||||
|
@ -97,6 +107,16 @@ public:
|
||||||
zk_input_hmacs[i]
|
zk_input_hmacs[i]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
zk_output_notes[i].reset(new output_note_gadget<FieldT>(
|
||||||
|
pb,
|
||||||
|
ZERO,
|
||||||
|
zk_phi->bits,
|
||||||
|
zk_h_sig->bits,
|
||||||
|
i ? true : false
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_r1cs_constraints() {
|
void generate_r1cs_constraints() {
|
||||||
|
@ -107,6 +127,9 @@ public:
|
||||||
// Constrain `ZERO`
|
// Constrain `ZERO`
|
||||||
generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO");
|
generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO");
|
||||||
|
|
||||||
|
// Constrain bitness of phi
|
||||||
|
zk_phi->generate_r1cs_constraints();
|
||||||
|
|
||||||
for (size_t i = 0; i < NumInputs; i++) {
|
for (size_t i = 0; i < NumInputs; i++) {
|
||||||
// Constrain the JoinSplit input constraints.
|
// Constrain the JoinSplit input constraints.
|
||||||
zk_input_notes[i]->generate_r1cs_constraints();
|
zk_input_notes[i]->generate_r1cs_constraints();
|
||||||
|
@ -114,6 +137,11 @@ public:
|
||||||
// Authenticate h_sig with a_sk
|
// Authenticate h_sig with a_sk
|
||||||
zk_hmac_authentication[i]->generate_r1cs_constraints();
|
zk_hmac_authentication[i]->generate_r1cs_constraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
// Constrain the JoinSplit output constraints.
|
||||||
|
zk_output_notes[i]->generate_r1cs_constraints();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_r1cs_witness(
|
void generate_r1cs_witness(
|
||||||
|
@ -128,6 +156,12 @@ public:
|
||||||
// Witness `zero`
|
// Witness `zero`
|
||||||
this->pb.val(ZERO) = FieldT::zero();
|
this->pb.val(ZERO) = FieldT::zero();
|
||||||
|
|
||||||
|
// Witness phi
|
||||||
|
zk_phi->bits.fill_with_bits(
|
||||||
|
this->pb,
|
||||||
|
trailing252(uint256_to_bool_vector(phi))
|
||||||
|
);
|
||||||
|
|
||||||
// Witness h_sig
|
// Witness h_sig
|
||||||
zk_h_sig->bits.fill_with_bits(
|
zk_h_sig->bits.fill_with_bits(
|
||||||
this->pb,
|
this->pb,
|
||||||
|
@ -142,6 +176,11 @@ public:
|
||||||
zk_hmac_authentication[i]->generate_r1cs_witness();
|
zk_hmac_authentication[i]->generate_r1cs_witness();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumOutputs; i++) {
|
||||||
|
// Witness the output information.
|
||||||
|
zk_output_notes[i]->generate_r1cs_witness(outputs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// This happens last, because only by now are all the
|
// This happens last, because only by now are all the
|
||||||
// verifier inputs resolved.
|
// verifier inputs resolved.
|
||||||
unpacker->generate_r1cs_witness_from_bits();
|
unpacker->generate_r1cs_witness_from_bits();
|
||||||
|
|
|
@ -105,3 +105,58 @@ public:
|
||||||
expose_nullifiers->generate_r1cs_witness();
|
expose_nullifiers->generate_r1cs_witness();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename FieldT>
|
||||||
|
class output_note_gadget : public note_gadget<FieldT> {
|
||||||
|
private:
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> rho;
|
||||||
|
|
||||||
|
std::shared_ptr<PRF_rho_gadget<FieldT>> prevent_faerie_gold;
|
||||||
|
|
||||||
|
public:
|
||||||
|
output_note_gadget(
|
||||||
|
protoboard<FieldT>& pb,
|
||||||
|
pb_variable<FieldT>& ZERO,
|
||||||
|
pb_variable_array<FieldT>& phi,
|
||||||
|
pb_variable_array<FieldT>& h_sig,
|
||||||
|
bool nonce
|
||||||
|
) : note_gadget<FieldT>(pb) {
|
||||||
|
rho.reset(new digest_variable<FieldT>(pb, 256, ""));
|
||||||
|
|
||||||
|
// Do not allow the caller to choose the same "rho"
|
||||||
|
// for any two valid notes in a given view of the
|
||||||
|
// blockchain. See protocol specification for more
|
||||||
|
// details.
|
||||||
|
prevent_faerie_gold.reset(new PRF_rho_gadget<FieldT>(
|
||||||
|
pb,
|
||||||
|
ZERO,
|
||||||
|
phi,
|
||||||
|
h_sig,
|
||||||
|
nonce,
|
||||||
|
rho
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_constraints() {
|
||||||
|
note_gadget<FieldT>::generate_r1cs_constraints();
|
||||||
|
|
||||||
|
// TODO: This constraint may not be necessary if SHA256
|
||||||
|
// already boolean constrains its outputs.
|
||||||
|
rho->generate_r1cs_constraints();
|
||||||
|
|
||||||
|
prevent_faerie_gold->generate_r1cs_constraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_witness(const Note& note) {
|
||||||
|
note_gadget<FieldT>::generate_r1cs_witness(note);
|
||||||
|
|
||||||
|
prevent_faerie_gold->generate_r1cs_witness();
|
||||||
|
|
||||||
|
// [SANITY CHECK] Witness rho ourselves with the
|
||||||
|
// note information.
|
||||||
|
rho->bits.fill_with_bits(
|
||||||
|
this->pb,
|
||||||
|
uint256_to_bool_vector(note.rho)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -116,3 +116,24 @@ public:
|
||||||
PRF_gadget<FieldT>::generate_r1cs_witness();
|
PRF_gadget<FieldT>::generate_r1cs_witness();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename FieldT>
|
||||||
|
class PRF_rho_gadget : public PRF_gadget<FieldT> {
|
||||||
|
public:
|
||||||
|
PRF_rho_gadget(
|
||||||
|
protoboard<FieldT>& pb,
|
||||||
|
pb_variable<FieldT>& ZERO,
|
||||||
|
pb_variable_array<FieldT>& phi,
|
||||||
|
pb_variable_array<FieldT>& h_sig,
|
||||||
|
bool nonce,
|
||||||
|
std::shared_ptr<digest_variable<FieldT>> result
|
||||||
|
) : PRF_gadget<FieldT>(pb, ZERO, 0, nonce, 1, 0, phi, h_sig, result) {}
|
||||||
|
|
||||||
|
void generate_r1cs_constraints() {
|
||||||
|
PRF_gadget<FieldT>::generate_r1cs_constraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_r1cs_witness() {
|
||||||
|
PRF_gadget<FieldT>::generate_r1cs_witness();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue