Compare commits

...

8 Commits

Author SHA1 Message Date
sydhds
f4c085ae42 Add missing test file 2025-07-18 10:53:09 +02:00
sydhds
ee712ea84f Add unit tests of BE keygen related functions against Q value 2025-07-18 10:52:48 +02:00
sydhds
2749be14c6 Fix fr_to_bytes_be function 2025-07-11 09:37:16 +02:00
sydhds
0f67f0ecd5 Add be functions in ffi 2025-07-10 08:24:45 +02:00
sydhds
acf313e032 Add hash_be and poseidon_hash_be functions 2025-06-30 12:18:47 +02:00
sydhds
2e3528c9b2 Add BE functions in public API too 2025-06-30 12:18:47 +02:00
sydhds
833bbd1fc3 Cargo fmt pass 2025-06-30 12:18:47 +02:00
sydhds
ce9e05484e Add BE serialization for Fr and identity pair/tuple 2025-06-30 12:18:47 +02:00
8 changed files with 386 additions and 4 deletions

7
Cargo.lock generated
View File

@@ -613,6 +613,12 @@ dependencies = [
"half",
]
[[package]]
name = "claims"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18"
[[package]]
name = "clap"
version = "4.5.39"
@@ -1639,6 +1645,7 @@ dependencies = [
"ark-std 0.5.0",
"byteorder",
"cfg-if",
"claims",
"criterion",
"document-features",
"lazy_static",

View File

@@ -64,6 +64,7 @@ document-features = { version = "0.2.11", optional = true }
[dev-dependencies]
criterion = { version = "0.6.0", features = ["html_reports"] }
claims = "0.8.0"
[features]
default = ["pmtree-ft"]

View File

@@ -466,6 +466,12 @@ pub extern "C" fn key_gen(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
call_with_output_arg!(ctx, key_gen, output_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn key_gen_be(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
call_with_output_arg!(ctx, key_gen_be, output_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn seeded_key_gen(
@@ -476,12 +482,28 @@ pub extern "C" fn seeded_key_gen(
call_with_output_arg!(ctx, seeded_key_gen, output_buffer, input_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn seeded_key_gen_be(
ctx: *const RLN,
input_buffer: *const Buffer,
output_buffer: *mut Buffer,
) -> bool {
call_with_output_arg!(ctx, seeded_key_gen_be, output_buffer, input_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn extended_key_gen(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
call_with_output_arg!(ctx, extended_key_gen, output_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn extended_key_gen_be(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
call_with_output_arg!(ctx, extended_key_gen_be, output_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn seeded_extended_key_gen(
@@ -492,6 +514,16 @@ pub extern "C" fn seeded_extended_key_gen(
call_with_output_arg!(ctx, seeded_extended_key_gen, output_buffer, input_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn seeded_extended_key_gen_be(
ctx: *const RLN,
input_buffer: *const Buffer,
output_buffer: *mut Buffer,
) -> bool {
call_with_output_arg!(ctx, seeded_extended_key_gen_be, output_buffer, input_buffer)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn recover_id_secret(

View File

@@ -20,8 +20,9 @@ use crate::hashers::{hash_to_field, poseidon_hash};
use crate::poseidon_tree::{MerkleProof, PoseidonTree};
use crate::public::RLN_IDENTIFIER;
use crate::utils::{
bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size, fr_to_bytes_le,
normalize_usize, to_bigint, vec_fr_to_bytes_le, vec_u8_to_bytes_le,
bytes_be_to_fr, bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size,
fr_to_bytes_be, fr_to_bytes_le, normalize_usize, to_bigint, vec_fr_to_bytes_le,
vec_u8_to_bytes_le,
};
use utils::{ZerokitMerkleProof, ZerokitMerkleTree};
///////////////////////////////////////////////////////
@@ -60,9 +61,17 @@ pub fn serialize_field_element(element: Fr) -> Vec<u8> {
fr_to_bytes_le(&element)
}
pub fn serialize_field_element_be(element: Fr) -> Vec<u8> {
fr_to_bytes_be(&element)
}
pub fn deserialize_field_element(serialized: Vec<u8>) -> Fr {
let (element, _) = bytes_le_to_fr(&serialized);
element
}
pub fn deserialize_field_element_be(serialized: Vec<u8>) -> Fr {
let (element, _) = bytes_be_to_fr(&serialized);
element
}
@@ -73,6 +82,13 @@ pub fn deserialize_identity_pair(serialized: Vec<u8>) -> (Fr, Fr) {
(identity_secret_hash, id_commitment)
}
pub fn deserialize_identity_pair_be(serialized: Vec<u8>) -> (Fr, Fr) {
let (identity_secret_hash, read) = bytes_be_to_fr(&serialized);
let (id_commitment, _) = bytes_be_to_fr(&serialized[read..]);
(identity_secret_hash, id_commitment)
}
pub fn deserialize_identity_tuple(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
let mut all_read = 0;
@@ -95,6 +111,28 @@ pub fn deserialize_identity_tuple(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
)
}
pub fn deserialize_identity_tuple_be(serialized: Vec<u8>) -> (Fr, Fr, Fr, Fr) {
let mut all_read = 0;
let (identity_trapdoor, read) = bytes_be_to_fr(&serialized[all_read..]);
all_read += read;
let (identity_nullifier, read) = bytes_be_to_fr(&serialized[all_read..]);
all_read += read;
let (identity_secret_hash, read) = bytes_be_to_fr(&serialized[all_read..]);
all_read += read;
let (identity_commitment, _) = bytes_be_to_fr(&serialized[all_read..]);
(
identity_trapdoor,
identity_nullifier,
identity_secret_hash,
identity_commitment,
)
}
/// Serializes witness
///
/// # Errors

View File

@@ -7,8 +7,8 @@ use crate::protocol::{
serialize_proof_values, serialize_witness, verify_proof,
};
use crate::utils::{
bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size, fr_to_bytes_le,
vec_fr_to_bytes_le, vec_u8_to_bytes_le,
bytes_be_to_vec_fr, bytes_le_to_fr, bytes_le_to_vec_fr, bytes_le_to_vec_u8, fr_byte_size,
fr_to_bytes_be, fr_to_bytes_le, vec_fr_to_bytes_le, vec_u8_to_bytes_le,
};
#[cfg(not(target_arch = "wasm32"))]
use {
@@ -1139,6 +1139,23 @@ impl RLN {
Ok(())
}
/// Same as key_gen but serialized in BE format
pub fn key_gen_be<W: Write>(&self, mut output_data: W) -> Result<(), RLNError> {
let (identity_secret_hash, id_commitment) = keygen();
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok(())
}
pub fn key_gen_be_2<W: Write>(&self, mut output_data: W) -> Result<(Fr, Fr), RLNError> {
let (identity_secret_hash, id_commitment) = keygen();
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok((identity_secret_hash, id_commitment))
}
/// Returns an identity trapdoor, nullifier, secret and commitment tuple.
///
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
@@ -1172,6 +1189,29 @@ impl RLN {
Ok(())
}
/// Same as extend_key_gen but serialized in BE format.
pub fn extended_key_gen_be<W: Write>(&self, mut output_data: W) -> Result<(), RLNError> {
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
extended_keygen();
output_data.write_all(&fr_to_bytes_be(&identity_trapdoor))?;
output_data.write_all(&fr_to_bytes_be(&identity_nullifier))?;
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok(())
}
pub fn extended_key_gen_be_2<W: Write>(&self, mut output_data: W) -> Result<(Fr, Fr, Fr, Fr), RLNError> {
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
extended_keygen();
output_data.write_all(&fr_to_bytes_be(&identity_trapdoor))?;
output_data.write_all(&fr_to_bytes_be(&identity_nullifier))?;
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok((identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment))
}
/// Returns an identity secret and identity commitment pair generated using a seed.
///
/// The identity commitment is the Poseidon hash of the identity secret.
@@ -1211,6 +1251,22 @@ impl RLN {
Ok(())
}
/// Same as seeded_key_gen but in BE format
pub fn seeded_key_gen_be<R: Read, W: Write>(
&self,
mut input_data: R,
mut output_data: W,
) -> Result<(), RLNError> {
let mut serialized: Vec<u8> = Vec::new();
input_data.read_to_end(&mut serialized)?;
let (identity_secret_hash, id_commitment) = seeded_keygen(&serialized);
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok(())
}
/// Returns an identity trapdoor, nullifier, secret and commitment tuple generated using a seed.
///
/// The identity secret is the Poseidon hash of the identity trapdoor and identity nullifier.
@@ -1257,6 +1313,25 @@ impl RLN {
Ok(())
}
/// same as seeded_extended_key_gen but in BE format
pub fn seeded_extended_key_gen_be<R: Read, W: Write>(
&self,
mut input_data: R,
mut output_data: W,
) -> Result<(), RLNError> {
let mut serialized: Vec<u8> = Vec::new();
input_data.read_to_end(&mut serialized)?;
let (identity_trapdoor, identity_nullifier, identity_secret_hash, id_commitment) =
extended_seeded_keygen(&serialized);
output_data.write_all(&fr_to_bytes_be(&identity_trapdoor))?;
output_data.write_all(&fr_to_bytes_be(&identity_nullifier))?;
output_data.write_all(&fr_to_bytes_be(&identity_secret_hash))?;
output_data.write_all(&fr_to_bytes_be(&id_commitment))?;
Ok(())
}
/// Recovers the identity secret from two set of proof values computed for same secret in same epoch with same rln identifier.
///
/// Input values are:
@@ -1437,6 +1512,20 @@ pub fn hash<R: Read, W: Write>(
Ok(())
}
/// same as hash function but in BE format
pub fn hash_be<R: Read, W: Write>(
mut input_data: R,
mut output_data: W,
) -> Result<(), std::io::Error> {
let mut serialized: Vec<u8> = Vec::new();
input_data.read_to_end(&mut serialized)?;
let hash = hash_to_field(&serialized);
output_data.write_all(&fr_to_bytes_be(&hash))?;
Ok(())
}
/// Hashes a set of elements to a single element in the working prime field, using Poseidon.
///
/// The result is computed as the Poseidon Hash of the input signal.
@@ -1473,3 +1562,18 @@ pub fn poseidon_hash<R: Read, W: Write>(
Ok(())
}
/// same as poseidon_hash function but in BE format. Note that input is expected in BE format too.
pub fn poseidon_hash_be<R: Read, W: Write>(
mut input_data: R,
mut output_data: W,
) -> Result<(), RLNError> {
let mut serialized: Vec<u8> = Vec::new();
input_data.read_to_end(&mut serialized)?;
let (inputs, _) = bytes_be_to_vec_fr(&serialized)?;
let hash = utils_poseidon_hash(inputs.as_ref());
output_data.write_all(&fr_to_bytes_be(&hash))?;
Ok(())
}

View File

@@ -47,6 +47,15 @@ pub fn bytes_le_to_fr(input: &[u8]) -> (Fr, usize) {
)
}
#[inline(always)]
pub fn bytes_be_to_fr(input: &[u8]) -> (Fr, usize) {
let el_size = fr_byte_size();
(
Fr::from(BigUint::from_bytes_be(&input[0..el_size])),
el_size,
)
}
#[inline(always)]
pub fn fr_to_bytes_le(input: &Fr) -> Vec<u8> {
let input_biguint: BigUint = (*input).into();
@@ -56,6 +65,19 @@ pub fn fr_to_bytes_le(input: &Fr) -> Vec<u8> {
res
}
#[inline(always)]
pub fn fr_to_bytes_be(input: &Fr) -> Vec<u8> {
let input_biguint: BigUint = (*input).into();
let mut res = input_biguint.to_bytes_be();
// For BE, insert 0 at the start of the Vec (see also fr_to_bytes_le comments)
let to_insert_count = fr_byte_size().saturating_sub(res.len());
if to_insert_count > 0 {
// Insert multi 0 at index 0
res.splice(0..0, std::iter::repeat_n(0, to_insert_count));
}
res
}
#[inline(always)]
pub fn vec_fr_to_bytes_le(input: &[Fr]) -> Vec<u8> {
// Calculate capacity for Vec:
@@ -120,6 +142,24 @@ pub fn bytes_le_to_vec_fr(input: &[u8]) -> Result<(Vec<Fr>, usize), ConversionEr
Ok((res, read))
}
#[inline(always)]
pub fn bytes_be_to_vec_fr(input: &[u8]) -> Result<(Vec<Fr>, usize), ConversionError> {
let mut read: usize = 0;
let mut res: Vec<Fr> = Vec::new();
let len = usize::try_from(u64::from_be_bytes(input[0..8].try_into()?))?;
read += 8;
let el_size = fr_byte_size();
for i in 0..len {
let (curr_el, _) = bytes_be_to_fr(&input[8 + el_size * i..8 + el_size * (i + 1)]);
res.push(curr_el);
read += el_size;
}
Ok((res, read))
}
#[inline(always)]
pub fn bytes_le_to_vec_usize(input: &[u8]) -> Result<Vec<usize>, ConversionError> {
let nof_elem = usize::try_from(u64::from_le_bytes(input[0..8].try_into()?))?;
@@ -149,3 +189,17 @@ pub fn normalize_usize(input: usize) -> [u8; 8] {
pub fn generate_input_buffer() -> Cursor<String> {
Cursor::new(json!({}).to_string())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_fr_be() {
let fr_1 = Fr::from(255);
let b = fr_to_bytes_be(&fr_1);
let fr_1_de = bytes_be_to_fr(&b).0;
assert_eq!(fr_1, fr_1_de);
}
}

View File

@@ -13,7 +13,9 @@ mod test {
use std::fs::File;
use std::io::Read;
use std::mem::MaybeUninit;
use std::str::FromStr;
use std::time::{Duration, Instant};
use claims::assert_lt;
const NO_OF_LEAVES: usize = 256;
@@ -860,6 +862,72 @@ mod test {
assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
}
#[test]
// Tests hash to field using FFI APIs
fn test_extended_keygen_be_ffi() {
let q = ark_bn254::Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495616").unwrap();
let mut c = 0;
loop {
// We create a RLN instance
let rln_pointer = create_rln_instance();
// We generate a new identity tuple 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 =
extended_key_gen_be(rln_pointer, 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_hash, id_commitment) =
deserialize_identity_pair_be(result_data);
// We check against expected values
// let expected_identity_trapdoor_seed_bytes = str_to_fr(
// "0x766ce6c7e7a01bdf5b3f257616f603918c30946fa23480f2859c597817e6716",
// 16,
// );
// let expected_identity_nullifier_seed_bytes = str_to_fr(
// "0x1f18714c7bc83b5bca9e89d404cf6f2f585bc4c0f7ed8b53742b7e2b298f50b4",
// 16,
// );
// let expected_identity_secret_hash_seed_bytes = str_to_fr(
// "0x2aca62aaa7abaf3686fff2caf00f55ab9462dc12db5b5d4bcf3994e671f8e521",
// 16,
// );
// let expected_id_commitment_seed_bytes = str_to_fr(
// "0x68b66aa0a8320d2e56842581553285393188714c48f9b17acd198b4f1734c5c",
// 16,
// );
// assert_eq!(
// identity_trapdoor,
// expected_identity_trapdoor_seed_bytes.unwrap()
// );
// assert_eq!(
// identity_nullifier,
// expected_identity_nullifier_seed_bytes.unwrap()
// );
// assert_eq!(
// identity_secret_hash,
// expected_identity_secret_hash_seed_bytes.unwrap()
// );
// assert_eq!(id_commitment, expected_id_commitment_seed_bytes.unwrap());
assert_lt!(identity_secret_hash, q);
assert_lt!(id_commitment, q);
c+=1;
if c > 1000 {
break;
}
}
}
#[test]
// Tests hash to field using FFI APIs
fn test_hash_to_field_ffi() {

78
rln/tests/public_be.rs Normal file
View File

@@ -0,0 +1,78 @@
#[cfg(test)]
mod test {
use std::io::Cursor;
use std::str::FromStr;
use ark_bn254::Fr;
use claims::assert_lt;
use rln::public::RLN;
use rln::utils::{bytes_be_to_fr, bytes_le_to_fr};
#[test]
fn test_keygen_be() {
// let q = Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495617").unwrap();
let q = Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495616").unwrap();
// println!("q: {}", q);
let mut c = 0;
loop {
let rln = RLN::default();
let mut output_buffer = Cursor::new(Vec::<u8>::new());
let (secret, id_co) = rln.key_gen_be_2(&mut output_buffer).expect("TODO: panic message");
let serialized_output = output_buffer.into_inner();
let (secret_de, read) = bytes_be_to_fr(&serialized_output);
let (id_commitment, _) = bytes_be_to_fr(&serialized_output[read..].to_vec());
assert_eq!(secret, secret_de);
assert_lt!(secret, q);
assert_eq!(id_commitment, id_co);
assert_lt!(id_commitment, q);
c+=1;
if c > 10_000 {
break;
}
}
println!("c: {}", c);
}
#[test]
fn test_extended_keygen_be() {
// let q = Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495617").unwrap();
let q = Fr::from_str("21888242871839275222246405745257275088548364400416034343698204186575808495616").unwrap();
// println!("q: {}", q);
let mut c = 0;
loop {
let rln = RLN::default();
let mut output_buffer = Cursor::new(Vec::<u8>::new());
let (trap, nulli, secret, id_co) = rln.extended_key_gen_be_2(&mut output_buffer).expect("TODO: panic message");
let serialized_output = output_buffer.into_inner();
let (trap_de, read) = bytes_be_to_fr(&serialized_output);
let (nulli_de, read) = bytes_be_to_fr(&serialized_output[read..].to_vec());
let (secret_de, read) = bytes_be_to_fr(&serialized_output[read+read..].to_vec());
let (id_co_de, _read) = bytes_be_to_fr(&serialized_output[read+read+read..].to_vec());
assert_eq!(trap, trap_de);
assert_eq!(nulli, nulli_de);
assert_eq!(secret, secret_de);
assert_lt!(secret, q);
assert_eq!(id_co, id_co_de);
assert_lt!(id_co, q);
c+=1;
if c > 5_000 {
break;
}
}
println!("c: {}", c);
}
}