mirror of https://github.com/vacp2p/zerokit.git
feat(rln): add seeded keygen (#56)
* refactor(rln/zerokit): move poseidon to separate utils crate * refactor(rln/zerokit): move merkle tree to utils crate * feat(rln): add seeded keygen
This commit is contained in:
parent
bbacc9dcce
commit
a5aa4e8d4f
|
@ -33,6 +33,7 @@ num-bigint = { version = "0.4.3", default-features = false, features = ["rand"]
|
|||
num-traits = "0.2.11"
|
||||
once_cell = "1.14.0"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3.1"
|
||||
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
|
||||
utils = { path = "../utils/", default-features = false }
|
||||
|
||||
|
|
|
@ -243,6 +243,26 @@ pub extern "C" fn key_gen(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seeded_key_gen(
|
||||
ctx: *const RLN,
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
let rln = unsafe { &*ctx };
|
||||
let input_data = <&[u8]>::from(unsafe { &*input_buffer });
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if rln.seeded_key_gen(input_data, &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(
|
||||
|
@ -758,6 +778,43 @@ mod test {
|
|||
assert_eq!(proof_is_valid, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_seeded_keygen_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 from an input seed
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let input_buffer = &Buffer::from(seed_bytes);
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = seeded_key_gen(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "seeded 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 check against expected values
|
||||
let expected_identity_secret_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(identity_secret, expected_identity_secret_seed_bytes);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_hash_to_field_ffi() {
|
||||
|
|
|
@ -429,4 +429,49 @@ mod test {
|
|||
let (deser, _) = deserialize_proof_values(&ser);
|
||||
assert_eq!(proof_values, deser);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Tests seeded keygen
|
||||
// Note that hardcoded values are only valid for Bn254
|
||||
fn test_seeded_keygen() {
|
||||
// Generate identity pair using a seed phrase
|
||||
let seed_phrase: &str = "A seed phrase example";
|
||||
let (identity_secret, id_commitment) = seeded_keygen(seed_phrase.as_bytes());
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_seed_phrase = str_to_fr(
|
||||
"0x20df38f3f00496f19fe7c6535492543b21798ed7cb91aebe4af8012db884eda3",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_phrase = str_to_fr(
|
||||
"0x1223a78a5d66043a7f9863e14507dc80720a5602b2a894923e5b5147d5a9c325",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(identity_secret, expected_identity_secret_seed_phrase);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_phrase);
|
||||
|
||||
// Generate identity pair using an byte array
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let (identity_secret, id_commitment) = seeded_keygen(seed_bytes);
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(identity_secret, expected_identity_secret_seed_bytes);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes);
|
||||
|
||||
// We check again if the identity pair generated with the same seed phrase corresponds to the previously generated one
|
||||
let (identity_secret, id_commitment) = seeded_keygen(seed_phrase.as_bytes());
|
||||
|
||||
assert_eq!(identity_secret, expected_identity_secret_seed_phrase);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_phrase);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ use ark_relations::r1cs::SynthesisError;
|
|||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use color_eyre::Result;
|
||||
use num_bigint::BigInt;
|
||||
use rand::Rng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::sync::Mutex;
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -338,6 +339,7 @@ pub fn compute_tree_root(
|
|||
|
||||
// Generates a tupe (identity_secret, id_commitment) where
|
||||
// identity_secret is random and id_commitment = PoseidonHash(identity_secret)
|
||||
// RNG is instantiated using thread_rng()
|
||||
pub fn keygen() -> (Fr, Fr) {
|
||||
let mut rng = thread_rng();
|
||||
let identity_secret = Fr::rand(&mut rng);
|
||||
|
@ -345,6 +347,23 @@ pub fn keygen() -> (Fr, Fr) {
|
|||
(identity_secret, id_commitment)
|
||||
}
|
||||
|
||||
// Generates a tupe (identity_secret, id_commitment) where
|
||||
// identity_secret is random and id_commitment = PoseidonHash(identity_secret)
|
||||
// RNG is instantiated using 20 rounds of ChaCha seeded with the hash of the input
|
||||
pub fn seeded_keygen(signal: &[u8]) -> (Fr, Fr) {
|
||||
// ChaCha20 requires a seed of exactly 32 bytes.
|
||||
// We first hash the input seed signal to a 32 bytes array and pass this as seed to ChaCha20
|
||||
let mut seed = [0; 32];
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(signal);
|
||||
hasher.finalize(&mut seed);
|
||||
|
||||
let mut rng = ChaCha20Rng::from_seed(seed);
|
||||
let identity_secret = Fr::rand(&mut rng);
|
||||
let id_commitment = poseidon_hash(&[identity_secret]);
|
||||
(identity_secret, id_commitment)
|
||||
}
|
||||
|
||||
// Hashes arbitrary signal to the underlying prime field
|
||||
pub fn hash_to_field(signal: &[u8]) -> Fr {
|
||||
// We hash the input signal using Keccak256
|
||||
|
|
|
@ -343,6 +343,21 @@ impl RLN<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn seeded_key_gen<R: Read, W: Write>(
|
||||
&self,
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
) -> io::Result<()> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (id_key, id_commitment_key) = seeded_keygen(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&id_key))?;
|
||||
output_data.write_all(&fr_to_bytes_le(&id_commitment_key))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn hash<R: Read, W: Write>(&self, mut input_data: R, mut output_data: W) -> io::Result<()> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
@ -824,6 +839,36 @@ mod test {
|
|||
assert!(verified);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeded_keygen() {
|
||||
let rln = RLN::default();
|
||||
|
||||
let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let mut input_buffer = Cursor::new(&seed_bytes);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
rln.seeded_key_gen(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
let serialized_output = output_buffer.into_inner();
|
||||
|
||||
let (identity_secret, read) = bytes_le_to_fr(&serialized_output);
|
||||
let (id_commitment, _) = bytes_le_to_fr(&serialized_output[read..].to_vec());
|
||||
|
||||
// We check against expected values
|
||||
let expected_identity_secret_seed_bytes = str_to_fr(
|
||||
"0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
|
||||
16,
|
||||
);
|
||||
let expected_id_commitment_seed_bytes = str_to_fr(
|
||||
"0xbf16d2b5c0d6f9d9d561e05bfca16a81b4b873bb063508fae360d8c74cef51f",
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(identity_secret, expected_identity_secret_seed_bytes);
|
||||
assert_eq!(id_commitment, expected_id_commitment_seed_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_field() {
|
||||
let rln = RLN::default();
|
||||
|
|
|
@ -15,4 +15,4 @@ tiny-keccak = { version = "2.0.2", features = ["keccak"] }
|
|||
|
||||
[features]
|
||||
default = ["parallel"]
|
||||
parallel = ["ark-ff/parallel"]
|
||||
parallel = ["ark-ff/parallel"]
|
||||
|
|
Loading…
Reference in New Issue