Skip to content

Commit 4d9fa24

Browse files
jonas-ljsblacksheartnowacki
authored
Add zklogin check and verify functions (#13817)
## Description Add functions which allows a user to prove that a certain identity was used to create a zklogin address and link this identity to the address (VerifiedID). ## Test Plan Unit tests --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [x] protocol change - [x] user-visible impact - [ ] breaking change for a client SDKs - [x] breaking change for FNs (FN binary must upgrade) - [x] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes Add a `verify_zklogin_id` function to the SUI framework which allows users to prove that a certain identity was used to create a zklogin address and link this identity to their address. The function returns a verified identity object which maybe used to authenticate towards smart contracts. A similar function where the user only reveals who issued the identity used to create a zklogin address, `verify_zklogin_iss` is also added. --------- Co-authored-by: Sam Blackshear <sam.blackshear@gmail.com> Co-authored-by: Todd Nowacki <tmn@mystenlabs.com>
1 parent 6da607a commit 4d9fa24

File tree

6 files changed

+346
-0
lines changed

6 files changed

+346
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
module sui::zklogin_verified_id {
5+
use std::string::String;
6+
use sui::object;
7+
use sui::object::UID;
8+
use sui::tx_context::TxContext;
9+
use sui::tx_context;
10+
11+
/// Error if any of the inputs are longer than the allowed upper bounds.
12+
const EInvalidInput: u64 = 0;
13+
14+
/// Error if the proof consisting of the inputs provided to the verification function is invalid.
15+
const EInvalidProof: u64 = 1;
16+
17+
/// Posession of a VerifiedID proves that the user's address was created using zklogin and the given parameters.
18+
struct VerifiedID has key {
19+
/// The ID of this VerifiedID
20+
id: UID,
21+
/// The address this VerifiedID is associated with
22+
owner: address,
23+
/// The name of the key claim
24+
key_claim_name: String,
25+
/// The value of the key claim
26+
key_claim_value: String,
27+
/// The issuer
28+
issuer: String,
29+
/// The audience (wallet)
30+
audience: String,
31+
}
32+
33+
/// Returns the address associated with the given VerifiedID
34+
public fun owner(verified_id: &VerifiedID): address {
35+
verified_id.owner
36+
}
37+
38+
/// Returns the name of the key claim associated with the given VerifiedID
39+
public fun key_claim_name(verified_id: &VerifiedID): &String {
40+
&verified_id.key_claim_name
41+
}
42+
43+
/// Returns the value of the key claim associated with the given VerifiedID
44+
public fun key_claim_value(verified_id: &VerifiedID): &String {
45+
&verified_id.key_claim_value
46+
}
47+
48+
/// Returns the issuer associated with the given VerifiedID
49+
public fun issuer(verified_id: &VerifiedID): &String {
50+
&verified_id.issuer
51+
}
52+
53+
/// Returns the audience (wallet) associated with the given VerifiedID
54+
public fun audience(verified_id: &VerifiedID): &String {
55+
&verified_id.audience
56+
}
57+
58+
/// Verify that the caller's address was created using zklogin and the given parametersand returns a `VerifiedID`
59+
/// with the caller's id (claim name and value, issuer and wallet id).
60+
///
61+
/// Aborts with `EInvalidInput` if any of the inputs are longer than the allowed upper bounds: `kc_name` must be at
62+
/// most 32 characters, `kc_value` must be at most 115 characters and `aud` must be at most 145 characters.
63+
///
64+
/// Aborts with `EInvalidProof` if the verification fails.
65+
public fun verify_zklogin_id(
66+
key_claim_name: String,
67+
key_claim_value: String,
68+
issuer: String,
69+
audience: String,
70+
pin_hash: u256,
71+
ctx: &mut TxContext,
72+
): VerifiedID {
73+
assert!(check_zklogin_id(tx_context::sender(ctx), &key_claim_name, &key_claim_value, &issuer, &audience, pin_hash), EInvalidProof);
74+
VerifiedID { id: object::new(ctx), owner: tx_context::sender(ctx), key_claim_name, key_claim_value, issuer, audience}
75+
}
76+
77+
/// Returns true if `address` was created using zklogin and the given parameters.
78+
///
79+
/// Aborts with `EInvalidInput` if any of the inputs are longer than the allowed upper bounds: `kc_name` must be at
80+
/// most 32 characters, `kc_value` must be at most 115 characters and `aud` must be at most 145 characters.
81+
public fun check_zklogin_id(
82+
address: address,
83+
key_claim_name: &String,
84+
key_claim_value: &String,
85+
issuer: &String,
86+
audience: &String,
87+
pin_hash: u256
88+
): bool {
89+
check_zklogin_id_internal(
90+
address,
91+
std::string::bytes(key_claim_name),
92+
std::string::bytes(key_claim_value),
93+
std::string::bytes(issuer),
94+
std::string::bytes(audience),
95+
pin_hash
96+
)
97+
}
98+
99+
/// Returns true if `address` was created using zklogin and the given parameters.
100+
///
101+
/// Aborts with `EInvalidInput` if any of `kc_name`, `kc_value`, `iss` and `aud` is not a properly encoded UTF-8
102+
/// string or if the inputs are longer than the allowed upper bounds: `kc_name` must be at most 32 characters,
103+
/// `kc_value` must be at most 115 characters and `aud` must be at most 145 characters.
104+
native fun check_zklogin_id_internal(
105+
address: address,
106+
key_claim_name: &vector<u8>,
107+
key_claim_value: &vector<u8>,
108+
issuer: &vector<u8>,
109+
audience: &vector<u8>,
110+
pin_hash: u256
111+
): bool;
112+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
spec sui::zklogin_verified_id {
5+
spec verify_zklogin_id {
6+
pragma opaque;
7+
// TODO: stub to be replaced by actual abort conditions if any
8+
aborts_if [abstract] true;
9+
// TODO: specify actual function behavior
10+
}
11+
12+
spec check_zklogin_id {
13+
pragma opaque;
14+
// TODO: stub to be replaced by actual abort conditions if any
15+
aborts_if [abstract] true;
16+
// TODO: specify actual function behavior
17+
}
18+
19+
spec check_zklogin_id_internal {
20+
pragma opaque;
21+
// TODO: stub to be replaced by actual abort conditions if any
22+
aborts_if [abstract] true;
23+
// TODO: specify actual function behavior
24+
}
25+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
module sui::zklogin_verified_issuer {
5+
use std::string::String;
6+
use sui::object;
7+
use sui::object::UID;
8+
use sui::tx_context::TxContext;
9+
use sui::tx_context;
10+
11+
/// Error if the proof consisting of the inputs provided to the verification function is invalid.
12+
const EInvalidInput: u64 = 0;
13+
14+
/// Error if the proof consisting of the inputs provided to the verification function is invalid.
15+
const EInvalidProof: u64 = 1;
16+
17+
/// Posession of a VerifiedIssuer proves that the user's address was created using zklogin and with the given issuer
18+
/// (identity provider).
19+
struct VerifiedIssuer has key {
20+
/// The ID of this VerifiedIssuer
21+
id: UID,
22+
/// The address this VerifiedID is associated with
23+
owner: address,
24+
/// The issuer
25+
issuer: String,
26+
}
27+
28+
/// Returns the address associated with the given VerifiedIssuer
29+
public fun owner(verified_issuer: &VerifiedIssuer): address {
30+
verified_issuer.owner
31+
}
32+
33+
/// Returns the issuer associated with the given VerifiedIssuer
34+
public fun issuer(verified_issuer: &VerifiedIssuer): &String {
35+
&verified_issuer.issuer
36+
}
37+
38+
/// Verify that the caller's address was created using zklogin with the given issuer. If so, a VerifiedIssuer object
39+
/// with the issuers id returned.
40+
///
41+
/// Aborts with `EInvalidProof` if the verification fails.
42+
public fun verify_zklogin_issuer(
43+
address_seed: u256,
44+
issuer: String,
45+
ctx: &mut TxContext,
46+
): VerifiedIssuer {
47+
assert!(check_zklogin_issuer(tx_context::sender(ctx), address_seed, &issuer), EInvalidProof);
48+
VerifiedIssuer {id: object::new(ctx), owner: tx_context::sender(ctx), issuer}
49+
}
50+
51+
/// Returns true if `address` was created using zklogin with the given issuer and address seed.
52+
public fun check_zklogin_issuer(
53+
address: address,
54+
address_seed: u256,
55+
issuer: &String,
56+
): bool {
57+
check_zklogin_issuer_internal(address, address_seed, std::string::bytes(issuer))
58+
}
59+
60+
/// Returns true if `address` was created using zklogin with the given issuer and address seed.
61+
///
62+
/// Aborts with `EInvalidInput` if the `iss` input is not a valid UTF-8 string.
63+
native fun check_zklogin_issuer_internal(
64+
address: address,
65+
address_seed: u256,
66+
issuer: &vector<u8>,
67+
): bool;
68+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
spec sui::zklogin_verified_issuer {
5+
spec verify_zklogin_issuer {
6+
pragma opaque;
7+
// TODO: stub to be replaced by actual abort conditions if any
8+
aborts_if [abstract] true;
9+
// TODO: specify actual function behavior
10+
}
11+
spec check_zklogin_issuer {
12+
pragma opaque;
13+
// TODO: stub to be replaced by actual abort conditions if any
14+
aborts_if [abstract] true;
15+
// TODO: specify actual function behavior
16+
}
17+
18+
spec check_zklogin_issuer_internal {
19+
pragma opaque;
20+
// TODO: stub to be replaced by actual abort conditions if any
21+
aborts_if [abstract] true;
22+
// TODO: specify actual function behavior
23+
}
24+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#[test_only]
5+
module sui::zklogin_verified_id_tests {
6+
use sui::zklogin_verified_id::check_zklogin_id;
7+
use sui::address;
8+
use std::string::utf8;
9+
10+
#[test]
11+
fun test_check_zklogin_id() {
12+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
13+
let kc_name = utf8(b"sub");
14+
let kc_value = utf8(b"106294049240999307923");
15+
let aud = utf8(b"575519204237-msop9ep45u2uo98hapqmngv8d84qdc8k.apps.googleusercontent.com");
16+
let iss = utf8(b"https://accounts.google.com");
17+
let salt_hash = 15232766888716517538274372547598053531354666056102343895255590477425668733026u256;
18+
assert!(check_zklogin_id(address, &kc_name, &kc_value, &iss, &aud, salt_hash), 0);
19+
20+
// Negative tests
21+
let other_address = address::from_bytes(x"016b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
22+
assert!(!check_zklogin_id(other_address, &kc_name, &kc_value, &iss, &aud, salt_hash), 1);
23+
24+
let other_kc_name = utf8(b"name");
25+
assert!(!check_zklogin_id(address, &other_kc_name, &kc_value, &iss, &aud, salt_hash), 2);
26+
27+
let other_kc_value = utf8(b"106294049240999307924");
28+
assert!(!check_zklogin_id(address, &other_kc_name, &other_kc_value, &iss, &aud, salt_hash), 3);
29+
30+
let other_iss = utf8(b"https://other.issuer.com");
31+
assert!(!check_zklogin_id(address, &kc_name, &kc_value, &other_iss, &aud, salt_hash), 4);
32+
33+
let other_aud = utf8(b"other.wallet.com");
34+
assert!(!check_zklogin_id(address, &kc_name, &kc_value, &iss, &other_aud, salt_hash), 5);
35+
36+
let other_salt_hash = 15232766888716517538274372547598053531354666056102343895255590477425668733027u256;
37+
assert!(!check_zklogin_id(address, &kc_name, &kc_value, &iss, &aud, other_salt_hash), 6);
38+
}
39+
40+
#[test]
41+
fun test_check_zklogin_id_upper_bounds() {
42+
// Set kc_name, kc_value and aud to be as long as they can be (32, 115 and 145 bytes respectively) and verify
43+
// that the check function doesn't abort.
44+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
45+
let kc_name = utf8(b"qvKbuwvu6LTnYocFPwz1EiIClFUAuMC3");
46+
let kc_value = utf8(b"BA7SREzYLKG5opithujfrU8SFaSpKI4zezu8Vb2GBPVpZsMUpYVeXl6oEAo84ryIlbHOqrMWpI7mlTfvr7HYxiF70jdyIY4rPOOpuJIYWwN3o1olTP2");
47+
let aud = utf8(b"munO2fnn2XnBNq5fXRmSmhC4GPL3yX4Rv9fGoECXTsmniR8dwkPTefbmLF08zh7BnVcaxriii4dEPNwzEF2puLIHmJoeuYbQxV84J9of4NRaL3IhwImVGubgPHWfMfCuGuedCdLn6KUdJsgG1");
48+
let iss = utf8(b"https://issuer.com");
49+
let salt_hash = 1234u256;
50+
assert!(!check_zklogin_id(address, &kc_name, &kc_value, &iss, &aud, salt_hash), 0);
51+
}
52+
53+
#[test]
54+
#[expected_failure(abort_code = sui::zklogin_verified_id::EInvalidInput)]
55+
fun test_check_zklogin_id_long_kc_name() {
56+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
57+
// Should at most be 32 bytes
58+
let long_kc_name = utf8(b"qvKbuwvu6LTnYocFPwz1EiIClFUAuMC3G");
59+
let kc_value = utf8(b"106294049240999307923");
60+
let aud = utf8(b"575519204237-msop9ep45u2uo98hapqmngv8d84qdc8k.apps.googleusercontent.com");
61+
let iss = utf8(b"https://accounts.google.com");
62+
let salt_hash = 15232766888716517538274372547598053531354666056102343895255590477425668733026u256;
63+
check_zklogin_id(address, &long_kc_name, &kc_value, &iss, &aud, salt_hash);
64+
}
65+
66+
#[test]
67+
#[expected_failure(abort_code = sui::zklogin_verified_id::EInvalidInput)]
68+
fun test_check_zklogin_id_long_aud() {
69+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
70+
let kc_name = utf8(b"sub");
71+
// Should at most be 115 bytes
72+
let long_kc_value = utf8(b"BA7SREzYLKG5opithujfrU8SFaSpKI4zezu8Vb2GBPVpZsMUpYVeXl6oEAo84ryIlbHOqrMWpI7mlTfvr7HYxiF70jdyIY4rPOOpuJIYWwN3o1olTP2c");
73+
let aud = utf8(b"575519204237-msop9ep45u2uo98hapqmngv8d84qdc8k.apps.googleusercontent.com");
74+
let iss = utf8(b"https://accounts.google.com");
75+
let salt_hash = 15232766888716517538274372547598053531354666056102343895255590477425668733026u256;
76+
check_zklogin_id(address, &kc_name, &long_kc_value, &iss, &aud, salt_hash);
77+
}
78+
79+
#[test]
80+
#[expected_failure(abort_code = sui::zklogin_verified_id::EInvalidInput)]
81+
fun test_check_zklogin_id_long_kc_value() {
82+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
83+
let kc_name = utf8(b"sub");
84+
let kc_value = utf8(b"106294049240999307923");
85+
// Should be at most 145 bytes
86+
let long_aud = utf8(b"munO2fnn2XnBNq5fXRmSmhC4GPL3yX4Rv9fGoECXTsmniR8dwkPTefbmLF08zh7BnVcaxriii4dEPNwzEF2puLIHmJoeuYbQxV84J9of4NRaL3IhwImVGubgPHWfMfCuGuedCdLn6KUdJsgG1S");
87+
let iss = utf8(b"https://accounts.google.com");
88+
let salt_hash = 15232766888716517538274372547598053531354666056102343895255590477425668733026u256;
89+
check_zklogin_id(address, &kc_name, &kc_value, &iss, &long_aud, salt_hash);
90+
}
91+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#[test_only]
5+
module sui::zklogin_verified_issuer_tests {
6+
use sui::zklogin_verified_issuer::check_zklogin_issuer;
7+
use sui::address;
8+
use std::string::utf8;
9+
10+
#[test]
11+
fun test_check_zklogin_issuer() {
12+
let address = address::from_bytes(x"1c6b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
13+
let iss = utf8(b"https://accounts.google.com");
14+
let address_seed = 3006596378422062745101035755700472756930796952630484939867684134047976874601u256;
15+
assert!(check_zklogin_issuer(address, address_seed, &iss,), 0);
16+
17+
let other_address = address::from_bytes(x"006b623a2f2c91333df730c98d220f11484953b391a3818680f922c264cc0c6b");
18+
assert!(!check_zklogin_issuer(other_address, address_seed, &iss), 1);
19+
20+
let other_address_seed = 1234u256;
21+
assert!(!check_zklogin_issuer(address, other_address_seed, &iss), 2);
22+
23+
let other_iss = utf8(b"https://other.issuer.com");
24+
assert!(!check_zklogin_issuer(address, address_seed, &other_iss), 3);
25+
}
26+
}

0 commit comments

Comments
 (0)