Skip to main content

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, PartialEq, Default)]
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())?;
261        let input_hash = calculate_all_inputs_hash(inputs, signature, &modulus, max_epoch)?;
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
299fn public_key_to_frs(signature: &SimpleSignature) -> Result<(Fr, Fr), SignatureError> {
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        _ => return Err(SignatureError::from_source("unknown signature scheme")),
320    };
321
322    //TODO this comment is wrong...
323    // Split the bytes deterministically such that the first element contains the first 128
324    // bits of the hash, and the second element contains the latter ones.
325    let (first_half, second_half) = buf.split_at(buf.len() - 16);
326
327    let eph_public_key_0 = Fr::from_be_bytes_mod_order(first_half);
328    let eph_public_key_1 = Fr::from_be_bytes_mod_order(second_half);
329    Ok((eph_public_key_0, eph_public_key_1))
330}
331
332pub(crate) type U256 = bnum::BUintD8<32>;
333pub(crate) type U2048 = bnum::BUintD8<256>;
334
335const MAX_HEADER_LEN: u8 = 248;
336const PACK_WIDTH: u8 = 248;
337const MAX_EXT_ISS_LEN: u8 = 165;
338const MAX_ISS_LEN_B64: u8 = 4 * (1 + MAX_EXT_ISS_LEN / 3);
339
340/// Pads a stream of bytes and maps it to a field element
341pub fn hash_ascii_str_to_field(s: &str, max_size: u8) -> Result<Fr, SignatureError> {
342    let str_padded = str_to_padded_char_codes(s, max_size)?;
343    hash_to_field(&str_padded, 8, PACK_WIDTH)
344}
345
346fn str_to_padded_char_codes(s: &str, max_len: u8) -> Result<Vec<U256>, SignatureError> {
347    let arr: Vec<U256> = s.bytes().map(U256::from).collect();
348    pad_with_zeroes(arr, max_len)
349}
350
351fn pad_with_zeroes(in_arr: Vec<U256>, out_count: u8) -> Result<Vec<U256>, SignatureError> {
352    if in_arr.len() > out_count as usize {
353        return Err(SignatureError::from_source("in_arr too long"));
354    }
355    let mut padded = in_arr;
356    padded.resize(out_count as usize, U256::ZERO);
357    Ok(padded)
358}
359
360/// Maps a stream of bigints to a single field element. First we convert the base from
361/// inWidth to packWidth. Then we compute the poseidon hash of the "packed" input.
362/// input is the input vector containing equal-width big ints. inWidth is the width of
363/// each input element.
364fn hash_to_field<T: ToBits>(
365    input: &[T],
366    in_width: u16,
367    pack_width: u8,
368) -> Result<Fr, SignatureError> {
369    let packed = convert_base(input, in_width, pack_width)?;
370
371    POSEIDON.hash(&packed).map_err(SignatureError::from_source)
372}
373
374/// Helper function to pack field elements from big ints.
375fn convert_base<T: ToBits>(
376    in_arr: &[T],
377    in_width: u16,
378    out_width: u8,
379) -> Result<Vec<Fr>, SignatureError> {
380    if out_width == 0 {
381        return Err(SignatureError::from_source("invalid input"));
382    }
383    let bits = big_int_array_to_bits(in_arr, in_width as usize)?;
384    let mut packed: Vec<Fr> = bits
385        .rchunks(out_width as usize)
386        .map(|chunk| {
387            U256::from_radix_be(chunk, 2)
388                .map(|v| Fr::from_be_bytes_mod_order(v.to_be().digits()))
389                .ok_or_else(|| SignatureError::from_source("invalid radix-2 conversion"))
390        })
391        .collect::<Result<_, _>>()?;
392    packed.reverse();
393    match packed.len() != (in_arr.len() * in_width as usize).div_ceil(out_width as usize) {
394        true => Err(SignatureError::from_source("invalid input")),
395        false => Ok(packed),
396    }
397}
398
399/// Convert a big int array to a bit array with 0 paddings.
400fn big_int_array_to_bits<T: ToBits>(
401    integers: &[T],
402    intended_size: usize,
403) -> Result<Vec<u8>, SignatureError> {
404    use itertools::Itertools;
405    use std::cmp::Ordering::Equal;
406    use std::cmp::Ordering::Greater;
407    use std::cmp::Ordering::Less;
408
409    integers
410        .iter()
411        .map(|integer| {
412            let bits = integer.to_bits();
413            match bits.len().cmp(&intended_size) {
414                Less => {
415                    let extra_bits = intended_size - bits.len();
416                    let mut padded = vec![0; extra_bits];
417                    padded.extend(bits);
418                    Ok(padded)
419                }
420                Equal => Ok(bits),
421                Greater => Err(SignatureError::from_source("invalid input")),
422            }
423        })
424        .flatten_ok()
425        .collect()
426}
427
428trait ToBits {
429    fn to_bits(&self) -> Vec<u8>;
430}
431
432impl ToBits for U256 {
433    fn to_bits(&self) -> Vec<u8> {
434        self.to_radix_be(2)
435    }
436}
437
438impl ToBits for U2048 {
439    fn to_bits(&self) -> Vec<u8> {
440        self.to_radix_be(2)
441    }
442}
443
444/// Calculate the poseidon hash from selected fields from inputs, along with the ephemeral pubkey.
445pub fn calculate_all_inputs_hash(
446    inputs: &ZkLoginInputs,
447    signature: &SimpleSignature,
448    modulus: &[u8],
449    max_epoch: u64,
450) -> Result<Fr, SignatureError> {
451    if inputs.header_base64().len() > MAX_HEADER_LEN as usize {
452        return Err(SignatureError::from_source("header too long"));
453    }
454
455    let (first, second) = public_key_to_frs(signature)?;
456
457    let address_seed = bn254_to_fr(inputs.address_seed());
458    let max_epoch_f = Fr::from_be_bytes_mod_order(U256::from(max_epoch).to_be().digits());
459    let index_mod_4_f = Fr::from_be_bytes_mod_order(
460        U256::from(inputs.iss_base64_details().index_mod_4)
461            .to_be()
462            .digits(),
463    );
464
465    let iss_base64_f =
466        hash_ascii_str_to_field(&inputs.iss_base64_details().value, MAX_ISS_LEN_B64)?;
467    let header_f = hash_ascii_str_to_field(inputs.header_base64(), MAX_HEADER_LEN)?;
468    let modulus_f = hash_to_field(
469        &[U2048::from_be_slice(modulus)
470            .ok_or_else(|| SignatureError::from_source("JWK modulus too large for U2048"))?],
471        2048,
472        PACK_WIDTH,
473    )?;
474
475    POSEIDON
476        .hash(&[
477            first,
478            second,
479            address_seed,
480            max_epoch_f,
481            iss_base64_f,
482            index_mod_4_f,
483            header_f,
484            modulus_f,
485        ])
486        .map_err(SignatureError::from_source)
487}
488
489/// Calculate the Sui address based on address seed and address params.
490#[allow(unused)]
491fn gen_address_seed(
492    salt: &str,
493    name: &str,  // i.e. "sub"
494    value: &str, // i.e. the sub value
495    aud: &str,   // i.e. the client ID
496) -> Result<String, SignatureError> {
497    let salt_hash = POSEIDON
498        .hash(&[bn254_to_fr(
499            &Bn254FieldElement::from_str(salt).map_err(SignatureError::from_source)?,
500        )])
501        .map_err(SignatureError::from_source)?;
502    gen_address_seed_with_salt_hash(salt_hash, name, value, aud)
503}
504
505const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32;
506const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115;
507const MAX_AUD_VALUE_LENGTH: u8 = 145;
508
509/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as input instead of the salt.
510pub(crate) fn gen_address_seed_with_salt_hash(
511    salt_hash: Fr,
512    name: &str,  // i.e. "sub"
513    value: &str, // i.e. the sub value
514    aud: &str,   // i.e. the client ID
515) -> Result<String, SignatureError> {
516    Ok(POSEIDON
517        .hash(&[
518            hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?,
519            hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?,
520            hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?,
521            salt_hash,
522        ])
523        .map_err(SignatureError::from_source)?
524        .to_string())
525}
526
527#[cfg(test)]
528mod test {
529    use super::*;
530    use sui_sdk_types::Ed25519Signature;
531
532    #[cfg(test)]
533    #[cfg(target_arch = "wasm32")]
534    use wasm_bindgen_test::wasm_bindgen_test as test;
535
536    #[test]
537    fn test_verify_zklogin_google() {
538        let user_salt = "206703048842351542647799591018316385612";
539
540        let pubkey = Ed25519PublicKey::new([
541            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
542            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
543        ]);
544        let signature = SimpleSignature::Ed25519 {
545            signature: Ed25519Signature::new([0; 64]),
546            public_key: pubkey,
547        };
548
549        // Get the address seed.
550        let address_seed = gen_address_seed(
551            user_salt,
552            "sub",
553            "106294049240999307923",
554            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
555        )
556        .unwrap();
557
558        let inputs = serde_json::json!({
559            "proof_points": {
560                "a": [
561                    "8247215875293406890829839156897863742504615191361518281091302475904551111016",
562                    "6872980335748205979379321982220498484242209225765686471076081944034292159666",
563                    "1"
564                ],
565                "b": [
566                    [
567                        "21419680064642047510915171723230639588631899775315750803416713283740137406807",
568                        "21566716915562037737681888858382287035712341650647439119820808127161946325890"
569                    ],
570                    [
571                        "17867714710686394159919998503724240212517838710399045289784307078087926404555",
572                        "21812769875502013113255155836896615164559280911997219958031852239645061854221"
573                    ],
574                    ["1","0"]
575                ],
576                "c": [
577                    "7530826803702928198368421787278524256623871560746240215547076095911132653214",
578                    "16244547936249959771862454850485726883972969173921727256151991751860694123976",
579                    "1"
580                ]
581            },
582            "iss_base64_details": {
583                "value": "yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC",
584                "index_mod_4": 1
585            },
586            "header_base64": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
587            "address_seed": address_seed
588        });
589
590        let zklogin_inputs: ZkLoginInputs = serde_json::from_value(inputs).unwrap();
591
592        let jwk = Jwk {
593            kty: "RSA".to_string(),
594            e: "AQAB".to_string(),
595            n: "oUriU8GqbRw-avcMn95DGW1cpZR1IoM6L7krfrWvLSSCcSX6Ig117o25Yk7QWBiJpaPV0FbP7Y5-DmThZ3SaF0AXW-3BsKPEXfFfeKVc6vBqk3t5mKlNEowjdvNTSzoOXO5UIHwsXaxiJlbMRalaFEUm-2CKgmXl1ss_yGh1OHkfnBiGsfQUndKoHiZuDzBMGw8Sf67am_Ok-4FShK0NuR3-q33aB_3Z7obC71dejSLWFOEcKUVCaw6DGVuLog3x506h1QQ1r0FXKOQxnmqrRgpoHqGSouuG35oZve1vgCU4vLZ6EAgBAbC0KL35I7_0wUDSMpiAvf7iZxzJVbspkQ".to_string(),
596            alg: "RS256".to_string(),
597        };
598
599        VerifyingKey::new_mainnet()
600            .verify_zklogin(&jwk, &zklogin_inputs, &signature, 10)
601            .unwrap();
602    }
603
604    #[test]
605    fn test_public_key_to_frs() {
606        let pubkey = Ed25519PublicKey::new([
607            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
608            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
609        ]);
610        let signature = SimpleSignature::Ed25519 {
611            signature: Ed25519Signature::new([0; 64]),
612            public_key: pubkey,
613        };
614        let (actual_0, actual_1) = public_key_to_frs(&signature).unwrap();
615        let expect_0 = Fr::from(ark_ff::BigInt([
616            1244302228903607218,
617            13386648721483054705,
618            0,
619            0,
620        ]));
621
622        let expect_1 = Fr::from(ark_ff::BigInt([
623            18225592963892023808,
624            2904666130704426303,
625            0,
626            0,
627        ]));
628        assert_eq!(actual_0, expect_0);
629        assert_eq!(actual_1, expect_1);
630    }
631
632    #[test]
633    fn test_hash_ascii_str_to_field() {
634        let actual = hash_ascii_str_to_field("sub", 32).unwrap();
635        let expect = Fr::from(ark_ff::BigInt([
636            9420274050661827129,
637            9736100402995345242,
638            10431892319505233812,
639            1450152190758097105,
640        ]));
641        assert_eq!(actual, expect);
642
643        let actual = hash_ascii_str_to_field("106294049240999307923", 115).unwrap();
644        let expect = Fr::from(ark_ff::BigInt([
645            1616959301818912987,
646            17318965991705091209,
647            15303466056770245354,
648            1596136658728187659,
649        ]));
650        assert_eq!(actual, expect);
651
652        let actual = hash_ascii_str_to_field(
653            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
654            145,
655        )
656        .unwrap();
657        let expect = Fr::from(ark_ff::BigInt([
658            5030944271044826582,
659            8577618269522081956,
660            6962871209781429610,
661            2149811477348923117,
662        ]));
663        assert_eq!(actual, expect);
664
665        let actual =
666            hash_ascii_str_to_field("yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC", 224)
667                .unwrap();
668        let expect = Fr::from(ark_ff::BigInt([
669            6021918591354572765,
670            14069258108381575504,
671            1736509627917450843,
672            2767185135515367512,
673        ]));
674        assert_eq!(actual, expect);
675
676        let actual = hash_ascii_str_to_field(
677        "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
678        248,
679    ).unwrap();
680        let expect = Fr::from(ark_ff::BigInt([
681            4239129243150064016,
682            15469804315138207306,
683            17534492051703966556,
684            2100329545252322607,
685        ]));
686        assert_eq!(actual, expect);
687    }
688}