11//! Tests related to prepare_attributes and get_attributes II canister calls.
22
3+ use candid:: Principal ;
34use canister_tests:: api:: internet_identity as api;
45use canister_tests:: framework:: * ;
6+ use ic_canister_sig_creation:: extract_raw_canister_sig_pk_from_der;
57use internet_identity_interface:: internet_identity:: types:: attributes:: {
68 CertifiedAttribute , CertifiedAttributes , GetAttributesRequest , PrepareAttributeRequest ,
79} ;
8- use internet_identity_interface:: internet_identity:: types:: OpenIdConfig ;
10+ use internet_identity_interface:: internet_identity:: types:: {
11+ GetDelegationResponse , OpenIdConfig , SignedDelegation ,
12+ } ;
13+ use pocket_ic:: PocketIc ;
914use pretty_assertions:: assert_eq;
15+ use serde_bytes:: ByteBuf ;
1016use std:: time:: Duration ;
1117
18+ fn verify_delegation_and_attribute_signatures (
19+ env : & PocketIc ,
20+ session_public_key : ByteBuf ,
21+ user_key : ByteBuf ,
22+ ii_backend_canister_id : Principal ,
23+ signed_delegation : SignedDelegation ,
24+ attributes : Vec < CertifiedAttribute > ,
25+ attribute_expiration : u64 ,
26+ ) {
27+ let root_key = env. root_key ( ) . unwrap ( ) ;
28+
29+ let now_timestamp_ns = env. get_time ( ) . as_nanos_since_unix_epoch ( ) ;
30+
31+ assert_eq ! ( signed_delegation. delegation. pubkey, session_public_key) ;
32+ assert ! (
33+ signed_delegation. delegation. expiration > now_timestamp_ns,
34+ "Delegation has expired: {} <= {}" ,
35+ signed_delegation. delegation. expiration,
36+ now_timestamp_ns
37+ ) ;
38+
39+ assert_eq ! ( signed_delegation. delegation. targets, None ) ;
40+ assert ! ( attribute_expiration > now_timestamp_ns) ;
41+
42+ // Ensure that the user key is a canister signature (we rely on `user_key` being DER-encoded)
43+ let canister_id_bytes = extract_raw_canister_sig_pk_from_der ( & user_key) . unwrap ( ) ;
44+
45+ let canister_id = {
46+ let canister_id_bytes_len = canister_id_bytes[ 0 ] as usize ;
47+ let bound = canister_id_bytes_len + 1 ;
48+ Principal :: from_slice ( & canister_id_bytes[ 1 ..bound] )
49+ } ;
50+
51+ assert_eq ! ( canister_id, ii_backend_canister_id) ;
52+
53+ verify_delegation ( env, user_key. clone ( ) , & signed_delegation, & root_key) ;
54+
55+ for attribute in & attributes {
56+ verify_attribute (
57+ env,
58+ user_key. clone ( ) ,
59+ attribute,
60+ attribute_expiration,
61+ & root_key,
62+ ) ;
63+ }
64+ }
65+
1266#[ test]
1367fn should_get_certified_attributes ( ) {
1468 let env = env ( ) ;
@@ -29,7 +83,7 @@ fn should_get_certified_attributes() {
2983 fedcm_uri: Some ( "https://accounts.google.com/gsi/fedcm.json" . into( ) ) ,
3084 } ] ) ;
3185
32- let canister_id = install_ii_canister_with_arg_and_cycles (
86+ let ii_backend_canister_id = install_ii_canister_with_arg_and_cycles (
3387 & env,
3488 II_WASM . clone ( ) ,
3589 Some ( init_args) ,
@@ -39,29 +93,40 @@ fn should_get_certified_attributes() {
3993 // This also handles the fetch triggered by initialize()
4094 crate :: openid:: mock_google_certs_response ( & env) ;
4195
42- deploy_archive_via_ii ( & env, canister_id ) ;
96+ deploy_archive_via_ii ( & env, ii_backend_canister_id ) ;
4397
4498 // Create an identity with the required authn method
45- let user_number = crate :: v2_api:: authn_method_test_helpers:: create_identity_with_authn_method (
46- & env,
47- canister_id,
48- & test_authn_method,
49- ) ;
99+ let identity_number =
100+ crate :: v2_api:: authn_method_test_helpers:: create_identity_with_authn_method (
101+ & env,
102+ ii_backend_canister_id,
103+ & test_authn_method,
104+ ) ;
50105
51106 // Sync time to the JWT iat
52107 let time_to_advance = Duration :: from_millis ( test_time) - Duration :: from_nanos ( time ( & env) ) ;
53108 env. advance_time ( time_to_advance) ;
54109
55110 // Add OpenID credential
56- api:: openid_credential_add ( & env, canister_id, test_principal, user_number, & jwt, & salt)
57- . expect ( "failed to add openid credential" )
58- . expect ( "openid_credential_add error" ) ;
111+ api:: openid_credential_add (
112+ & env,
113+ ii_backend_canister_id,
114+ test_principal,
115+ identity_number,
116+ & jwt,
117+ & salt,
118+ )
119+ . expect ( "failed to add openid credential" )
120+ . expect ( "openid_credential_add error" ) ;
59121
60122 let origin = "https://some-dapp.com" ;
61123
62124 // 1. Prepare attributes
125+
126+ env. advance_time ( Duration :: from_secs ( 15 ) ) ;
127+
63128 let prepare_request = PrepareAttributeRequest {
64- identity_number : user_number ,
129+ identity_number,
65130 origin : origin. to_string ( ) ,
66131 account_number : None ,
67132 attribute_keys : vec ! [
@@ -70,59 +135,110 @@ fn should_get_certified_attributes() {
70135 ] ,
71136 } ;
72137
73- let prepare_response =
74- api:: prepare_attributes ( & env, canister_id, test_principal, prepare_request)
75- . expect ( "failed to call prepare_attributes" )
76- . expect ( "prepare_attributes error" ) ;
138+ let prepare_response = api:: prepare_attributes (
139+ & env,
140+ ii_backend_canister_id,
141+ test_principal,
142+ prepare_request,
143+ )
144+ . expect ( "failed to call prepare_attributes" )
145+ . expect ( "prepare_attributes error" ) ;
77146
78147 assert_eq ! ( prepare_response. attributes. len( ) , 2 ) ;
79148
80149 // 2. Get attributes
150+ env. advance_time ( Duration :: from_secs ( 5 ) ) ;
151+
81152 let get_request = GetAttributesRequest {
82- identity_number : user_number ,
153+ identity_number,
83154 origin : origin. to_string ( ) ,
84155 account_number : None ,
85156 issued_at_timestamp_ns : prepare_response. issued_at_timestamp_ns ,
86157 attributes : prepare_response. attributes . clone ( ) ,
87158 } ;
88159
89- let get_response = api:: get_attributes ( & env, canister_id, test_principal, get_request)
90- . expect ( "failed to call get_attributes" )
91- . expect ( "get_attributes error" ) ;
160+ let get_response =
161+ api:: get_attributes ( & env, ii_backend_canister_id, test_principal, get_request)
162+ . expect ( "failed to call get_attributes" )
163+ . expect ( "get_attributes error" ) ;
92164
93- let mut actual_response = get_response;
94- actual_response . certified_attributes . sort ( ) ;
165+ let mut redacted_response = get_response. clone ( ) ;
166+ redacted_response . certified_attributes . sort ( ) ;
95167
96168 // Check the signatures (not a full verification step, just a smoke test)
97- for attribute in actual_response . certified_attributes . iter ( ) {
169+ for attribute in redacted_response . certified_attributes . iter ( ) {
98170 assert ! ( attribute
99171 . signature
100172 . starts_with( & [ 217 , 217 , 247 , 162 , 107 , 99 , 101 , 114 ] ) ) ;
101173 }
102174
103175 // Redact the signatures so we can compare the response
104- actual_response
176+ redacted_response
105177 . certified_attributes
106178 . iter_mut ( )
107179 . for_each ( |attr| attr. signature = vec ! [ ] ) ;
108180
109181 assert_eq ! (
110- actual_response ,
182+ redacted_response ,
111183 CertifiedAttributes {
112184 certified_attributes: vec![
113185 CertifiedAttribute {
114186 key: "openid:https://accounts.google.com:email" . into( ) ,
115187 value: b"andri.schatz@dfinity.org" . to_vec( ) ,
116- signature: vec![ ] // redacted
188+ signature: vec![ ] , // redacted
117189 } ,
118190 CertifiedAttribute {
119191 key: "openid:https://accounts.google.com:name" . into( ) ,
120192 value: b"Andri Schatz" . to_vec( ) ,
121- signature: vec![ ] ,
193+ signature: vec![ ] , // redacted
122194 } ,
123195 ] ,
124196 expires_at_timestamp_ns: prepare_response. issued_at_timestamp_ns
125197 + Duration :: from_secs( 30 * 60 ) . as_nanos( ) as u64 ,
126198 }
127199 ) ;
200+
201+ // Verify the signatures; this relies on delegation verification for the same (origin, user).
202+
203+ let session_public_key = ByteBuf :: from ( "session public key" ) ;
204+
205+ env. advance_time ( Duration :: from_secs ( 35 ) ) ;
206+
207+ let ( canister_sig_key, expiration) = api:: prepare_delegation (
208+ & env,
209+ ii_backend_canister_id,
210+ test_principal,
211+ identity_number,
212+ origin,
213+ & session_public_key,
214+ None ,
215+ )
216+ . unwrap ( ) ;
217+
218+ env. advance_time ( Duration :: from_secs ( 5 ) ) ;
219+
220+ let signed_delegation = match api:: get_delegation (
221+ & env,
222+ ii_backend_canister_id,
223+ test_principal,
224+ identity_number,
225+ origin,
226+ & session_public_key,
227+ expiration,
228+ )
229+ . unwrap ( )
230+ {
231+ GetDelegationResponse :: SignedDelegation ( delegation) => delegation,
232+ GetDelegationResponse :: NoSuchDelegation => panic ! ( "failed to get delegation" ) ,
233+ } ;
234+
235+ verify_delegation_and_attribute_signatures (
236+ & env,
237+ session_public_key,
238+ canister_sig_key,
239+ ii_backend_canister_id,
240+ signed_delegation,
241+ get_response. certified_attributes ,
242+ get_response. expires_at_timestamp_ns ,
243+ ) ;
128244}
0 commit comments