diff --git a/README.md b/README.md index 354865f0..a4b8a40c 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ file. ```groovy dependencies { - implementation "eu.europa.ec.eudi:eudi-lib-android-wallet-core:0.5.2-SNAPSHOT" + implementation "eu.europa.ec.eudi:eudi-lib-android-wallet-core:0.5.3-SNAPSHOT" implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05" } ``` diff --git a/gradle.properties b/gradle.properties index 383365d9..7e329b08 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,7 +43,7 @@ systemProp.sonar.gradle.skipCompile=true systemProp.sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.xml,build/reports/jacoco/testReleaseUnitTestCoverage/testReleaseUnitTestCoverage.xml systemProp.sonar.projectName=eudi-lib-android-wallet-core -VERSION_NAME=0.5.2-SNAPSHOT +VERSION_NAME=0.5.3-SNAPSHOT SONATYPE_HOST=S01 SONATYPE_AUTOMATIC_RELEASE=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 076312d9..18cb6c48 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ espresso-core = "3.5.1" eudi-document-manager = "0.2.3-SNAPSHOT" eudi-iso18013-data-transfer = "0.2.0-SNAPSHOT" eudi-lib-jvm-openid4vci-kt = "0.1.2-SNAPSHOT" -eudi-lib-jvm-siop-openid4vp-kt = "0.3.1-SNAPSHOT" +eudi-lib-jvm-siop-openid4vp-kt = "0.3.3" gradle-plugin = "7.4.0" identity-credential = "20231002" identity-credential-android = "20231002" diff --git a/licenses.md b/licenses.md index a5d136c1..6cb7ae37 100644 --- a/licenses.md +++ b/licenses.md @@ -1,7 +1,7 @@ # EUDI Wallet Core library for Android ## Dependency License Report -_2024-03-04 19:48:37 EET_ +_2024-04-04 16:00:54 EEST_ ## Apache License, Version 2.0 **1** **Group:** `androidx.appcompat` **Name:** `appcompat` **Version:** `1.6.1` diff --git a/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4VpCBORResponseGeneratorImpl.kt b/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4VpCBORResponseGeneratorImpl.kt index 0ff96827..2c5f1737 100644 --- a/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4VpCBORResponseGeneratorImpl.kt +++ b/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4VpCBORResponseGeneratorImpl.kt @@ -100,32 +100,37 @@ class OpenId4VpCBORResponseGeneratorImpl(private val documentsResolver: Document * @return [RequestedDocumentData] */ override fun parseRequest(request: OpenId4VpRequest): RequestedDocumentData { - val inputDescriptor = request.presentationDefinition.inputDescriptors.first() - val fieldConstraints = inputDescriptor.constraints.fields() + return createRequestedDocumentData( + request.presentationDefinition.inputDescriptors + .mapNotNull { inputDescriptor -> + inputDescriptor.format?.jsonObject() + ?.takeIf { it.containsKey("mso_mdoc") } // ignore formats other than "mso_mdoc" + ?.run { + inputDescriptor.id.value.trim() to inputDescriptor.constraints.fields() + .mapNotNull { fieldConstraint -> + // path shall contain a requested data element as: $[''][''] + val path = fieldConstraint.paths.first().value + Regex("\\\$\\['(.*?)']\\['(.*?)']").find(path) + ?.let { matchResult -> + val (namespace, elementIdentifier) = matchResult.destructured + if (namespace.isNotBlank() && elementIdentifier.isNotBlank()) { + namespace to elementIdentifier + } else { + null + } + } - // find doc type - val docType = fieldConstraints.find { fieldConstraint -> - fieldConstraint.paths.first().value == "$.mdoc.doctype" - }?.filter?.get("const").toString().replace("\"", "") - - // find namespace - val namespace = fieldConstraints.find { fieldConstraint -> - fieldConstraint.paths.first().value == "$.mdoc.namespace" - }?.filter?.get("const").toString().replace("\"", "") - - // find requested fields - val requestedFields = - fieldConstraints.filter { fieldConstraint -> - fieldConstraint.intentToRetain != null - }.map { fieldConstraint -> - fieldConstraint.paths.first().value.replace( - "$.mdoc.", - "" - ).replace("\"", "") - } - - return parseRequest( - mapOf(docType to mapOf(namespace to requestedFields)), + }.groupBy({ it.first }, { it.second }) + .mapValues { (_, values) -> values.toList() } + .toMap() + } ?: run { + Log.w( + TAG, + "Input descriptor with id ${inputDescriptor.id} and format ${inputDescriptor.format} is skipped. Format is not mso_mdoc." + ) + null + } + }.toMap(), openid4VpX509CertificateTrust.getReaderAuth() ) } @@ -201,7 +206,7 @@ class OpenId4VpCBORResponseGeneratorImpl(private val documentsResolver: Document return AddDocumentToResponse.Success } - private fun parseRequest( + private fun createRequestedDocumentData( requestedFields: Map>>, readerAuth: ReaderAuth? ): RequestedDocumentData { diff --git a/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4vpManager.kt b/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4vpManager.kt index e813de61..7236a737 100644 --- a/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4vpManager.kt +++ b/wallet-core/src/main/java/eu/europa/ec/eudi/wallet/transfer/openid4vp/OpenId4vpManager.kt @@ -34,7 +34,6 @@ import eu.europa.ec.eudi.openid4vp.SiopOpenId4VPConfig import eu.europa.ec.eudi.openid4vp.SiopOpenId4Vp import eu.europa.ec.eudi.openid4vp.SupportedClientIdScheme import eu.europa.ec.eudi.openid4vp.asException -import eu.europa.ec.eudi.prex.ClaimFormat import eu.europa.ec.eudi.prex.DescriptorMap import eu.europa.ec.eudi.prex.Id import eu.europa.ec.eudi.prex.JsonPath @@ -50,6 +49,7 @@ import java.net.URI import java.net.URLDecoder import java.nio.charset.StandardCharsets import java.util.Base64 +import java.util.UUID import java.util.concurrent.Executor /** @@ -261,25 +261,26 @@ class OpenId4vpManager( resolvedRequestObject?.let { resolvedRequestObject -> when (resolvedRequestObject) { is ResolvedRequestObject.OpenId4VPAuthorization -> { - val presentationDefinition = - (resolvedRequestObject).presentationDefinition - val inputDescriptor = - presentationDefinition.inputDescriptors.first() + val vpToken = Base64.getUrlEncoder().withoutPadding().encodeToString(deviceResponse) Log.d(TAG, "VpToken: $vpToken") + + val presentationDefinition = + (resolvedRequestObject).presentationDefinition val consensus = Consensus.PositiveConsensus.VPTokenConsensus( vpToken, presentationSubmission = PresentationSubmission( - id = Id("pid-res"), // TODO id value ? + id = Id(UUID.randomUUID().toString()), definitionId = presentationDefinition.id, - listOf( - DescriptorMap( - id = inputDescriptor.id, - format = ClaimFormat.MsoMdoc, - path = JsonPath.jsonPath("$")!! // TODO path ? - ) - ) + presentationDefinition.inputDescriptors.map { + inputDescriptor -> + DescriptorMap( + inputDescriptor.id, + "mso_mdoc", + path = JsonPath.jsonPath("$")!! + ) + } ) ) runCatching { siopOpenId4Vp.dispatch(resolvedRequestObject, consensus) }.onSuccess { dispatchOutcome ->