From a366d57ea9f02f959e220c5be647fd867a2c18ca Mon Sep 17 00:00:00 2001 From: suzannajiwani <69648786+suzannajiwani@users.noreply.github.com> Date: Wed, 4 Dec 2024 11:50:37 -0500 Subject: [PATCH] Allow null apu or apv in OpenID4VP Response (#813) When a wallet did not provide the apu or apv JWE header parameters in the response, the verifier would respond with status code (400). To instead allow the verifier to continue parsing the response, the apu and apv are checked to see if null and if so, are not added to the sessionTranscript. Tested manually against a modified wallet which did not set apu and apv. Signed-off-by: Suzanna Jiwani --- .../identity/wallet/server/VerifierServlet.kt | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/server/src/main/java/com/android/identity/wallet/server/VerifierServlet.kt b/server/src/main/java/com/android/identity/wallet/server/VerifierServlet.kt index 722dd201c..b86edcd0b 100644 --- a/server/src/main/java/com/android/identity/wallet/server/VerifierServlet.kt +++ b/server/src/main/java/com/android/identity/wallet/server/VerifierServlet.kt @@ -1000,11 +1000,22 @@ lrW+vvdmRHBgS+ss56uWyYor6W7ah9ygBwYFK4EEACI= session.deviceResponse = vpToken.toByteArray() } + // According to ISO 23220-4, the mdoc profile is required to have the apv and apu params + // set in the JWE header. However, there is no such requirement for the sd-jwt profile. + val apv = encryptedJWT.header.agreementPartyVInfo + val apu = encryptedJWT.header.agreementPartyUInfo + if (session.requestFormat == "mdoc") { + if ((apu == null) or (apv == null)) { + // Log a warning here instead of throwing an error since apu + apv are not req + // for functionality. + Logger.w(TAG, "Mdoc wallet did not provide both apu and apv JWE headers as expected.") + } + } session.sessionTranscript = createSessionTranscriptOpenID4VP( clientId = clientId, responseUri = session.responseUri!!, - authorizationRequestNonce = encryptedJWT.header.agreementPartyVInfo.toString(), - mdocGeneratedNonce = encryptedJWT.header.agreementPartyUInfo.toString() + authorizationRequestNonce = apv?.toString(), + mdocGeneratedNonce = apu?.toString() ) // Save `deviceResponse` and `sessionTranscript`, for later @@ -1178,35 +1189,27 @@ lrW+vvdmRHBgS+ss56uWyYor6W7ah9ygBwYFK4EEACI= private fun createSessionTranscriptOpenID4VP( clientId: String, responseUri: String, - authorizationRequestNonce: String, - mdocGeneratedNonce: String + authorizationRequestNonce: String?, + mdocGeneratedNonce: String? ): ByteArray { - val clientIdToHash = Cbor.encode(CborArray.builder() - .add(clientId) - .add(mdocGeneratedNonce) - .end() - .build()) - val clientIdHash = Crypto.digest(Algorithm.SHA256, clientIdToHash) + val clientIdBuilder = CborArray.builder().add(clientId) + mdocGeneratedNonce?.let { clientIdBuilder.add(it) } + val clientIdHash = Crypto.digest(Algorithm.SHA256, Cbor.encode(clientIdBuilder.end().build())) - val responseUriToHash = Cbor.encode(CborArray.builder() - .add(responseUri) - .add(mdocGeneratedNonce) - .end() - .build()) - val responseUriHash = Crypto.digest(Algorithm.SHA256, responseUriToHash) + val responseUriBuilder = CborArray.builder().add(responseUri) + mdocGeneratedNonce?.let { responseUriBuilder.add(it) } + val responseUriHash = Crypto.digest(Algorithm.SHA256, Cbor.encode(responseUriBuilder.end().build())) - val oid4vpHandover = CborArray.builder() + val oid4vpHandoverBuilder = CborArray.builder() .add(clientIdHash) .add(responseUriHash) - .add(authorizationRequestNonce) - .end() - .build() + authorizationRequestNonce?.let { oid4vpHandoverBuilder.add(it) } return Cbor.encode( CborArray.builder() .add(Simple.NULL) .add(Simple.NULL) - .add(oid4vpHandover) + .add(oid4vpHandoverBuilder.end().build()) .end() .build() )