|
pragma circom 2.1.6;
|
|
|
|
include "node_modules/circomlib/circuits/poseidon.circom";
|
|
include "node_modules/circomlib/circuits/mux1.circom";
|
|
include "node_modules/circomlib/circuits/comparators.circom";
|
|
include "node_modules/circomlib/circuits/gates.circom";
|
|
include "./templates/chaff.circom";
|
|
|
|
template grapevine(num_felts) {
|
|
|
|
// in_out schema
|
|
// 0: degrees of separation
|
|
// 1: secret hash from previous step
|
|
// 2: hash of username + secret hash from previous step
|
|
// 3: chaff
|
|
|
|
signal input ivc_input[4];
|
|
signal output ivc_output[4];
|
|
|
|
// private inputs
|
|
signal input phrase[num_felts]; // secret phrase, if first iteration
|
|
signal input usernames[2]; // prev username, current username
|
|
signal input auth_secrets[2]; // prev degree's user secret, current degree's user secret
|
|
|
|
// name inputs from step_in
|
|
signal degrees_of_separation <== ivc_input[0];
|
|
signal given_phrase_hash <== ivc_input[1];
|
|
signal given_degree_secret_hash <== ivc_input[2];
|
|
signal is_chaff_step <== ivc_input[3];
|
|
|
|
// determine whether degrees of separation from secret is zero
|
|
component is_degree_zero = IsZero();
|
|
is_degree_zero.in <== degrees_of_separation;
|
|
|
|
// compute poseidon hash of secret
|
|
// same as the word essentially
|
|
component phrase_hasher = Poseidon(num_felts);
|
|
phrase_hasher.inputs <== phrase;
|
|
|
|
// mux between computed hash and previous iteration's hash to get phrase hash to use
|
|
// if degrees of separation = 0 use computed hash, else use hash from previous step
|
|
component phrase_mux = Mux1();
|
|
phrase_mux.c[0] <== given_phrase_hash;
|
|
phrase_mux.c[1] <== phrase_hasher.out;
|
|
phrase_mux.s <== is_degree_zero.out;
|
|
|
|
// compute hash of given degree secret
|
|
// H(H(preimage), username, auth_secret[0])
|
|
// where preimage is muxed depending on whether degree N is 1 or > 1
|
|
component degree_secret_hasher = Poseidon(3);
|
|
degree_secret_hasher.inputs[0] <== phrase_mux.out;
|
|
degree_secret_hasher.inputs[1] <== usernames[0];
|
|
degree_secret_hasher.inputs[2] <== auth_secrets[0];
|
|
|
|
// compare computed degree secret hash to prev degree secret hash
|
|
component degree_secret_hash_match = IsEqual();
|
|
degree_secret_hash_match.in[0] <== degree_secret_hasher.out;
|
|
degree_secret_hash_match.in[1] <== given_degree_secret_hash;
|
|
|
|
// create boolean that is true if either is true:
|
|
// - given degree secret hash matches computed hash
|
|
// - is a chaff step
|
|
component degree_secret_match_or_chaff = OR();
|
|
degree_secret_match_or_chaff.a <== degree_secret_hash_match.out;
|
|
degree_secret_match_or_chaff.b <== is_chaff_step;
|
|
|
|
// create boolean that is muxes according to:
|
|
// - if degrees of separation = 0, always true (no check needed)
|
|
// - if degree of separation > 0, return output of degree_secret_match_or_chaff
|
|
component degree_secret_satisfied_mux = Mux1();
|
|
degree_secret_satisfied_mux.c[0] <== degree_secret_match_or_chaff.out;
|
|
degree_secret_satisfied_mux.c[1] <== 1;
|
|
degree_secret_satisfied_mux.s <== is_degree_zero.out;
|
|
|
|
// constrain degree_secret_satisfied_mux to be true
|
|
degree_secret_satisfied_mux.out === 1;
|
|
|
|
// compute the next username hash
|
|
component next_degree_secret_hash = Poseidon(3);
|
|
next_degree_secret_hash.inputs[0] <== phrase_mux.out;
|
|
next_degree_secret_hash.inputs[1] <== usernames[1];
|
|
next_degree_secret_hash.inputs[2] <== auth_secrets[1];
|
|
|
|
// mux step_out signal according to whether or not this is a chaff step
|
|
component chaff_mux = ChaffMux();
|
|
chaff_mux.degrees_of_separation <== degrees_of_separation;
|
|
chaff_mux.given_phrase_hash <== given_phrase_hash;
|
|
chaff_mux.given_degree_secret_hash <== given_degree_secret_hash;
|
|
chaff_mux.is_chaff_step <== is_chaff_step;
|
|
chaff_mux.computed_phrase_hash <== phrase_mux.out;
|
|
chaff_mux.computed_degree_secret_hash <== next_degree_secret_hash.out;
|
|
|
|
// wire output signals
|
|
ivc_output <== chaff_mux.out;
|
|
}
|
|
|
|
component main { public [ivc_input] } = grapevine(6);
|