Skip to content

Commit bb78f8a

Browse files
committed
[VCI] Support Cred Request encryption
1 parent 0b97df8 commit bb78f8a

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

app/src/main/java/com/credman/cmwallet/openid4vci/OpenId4VCI.kt

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.util.Log
77
import androidx.compose.ui.input.key.Key
88
import com.credman.cmwallet.CmWalletApplication.Companion.TAG
99
import com.credman.cmwallet.createJWTES256
10+
import com.credman.cmwallet.jweSerialization
1011
import com.credman.cmwallet.loadECPrivateKey
1112
import com.credman.cmwallet.openid4vci.data.CredentialOffer
1213
import com.credman.cmwallet.openid4vci.data.CredentialRequest
@@ -37,6 +38,7 @@ import io.ktor.http.parameters
3738
import io.ktor.serialization.kotlinx.json.json
3839
import io.ktor.util.encodeBase64
3940
import kotlinx.coroutines.delay
41+
import kotlinx.serialization.encodeToString
4042
import kotlinx.serialization.json.Json
4143
import kotlinx.serialization.json.JsonArray
4244
import kotlinx.serialization.json.JsonObject
@@ -283,6 +285,19 @@ class OpenId4VCI(val credentialOfferJson: String) {
283285
return result.body()
284286
}
285287

288+
fun requireCredentialRequestEncryption(): Boolean = credentialOffer.issuerMetadata.credentialRequestEncryption?.encryptionRequired ?: false
289+
fun getCredentialRequestEncryptionKey(): JSONObject {
290+
require(credentialOffer.issuerMetadata.credentialRequestEncryption!!.encValuesSupported.contains("A128GCM")) {
291+
"Don't support the credential request encryption method yet"
292+
}
293+
val keys = credentialOffer.issuerMetadata.credentialRequestEncryption.jwks.keys
294+
val key = keys.firstOrNull{
295+
it.alg == "ECDH-ES"
296+
} ?: throw java.lang.UnsupportedOperationException("No supported encryption key")
297+
return JSONObject(Json.encodeToString(key))
298+
}
299+
fun requireCredentialResponseEncryption(): Boolean = credentialOffer.issuerMetadata.credentialResponseEncryption?.encryptionRequired ?: false
300+
286301
@OptIn(ExperimentalUuidApi::class)
287302
suspend fun requestCredentialFromEndpoint(
288303
accessToken: String,
@@ -299,9 +314,18 @@ class OpenId4VCI(val credentialOfferJson: String) {
299314
header(HttpHeaders.Authorization, "Dpop $accessToken")
300315
header("dpop", dpop)
301316

302-
contentType(ContentType.Application.Json)
303-
setBody(json.encodeToJsonElement(credentialRequest))
304-
317+
if (requireCredentialRequestEncryption()) {
318+
contentType(ContentType("application", "jwt"))
319+
setBody(jweSerialization(
320+
recipientKeyJwk = getCredentialRequestEncryptionKey(),
321+
plainText = json.encodeToJsonElement(credentialRequest).toString()
322+
))
323+
} else {
324+
contentType(ContentType.Application.Json)
325+
setBody(
326+
json.encodeToJsonElement(credentialRequest)
327+
)
328+
}
305329
}
306330

307331
if (result.status == HttpStatusCode.Unauthorized) {

app/src/main/java/com/credman/cmwallet/openid4vci/data/CredentialOfferEndpoint.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,29 @@ data class Grants(
2525
@SerialName("authorization_code") val authorizationCode: GrantAuthorizationCode?,
2626
)
2727

28+
@Serializable
29+
data class JwkKey(
30+
@SerialName("kty") val kty: String,
31+
@SerialName("use") val use: String?,
32+
@SerialName("alg") val alg: String?,
33+
@SerialName("kid") val kid: String?,
34+
@SerialName("x") val x: String?,
35+
@SerialName("y") val y: String?,
36+
)
37+
38+
@Serializable
39+
data class Jwks(
40+
@SerialName("keys") val keys: List<JwkKey>,
41+
)
42+
43+
44+
@Serializable
45+
data class CredentialRequestEncryption(
46+
@SerialName("jwks") val jwks: Jwks,
47+
@SerialName("enc_values_supported") val encValuesSupported: List<String>,
48+
@SerialName("encryption_required") val encryptionRequired: Boolean
49+
)
50+
2851
@Serializable
2952
data class CredentialResponseEncryption(
3053
@SerialName("alg_values_supported") val algValuesSupported: List<String>,
@@ -170,6 +193,7 @@ data class CredentialIssuerMetadata(
170193
@SerialName("nonce_endpoint") val nonceEndpoint: String?,
171194
@SerialName("deferred_credential_endpoint") val deferredCredentialEndpoint: String?,
172195
@SerialName("notification_endpoint") val notificationEndpoint: String?,
196+
@SerialName("credential_request_encryption") val credentialRequestEncryption: CredentialRequestEncryption?,
173197
@SerialName("credential_response_encryption") val credentialResponseEncryption: CredentialResponseEncryption?,
174198
@SerialName("batch_credential_issuance") val batchCredentialIssuance: BatchCredentialIssuance?,
175199
@SerialName("signed_metadata") val signedMetadata: String?,

0 commit comments

Comments
 (0)