sui_crypto/zklogin/
verify.rs

1use std::str::FromStr;
2
3use crate::SignatureError;
4use ark_bn254::Fq;
5use ark_bn254::Fq2;
6use ark_bn254::Fr;
7use ark_bn254::G1Affine;
8use ark_bn254::G1Projective;
9use ark_bn254::G2Affine;
10use ark_bn254::G2Projective;
11use ark_ff::PrimeField;
12use ark_groth16::PreparedVerifyingKey;
13use ark_groth16::Proof;
14use sui_sdk_types::Bn254FieldElement;
15use sui_sdk_types::CircomG1;
16use sui_sdk_types::CircomG2;
17use sui_sdk_types::Ed25519PublicKey;
18use sui_sdk_types::Jwk;
19use sui_sdk_types::Secp256k1PublicKey;
20use sui_sdk_types::Secp256r1PublicKey;
21use sui_sdk_types::SimpleSignature;
22use sui_sdk_types::ZkLoginInputs;
23use sui_sdk_types::ZkLoginProof;
24
25use super::POSEIDON;
26
27#[derive(Clone, Debug)]
28pub struct VerifyingKey {
29    inner: PreparedVerifyingKey<ark_bn254::Bn254>,
30}
31
32const fn str_to_bn254(s: &str) -> Bn254FieldElement {
33    match Bn254FieldElement::from_str_radix_10(s) {
34        Ok(e) => e,
35        Err(_) => panic!("unable to convert bn254"),
36    }
37}
38
39const fn build_circom_g1([e0, e1, e2]: [&str; 3]) -> CircomG1 {
40    CircomG1([str_to_bn254(e0), str_to_bn254(e1), str_to_bn254(e2)])
41}
42
43const fn build_circom_g2([[e00, e01], [e10, e11], [e20, e21]]: [[&str; 2]; 3]) -> CircomG2 {
44    CircomG2([
45        [str_to_bn254(e00), str_to_bn254(e01)],
46        [str_to_bn254(e10), str_to_bn254(e11)],
47        [str_to_bn254(e20), str_to_bn254(e21)],
48    ])
49}
50
51fn circom_to_arkworks_g1(g1: &CircomG1) -> Result<G1Affine, SignatureError> {
52    let CircomG1([f0, f1, f2]) = g1;
53
54    let g1: G1Affine =
55        G1Projective::new_unchecked(bn254_to_fq(f0), bn254_to_fq(f1), bn254_to_fq(f2)).into();
56
57    if !g1.is_on_curve() || !g1.is_in_correct_subgroup_assuming_on_curve() {
58        return Err(SignatureError::from_source("invalid G1 input"));
59    }
60
61    Ok(g1)
62}
63
64fn circom_to_arkworks_g2(g2: &CircomG2) -> Result<G2Affine, SignatureError> {
65    let CircomG2([[f00, f01], [f10, f11], [f20, f21]]) = g2;
66
67    let g2: G2Affine = G2Projective::new_unchecked(
68        Fq2::new(bn254_to_fq(f00), bn254_to_fq(f01)),
69        Fq2::new(bn254_to_fq(f10), bn254_to_fq(f11)),
70        Fq2::new(bn254_to_fq(f20), bn254_to_fq(f21)),
71    )
72    .into();
73
74    if !g2.is_on_curve() || !g2.is_in_correct_subgroup_assuming_on_curve() {
75        return Err(SignatureError::from_source("invalid G2 input"));
76    }
77
78    Ok(g2)
79}
80
81fn bn254_to_fq(f: &Bn254FieldElement) -> Fq {
82    Fq::from_be_bytes_mod_order(f.padded())
83}
84
85fn bn254_to_fr(f: &Bn254FieldElement) -> Fr {
86    Fr::from_be_bytes_mod_order(f.padded())
87}
88
89fn mainnet_verifying_key() -> VerifyingKey {
90    const CIRCOM_ALPHA_G1: CircomG1 = build_circom_g1([
91        "21529901943976716921335152104180790524318946701278905588288070441048877064089",
92        "7775817982019986089115946956794180159548389285968353014325286374017358010641",
93        "1",
94    ]);
95
96    const CIRCOM_BETA_G2: CircomG2 = build_circom_g2([
97        [
98            "6600437987682835329040464538375790690815756241121776438004683031791078085074",
99            "16207344858883952201936462217289725998755030546200154201671892670464461194903",
100        ],
101        [
102            "17943105074568074607580970189766801116106680981075272363121544016828311544390",
103            "18339640667362802607939727433487930605412455701857832124655129852540230493587",
104        ],
105        ["1", "0"],
106    ]);
107
108    const CIRCOM_GAMMA_G2: CircomG2 = build_circom_g2([
109        [
110            "10857046999023057135944570762232829481370756359578518086990519993285655852781",
111            "11559732032986387107991004021392285783925812861821192530917403151452391805634",
112        ],
113        [
114            "8495653923123431417604973247489272438418190587263600148770280649306958101930",
115            "4082367875863433681332203403145435568316851327593401208105741076214120093531",
116        ],
117        ["1", "0"],
118    ]);
119
120    const CIRCOM_DELTA_G2: CircomG2 = build_circom_g2([
121        [
122            "19260309516619721648285279557078789954438346514188902804737557357941293711874",
123            "2480422554560175324649200374556411861037961022026590718777465211464278308900",
124        ],
125        [
126            "14489104692423540990601374549557603533921811847080812036788172274404299703364",
127            "12564378633583954025611992187142343628816140907276948128970903673042690269191",
128        ],
129        ["1", "0"],
130    ]);
131
132    const CIRCOM_GAMMA_ABC_G1: [CircomG1; 2] = [
133        build_circom_g1([
134            "1607694606386445293170795095076356565829000940041894770459712091642365695804",
135            "18066827569413962196795937356879694709963206118612267170825707780758040578649",
136            "1",
137        ]),
138        build_circom_g1([
139            "20653794344898475822834426774542692225449366952113790098812854265588083247207",
140            "3296759704176575765409730962060698204792513807296274014163938591826372646699",
141            "1",
142        ]),
143    ];
144
145    let vk = ark_groth16::VerifyingKey {
146        alpha_g1: circom_to_arkworks_g1(&CIRCOM_ALPHA_G1).unwrap(),
147        beta_g2: circom_to_arkworks_g2(&CIRCOM_BETA_G2).unwrap(),
148        gamma_g2: circom_to_arkworks_g2(&CIRCOM_GAMMA_G2).unwrap(),
149        delta_g2: circom_to_arkworks_g2(&CIRCOM_DELTA_G2).unwrap(),
150        gamma_abc_g1: CIRCOM_GAMMA_ABC_G1
151            .iter()
152            .map(circom_to_arkworks_g1)
153            .collect::<Result<_, _>>()
154            .unwrap(),
155    };
156
157    VerifyingKey {
158        inner: PreparedVerifyingKey::from(vk),
159    }
160}
161
162/// Load a fixed verifying key from zkLogin.vkey output. This is based on a local setup and should not use in production.
163fn dev_verifying_key() -> VerifyingKey {
164    const CIRCOM_ALPHA_G1: CircomG1 = build_circom_g1([
165        "20491192805390485299153009773594534940189261866228447918068658471970481763042",
166        "9383485363053290200918347156157836566562967994039712273449902621266178545958",
167        "1",
168    ]);
169
170    const CIRCOM_BETA_G2: CircomG2 = build_circom_g2([
171        [
172            "6375614351688725206403948262868962793625744043794305715222011528459656738731",
173            "4252822878758300859123897981450591353533073413197771768651442665752259397132",
174        ],
175        [
176            "10505242626370262277552901082094356697409835680220590971873171140371331206856",
177            "21847035105528745403288232691147584728191162732299865338377159692350059136679",
178        ],
179        ["1", "0"],
180    ]);
181
182    const CIRCOM_GAMMA_G2: CircomG2 = build_circom_g2([
183        [
184            "10857046999023057135944570762232829481370756359578518086990519993285655852781",
185            "11559732032986387107991004021392285783925812861821192530917403151452391805634",
186        ],
187        [
188            "8495653923123431417604973247489272438418190587263600148770280649306958101930",
189            "4082367875863433681332203403145435568316851327593401208105741076214120093531",
190        ],
191        ["1", "0"],
192    ]);
193
194    const CIRCOM_DELTA_G2: CircomG2 = build_circom_g2([
195        [
196            "10857046999023057135944570762232829481370756359578518086990519993285655852781",
197            "11559732032986387107991004021392285783925812861821192530917403151452391805634",
198        ],
199        [
200            "8495653923123431417604973247489272438418190587263600148770280649306958101930",
201            "4082367875863433681332203403145435568316851327593401208105741076214120093531",
202        ],
203        ["1", "0"],
204    ]);
205
206    const CIRCOM_GAMMA_ABC_G1: [CircomG1; 2] = [
207        build_circom_g1([
208            "20701306374481714853949730154526815782802808896228594855451770849676897643964",
209            "2766989084754673216772682210231588284954002353414778477810174100808747060165",
210            "1",
211        ]),
212        build_circom_g1([
213            "501195541410525737371980194958674422793469475773065719916327137354779402600",
214            "13527631693157515024233848630878973193664410306029731429350155106228769355415",
215            "1",
216        ]),
217    ];
218
219    let vk = ark_groth16::VerifyingKey {
220        alpha_g1: circom_to_arkworks_g1(&CIRCOM_ALPHA_G1).unwrap(),
221        beta_g2: circom_to_arkworks_g2(&CIRCOM_BETA_G2).unwrap(),
222        gamma_g2: circom_to_arkworks_g2(&CIRCOM_GAMMA_G2).unwrap(),
223        delta_g2: circom_to_arkworks_g2(&CIRCOM_DELTA_G2).unwrap(),
224        gamma_abc_g1: CIRCOM_GAMMA_ABC_G1
225            .iter()
226            .map(circom_to_arkworks_g1)
227            .collect::<Result<_, _>>()
228            .unwrap(),
229    };
230
231    VerifyingKey::new(PreparedVerifyingKey::from(vk))
232}
233
234impl VerifyingKey {
235    fn new(inner: PreparedVerifyingKey<ark_bn254::Bn254>) -> Self {
236        Self { inner }
237    }
238
239    pub fn new_mainnet() -> Self {
240        mainnet_verifying_key()
241    }
242
243    pub fn new_dev() -> Self {
244        dev_verifying_key()
245    }
246
247    pub fn verify_zklogin(
248        &self,
249        jwk: &Jwk,
250        inputs: &ZkLoginInputs,
251        signature: &SimpleSignature,
252        max_epoch: u64,
253    ) -> Result<(), SignatureError> {
254        use base64ct::Base64UrlUnpadded;
255        use base64ct::Encoding;
256        // Decode modulus to bytes.
257        let modulus = Base64UrlUnpadded::decode_vec(&jwk.n)
258            .map_err(|e| SignatureError::from_source(e.to_string()))?;
259
260        let proof = zklogin_proof_to_arkworks(&inputs.proof_points).unwrap();
261        let input_hash = calculate_all_inputs_hash(inputs, signature, &modulus, max_epoch).unwrap();
262
263        self.verify_proof(&proof, &[input_hash])
264    }
265
266    fn verify_proof(
267        &self,
268        proof: &Proof<ark_bn254::Bn254>,
269        public_inputs: &[ark_bn254::Fr],
270    ) -> Result<(), SignatureError> {
271        use ark_snark::SNARK;
272
273        if ark_groth16::Groth16::<ark_bn254::Bn254>::verify_with_processed_vk(
274            &self.inner,
275            public_inputs,
276            proof,
277        )
278        .map_err(|e| SignatureError::from_source(e.to_string()))?
279        {
280            Ok(())
281        } else {
282            Err(SignatureError::from_source("Groth16 proof verify failed"))
283        }
284    }
285}
286
287fn zklogin_proof_to_arkworks(
288    proof: &ZkLoginProof,
289) -> Result<Proof<ark_bn254::Bn254>, SignatureError> {
290    Ok(Proof {
291        a: circom_to_arkworks_g1(&proof.a)?,
292        b: circom_to_arkworks_g2(&proof.b)?,
293        c: circom_to_arkworks_g1(&proof.c)?,
294    })
295}
296
297/// Given a SimpleSignature convert the corrisponding public key, prefixed with the signature
298/// scheme flag, to two Bn254Frs
299pub fn public_key_to_frs(signature: &SimpleSignature) -> (Fr, Fr) {
300    // buf length of the longest public key secp256r1/secp256k1 of 33 bytes plus 1 byte for the
301    // scheme
302    let mut buf = [0u8; 34];
303
304    buf[0] = signature.scheme().to_u8();
305
306    let buf = match signature {
307        SimpleSignature::Ed25519 { public_key, .. } => {
308            buf[1..Ed25519PublicKey::LENGTH + 1].copy_from_slice(public_key.inner());
309            &buf[..Ed25519PublicKey::LENGTH + 1]
310        }
311        SimpleSignature::Secp256k1 { public_key, .. } => {
312            buf[1..Secp256k1PublicKey::LENGTH + 1].copy_from_slice(public_key.inner());
313            &buf[..Secp256k1PublicKey::LENGTH + 1]
314        }
315        SimpleSignature::Secp256r1 { public_key, .. } => {
316            buf[1..Secp256r1PublicKey::LENGTH + 1].copy_from_slice(public_key.inner());
317            &buf[..Secp256r1PublicKey::LENGTH + 1]
318        }
319    };
320
321    //TODO this comment is wrong...
322    // Split the bytes deterministically such that the first element contains the first 128
323    // bits of the hash, and the second element contains the latter ones.
324    let (first_half, second_half) = buf.split_at(buf.len() - 16);
325
326    let eph_public_key_0 = Fr::from_be_bytes_mod_order(first_half);
327    let eph_public_key_1 = Fr::from_be_bytes_mod_order(second_half);
328    (eph_public_key_0, eph_public_key_1)
329}
330
331pub(crate) type U256 = bnum::BUintD8<32>;
332pub(crate) type U2048 = bnum::BUintD8<256>;
333
334const MAX_HEADER_LEN: u8 = 248;
335const PACK_WIDTH: u8 = 248;
336const MAX_EXT_ISS_LEN: u8 = 165;
337const MAX_ISS_LEN_B64: u8 = 4 * (1 + MAX_EXT_ISS_LEN / 3);
338
339/// Pads a stream of bytes and maps it to a field element
340pub fn hash_ascii_str_to_field(s: &str, max_size: u8) -> Result<Fr, SignatureError> {
341    let str_padded = str_to_padded_char_codes(s, max_size)?;
342    hash_to_field(&str_padded, 8, PACK_WIDTH)
343}
344
345fn str_to_padded_char_codes(s: &str, max_len: u8) -> Result<Vec<U256>, SignatureError> {
346    let arr: Vec<U256> = s.bytes().map(U256::from).collect();
347    pad_with_zeroes(arr, max_len)
348}
349
350fn pad_with_zeroes(in_arr: Vec<U256>, out_count: u8) -> Result<Vec<U256>, SignatureError> {
351    if in_arr.len() > out_count as usize {
352        return Err(SignatureError::from_source("in_arr too long"));
353    }
354    let mut padded = in_arr;
355    padded.resize(out_count as usize, U256::ZERO);
356    Ok(padded)
357}
358
359/// Maps a stream of bigints to a single field element. First we convert the base from
360/// inWidth to packWidth. Then we compute the poseidon hash of the "packed" input.
361/// input is the input vector containing equal-width big ints. inWidth is the width of
362/// each input element.
363fn hash_to_field<T: ToBits>(
364    input: &[T],
365    in_width: u16,
366    pack_width: u8,
367) -> Result<Fr, SignatureError> {
368    let packed = convert_base(input, in_width, pack_width)?;
369
370    POSEIDON.hash(&packed).map_err(SignatureError::from_source)
371}
372
373/// Helper function to pack field elements from big ints.
374fn convert_base<T: ToBits>(
375    in_arr: &[T],
376    in_width: u16,
377    out_width: u8,
378) -> Result<Vec<Fr>, SignatureError> {
379    if out_width == 0 {
380        return Err(SignatureError::from_source("invalid input"));
381    }
382    let bits = big_int_array_to_bits(in_arr, in_width as usize)?;
383    let mut packed: Vec<Fr> = bits
384        .rchunks(out_width as usize)
385        .map(|chunk| {
386            Fr::from_be_bytes_mod_order(U256::from_radix_be(chunk, 2).unwrap().to_be().digits())
387        })
388        .collect();
389    packed.reverse();
390    match packed.len() != (in_arr.len() * in_width as usize).div_ceil(out_width as usize) {
391        true => Err(SignatureError::from_source("invalid input")),
392        false => Ok(packed),
393    }
394}
395
396/// Convert a big int array to a bit array with 0 paddings.
397fn big_int_array_to_bits<T: ToBits>(
398    integers: &[T],
399    intended_size: usize,
400) -> Result<Vec<u8>, SignatureError> {
401    use itertools::Itertools;
402    use std::cmp::Ordering::Equal;
403    use std::cmp::Ordering::Greater;
404    use std::cmp::Ordering::Less;
405
406    integers
407        .iter()
408        .map(|integer| {
409            let bits = integer.to_bits();
410            match bits.len().cmp(&intended_size) {
411                Less => {
412                    let extra_bits = intended_size - bits.len();
413                    let mut padded = vec![0; extra_bits];
414                    padded.extend(bits);
415                    Ok(padded)
416                }
417                Equal => Ok(bits),
418                Greater => Err(SignatureError::from_source("invalid input")),
419            }
420        })
421        .flatten_ok()
422        .collect()
423}
424
425trait ToBits {
426    fn to_bits(&self) -> Vec<u8>;
427}
428
429impl ToBits for U256 {
430    fn to_bits(&self) -> Vec<u8> {
431        self.to_radix_be(2)
432    }
433}
434
435impl ToBits for U2048 {
436    fn to_bits(&self) -> Vec<u8> {
437        self.to_radix_be(2)
438    }
439}
440
441/// Calculate the poseidon hash from selected fields from inputs, along with the ephemeral pubkey.
442pub fn calculate_all_inputs_hash(
443    inputs: &ZkLoginInputs,
444    signature: &SimpleSignature,
445    modulus: &[u8],
446    max_epoch: u64,
447) -> Result<Fr, SignatureError> {
448    if inputs.header_base64.len() > MAX_HEADER_LEN as usize {
449        return Err(SignatureError::from_source("header too long"));
450    }
451
452    let (first, second) = public_key_to_frs(signature);
453
454    let address_seed = bn254_to_fr(&inputs.address_seed);
455    let max_epoch_f = Fr::from_be_bytes_mod_order(U256::from(max_epoch).to_be().digits());
456    let index_mod_4_f = Fr::from_be_bytes_mod_order(
457        U256::from(inputs.iss_base64_details.index_mod_4)
458            .to_be()
459            .digits(),
460    );
461
462    let iss_base64_f = hash_ascii_str_to_field(&inputs.iss_base64_details.value, MAX_ISS_LEN_B64)?;
463    let header_f = hash_ascii_str_to_field(&inputs.header_base64, MAX_HEADER_LEN)?;
464    let modulus_f = hash_to_field(&[U2048::from_be_slice(modulus).unwrap()], 2048, PACK_WIDTH)?;
465
466    POSEIDON
467        .hash(&[
468            first,
469            second,
470            address_seed,
471            max_epoch_f,
472            iss_base64_f,
473            index_mod_4_f,
474            header_f,
475            modulus_f,
476        ])
477        .map_err(SignatureError::from_source)
478}
479
480/// Calculate the Sui address based on address seed and address params.
481#[allow(unused)]
482fn gen_address_seed(
483    salt: &str,
484    name: &str,  // i.e. "sub"
485    value: &str, // i.e. the sub value
486    aud: &str,   // i.e. the client ID
487) -> Result<String, SignatureError> {
488    let salt_hash = POSEIDON
489        .hash(&[bn254_to_fr(
490            &Bn254FieldElement::from_str(salt).map_err(SignatureError::from_source)?,
491        )])
492        .map_err(SignatureError::from_source)?;
493    gen_address_seed_with_salt_hash(salt_hash, name, value, aud)
494}
495
496const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32;
497const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115;
498const MAX_AUD_VALUE_LENGTH: u8 = 145;
499
500/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as input instead of the salt.
501pub(crate) fn gen_address_seed_with_salt_hash(
502    salt_hash: Fr,
503    name: &str,  // i.e. "sub"
504    value: &str, // i.e. the sub value
505    aud: &str,   // i.e. the client ID
506) -> Result<String, SignatureError> {
507    Ok(POSEIDON
508        .hash(&[
509            hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?,
510            hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?,
511            hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?,
512            salt_hash,
513        ])
514        .map_err(SignatureError::from_source)?
515        .to_string())
516}
517
518#[cfg(test)]
519mod test {
520    use super::*;
521    use sui_sdk_types::Ed25519Signature;
522
523    #[cfg(test)]
524    #[cfg(target_arch = "wasm32")]
525    use wasm_bindgen_test::wasm_bindgen_test as test;
526
527    #[test]
528    fn test_verify_zklogin_google() {
529        let user_salt = "206703048842351542647799591018316385612";
530
531        let pubkey = Ed25519PublicKey::new([
532            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
533            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
534        ]);
535        let signature = SimpleSignature::Ed25519 {
536            signature: Ed25519Signature::new([0; 64]),
537            public_key: pubkey,
538        };
539
540        // Get the address seed.
541        let address_seed = gen_address_seed(
542            user_salt,
543            "sub",
544            "106294049240999307923",
545            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
546        )
547        .unwrap();
548
549        let inputs = serde_json::json!({
550            "proof_points": {
551                "a": [
552                    "8247215875293406890829839156897863742504615191361518281091302475904551111016",
553                    "6872980335748205979379321982220498484242209225765686471076081944034292159666",
554                    "1"
555                ],
556                "b": [
557                    [
558                        "21419680064642047510915171723230639588631899775315750803416713283740137406807",
559                        "21566716915562037737681888858382287035712341650647439119820808127161946325890"
560                    ],
561                    [
562                        "17867714710686394159919998503724240212517838710399045289784307078087926404555",
563                        "21812769875502013113255155836896615164559280911997219958031852239645061854221"
564                    ],
565                    ["1","0"]
566                ],
567                "c": [
568                    "7530826803702928198368421787278524256623871560746240215547076095911132653214",
569                    "16244547936249959771862454850485726883972969173921727256151991751860694123976",
570                    "1"
571                ]
572            },
573            "iss_base64_details": {
574                "value": "yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC",
575                "index_mod_4": 1
576            },
577            "header_base64": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
578            "address_seed": address_seed
579        });
580
581        let zklogin_inputs: ZkLoginInputs = serde_json::from_value(inputs).unwrap();
582
583        let jwk = Jwk {
584            kty: "RSA".to_string(),
585            e: "AQAB".to_string(),
586            n: "oUriU8GqbRw-avcMn95DGW1cpZR1IoM6L7krfrWvLSSCcSX6Ig117o25Yk7QWBiJpaPV0FbP7Y5-DmThZ3SaF0AXW-3BsKPEXfFfeKVc6vBqk3t5mKlNEowjdvNTSzoOXO5UIHwsXaxiJlbMRalaFEUm-2CKgmXl1ss_yGh1OHkfnBiGsfQUndKoHiZuDzBMGw8Sf67am_Ok-4FShK0NuR3-q33aB_3Z7obC71dejSLWFOEcKUVCaw6DGVuLog3x506h1QQ1r0FXKOQxnmqrRgpoHqGSouuG35oZve1vgCU4vLZ6EAgBAbC0KL35I7_0wUDSMpiAvf7iZxzJVbspkQ".to_string(),
587            alg: "RS256".to_string(),
588        };
589
590        VerifyingKey::new_mainnet()
591            .verify_zklogin(&jwk, &zklogin_inputs, &signature, 10)
592            .unwrap();
593    }
594
595    #[test]
596    fn test_public_key_to_frs() {
597        let pubkey = Ed25519PublicKey::new([
598            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
599            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
600        ]);
601        let signature = SimpleSignature::Ed25519 {
602            signature: Ed25519Signature::new([0; 64]),
603            public_key: pubkey,
604        };
605        let (actual_0, actual_1) = public_key_to_frs(&signature);
606        let expect_0 = Fr::from(ark_ff::BigInt([
607            1244302228903607218,
608            13386648721483054705,
609            0,
610            0,
611        ]));
612
613        let expect_1 = Fr::from(ark_ff::BigInt([
614            18225592963892023808,
615            2904666130704426303,
616            0,
617            0,
618        ]));
619        assert_eq!(actual_0, expect_0);
620        assert_eq!(actual_1, expect_1);
621    }
622
623    #[test]
624    fn test_hash_ascii_str_to_field() {
625        let actual = hash_ascii_str_to_field("sub", 32).unwrap();
626        let expect = Fr::from(ark_ff::BigInt([
627            9420274050661827129,
628            9736100402995345242,
629            10431892319505233812,
630            1450152190758097105,
631        ]));
632        assert_eq!(actual, expect);
633
634        let actual = hash_ascii_str_to_field("106294049240999307923", 115).unwrap();
635        let expect = Fr::from(ark_ff::BigInt([
636            1616959301818912987,
637            17318965991705091209,
638            15303466056770245354,
639            1596136658728187659,
640        ]));
641        assert_eq!(actual, expect);
642
643        let actual = hash_ascii_str_to_field(
644            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
645            145,
646        )
647        .unwrap();
648        let expect = Fr::from(ark_ff::BigInt([
649            5030944271044826582,
650            8577618269522081956,
651            6962871209781429610,
652            2149811477348923117,
653        ]));
654        assert_eq!(actual, expect);
655
656        let actual =
657            hash_ascii_str_to_field("yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC", 224)
658                .unwrap();
659        let expect = Fr::from(ark_ff::BigInt([
660            6021918591354572765,
661            14069258108381575504,
662            1736509627917450843,
663            2767185135515367512,
664        ]));
665        assert_eq!(actual, expect);
666
667        let actual = hash_ascii_str_to_field(
668        "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
669        248,
670    ).unwrap();
671        let expect = Fr::from(ark_ff::BigInt([
672            4239129243150064016,
673            15469804315138207306,
674            17534492051703966556,
675            2100329545252322607,
676        ]));
677        assert_eq!(actual, expect);
678    }
679}