grapevine-sonobe/src/utils/inputs.rs
src/utils/inputs.rsRaw
1use ark_bn254::Fr;
2use ark_ff::{BigInteger, PrimeField};
3use ark_std::rand::rngs::OsRng;
4use num_bigint::{BigInt, RandBigInt, Sign::Plus};
5use std::error::Error;
6
7use super::{MAX_SECRET_LENGTH, MAX_USERNAME_LENGTH, SECRET_FIELD_LENGTH};
8
9#[derive(Clone, Debug)]
10pub struct CircomPrivateInput {
11 pub phrase: Option<String>,
12 pub usernames: [Option<String>; 2],
13 pub auth_secrets: [Option<BigInt>; 2],
14 pub chaff: bool,
15}
16
17impl CircomPrivateInput {
18 /**
19 * Creates empty inputs
20 *
21 * @param chaff - if true, should compute random vars for chaff in circuit
22 */
23 pub fn empty(chaff: bool) -> Self {
24 Self {
25 phrase: None,
26 usernames: [None, None],
27 auth_secrets: [None, None],
28 chaff,
29 }
30 }
31
32 pub fn uninitialized(&self) -> bool {
33 let not_chaff = self.phrase.is_none()
34 && self.usernames.iter().all(|u| u.is_none())
35 && self.auth_secrets.iter().all(|a| a.is_none());
36 not_chaff && !self.chaff
37 }
38}
39
40/** Get the starting ivc inputs (z0) for the grapevine circuit */
41pub fn get_z0<F: PrimeField>() -> [F; 4] {
42 (0..4)
43 .map(|_| F::zero())
44 .collect::<Vec<F>>()
45 .try_into()
46 .unwrap()
47}
48
49/** Generates a random field element for given field as bigint */
50pub fn random_f_bigint<F: PrimeField>() -> BigInt {
51 let lower_bound = BigInt::from(0);
52 let upper_bound = BigInt::from_bytes_be(Plus, &F::MODULUS.to_bytes_be());
53 OsRng.gen_bigint_range(&lower_bound, &upper_bound)
54}
55
56/**
57 * Converts a given word to array of 6 field elements
58 * @dev split into 31-byte strings to fit in finite field and pad with 0's where necessary
59 *
60 * @param phrase - the string entered by user to compute hash for (will be length checked)
61 * @return - array of 6 Fr elements
62 */
63pub fn serialize_phrase(phrase: &String) -> Result<[BigInt; SECRET_FIELD_LENGTH], Box<dyn Error>> {
64 // check length
65 if phrase.len() > MAX_SECRET_LENGTH {
66 return Err("Phrase must be <= 180 characters".into());
67 }
68 // convert each 31-byte chunk to field element
69 let mut chunks: [BigInt; SECRET_FIELD_LENGTH] = Default::default();
70 for i in 0..SECRET_FIELD_LENGTH {
71 // get the range
72 let start = i * 31;
73 let end = (i + 1) * 31;
74 let mut chunk: [u8; 32] = [0; 32];
75 // select slice from range and pad if needed
76 if start >= phrase.len() {
77 } else if end > phrase.len() {
78 chunk[1..(phrase.len() - start + 1)].copy_from_slice(&phrase.as_bytes()[start..]);
79 } else {
80 chunk[1..32].copy_from_slice(&phrase.as_bytes()[start..end]);
81 }
82 // wrap in field element
83 chunks[i] = BigInt::from_bytes_be(Plus, &chunk);
84 }
85 Ok(chunks)
86}
87
88/**
89* Converts a given username to a field element
90*
91* @param username - the username to convert to utf8 and into field element
92* @return - the username serialied into the field element
93*/
94pub fn serialize_username(username: &String) -> Result<BigInt, Box<dyn Error>> {
95 // check length
96 if username.len() > MAX_USERNAME_LENGTH {
97 return Err("Username must be <= 30 characters".into());
98 }
99 // convert to big endian bytes
100 let mut bytes: [u8; 32] = [0; 32];
101 bytes[1..(username.len() + 1)].copy_from_slice(&username.as_bytes()[..]);
102 // convert to bigint
103 Ok(BigInt::from_bytes_be(Plus, &bytes))
104}
105
106pub fn prepare_external_inputs<F: PrimeField>(inputs: &CircomPrivateInput) -> Vec<F> {
107 // handle phrase presence (if not present infer chaff)
108 let phrase = match &inputs.phrase {
109 Some(phrase) => serialize_phrase(&phrase).unwrap().to_vec(),
110 None => (0..6)
111 .map(|_| random_f_bigint::<F>())
112 .collect::<Vec<BigInt>>(),
113 };
114
115 // determine inputs: first step ([0] = None), Nth step ([1] = Some), and chaff ([2] = None)
116 // marshal usernames
117 let usernames = match inputs.usernames[0] {
118 Some(_) => inputs
119 .usernames
120 .iter()
121 .map(|u| serialize_username(&u.clone().unwrap()).unwrap())
122 .collect::<Vec<BigInt>>(),
123 None => match &inputs.usernames[1] {
124 Some(username) => vec![BigInt::from(0), serialize_username(&username).unwrap()],
125 None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
126 },
127 };
128
129 // marshal auth secrets
130 let auth_sec = match inputs.auth_secrets[0] {
131 Some(_) => inputs
132 .auth_secrets
133 .iter()
134 .map(|a| a.clone().unwrap())
135 .collect::<Vec<BigInt>>(),
136 None => match &inputs.auth_secrets[1] {
137 Some(auth_secret) => vec![BigInt::from(0), auth_secret.clone()],
138 None => vec![random_f_bigint::<F>(), random_f_bigint::<F>()],
139 },
140 };
141
142 // NOTE: probably wold be better that the inputs are prepared already as F instead of
143 // BigInt (at the methods serialize_phrase, serialize_username).
144
145 let inp: Vec<BigInt> = [phrase, usernames, auth_sec].concat();
146 inp.iter()
147 .map(|v| {
148 let (_, b) = v.to_bytes_le();
149 F::from_le_bytes_mod_order(&b)
150 })
151 .collect()
152}