arnaucube
arnaucube/grapevine-sonobePublic Self-hosted repository
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}