@@ -27,6 +27,7 @@ import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_GET_PHONE_NUMBER
2727import com.credman.cmwallet.pnv.PnvTokenRegistry.Companion.VCT_VERIFY_PHONE_NUMBER
2828import com.credman.cmwallet.toBase64UrlNoPadding
2929import com.credman.cmwallet.toJWK
30+ import io.ktor.util.encodeBase64
3031import kotlinx.serialization.json.add
3132import kotlinx.serialization.json.buildJsonArray
3233import kotlinx.serialization.json.buildJsonObject
@@ -295,9 +296,10 @@ fun maybeHandlePnv(
295296 // TODO: validate the aggregator cert is allowed to request phone number verification for the given carrier
296297
297298 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() }
301303
302304 val aggregatorNonce = credAuthJwtpayload.getString(" nonce" )
303305 require(aggregatorNonce == openId4VPRequest.nonce) { " Aggregator nonce should match the verifier nonce" }
@@ -319,11 +321,7 @@ fun maybeHandlePnv(
319321
320322 // Generate the phone number token SD-JWT
321323 val tempTokenJson = buildJsonObject {
322- put(" iss" , selectedCred.iss)
323- put(" vct" , selectedCred.vct)
324324 put(" temp_token" , getTempTokenForCredential(selectedCred))
325- put(" subscription_hint" , selectedCred.subscriptionHint)
326- put(" carrier_hint" , selectedCred.carrierHint)
327325 }
328326 val encryptedTempTokenJwe = jweSerialization(aggregatorEncKey, tempTokenJson.toString())
329327
@@ -336,20 +334,25 @@ fun maybeHandlePnv(
336334 val deviceTelModuleJwt = createJWTES256(
337335 header = buildJsonObject {
338336 put(" alg" , " ES256" )
337+ put(" typ" , " dc+sd-jwt-pnv" )
339338 put(" x5c" , buildJsonArray {
340339 add(" MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+TdTS2XLm+GzoFnwcBfDBzxpHyfA5t9NHIqGravV6L2i4BttORRF1DrjTBDELi265/KIjgtJ05zSFZ/jrUpkmw==" )
341340 })
342341 },
343342 payload = buildJsonObject {
343+ put(" iss" , " https://example.com/issuer" )
344+ put(" vct" , selectedCred.vct)
344345 put(" cnf" , buildJsonObject {
345346 put(" jwk" , deviceKp.public.toJWK())
346347 })
348+ put(" exp" , 1883000000 )
349+ put(" iat" , 1683000000 )
347350 },
348351 privateKey = deviceTelModulePrivateKey
349352 )
350353 val sdJwt = deviceTelModuleJwt + " ~"
351354
352- val md = MessageDigest .getInstance( " SHA-256 " )
355+ md.reset( )
353356 val digest = md.digest(sdJwt.encodeToByteArray()).toBase64UrlNoPadding()
354357 val kbJwt = createJWTES256(
355358 header = buildJsonObject {
@@ -361,12 +364,15 @@ fun maybeHandlePnv(
361364 put(" aud" , origin)
362365 put(" nonce" , openId4VPRequest.nonce)
363366 put(" encrypted_credential" , encryptedTempTokenJwe)
367+ put(" consent_data_hash" , consentDataHash)
364368 put(" sd_hash" , digest)
369+ put(" subscription_hint" , selectedCred.subscriptionHint)
370+ put(" carrier_hint" , selectedCred.carrierHint)
371+ put(" android_carrier_hint" , selectedCred.androidCarrierHint)
365372 },
366373 privateKey = deviceKp.private
367374 )
368375
369- // We don't use selective disclosure, so the sd-jwt is simply jwt + "~"
370376 val tempTokenDcSdJwt = " ${deviceTelModuleJwt} ~${kbJwt} "
371377
372378 val vpToken = JSONObject ().apply {
0 commit comments