@@ -3,25 +3,25 @@ package com.credman.cmwallet.pnv
3
3
import android.util.Base64
4
4
import android.util.Log
5
5
import androidx.credentials.provider.CallingAppInfo
6
- import com.credman.cmwallet.CmWalletApplication.Companion.TAG
7
6
import com.credman.cmwallet.CmWalletApplication.Companion.computeClientId
8
7
import com.credman.cmwallet.createJWTES256
8
+ import com.credman.cmwallet.data.repository.CredentialRepository.Companion.ICON
9
+ import com.credman.cmwallet.data.repository.CredentialRepository.Companion.LENGTH
10
+ import com.credman.cmwallet.data.repository.CredentialRepository.Companion.START
11
+ import com.credman.cmwallet.data.repository.CredentialRepository.RegistryIcon
12
+ import com.credman.cmwallet.decodeBase64
9
13
import com.credman.cmwallet.getcred.GetCredentialActivity.DigitalCredentialRequestOptions
10
14
import com.credman.cmwallet.getcred.GetCredentialActivity.DigitalCredentialResult
11
15
import com.credman.cmwallet.jweSerialization
12
16
import com.credman.cmwallet.jwsDeserialization
13
17
import com.credman.cmwallet.loadECPrivateKey
14
18
import com.credman.cmwallet.openid4vp.OpenId4VP
15
19
import com.credman.cmwallet.openid4vp.OpenId4VP.Companion.IDENTIFIERS_1_0
16
- import com.credman.cmwallet.openid4vp.OpenId4VPMatchedCredential
17
- import com.credman.cmwallet.openid4vp.OpenId4VPMatchedMDocClaims
18
- import com.credman.cmwallet.openid4vp.OpenId4VPMatchedSdJwtClaims
19
20
import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.TEST_PNV_1_GET_PHONE_NUMBER
20
21
import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.TEST_PNV_1_VERIFY_PHONE_NUMBER
21
- import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.TEST_PNV_2_GET_PHONE_NUMBER
22
+ import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.TEST_PNV_2_VERIFY_PHONE_NUMBER
22
23
import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_GET_PHONE_NUMBER
23
24
import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_VERIFY_PHONE_NUMBER
24
- import com.credman.cmwallet.toJWK
25
25
import kotlinx.serialization.json.buildJsonObject
26
26
import kotlinx.serialization.json.put
27
27
import org.json.JSONArray
@@ -30,7 +30,6 @@ import java.io.ByteArrayOutputStream
30
30
import java.nio.ByteBuffer
31
31
import java.nio.ByteOrder
32
32
import java.security.interfaces.ECPrivateKey
33
- import java.time.Instant
34
33
35
34
/* *
36
35
* A phone number verification entry to be registered with the Credential Manager.
@@ -48,11 +47,14 @@ data class PnvTokenRegistry(
48
47
val tokenId : String ,
49
48
val vct : String ,
50
49
val title : String ,
50
+ val subtitle : String? = null ,
51
51
val providerConsent : String? ,
52
52
val subscriptionHint : Int ,
53
53
val carrierHint : String ,
54
54
val phoneNumberHint : String? ,
55
55
val iss : String ,
56
+ val icon : String? = null ,
57
+ val phoneNumberAttributeDisplayName : String , // Should be localized
56
58
) {
57
59
/* * Converts this TS43 entry to the more generic SD-JWT registry item(s). */
58
60
private fun toSdJwtRegistryItems (): SdJwtRegistryItem {
@@ -65,7 +67,7 @@ data class PnvTokenRegistry(
65
67
RegistryClaim (" carrier_hint" , null , carrierHint),
66
68
RegistryClaim (" phone_number_hint" , null , phoneNumberHint),
67
69
),
68
- displayData = ItemDisplayData (title = title, subtitle = null , description = providerConsent),
70
+ displayData = ItemDisplayData (title = title, subtitle = subtitle , description = providerConsent),
69
71
)
70
72
}
71
73
@@ -82,51 +84,79 @@ data class PnvTokenRegistry(
82
84
internal const val PATHS = " paths"
83
85
internal const val VALUE = " value"
84
86
internal const val DISPLAY = " display"
87
+ internal const val SHARED_ATTRIBUTE_DISPLAY_NAME = " shared_attribute_display_name"
85
88
86
89
val TEST_PNV_1_GET_PHONE_NUMBER = PnvTokenRegistry (
87
90
tokenId = " pnv_1" ,
88
91
vct = VCT_GET_PHONE_NUMBER ,
89
- title = " Phone Number" ,
90
- providerConsent = " CMWallet will enable your carrier {carrier name} to share your phone number iwth {app/domain name}" ,
92
+ title = " Terrific Telecom" ,
93
+ subtitle = " +16502154321" ,
94
+ providerConsent = " CMWallet will enable your carrier (Terrific Telecom) to share your phone number" ,
91
95
subscriptionHint = 1 ,
92
96
carrierHint = " 310250" ,
93
97
phoneNumberHint = " +16502154321" ,
94
- iss = " https://example-carrier2.com" ,
98
+ iss = " https://example.terrific-telecom.dev" ,
99
+ icon = " iVBORw0KGgoAAAANSUhEUgAAAA4AAAAWCAYAAADwza0nAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACBSURBVHgB7ZTBDUVQEEXvff8VoITfC0YZatABHWiH6IUOaICHBCuReXbEWd1kcmZmMRmGIinhSpABFBDoJpqccSKtA/7wY7C71FQ1NUaUyKIg4Ba8MbiJ3YPnqvcnfuI7xAfdqr0qXjU9FTXTGUnca//NIYGdoflla1BbDsPIqZgBHcEomi+uUHMAAAAASUVORK5CYII=" ,
100
+ phoneNumberAttributeDisplayName = " Phone number" ,
95
101
)
96
102
val TEST_PNV_1_VERIFY_PHONE_NUMBER = TEST_PNV_1_GET_PHONE_NUMBER .copy(
97
103
vct = VCT_VERIFY_PHONE_NUMBER ,
98
104
)
99
- val TEST_PNV_2_GET_PHONE_NUMBER = PnvTokenRegistry (
105
+ val TEST_PNV_2_VERIFY_PHONE_NUMBER = PnvTokenRegistry (
100
106
tokenId = " pnv_2" ,
101
- vct = VCT_GET_PHONE_NUMBER ,
102
- title = " Phone Number" ,
103
- providerConsent = " CMWallet will enable your carrier MOCK-CARRIER-2 to share your phone number" ,
107
+ vct = VCT_VERIFY_PHONE_NUMBER ,
108
+ title = " Work Number" ,
109
+ subtitle = " Timely Telecom" ,
110
+ providerConsent = " CMWallet will enable your carrier (Timely Telecom) to share your phone number" ,
104
111
subscriptionHint = 2 ,
105
112
carrierHint = " 380250" ,
106
113
phoneNumberHint = " +16502157890" ,
107
- iss = " https://example-carrier2.com"
114
+ iss = " https://example.timely-telecom.dev" ,
115
+ icon = " iVBORw0KGgoAAAANSUhEUgAAAA4AAAAWCAYAAADwza0nAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACBSURBVHgB7ZTBDUVQEEXvff8VoITfC0YZatABHWiH6IUOaICHBCuReXbEWd1kcmZmMRmGIinhSpABFBDoJpqccSKtA/7wY7C71FQ1NUaUyKIg4Ba8MbiJ3YPnqvcnfuI7xAfdqr0qXjU9FTXTGUnca//NIYGdoflla1BbDsPIqZgBHcEomi+uUHMAAAAASUVORK5CYII=" ,
116
+ phoneNumberAttributeDisplayName = " Phone number" ,
108
117
)
109
118
110
119
fun buildRegistryDatabase (items : List <PnvTokenRegistry >): ByteArray {
111
120
val out = ByteArrayOutputStream ()
112
121
113
- // We don't support icon for phone number tokens, yet
122
+ val iconMap: Map <String , RegistryIcon > = items.associate {
123
+ Pair (
124
+ it.tokenId,
125
+ RegistryIcon (it.icon?.decodeBase64() ? : ByteArray (0 ))
126
+ )
127
+ }
128
+
114
129
// Write the offset to the json
115
- val jsonOffset = 4
130
+ val jsonOffset = 4 + iconMap.values.sumOf { it.iconValue.size }
116
131
val buffer = ByteBuffer .allocate(4 )
117
132
buffer.order(ByteOrder .LITTLE_ENDIAN )
118
133
buffer.putInt(jsonOffset)
119
134
out .write(buffer.array())
120
135
136
+ // Write the icons
137
+ var currIconOffset = 4
138
+ iconMap.values.forEach {
139
+ it.iconOffset = currIconOffset
140
+ out .write(it.iconValue)
141
+ currIconOffset + = it.iconValue.size
142
+ }
143
+
121
144
val sdJwtCredentials = JSONObject ()
122
- for (item in items.map { it.toSdJwtRegistryItems() }) {
145
+ for (item in items) {
146
+ val sdJwtRegistryItem = item.toSdJwtRegistryItems()
123
147
val credJson = JSONObject ()
124
- credJson.put(ID , item.id)
125
- credJson.put(TITLE , item.displayData.title)
126
- credJson.putOpt(SUBTITLE , item.displayData.subtitle)
127
- credJson.putOpt(DISCLAIMER , item.displayData.description)
148
+ credJson.put(SHARED_ATTRIBUTE_DISPLAY_NAME , item.phoneNumberAttributeDisplayName)
149
+ credJson.put(ID , sdJwtRegistryItem.id)
150
+ credJson.put(TITLE , sdJwtRegistryItem.displayData.title)
151
+ credJson.putOpt(SUBTITLE , sdJwtRegistryItem.displayData.subtitle)
152
+ credJson.putOpt(DISCLAIMER , sdJwtRegistryItem.displayData.description)
153
+ val iconJson = JSONObject ().apply {
154
+ put(START , iconMap[sdJwtRegistryItem.id]!! .iconOffset)
155
+ put(LENGTH , iconMap[sdJwtRegistryItem.id]!! .iconValue.size)
156
+ }
157
+ credJson.putOpt(ICON , iconJson)
128
158
val paths = JSONObject ()
129
- for (claim in item .claims) {
159
+ for (claim in sdJwtRegistryItem .claims) {
130
160
paths.put(claim.path, JSONObject ().putOpt(DISPLAY , claim.display).putOpt(VALUE , claim.value))
131
161
}
132
162
@@ -153,7 +183,11 @@ private class RegistryClaim(
153
183
val value : Any? ,
154
184
)
155
185
156
- private class ItemDisplayData (val title : String , val subtitle : String? , val description : String? )
186
+ private class ItemDisplayData (
187
+ val title : String ,
188
+ val subtitle : String? ,
189
+ val description : String? ,
190
+ )
157
191
158
192
private class SdJwtRegistryItem (
159
193
val id : String ,
@@ -170,7 +204,7 @@ fun maybeHandlePnv(
170
204
origin : String , // Either the web origin or the calling app sha
171
205
callingAppInfo : CallingAppInfo
172
206
): DigitalCredentialResult ? {
173
- if (selectedID != TEST_PNV_1_GET_PHONE_NUMBER .tokenId && selectedID != TEST_PNV_2_GET_PHONE_NUMBER .tokenId) {
207
+ if (selectedID != TEST_PNV_1_GET_PHONE_NUMBER .tokenId && selectedID != TEST_PNV_2_VERIFY_PHONE_NUMBER .tokenId) {
174
208
return null
175
209
}
176
210
val digitalCredentialOptions = DigitalCredentialRequestOptions .createFrom(requestJson)
@@ -199,7 +233,7 @@ fun maybeHandlePnv(
199
233
200
234
val selectedCred = when (selectedID) {
201
235
TEST_PNV_1_GET_PHONE_NUMBER .tokenId -> if (vct == TEST_PNV_1_GET_PHONE_NUMBER .vct) TEST_PNV_1_GET_PHONE_NUMBER else TEST_PNV_1_VERIFY_PHONE_NUMBER
202
- TEST_PNV_2_GET_PHONE_NUMBER .tokenId -> TEST_PNV_2_GET_PHONE_NUMBER
236
+ TEST_PNV_2_VERIFY_PHONE_NUMBER .tokenId -> TEST_PNV_2_VERIFY_PHONE_NUMBER
203
237
else -> return null
204
238
}
205
239
0 commit comments