Skip to content

Commit c3a7fd7

Browse files
marinaioannousraptis-scy
authored andcommitted
OpenID4VPHandover updated to follow OpenID4VP 1.0
1 parent 90acccf commit c3a7fd7

3 files changed

Lines changed: 85 additions & 71 deletions

File tree

wallet-core/src/main/java/eu/europa/ec/eudi/wallet/internal/OpenId4VpUtils.kt

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -101,37 +101,39 @@ private const val SHA_256_ALGORITHM = "SHA-256"
101101
* EReaderKeyBytes = null
102102
*
103103
* Handover = OID4VPHandover
104-
* OID4VPHandover = [
105-
* clientIdHash
106-
* responseUriHash
107-
* nonce
108-
* ]
109-
*
110-
* clientIdHash = bstr
111-
* responseUriHash = bstr
112104
*
113-
* where clientIdHash is the SHA-256 hash of clientIdToHash and responseUriHash is the SHA-256 hash of the responseUriToHash.
105+
* OpenID4VPHandover = [
106+
* "OpenID4VPHandover", ; A fixed identifier for this handover type
107+
* OpenID4VPHandoverInfoHash ; A cryptographic hash of OpenID4VPHandoverInfo
108+
* ]
114109
*
110+
* ; Contains the sha-256 hash of OpenID4VPHandoverInfoBytes
111+
* OpenID4VPHandoverInfoHash = bstr
115112
*
116-
* clientIdToHash = [clientId, mdocGeneratedNonce]
117-
* responseUriToHash = [responseUri, mdocGeneratedNonce]
113+
* ; Contains the bytes of OpenID4VPHandoverInfo encoded as CBOR
114+
* OpenID4VPHandoverInfoBytes = bstr .cbor OpenID4VPHandoverInfo
118115
*
116+
* OpenID4VPHandoverInfo = [
117+
* clientId,
118+
* nonce,
119+
* jwkThumbprint,
120+
* responseUri
121+
* ] ; Array containing handover parameters
119122
*
120-
* mdocGeneratedNonce = tstr
121123
* clientId = tstr
122-
* responseUri = tstr
123124
* nonce = tstr
124-
*
125+
* jwkThumbprint = bstr
126+
* responseUri = tstr
125127
*/
126128
internal fun generateSessionTranscript(
127129
clientId: String,
128-
responseUri: String,
129130
nonce: String,
130-
mdocGeneratedNonce: String,
131+
jwkThumbprint: ByteArray?,
132+
responseOrRedirectUri: String
131133
): SessionTranscriptBytes {
132134

133135
val openID4VPHandover =
134-
generateOpenId4VpHandover(clientId, responseUri, nonce, mdocGeneratedNonce)
136+
generateOpenId4VpHandover(clientId, nonce, jwkThumbprint, responseOrRedirectUri)
135137

136138
val sessionTranscriptBytes =
137139
CBORObject.NewArray().apply {
@@ -144,38 +146,36 @@ internal fun generateSessionTranscript(
144146
}
145147

146148
/**
147-
* Generates the OpenID4VP handover CBOR object containing clientId hash, responseUri hash, and nonce.
149+
* Generates the OpenID4VP handover CBOR object
148150
*
149151
* @param clientId The client identifier.
150-
* @param responseUri The response URI.
151-
* @param nonce The nonce for the session.
152-
* @param mdocGeneratedNonce The generated nonce for mdoc.
153-
* @return The handover as a CBORObject.
152+
* @param nonce The nonce value.
153+
* @param jwkThumbprint The JWK thumbprint as a byte array.
154+
* @param responseOrRedirectUri The response URI or redirect URI.
155+
* @return The CBOR object representing the OpenID4VP handover.
154156
*/
155157
internal fun generateOpenId4VpHandover(
156158
clientId: String,
157-
responseUri: String,
158159
nonce: String,
159-
mdocGeneratedNonce: String,
160+
jwkThumbprint: ByteArray?,
161+
responseOrRedirectUri: String,
160162
): CBORObject {
161-
val clientIdToHash = CBORObject.NewArray().apply {
162-
Add(clientId)
163-
Add(mdocGeneratedNonce)
164-
}.EncodeToBytes()
165163

166-
val responseUriToHash = CBORObject.NewArray().apply {
167-
Add(responseUri)
168-
Add(mdocGeneratedNonce)
164+
val openID4VPHandoverInfoBytes = CBORObject.NewArray().apply {
165+
Add(clientId)
166+
Add(nonce)
167+
Add(jwkThumbprint ?: CBORObject.Null)
168+
Add(responseOrRedirectUri)
169169
}.EncodeToBytes()
170170

171-
val clientIdHash = MessageDigest.getInstance(SHA_256_ALGORITHM).digest(clientIdToHash)
172-
val responseUriHash = MessageDigest.getInstance(SHA_256_ALGORITHM).digest(responseUriToHash)
171+
val openID4VPHandoverInfoHash = MessageDigest.getInstance(SHA_256_ALGORITHM)
172+
.digest(openID4VPHandoverInfoBytes)
173173

174174
val openID4VPHandover = CBORObject.NewArray().apply {
175-
Add(clientIdHash)
176-
Add(responseUriHash)
177-
Add(nonce)
175+
Add("OpenID4VPHandover")
176+
Add(openID4VPHandoverInfoHash)
178177
}
178+
179179
return openID4VPHandover
180180
}
181181

@@ -232,24 +232,25 @@ internal fun makeOpenId4VPConfig(
232232
/**
233233
* Extension function to get the session transcript bytes from a resolved OpenID4VP authorization request.
234234
*
235-
* @param mdocGeneratedNonce The generated nonce for mdoc.
236235
* @return The session transcript as a byte array.
237236
*/
238-
internal fun ResolvedRequestObject.getSessionTranscriptBytes(
239-
mdocGeneratedNonce: String,
240-
): SessionTranscriptBytes {
241-
val clientId = this.client.id.clientId
242-
val responseUri = when (val mode = this.responseMode) {
237+
internal fun ResolvedRequestObject.getSessionTranscriptBytes(): SessionTranscriptBytes {
238+
val clientId = client.id.clientId
239+
val nonce = nonce
240+
val jwkThumbprint = responseEncryptionSpecification?.recipientKey?.computeThumbprint()?.decode()
241+
val responseOrRedirectUri = when (val mode = this.responseMode) {
243242
is ResponseMode.DirectPostJwt -> mode.responseURI.toString()
244-
else -> ""
243+
is ResponseMode.DirectPost -> mode.responseURI.toString()
244+
is ResponseMode.Fragment -> mode.redirectUri.toString()
245+
is ResponseMode.FragmentJwt -> mode.redirectUri.toString()
246+
is ResponseMode.Query -> mode.redirectUri.toString()
247+
is ResponseMode.QueryJwt -> mode.redirectUri.toString()
245248
}
246-
val nonce = this.nonce
247-
248249
val sessionTranscriptBytes = generateSessionTranscript(
249-
clientId,
250-
responseUri,
251-
nonce,
252-
mdocGeneratedNonce
250+
clientId = clientId,
251+
nonce = nonce,
252+
jwkThumbprint = jwkThumbprint,
253+
responseOrRedirectUri = responseOrRedirectUri
253254
)
254255
return sessionTranscriptBytes
255256
}

wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openId4vp/dcql/ProcessedDcqlRequest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,11 @@ class ProcessedDcqlRequest(
170170
// Generate the appropriate verifiable presentation based on format
171171
val vp = when (format) {
172172
FORMAT_MSO_MDOC -> {
173-
// For MSO mdoc, include session transcript and handle devic engagement
173+
// For MSO mdoc, include session transcript and handle device engagement
174174
verifiablePresentationForMsoMdoc(
175175
documentManager = documentManager,
176176
sessionTranscript = resolvedRequestObject
177-
.getSessionTranscriptBytes(msoMdocNonce),
177+
.getSessionTranscriptBytes(),
178178
disclosedDocument = disclosedDocument,
179179
requestedDocuments = requestedDocuments,
180180
signatureAlgorithm = signatureAlgorithm

wallet-core/src/test/java/eu/europa/ec/eudi/wallet/internal/Openid4VpUtilsTest.kt

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ package eu.europa.ec.eudi.wallet.internal
3535
//import eu.europa.ec.eudi.wallet.transfer.openId4vp.JwsAlgorithm
3636
//import eu.europa.ec.eudi.wallet.transfer.openId4vp.OpenId4VpConfig
3737
//import eu.europa.ec.eudi.wallet.transfer.openId4vp.OpenId4VpReaderTrust
38+
import com.nimbusds.jose.jwk.JWK
3839
import eu.europa.ec.eudi.wallet.transfer.openId4vp.OpenId4VpReaderTrustImpl
3940
import org.bouncycastle.util.encoders.Hex
4041
import kotlin.test.Test
@@ -43,42 +44,54 @@ import kotlin.test.assertEquals
4344

4445
/**
4546
* Examples has been taken from:
46-
* https://github.com/awoie/annex-b-examples/pull/2
47-
* https://github.com/awoie/annex-b-examples/blob/main/examples/annex-b-examples.txt
47+
* https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-handover-and-sessiontranscr
4848
*/
4949

50-
const val ANNEX_B_OPENID4VP_HANDOVER =
51-
"835820DA25C527E5FB75BC2DD31267C02237C4462BA0C1BF37071F692E7DD93B10AD0B5820F6ED8E3220D3C59A5F17EB45F48AB70AEECF9EE21744B1014982350BD96AC0C572616263646566676831323334353637383930"
52-
const val ANNEX_B_SESSION_TRANSCRIPT =
53-
"83F6F6835820DA25C527E5FB75BC2DD31267C02237C4462BA0C1BF37071F692E7DD93B10AD0B5820F6ED8E3220D3C59A5F17EB45F48AB70AEECF9EE21744B1014982350BD96AC0C572616263646566676831323334353637383930"
50+
const val OPENID4VP_1_0_HANDOVER =
51+
"82714f70656e494434565048616e646f7665725820048bc053c00442af9b8eed494cefdd9d95240d254b046b11b68013722aad38ac"
52+
const val OPENID4VP_1_0_SESSION_TRANSCRIPT =
53+
"83f6f682714f70656e494434565048616e646f7665725820048bc053c00442af9b8eed494cefdd9d95240d254b046b11b68013722aad38ac"
5454

55-
const val clientId = "example.com"
56-
const val responseUri = "https://example.com/12345/response"
57-
const val nonce = "abcdefgh1234567890"
58-
const val mdocGeneratedNonce = "1234567890abcdefgh"
55+
const val clientId = "x509_san_dns:example.com"
56+
const val nonce = "exc7gBkxjx1rdc9udRrveKvSsJIq80avlXeLHhGwqtA"
57+
const val jwk = """
58+
{
59+
"kty": "EC",
60+
"crv": "P-256",
61+
"x": "DxiH5Q4Yx3UrukE2lWCErq8N8bqC9CHLLrAwLz5BmE0",
62+
"y": "XtLM4-3h5o3HUH0MHVJV0kyq0iBlrBwlh8qEDMZ4-Pc",
63+
"use": "enc",
64+
"alg": "ECDH-ES",
65+
"kid": "1"
66+
}
67+
"""
68+
const val responseUri = "https://example.com/response"
5969

6070
class Openid4VpUtilsTest {
6171

6272
@Test
6373
fun testGenerateOpenId4VpHandover() {
6474
val openid4VpHandover = generateOpenId4VpHandover(
65-
clientId,
66-
responseUri,
67-
nonce,
68-
mdocGeneratedNonce
75+
clientId = clientId,
76+
nonce = nonce,
77+
jwkThumbprint = JWK.parse(jwk).computeThumbprint().decode(),
78+
responseOrRedirectUri = responseUri
6979
).EncodeToBytes()
70-
assertEquals(ANNEX_B_OPENID4VP_HANDOVER, Hex.toHexString(openid4VpHandover).uppercase())
80+
assertEquals(OPENID4VP_1_0_HANDOVER, Hex.toHexString(openid4VpHandover).lowercase())
7181
}
7282

7383
@Test
7484
fun testGenerateSessionTranscript() {
7585
val sessionTranscript = generateSessionTranscript(
76-
clientId,
77-
responseUri,
78-
nonce,
79-
mdocGeneratedNonce
86+
clientId = clientId,
87+
nonce = nonce,
88+
jwkThumbprint = JWK.parse(jwk).computeThumbprint().decode(),
89+
responseOrRedirectUri = responseUri
90+
)
91+
assertEquals(
92+
OPENID4VP_1_0_SESSION_TRANSCRIPT,
93+
Hex.toHexString(sessionTranscript).lowercase()
8094
)
81-
assertEquals(ANNEX_B_SESSION_TRANSCRIPT, Hex.toHexString(sessionTranscript).uppercase())
8295
}
8396

8497
@Test

0 commit comments

Comments
 (0)