mirror of https://github.com/vacp2p/zerokit.git
feat(rln): add recover identity secret API (#80)
* feat(rln): add API to recover id_secret when a user is slashed * feat(rln): add RLN API for recovering id_secret * feat(rln): add recover_id_secret FFI API; update public API docs * fix(rln): address reviewers' comments * fix(rln): removing ; for clippy
This commit is contained in:
parent
284e51483c
commit
60e3369621
195
rln/src/ffi.rs
195
rln/src/ffi.rs
|
@ -297,6 +297,31 @@ pub extern "C" fn seeded_key_gen(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn recover_id_secret(
|
||||
ctx: *const RLN,
|
||||
input_proof_buffer_1: *const Buffer,
|
||||
input_proof_buffer_2: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
let rln = unsafe { &*ctx };
|
||||
let input_proof_data_1 = <&[u8]>::from(unsafe { &*input_proof_buffer_1 });
|
||||
let input_proof_data_2 = <&[u8]>::from(unsafe { &*input_proof_buffer_2 });
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if rln
|
||||
.recover_id_secret(input_proof_data_1, input_proof_data_2, &mut output_data)
|
||||
.is_ok()
|
||||
{
|
||||
unsafe { *output_buffer = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
} else {
|
||||
std::mem::forget(output_data);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn hash(
|
||||
|
@ -400,7 +425,7 @@ mod test {
|
|||
let leaves_ser = vec_fr_to_bytes_le(&leaves);
|
||||
let input_buffer = &Buffer::from(leaves_ser.as_ref());
|
||||
let success = init_tree_with_leaves(rln_pointer, input_buffer);
|
||||
assert!(success, "set leaves call failed");
|
||||
assert!(success, "init tree with leaves call failed");
|
||||
|
||||
// We get the root of the tree obtained adding leaves in batch
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
|
@ -891,7 +916,7 @@ mod test {
|
|||
let leaves_ser = vec_fr_to_bytes_le(&leaves);
|
||||
let input_buffer = &Buffer::from(leaves_ser.as_ref());
|
||||
let success = init_tree_with_leaves(rln_pointer, input_buffer);
|
||||
assert!(success, "set leaves call failed");
|
||||
assert!(success, "init tree with leaves call failed");
|
||||
|
||||
// We generate a new identity pair
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
|
@ -931,7 +956,7 @@ mod test {
|
|||
let input_buffer = &Buffer::from(serialized.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = generate_rln_proof(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "set leaves call failed");
|
||||
assert!(success, "generate rln proof call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
// result_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let mut proof_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
@ -1016,7 +1041,7 @@ mod test {
|
|||
let input_buffer = &Buffer::from(serialized.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = generate_rln_proof(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "set leaves call failed");
|
||||
assert!(success, "generate rln proof call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
// result_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let mut proof_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
@ -1079,6 +1104,168 @@ mod test {
|
|||
assert_eq!(proof_is_valid, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Computes and verifies an RLN ZK proof using FFI APIs
|
||||
fn test_recover_id_secret_ffi() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
// We create a RLN instance
|
||||
let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit();
|
||||
let input_buffer = &Buffer::from(TEST_RESOURCES_FOLDER.as_bytes());
|
||||
let success = new(tree_height, input_buffer, rln_pointer.as_mut_ptr());
|
||||
assert!(success, "RLN object creation failed");
|
||||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
// We generate a new identity pair
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = key_gen(rln_pointer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_secret, read) = bytes_le_to_fr(&result_data);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&result_data[read..].to_vec());
|
||||
|
||||
// We set as leaf id_commitment, its index would be equal to 0 since tree is empty
|
||||
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_next_leaf(rln_pointer, input_buffer);
|
||||
assert!(success, "set next leaf call failed");
|
||||
|
||||
let identity_index: u64 = 0;
|
||||
|
||||
// We generate two proofs using same epoch but different signals.
|
||||
|
||||
// We generate two random signals
|
||||
let mut rng = rand::thread_rng();
|
||||
let signal1: [u8; 32] = rng.gen();
|
||||
let signal1_len = u64::try_from(signal1.len()).unwrap();
|
||||
|
||||
// We generate two random signals
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
let signal2_len = u64::try_from(signal2.len()).unwrap();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
|
||||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let mut serialized1: Vec<u8> = Vec::new();
|
||||
serialized1.append(&mut fr_to_bytes_le(&identity_secret));
|
||||
serialized1.append(&mut identity_index.to_le_bytes().to_vec());
|
||||
serialized1.append(&mut fr_to_bytes_le(&epoch));
|
||||
|
||||
// The first part is the same for both proof input, so we clone
|
||||
let mut serialized2 = serialized1.clone();
|
||||
|
||||
// We attach the first signal to the first proof input
|
||||
serialized1.append(&mut signal1_len.to_le_bytes().to_vec());
|
||||
serialized1.append(&mut signal1.to_vec());
|
||||
|
||||
// We attach the second signal to the first proof input
|
||||
serialized2.append(&mut signal2_len.to_le_bytes().to_vec());
|
||||
serialized2.append(&mut signal2.to_vec());
|
||||
|
||||
// We call generate_rln_proof for first proof values
|
||||
let input_buffer = &Buffer::from(serialized1.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = generate_rln_proof(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "generate rln proof call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
// result_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let proof_data_1 = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
// We call generate_rln_proof
|
||||
let input_buffer = &Buffer::from(serialized2.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = generate_rln_proof(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "generate rln proof call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
// result_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let proof_data_2 = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
let input_proof_buffer_1 = &Buffer::from(proof_data_1.as_ref());
|
||||
let input_proof_buffer_2 = &Buffer::from(proof_data_2.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = recover_id_secret(
|
||||
rln_pointer,
|
||||
input_proof_buffer_1,
|
||||
input_proof_buffer_2,
|
||||
output_buffer.as_mut_ptr(),
|
||||
);
|
||||
assert!(success, "recover id secret call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let serialized_id_secret = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
// We passed two shares for the same secret, so recovery should be successful
|
||||
// To check it, we ensure that recovered_id_secret is non-empty
|
||||
assert!(!serialized_id_secret.is_empty());
|
||||
|
||||
// We check if the recovered id secret corresponds to the original one
|
||||
let (recovered_id_secret, _) = bytes_le_to_fr(&serialized_id_secret);
|
||||
assert_eq!(recovered_id_secret, identity_secret);
|
||||
|
||||
// We now test that computing_id_secret is unsuccessful if shares computed from two different id secrets but within same epoch are passed
|
||||
|
||||
// We generate a new identity pair
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = key_gen(rln_pointer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "key gen call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (identity_secret_new, read) = bytes_le_to_fr(&result_data);
|
||||
let (id_commitment_new, _) = bytes_le_to_fr(&result_data[read..].to_vec());
|
||||
|
||||
// We set as leaf id_commitment, its index would be equal to 1 since at 0 there is id_commitment
|
||||
let leaf_ser = fr_to_bytes_le(&id_commitment_new);
|
||||
let input_buffer = &Buffer::from(leaf_ser.as_ref());
|
||||
let success = set_next_leaf(rln_pointer, input_buffer);
|
||||
assert!(success, "set next leaf call failed");
|
||||
|
||||
let identity_index_new: u64 = 1;
|
||||
|
||||
// We generate a random signals
|
||||
let signal3: [u8; 32] = rng.gen();
|
||||
let signal3_len = u64::try_from(signal3.len()).unwrap();
|
||||
|
||||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
// Note that epoch is the same as before
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
serialized.append(&mut fr_to_bytes_le(&identity_secret_new));
|
||||
serialized.append(&mut identity_index_new.to_le_bytes().to_vec());
|
||||
serialized.append(&mut fr_to_bytes_le(&epoch));
|
||||
serialized.append(&mut signal3_len.to_le_bytes().to_vec());
|
||||
serialized.append(&mut signal3.to_vec());
|
||||
|
||||
// We call generate_rln_proof
|
||||
let input_buffer = &Buffer::from(serialized.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = generate_rln_proof(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "generate rln proof call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
// result_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let proof_data_3 = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
// We attempt to recover the secret using share1 (coming from identity_secret) and share3 (coming from identity_secret_new)
|
||||
|
||||
let input_proof_buffer_1 = &Buffer::from(proof_data_1.as_ref());
|
||||
let input_proof_buffer_3 = &Buffer::from(proof_data_3.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = recover_id_secret(
|
||||
rln_pointer,
|
||||
input_proof_buffer_1,
|
||||
input_proof_buffer_3,
|
||||
output_buffer.as_mut_ptr(),
|
||||
);
|
||||
assert!(success, "recover id secret call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let serialized_id_secret = <&[u8]>::from(&output_buffer).to_vec();
|
||||
|
||||
// We passed two shares for different secrets, so recovery should be not successful
|
||||
// To check it, we ensure that recovered_id_secret is empty
|
||||
assert!(serialized_id_secret.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_keygen_ffi() {
|
||||
|
|
|
@ -253,8 +253,7 @@ pub fn proof_values_from_witness(rln_witness: &RLNWitnessInput) -> RLNProofValue
|
|||
// y share
|
||||
let a_0 = rln_witness.identity_secret;
|
||||
let a_1 = poseidon_hash(&[a_0, rln_witness.epoch]);
|
||||
let y = rln_witness.x * a_1;
|
||||
let y = y + a_0;
|
||||
let y = a_0 + rln_witness.x * a_1;
|
||||
|
||||
// Nullifier
|
||||
let nullifier = poseidon_hash(&[a_1, rln_witness.rln_identifier]);
|
||||
|
@ -290,6 +289,8 @@ pub fn serialize_proof_values(rln_proof_values: &RLNProofValues) -> Vec<u8> {
|
|||
serialized
|
||||
}
|
||||
|
||||
// Note: don't forget to skip the 128 bytes ZK proof, if serialized contains it.
|
||||
// This proc deserialzies only proof _values_, i.e. circuit outputs, not the zk proof.
|
||||
pub fn deserialize_proof_values(serialized: &[u8]) -> (RLNProofValues, usize) {
|
||||
let mut all_read: usize = 0;
|
||||
|
||||
|
@ -382,7 +383,7 @@ pub fn compute_tree_root(
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Signal/nullifier utility functions
|
||||
// Protocol utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
// Generates a tupe (identity_secret, id_commitment) where
|
||||
|
@ -426,6 +427,30 @@ pub fn hash_to_field(signal: &[u8]) -> Fr {
|
|||
el
|
||||
}
|
||||
|
||||
pub fn compute_id_secret(share1: (Fr, Fr), share2: (Fr, Fr), epoch: Fr) -> Result<Fr, String> {
|
||||
// Assuming a0 is the identity secret and a1 = poseidonHash([a0, epoch]),
|
||||
// a (x,y) share satisfies the following relation
|
||||
// y = a_0 + x * a_1
|
||||
let (x1, y1) = share1;
|
||||
let (x2, y2) = share2;
|
||||
|
||||
// If the two input shares were computed for the same epoch and identity secret, we can recover the latter
|
||||
// y1 = a_0 + x1 * a_1
|
||||
// y2 = a_0 + x2 * a_1
|
||||
let a_1 = (y1 - y2) / (x1 - x2);
|
||||
let a_0 = y1 - x1 * a_1;
|
||||
|
||||
// If shares come from the same polynomial, a0 is correctly recovered and a1 = poseidonHash([a0, epoch])
|
||||
let computed_a_1 = poseidon_hash(&[a_0, epoch]);
|
||||
|
||||
if a_1 == computed_a_1 {
|
||||
// We successfully recovered the identity secret
|
||||
return Ok(a_0);
|
||||
} else {
|
||||
return Err("Cannot recover id_secret from provided shares".into());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// zkSNARK utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
|
|
@ -528,7 +528,7 @@ impl RLN<'_> {
|
|||
/// rln.generate_rln_proof(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // proof_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
/// // proof_data is [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||
/// let mut proof_data = output_buffer.into_inner();
|
||||
/// ```
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -830,6 +830,77 @@ impl RLN<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Recovers the identity secret from two set of proof values computed for same secret in same epoch.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_proof_data_1`: a reader for the serialization of a RLN zkSNARK proof concatenated with a serialization of the circuit output values and -optionally- the signal information, i.e. either `[ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]` or `[ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ]` (to maintain compatibility with both output of [`generate_rln_proof`](crate::public::RLN::generate_rln_proof) and input of [`verify_rln_proof`](crate::public::RLN::verify_rln_proof))
|
||||
/// - `input_proof_data_2`: same as `input_proof_data_1`
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the recovered identity secret field element if correctly recovered (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le)), a writer receiving an empty byte vector if not.
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// // identity_secret, proof_data_1 and proof_data_2 are computed as in the example code snippet provided for rln::public::RLN::generate_rln_proof using same identity secret and epoch (but not necessarily same signal)
|
||||
///
|
||||
/// let mut input_proof_data_1 = Cursor::new(proof_data_1);
|
||||
/// let mut input_proof_data_2 = Cursor::new(proof_data_2);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.recover_id_secret(
|
||||
/// &mut input_proof_data_1,
|
||||
/// &mut input_proof_data_2,
|
||||
/// &mut output_buffer,
|
||||
/// )
|
||||
/// .unwrap();
|
||||
///
|
||||
/// let serialized_id_secret = output_buffer.into_inner();
|
||||
///
|
||||
/// // We ensure that a non-empty value is written to output_buffer
|
||||
/// assert!(!serialized_id_secret.is_empty());
|
||||
///
|
||||
/// // We check if the recovered id secret corresponds to the original one
|
||||
/// let (recovered_id_secret, _) = bytes_le_to_fr(&serialized_id_secret);
|
||||
/// assert_eq!(recovered_id_secret, identity_secret);
|
||||
/// ```
|
||||
pub fn recover_id_secret<R: Read, W: Write>(
|
||||
&self,
|
||||
mut input_proof_data_1: R,
|
||||
mut input_proof_data_2: R,
|
||||
mut output_data: W,
|
||||
) -> io::Result<()> {
|
||||
// We deserialize the two proofs and we get the corresponding RLNProofValues objects
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_proof_data_1.read_to_end(&mut serialized)?;
|
||||
// We skip deserialization of the zk-proof at the beginning
|
||||
let (proof_values_1, _) = deserialize_proof_values(&serialized[128..].to_vec());
|
||||
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_proof_data_2.read_to_end(&mut serialized)?;
|
||||
// We skip deserialization of the zk-proof at the beginning
|
||||
let (proof_values_2, _) = deserialize_proof_values(&serialized[128..].to_vec());
|
||||
|
||||
// We continue only if the proof values are for the same epoch
|
||||
// The idea is that proof values that go as input to this function are verified first (with zk-proof verify), hence ensuring validity of epoch and other fields.
|
||||
// Only in case all fields are valid, an external_nullifier for the message will be stored (otherwise signal/proof will be simply discarded)
|
||||
// If the nullifier matches one already seen, we can recovery of identity secret.
|
||||
if proof_values_1.epoch == proof_values_2.epoch {
|
||||
// We extract the two shares
|
||||
let share1 = (proof_values_1.x, proof_values_1.y);
|
||||
let share2 = (proof_values_2.x, proof_values_2.y);
|
||||
|
||||
// We recover the secret
|
||||
let recovered_id_secret = compute_id_secret(share1, share2, proof_values_1.epoch);
|
||||
|
||||
// If an id secret is recovered, we write it to output_data, otherwise nothing will be written.
|
||||
if recovered_id_secret.is_ok() {
|
||||
let id_secret = recovered_id_secret.unwrap();
|
||||
output_data.write_all(&fr_to_bytes_le(&id_secret))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hashes an input signal to an element in the working prime field.
|
||||
///
|
||||
/// The result is computed as the Keccak256 of the input signal modulo the prime field characteristic.
|
||||
|
@ -1619,4 +1690,132 @@ mod test {
|
|||
|
||||
assert!(verified);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recover_id_secret() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
// We create a new RLN instance
|
||||
let input_buffer = Cursor::new(TEST_RESOURCES_FOLDER);
|
||||
let mut rln = RLN::new(tree_height, input_buffer);
|
||||
|
||||
// Generate identity pair
|
||||
let (identity_secret, id_commitment) = keygen();
|
||||
|
||||
// We set as leaf id_commitment after storing its index
|
||||
let identity_index = u64::try_from(rln.tree.leaves_set()).unwrap();
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
||||
rln.set_next_leaf(&mut buffer).unwrap();
|
||||
|
||||
// We generate two random signals
|
||||
let mut rng = rand::thread_rng();
|
||||
let signal1: [u8; 32] = rng.gen();
|
||||
let signal1_len = u64::try_from(signal1.len()).unwrap();
|
||||
|
||||
let signal2: [u8; 32] = rng.gen();
|
||||
let signal2_len = u64::try_from(signal2.len()).unwrap();
|
||||
|
||||
// We generate a random epoch
|
||||
let epoch = hash_to_field(b"test-epoch");
|
||||
|
||||
// We generate two proofs using same epoch but different signals.
|
||||
|
||||
// We prepare input for generate_rln_proof API
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let mut serialized1: Vec<u8> = Vec::new();
|
||||
serialized1.append(&mut fr_to_bytes_le(&identity_secret));
|
||||
serialized1.append(&mut identity_index.to_le_bytes().to_vec());
|
||||
serialized1.append(&mut fr_to_bytes_le(&epoch));
|
||||
|
||||
// The first part is the same for both proof input, so we clone
|
||||
let mut serialized2 = serialized1.clone();
|
||||
|
||||
// We attach the first signal to the first proof input
|
||||
serialized1.append(&mut signal1_len.to_le_bytes().to_vec());
|
||||
serialized1.append(&mut signal1.to_vec());
|
||||
|
||||
// We attach the second signal to the first proof input
|
||||
serialized2.append(&mut signal2_len.to_le_bytes().to_vec());
|
||||
serialized2.append(&mut signal2.to_vec());
|
||||
|
||||
// We generate the first proof
|
||||
let mut input_buffer = Cursor::new(serialized1);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.generate_rln_proof(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
let proof_data_1 = output_buffer.into_inner();
|
||||
|
||||
// We generate the second proof
|
||||
let mut input_buffer = Cursor::new(serialized2);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.generate_rln_proof(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
let proof_data_2 = output_buffer.into_inner();
|
||||
|
||||
let mut input_proof_data_1 = Cursor::new(proof_data_1.clone());
|
||||
let mut input_proof_data_2 = Cursor::new(proof_data_2);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.recover_id_secret(
|
||||
&mut input_proof_data_1,
|
||||
&mut input_proof_data_2,
|
||||
&mut output_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let serialized_id_secret = output_buffer.into_inner();
|
||||
|
||||
// We ensure that a non-empty value is written to output_buffer
|
||||
assert!(!serialized_id_secret.is_empty());
|
||||
|
||||
// We check if the recovered id secret corresponds to the original one
|
||||
let (recovered_id_secret, _) = bytes_le_to_fr(&serialized_id_secret);
|
||||
assert_eq!(recovered_id_secret, identity_secret);
|
||||
|
||||
// We now test that computing_id_secret is unsuccessful if shares computed from two different id secrets but within same epoch are passed
|
||||
|
||||
// We generate a new identity pair
|
||||
let (identity_secret_new, id_commitment_new) = keygen();
|
||||
|
||||
// We add it to the tree
|
||||
let identity_index_new = u64::try_from(rln.tree.leaves_set()).unwrap();
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment_new));
|
||||
rln.set_next_leaf(&mut buffer).unwrap();
|
||||
|
||||
// We generate a random signals
|
||||
let signal3: [u8; 32] = rng.gen();
|
||||
let signal3_len = u64::try_from(signal3.len()).unwrap();
|
||||
|
||||
// We prepare proof input. Note that epoch is the same as before
|
||||
// input_data is [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let mut serialized3: Vec<u8> = Vec::new();
|
||||
serialized3.append(&mut fr_to_bytes_le(&identity_secret_new));
|
||||
serialized3.append(&mut identity_index_new.to_le_bytes().to_vec());
|
||||
serialized3.append(&mut fr_to_bytes_le(&epoch));
|
||||
serialized3.append(&mut signal3_len.to_le_bytes().to_vec());
|
||||
serialized3.append(&mut signal3.to_vec());
|
||||
|
||||
// We generate the proof
|
||||
let mut input_buffer = Cursor::new(serialized3);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.generate_rln_proof(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
let proof_data_3 = output_buffer.into_inner();
|
||||
|
||||
// We attempt to recover the secret using share1 (coming from identity_secret) and share3 (coming from identity_secret_new)
|
||||
|
||||
let mut input_proof_data_1 = Cursor::new(proof_data_1.clone());
|
||||
let mut input_proof_data_3 = Cursor::new(proof_data_3);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.recover_id_secret(
|
||||
&mut input_proof_data_1,
|
||||
&mut input_proof_data_3,
|
||||
&mut output_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let serialized_id_secret = output_buffer.into_inner();
|
||||
|
||||
// We ensure that an empty value was written to output_buffer, i.e. no secret is recovered
|
||||
assert!(serialized_id_secret.is_empty());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue