@@ -27,6 +27,7 @@ import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_GET_PHONE_NUMBER
27
27
import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_VERIFY_PHONE_NUMBER
28
28
import com.credman.cmwallet.toBase64UrlNoPadding
29
29
import com.credman.cmwallet.toJWK
30
+ import io.ktor.util.encodeBase64
30
31
import kotlinx.serialization.json.add
31
32
import kotlinx.serialization.json.buildJsonArray
32
33
import kotlinx.serialization.json.buildJsonObject
@@ -295,9 +296,10 @@ fun maybeHandlePnv(
295
296
// TODO: validate the aggregator cert is allowed to request phone number verification for the given carrier
296
297
297
298
val consentData = credAuthJwtpayload.optString(" consent_data" )
298
- // TODO: when the matcher renders the consent data, create the consent data digest to sign over
299
- // and prove that it has been displayed.
300
- val consentDataHash: String? = null
299
+ val md = MessageDigest .getInstance(" SHA-256" )
300
+ val consentDataHash: String? =
301
+ if (consentData.isEmpty()) { null }
302
+ else { md.digest(consentData.encodeToByteArray()).toBase64UrlNoPadding() }
301
303
302
304
val aggregatorNonce = credAuthJwtpayload.getString(" nonce" )
303
305
require(aggregatorNonce == openId4VPRequest.nonce) { " Aggregator nonce should match the verifier nonce" }
@@ -319,11 +321,7 @@ fun maybeHandlePnv(
319
321
320
322
// Generate the phone number token SD-JWT
321
323
val tempTokenJson = buildJsonObject {
322
- put(" iss" , selectedCred.iss)
323
- put(" vct" , selectedCred.vct)
324
324
put(" temp_token" , getTempTokenForCredential(selectedCred))
325
- put(" subscription_hint" , selectedCred.subscriptionHint)
326
- put(" carrier_hint" , selectedCred.carrierHint)
327
325
}
328
326
val encryptedTempTokenJwe = jweSerialization(aggregatorEncKey, tempTokenJson.toString())
329
327
@@ -336,20 +334,25 @@ fun maybeHandlePnv(
336
334
val deviceTelModuleJwt = createJWTES256(
337
335
header = buildJsonObject {
338
336
put(" alg" , " ES256" )
337
+ put(" typ" , " dc+sd-jwt-pnv" )
339
338
put(" x5c" , buildJsonArray {
340
339
add(" MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+TdTS2XLm+GzoFnwcBfDBzxpHyfA5t9NHIqGravV6L2i4BttORRF1DrjTBDELi265/KIjgtJ05zSFZ/jrUpkmw==" )
341
340
})
342
341
},
343
342
payload = buildJsonObject {
343
+ put(" iss" , " https://example.com/issuer" )
344
+ put(" vct" , selectedCred.vct)
344
345
put(" cnf" , buildJsonObject {
345
346
put(" jwk" , deviceKp.public.toJWK())
346
347
})
348
+ put(" exp" , 1883000000 )
349
+ put(" iat" , 1683000000 )
347
350
},
348
351
privateKey = deviceTelModulePrivateKey
349
352
)
350
353
val sdJwt = deviceTelModuleJwt + " ~"
351
354
352
- val md = MessageDigest .getInstance( " SHA-256 " )
355
+ md.reset( )
353
356
val digest = md.digest(sdJwt.encodeToByteArray()).toBase64UrlNoPadding()
354
357
val kbJwt = createJWTES256(
355
358
header = buildJsonObject {
@@ -361,12 +364,15 @@ fun maybeHandlePnv(
361
364
put(" aud" , origin)
362
365
put(" nonce" , openId4VPRequest.nonce)
363
366
put(" encrypted_credential" , encryptedTempTokenJwe)
367
+ put(" consent_data_hash" , consentDataHash)
364
368
put(" sd_hash" , digest)
369
+ put(" subscription_hint" , selectedCred.subscriptionHint)
370
+ put(" carrier_hint" , selectedCred.carrierHint)
371
+ put(" android_carrier_hint" , selectedCred.androidCarrierHint)
365
372
},
366
373
privateKey = deviceKp.private
367
374
)
368
375
369
- // We don't use selective disclosure, so the sd-jwt is simply jwt + "~"
370
376
val tempTokenDcSdJwt = " ${deviceTelModuleJwt} ~${kbJwt} "
371
377
372
378
val vpToken = JSONObject ().apply {
0 commit comments