Skip to content

Commit ef2efee

Browse files
committed
Initial setup for multi-credential return
1 parent c72d975 commit ef2efee

File tree

12 files changed

+549
-406
lines changed

12 files changed

+549
-406
lines changed
2.67 KB
Binary file not shown.

app/src/main/assets/pnv.wasm

2 Bytes
Binary file not shown.

app/src/main/java/com/credman/cmwallet/createcred/CreateCredentialViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,10 @@ class CreateCredentialViewModel : ViewModel() {
285285
val vpResponse = createOpenID4VPResponse(
286286
openId4VPRequest,
287287
"wallet",
288-
selectedCredential,
289-
matchedCredential
288+
listOf(com.credman.cmwallet.getcred.MatchedCredential(
289+
selectedCredential,
290+
matchedCredential
291+
))
290292
)
291293
uiState = uiState.copy(vpResponse = selectedCredential, tmpCode = grant)
292294

app/src/main/java/com/credman/cmwallet/getcred/GetCredentialActivity.kt

Lines changed: 158 additions & 89 deletions
Large diffs are not rendered by default.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.credman.cmwallet.getcred
2+
3+
import androidx.annotation.VisibleForTesting
4+
import androidx.credentials.provider.ProviderGetCredentialRequest
5+
import androidx.credentials.registry.provider.digitalcredentials.DigitalCredentialEntry
6+
import com.credman.cmwallet.getcred.SelectionInfo.SelectedCredential
7+
import org.json.JSONArray
8+
import org.json.JSONObject
9+
10+
data class SelectedClaims(
11+
val paths: JSONArray
12+
)
13+
14+
data class SelectedCred(
15+
val entryId: String,
16+
val dcqlId: String,
17+
val matchedClaimPaths: SelectedClaims?
18+
)
19+
20+
data class InternalSelectionInfo(
21+
val requestIdx: Int,
22+
val creds: List<SelectedCred>
23+
) {
24+
companion object {
25+
fun fromSelectionInfo(selectionInfo: SelectionInfo): InternalSelectionInfo {
26+
val requestId = selectionInfo.setId.substringBefore(";").substringAfter("req:").toInt()
27+
val credList = mutableListOf<SelectedCred>()
28+
for (credential in selectionInfo.credentials) {
29+
val entryId = credential.credId
30+
val metadata = JSONObject(credential.metadata!!)
31+
32+
val claims = metadata.getJSONArray("claims")
33+
val dqclId = metadata.getString("dcql_cred_id")
34+
credList.add(SelectedCred(entryId, dqclId, SelectedClaims(claims)))
35+
}
36+
return InternalSelectionInfo(requestId, credList)
37+
}
38+
39+
fun fromEntryIdJson(entryId: String): InternalSelectionInfo {
40+
val entryIdJson = JSONObject(entryId)
41+
val requestIdx =
42+
if (entryIdJson.has("req_idx")) entryIdJson.getInt("req_idx") else entryIdJson.getInt(
43+
"provider_idx"
44+
)
45+
val selectedId =
46+
if (entryIdJson.has("entry_id")) entryIdJson.getString("entry_id") else entryIdJson.getString(
47+
"id"
48+
)
49+
val dqclCredId = entryIdJson.getString("dcql_cred_id")
50+
return InternalSelectionInfo(
51+
requestIdx,
52+
listOf(SelectedCred(selectedId, dqclCredId, null))
53+
)
54+
}
55+
}
56+
}
57+
58+
/** Classes below belong to Jetpack */
59+
data class SelectionInfo(
60+
val setId: String,
61+
val credentials: List<SelectedCredential>,
62+
) {
63+
data class SelectedCredential(
64+
val credId: String,
65+
val metadata: String?,
66+
)
67+
}
68+
69+
public val ProviderGetCredentialRequest.selectionInfo: SelectionInfo?
70+
get() = this.sourceBundle?.let {
71+
val setId = it.getString(EXTRA_CREDENTIAL_SET_ID) ?: return null
72+
val credentials = mutableListOf<SelectedCredential>()
73+
val setLength = it.getInt(EXTRA_CREDENTIAL_SET_ELEMENT_LENGTH, 0)
74+
for (i in 0 until setLength) {
75+
val credId = it.getString("${EXTRA_CREDENTIAL_SET_ELEMENT_ID_PREFIX}$i") ?: return null
76+
val metadata = it.getString("${EXTRA_CREDENTIAL_SET_ELEMENT_METADATA_PREFIX}$i")
77+
credentials.add(SelectedCredential(credId, metadata))
78+
}
79+
SelectionInfo(setId, credentials)
80+
}
81+
82+
83+
private const val EXTRA_CREDENTIAL_SET_ID =
84+
"androidx.credentials.registry.provider.extra.CREDENTIAL_SET_ID"
85+
private const val EXTRA_CREDENTIAL_SET_ELEMENT_LENGTH =
86+
"androidx.credentials.registry.provider.extra.CREDENTIAL_SET_ELEMENT_LENGTH"
87+
private const val EXTRA_CREDENTIAL_SET_ELEMENT_ID_PREFIX =
88+
"androidx.credentials.registry.provider.extra.CREDENTIAL_SET_ELEMENT_ID_"
89+
private const val EXTRA_CREDENTIAL_SET_ELEMENT_METADATA_PREFIX =
90+
"androidx.credentials.registry.provider.extra.CREDENTIAL_SET_ELEMENT_METADATA_"

matcher/credentialmanager.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ __attribute__((import_module("credman"), import_name("AddField")))
1616
#endif
1717
void AddField(long long cred_id, char *field_display_name, char *field_display_value);
1818

19+
#if defined(__wasm__)
20+
__attribute__((import_module("credman_v2"), import_name("AddEntrySet")))
21+
#endif
22+
void AddEntrySet(char *set_id, int set_length);
23+
24+
#if defined(__wasm__)
25+
__attribute__((import_module("credman_v2"), import_name("AddEntryToSet")))
26+
#endif
27+
void AddEntryToSet(char *cred_id, char* icon, size_t icon_len, char *title, char *subtitle, char *disclaimer, char *warning, char *metadata, char *set_id, int set_index);
28+
29+
#if defined(__wasm__)
30+
__attribute__((import_module("credman_v2"), import_name("AddFieldToEntrySet")))
31+
#endif
32+
void AddFieldToEntrySet(char *cred_id, char *field_display_name, char *field_display_value, char *set_id, int set_index);
33+
34+
#if defined(__wasm__)
35+
__attribute__((import_module("credman_v2"), import_name("AddPaymentEntryToSet")))
36+
#endif
37+
void AddPaymentEntryToSet(char *cred_id, char *merchant_name, char *payment_method_name, char *payment_method_subtitle, char* payment_method_icon, size_t payment_method_icon_len, char *transaction_amount, char* bank_icon, size_t bank_icon_len, char* payment_provider_icon, size_t payment_provider_icon_len, char *metadata, char *set_id, int set_index);
38+
1939
#if defined(__wasm__)
2040
__attribute__((import_module("credman"), import_name("AddStringIdEntry")))
2141
#endif
@@ -46,6 +66,11 @@ __attribute__((import_module("credman"), import_name("GetCredentialsSize")))
4666
#endif
4767
void GetCredentialsSize(uint32_t* size);
4868

69+
#if defined(__wasm__)
70+
__attribute__((import_module("credman"), import_name("GetWasmVersion")))
71+
#endif
72+
void GetWasmVersion(uint32_t* version);
73+
4974
#if defined(__wasm__)
5075
__attribute__((import_module("credman"), import_name("AddPaymentEntry")))
5176
#endif

matcher/dcql.c

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
7474
cJSON_AddItemReferenceToObject(matched_credential, "subtitle", cJSON_GetObjectItemCaseSensitive(candidate, "subtitle"));
7575
cJSON_AddItemReferenceToObject(matched_credential, "icon", cJSON_GetObjectItemCaseSensitive(candidate, "icon"));
7676
cJSON* matched_claim_names = cJSON_CreateArray();
77+
cJSON* matched_claim_metadata = cJSON_CreateArray();
7778
//printf("candidate %s\n", cJSON_Print(candidate));
7879
AddAllClaims(matched_claim_names, cJSON_GetObjectItemCaseSensitive(candidate, "paths"));
7980
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_names", matched_claim_names);
81+
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_metadata", matched_claim_metadata); // Empty array represents all matched
8082
cJSON_AddItemReferenceToArray(matched_credentials, matched_credential);
8183
}
8284
} else {
@@ -89,6 +91,7 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
8991
cJSON_AddItemReferenceToObject(matched_credential, "subtitle", cJSON_GetObjectItemCaseSensitive(candidate, "subtitle"));
9092
cJSON_AddItemReferenceToObject(matched_credential, "icon", cJSON_GetObjectItemCaseSensitive(candidate, "icon"));
9193
cJSON* matched_claim_names = cJSON_CreateArray();
94+
cJSON* matched_claim_metadata = cJSON_CreateArray();
9295

9396
cJSON* claim;
9497
cJSON* candidate_claims = cJSON_GetObjectItemCaseSensitive(candidate, "paths");
@@ -112,16 +115,19 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
112115
cJSON* v;
113116
cJSON_ArrayForEach(v, claim_values) {
114117
if (cJSON_Compare(v, cJSON_GetObjectItemCaseSensitive(curr_claim, "value"), cJSON_True)) {
118+
cJSON_AddItemReferenceToArray(matched_claim_metadata, paths);
115119
cJSON_AddItemReferenceToArray(matched_claim_names, cJSON_GetObjectItem(curr_claim, "display"));
116120
break;
117121
}
118122
}
119123
} else {
124+
cJSON_AddItemReferenceToArray(matched_claim_metadata, paths);
120125
cJSON_AddItemReferenceToArray(matched_claim_names, cJSON_GetObjectItem(curr_claim, "display"));
121126
}
122127
}
123128
}
124129
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_names", matched_claim_names);
130+
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_metadata", matched_claim_metadata);
125131
if (cJSON_GetArraySize(matched_claim_names) == cJSON_GetArraySize(claims)) {
126132
cJSON_AddItemReferenceToArray(matched_credentials, matched_credential);
127133
}
@@ -159,7 +165,10 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
159165
cJSON* v;
160166
cJSON_ArrayForEach(v, claim_values) {
161167
if (cJSON_Compare(v, cJSON_GetObjectItemCaseSensitive(curr_claim, "value"), cJSON_True)) {
162-
cJSON_AddItemReferenceToObject(matched_claim_ids, claim_id, cJSON_GetObjectItem(curr_claim, "display"));
168+
cJSON* matched_claim_info = cJSON_CreateObject();
169+
cJSON_AddItemReferenceToObject(matched_claim_info, "claim_display", cJSON_GetObjectItem(curr_claim, "display"));
170+
cJSON_AddItemReferenceToObject(matched_claim_info, "claim_path", paths);
171+
cJSON_AddItemReferenceToObject(matched_claim_ids, claim_id, matched_claim_info);
163172
break;
164173
}
165174
}
@@ -171,14 +180,18 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
171180
cJSON* claim_set;
172181
cJSON_ArrayForEach(claim_set, claim_sets) {
173182
cJSON* matched_claim_names = cJSON_CreateArray();
183+
cJSON* matched_claim_metadata = cJSON_CreateArray();
174184
cJSON* c;
175185
cJSON_ArrayForEach(c, claim_set) {
176186
if (cJSON_HasObjectItem(matched_claim_ids, cJSON_GetStringValue(c))) {
177-
cJSON_AddItemReferenceToArray(matched_claim_names, cJSON_GetObjectItemCaseSensitive(matched_claim_ids, cJSON_GetStringValue(c)));
187+
cJSON* matched_claim_info = cJSON_GetObjectItemCaseSensitive(matched_claim_ids, cJSON_GetStringValue(c));
188+
cJSON_AddItemReferenceToArray(matched_claim_metadata, cJSON_GetObjectItemCaseSensitive(matched_claim_info, "claim_path"));
189+
cJSON_AddItemReferenceToArray(matched_claim_names, cJSON_GetObjectItemCaseSensitive(matched_claim_info, "claim_display"));
178190
}
179191
}
180192
if (cJSON_GetArraySize(matched_claim_names) == cJSON_GetArraySize(claim_set)) {
181193
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_names", matched_claim_names);
194+
cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_metadata", matched_claim_metadata);
182195
cJSON_AddItemReferenceToArray(matched_credentials, matched_credential);
183196
break;
184197
}
@@ -190,10 +203,12 @@ cJSON* MatchCredential(cJSON* credential, cJSON* credential_store) {
190203
return matched_credentials;
191204
}
192205

193-
cJSON* dcql_query(cJSON* query, cJSON* credential_store) {
194-
cJSON* matched_credentials = cJSON_CreateObject();
206+
cJSON* dcql_query(const int request_id, cJSON* query, cJSON* credential_store) {
207+
cJSON* match_result = cJSON_CreateObject();
208+
cJSON* matched_credential_sets = cJSON_CreateArray();
195209
cJSON* candidate_matched_credentials = cJSON_CreateObject();
196210
cJSON* credentials = cJSON_GetObjectItemCaseSensitive(query, "credentials");
211+
cJSON* credential_sets = cJSON_GetObjectItemCaseSensitive(query, "credential_sets");
197212

198213
cJSON* credential;
199214
cJSON_ArrayForEach(credential, credentials) {
@@ -205,11 +220,67 @@ cJSON* dcql_query(cJSON* query, cJSON* credential_store) {
205220
cJSON_AddItemReferenceToObject(m, "matched", matched);
206221
cJSON_AddItemReferenceToObject(candidate_matched_credentials, id, m);
207222
}
223+
}
208224

209-
// Only support matching 1 credential for now
210-
if (cJSON_GetArraySize(candidate_matched_credentials) > 0) {
211-
matched_credentials = candidate_matched_credentials;
225+
if (credential_sets == NULL) {
226+
cJSON* single_matched_credential_set = cJSON_CreateObject();
227+
cJSON* matched_cred_ids = cJSON_CreateArray();
228+
cJSON* matched_credential;
229+
cJSON_ArrayForEach(matched_credential, credentials) {
230+
cJSON_AddItemReferenceToArray(matched_cred_ids, cJSON_GetObjectItemCaseSensitive(matched_credential, "id"));
231+
}
232+
char set_id_buffer[16];
233+
int chars_written = sprintf(set_id_buffer, "req:%d;null", request_id);
234+
cJSON_AddStringToObject(single_matched_credential_set, "set_id", set_id_buffer);
235+
cJSON_AddItemReferenceToObject(single_matched_credential_set, "matched_credential_ids", matched_cred_ids);
236+
cJSON_AddItemReferenceToArray(matched_credential_sets, single_matched_credential_set);
237+
cJSON_AddItemReferenceToObject(match_result, "matched_credential_sets", matched_credential_sets);
238+
cJSON_AddItemReferenceToObject(match_result, "matched_credentials", candidate_matched_credentials);
239+
} else {
240+
cJSON* credential_set;
241+
int matched = 1;
242+
int set_idx = 0;
243+
cJSON_ArrayForEach(credential_set, credential_sets) {
244+
if (cJSON_IsFalse(cJSON_GetObjectItemCaseSensitive(credential_set, "required"))) {
245+
++set_idx;
246+
continue;
247+
}
248+
cJSON* options = cJSON_GetObjectItemCaseSensitive(credential_set, "options");
249+
cJSON* option;
250+
int credential_set_matched = 0;
251+
int option_idx = 0;
252+
cJSON_ArrayForEach(option, options) {
253+
cJSON* matched_cred_ids = cJSON_CreateArray();
254+
cJSON* cred_id;
255+
credential_set_matched = 1;
256+
cJSON_ArrayForEach(cred_id, option) {
257+
if (cJSON_GetObjectItemCaseSensitive(candidate_matched_credentials, cJSON_GetStringValue(cred_id)) == NULL) {
258+
credential_set_matched = 0;
259+
break;
260+
} // Remove for multi-provider support
261+
cJSON_AddItemReferenceToArray(matched_cred_ids, cred_id);
262+
}
263+
if (credential_set_matched != 0) {
264+
cJSON* cred_set_info = cJSON_CreateObject();
265+
char set_id_buffer[26];
266+
int chars_written = sprintf(set_id_buffer, "req:%d;set:%d;option:%d", request_id, set_idx, option_idx);
267+
cJSON_AddStringToObject(cred_set_info, "set_id", set_id_buffer);
268+
cJSON_AddItemReferenceToObject(cred_set_info, "matched_credential_ids", matched_cred_ids);
269+
cJSON_AddItemReferenceToArray(matched_credential_sets, cred_set_info);
270+
}
271+
++option_idx;
272+
}
273+
if (credential_set_matched == 0) {
274+
matched = 0;
275+
break;
276+
}
277+
++set_idx;
278+
}
279+
if (matched != 0) {
280+
cJSON_AddItemReferenceToObject(match_result, "matched_credential_sets", matched_credential_sets);
281+
cJSON_AddItemReferenceToObject(match_result, "matched_credentials", candidate_matched_credentials);
212282
}
213283
}
214-
return matched_credentials;
284+
285+
return match_result;
215286
}

matcher/dcql.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44
#include "cJSON/cJSON.h"
55

6-
cJSON* dcql_query(cJSON* query, cJSON* credential_store);
6+
cJSON* dcql_query(const int request_id, cJSON* query, cJSON* credential_store);
77

88
#endif

0 commit comments

Comments
 (0)