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()).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
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            Fr::from_be_bytes_mod_order(U256::from_radix_be(chunk, 2).unwrap().to_be().digits())
388        })
389        .collect();
390    packed.reverse();
391    match packed.len() != (in_arr.len() * in_width as usize).div_ceil(out_width as usize) {
392        true => Err(SignatureError::from_source("invalid input")),
393        false => Ok(packed),
394    }
395}
396
397/// Convert a big int array to a bit array with 0 paddings.
398fn big_int_array_to_bits<T: ToBits>(
399    integers: &[T],
400    intended_size: usize,
401) -> Result<Vec<u8>, SignatureError> {
402    use itertools::Itertools;
403    use std::cmp::Ordering::Equal;
404    use std::cmp::Ordering::Greater;
405    use std::cmp::Ordering::Less;
406
407    integers
408        .iter()
409        .map(|integer| {
410            let bits = integer.to_bits();
411            match bits.len().cmp(&intended_size) {
412                Less => {
413                    let extra_bits = intended_size - bits.len();
414                    let mut padded = vec![0; extra_bits];
415                    padded.extend(bits);
416                    Ok(padded)
417                }
418                Equal => Ok(bits),
419                Greater => Err(SignatureError::from_source("invalid input")),
420            }
421        })
422        .flatten_ok()
423        .collect()
424}
425
426trait ToBits {
427    fn to_bits(&self) -> Vec<u8>;
428}
429
430impl ToBits for U256 {
431    fn to_bits(&self) -> Vec<u8> {
432        self.to_radix_be(2)
433    }
434}
435
436impl ToBits for U2048 {
437    fn to_bits(&self) -> Vec<u8> {
438        self.to_radix_be(2)
439    }
440}
441
442/// Calculate the poseidon hash from selected fields from inputs, along with the ephemeral pubkey.
443pub fn calculate_all_inputs_hash(
444    inputs: &ZkLoginInputs,
445    signature: &SimpleSignature,
446    modulus: &[u8],
447    max_epoch: u64,
448) -> Result<Fr, SignatureError> {
449    if inputs.header_base64().len() > MAX_HEADER_LEN as usize {
450        return Err(SignatureError::from_source("header too long"));
451    }
452
453    let (first, second) = public_key_to_frs(signature)?;
454
455    let address_seed = bn254_to_fr(inputs.address_seed());
456    let max_epoch_f = Fr::from_be_bytes_mod_order(U256::from(max_epoch).to_be().digits());
457    let index_mod_4_f = Fr::from_be_bytes_mod_order(
458        U256::from(inputs.iss_base64_details().index_mod_4)
459            .to_be()
460            .digits(),
461    );
462
463    let iss_base64_f =
464        hash_ascii_str_to_field(&inputs.iss_base64_details().value, MAX_ISS_LEN_B64)?;
465    let header_f = hash_ascii_str_to_field(inputs.header_base64(), MAX_HEADER_LEN)?;
466    let modulus_f = hash_to_field(&[U2048::from_be_slice(modulus).unwrap()], 2048, PACK_WIDTH)?;
467
468    POSEIDON
469        .hash(&[
470            first,
471            second,
472            address_seed,
473            max_epoch_f,
474            iss_base64_f,
475            index_mod_4_f,
476            header_f,
477            modulus_f,
478        ])
479        .map_err(SignatureError::from_source)
480}
481
482/// Calculate the Sui address based on address seed and address params.
483#[allow(unused)]
484fn gen_address_seed(
485    salt: &str,
486    name: &str,  // i.e. "sub"
487    value: &str, // i.e. the sub value
488    aud: &str,   // i.e. the client ID
489) -> Result<String, SignatureError> {
490    let salt_hash = POSEIDON
491        .hash(&[bn254_to_fr(
492            &Bn254FieldElement::from_str(salt).map_err(SignatureError::from_source)?,
493        )])
494        .map_err(SignatureError::from_source)?;
495    gen_address_seed_with_salt_hash(salt_hash, name, value, aud)
496}
497
498const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32;
499const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115;
500const MAX_AUD_VALUE_LENGTH: u8 = 145;
501
502/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as input instead of the salt.
503pub(crate) fn gen_address_seed_with_salt_hash(
504    salt_hash: Fr,
505    name: &str,  // i.e. "sub"
506    value: &str, // i.e. the sub value
507    aud: &str,   // i.e. the client ID
508) -> Result<String, SignatureError> {
509    Ok(POSEIDON
510        .hash(&[
511            hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?,
512            hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?,
513            hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?,
514            salt_hash,
515        ])
516        .map_err(SignatureError::from_source)?
517        .to_string())
518}
519
520#[cfg(test)]
521mod test {
522    use super::*;
523    use sui_sdk_types::Ed25519Signature;
524
525    #[cfg(test)]
526    #[cfg(target_arch = "wasm32")]
527    use wasm_bindgen_test::wasm_bindgen_test as test;
528
529    #[test]
530    fn test_verify_zklogin_google() {
531        let user_salt = "206703048842351542647799591018316385612";
532
533        let pubkey = Ed25519PublicKey::new([
534            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
535            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
536        ]);
537        let signature = SimpleSignature::Ed25519 {
538            signature: Ed25519Signature::new([0; 64]),
539            public_key: pubkey,
540        };
541
542        // Get the address seed.
543        let address_seed = gen_address_seed(
544            user_salt,
545            "sub",
546            "106294049240999307923",
547            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
548        )
549        .unwrap();
550
551        let inputs = serde_json::json!({
552            "proof_points": {
553                "a": [
554                    "8247215875293406890829839156897863742504615191361518281091302475904551111016",
555                    "6872980335748205979379321982220498484242209225765686471076081944034292159666",
556                    "1"
557                ],
558                "b": [
559                    [
560                        "21419680064642047510915171723230639588631899775315750803416713283740137406807",
561                        "21566716915562037737681888858382287035712341650647439119820808127161946325890"
562                    ],
563                    [
564                        "17867714710686394159919998503724240212517838710399045289784307078087926404555",
565                        "21812769875502013113255155836896615164559280911997219958031852239645061854221"
566                    ],
567                    ["1","0"]
568                ],
569                "c": [
570                    "7530826803702928198368421787278524256623871560746240215547076095911132653214",
571                    "16244547936249959771862454850485726883972969173921727256151991751860694123976",
572                    "1"
573                ]
574            },
575            "iss_base64_details": {
576                "value": "yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC",
577                "index_mod_4": 1
578            },
579            "header_base64": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
580            "address_seed": address_seed
581        });
582
583        let zklogin_inputs: ZkLoginInputs = serde_json::from_value(inputs).unwrap();
584
585        let jwk = Jwk {
586            kty: "RSA".to_string(),
587            e: "AQAB".to_string(),
588            n: "oUriU8GqbRw-avcMn95DGW1cpZR1IoM6L7krfrWvLSSCcSX6Ig117o25Yk7QWBiJpaPV0FbP7Y5-DmThZ3SaF0AXW-3BsKPEXfFfeKVc6vBqk3t5mKlNEowjdvNTSzoOXO5UIHwsXaxiJlbMRalaFEUm-2CKgmXl1ss_yGh1OHkfnBiGsfQUndKoHiZuDzBMGw8Sf67am_Ok-4FShK0NuR3-q33aB_3Z7obC71dejSLWFOEcKUVCaw6DGVuLog3x506h1QQ1r0FXKOQxnmqrRgpoHqGSouuG35oZve1vgCU4vLZ6EAgBAbC0KL35I7_0wUDSMpiAvf7iZxzJVbspkQ".to_string(),
589            alg: "RS256".to_string(),
590        };
591
592        VerifyingKey::new_mainnet()
593            .verify_zklogin(&jwk, &zklogin_inputs, &signature, 10)
594            .unwrap();
595    }
596
597    #[test]
598    fn test_public_key_to_frs() {
599        let pubkey = Ed25519PublicKey::new([
600            185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114,
601            116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0,
602        ]);
603        let signature = SimpleSignature::Ed25519 {
604            signature: Ed25519Signature::new([0; 64]),
605            public_key: pubkey,
606        };
607        let (actual_0, actual_1) = public_key_to_frs(&signature).unwrap();
608        let expect_0 = Fr::from(ark_ff::BigInt([
609            1244302228903607218,
610            13386648721483054705,
611            0,
612            0,
613        ]));
614
615        let expect_1 = Fr::from(ark_ff::BigInt([
616            18225592963892023808,
617            2904666130704426303,
618            0,
619            0,
620        ]));
621        assert_eq!(actual_0, expect_0);
622        assert_eq!(actual_1, expect_1);
623    }
624
625    #[test]
626    fn test_hash_ascii_str_to_field() {
627        let actual = hash_ascii_str_to_field("sub", 32).unwrap();
628        let expect = Fr::from(ark_ff::BigInt([
629            9420274050661827129,
630            9736100402995345242,
631            10431892319505233812,
632            1450152190758097105,
633        ]));
634        assert_eq!(actual, expect);
635
636        let actual = hash_ascii_str_to_field("106294049240999307923", 115).unwrap();
637        let expect = Fr::from(ark_ff::BigInt([
638            1616959301818912987,
639            17318965991705091209,
640            15303466056770245354,
641            1596136658728187659,
642        ]));
643        assert_eq!(actual, expect);
644
645        let actual = hash_ascii_str_to_field(
646            "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com",
647            145,
648        )
649        .unwrap();
650        let expect = Fr::from(ark_ff::BigInt([
651            5030944271044826582,
652            8577618269522081956,
653            6962871209781429610,
654            2149811477348923117,
655        ]));
656        assert_eq!(actual, expect);
657
658        let actual =
659            hash_ascii_str_to_field("yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC", 224)
660                .unwrap();
661        let expect = Fr::from(ark_ff::BigInt([
662            6021918591354572765,
663            14069258108381575504,
664            1736509627917450843,
665            2767185135515367512,
666        ]));
667        assert_eq!(actual, expect);
668
669        let actual = hash_ascii_str_to_field(
670        "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ",
671        248,
672    ).unwrap();
673        let expect = Fr::from(ark_ff::BigInt([
674            4239129243150064016,
675            15469804315138207306,
676            17534492051703966556,
677            2100329545252322607,
678        ]));
679        assert_eq!(actual, expect);
680    }
681}