From 3c59ee35894d13c8e3fd27b534244d9223b84448 Mon Sep 17 00:00:00 2001 From: Cristian G Date: Mon, 16 Sep 2024 14:27:41 -0400 Subject: [PATCH] feat: connectionless credential offer Signed-off-by: Cristian G --- .editorconfig | 3 + .github/CODEOWNERS | 2 +- .github/workflows/e2e-tests.yml | 4 +- .github/workflows/release.yml | 2 +- README.md | 2 +- build.gradle.kts | 113 +---- edge-agent-sdk/build.gradle.kts | 115 ++++- .../walletsdk/edgeagent/AnoncredsTests.kt | 10 +- .../walletsdk/pluto/data/DbConnectionImpl.kt | 6 +- .../identus/walletsdk/apollo/ApolloImpl.kt | 9 +- .../identus/walletsdk/castor/CastorImpl.kt | 6 +- .../walletsdk/castor/shared/CastorShared.kt | 4 +- .../walletsdk/domain/buildingblocks/Pollux.kt | 4 +- .../walletsdk/edgeagent/ConnectionManager.kt | 5 +- .../identus/walletsdk/edgeagent/EdgeAgent.kt | 409 ++++++++++++++---- .../mediation/BasicMediatorHandler.kt | 7 +- .../models/ConnectionlessMessageData.kt | 11 + .../issueCredential/CredentialPreview.kt | 2 +- .../protocols/pickup/PickupRunner.kt | 32 +- .../logger/{PrismLogger.kt => Logger.kt} | 44 +- .../identus/walletsdk/mercury/MercuryImpl.kt | 6 +- .../mercury/resolvers/DIDCommWrapper.kt | 5 +- .../identus/walletsdk/pluto/PlutoImpl.kt | 8 +- .../identus/walletsdk/pollux/PolluxImpl.kt | 67 ++- .../walletsdk/pollux/models/AnonCredential.kt | 7 +- .../edgeagent/ConnectionManagerTest.kt | 157 +++---- .../walletsdk/edgeagent/EdgeAgentTests.kt | 130 ++++-- .../identus/walletsdk/edgeagent/PolluxMock.kt | 4 +- .../{PrismLoggerMock.kt => LoggerMock.kt} | 2 +- .../identus/walletsdk/mercury/MercuryTests.kt | 4 +- .../walletsdk/pluto/BackupRestorationTests.kt | 5 +- .../identus/walletsdk/pluto/PlutoImplTest.kt | 339 +++++++-------- .../walletsdk/pollux/PolluxImplTest.kt | 37 +- .../walletsdk/pluto/data/DbConnectionImpl.kt | 1 + gradle.properties | 2 +- .../identus/walletsdk/sampleapp/Sdk.kt | 2 +- tests/end-to-end/README.md | 40 +- tests/end-to-end/build.gradle.kts | 12 +- .../identus/walletsdk/TestSuite.kt | 2 +- .../walletsdk/abilities/UseWalletSdk.kt | 141 ++++-- .../configuration/DbConnectionInMemory.kt | 17 + .../walletsdk/configuration/Environment.kt | 8 +- .../walletsdk/configuration/HooksSetup.kt | 11 + .../walletsdk/steps/CloudAgentSteps.kt | 10 + .../identus/walletsdk/steps/EdgeAgentSteps.kt | 131 +++++- .../walletsdk/workflow/CloudAgentWorkflow.kt | 55 ++- .../walletsdk/workflow/EdgeAgentWorkflow.kt | 168 ++++++- .../resources/features/backup/Backup.feature | 25 ++ .../anoncred/PresentProof.feature} | 8 + .../Receive.feature} | 0 .../credential/anoncred/Verify.feature | 10 + .../jwt/PresentProof.feature} | 0 .../Receive.feature} | 4 +- .../{RevokeJWT.feature => jwt/Revoke.feature} | 0 .../features/credential/jwt/Verify.feature | 18 + 55 files changed, 1528 insertions(+), 698 deletions(-) create mode 100644 edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/models/ConnectionlessMessageData.kt rename edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/{PrismLogger.kt => Logger.kt} (92%) rename edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/{PrismLoggerMock.kt => LoggerMock.kt} (97%) create mode 100644 tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/DbConnectionInMemory.kt create mode 100644 tests/end-to-end/src/test/resources/features/backup/Backup.feature rename tests/end-to-end/src/test/resources/features/{present-proof/AnoncredPresentProof.feature => credential/anoncred/PresentProof.feature} (54%) rename tests/end-to-end/src/test/resources/features/credential/{Anoncred.feature => anoncred/Receive.feature} (100%) create mode 100644 tests/end-to-end/src/test/resources/features/credential/anoncred/Verify.feature rename tests/end-to-end/src/test/resources/features/{present-proof/JWTPresentProof.feature => credential/jwt/PresentProof.feature} (100%) rename tests/end-to-end/src/test/resources/features/credential/{Credential.feature => jwt/Receive.feature} (88%) rename tests/end-to-end/src/test/resources/features/credential/{RevokeJWT.feature => jwt/Revoke.feature} (100%) create mode 100644 tests/end-to-end/src/test/resources/features/credential/jwt/Verify.feature diff --git a/.editorconfig b/.editorconfig index 90ef09fdf..d9fa9912c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,9 @@ root = true [*.{kt,kts}] +ij_kotlin_packages_to_use_import_on_demand = unset +ij_kotlin_name_count_to_use_star_import_for_members = 99 +ij_kotlin_name_count_to_use_star_import = 99 ktlint_experimental = disabled ktlint_code_style = intellij_idea ktlint_standard_no_semi = disabled diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bed0bb858..6b96d4eb8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -@goncalo-frade-iohk @yshyn-iohk @elribonazo @cristianIOHK +@elribonazo @cristianIOHK # Test related: /tests/ @amagyar-iohk diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 053415c96..7970bbd7f 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -13,7 +13,7 @@ on: required: true description: Mediator out-of-band url default: https://beta-mediator.atalaprism.io/invitationOOB - prismAgentUrl: + agentUrl: required: true description: Prism-agent server url default: https://sit-prism-agent-issuer.atalaprism.io/prism-agent/ @@ -74,7 +74,7 @@ jobs: - name: Run tests env: MEDIATOR_OOB_URL: ${{ inputs.mediatorOobUrl || vars.MEDIATOR_OOB_URL }} - PRISM_AGENT_URL: ${{ inputs.prismAgentUrl || vars.PRISM_AGENT_URL }} + AGENT_URL: ${{ inputs.agentUrl || vars.AGENT_URL }} PUBLISHED_DID: ${{ inputs.publishedDid || vars.PUBLISHED_DID }} JWT_SCHEMA_GUID: ${{ inputs.jwtSchemaGuid || vars.SCHEMA_ID }} ANONCRED_DEFINITION_GUID: ${{ inputs.anoncredDefinitionGuid || vars.ANONCRED_DEFINITION_GUID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 614be8850..36a26bd86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: token: ${{ secrets.IDENTUS_CI }} fetch-depth: 0 diff --git a/README.md b/README.md index 0f74844a2..299e354b6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The complete platform is separated into multiple repositories: * [edge-agent-sdk-swift](https://github.com/hyperledger/identus-edge-agent-sdk-swift/) - Repo that implements Edge Agent for Apple platforms in Swift. * [edge-agent-sdk-ts](https://github.com/hyperledger/identus-edge-agent-sdk-ts/) - Repo that implements Edge Agent for Browser and Node.js platforms in Typescript. * [identus-cloud-agent](https://github.com/hyperledger/identus-cloud-agent/) - Repo that contains the platform Building Blocks. -* [mediator](https://github.com/hyperledger/identus-mediator/) - Repo for DIDComm V2 Mediator +* [mediator](https://github.com/hyperledger/identus-mediator/) - Repo for DIDComm V2 Mediator. ### Modules / APIs diff --git a/build.gradle.kts b/build.gradle.kts index f596107bc..3a2ecba9e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ import org.gradle.internal.os.OperatingSystem -val publishedMavenId = "org.hyperledger.identus" +val groupId = "org.hyperledger.identus" val os: OperatingSystem = OperatingSystem.current() plugins { @@ -10,8 +10,6 @@ plugins { id("org.jlleitschuh.gradle.ktlint") version "12.1.0" id("org.jetbrains.dokka") version "1.9.20" id("org.jetbrains.kotlin.kapt") version "1.9.10" - id("maven-publish") - id("signing") id("io.github.gradle-nexus.publish-plugin") version "2.0.0" } @@ -37,7 +35,7 @@ java { } allprojects { - this.group = publishedMavenId + this.group = groupId repositories { mavenLocal() @@ -101,118 +99,13 @@ subprojects { } } } - - if (this.name == "edge-agent-sdk") { - apply(plugin = "org.gradle.maven-publish") - apply(plugin = "org.gradle.signing") - - publishing { - repositories { - maven { - name = "OSSRH" - url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") - credentials { - username = - project.findProperty("sonatypeUsername") as String? ?: System.getenv("OSSRH_USERNAME") - password = project.findProperty("sonatypePassword") as String? ?: System.getenv("OSSRH_TOKEN") - } - } - } - publications { - withType { - groupId = publishedMavenId - artifactId = project.name - version = project.version.toString() - pom { - name.set("Edge Agent SDK") - description.set(" Edge Agent SDK - Kotlin Multiplatform (Android/JVM)") - url.set("https://docs.atalaprism.io/") - organization { - name.set("Hyperledger") - url.set("https://hyperledger.org/") - } - issueManagement { - system.set("Github") - url.set("https://github.com/hyperledger/identus-edge-agent-sdk-kmp") - } - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - developers { - developer { - id.set("cristianIOHK") - name.set("Cristian Gonzalez") - email.set("cristian.castro@iohk.io") - organization.set("IOG") - roles.add("developer") - url.set("https://github.com/cristianIOHK") - } - developer { - id.set("hamada147") - name.set("Ahmed Moussa") - email.set("ahmed.moussa@iohk.io") - organization.set("IOG") - roles.add("developer") - url.set("https://github.com/hamada147") - } - developer { - id.set("elribonazo") - name.set("Javier Ribó") - email.set("javier.ribo@iohk.io") - organization.set("IOG") - roles.add("developer") - } - developer { - id.set("amagyar-iohk") - name.set("Allain Magyar") - email.set("allain.magyar@iohk.io") - organization.set("IOG") - roles.add("qc") - } - developer { - id.set("antonbaliasnikov") - name.set("Anton Baliasnikov") - email.set("anton.baliasnikov@iohk.io") - organization.set("IOG") - roles.add("qc") - } - developer { - id.set("goncalo-frade-iohk") - name.set("Gonçalo Frade") - email.set("goncalo.frade@iohk.io") - organization.set("IOG") - roles.add("developer") - } - } - scm { - connection.set("scm:git:git://hyperledger/identus-edge-agent-sdk-kmp.git") - developerConnection.set("scm:git:ssh://hyperledger/identus-edge-agent-sdk-kmp.git") - url.set("https://github.com/hyperledger/identus-edge-agent-sdk-kmp") - } - } - signing { - useInMemoryPgpKeys( - project.findProperty("signing.signingSecretKey") as String? - ?: System.getenv("OSSRH_GPG_SECRET_KEY"), - project.findProperty("signing.signingSecretKeyPassword") as String? - ?: System.getenv("OSSRH_GPG_SECRET_KEY_PASSWORD") - ) - sign(this@withType) - } - } - } - } - } } nexusPublishing { repositories { sonatype { nexusUrl.set(uri("https://oss.sonatype.org/service/local/")) - snapshotRepositoryUrl.set(uri("https://oss.sonatype.org/content/repositories/snapshots/")) + snapshotRepositoryUrl.set(uri("https://oss.sonatype.org/content/repositories/releases/")) username.set(System.getenv("OSSRH_USERNAME")) password.set(System.getenv("OSSRH_TOKEN")) } diff --git a/edge-agent-sdk/build.gradle.kts b/edge-agent-sdk/build.gradle.kts index 291b36ff3..98da89c29 100644 --- a/edge-agent-sdk/build.gradle.kts +++ b/edge-agent-sdk/build.gradle.kts @@ -17,6 +17,107 @@ plugins { id("com.android.library") id("org.jetbrains.dokka") id("org.jetbrains.kotlinx.kover") version "0.7.6" + id("org.gradle.maven-publish") + id("org.gradle.signing") +} + +publishing { + repositories { + maven { + name = "OSSRH" + url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") + credentials { + username = System.getenv("OSSRH_USERNAME") + password = System.getenv("OSSRH_TOKEN") + } + } + } + publications { + withType { + artifactId = project.name + version = project.version.toString() + pom { + name.set("Edge Agent SDK") + description.set(" Edge Agent SDK - Kotlin Multiplatform (Android/JVM)") + url.set("https://docs.atalaprism.io/") + organization { + name.set("Hyperledger") + url.set("https://hyperledger.org/") + } + issueManagement { + system.set("Github") + url.set("https://github.com/hyperledger/identus-edge-agent-sdk-kmp") + } + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("cristianIOHK") + name.set("Cristian Gonzalez") + email.set("cristian.castro@iohk.io") + organization.set("IOG") + roles.add("developer") + url.set("https://github.com/cristianIOHK") + } + developer { + id.set("hamada147") + name.set("Ahmed Moussa") + email.set("ahmed.moussa@iohk.io") + organization.set("IOG") + roles.add("developer") + url.set("https://github.com/hamada147") + } + developer { + id.set("elribonazo") + name.set("Javier Ribó") + email.set("javier.ribo@iohk.io") + organization.set("IOG") + roles.add("developer") + } + developer { + id.set("amagyar-iohk") + name.set("Allain Magyar") + email.set("allain.magyar@iohk.io") + organization.set("IOG") + roles.add("qc") + } + developer { + id.set("antonbaliasnikov") + name.set("Anton Baliasnikov") + email.set("anton.baliasnikov@iohk.io") + organization.set("IOG") + roles.add("qc") + } + developer { + id.set("goncalo-frade-iohk") + name.set("Gonçalo Frade") + email.set("goncalo.frade@iohk.io") + organization.set("IOG") + roles.add("developer") + } + } + scm { + connection.set("scm:git:git://hyperledger/identus-edge-agent-sdk-kmp.git") + developerConnection.set("scm:git:ssh://hyperledger/identus-edge-agent-sdk-kmp.git") + url.set("https://github.com/hyperledger/identus-edge-agent-sdk-kmp") + } + } + } + } +} + +if (System.getenv().containsKey("OSSRH_GPG_SECRET_KEY")) { + signing { + useInMemoryPgpKeys( + System.getenv("OSSRH_GPG_SECRET_KEY"), + System.getenv("OSSRH_GPG_SECRET_KEY_PASSWORD") + ) + sign(publishing.publications) + } } kover { @@ -80,9 +181,6 @@ kotlin { jvmTarget = "17" } } - testRuns["test"].executionTask.configure { - useJUnitPlatform() - } publishing { publications { withType { @@ -145,10 +243,9 @@ kotlin { } val commonTest by getting { dependencies { - implementation(kotlin("test")) + implementation(kotlin("test-junit")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") implementation("io.ktor:ktor-client-mock:2.3.11") - implementation("junit:junit:4.13.2") implementation("org.mockito:mockito-core:4.4.0") implementation("org.mockito.kotlin:mockito-kotlin:4.0.0") } @@ -172,10 +269,8 @@ kotlin { val androidInstrumentedTest by getting { dependencies { dependsOn(commonTest) - implementation(kotlin("test")) implementation("androidx.test.espresso:espresso-core:3.5.1") implementation("androidx.test.ext:junit:1.1.5") - implementation("junit:junit:4.13.2") } } /* @@ -285,6 +380,12 @@ val buildProtoLibsGen: Task by tasks.creating { } afterEvaluate { + tasks.withType { + dependsOn(tasks.withType()) + } + tasks.withType { + dependsOn(tasks.withType()) + } tasks.getByName("runKtlintCheckOverCommonMainSourceSet") { dependsOn(buildProtoLibsGen) } diff --git a/edge-agent-sdk/src/androidInstrumentedTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AnoncredsTests.kt b/edge-agent-sdk/src/androidInstrumentedTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AnoncredsTests.kt index 837c7d2a2..b2c61a84d 100644 --- a/edge-agent-sdk/src/androidInstrumentedTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AnoncredsTests.kt +++ b/edge-agent-sdk/src/androidInstrumentedTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/AnoncredsTests.kt @@ -40,7 +40,7 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.issueCredential.Off import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmission import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation import org.hyperledger.identus.walletsdk.domain.models.KeyValue -import org.hyperledger.identus.walletsdk.logger.PrismLoggerMock +import org.hyperledger.identus.walletsdk.logger.LoggerMock import org.hyperledger.identus.walletsdk.mercury.ApiMock import org.hyperledger.identus.walletsdk.pollux.PolluxImpl import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential @@ -114,7 +114,7 @@ class AnoncredsTests { connectionManager = connectionManager, seed = null, api = ApiMock(statusCode = HttpStatusCode.OK, response = "{\"success\":\"true\"}"), - logger = PrismLoggerMock() + logger = LoggerMock() ) val attachmentDescriptor = @@ -192,7 +192,7 @@ class AnoncredsTests { connectionManager = connectionManager, seed = null, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock() + logger = LoggerMock() ) val attachmentDescriptor = @@ -263,7 +263,7 @@ class AnoncredsTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val credential = AnonCredential.fromStorableData( @@ -355,7 +355,7 @@ class AnoncredsTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) ) diff --git a/edge-agent-sdk/src/androidMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt b/edge-agent-sdk/src/androidMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt index 01fab6a2a..8b72c4a9d 100644 --- a/edge-agent-sdk/src/androidMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt +++ b/edge-agent-sdk/src/androidMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt @@ -24,10 +24,10 @@ actual class DbConnectionImpl actual constructor() : DbConnection { */ actual val SqlDriver.isConnected: Boolean get() { - try { - return this.execute(null, "SELECT 1", 0).value == 1L + return try { + this.execute(null, "SELECT 1", 0).value == 0L // return this.executeQuery(null, "SELECT 1", 0).next() } catch (ex: Exception) { - return false + false } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt index 65dcbe168..11c67a738 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/apollo/ApolloImpl.kt @@ -40,11 +40,17 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.SeedKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorablePrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey +import org.hyperledger.identus.walletsdk.logger.LogComponent +import org.hyperledger.identus.walletsdk.logger.LogLevel +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl /** * Apollo defines the set of cryptographic operations that are used in the Atala PRISM. */ -class ApolloImpl : Apollo { +class ApolloImpl( + val logger: Logger = LoggerImpl(LogComponent.APOLLO) +) : Apollo { /** * Creates a random set of mnemonic phrases that can be used as a seed for generating a private key. @@ -52,6 +58,7 @@ class ApolloImpl : Apollo { * @return An array of mnemonic phrases. */ override fun createRandomMnemonics(): Array { + LogComponent.APOLLO.logLevel = LogLevel.INFO return MnemonicHelper.createRandomMnemonics().toTypedArray() } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/CastorImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/CastorImpl.kt index 254df2860..67a348b62 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/CastorImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/CastorImpl.kt @@ -24,8 +24,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.LogLevel import org.hyperledger.identus.walletsdk.logger.Metadata -import org.hyperledger.identus.walletsdk.logger.PrismLogger -import org.hyperledger.identus.walletsdk.logger.PrismLoggerImpl +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl /** * Castor is a powerful and flexible library for working with DIDs. Whether you are building a decentralised application @@ -36,7 +36,7 @@ class CastorImpl @JvmOverloads constructor( val apollo: Apollo, - private val logger: PrismLogger = PrismLoggerImpl(LogComponent.CASTOR) + private val logger: Logger = LoggerImpl(LogComponent.CASTOR) ) : Castor { var resolvers: Array = arrayOf( LongFormPrismDIDResolver(this.apollo), diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/shared/CastorShared.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/shared/CastorShared.kt index 8cd2a21fd..bfc976a57 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/shared/CastorShared.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/castor/shared/CastorShared.kt @@ -44,8 +44,8 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.KeyPair import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.LogLevel +import org.hyperledger.identus.walletsdk.logger.LoggerImpl import org.hyperledger.identus.walletsdk.logger.Metadata -import org.hyperledger.identus.walletsdk.logger.PrismLoggerImpl import org.kotlincrypto.hash.sha2.SHA256 import pbandk.decodeFromByteArray import pbandk.encodeToByteArray @@ -64,7 +64,7 @@ internal class CastorShared { * * @property logger The logger instance. */ - private val logger = PrismLoggerImpl(LogComponent.CASTOR) + private val logger = LoggerImpl(LogComponent.CASTOR) /** * parseDID parses a string representation of a Decentralized Identifier (DID) into a DID object. diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt index 5d41da2fd..2d60e06e6 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/domain/buildingblocks/Pollux.kt @@ -49,7 +49,7 @@ interface Pollux { * @param offerJson The JSON object representing the credential offer. * @return The string representation of the processed result. */ - fun processCredentialRequestJWT( + suspend fun processCredentialRequestJWT( subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject @@ -63,7 +63,7 @@ interface Pollux { * @param offerJson The JSON object representing the credential offer. * @return The string representation of the processed result. */ - fun processCredentialRequestSDJWT( + suspend fun processCredentialRequestSDJWT( subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManager.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManager.kt index 00c53908e..e7f38fbcf 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManager.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManager.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import org.hyperledger.identus.apollo.base64.base64UrlDecoded import org.hyperledger.identus.walletsdk.domain.buildingblocks.Castor @@ -25,7 +26,6 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType import org.hyperledger.identus.walletsdk.edgeagent.protocols.issueCredential.IssueCredential import org.hyperledger.identus.walletsdk.edgeagent.protocols.revocation.RevocationNotification import java.time.Duration -import kotlin.jvm.Throws interface ConnectionManager : ConnectionsManager, DIDCommConnection { @@ -106,10 +106,9 @@ class ConnectionManagerImpl( } } } - // Fallback mechanism if no WebSocket service endpoint is available if (serviceEndpoint == null) { - while (true) { + while (this.isActive) { // Continuously await and process new messages awaitMessages().collect { array -> processMessages(array) diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt index 7f3005358..4cb78b267 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgent.kt @@ -60,8 +60,8 @@ import org.hyperledger.identus.walletsdk.domain.buildingblocks.Pollux import org.hyperledger.identus.walletsdk.domain.models.Api import org.hyperledger.identus.walletsdk.domain.models.ApiImpl import org.hyperledger.identus.walletsdk.domain.models.ApolloError +import org.hyperledger.identus.walletsdk.domain.models.AttachmentData import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentBase64 -import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentJsonData import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor import org.hyperledger.identus.walletsdk.domain.models.Credential import org.hyperledger.identus.walletsdk.domain.models.CredentialOperationsOptions @@ -93,6 +93,7 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey import org.hyperledger.identus.walletsdk.edgeagent.helpers.AgentOptions import org.hyperledger.identus.walletsdk.edgeagent.mediation.BasicMediatorHandler import org.hyperledger.identus.walletsdk.edgeagent.mediation.MediationHandler +import org.hyperledger.identus.walletsdk.edgeagent.models.ConnectionlessMessageData import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType import org.hyperledger.identus.walletsdk.edgeagent.protocols.connection.DIDCommConnectionRunner import org.hyperledger.identus.walletsdk.edgeagent.protocols.findProtocolTypeByValue @@ -111,8 +112,8 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.Metadata -import org.hyperledger.identus.walletsdk.logger.PrismLogger -import org.hyperledger.identus.walletsdk.logger.PrismLoggerImpl +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl import org.hyperledger.identus.walletsdk.pluto.PlutoBackupTask import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask import org.hyperledger.identus.walletsdk.pluto.models.backup.BackupV0_0_1 @@ -157,7 +158,7 @@ open class EdgeAgent { private val edgeAgentScope: CoroutineScope = CoroutineScope(Dispatchers.Default) private val api: Api internal var connectionManager: ConnectionManager - private var logger: PrismLogger + private var logger: Logger private val agentOptions: AgentOptions /** @@ -185,7 +186,7 @@ open class EdgeAgent { connectionManager: ConnectionManager, seed: Seed?, api: Api?, - logger: PrismLogger = PrismLoggerImpl(LogComponent.PRISM_AGENT), + logger: Logger = LoggerImpl(LogComponent.EDGE_AGENT), agentOptions: AgentOptions = AgentOptions() ) { edgeAgentScope.launch { @@ -239,7 +240,7 @@ open class EdgeAgent { seed: Seed? = null, api: Api? = null, mediatorHandler: MediationHandler, - logger: PrismLogger = PrismLoggerImpl(LogComponent.PRISM_AGENT), + logger: Logger = LoggerImpl(LogComponent.EDGE_AGENT), agentOptions: AgentOptions = AgentOptions() ) { edgeAgentScope.launch { @@ -654,10 +655,7 @@ open class EdgeAgent { KeyCurve(Curve.SECP256K1, privateKeyKeyPath) ) val offerDataString = offer.attachments.firstNotNullOf { - when (it.data) { - is AttachmentJsonData -> it.data.data - else -> null - } + it.data.getDataAsJsonString() } val offerJsonObject = Json.parseToJsonElement(offerDataString).jsonObject val jwtString = @@ -922,77 +920,148 @@ open class EdgeAgent { } val jsonString = invitation.attachments.firstNotNullOf { it.data.getDataAsJsonString() } - val requestPresentationJson = Json.parseToJsonElement(jsonString).jsonObject - if (!requestPresentationJson.containsKey("id")) { - throw EdgeAgentError.MissingOrNullFieldError("id", "Request") - } - if (!requestPresentationJson.containsKey("body")) { - throw EdgeAgentError.MissingOrNullFieldError("body", "Request") - } - if (!requestPresentationJson.containsKey("attachments")) { - throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") - } - if (!requestPresentationJson.containsKey("thid")) { - throw EdgeAgentError.MissingOrNullFieldError("thid", "Request") - } - if (!requestPresentationJson.containsKey("from")) { - throw EdgeAgentError.MissingOrNullFieldError("from", "Request") - } + connectionlessInvitation(ownDID, jsonString) + } else { + // Regular OOB invitation + val pair = DIDCommConnectionRunner(invitation, pluto, ownDID, connectionManager).run() + connectionManager.addConnection(pair) + } + } - val requestId = requestPresentationJson["id"]!! - val requestBody = requestPresentationJson["body"]!! - val requestAttachments = requestPresentationJson["attachments"]!! - val requestThid = requestPresentationJson["thid"]!! - val requestFrom = requestPresentationJson["from"]!! +<<<<<<< HEAD + private fun handleConnectionlessCredentialOffer(did: DID, credentialOfferJson: JsonObject) { + if (!credentialOfferJson.containsKey("id")) { + throw EdgeAgentError.MissingOrNullFieldError("id", "Request") + } + if (!credentialOfferJson.containsKey("body")) { + throw EdgeAgentError.MissingOrNullFieldError("body", "Request") + } + if (!credentialOfferJson.containsKey("attachments")) { + throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") + } + if (!credentialOfferJson.containsKey("thid")) { + throw EdgeAgentError.MissingOrNullFieldError("thid", "Request") + } + if (!credentialOfferJson.containsKey("from")) { + throw EdgeAgentError.MissingOrNullFieldError("from", "Request") + } - if (requestAttachments.jsonArray.size == 0) { - throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") - } - val attachmentJsonObject = requestAttachments.jsonArray[0] - if (!attachmentJsonObject.jsonObject.containsKey("id")) { - throw EdgeAgentError.MissingOrNullFieldError("id", "Request attachments") - } - if (!attachmentJsonObject.jsonObject.containsKey("media_type")) { - throw EdgeAgentError.MissingOrNullFieldError("media_type", "Request attachments") - } - if (!attachmentJsonObject.jsonObject.containsKey("data")) { - if (!attachmentJsonObject.jsonObject["data"]!!.jsonObject.containsKey("json")) { - throw EdgeAgentError.MissingOrNullFieldError("json", "Request attachments data") - } - throw EdgeAgentError.MissingOrNullFieldError("data", "Request attachments") - } - if (!attachmentJsonObject.jsonObject.containsKey("format")) { - throw EdgeAgentError.MissingOrNullFieldError("format", "Request attachments") + val offerId = credentialOfferJson["id"]!! + val offerBody = credentialOfferJson["body"]!! + val offerAttachment = credentialOfferJson["attachments"]!! + val offerThid = credentialOfferJson["thid"]!! + val offerFrom = credentialOfferJson["from"]!! + + if (offerAttachment.jsonArray.size == 0) { + throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") + } + val attachmentJsonObject = offerAttachment.jsonArray[0] + if (!attachmentJsonObject.jsonObject.containsKey("id")) { + throw EdgeAgentError.MissingOrNullFieldError("id", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("media_type")) { + throw EdgeAgentError.MissingOrNullFieldError("media_type", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("data")) { + if (!attachmentJsonObject.jsonObject["data"]!!.jsonObject.containsKey("json")) { + throw EdgeAgentError.MissingOrNullFieldError("json", "Request attachments data") } - val attachmentId = attachmentJsonObject.jsonObject["id"]!! - val attachmentMediaType = attachmentJsonObject.jsonObject["media_type"]!! - val attachmentData = attachmentJsonObject.jsonObject["data"]!!.jsonObject["json"]!! - val attachmentFormat = attachmentJsonObject.jsonObject["format"]!! - - val attachmentDescriptor = AttachmentDescriptor( - id = attachmentId.jsonPrimitive.content, - mediaType = attachmentMediaType.jsonPrimitive.content, - data = AttachmentJsonData(attachmentData.toString()), - format = attachmentFormat.jsonPrimitive.content - ) + throw EdgeAgentError.MissingOrNullFieldError("data", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("format")) { + throw EdgeAgentError.MissingOrNullFieldError("format", "Request attachments") + } + val attachmentId = attachmentJsonObject.jsonObject["id"]!! + val attachmentMediaType = attachmentJsonObject.jsonObject["media_type"]!! + val attachmentData = attachmentJsonObject.jsonObject["data"]!!.jsonObject["json"]!! + val attachmentFormat = attachmentJsonObject.jsonObject["format"]!! + + val attachmentDescriptor = AttachmentDescriptor( + id = attachmentId.jsonPrimitive.content, + mediaType = attachmentMediaType.jsonPrimitive.content, + data = AttachmentData.AttachmentJsonData(attachmentData.toString()), + format = attachmentFormat.jsonPrimitive.content + ) - val requestPresentation = RequestPresentation( - id = requestId.jsonPrimitive.content, - body = Json.decodeFromString(requestBody.jsonObject.toString()), - attachments = arrayOf(attachmentDescriptor), - thid = requestThid.jsonPrimitive.content, - from = DID(requestFrom.jsonPrimitive.content), - to = ownDID - ) + val offerCredential = OfferCredential( + id = offerId.jsonPrimitive.content, + body = Json.decodeFromString(offerBody.jsonObject.toString()), + attachments = arrayOf(attachmentDescriptor), + thid = offerThid.jsonPrimitive.content, + from = DID(offerFrom.jsonPrimitive.content), + to = did + ) + pluto.storeMessage(offerCredential.makeMessage()) + } - pluto.storeMessage(requestPresentation.makeMessage()) - } else { - // Regular OOB invitation - val pair = DIDCommConnectionRunner(invitation, pluto, ownDID, connectionManager).run() - connectionManager.addConnection(pair) + private fun handleConnectionlessRequestPresentation(did: DID, requestPresentationJson: JsonObject) { + if (!requestPresentationJson.containsKey("id")) { + throw EdgeAgentError.MissingOrNullFieldError("id", "Request") + } + if (!requestPresentationJson.containsKey("body")) { + throw EdgeAgentError.MissingOrNullFieldError("body", "Request") + } + if (!requestPresentationJson.containsKey("attachments")) { + throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") + } + if (!requestPresentationJson.containsKey("thid")) { + throw EdgeAgentError.MissingOrNullFieldError("thid", "Request") } + if (!requestPresentationJson.containsKey("from")) { + throw EdgeAgentError.MissingOrNullFieldError("from", "Request") + } + + val requestId = requestPresentationJson["id"]!! + val requestBody = requestPresentationJson["body"]!! + val requestAttachments = requestPresentationJson["attachments"]!! + val requestThid = requestPresentationJson["thid"]!! + val requestFrom = requestPresentationJson["from"]!! + + if (requestAttachments.jsonArray.size == 0) { + throw EdgeAgentError.MissingOrNullFieldError("attachments", "Request") + } + val attachmentJsonObject = requestAttachments.jsonArray[0] + if (!attachmentJsonObject.jsonObject.containsKey("id")) { + throw EdgeAgentError.MissingOrNullFieldError("id", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("media_type")) { + throw EdgeAgentError.MissingOrNullFieldError("media_type", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("data")) { + if (!attachmentJsonObject.jsonObject["data"]!!.jsonObject.containsKey("json")) { + throw EdgeAgentError.MissingOrNullFieldError("json", "Request attachments data") + } + throw EdgeAgentError.MissingOrNullFieldError("data", "Request attachments") + } + if (!attachmentJsonObject.jsonObject.containsKey("format")) { + throw EdgeAgentError.MissingOrNullFieldError("format", "Request attachments") + } + val attachmentId = attachmentJsonObject.jsonObject["id"]!! + val attachmentMediaType = attachmentJsonObject.jsonObject["media_type"]!! + val attachmentData = attachmentJsonObject.jsonObject["data"]!!.jsonObject["json"]!! + val attachmentFormat = attachmentJsonObject.jsonObject["format"]!! + + val attachmentDescriptor = AttachmentDescriptor( + id = attachmentId.jsonPrimitive.content, + mediaType = attachmentMediaType.jsonPrimitive.content, + data = AttachmentData.AttachmentJsonData(attachmentData.toString()), + format = attachmentFormat.jsonPrimitive.content + ) + + val requestPresentation = RequestPresentation( + id = requestId.jsonPrimitive.content, + body = Json.decodeFromString(requestBody.jsonObject.toString()), + attachments = arrayOf(attachmentDescriptor), + thid = requestThid.jsonPrimitive.content, + from = DID(requestFrom.jsonPrimitive.content), + to = did + ) + + pluto.storeMessage(requestPresentation.makeMessage()) } +======= +>>>>>>> cb3760275 (feat: connectionless credential offer) /** * Accepts a Prism Onboarding invitation and performs the onboarding process * @param invitation The Prism Onboarding invitation to accept @@ -1057,12 +1126,9 @@ open class EdgeAgent { if (format != CredentialType.ANONCREDS_PROOF_REQUEST) { throw EdgeAgentError.InvalidCredentialFormatError(CredentialType.ANONCREDS_PROOF_REQUEST) } - val requestData = request.attachments.mapNotNull { - when (it.data) { - is AttachmentJsonData -> it.data.data - else -> null - } - }.first() + val requestData = request.attachments.firstNotNullOf { + it.data.getDataAsJsonString() + } val linkSecret = getLinkSecret() try { presentationString = credential.presentation( @@ -1093,10 +1159,7 @@ open class EdgeAgent { KeyCurve(Curve.SECP256K1, privateKeyKeyPath) ) val requestData = request.attachments.firstNotNullOf { - when (it.data) { - is AttachmentJsonData -> it.data.data - else -> null - } + it.data.getDataAsJsonString() } try { presentationString = credential.presentation( @@ -1113,15 +1176,12 @@ open class EdgeAgent { } CredentialType.SDJWT.type -> { - val requestData = request.attachments.mapNotNull { - when (it.data) { - is AttachmentJsonData -> it.data.data - else -> null - } - }.first().encodeToByteArray() + val requestData = request.attachments.firstNotNullOf { + it.data.getDataAsJsonString() + } try { presentationString = credential.presentation( - requestData, + requestData.encodeToByteArray(), listOf(CredentialOperationsOptions.DisclosingClaims(listOf(credential.claims.toString()))) ) } catch (e: Exception) { @@ -1194,7 +1254,7 @@ open class EdgeAgent { type = type, presentationClaims = presentationClaims, options = AnoncredsPresentationOptions( - nonce = generateNonce() + nonce = generateNumericNonce() ) ) attachmentDescriptor = AttachmentDescriptor( @@ -1244,6 +1304,7 @@ open class EdgeAgent { ?: throw EdgeAgentError.CannotFindDIDPrivateKey(didString) val privateKey = apollo.restorePrivateKey(storablePrivateKey.restorationIdentifier, storablePrivateKey.data) + val presentationSubmissionProof = pollux.createJWTPresentationSubmission( presentationDefinitionRequest = presentationDefinitionRequestString, credential = credential, @@ -1266,6 +1327,7 @@ open class EdgeAgent { ) } else { val linkSecret = getLinkSecret() + val presentationSubmissionProof = pollux.createAnoncredsPresentationSubmission( presentationDefinitionRequest = presentationDefinitionRequestString, credential = credential, @@ -1490,11 +1552,168 @@ open class EdgeAgent { } } - private fun generateNonce(size: Int = 16): String { + private fun generateNumericNonce(size: Int = 16): String { val random = SecureRandom() - val nonce = ByteArray(size) - random.nextBytes(nonce) - return Base64.getUrlEncoder().withoutPadding().encodeToString(nonce) + val nonce = StringBuilder(size) + + repeat(size) { + val digit = random.nextInt(10) // Generates a number between 0 and 9 + nonce.append(digit) + } + + return nonce.toString() + } + + /** + * Parses and validates a connectionless message from a JSON object. The method checks for the existence + * of required fields (e.g., id, body, attachments, thid, from) and throws errors if any are missing. + * It extracts necessary information from the message, including the attachment details, and returns + * a ConnectionlessMessageData object containing the parsed information. + * + * @param messageJson The JsonObject representing the connectionless message. + * @return A ConnectionlessMessageData object containing the parsed message data. + * @throws EdgeAgentError.MissingOrNullFieldError if any required field is missing or null. + */ + private fun parseAndValidateMessage(messageJson: JsonObject): ConnectionlessMessageData { + // Perform validation + if (!messageJson.containsKey("id")) throw EdgeAgentError.MissingOrNullFieldError("id", "Request") + if (!messageJson.containsKey("body")) throw EdgeAgentError.MissingOrNullFieldError("body", "Request") + if (!messageJson.containsKey("attachments")) { + throw EdgeAgentError.MissingOrNullFieldError( + "attachments", + "Request" + ) + } + if (!messageJson.containsKey("thid")) throw EdgeAgentError.MissingOrNullFieldError("thid", "Request") + if (!messageJson.containsKey("from")) throw EdgeAgentError.MissingOrNullFieldError("from", "Request") + + val messageId = messageJson["id"]!!.jsonPrimitive.content + val messageBody = messageJson["body"]!!.toString() + val messageThid = messageJson["thid"]!!.jsonPrimitive.content + val messageFrom = messageJson["from"]!!.jsonPrimitive.content + + // Validate and parse the first attachment + val attachmentJsonObject = messageJson["attachments"]!!.jsonArray.first().jsonObject + if (!attachmentJsonObject.containsKey("id")) { + throw EdgeAgentError.MissingOrNullFieldError( + "id", + "Request attachments" + ) + } + if (!attachmentJsonObject.containsKey("media_type")) { + throw EdgeAgentError.MissingOrNullFieldError( + "media_type", + "Request attachments" + ) + } + if (!attachmentJsonObject.containsKey("data")) { + throw EdgeAgentError.MissingOrNullFieldError( + "data", + "Request attachments" + ) + } + if (!attachmentJsonObject.containsKey("format")) { + throw EdgeAgentError.MissingOrNullFieldError( + "format", + "Request attachments" + ) + } + + val attachmentId = attachmentJsonObject["id"]!!.jsonPrimitive.content + val attachmentMediaType = attachmentJsonObject["media_type"]!!.jsonPrimitive.content + val attachmentData = attachmentJsonObject["data"]!!.jsonObject["json"]!!.toString() + val attachmentFormat = attachmentJsonObject["format"]!!.jsonPrimitive.content + + val attachmentDescriptor = AttachmentDescriptor( + id = attachmentId, + mediaType = attachmentMediaType, + data = AttachmentData.AttachmentJsonData(attachmentData), + format = attachmentFormat + ) + + // Return the extracted data + return ConnectionlessMessageData( + messageId = messageId, + messageBody = messageBody, + attachmentDescriptor = attachmentDescriptor, + messageThid = messageThid, + messageFrom = messageFrom + ) + } + + /** + * Handles a connectionless invitation by parsing the invitation string, extracting the necessary + * message data, and invoking the appropriate handler based on the type of the message. + * + * @param did The DID (Decentralized Identifier) associated with the invitation. + * @param invitationString The JSON string representing the invitation. + * @throws EdgeAgentError.MissingOrNullFieldError if any required field is missing or null. + * @throws EdgeAgentError.UnknownInvitationTypeError if the invitation type is unknown. + */ + private fun connectionlessInvitation(did: DID, invitationString: String) { + val invitationJson = Json.parseToJsonElement(invitationString).jsonObject + if (!invitationJson.containsKey("type")) { + throw EdgeAgentError.MissingOrNullFieldError("type", "Request") + } + val connectionLessMessageData = parseAndValidateMessage(invitationJson) + when (val type: String? = invitationJson["type"]?.jsonPrimitive?.content) { + ProtocolType.DidcommOfferCredential.value -> { + handleConnectionlessOfferCredential(connectionLessMessageData, did) + } + + ProtocolType.DidcommRequestPresentation.value -> { + handleConnectionlessRequestPresentation(connectionLessMessageData, did) + } + + else -> { + throw EdgeAgentError.UnknownInvitationTypeError(type ?: "Empty") + } + } + } + + /** + * Handles a connectionless Offer Credential message by extracting the necessary data + * from the ConnectionlessMessageData and storing the message using the Pluto service. + * + * @param connectionlessMessageData The parsed data from the connectionless message. + * @param did The DID (Decentralized Identifier) associated with the message. + */ + private fun handleConnectionlessOfferCredential( + connectionlessMessageData: ConnectionlessMessageData, + did: DID + ) { + val offerCredential = OfferCredential( + id = connectionlessMessageData.messageId, + body = Json.decodeFromString(connectionlessMessageData.messageBody), + attachments = arrayOf(connectionlessMessageData.attachmentDescriptor), + thid = connectionlessMessageData.messageThid, + from = DID(connectionlessMessageData.messageFrom), + to = did + ) + pluto.storeMessage(offerCredential.makeMessage()) + } + + /** + * Handles a connectionless Request Presentation message by extracting the necessary data + * from the ConnectionlessMessageData and storing the message using the Pluto service. + * + * @param connectionlessMessageData The parsed data from the connectionless message. + * @param did The DID (Decentralized Identifier) associated with the message. + */ + private fun handleConnectionlessRequestPresentation( + connectionlessMessageData: ConnectionlessMessageData, + did: DID + ) { + val requestPresentation = RequestPresentation( + id = connectionlessMessageData.messageId, + body = Json.decodeFromString(connectionlessMessageData.messageBody), + attachments = arrayOf(connectionlessMessageData.attachmentDescriptor), + thid = connectionlessMessageData.messageThid, + from = DID(connectionlessMessageData.messageFrom), + to = did + ) + + pluto.storeMessage(requestPresentation.makeMessage()) } /** diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/mediation/BasicMediatorHandler.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/mediation/BasicMediatorHandler.kt index e53a9d5b8..0c3770963 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/mediation/BasicMediatorHandler.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/mediation/BasicMediatorHandler.kt @@ -155,12 +155,7 @@ class BasicMediatorHandler( return flow { val message = mercury.sendMessageParseResponse(requestMessage) message?.let { - try { - emit(PickupRunner(message, mercury).run()) - } catch (e: Exception) { - println("Message of type ${message.piuri} cannot be sent to PickupRunner") - e.printStackTrace() - } + emit(PickupRunner(message, mercury).run()) } } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/models/ConnectionlessMessageData.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/models/ConnectionlessMessageData.kt new file mode 100644 index 000000000..05db6cb21 --- /dev/null +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/models/ConnectionlessMessageData.kt @@ -0,0 +1,11 @@ +package org.hyperledger.identus.walletsdk.edgeagent.models + +import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor + +data class ConnectionlessMessageData( + val messageId: String, + val messageBody: String, + val attachmentDescriptor: AttachmentDescriptor, + val messageThid: String, + val messageFrom: String +) diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/issueCredential/CredentialPreview.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/issueCredential/CredentialPreview.kt index 86b841d95..2c6244bc4 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/issueCredential/CredentialPreview.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/issueCredential/CredentialPreview.kt @@ -106,6 +106,6 @@ constructor( val name: String, val value: String, @SerialName("media_type") - val mediaType: String? + val mediaType: String? = null ) } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/pickup/PickupRunner.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/pickup/PickupRunner.kt index 812aa1132..1e642130f 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/pickup/PickupRunner.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/edgeagent/protocols/pickup/PickupRunner.kt @@ -1,9 +1,6 @@ package org.hyperledger.identus.walletsdk.edgeagent.protocols.pickup -import org.hyperledger.identus.apollo.base64.base64UrlDecoded import org.hyperledger.identus.walletsdk.domain.buildingblocks.Mercury -import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentBase64 -import org.hyperledger.identus.walletsdk.domain.models.AttachmentData.AttachmentJsonData import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor import org.hyperledger.identus.walletsdk.domain.models.Message import org.hyperledger.identus.walletsdk.edgeagent.EdgeAgentError @@ -27,7 +24,8 @@ class PickupRunner(message: Message, private val mercury: Mercury) { */ enum class PickupResponseType(val type: String) { STATUS("status"), - DELIVERY("delivery") + DELIVERY("delivery"), + PROBLEM_REPORT("problem_report") } /** @@ -63,6 +61,10 @@ class PickupRunner(message: Message, private val mercury: Mercury) { this.message = PickupResponse(PickupResponseType.DELIVERY, message) } + ProtocolType.ProblemReport.value -> { + this.message = PickupResponse(PickupResponseType.PROBLEM_REPORT, message) + } + else -> { throw EdgeAgentError.InvalidMessageType( type = message.piuri, @@ -97,22 +99,12 @@ class PickupRunner(message: Message, private val mercury: Mercury) { * @return The PickupAttachment object if the attachment data is of type AttachmentBase64 or AttachmentJsonData, otherwise null. */ private fun processAttachment(attachment: AttachmentDescriptor): PickupAttachment? { - return when (attachment.data) { - is AttachmentBase64 -> { - PickupAttachment( - attachmentId = attachment.id, - data = attachment.data.base64.base64UrlDecoded - ) - } + val data = attachment.data.getDataAsJsonString() + val id = attachment.id - is AttachmentJsonData -> { - PickupAttachment( - attachmentId = attachment.id, - data = attachment.data.data - ) - } - - else -> null - } + return PickupAttachment( + attachmentId = id, + data = data + ) } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLogger.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/Logger.kt similarity index 92% rename from edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLogger.kt rename to edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/Logger.kt index e62bf099a..9412cbd2e 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLogger.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/logger/Logger.kt @@ -24,10 +24,10 @@ private const val METADATA_PRIVACY_STR = "------" private val hashingLog = UUID.randomUUID().toString() /** - * PrismLogger is an interface that defines methods for logging messages + * Logger is an interface that defines methods for logging messages * with different log levels and metadata. */ -interface PrismLogger { +interface Logger { /** * Logs a debug message with optional metadata. * @@ -66,22 +66,24 @@ interface PrismLogger { * @param error The error to be logged. * @param metadata An array of metadata objects to be included in the log message. Defaults to an empty array if not provided. * - * @see PrismLogger.error + * @see Logger.error * @see Metadata */ fun error(error: Error, metadata: Array = arrayOf()) } /** - * Implementation of the PrismLogger interface. + * Implementation of the Logger interface. * * @property category the LogComponent category for this logger */ -class PrismLoggerImpl(category: LogComponent) : PrismLogger { +class LoggerImpl(private val category: LogComponent) : + Logger { - private val log = logging("[io.prism.kmm.sdk.$category]") + private val log = logging( + "[${category::class.qualifiedName}.$category]" - private var logLevel: LogLevel = LogLevel.INFO + ) /** * Logs a debug message with optional metadata. @@ -90,7 +92,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @param metadata An array of metadata objects associated with the message (optional). */ override fun debug(message: String, metadata: Array) { - if (logLevel != LogLevel.NONE) { + if (category.logLevel != LogLevel.NONE) { log.debug { message } if (metadata.isNotEmpty()) { log.debug { "Metadata: ${arrayToString(metadata)}" } @@ -105,7 +107,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @param metadata An array of metadata objects to be associated with the information message. */ override fun info(message: String, metadata: Array) { - if (logLevel != LogLevel.NONE) { + if (category.logLevel != LogLevel.NONE) { log.info { message } if (metadata.isNotEmpty()) { log.info { "Metadata: ${arrayToString(metadata)}" } @@ -120,7 +122,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @param metadata An array of metadata objects associated with the warning message. */ override fun warning(message: String, metadata: Array) { - if (logLevel != LogLevel.NONE) { + if (category.logLevel != LogLevel.NONE) { log.warn { message } if (metadata.isNotEmpty()) { log.warn { "Metadata: ${arrayToString(metadata)}" } @@ -135,7 +137,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @param metadata An array of metadata objects to be associated with the error message (optional). */ override fun error(message: String, metadata: Array) { - if (logLevel != LogLevel.NONE) { + if (category.logLevel != LogLevel.NONE) { log.error { message } if (metadata.isNotEmpty()) { log.error { "Metadata: ${arrayToString(metadata)}" } @@ -150,7 +152,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @param metadata An array of metadata objects associated with the error (optional). */ override fun error(error: Error, metadata: Array) { - if (logLevel != LogLevel.NONE) { + if (category.logLevel != LogLevel.NONE) { log.error { error.message } if (metadata.isNotEmpty()) { log.error { "Metadata: ${arrayToString(metadata)}" } @@ -165,7 +167,7 @@ class PrismLoggerImpl(category: LogComponent) : PrismLogger { * @return The converted String representation of the Metadata objects, with each object's value separated by a new line. */ private fun arrayToString(array: Array): String { - return array.joinToString { "${it.getValue(logLevel)}\n" } + return array.joinToString { "${it.getValue(category.logLevel)}\n" } } } @@ -333,13 +335,13 @@ enum class LogLevel(val value: Int) { * - MERCURY * - PLUTO * - POLLUX - * - PRISM_AGENT + * - EDGE_AGENT */ -enum class LogComponent { - APOLLO, - CASTOR, - MERCURY, - PLUTO, - POLLUX, - PRISM_AGENT +enum class LogComponent(var logLevel: LogLevel) { + APOLLO(LogLevel.DEBUG), + CASTOR(LogLevel.DEBUG), + MERCURY(LogLevel.DEBUG), + PLUTO(LogLevel.DEBUG), + POLLUX(LogLevel.DEBUG), + EDGE_AGENT(LogLevel.DEBUG) } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryImpl.kt index ae7d49ff1..d6a1c74a7 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryImpl.kt @@ -15,9 +15,9 @@ import org.hyperledger.identus.walletsdk.domain.models.MercuryError import org.hyperledger.identus.walletsdk.domain.models.Message import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.LogLevel +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl import org.hyperledger.identus.walletsdk.logger.Metadata -import org.hyperledger.identus.walletsdk.logger.PrismLogger -import org.hyperledger.identus.walletsdk.logger.PrismLoggerImpl import org.hyperledger.identus.walletsdk.mercury.forward.ForwardMessage /** @@ -55,7 +55,7 @@ constructor( private val castor: Castor, private val protocol: DIDCommProtocol, private val api: Api, - private val logger: PrismLogger = PrismLoggerImpl(LogComponent.MERCURY) + private val logger: Logger = LoggerImpl(LogComponent.MERCURY) ) : Mercury { /** diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/resolvers/DIDCommWrapper.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/resolvers/DIDCommWrapper.kt index af8dc6190..c0d5566ad 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/resolvers/DIDCommWrapper.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/mercury/resolvers/DIDCommWrapper.kt @@ -35,8 +35,8 @@ import org.hyperledger.identus.walletsdk.domain.models.MercuryError import org.hyperledger.identus.walletsdk.domain.models.Message import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.logger.LogLevel +import org.hyperledger.identus.walletsdk.logger.LoggerImpl import org.hyperledger.identus.walletsdk.logger.Metadata -import org.hyperledger.identus.walletsdk.logger.PrismLoggerImpl import org.hyperledger.identus.walletsdk.mercury.ATTACHMENT_SEPARATOR import org.hyperledger.identus.walletsdk.mercury.BASE64 import org.hyperledger.identus.walletsdk.mercury.DIDCommProtocol @@ -44,7 +44,6 @@ import org.hyperledger.identus.walletsdk.mercury.HASH import org.hyperledger.identus.walletsdk.mercury.JSON import org.hyperledger.identus.walletsdk.mercury.LINKS import java.time.Instant.now -import kotlin.jvm.Throws /** * Wrapper class for the DIDComm functionality. @@ -57,7 +56,7 @@ class DIDCommWrapper(castor: Castor, pluto: Pluto, apollo: Apollo) : DIDCommProt private val didDocResolver = DIDCommDIDResolver(castor) private val secretsResolver = DIDCommSecretsResolver(pluto, apollo) private val didComm = DIDComm(didDocResolver, secretsResolver) - private val logger = PrismLoggerImpl(LogComponent.MERCURY) + private val logger = LoggerImpl(LogComponent.MERCURY) /** * Converts a JSON element to a map. diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImpl.kt index 986b40b64..b20485109 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImpl.kt @@ -35,6 +35,9 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.JWK import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorablePrivateKey +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl +import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.pluto.models.backup.BackupV0_0_1 import org.hyperledger.identus.walletsdk.pluto.data.DbConnection import org.hyperledger.identus.walletsdk.pluto.data.isConnected @@ -55,7 +58,10 @@ import org.hyperledger.identus.walletsdk.pluto.data.StorableCredential as Storab * @property db The instance of `SdkPlutoDb` representing the connection to the database. * @property isConnected A flag to indicate whether the database connection is established or not. */ -class PlutoImpl(private val connection: DbConnection) : Pluto { +class PlutoImpl( + private val connection: DbConnection, + private val logger: Logger = LoggerImpl(LogComponent.PLUTO) +) : Pluto { private var db: SdkPlutoDb? = null init { diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt index edd3b73b9..3f1c96a18 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImpl.kt @@ -53,6 +53,8 @@ import org.hyperledger.identus.apollo.base64.base64UrlDecodedBytes import org.hyperledger.identus.apollo.utils.KMMECSecp256k1PublicKey import org.hyperledger.identus.walletsdk.apollo.helpers.gunzip import org.hyperledger.identus.walletsdk.apollo.utils.Secp256k1PrivateKey +import org.hyperledger.identus.walletsdk.castor.did.prismdid.PrismDIDPublicKey +import org.hyperledger.identus.walletsdk.castor.did.prismdid.defaultId import org.hyperledger.identus.walletsdk.domain.buildingblocks.Apollo import org.hyperledger.identus.walletsdk.domain.buildingblocks.Castor import org.hyperledger.identus.walletsdk.domain.buildingblocks.Pollux @@ -80,6 +82,7 @@ import org.hyperledger.identus.walletsdk.domain.models.httpClient import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurveKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurvePointXKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.CurvePointYKey +import org.hyperledger.identus.walletsdk.domain.models.keyManagement.ExportableKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.KeyTypes import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PublicKey @@ -92,6 +95,9 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptionsAnoncreds import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptionsJWT +import org.hyperledger.identus.walletsdk.logger.Logger +import org.hyperledger.identus.walletsdk.logger.LoggerImpl +import org.hyperledger.identus.walletsdk.logger.LogComponent import org.hyperledger.identus.walletsdk.pluto.RestorationID import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import org.hyperledger.identus.walletsdk.pollux.models.AnoncredsPresentationDefinitionRequest @@ -114,7 +120,8 @@ import org.hyperledger.identus.walletsdk.pollux.utils.BitString open class PolluxImpl( val apollo: Apollo, val castor: Castor, - private val api: Api = ApiImpl(httpClient()) + private val api: Api = ApiImpl(httpClient()), + private val logger: Logger = LoggerImpl(LogComponent.POLLUX) ) : Pollux { /** @@ -251,15 +258,14 @@ open class PolluxImpl( * @return The created verifiable presentation JWT. */ @Throws(PolluxError.NoDomainOrChallengeFound::class) - override fun processCredentialRequestJWT( + override suspend fun processCredentialRequestJWT( subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject ): String { - val parsedPrivateKey = parsePrivateKey(privateKey) val domain = getDomain(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() val challenge = getChallenge(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() - return signClaimsRequestCredentialJWT(subjectDID, parsedPrivateKey, domain, challenge) + return signClaimsRequestCredentialJWT(subjectDID, privateKey, domain, challenge) } /** @@ -272,15 +278,14 @@ open class PolluxImpl( * @return The created verifiable presentation JWT. */ @Throws(PolluxError.NoDomainOrChallengeFound::class) - override fun processCredentialRequestSDJWT( + override suspend fun processCredentialRequestSDJWT( subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject ): String { - val parsedPrivateKey = parsePrivateKey(privateKey) val domain = getDomain(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() val challenge = getChallenge(offerJson) ?: throw PolluxError.NoDomainOrChallengeFound() - return signClaimsRequestCredentialJWT(subjectDID, parsedPrivateKey, domain, challenge) + return signClaimsRequestCredentialJWT(subjectDID, privateKey, domain, challenge) } /** @@ -630,9 +635,9 @@ open class PolluxImpl( * @param challenge The challenge value for the JWT. * @return The signed JWT as a string. */ - private fun signClaimsRequestCredentialJWT( + private suspend fun signClaimsRequestCredentialJWT( subjectDID: DID, - privateKey: ECPrivateKey, + privateKey: PrivateKey, domain: String, challenge: String ): String { @@ -649,9 +654,9 @@ open class PolluxImpl( * @param challenge The challenge value for the JWT. * @return The signed JWT as a string. */ - internal fun signClaimsProofPresentationJWT( + private suspend fun signClaimsProofPresentationJWT( subjectDID: DID, - privateKey: ECPrivateKey, + privateKey: PrivateKey, credential: Credential, domain: String, challenge: String @@ -669,9 +674,9 @@ open class PolluxImpl( * @param challenge The challenge value for the JWT. * @return The signed JWT as a string. */ - internal fun signClaimsProofPresentationSDJWT( + internal suspend fun signClaimsProofPresentationSDJWT( subjectDID: DID, - privateKey: ECPrivateKey, + privateKey: PrivateKey, credential: Credential, domain: String, challenge: String @@ -689,13 +694,18 @@ open class PolluxImpl( * @param credential The optional credential to be included in the JWT. * @return The signed JWT as a string. */ - private fun signClaims( + internal suspend fun signClaims( subjectDID: DID, - privateKey: ECPrivateKey, + privateKey: PrivateKey, domain: String, challenge: String, credential: Credential? = null ): String { + if (privateKey !is ExportableKey) { + throw PolluxError.PrivateKeyTypeNotSupportedError("The private key should be ${ExportableKey::class.simpleName}") + } + val ecPrivateKey = parsePrivateKey(privateKey) + val presentation: MutableMap> = mutableMapOf( CONTEXT to setOf(CONTEXT_URL), TYPE to setOf(VERIFIABLE_PRESENTATION) @@ -711,14 +721,17 @@ open class PolluxImpl( .claim(VP, presentation) .build() + val kid = getSigningKid(subjectDID) + // Generate a JWS header with the ES256K algorithm val header = JWSHeader.Builder(JWSAlgorithm.ES256K) + .keyID(kid) .build() // Sign the JWT with the private key val jwsObject = SignedJWT(header, claims) val signer = ECDSASigner( - privateKey as java.security.PrivateKey, + ecPrivateKey as java.security.PrivateKey, com.nimbusds.jose.jwk.Curve.SECP256K1 ) val provider = BouncyCastleProviderSingleton.getInstance() @@ -915,10 +928,9 @@ open class PolluxImpl( val signedChallenge = privateKey.sign(jwtPresentationDefinitionRequest.options.challenge.encodeToByteArray()) - val ecPrivateKey = parsePrivateKey(privateKey) val presentationJwt = signClaimsProofPresentationJWT( subjectDID = DID(subject), - privateKey = ecPrivateKey, + privateKey = privateKey, credential = credential, domain = jwtPresentationDefinitionRequest.options.domain, challenge = jwtPresentationDefinitionRequest.options.challenge @@ -1290,4 +1302,23 @@ open class PolluxImpl( } return ecPublicKeys.toTypedArray() } + + /** + * Method to get the kId from the DID authentication property, Master key. + * + * @param did the DID to resolve + * @return The verification method id as a string or null. + */ + private suspend fun getSigningKid(did: DID): String? { + val didDocHolder = castor.resolveDID(did.toString()) + val authentication = didDocHolder.coreProperties.find { property -> + property::class == DIDDocument.Authentication::class + } + val verificationMethod = + (authentication as DIDDocument.Authentication).verificationMethods.find { verificationMethod -> + verificationMethod.id.did == did && verificationMethod.id.fragment == PrismDIDPublicKey.Usage.AUTHENTICATION_KEY.defaultId() + } + + return verificationMethod?.id?.string() + } } diff --git a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt index 08839e35c..8e2520dcf 100644 --- a/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt +++ b/edge-agent-sdk/src/commonMain/kotlin/org/hyperledger/identus/walletsdk/pollux/models/AnonCredential.kt @@ -166,11 +166,12 @@ data class AnonCredential( throw UnknownError.SomethingWentWrongError() } - val presentationRequest = PresentationRequest(request.toString()) + val decodedRequest = request.decodeToString() + val presentationRequest = PresentationRequest(decodedRequest) val cred = anoncreds_uniffi.Credential(this.id) - val requestedAttributes = extractRequestedAttributes(request.toString()) - val requestedPredicates = extractRequestedPredicatesKeys(request.toString()) + val requestedAttributes = extractRequestedAttributes(decodedRequest) + val requestedPredicates = extractRequestedPredicatesKeys(decodedRequest) val credentialRequests = anoncreds_uniffi.RequestedCredential( cred = cred, diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManagerTest.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManagerTest.kt index 73abb9252..be7f4be1c 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManagerTest.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/ConnectionManagerTest.kt @@ -1,10 +1,10 @@ package org.hyperledger.identus.walletsdk.edgeagent import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.hyperledger.identus.apollo.base64.base64UrlEncoded import org.hyperledger.identus.walletsdk.domain.buildingblocks.Castor @@ -54,7 +54,8 @@ class ConnectionManagerTest { lateinit var connectionManager: ConnectionManagerImpl - val testDispatcher = TestCoroutineDispatcher() + @OptIn(ExperimentalCoroutinesApi::class) + val testDispatcher = UnconfinedTestDispatcher() @BeforeTest fun setup() { @@ -118,7 +119,7 @@ class ConnectionManagerTest { `when`(castorMock.resolveDID(any())).thenReturn(didDoc) connectionManager.startFetchingMessages() - assertNotNull((connectionManager as ConnectionManagerImpl).fetchingMessagesJob) + assertNotNull(connectionManager.fetchingMessagesJob) verify(basicMediatorHandlerMock).listenUnreadMessages(any(), any()) } @@ -220,91 +221,91 @@ class ConnectionManagerTest { } @Test - fun testStartFetchingMessages_whenServiceEndpointNotContainsWSS_thenUseAPIRequest() = - runBlockingTest { - `when`(basicMediatorHandlerMock.mediatorDID) - .thenReturn(DID("did:prism:b6c0c33d701ac1b9a262a14454d1bbde3d127d697a76950963c5fd930605:Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VmsxEiECSTjyV7sUfCr_ArpN9rvCwR9fRMAhcsr_S7ZRiJk4p5k")) - - val vmAuthentication = DIDDocument.VerificationMethod( - id = DIDUrl(DID("2", "1", "0")), - controller = DID("2", "2", "0"), - type = Curve.ED25519.value, - publicKeyJwk = mapOf("crv" to Curve.ED25519.value, "x" to "") - ) + fun testStartFetchingMessages_whenServiceEndpointNotContainsWSS_thenUseAPIRequest() = runTest { + `when`(basicMediatorHandlerMock.mediatorDID) + .thenReturn(DID("did:prism:b6c0c33d701ac1b9a262a14454d1bbde3d127d697a76950963c5fd930605:Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VmsxEiECSTjyV7sUfCr_ArpN9rvCwR9fRMAhcsr_S7ZRiJk4p5k")) - val vmKeyAgreement = DIDDocument.VerificationMethod( - id = DIDUrl(DID("3", "1", "0")), - controller = DID("3", "2", "0"), - type = Curve.X25519.value, - publicKeyJwk = mapOf("crv" to Curve.X25519.value, "x" to "") - ) + val vmAuthentication = DIDDocument.VerificationMethod( + id = DIDUrl(DID("2", "1", "0")), + controller = DID("2", "2", "0"), + type = Curve.ED25519.value, + publicKeyJwk = mapOf("crv" to Curve.ED25519.value, "x" to "") + ) - val vmService = DIDDocument.Service( - id = UUID.randomUUID().toString(), - type = emptyArray(), - serviceEndpoint = DIDDocument.ServiceEndpoint( - uri = "https://serviceEndpoint" - ) + val vmKeyAgreement = DIDDocument.VerificationMethod( + id = DIDUrl(DID("3", "1", "0")), + controller = DID("3", "2", "0"), + type = Curve.X25519.value, + publicKeyJwk = mapOf("crv" to Curve.X25519.value, "x" to "") + ) + + val vmService = DIDDocument.Service( + id = UUID.randomUUID().toString(), + type = emptyArray(), + serviceEndpoint = DIDDocument.ServiceEndpoint( + uri = "https://serviceEndpoint" ) + ) - val didDoc = DIDDocument( - id = DID("did:prism:asdfasdf"), - coreProperties = arrayOf( - DIDDocument.Authentication( - urls = emptyArray(), - verificationMethods = arrayOf(vmAuthentication) - ), - DIDDocument.KeyAgreement( - urls = emptyArray(), - verificationMethods = arrayOf(vmKeyAgreement) - ), - DIDDocument.Services( - values = arrayOf(vmService) - ) + val didDoc = DIDDocument( + id = DID("did:prism:asdfasdf"), + coreProperties = arrayOf( + DIDDocument.Authentication( + urls = emptyArray(), + verificationMethods = arrayOf(vmAuthentication) + ), + DIDDocument.KeyAgreement( + urls = emptyArray(), + verificationMethods = arrayOf(vmKeyAgreement) + ), + DIDDocument.Services( + values = arrayOf(vmService) ) ) + ) - `when`(castorMock.resolveDID(any())).thenReturn(didDoc) - val messages = arrayOf(Pair("1234", Message(piuri = "", body = ""))) - `when`(basicMediatorHandlerMock.pickupUnreadMessages(any())).thenReturn( - flow { - emit( - messages - ) - } - ) - val attachments: Array = - arrayOf( - AttachmentDescriptor( - mediaType = "application/json", - format = CredentialType.JWT.type, - data = AttachmentBase64(base64 = "asdfasdfasdfasdfasdfasdfasdfasdfasdf".base64UrlEncoded) - ) + `when`(castorMock.resolveDID(any())).thenReturn(didDoc) + val messages = arrayOf(Pair("1234", Message(piuri = "", body = ""))) + `when`(basicMediatorHandlerMock.pickupUnreadMessages(any())).thenReturn( + flow { + emit( + messages ) - val listMessages = listOf( - Message( - piuri = ProtocolType.DidcommconnectionRequest.value, - body = "" - ), - Message( - piuri = ProtocolType.DidcommIssueCredential.value, - thid = UUID.randomUUID().toString(), - from = DID("did:peer:asdf897a6sdf"), - to = DID("did:peer:f706sg678ha"), - attachments = attachments, - body = """{}""" + } + ) + val attachments: Array = + arrayOf( + AttachmentDescriptor( + mediaType = "application/json", + format = CredentialType.JWT.type, + data = AttachmentBase64(base64 = "asdfasdfasdfasdfasdfasdfasdfasdfasdf".base64UrlEncoded) ) ) - val messageList: Flow> = flow { - emit(listMessages) - } - `when`(plutoMock.getAllMessages()).thenReturn(messageList) - - connectionManager.startFetchingMessages() - assertNotNull((connectionManager as ConnectionManagerImpl).fetchingMessagesJob) - verify(basicMediatorHandlerMock).pickupUnreadMessages(10) - verify(basicMediatorHandlerMock).registerMessagesAsRead(arrayOf("1234")) + val listMessages = listOf( + Message( + piuri = ProtocolType.DidcommconnectionRequest.value, + body = "" + ), + Message( + piuri = ProtocolType.DidcommIssueCredential.value, + thid = UUID.randomUUID().toString(), + from = DID("did:peer:asdf897a6sdf"), + to = DID("did:peer:f706sg678ha"), + attachments = attachments, + body = """{}""" + ) + ) + val messageList: Flow> = flow { + emit(listMessages) } + `when`(plutoMock.getAllMessages()).thenReturn(messageList) + + connectionManager.startFetchingMessages() + assertNotNull(connectionManager.fetchingMessagesJob) + assert(connectionManager.fetchingMessagesJob?.isActive == true) + verify(basicMediatorHandlerMock).pickupUnreadMessages(10) + verify(basicMediatorHandlerMock).registerMessagesAsRead(arrayOf("1234")) + } @Test fun testConnectionManager_whenProcessMessageRevoke_thenAllCorrect() = runTest { diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt index efcab27ed..12654814c 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/EdgeAgentTests.kt @@ -15,7 +15,9 @@ import kotlinx.coroutines.test.runTest import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import org.hyperledger.identus.apollo.base64.base64UrlDecoded import org.hyperledger.identus.apollo.base64.base64UrlDecodedBytes import org.hyperledger.identus.apollo.base64.base64UrlEncoded @@ -72,7 +74,7 @@ import org.hyperledger.identus.walletsdk.edgeagent.protocols.issueCredential.Off import org.hyperledger.identus.walletsdk.edgeagent.protocols.outOfBand.OutOfBandInvitation import org.hyperledger.identus.walletsdk.edgeagent.protocols.outOfBand.PrismOnboardingInvitation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation -import org.hyperledger.identus.walletsdk.logger.PrismLoggerMock +import org.hyperledger.identus.walletsdk.logger.LoggerMock import org.hyperledger.identus.walletsdk.mercury.ApiMock import org.hyperledger.identus.walletsdk.pluto.CredentialRecovery import org.hyperledger.identus.walletsdk.pluto.PlutoBackupTask @@ -188,7 +190,7 @@ class EdgeAgentTests { connectionManager = connectionManager, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -230,7 +232,7 @@ class EdgeAgentTests { connectionManager = connectionManager, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -264,7 +266,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -284,7 +286,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -306,7 +308,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -329,7 +331,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -354,7 +356,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = seed, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) plutoMockOld.getPrismLastKeyPathIndexReturn = flow { emit(0) } @@ -377,7 +379,7 @@ class EdgeAgentTests { connectionManagerMock, seed, null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) ) @@ -393,7 +395,7 @@ class EdgeAgentTests { @Test fun testCreateNewPeerDID_whenUpdateMediatorFalse_thenShouldUseProvidedServices() = runTest { val apollo = ApolloImpl() - val castor = CastorImpl(apollo = apollo, logger = PrismLoggerMock()) + val castor = CastorImpl(apollo = apollo, logger = LoggerMock()) val agent = EdgeAgent( apollo, castor, @@ -403,7 +405,7 @@ class EdgeAgentTests { connectionManagerOld, null, null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -439,7 +441,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock() + logger = LoggerMock() ) val invitationString = """ { @@ -466,7 +468,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = api, - logger = PrismLoggerMock() + logger = LoggerMock() ) val invitationString = """ { @@ -493,7 +495,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock() + logger = LoggerMock() ) val invitationString = """ { @@ -519,7 +521,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock() + logger = LoggerMock() ) plutoMockOld.getDIDPrivateKeysReturn = flow { emit(listOf(null)) } @@ -545,7 +547,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -588,7 +590,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -630,7 +632,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -671,7 +673,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -718,7 +720,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock() + logger = LoggerMock() ) val invitationString = """ @@ -748,7 +750,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) assertEquals(EdgeAgent.State.STOPPED, agent.state) @@ -767,7 +769,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) agent.stop() @@ -787,7 +789,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = null, - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) val x = agent.parseInvitation(oob) @@ -849,7 +851,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = seed, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -891,7 +893,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = seed, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -919,7 +921,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = seed, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -965,7 +967,7 @@ class EdgeAgentTests { connectionManager = connectionManagerOld, seed = null, api = ApiMock(HttpStatusCode.OK, "{\"success\":\"true\"}"), - logger = PrismLoggerMock(), + logger = LoggerMock(), agentOptions = AgentOptions() ) @@ -1039,7 +1041,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val vmAuthentication = DIDDocument.VerificationMethod( @@ -1137,7 +1139,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val vmAuthentication = DIDDocument.VerificationMethod( @@ -1255,7 +1257,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val msg = Json.decodeFromString( "{\"id\":\"00000000-685c-4004-0000-000036ac64ee\",\"piuri\":\"https://didcomm.atalaprism.io/present-proof/3.0/request-presentation\",\"from\":{\"method\":\"peer\",\"methodId\":\"asdfasdf\"},\"to\":{\"method\":\"peer\",\"methodId\":\"fdsafdsa\"},\"fromPrior\":null,\"body\":\"{}\",\"createdTime\":\"2024-03-08T19:27:38.196506Z\",\"expiresTimePlus\":\"2024-03-09T19:27:38.196559Z\",\"attachments\":[{\"id\":\"00000000-9c2e-4249-0000-0000c1176949\",\"mediaType\":\"application/json\",\"data\":{\"type\":\"org.hyperledger.identus.walletsdk.domain.models.AttachmentBase64\",\"base64\":\"eyJwcmVzZW50YXRpb25fZGVmaW5pdGlvbiI6eyJpZCI6IjMyZjU0MTYzLTcxNjYtNDhmMS05M2Q4LWZmMjE3YmRiMDY1MyIsImlucHV0X2Rlc2NyaXB0b3JzIjpbeyJpZCI6IndhX2RyaXZlcl9saWNlbnNlIiwibmFtZSI6Ildhc2hpbmd0b24gU3RhdGUgQnVzaW5lc3MgTGljZW5zZSIsInB1cnBvc2UiOiJXZSBjYW4gb25seSBhbGxvdyBsaWNlbnNlZCBXYXNoaW5ndG9uIFN0YXRlIGJ1c2luZXNzIHJlcHJlc2VudGF0aXZlcyBpbnRvIHRoZSBXQSBCdXNpbmVzcyBDb25mZXJlbmNlIiwiY29uc3RyYWludHMiOnsiZmllbGRzIjpbeyJwYXRoIjpbIiQuY3JlZGVudGlhbFN1YmplY3QuZGF0ZU9mQmlydGgiLCIkLmNyZWRlbnRpYWxTdWJqZWN0LmRvYiIsIiQudmMuY3JlZGVudGlhbFN1YmplY3QuZGF0ZU9mQmlydGgiLCIkLnZjLmNyZWRlbnRpYWxTdWJqZWN0LmRvYiJdfV19fV0sImZvcm1hdCI6eyJqd3QiOnsiYWxnIjpbIkVTMjU2SyJdfX19LCAib3B0aW9ucyI6IHsiZG9tYWluIjogImRvbWFpbiIsICJjaGFsbGVuZ2UiOiAiY2hhbGxlbmdlIn19\"},\"format\":\"dif/presentation-exchange/fail_test@v1.0\"}],\"thid\":\"00000000-ef9d-4722-0000-00003b1bc908\",\"ack\":[]}" @@ -1295,7 +1297,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val msg = Json.decodeFromString( "{\"id\":\"00000000-685c-4004-0000-000036ac64ee\",\"piuri\":\"https://didcomm.atalaprism.io/present-proof/3.0/presentation\",\"from\":{\"method\":\"peer\",\"methodId\":\"asdfasdf\"},\"to\":{\"method\":\"peer\",\"methodId\":\"fdsafdsa\"},\"fromPrior\":null,\"body\":\"{}\",\"createdTime\":\"2024-03-08T19:27:38.196506Z\",\"expiresTimePlus\":\"2024-03-09T19:27:38.196559Z\",\"attachments\":[{\"id\":\"00000000-9c2e-4249-0000-0000c1176949\",\"mediaType\":\"application/json\",\"data\":{\"type\":\"org.hyperledger.identus.walletsdk.domain.models.AttachmentBase64\",\"base64\":\"eyJ2ZXJpZmlhYmxlUHJlc2VudGF0aW9uIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpZlEuZXlKcGMzTWlPaUprYVdRNmNISnBjMjA2TWpVM01UbGhPVFppTVRVeE1qQTNNVFk1T0RGaE9EUXpNR0ZrTUdOaU9UWTRaR1ExTXpRd056TTFPVE5qT0dOa00yWXhaREkzWVRZNE1EUmxZelV3WlRwRGNHOURRM0JqUTBWc2IwdENWM1JzWlZNd2VFVkJTa05VZDI5S1l6SldhbU5FU1RGT2JYTjRSV2xCUlc5VFEyNDFkSGxFWVRaWk5uSXRTVzFUY1hCS09Ga3hiV28zU2tNelgyOVZla1V3VG5sNVJXbERRbTluYzJkT1lXVlNaR05EVWtkUWJHVTRNbFoyT1hSS1prNTNiRFp5WnpaV1kyaFNNMDl4YUdsV1lsUmhPRk5YZDI5SFdWaFdNR0ZETUhoRlFWSkRWSGR2U21NeVZtcGpSRWt4VG0xemVFVnBSRTFyUW1RMlJuUnBiMHByTTFoUFJuVXRYMk41TlZodFVpMDBkRlZSTWs1TVIybFhPR0ZKVTI5dGExSnZaelpUWkdVNVVIZHVSekJSTUZOQ1ZHMUdVMVJFWWxOTFFuWkpWalpEVkV4WWNtcEpTblIwWlVkSmJVRlRXRUZ2U0dKWFJucGtSMVo1VFVKQlFsRnJPRXREV0U1c1dUTkJlVTVVV25KTlVrbG5UemN4TUcxME1WZGZhWGhFZVZGTk0zaEpjemRVY0dwTVEwNVBSRkY0WjFab2VEVnphR1pMVGxneGIyRkpTRmRRY25jM1NWVkxiR1pwWWxGMGVEWkthelJVVTJwblkxZE9UMlpqVDNSVk9VUTVVSFZhTjFRNWRDSXNJbk4xWWlJNkltUnBaRHB3Y21semJUcGlaV1ZoTlRJek5HRm1ORFk0TURRM01UUmtPR1ZoT0dWak56ZGlOalpqWXpkbU0yVTRNVFZqTmpoaFltSTBOelZtTWpVMFkyWTVZek13TmpJMk56WXpPa056WTBKRGMxRkNSVzFSUzBReVJqRmtSMmhzWW01U2NGa3lSakJoVnpsMVRVSkJSVkZyT0V0RFdFNXNXVE5CZVU1VVduSk5Va2xuWlZObkxUSlBUekZLWkc1d2VsVlBRbWwwZWtscFkxaGtabnBsUVdOVVpsZEJUaTFaUTJWMVEySjVTV0ZKU2xFMFIxUkpNekIwWVZacGQyTm9WRE5sTUc1TVdFSlRORE5DTkdvNWFteHpiRXR2TWxwc1pGaDZha1ZzZDB0Q01qRm9Zek5TYkdOcVFWRkJWVXBRUTJkc2VscFhUbmROYWxVeVlYcEZVMGxJYTI5UWRHcHFkRk5ZV2paak1VUm5XWEpqZVVsdVJqTllPRE5uU0VVek1XZEVabTFCYm5KbmJUaHBSMmxEVlU5Q2EzbE9PVXhYYkZselNFbFZPVE4wU25reGQxVjFUbmRsU1Y5Wk5XSktVM0ZPYlZwWVZqZzBkeUlzSW01aVppSTZNVFk0TlRZek1UazVOU3dpWlhod0lqb3hOamcxTmpNMU5UazFMQ0oyWXlJNmV5SmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUpoWkdScGRHbHZibUZzVUhKdmNESWlPaUpVWlhOME15SXNJbWxrSWpvaVpHbGtPbkJ5YVhOdE9tSmxaV0UxTWpNMFlXWTBOamd3TkRjeE5HUTRaV0U0WldNM04ySTJObU5qTjJZelpUZ3hOV00yT0dGaVlqUTNOV1l5TlRSalpqbGpNekEyTWpZM05qTTZRM05qUWtOelVVSkZiVkZMUkRKR01XUkhhR3hpYmxKd1dUSkdNR0ZYT1hWTlFrRkZVV3M0UzBOWVRteFpNMEY1VGxSYWNrMVNTV2RsVTJjdE1rOVBNVXBrYm5CNlZVOUNhWFI2U1dsaldHUm1lbVZCWTFSbVYwRk9MVmxEWlhWRFlubEpZVWxLVVRSSFZFa3pNSFJoVm1sM1kyaFVNMlV3Ymt4WVFsTTBNMEkwYWpscWJITnNTMjh5V214a1dIcHFSV3gzUzBJeU1XaGpNMUpzWTJwQlVVRlZTbEJEWjJ4NldsZE9kMDFxVlRKaGVrVlRTVWhyYjFCMGFtcDBVMWhhTm1NeFJHZFpjbU41U1c1R00xZzRNMmRJUlRNeFowUm1iVUZ1Y21kdE9HbEhhVU5WVDBKcmVVNDVURmRzV1hOSVNWVTVNM1JLZVRGM1ZYVk9kMlZKWDFrMVlrcFRjVTV0V2xoV09EUjNJbjBzSW5SNWNHVWlPbHNpVm1WeWFXWnBZV0pzWlVOeVpXUmxiblJwWVd3aVhTd2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZYQzljTDNkM2R5NTNNeTV2Y21kY0x6SXdNVGhjTDJOeVpXUmxiblJwWVd4elhDOTJNU0pkZlgwLngwU0YxN1kwVkNEbXQ3SGNlT2RUeGZIbG9mc1ptWTE4Um42VlFiMC1yLWtfQm0zaFRpMS1rMnZrZGpCMjVoZHh5VEN2eGFtLUFrQVAtQWczQWhuNU5nIl19\"},\"format\":\"dif/presentation-exchange/definitions@v1.0\"}],\"thid\":\"00000000-ef9d-4722-0000-00003b1bc908\",\"ack\":[]}" @@ -1367,7 +1369,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val msg = Json.decodeFromString( """{"id":"00000000-685c-4004-0000-000036ac64ee","piuri":"https://didcomm.atalaprism.io/present-proof/3.0/request-presentation","from":{"method":"peer","methodId":"asdfasdf"},"to":{"method":"peer","methodId":"fdsafdsa"},"fromPrior":null,"body":"{}","createdTime":"2024-03-08T19:27:38.196506Z","expiresTimePlus":"2024-03-09T19:27:38.196559Z","attachments":[{"id":"00000000-9c2e-4249-0000-0000c1176949","mediaType":"application/json","data":{"type":"org.hyperledger.identus.walletsdk.domain.models.AttachmentBase64","base64":"eyJwcmVzZW50YXRpb25fZGVmaW5pdGlvbiI6eyJpZCI6IjMyZjU0MTYzLTcxNjYtNDhmMS05M2Q4LWZmMjE3YmRiMDY1MyIsImlucHV0X2Rlc2NyaXB0b3JzIjpbeyJpZCI6IndhX2RyaXZlcl9saWNlbnNlIiwibmFtZSI6Ildhc2hpbmd0b24gU3RhdGUgQnVzaW5lc3MgTGljZW5zZSIsInB1cnBvc2UiOiJXZSBjYW4gb25seSBhbGxvdyBsaWNlbnNlZCBXYXNoaW5ndG9uIFN0YXRlIGJ1c2luZXNzIHJlcHJlc2VudGF0aXZlcyBpbnRvIHRoZSBXQSBCdXNpbmVzcyBDb25mZXJlbmNlIiwiY29uc3RyYWludHMiOnsiZmllbGRzIjpbeyJwYXRoIjpbIiQuY3JlZGVudGlhbFN1YmplY3QuZGF0ZU9mQmlydGgiLCIkLmNyZWRlbnRpYWxTdWJqZWN0LmRvYiIsIiQudmMuY3JlZGVudGlhbFN1YmplY3QuZGF0ZU9mQmlydGgiLCIkLnZjLmNyZWRlbnRpYWxTdWJqZWN0LmRvYiJdfV19fV0sImZvcm1hdCI6eyJqd3QiOnsiYWxnIjpbIkVTMjU2SyJdfX19LCAib3B0aW9ucyI6IHsiZG9tYWluIjogImRvbWFpbiIsICJjaGFsbGVuZ2UiOiAiY2hhbGxlbmdlIn19"},"format":"dif/presentation-exchange/definitions@v1.0"}],"thid":"00000000-ef9d-4722-0000-00003b1bc908","ack":[]}""" @@ -1421,7 +1423,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val credential = JWTCredential.fromJwtString( @@ -1530,7 +1532,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val msgString = @@ -1653,7 +1655,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = apiMock, - logger = PrismLoggerMock() + logger = LoggerMock() ) val msgString = @@ -1674,7 +1676,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock() + logger = LoggerMock() ) `when`( apolloMock.createPrivateKey( @@ -1868,7 +1870,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock() + logger = LoggerMock() ) ) @@ -1896,7 +1898,7 @@ class EdgeAgentTests { connectionManager = connectionManagerMock, seed = seed, api = null, - logger = PrismLoggerMock() + logger = LoggerMock() ) ) @@ -1926,6 +1928,58 @@ class EdgeAgentTests { assertTrue(json.jsonObject.containsKey("presentation_definition")) } + @Test + fun `test connectionless credential offer correctly`() = runTest { + val agent = spy( + EdgeAgent( + apollo = apolloMock, + castor = castorMock, + pluto = plutoMock, + mercury = mercuryMock, + pollux = polluxMock, + connectionManager = connectionManagerMock, + seed = seed, + api = null, + logger = LoggerMock() + ) + ) + + val notExpiredTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(30) + val notExpiredInvitation = + """{"id":"f96e3699-591c-4ae7-b5e6-6efe6d26255b","type":"https://didcomm.org/out-of-band/2.0/invitation","from":"did:peer:2.Ez6LSfsKMe8vSSWkYdZCpn4YViPERfdGAhdLAGHgx2LGJwfmA.Vz6Mkpw1kSabBMzkA3v59tQFnh3FtkKy6xLhLxd9S6BAoaBg2.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuMzc6ODA4MC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0","body":{"goal_code":"issue-vc","goal":"To issue a Faber College Graduate credential","accept":["didcomm/v2"]},"attachments":[{"id":"70cdc90c-9a99-4cda-87fe-4f4b2595112a","media_type":"application/json","data":{"json":{"id":"655e9a2c-48ed-459b-b3da-6b3686655564","type":"https://didcomm.org/issue-credential/3.0/offer-credential","body":{"goal_code":"Offer Credential","credential_preview":{"type":"https://didcomm.org/issue-credential/3.0/credential-credential","body":{"attributes":[{"name":"familyName","value":"Wonderland"},{"name":"givenName","value":"Alice"},{"name":"drivingClass","value":"Mw==","media_type":"application/json"},{"name":"dateOfIssuance","value":"2020-11-13T20:20:39+00:00"},{"name":"emailAddress","value":"alice@wonderland.com"},{"name":"drivingLicenseID","value":"12345"}]}}},"attachments":[{"id":"8404678b-9a36-4989-af1d-0f445347e0e3","media_type":"application/json","data":{"json":{"options":{"challenge":"ad0f43ad-8538-41d4-9cb8-20967bc685bc","domain":"domain"},"presentation_definition":{"id":"748efa58-2bce-440d-921f-2520a8446663","input_descriptors":[],"format":{"jwt":{"alg":["ES256K"],"proof_type":[]}}}}},"format":"prism/jwt"}],"thid":"f96e3699-591c-4ae7-b5e6-6efe6d26255b","from":"did:peer:2.Ez6LSfsKMe8vSSWkYdZCpn4YViPERfdGAhdLAGHgx2LGJwfmA.Vz6Mkpw1kSabBMzkA3v59tQFnh3FtkKy6xLhLxd9S6BAoaBg2.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuMzc6ODA4MC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0"}}}],"created_time":1724851139,"expires_time":$notExpiredTime}""" + val base64Invitation = notExpiredInvitation.base64UrlEncoded + + val outOfBandUrl = "https://my.domain.com/path?_oob=$base64Invitation" + val oob = agent.parseInvitation(outOfBandUrl) + assertTrue(oob is OutOfBandInvitation) + oob as OutOfBandInvitation + + doReturn(DID("did:peer:asdf")).`when`(agent).createNewPeerDID(updateMediator = true) + agent.acceptOutOfBandInvitation(oob) + val captor = argumentCaptor() + verify(plutoMock).storeMessage(captor.capture()) + val msg = captor.lastValue + assertEquals(ProtocolType.DidcommOfferCredential.value, msg.piuri) + assertEquals("f96e3699-591c-4ae7-b5e6-6efe6d26255b", msg.thid) + val attachments = msg.attachments + assertEquals(1, attachments.size) + val attachmentJsonData = attachments.first().data + assertTrue(attachmentJsonData is AttachmentData.AttachmentJsonData) + val json = Json.parseToJsonElement(attachmentJsonData.getDataAsJsonString()) + assertTrue(json.jsonObject.containsKey("options")) + assertTrue(json.jsonObject["options"]!!.jsonObject.containsKey("challenge")) + assertTrue(json.jsonObject["options"]!!.jsonObject.containsKey("domain")) + assertTrue(json.jsonObject.containsKey("presentation_definition")) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject.containsKey("id")) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject.containsKey("input_descriptors")) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject.containsKey("format")) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject["format"]!!.jsonObject.contains("jwt")) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject["format"]!!.jsonObject["jwt"]!!.jsonObject.contains("alg")) + val algs = json.jsonObject["presentation_definition"]!!.jsonObject["format"]!!.jsonObject["jwt"]!!.jsonObject["alg"]!!.jsonArray + assertEquals("ES256K", algs.first().jsonPrimitive.content) + assertTrue(json.jsonObject["presentation_definition"]!!.jsonObject["format"]!!.jsonObject["jwt"]!!.jsonObject.contains("proof_type")) + } + val getCredentialDefinitionResponse = "{\"schemaId\":\"http://host.docker.internal:8000/prism-agent/schema-registry/schemas/5e0d5a93-4bfd-3111-a956-5d5bc82f76cc\",\"type\":\"CL\",\"tag\":\"licence\",\"value\":{\"primary\":{\"n\":\"105195159277979097653318357586659371305119697478469834190626350283715795188687389523188659352120689851168860621983864738336838773213022505168653440146374011050277159372491059901432822905781969400722059341786498751125483895348734607382548396665339315322605154516776326303787844694026898270194867398625429469096229269732265502538641116512214652017416624138065704599041020588805936844771273861390913500753293895219370960892829297672575154196820931047049021760519166121287056337193413235473255257349024671869248216238831094979209384406168241010010012567685965827447177652200129684927663161550376084422586141212281146491949\",\"s\":\"85376740935726732134199731472843597191822272986425414914465211197069650618238336366149699822721009443794877925725075553195071288777117865451699414058058985000654277974066307286552934230286237253977472401290858765904161191229985245519871949378628131263513153683765553672655918133136828182050729012388157183851720391379381006921499997765191873729408614024320763554099291141052786589157823043612948619201525441997065264492145372001259366749278235381762443117203343617927241093647322654346302447381494008414208398219626199373278313446814209403507903682881070548386699522575055488393512785511441688197244526708647113340516\",\"r\":{\"dateofissuance\":\"16159515692057558658031632775257139859912833740243870833808276956469677196577164655991169139545328065546186056342530531355718904597216453319851305621683589202769847381737819412615902541110462703838858425423753481085962114120185123089078513531045426316918036549403698066078445947881055316312848598741184161901260446303171175343050250045452903485086185722998336149005743485268486377824763449026501058416292877646187105446333888525480394665310217044483841168928926515929150167890936706159800372381200383816724043496032886366767166850459338411710056171379538841845247931898550165532492578625954615979453881721709564750235\",\"drivingclass\":\"83649701835078373520097916558245060224505938113940626586910000950978790663411517512280043632278010831292224659523658613504637416710001103641231226266903556936380105758523760424939825687213460920436570466066231912959327201876189240504388424799892400351592593406285436824571943165913587899115814843543998396726679289422080229750418336051741708013580146373647528674381958028243228435161765957312248113519708734663989428761879029086059388435772829434952754093999424834120341657211221855300108096057633128467059590470639772605075954658131680801785637700237403873940041665483384938586320674338994185073499523485570537331062\",\"emailaddress\":\"96995643129591814391344614133120459563648002327749700279517548454036811217735867585059116635583558148259032071807493674533230465312311981127622542797279917256478867847832932893748528200469349058284133058865149153179959849308383505167342565738382180666525211256221655129861213392455759272915565057394420728271409215556596974900718332893753172173500744392522771654048192448229319313386967045678744665093451560743782910263014930200762027209565313884859542996067229707388839912195826334964819133016500346618083969320902775088800287566711941842968839787149808739739233388585677095545116231323172342995837636586249573194609\",\"drivinglicenseid\":\"102840929811153624977554462471309185033977661854754815794111114507549576719389525167082631547450413573293352276930065480432301200611396989595571202142654033217842162456070556560693402484110499573693863745648118310258284468114751958738878996458420605301017450868522680454545537837403398645500541915771765220093329728663621098538954397330411649083351383375839056527007892276284168437065687748085384178113959961057476582871100422859953560730152958588610850909069434658487744782540788968302663076149478487413357533660817020800754493642858564081116318655661240523146995256712471572605700346459123074377380656921337264554594\",\"familyname\":\"2428690037146701497427424649573806616639612325136606164619283916796880313617677563507218774958436668407050506838114136163250163675016510113975582318007560622124292458766639319715064358235569650961433812439763343736699708535945693241909905707497180931492818502593885932421170612418693515054756633264933222189766691632082890045477718331705366111669009551578289182848340651375008362238266590844461708981816856194045325523248527964502118319210042254240848590574645476930113881493472578612352948284862674703949781070309344526122291448990325949065193279599181502524961004046979227803224474342778516917124487012958845744311\",\"master_secret\":\"96236339155824229583363924057798366491998077727991424922911165403434522806469328114407334094535810942859512352089785125683335350062474092708044674085769524387654467267128528564551803293661877480971961092735622606052503557881856409855812611523475975566606131897917979412576797874632169829901968854843162299366867885636535326810998541141840561418097240137120398317445832694001031827068485975315937269024666370665530455146256019590700349556357390218401217383173228376078058967743472704019765210324846681867991543267171763037513180046865961560351035005185946817643006206395175857900512245900162751815626427008481585714891\"},\"rctxt\":\"54359809198312125478916383106913469635175253891208897419510030559787479974126666313900084654632259260010008369569778456071591398552341004538623276997178295939490854663263886825856426285604332554317424030793691008221895556474599466123873279022389276698551452690414982831059651505731449763128921782866843113361548859434294057249048041670761184683271568216202174527891374770703485794299697663353847310928998125365841476766767508733046891626759537001358973715760759776149482147060701775948253839125589216812475133616408444838011643485797584321993661048373877626880635937563283836661934456534313802815974883441215836680800\",\"z\":\"99592262675748359673042256590146366586480829950402370244401571195191609039150608482506917768910598228167758026656953725016982562881531475875469671976107506976812319765644401707559997823702387678953647104105378063905395973550729717937712350758544336716556268064226491839700352305793370980462034813589488455836259737325502578253339820590260554457468082536249525493340350556649403477875367398139579018197084796440810685458274393317299082017275568964540311198115802021902455672385575542594821996060452628805634468222196284384514736044680778624637228114693554834388824212714580770066729185685978935409859595244639193538156\"}},\"issuerId\":\"did:prism:604ba1764ab89993f9a74625cc4f3e04737919639293eb382cc7adc53767f550\"}" diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt index ea533e17e..223a5670f 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/edgeagent/PolluxMock.kt @@ -35,11 +35,11 @@ class PolluxMock : Pollux { TODO("Not yet implemented") } - override fun processCredentialRequestJWT(subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject): String { + override suspend fun processCredentialRequestJWT(subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject): String { TODO("Not yet implemented") } - override fun processCredentialRequestSDJWT(subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject): String { + override suspend fun processCredentialRequestSDJWT(subjectDID: DID, privateKey: PrivateKey, offerJson: JsonObject): String { TODO("Not yet implemented") } diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLoggerMock.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/LoggerMock.kt similarity index 97% rename from edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLoggerMock.kt rename to edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/LoggerMock.kt index cad70ab24..5a6eaa2cd 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/PrismLoggerMock.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/logger/LoggerMock.kt @@ -1,6 +1,6 @@ package org.hyperledger.identus.walletsdk.logger -class PrismLoggerMock : PrismLogger { +class LoggerMock : Logger { private var logLevel: LogLevel = LogLevel.NONE diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryTests.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryTests.kt index 5d8f5e535..eb42ae62a 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryTests.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/mercury/MercuryTests.kt @@ -6,7 +6,7 @@ import org.hyperledger.identus.walletsdk.domain.models.DID import org.hyperledger.identus.walletsdk.domain.models.DIDDocument import org.hyperledger.identus.walletsdk.domain.models.MercuryError import org.hyperledger.identus.walletsdk.domain.models.Message -import org.hyperledger.identus.walletsdk.logger.PrismLoggerMock +import org.hyperledger.identus.walletsdk.logger.LoggerMock import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFailsWith @@ -24,7 +24,7 @@ class MercuryTests { apiMock = ApiMock(HttpStatusCode.OK, "") castorMock = CastorMock() protocolMock = ProtocolMock() - sut = MercuryImpl(castorMock, protocolMock, apiMock, PrismLoggerMock()) + sut = MercuryImpl(castorMock, protocolMock, apiMock, LoggerMock()) } @Test diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/BackupRestorationTests.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/BackupRestorationTests.kt index d0be7d4b7..ad678b453 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/BackupRestorationTests.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/BackupRestorationTests.kt @@ -32,7 +32,7 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.TypeKey import org.hyperledger.identus.walletsdk.edgeagent.EdgeAgent import org.hyperledger.identus.walletsdk.edgeagent.MediationHandlerMock import org.hyperledger.identus.walletsdk.edgeagent.MercuryMock -import org.hyperledger.identus.walletsdk.logger.PrismLoggerMock +import org.hyperledger.identus.walletsdk.logger.LoggerMock import org.hyperledger.identus.walletsdk.pluto.models.backup.BackupV0_0_1 import org.hyperledger.identus.walletsdk.pollux.PolluxImpl import org.hyperledger.identus.walletsdk.pollux.models.JWTCredential @@ -578,7 +578,8 @@ class BackupRestorationTests { @Test fun restoreFromTS_Swift() = runTest { val realPluto = PlutoImpl(DbConnectionInMemory()) - val logger = PrismLoggerMock() + val logger = LoggerMock() + val realCastor = CastorImpl(apollo, logger) edgeAgent = EdgeAgent( diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImplTest.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImplTest.kt index db5c59fb3..29b8b5894 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImplTest.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pluto/PlutoImplTest.kt @@ -3,7 +3,6 @@ package org.hyperledger.identus.walletsdk.pluto import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertNull import junit.framework.TestCase.assertTrue -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.hyperledger.identus.apollo.base64.base64UrlEncoded @@ -19,83 +18,85 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.mockito.MockitoAnnotations -import java.util.* +import java.util.UUID import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertFailsWith class PlutoImplTest { - private var pluto: PlutoImpl? = null + private lateinit var pluto: PlutoImpl + private lateinit var dbConnection: DbConnectionInMemory @Before fun setUp() { MockitoAnnotations.openMocks(this) - pluto = PlutoImpl(DbConnectionInMemory()) + dbConnection = DbConnectionInMemory() + pluto = PlutoImpl(dbConnection) } @After fun destroy() { - pluto = null + if (pluto.isConnected) { + pluto.stop() + } } @Test fun `verify Pluto is connected after start`() = runTest { - pluto?.start() - assertEquals(false, pluto?.isConnected) + pluto.start() + assertEquals(true, pluto.isConnected) } @Test fun `verify Pluto is not connected if not started`() = runTest { - assertEquals(false, pluto?.isConnected) + assertEquals(false, pluto.isConnected) } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun `test Pluto throws an expection if started more than once`() = runTest { val context = Any() - pluto?.start(context) + pluto.start(context) assertFailsWith { - pluto?.start(context) + pluto.start(context) } } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun `test storePrismDIDAndPrivateKeys stores DID and private keys`() = runTest { val did = DID("did:prism:example") val secp256PrivateKey = Secp256k1KeyPair.generateKeyPair() val alias = "alias" - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(did, 1, alias, listOf(secp256PrivateKey.privateKey as StorableKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(did, 1, alias, listOf(secp256PrivateKey.privateKey as StorableKey)) - val dids = pluto?.getAllDIDs()?.first() + val dids = pluto.getAllDIDs().first() assertNotNull(dids) - assertTrue(dids!!.isNotEmpty()) + assertTrue(dids.isNotEmpty()) assertEquals(1, dids.size) assertEquals(did.toString(), dids.first().toString()) - val privateKeys = pluto?.getAllPrivateKeys()?.first() + val privateKeys = pluto.getAllPrivateKeys().first() assertNotNull(privateKeys) - assertTrue(privateKeys!!.isNotEmpty()) + assertTrue(privateKeys.isNotEmpty()) assertEquals(1, privateKeys.size) - val didKeyLinks = pluto?.getAllDIDKeyLinkData()?.first() + val didKeyLinks = pluto.getAllDIDKeyLinkData().first() assertNotNull(didKeyLinks) - assertTrue(didKeyLinks!!.isNotEmpty()) + assertTrue(didKeyLinks.isNotEmpty()) assertEquals(1, didKeyLinks.size) } @Test fun `test storePeerDID stores DID`() = runTest { val peerDID = DID("did:peer:test") - pluto?.start() - pluto?.storePeerDID(peerDID) + pluto.start() + pluto.storePeerDID(peerDID) - val dids = pluto?.getAllDIDs()?.first() + val dids = pluto.getAllDIDs().first() assertNotNull(dids) - assertTrue(dids!!.isNotEmpty()) + assertTrue(dids.isNotEmpty()) assertEquals(1, dids.size) assertEquals(peerDID.toString(), dids.first().toString()) } @@ -105,12 +106,12 @@ class PlutoImplTest { val host = DID("did:peer:host") val receiver = DID("did:peer:receiver") val alias = "alias" - pluto?.start() - pluto?.storeDIDPair(host, receiver, alias) + pluto.start() + pluto.storeDIDPair(host, receiver, alias) - val didPairs = pluto?.getAllDidPairs()?.first() + val didPairs = pluto.getAllDidPairs().first() assertNotNull(didPairs) - assertTrue(didPairs!!.isNotEmpty()) + assertTrue(didPairs.isNotEmpty()) assertEquals(1, didPairs.size) val didPair = didPairs.first() assertEquals(host.toString(), didPair.holder.toString()) @@ -127,12 +128,12 @@ class PlutoImplTest { body = "{}" ) - pluto?.start() - pluto?.storeMessage(message) + pluto.start() + pluto.storeMessage(message) - val messages = pluto?.getAllMessages()?.first() + val messages = pluto.getAllMessages().first() assertNotNull(messages) - assertTrue(messages!!.isNotEmpty()) + assertTrue(messages.isNotEmpty()) assertEquals(1, messages.size) val msg = messages.first() assertEquals(message, msg) @@ -143,22 +144,22 @@ class PlutoImplTest { val secp256PrivateKey = Secp256k1KeyPair.generateKeyPair().privateKey val did = DID("did:peer:example") - pluto?.start() - pluto?.storePrivateKeys( + pluto.start() + pluto.storePrivateKeys( storableKey = secp256PrivateKey as StorableKey, did = did, keyPathIndex = 0, metaId = "" ) - val privateKeys = pluto?.getAllPrivateKeys()?.first() + val privateKeys = pluto.getAllPrivateKeys().first() assertNotNull(privateKeys) - assertTrue(privateKeys!!.isNotEmpty()) + assertTrue(privateKeys.isNotEmpty()) assertEquals(1, privateKeys.size) - val didKeyLinks = pluto?.getAllDIDKeyLinkData()?.first() + val didKeyLinks = pluto.getAllDIDKeyLinkData().first() assertNotNull(didKeyLinks) - assertTrue(didKeyLinks!!.isNotEmpty()) + assertTrue(didKeyLinks.isNotEmpty()) assertEquals(1, didKeyLinks.size) } @@ -166,12 +167,12 @@ class PlutoImplTest { fun `test storePrivate`() = runTest { val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey - pluto?.start() - pluto?.storePrivate(privateKey as StorableKey, recoveryId = "recoveryId") + pluto.start() + pluto.storePrivate(privateKey as StorableKey, recoveryId = "recoveryId") - val privateKeys = pluto?.getAllPrivateKeys()?.first() + val privateKeys = pluto.getAllPrivateKeys().first() assertNotNull(privateKeys) - assertTrue(privateKeys!!.isNotEmpty()) + assertTrue(privateKeys.isNotEmpty()) assertEquals(1, privateKeys.size) assertEquals("recoveryId", privateKeys.first().restorationIdentifier) } @@ -192,12 +193,12 @@ class PlutoImplTest { body = "{}" ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val messages = pluto?.getAllMessages()?.first() + val messages = pluto.getAllMessages().first() assertNotNull(messages) - assertTrue(messages!!.isNotEmpty()) + assertTrue(messages.isNotEmpty()) assertEquals(2, messages.size) val msg = messages.first() assertEquals(message, msg) @@ -211,22 +212,22 @@ class PlutoImplTest { val hostDID = DID("did:peer:host") val routingDID = DID("did:peer:routing") - pluto?.start() - pluto?.storePeerDID(hostDID) - pluto?.storeMediator(mediatorDID, hostDID, routingDID) + pluto.start() + pluto.storePeerDID(hostDID) + pluto.storeMediator(mediatorDID, hostDID, routingDID) - val dids = pluto?.getAllDIDs()?.first() + val dids = pluto.getAllDIDs().first() assertNotNull(dids) - assertTrue(dids!!.isNotEmpty()) + assertTrue(dids.isNotEmpty()) assertEquals(3, dids.size) val didsString = dids.map { it.toString() } assertTrue(didsString.contains(mediatorDID.toString())) assertTrue(didsString.contains(hostDID.toString())) assertTrue(didsString.contains(routingDID.toString())) - val mediators = pluto?.getAllMediators()?.first() + val mediators = pluto.getAllMediators().first() assertNotNull(mediators) - assertTrue(mediators!!.isNotEmpty()) + assertTrue(mediators.isNotEmpty()) assertEquals(1, mediators.size) val mediator = mediators.first() assertEquals(mediatorDID.toString(), mediator.mediatorDID.toString()) @@ -240,12 +241,12 @@ class PlutoImplTest { "eyJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6cHJpc206MjU3MTlhOTZiMTUxMjA3MTY5ODFhODQzMGFkMGNiOTY4ZGQ1MzQwNzM1OTNjOGNkM2YxZDI3YTY4MDRlYzUwZTpDcG9DQ3BjQ0Vsb0tCV3RsZVMweEVBSkNUd29KYzJWamNESTFObXN4RWlBRW9TQ241dHlEYTZZNnItSW1TcXBKOFkxbWo3SkMzX29VekUwTnl5RWlDQm9nc2dOYWVSZGNDUkdQbGU4MlZ2OXRKZk53bDZyZzZWY2hSM09xaGlWYlRhOFNXd29HWVhWMGFDMHhFQVJDVHdvSmMyVmpjREkxTm1zeEVpRE1rQmQ2RnRpb0prM1hPRnUtX2N5NVhtUi00dFVRMk5MR2lXOGFJU29ta1JvZzZTZGU5UHduRzBRMFNCVG1GU1REYlNLQnZJVjZDVExYcmpJSnR0ZUdJbUFTWEFvSGJXRnpkR1Z5TUJBQlFrOEtDWE5sWTNBeU5UWnJNUklnTzcxMG10MVdfaXhEeVFNM3hJczdUcGpMQ05PRFF4Z1ZoeDVzaGZLTlgxb2FJSFdQcnc3SVVLbGZpYlF0eDZKazRUU2pnY1dOT2ZjT3RVOUQ5UHVaN1Q5dCIsInN1YiI6ImRpZDpwcmlzbTpiZWVhNTIzNGFmNDY4MDQ3MTRkOGVhOGVjNzdiNjZjYzdmM2U4MTVjNjhhYmI0NzVmMjU0Y2Y5YzMwNjI2NzYzOkNzY0JDc1FCRW1RS0QyRjFkR2hsYm5ScFkyRjBhVzl1TUJBRVFrOEtDWE5sWTNBeU5UWnJNUklnZVNnLTJPTzFKZG5welVPQml0eklpY1hkZnplQWNUZldBTi1ZQ2V1Q2J5SWFJSlE0R1RJMzB0YVZpd2NoVDNlMG5MWEJTNDNCNGo5amxzbEtvMlpsZFh6akVsd0tCMjFoYzNSbGNqQVFBVUpQQ2dselpXTndNalUyYXpFU0lIa29QdGpqdFNYWjZjMURnWXJjeUluRjNYODNnSEUzMWdEZm1BbnJnbThpR2lDVU9Ca3lOOUxXbFlzSElVOTN0Snkxd1V1TndlSV9ZNWJKU3FObVpYVjg0dyIsIm5iZiI6MTY4NTYzMTk5NSwiZXhwIjoxNjg1NjM1NTk1LCJ2YyI6eyJjcmVkZW50aWFsU3ViamVjdCI6eyJhZGRpdGlvbmFsUHJvcDIiOiJUZXN0MyIsImlkIjoiZGlkOnByaXNtOmJlZWE1MjM0YWY0NjgwNDcxNGQ4ZWE4ZWM3N2I2NmNjN2YzZTgxNWM2OGFiYjQ3NWYyNTRjZjljMzA2MjY3NjM6Q3NjQkNzUUJFbVFLRDJGMWRHaGxiblJwWTJGMGFXOXVNQkFFUWs4S0NYTmxZM0F5TlRack1SSWdlU2ctMk9PMUpkbnB6VU9CaXR6SWljWGRmemVBY1RmV0FOLVlDZXVDYnlJYUlKUTRHVEkzMHRhVml3Y2hUM2UwbkxYQlM0M0I0ajlqbHNsS28yWmxkWHpqRWx3S0IyMWhjM1JsY2pBUUFVSlBDZ2x6WldOd01qVTJhekVTSUhrb1B0amp0U1haNmMxRGdZcmN5SW5GM1g4M2dIRTMxZ0RmbUFucmdtOGlHaUNVT0JreU45TFdsWXNISVU5M3RKeTF3VXVOd2VJX1k1YkpTcU5tWlhWODR3In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiQGNvbnRleHQiOlsiaHR0cHM6XC9cL3d3dy53My5vcmdcLzIwMThcL2NyZWRlbnRpYWxzXC92MSJdfX0.x0SF17Y0VCDmt7HceOdTxfHlofsZmY18Rn6VQb0-r-k_Bm3hTi1-k2vkdjB25hdxyTCvxam-AkAP-Ag3Ahn5Ng" ) - pluto?.start() - pluto?.storeCredential(credential.toStorableCredential()) + pluto.start() + pluto.storeCredential(credential.toStorableCredential()) - val credentials = pluto?.getAllCredentials()?.first() + val credentials = pluto.getAllCredentials().first() assertNotNull(credentials) - assertTrue(credentials!!.isNotEmpty()) + assertTrue(credentials.isNotEmpty()) assertEquals(1, credentials.size) assertEquals("jwt+credential", credentials.first().restorationId) } @@ -253,10 +254,10 @@ class PlutoImplTest { @Test fun `test storeLinkSecret`() = runTest { val linkSecret = "linkSecret" - pluto?.start() - pluto?.storeLinkSecret(linkSecret) + pluto.start() + pluto.storeLinkSecret(linkSecret) - val linkSecretString = pluto?.getLinkSecret()?.first() + val linkSecretString = pluto.getLinkSecret().first() assertEquals(linkSecret, linkSecretString) } @@ -266,25 +267,25 @@ class PlutoImplTest { val linkSecretName = "linkSecretName" val json = "{}" - pluto?.start() - pluto?.storeCredentialMetadata(name, linkSecretName, json) + pluto.start() + pluto.storeCredentialMetadata(name, linkSecretName, json) - val credentialMetadata = pluto?.getCredentialMetadata(linkSecretName)?.first() + val credentialMetadata = pluto.getCredentialMetadata(linkSecretName).first() assertNotNull(credentialMetadata) - assertEquals(linkSecretName, credentialMetadata!!.linkSecretName) - assertEquals(json, credentialMetadata.json) + assertEquals(linkSecretName, credentialMetadata?.linkSecretName) + assertEquals(json, credentialMetadata?.json) } @Test fun `test getAllPrismDIDs`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, null, null, listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, null, null, listOf(privateKey)) - val allPrismDids = pluto?.getAllPrismDIDs()?.first() + val allPrismDids = pluto.getAllPrismDIDs().first() assertNotNull(allPrismDids) - assertEquals(1, allPrismDids!!.size) + assertEquals(1, allPrismDids.size) val prismDidInfo = allPrismDids.first() assertEquals(prismDID.toString(), prismDidInfo.did.toString()) assertNull(prismDidInfo.keyPathIndex) @@ -295,24 +296,24 @@ class PlutoImplTest { fun `test getDIDInfoByDID`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, null, null, listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, null, null, listOf(privateKey)) - val prismDidInfo = pluto?.getDIDInfoByDID(prismDID)?.first() + val prismDidInfo = pluto.getDIDInfoByDID(prismDID).first() assertNotNull(prismDidInfo) - assertEquals(prismDID.toString(), prismDidInfo!!.did.toString()) + assertEquals(prismDID.toString(), prismDidInfo?.did.toString()) } @Test fun `test getDIDInfoByAlias`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) - val prismDidInfo = pluto?.getDIDInfoByAlias("alias")?.first() + val prismDidInfo = pluto.getDIDInfoByAlias("alias").first() assertNotNull(prismDidInfo) - assertEquals(1, prismDidInfo!!.size) + assertEquals(1, prismDidInfo.size) assertEquals(prismDID.toString(), prismDidInfo.first().did.toString()) } @@ -320,12 +321,12 @@ class PlutoImplTest { fun `test getDIDPrivateKeysByDID`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) - val storablePrivateKey = pluto?.getDIDPrivateKeysByDID(prismDID)?.first() + val storablePrivateKey = pluto.getDIDPrivateKeysByDID(prismDID).first() assertNotNull(storablePrivateKey) - assertEquals(1, storablePrivateKey!!.size) + assertEquals(1, storablePrivateKey.size) val storableKey = storablePrivateKey.first() assertEquals("secp256k1+priv", storableKey.restorationIdentifier) assertEquals(privateKey.storableData.base64UrlEncoded, storableKey.data) @@ -335,23 +336,23 @@ class PlutoImplTest { fun `test getDIDPrivateKeyByID`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, null, "alias", listOf(privateKey)) - val storablePrivateKey = pluto?.getDIDPrivateKeyByID(prismDID.toString())?.first() + val storablePrivateKey = pluto.getDIDPrivateKeyByID(prismDID.toString()).first() assertNotNull(storablePrivateKey) - assertEquals("secp256k1+priv", storablePrivateKey!!.restorationIdentifier) - assertEquals(privateKey.storableData.base64UrlEncoded, storablePrivateKey.data) + assertEquals("secp256k1+priv", storablePrivateKey?.restorationIdentifier) + assertEquals(privateKey.storableData.base64UrlEncoded, storablePrivateKey?.data) } @Test fun `test getPrismDIDKeyPathIndex`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, 99, "alias", listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, 99, "alias", listOf(privateKey)) - val keyPathIndex = pluto?.getPrismDIDKeyPathIndex(prismDID)?.first() + val keyPathIndex = pluto.getPrismDIDKeyPathIndex(prismDID).first() assertNotNull(keyPathIndex) assertEquals(99, keyPathIndex) } @@ -360,11 +361,11 @@ class PlutoImplTest { fun `test getPrismLastKeyPathIndex`() = runTest { val prismDID = DID("did:prism:test") val privateKey = Secp256k1KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePrismDIDAndPrivateKeys(prismDID, 10, "alias", listOf(privateKey)) - pluto?.storePrismDIDAndPrivateKeys(prismDID, 9, "alias", listOf(privateKey)) + pluto.start() + pluto.storePrismDIDAndPrivateKeys(prismDID, 10, "alias", listOf(privateKey)) + pluto.storePrismDIDAndPrivateKeys(prismDID, 9, "alias", listOf(privateKey)) - val keyPathIndex = pluto?.getPrismLastKeyPathIndex()?.first() + val keyPathIndex = pluto.getPrismLastKeyPathIndex().first() assertNotNull(keyPathIndex) assertEquals(10, keyPathIndex) } @@ -378,19 +379,19 @@ class PlutoImplTest { val peerDID3 = DID("did:peer:test3") val privateKey3 = X25519KeyPair.generateKeyPair().privateKey as StorableKey - pluto?.start() - pluto?.storePeerDID(peerDID1) - pluto?.storePrivateKeys(privateKey1, peerDID1, null, "$peerDID1#key-1") - pluto?.storePeerDID(peerDID2) - pluto?.storePrivateKeys(privateKey2, peerDID2, null, "$peerDID2#key-1") - pluto?.storePeerDID(peerDID3) - pluto?.storePrivateKeys(privateKey3, peerDID3, null, "$peerDID3#key-1") + pluto.start() + pluto.storePeerDID(peerDID1) + pluto.storePrivateKeys(privateKey1, peerDID1, null, "$peerDID1#key-1") + pluto.storePeerDID(peerDID2) + pluto.storePrivateKeys(privateKey2, peerDID2, null, "$peerDID2#key-1") + pluto.storePeerDID(peerDID3) + pluto.storePrivateKeys(privateKey3, peerDID3, null, "$peerDID3#key-1") - val peerDIDs = pluto?.getAllPeerDIDs()?.first() + val peerDIDs = pluto.getAllPeerDIDs().first() assertNotNull(peerDIDs) - assertEquals(3, peerDIDs?.size) + assertEquals(3, peerDIDs.size) - assertEquals(peerDID1.toString(), peerDIDs!![0].did.toString()) + assertEquals(peerDID1.toString(), peerDIDs[0].did.toString()) assertEquals(1, peerDIDs[0].privateKeys.size) assertContentEquals(privateKey1.storableData, peerDIDs[0].privateKeys.first().raw) @@ -409,16 +410,16 @@ class PlutoImplTest { val peerDID2 = DID("did:peer:test2") val peerDID3 = DID("did:peer:test3") - pluto?.start() - pluto?.storePeerDID(peerDID1) - pluto?.storePeerDID(peerDID2) - pluto?.storePeerDID(peerDID3) + pluto.start() + pluto.storePeerDID(peerDID1) + pluto.storePeerDID(peerDID2) + pluto.storePeerDID(peerDID3) - val dids = pluto?.getAllDIDs()?.first() + val dids = pluto.getAllDIDs().first() assertNotNull(dids) - assertEquals(3, dids?.size) + assertEquals(3, dids.size) - assertEquals(peerDID1.toString(), dids!![0].toString()) + assertEquals(peerDID1.toString(), dids[0].toString()) assertEquals(peerDID2.toString(), dids[1].toString()) assertEquals(peerDID3.toString(), dids[2].toString()) } @@ -432,19 +433,19 @@ class PlutoImplTest { val receiverPeerDID2 = DID("did:peer:test3") val name2 = "name2" - pluto?.start() - pluto?.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) - pluto?.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) + pluto.start() + pluto.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) + pluto.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) - val didPairs = pluto?.getAllDidPairs()?.first() + val didPairs = pluto.getAllDidPairs().first() assertNotNull(didPairs) - assertEquals(2, didPairs?.size) + assertEquals(2, didPairs.size) - assertEquals(hostPeerDID1.toString(), didPairs!![0].holder.toString()) + assertEquals(hostPeerDID1.toString(), didPairs[0].holder.toString()) assertEquals(receiverPeerDID1.toString(), didPairs[0].receiver.toString()) assertEquals(name1, didPairs[0].name) - assertEquals(hostPeerDID2.toString(), didPairs!![1].holder.toString()) + assertEquals(hostPeerDID2.toString(), didPairs[1].holder.toString()) assertEquals(receiverPeerDID2.toString(), didPairs[1].receiver.toString()) assertEquals(name2, didPairs[1].name) } @@ -458,16 +459,16 @@ class PlutoImplTest { val receiverPeerDID2 = DID("did:peer:test3") val name2 = "name2" - pluto?.start() - pluto?.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) - pluto?.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) + pluto.start() + pluto.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) + pluto.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) - val didPair = pluto?.getPairByDID(hostPeerDID2)?.first() + val didPair = pluto.getPairByDID(hostPeerDID2).first() assertNotNull(didPair) - assertEquals(hostPeerDID2.toString(), didPair!!.holder.toString()) - assertEquals(receiverPeerDID2.toString(), didPair.receiver.toString()) - assertEquals(name2, didPair.name) + assertEquals(hostPeerDID2.toString(), didPair?.holder.toString()) + assertEquals(receiverPeerDID2.toString(), didPair?.receiver.toString()) + assertEquals(name2, didPair?.name) } @Test @@ -479,16 +480,16 @@ class PlutoImplTest { val receiverPeerDID2 = DID("did:peer:test3") val name2 = "name2" - pluto?.start() - pluto?.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) - pluto?.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) + pluto.start() + pluto.storeDIDPair(hostPeerDID1, receiverPeerDID1, name1) + pluto.storeDIDPair(hostPeerDID2, receiverPeerDID2, name2) - val didPair = pluto?.getPairByName(name2)?.first() + val didPair = pluto.getPairByName(name2).first() assertNotNull(didPair) - assertEquals(hostPeerDID2.toString(), didPair!!.holder.toString()) - assertEquals(receiverPeerDID2.toString(), didPair.receiver.toString()) - assertEquals(name2, didPair.name) + assertEquals(hostPeerDID2.toString(), didPair?.holder.toString()) + assertEquals(receiverPeerDID2.toString(), didPair?.receiver.toString()) + assertEquals(name2, didPair?.name) } @Test @@ -507,14 +508,14 @@ class PlutoImplTest { body = "{}" ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val messages = pluto?.getAllMessages(message.from!!, message.to!!)?.first() + val messages = pluto.getAllMessages(message.from!!, message.to!!).first() assertNotNull(messages) - assertEquals(1, messages?.size) + assertEquals(1, messages.size) - assertEquals(message.piuri, messages!![0].piuri) + assertEquals(message.piuri, messages[0].piuri) assertEquals(message.from, messages[0].from) assertEquals(message.to, messages[0].to) } @@ -536,20 +537,20 @@ class PlutoImplTest { direction = Message.Direction.SENT ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val messages = pluto?.getAllMessagesSent()?.first() + val messages = pluto.getAllMessagesSent().first() assertNotNull(messages) - assertEquals(1, messages?.size) + assertEquals(1, messages.size) - assertEquals(message1.piuri, messages!![0].piuri) + assertEquals(message1.piuri, messages[0].piuri) assertEquals(message1.from, messages[0].from) assertEquals(message1.to, messages[0].to) - val messages1 = pluto?.getAllMessagesReceived()?.first() + val messages1 = pluto.getAllMessagesReceived().first() assertNotNull(messages1) - assertEquals(1, messages1!!.size) + assertEquals(1, messages1.size) assertEquals(message.piuri, messages1[0].piuri) assertEquals(message.from, messages1[0].from) @@ -573,16 +574,16 @@ class PlutoImplTest { direction = Message.Direction.SENT ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val messages = pluto?.getAllMessagesSentTo(message1.to!!)?.first() + val messages = pluto.getAllMessagesSentTo(message1.to!!).first() assertNotNull(messages) - assertEquals(1, messages?.size) + assertEquals(1, messages.size) - assertEquals(message1.piuri, messages!![0].piuri) + assertEquals(message1.piuri, messages[0].piuri) assertEquals(message1.from, messages[0].from) - assertEquals(message1.to, messages[0].to) + assertEquals(message1.to, messages[0].to!!) } @Test @@ -602,16 +603,16 @@ class PlutoImplTest { direction = Message.Direction.SENT ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val messages = pluto?.getAllMessagesSentTo(message.to!!)?.first() + val messages = pluto.getAllMessagesSentTo(message.to!!).first() assertNotNull(messages) - assertEquals(1, messages?.size) + assertEquals(1, messages.size) - assertEquals(message.piuri, messages!![0].piuri) - assertEquals(message.from, messages[0].from) - assertEquals(message.to, messages[0].to) + assertEquals(message.piuri, messages[0].piuri) + assertEquals(message.from, messages[0].from!!) + assertEquals(message.to, messages[0].to!!) } @Test @@ -632,15 +633,15 @@ class PlutoImplTest { thid = UUID.randomUUID().toString() ) - pluto?.start() - pluto?.storeMessages(listOf(message, message1)) + pluto.start() + pluto.storeMessages(listOf(message, message1)) - val message2 = pluto?.getMessageByThidAndPiuri(message1.thid!!, piuri = message1.piuri)?.first() + val message2 = pluto.getMessageByThidAndPiuri(message1.thid!!, piuri = message1.piuri).first() assertNotNull(message2) - assertEquals(message1.piuri, message2!!.piuri) - assertEquals(message1.from, message2.from) - assertEquals(message1.to, message2.to) + assertEquals(message1.piuri, message2?.piuri) + assertEquals(message1.from, message2?.from) + assertEquals(message1.to, message2?.to) } @Test @@ -649,16 +650,16 @@ class PlutoImplTest { "eyJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6cHJpc206MjU3MTlhOTZiMTUxMjA3MTY5ODFhODQzMGFkMGNiOTY4ZGQ1MzQwNzM1OTNjOGNkM2YxZDI3YTY4MDRlYzUwZTpDcG9DQ3BjQ0Vsb0tCV3RsZVMweEVBSkNUd29KYzJWamNESTFObXN4RWlBRW9TQ241dHlEYTZZNnItSW1TcXBKOFkxbWo3SkMzX29VekUwTnl5RWlDQm9nc2dOYWVSZGNDUkdQbGU4MlZ2OXRKZk53bDZyZzZWY2hSM09xaGlWYlRhOFNXd29HWVhWMGFDMHhFQVJDVHdvSmMyVmpjREkxTm1zeEVpRE1rQmQ2RnRpb0prM1hPRnUtX2N5NVhtUi00dFVRMk5MR2lXOGFJU29ta1JvZzZTZGU5UHduRzBRMFNCVG1GU1REYlNLQnZJVjZDVExYcmpJSnR0ZUdJbUFTWEFvSGJXRnpkR1Z5TUJBQlFrOEtDWE5sWTNBeU5UWnJNUklnTzcxMG10MVdfaXhEeVFNM3hJczdUcGpMQ05PRFF4Z1ZoeDVzaGZLTlgxb2FJSFdQcnc3SVVLbGZpYlF0eDZKazRUU2pnY1dOT2ZjT3RVOUQ5UHVaN1Q5dCIsInN1YiI6ImRpZDpwcmlzbTpiZWVhNTIzNGFmNDY4MDQ3MTRkOGVhOGVjNzdiNjZjYzdmM2U4MTVjNjhhYmI0NzVmMjU0Y2Y5YzMwNjI2NzYzOkNzY0JDc1FCRW1RS0QyRjFkR2hsYm5ScFkyRjBhVzl1TUJBRVFrOEtDWE5sWTNBeU5UWnJNUklnZVNnLTJPTzFKZG5welVPQml0eklpY1hkZnplQWNUZldBTi1ZQ2V1Q2J5SWFJSlE0R1RJMzB0YVZpd2NoVDNlMG5MWEJTNDNCNGo5amxzbEtvMlpsZFh6akVsd0tCMjFoYzNSbGNqQVFBVUpQQ2dselpXTndNalUyYXpFU0lIa29QdGpqdFNYWjZjMURnWXJjeUluRjNYODNnSEUzMWdEZm1BbnJnbThpR2lDVU9Ca3lOOUxXbFlzSElVOTN0Snkxd1V1TndlSV9ZNWJKU3FObVpYVjg0dyIsIm5iZiI6MTY4NTYzMTk5NSwiZXhwIjoxNjg1NjM1NTk1LCJ2YyI6eyJjcmVkZW50aWFsU3ViamVjdCI6eyJhZGRpdGlvbmFsUHJvcDIiOiJUZXN0MyIsImlkIjoiZGlkOnByaXNtOmJlZWE1MjM0YWY0NjgwNDcxNGQ4ZWE4ZWM3N2I2NmNjN2YzZTgxNWM2OGFiYjQ3NWYyNTRjZjljMzA2MjY3NjM6Q3NjQkNzUUJFbVFLRDJGMWRHaGxiblJwWTJGMGFXOXVNQkFFUWs4S0NYTmxZM0F5TlRack1SSWdlU2ctMk9PMUpkbnB6VU9CaXR6SWljWGRmemVBY1RmV0FOLVlDZXVDYnlJYUlKUTRHVEkzMHRhVml3Y2hUM2UwbkxYQlM0M0I0ajlqbHNsS28yWmxkWHpqRWx3S0IyMWhjM1JsY2pBUUFVSlBDZ2x6WldOd01qVTJhekVTSUhrb1B0amp0U1haNmMxRGdZcmN5SW5GM1g4M2dIRTMxZ0RmbUFucmdtOGlHaUNVT0JreU45TFdsWXNISVU5M3RKeTF3VXVOd2VJX1k1YkpTcU5tWlhWODR3In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiQGNvbnRleHQiOlsiaHR0cHM6XC9cL3d3dy53My5vcmdcLzIwMThcL2NyZWRlbnRpYWxzXC92MSJdfX0.x0SF17Y0VCDmt7HceOdTxfHlofsZmY18Rn6VQb0-r-k_Bm3hTi1-k2vkdjB25hdxyTCvxam-AkAP-Ag3Ahn5Ng" ) - pluto?.start() - pluto?.storeCredential(credential.toStorableCredential()) + pluto.start() + pluto.storeCredential(credential.toStorableCredential()) - val observeRevoked = pluto?.observeRevokedCredentials() + val observeRevoked = pluto.observeRevokedCredentials() - pluto?.revokeCredential(credential.id) - val credentials = pluto?.getAllCredentials()?.first() + pluto.revokeCredential(credential.id) + val credentials = pluto.getAllCredentials().first() assertNotNull(credentials) - val cred = credentials!!.first() + val cred = credentials.first() assertTrue(cred.revoked) - assertEquals(1, observeRevoked?.first()?.size) + assertEquals(1, observeRevoked.first().size) } } diff --git a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt index 557c90a24..037aec172 100644 --- a/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt +++ b/edge-agent-sdk/src/commonTest/kotlin/org/hyperledger/identus/walletsdk/pollux/PolluxImplTest.kt @@ -20,7 +20,10 @@ import junit.framework.TestCase.assertTrue import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import org.didcommx.didcomm.common.Typ +import org.hyperledger.identus.apollo.base64.base64UrlDecoded import org.hyperledger.identus.apollo.derivation.MnemonicHelper import org.hyperledger.identus.walletsdk.apollo.ApolloImpl import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519KeyPair @@ -50,7 +53,7 @@ import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.AnoncredsPresentationOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.JWTPresentationOptions import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.PresentationSubmissionOptionsJWT -import org.hyperledger.identus.walletsdk.logger.PrismLogger +import org.hyperledger.identus.walletsdk.logger.Logger import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential import org.hyperledger.identus.walletsdk.pollux.models.AnoncredsPresentationDefinitionRequest import org.hyperledger.identus.walletsdk.pollux.models.JWTCredential @@ -81,7 +84,7 @@ class PolluxImplTest { lateinit var castorMock: Castor @Mock - lateinit var loggerMock: PrismLogger + lateinit var loggerMock: Logger @Before fun setup() { @@ -330,7 +333,7 @@ class PolluxImplTest { @Test fun testCreatePresentationSubmission_whenAllCorrect_thenPresentationSubmissionProofWellFormed() = runTest { - val loggerMock = mock() + val loggerMock = mock() val castor: Castor = CastorImpl(apollo = ApolloImpl(), loggerMock) val issuerKeyPair = @@ -1154,6 +1157,34 @@ class PolluxImplTest { assertFalse(pollux.verifyStatusListIndexForEncodedList(encodedList, 3)) } + @Test + fun `Test signClaims for JWT including kid`() = runTest { + pollux = PolluxImpl(apollo, castor, api) + val keyPair = Secp256k1KeyPair.generateKeyPair() + + val did = + DID("did:prism:cd6cf9f94a43c53e286b0f2015c0083701350a694f52a22ee02e3bd29d93eba9:CrQBCrEBEjsKB21hc3RlcjAQAUouCglzZWNwMjU2azESIQKJIokEe_iKRGsr0f2EEa1JHGm59g0qP7QMtw6FcVxW9xJDCg9hdXRoZW50aWNhdGlvbjAQBEouCglzZWNwMjU2azESIQKJIokEe_iKRGsr0f2EEa1JHGm59g0qP7QMtw6FcVxW9xotCgojZGlkY29tbS0xEhBESURDb21tTWVzc2FnaW5nGg1kaWQ6cGVlcjp0ZXN0") + val domain = "domain" + val challenge = "challenge" + val credential = JWTCredential.fromJwtString( + "eyJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6cHJpc206ZTAyZTgwOTlkNTAzNTEzNDVjNWRkODMxYTllOTExMmIzOTRhODVkMDA2NGEyZWI1OTQyOTA4MDczNGExNTliNjpDcmtCQ3JZQkVqb0tCbUYxZEdndE1SQUVTaTRLQ1hObFkzQXlOVFpyTVJJaEF1Vlljb3JmV25MMGZZdEE1dmdKSzRfLW9iM2JVRGMtdzJVT0hkTzNRRXZxRWpzS0IybHpjM1ZsTFRFUUFrb3VDZ2x6WldOd01qVTJhekVTSVFMQ3U5Tm50cXVwQmotME5DZE1BNzV6UmVCZXlhQ0pPMWFHWWVQNEJNUUhWQkk3Q2dkdFlYTjBaWEl3RUFGS0xnb0pjMlZqY0RJMU5tc3hFaUVET1dndlF4NnZSdTZ3VWI0RlljSnVhRUNqOUJqUE1KdlJwOEx3TTYxaEVUNCIsInN1YiI6ImRpZDpwcmlzbTpiZDgxZmY1NDQzNDJjMTAwNDZkZmE0YmEyOTVkNWIzNmU0Y2ZlNWE3ZWIxMjBlMTBlZTVjMjQ4NzAwNjUxMDA5OkNvVUJDb0lCRWpzS0IyMWhjM1JsY2pBUUFVb3VDZ2x6WldOd01qVTJhekVTSVFQdjVQNXl5Z3Jad2FKbFl6bDU5bTJIQURLVFhVTFBzUmUwa2dlRUh2dExnQkpEQ2c5aGRYUm9aVzUwYVdOaGRHbHZiakFRQkVvdUNnbHpaV053TWpVMmF6RVNJUVB2NVA1eXlnclp3YUpsWXpsNTltMkhBREtUWFVMUHNSZTBrZ2VFSHZ0TGdBIiwibmJmIjoxNzE1MDAwNjc0LCJleHAiOjE3MTg2MDA2NzQsInZjIjp7ImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImVtYWlsQWRkcmVzcyI6ImNyaXN0aWFuLmNhc3Ryb0Bpb2hrLmlvIiwiaWQiOiJkaWQ6cHJpc206YmQ4MWZmNTQ0MzQyYzEwMDQ2ZGZhNGJhMjk1ZDViMzZlNGNmZTVhN2ViMTIwZTEwZWU1YzI0ODcwMDY1MTAwOTpDb1VCQ29JQkVqc0tCMjFoYzNSbGNqQVFBVW91Q2dselpXTndNalUyYXpFU0lRUHY1UDV5eWdyWndhSmxZemw1OW0ySEFES1RYVUxQc1JlMGtnZUVIdnRMZ0JKRENnOWhkWFJvWlc1MGFXTmhkR2x2YmpBUUJFb3VDZ2x6WldOd01qVTJhekVTSVFQdjVQNXl5Z3Jad2FKbFl6bDU5bTJIQURLVFhVTFBzUmUwa2dlRUh2dExnQSJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sIkBjb250ZXh0IjpbImh0dHBzOlwvXC93d3cudzMub3JnXC8yMDE4XC9jcmVkZW50aWFsc1wvdjEiXSwiY3JlZGVudGlhbFN0YXR1cyI6eyJzdGF0dXNQdXJwb3NlIjoiUmV2b2NhdGlvbiIsInN0YXR1c0xpc3RJbmRleCI6MjUsImlkIjoiaHR0cDpcL1wvMTAuOTEuMTAwLjEyNjo4MDAwXC9wcmlzbS1hZ2VudFwvY3JlZGVudGlhbC1zdGF0dXNcLzUxNGU4NTI4LTRiMzgtNDc3YS1iMGU0LTMyNGJiZTIyMDQ2NCMyNSIsInR5cGUiOiJTdGF0dXNMaXN0MjAyMUVudHJ5Iiwic3RhdHVzTGlzdENyZWRlbnRpYWwiOiJodHRwOlwvXC8xMC45MS4xMDAuMTI2OjgwMDBcL3ByaXNtLWFnZW50XC9jcmVkZW50aWFsLXN0YXR1c1wvNTE0ZTg1MjgtNGIzOC00NzdhLWIwZTQtMzI0YmJlMjIwNDY0In19fQ.5OmmL5tdcRKugiHVt01PJUhp9r22zgMPPALUOB41g_1_BPHE3ezqJ2QhSmScU_EOGYcKksHftdrvfoND65nSjw" + ) + val signedClaims = pollux.signClaims( + subjectDID = did, + privateKey = keyPair.privateKey, + domain = domain, + challenge = challenge, + credential = credential + ) + assertTrue(signedClaims.contains(".")) + val splits = signedClaims.split(".") + val header = splits[0].base64UrlDecoded + val json = Json.parseToJsonElement(header) + assertTrue(json.jsonObject.containsKey("kid")) + val kid = json.jsonObject["kid"]!!.jsonPrimitive.content + assertEquals("did:prism:cd6cf9f94a43c53e286b0f2015c0083701350a694f52a22ee02e3bd29d93eba9:CrQBCrEBEjsKB21hc3RlcjAQAUouCglzZWNwMjU2azESIQKJIokEe_iKRGsr0f2EEa1JHGm59g0qP7QMtw6FcVxW9xJDCg9hdXRoZW50aWNhdGlvbjAQBEouCglzZWNwMjU2azESIQKJIokEe_iKRGsr0f2EEa1JHGm59g0qP7QMtw6FcVxW9xotCgojZGlkY29tbS0xEhBESURDb21tTWVzc2FnaW5nGg1kaWQ6cGVlcjp0ZXN0#authentication0", kid) + } + private suspend fun createVerificationTestCase(testCaseOptions: VerificationTestCase): Triple { val currentDate = Calendar.getInstance() val nextMonthDate = currentDate.clone() as Calendar diff --git a/edge-agent-sdk/src/jvmMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt b/edge-agent-sdk/src/jvmMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt index 0676ac992..653dfe714 100644 --- a/edge-agent-sdk/src/jvmMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt +++ b/edge-agent-sdk/src/jvmMain/kotlin/org/hyperledger/identus/walletsdk/pluto/data/DbConnectionImpl.kt @@ -6,6 +6,7 @@ import org.hyperledger.identus.walletsdk.SdkPlutoDb actual class DbConnectionImpl actual constructor() : DbConnection { actual override var driver: SqlDriver? = null + actual override suspend fun connectDb(context: Any?): SqlDriver { val driver = JdbcSqliteDriver("jdbc:sqlite:prism.db") SdkPlutoDb.Schema.create(driver) diff --git a/gradle.properties b/gradle.properties index e18b58edf..67217c908 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version = 3.0.0 +version = 4.0.0 org.gradle.jvmargs = -Xmx3072M -Dkotlin.daemon.jvm.options="-Xmx3072M" kotlin.code.style = official android.useAndroidX = true diff --git a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/Sdk.kt b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/Sdk.kt index 4d76e439e..76f38f7a5 100644 --- a/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/Sdk.kt +++ b/sampleapp/src/main/java/org/hyperledger/identus/walletsdk/sampleapp/Sdk.kt @@ -178,7 +178,7 @@ class Sdk { mercury = mercury, pollux = pollux, seed = seed, - mediatorHandler = handler + mediatorHandler = handler, ) } diff --git a/tests/end-to-end/README.md b/tests/end-to-end/README.md index b03ae0c45..f529ec9fe 100644 --- a/tests/end-to-end/README.md +++ b/tests/end-to-end/README.md @@ -1,6 +1,6 @@ # End-to-end tests -## Preparation +## Setting up the environment variables Duplicate `local.properties.example` file from `test/resources` and rename the copy to `local.properties` @@ -15,3 +15,41 @@ Setup properties: | ANONCRED_DEFINITION_GUID | Existing Anoncred definition guid | | APIKEY | APIKEY header token authentication | +## Running the end-to-end tests + +### Building the SDK + +In the command line navigate to the SDK directory + +1. Set the `GITHUB_ACTOR` environment variable with your GitHub email +2. Set `GITHUB_TOKEN` environment variable with your GitHub token +3. Change the version in `gradle.properties` to something else +4. Run the following command + +```bash +./gradlew publishToMavenLocal +``` + +### Update the SDK dependency in e2e test + +Now, in the `build.gradle.kts` file inside the `e2e tests` directory you'll have to update +the version of the sdk with the new one you published to your maven local + +E.g. +```kotlin +testImplementation("org.hyperledger.identus:edge-agent-sdk:1.2.3-MY-CHANGE") +``` + +### Running the tests + +Full regression + +```bash +./gradlew test --tests "org.hyperledger.identus.walletsdk.TestSuite" +``` + +Tagged scenario + +```bash +./gradlew test --tests "org.hyperledger.identus.walletsdk.TestSuite" -Dcucumber.filter.tags="@mytag and @anothertag" +``` diff --git a/tests/end-to-end/build.gradle.kts b/tests/end-to-end/build.gradle.kts index fa47a2b1d..43721dffc 100644 --- a/tests/end-to-end/build.gradle.kts +++ b/tests/end-to-end/build.gradle.kts @@ -14,23 +14,25 @@ repositories { maven { url = uri("https://maven.pkg.github.com/input-output-hk/atala-automation/") credentials { - username = System.getenv("ATALA_GITHUB_ACTOR") - password = System.getenv("ATALA_GITHUB_TOKEN") + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") } } maven { url = uri("https://maven.pkg.github.com/hyperledger/identus-cloud-agent/") credentials { - username = System.getenv("ATALA_GITHUB_ACTOR") - password = System.getenv("ATALA_GITHUB_TOKEN") + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") } } } dependencies { testImplementation("org.hyperledger.identus:edge-agent-sdk:3.0.0") - testImplementation("io.iohk.atala.prism:prism-kotlin-client:1.31.0") + testImplementation("org.hyperledger.identus:cloud-agent-client-kotlin:1.38.0") testImplementation("io.iohk.atala:atala-automation:0.3.2") + testImplementation("app.cash.sqldelight:sqlite-driver:2.0.2") + testImplementation("io.ktor:ktor-client-core-jvm:2.3.12") } tasks.register("cleanTarget") { diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/TestSuite.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/TestSuite.kt index bfbe04d33..19dd1be46 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/TestSuite.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/TestSuite.kt @@ -8,6 +8,6 @@ import org.junit.runner.RunWith @CucumberOptions( features = ["src/test/resources/features"], plugin = ["pretty"], - tags = "not (@proof and @anoncred)" + tags = "not @disabled" ) class TestSuite diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/abilities/UseWalletSdk.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/abilities/UseWalletSdk.kt index 5d219a341..7c31b1e2b 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/abilities/UseWalletSdk.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/abilities/UseWalletSdk.kt @@ -2,37 +2,55 @@ package org.hyperledger.identus.walletsdk.abilities import com.jayway.jsonpath.JsonPath import io.iohk.atala.automation.utils.Logger -import org.hyperledger.identus.walletsdk.configuration.Environment -import org.hyperledger.identus.walletsdk.workflow.EdgeAgentWorkflow import io.restassured.RestAssured import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.serenitybdd.screenplay.Ability import net.serenitybdd.screenplay.Actor +import net.serenitybdd.screenplay.HasTeardown import net.serenitybdd.screenplay.Question import net.serenitybdd.screenplay.SilentInteraction import org.hyperledger.identus.walletsdk.apollo.ApolloImpl import org.hyperledger.identus.walletsdk.castor.CastorImpl +import org.hyperledger.identus.walletsdk.configuration.DbConnectionInMemory +import org.hyperledger.identus.walletsdk.configuration.Environment import org.hyperledger.identus.walletsdk.domain.models.ApiImpl import org.hyperledger.identus.walletsdk.domain.models.DID import org.hyperledger.identus.walletsdk.domain.models.Message +import org.hyperledger.identus.walletsdk.domain.models.Seed import org.hyperledger.identus.walletsdk.domain.models.httpClient import org.hyperledger.identus.walletsdk.edgeagent.EdgeAgent +import org.hyperledger.identus.walletsdk.edgeagent.helpers.AgentOptions +import org.hyperledger.identus.walletsdk.edgeagent.helpers.Experiments import org.hyperledger.identus.walletsdk.edgeagent.mediation.BasicMediatorHandler -import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType +import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType.DidcommIssueCredential +import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType.DidcommOfferCredential +import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType.DidcommPresentation +import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType.DidcommRequestPresentation +import org.hyperledger.identus.walletsdk.edgeagent.protocols.ProtocolType.PrismRevocation import org.hyperledger.identus.walletsdk.mercury.MercuryImpl import org.hyperledger.identus.walletsdk.mercury.resolvers.DIDCommWrapper import org.hyperledger.identus.walletsdk.pluto.PlutoImpl -import org.hyperledger.identus.walletsdk.pluto.data.DbConnection import org.hyperledger.identus.walletsdk.pollux.PolluxImpl -import java.util.* +import org.hyperledger.identus.walletsdk.workflow.EdgeAgentWorkflow +import org.lighthousegames.logging.KmLogging +import org.lighthousegames.logging.LogLevel +import java.util.Base64 +import java.util.Collections -class UseWalletSdk : Ability { +class UseWalletSdk : Ability, HasTeardown { companion object { private fun `as`(actor: Actor): UseWalletSdk { + if (actor.abilityTo(UseWalletSdk::class.java) != null) { + val ability = actor.abilityTo(UseWalletSdk::class.java) + if (!ability.isInitialized) { + ability.initialize() + } + } return actor.abilityTo(UseWalletSdk::class.java) ?: throw ActorCannotUseWalletSdk(actor) } @@ -60,6 +78,12 @@ class UseWalletSdk : Ability { } } + fun presentationStackSize(): Question { + return Question.about("presentation messages stack").answeredBy { + `as`(it).context.presentationStack.size + } + } + fun execute(callback: suspend (sdk: SdkContext) -> Unit): SilentInteraction { return object : SilentInteraction() { override fun performAs(actor: T) { @@ -70,71 +94,103 @@ class UseWalletSdk : Ability { } } } + } - fun stop(): SilentInteraction { - return object : SilentInteraction() { - override fun performAs(actor: T) { - val walletSdk = `as`(actor) - runBlocking { - walletSdk.context.sdk.stopFetchingMessages() - walletSdk.context.sdk.stop() - } - } + private val logger = Logger.get() + private lateinit var context: SdkContext + private val receivedMessages = mutableListOf() + private var isInitialized = false + private lateinit var fetchJob: Job + + fun initialize() { + createSdk() + startPluto() + startSdk() + listenToMessages() + isInitialized = true + } - } + override fun tearDown() { + if (isInitialized) { + context.sdk.stopFetchingMessages() + context.sdk.stop() + fetchJob.cancel() } } - private val logger = Logger.get() - private val context: SdkContext - private val receivedMessages = mutableListOf() + fun recoverWallet(seed: Seed, jwe: String) { + createSdk(seed) + startPluto() + runBlocking { + context.sdk.recoverWallet(jwe) + } + startSdk() + listenToMessages() + isInitialized = true + } - init { + private fun createSdk(initialSeed: Seed? = null) { + val api = ApiImpl(httpClient()) val apollo = ApolloImpl() val castor = CastorImpl(apollo) - val pluto = PlutoImpl(DbConnection()) - val pollux = PolluxImpl(castor) + val pluto = PlutoImpl(DbConnectionInMemory()) + val pollux = PolluxImpl(apollo, castor, api) val didcommWrapper = DIDCommWrapper(castor, pluto, apollo) - val api = ApiImpl(httpClient()) val mercury = MercuryImpl(castor, didcommWrapper, api) val mediatorDid = DID(getMediatorDidThroughOob()) val store = BasicMediatorHandler.PlutoMediatorRepositoryImpl(pluto) val handler = BasicMediatorHandler(mediatorDid, mercury, store) - val seed = apollo.createRandomSeed().seed + val seed = initialSeed ?: apollo.createRandomSeed().seed val sdk = EdgeAgent( - apollo, - castor, - pluto, - mercury, - pollux, - seed, - api, - handler + apollo = apollo, + castor = castor, + pluto = pluto, + mercury = mercury, + pollux = pollux, + seed = seed, + api = api, + mediatorHandler = handler, + agentOptions = AgentOptions( + experiments = Experiments( + liveMode = false + ) + ) ) + KmLogging.setLogLevel(LogLevel.Warn) this.context = SdkContext(sdk) + } + + private fun startPluto() { + runBlocking { + context.sdk.pluto.start(this) + } + } + private fun startSdk() { runBlocking { - pluto.start(this) - sdk.start() - sdk.startFetchingMessages(1) + context.sdk.start() } + context.sdk.startFetchingMessages(1) + } - CoroutineScope(Dispatchers.Default).launch { - sdk.handleReceivedMessagesEvents().collect { messageList: List -> + private fun listenToMessages() { + fetchJob = CoroutineScope(Dispatchers.Default).launch { + context.sdk.handleReceivedMessagesEvents().collect { messageList: List -> messageList.forEach { message -> if (receivedMessages.contains(message.id)) { return@forEach } receivedMessages.add(message.id) when (message.piuri) { - ProtocolType.DidcommOfferCredential.value -> context.credentialOfferStack.add(message) - ProtocolType.DidcommIssueCredential.value -> context.issuedCredentialStack.add(message) - ProtocolType.DidcommRequestPresentation.value -> context.proofRequestStack.add(message) - ProtocolType.PrismRevocation.value -> context.revocationNotificationStack.add(message) + DidcommOfferCredential.value -> context.credentialOfferStack.add(message) + DidcommIssueCredential.value -> context.issuedCredentialStack.add(message) + DidcommRequestPresentation.value -> context.proofRequestStack.add(message) + PrismRevocation.value -> context.revocationNotificationStack.add(message) + DidcommPresentation.value -> context.presentationStack.add(message) else -> logger.debug("other message: ${message.piuri}") } } @@ -157,7 +213,8 @@ data class SdkContext( val credentialOfferStack: MutableList = Collections.synchronizedList(mutableListOf()), val proofRequestStack: MutableList = Collections.synchronizedList(mutableListOf()), val issuedCredentialStack: MutableList = Collections.synchronizedList(mutableListOf()), - val revocationNotificationStack: MutableList = Collections.synchronizedList(mutableListOf()) + val revocationNotificationStack: MutableList = Collections.synchronizedList(mutableListOf()), + val presentationStack: MutableList = Collections.synchronizedList(mutableListOf()) ) class ActorCannotUseWalletSdk(actor: Actor) : diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/DbConnectionInMemory.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/DbConnectionInMemory.kt new file mode 100644 index 000000000..24ff9beb4 --- /dev/null +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/DbConnectionInMemory.kt @@ -0,0 +1,17 @@ +package org.hyperledger.identus.walletsdk.configuration + +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import org.hyperledger.identus.walletsdk.SdkPlutoDb +import org.hyperledger.identus.walletsdk.pluto.data.DbConnection + +class DbConnectionInMemory : DbConnection { + override var driver: SqlDriver? = null + + override suspend fun connectDb(context: Any?): SqlDriver { + val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + SdkPlutoDb.Schema.create(driver) + this.driver = driver + return driver + } +} diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/Environment.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/Environment.kt index 7596b76b4..ceb417646 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/Environment.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/Environment.kt @@ -1,7 +1,6 @@ package org.hyperledger.identus.walletsdk.configuration import io.iohk.atala.automation.utils.Wait -import io.iohk.atala.prism.models.* import org.hyperledger.identus.walletsdk.utils.Notes import io.restassured.RestAssured import io.restassured.builder.RequestSpecBuilder @@ -9,6 +8,13 @@ import io.restassured.response.Response import net.serenitybdd.rest.SerenityRest import org.apache.http.HttpStatus import org.assertj.core.api.Assertions.assertThat +import org.hyperledger.identus.client.models.CreateManagedDidRequest +import org.hyperledger.identus.client.models.CreateManagedDidRequestDocumentTemplate +import org.hyperledger.identus.client.models.CredentialDefinitionInput +import org.hyperledger.identus.client.models.CredentialDefinitionResponse +import org.hyperledger.identus.client.models.CredentialSchemaInput +import org.hyperledger.identus.client.models.ManagedDIDKeyTemplate +import org.hyperledger.identus.client.models.Purpose import org.hyperledger.identus.walletsdk.models.AnoncredSchema import org.hyperledger.identus.walletsdk.models.JwtSchema import org.hyperledger.identus.walletsdk.models.JwtSchemaProperty diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/HooksSetup.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/HooksSetup.kt index 419916b5f..bf6aa01ef 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/HooksSetup.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/configuration/HooksSetup.kt @@ -1,5 +1,6 @@ package org.hyperledger.identus.walletsdk.configuration +import io.cucumber.java.After import io.cucumber.java.Before import io.cucumber.java.BeforeAll import io.cucumber.java.ParameterType @@ -15,6 +16,11 @@ fun setupEnvironment() { Environment.setup() } +@After +fun drawTheCurtain() { + OnStage.drawTheCurtain() +} + class HooksSetup { @Before fun setStage() { @@ -25,6 +31,11 @@ class HooksSetup { UseWalletSdk() ) + cast.actorNamed("Verifier Edge Agent", + CallAnApi.at(Environment.mediatorOobUrl), + UseWalletSdk() + ) + cast.actorNamed("Cloud Agent", CallAnApi.at(Environment.agentUrl) ) diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/CloudAgentSteps.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/CloudAgentSteps.kt index 5eb0fe899..71d406433 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/CloudAgentSteps.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/CloudAgentSteps.kt @@ -63,6 +63,11 @@ class CloudAgentSteps { cloudAgentWorkflow.askForPresentProofForAnoncred(cloudAgent) } + @When("{actor} asks for present-proof for anoncred with unexpected attributes") + fun `Cloud Agent asks for present-proof for anoncred with unexpected attributes`(cloudAgent: Actor) { + cloudAgentWorkflow.askForPresentProofForAnoncredWithUnexpectedAttributes(cloudAgent) + } + @When("{actor} revokes '{int}' credentials") fun `Cloud Agent revokes {} credentials`(cloudAgent: Actor, numberOfRevokedCredentials: Int) { cloudAgentWorkflow.revokeCredential(cloudAgent, numberOfRevokedCredentials) @@ -78,6 +83,11 @@ class CloudAgentSteps { cloudAgentWorkflow.verifyPresentProof(cloudAgent, "PresentationVerified") } + @Then("{actor} should see the present-proof is not verified") + fun `Cloud Agent should see the present-proof is not verified`(cloudAgent: Actor) { + cloudAgentWorkflow.verifyPresentProof(cloudAgent, "PresentationFailed") + } + @Then("{actor} should see all credentials were accepted") fun `Cloud Agent should see all credentials were accepted`(cloudAgent: Actor) { val recordIdList = cloudAgent.recall>("recordIdList") diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/EdgeAgentSteps.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/EdgeAgentSteps.kt index 14bac61c0..b096dc3a2 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/EdgeAgentSteps.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/steps/EdgeAgentSteps.kt @@ -1,13 +1,24 @@ package org.hyperledger.identus.walletsdk.steps -import io.cucumber.java.After +import io.cucumber.java.en.Given import io.cucumber.java.en.Then import io.cucumber.java.en.When +import kotlinx.coroutines.flow.first +import net.serenitybdd.screenplay.Actor +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat import org.hyperledger.identus.walletsdk.abilities.UseWalletSdk +import org.hyperledger.identus.walletsdk.domain.models.AnoncredsInputFieldFilter +import org.hyperledger.identus.walletsdk.domain.models.AnoncredsPresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.CredentialType +import org.hyperledger.identus.walletsdk.domain.models.DID +import org.hyperledger.identus.walletsdk.domain.models.InputFieldFilter +import org.hyperledger.identus.walletsdk.domain.models.JWTPresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.NonRevoked +import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.RequestedAttributes import org.hyperledger.identus.walletsdk.workflow.CloudAgentWorkflow import org.hyperledger.identus.walletsdk.workflow.EdgeAgentWorkflow -import net.serenitybdd.screenplay.Actor -import net.serenitybdd.screenplay.actors.OnStage import javax.inject.Inject class EdgeAgentSteps { @@ -18,11 +29,26 @@ class EdgeAgentSteps { @Inject private lateinit var cloudAgentWorkflow: CloudAgentWorkflow + @Given("{actor} has created a backup") + fun `Edge Agent has created a backup`(edgeAgent: Actor) { + edgeAgentWorkflow.createBackup(edgeAgent) + } + @When("{actor} connects through the invite") fun `Edge Agent connects through the invite`(edgeAgent: Actor) { edgeAgentWorkflow.connect(edgeAgent) } + @When("{actor} creates '{}' peer DIDs") + fun `Edge Agent creates Peer DIDs`(edgeAgent: Actor, numberOfDids: Int) { + edgeAgentWorkflow.createPeerDids(edgeAgent, numberOfDids) + } + + @When("{actor} creates '{}' prism DIDs") + fun `Edge Agent creates Prism DIDs`(edgeAgent: Actor, numberOfDids: Int) { + edgeAgentWorkflow.createPrismDids(edgeAgent, numberOfDids) + } + @When("{actor} has '{}' jwt credentials issued by {actor}") fun `Edge Agent has {} jwt issued credential`(edgeAgent: Actor, numberOfCredentialsIssued: Int, cloudAgent: Actor) { val recordIdList = mutableListOf() @@ -40,7 +66,11 @@ class EdgeAgentSteps { } @When("{actor} has '{}' anonymous credentials issued by {actor}") - fun `Edge Agent has {} anonymous issued credential`(edgeAgent: Actor, numberOfCredentialsIssued: Int, cloudAgent: Actor) { + fun `Edge Agent has {} anonymous issued credential`( + edgeAgent: Actor, + numberOfCredentialsIssued: Int, + cloudAgent: Actor + ) { repeat(numberOfCredentialsIssued) { cloudAgentWorkflow.offerAnonymousCredential(cloudAgent) edgeAgentWorkflow.waitForCredentialOffer(edgeAgent, 1) @@ -103,6 +133,42 @@ class EdgeAgentSteps { edgeAgentWorkflow.presentProof(edgeAgent) } + @When("{actor} request {actor} to verify the JWT credential") + fun `Verifier requests Holder to verify the JWT Credential`(verifierEdgeAgent: Actor, holderEdgeAgent: Actor) { + edgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1) + val did = holderEdgeAgent.recall("did") + val claims = JWTPresentationClaims( + claims = mapOf( + "automation-required" to InputFieldFilter(type = "string", pattern = "required value") + ) + ) + edgeAgentWorkflow.initiatePresentationRequest(CredentialType.JWT, verifierEdgeAgent, did, claims) + } + + @When("{actor} request {actor} to verify the anonymous credential") + fun `Verifier requests Holder to verify the anoncred credential`(verifierEdgeAgent: Actor, holderEdgeAgent: Actor) { + edgeAgentWorkflow.createPeerDids(holderEdgeAgent, 1) + val did = holderEdgeAgent.recall("did") + val claims = AnoncredsPresentationClaims( + attributes = mapOf( + "name" to RequestedAttributes( + name = "name", + names = setOf("name"), + restrictions = emptyMap(), + null + ) + ), + predicates = emptyMap() + ) + edgeAgentWorkflow.initiatePresentationRequest(CredentialType.ANONCREDS_PROOF_REQUEST, verifierEdgeAgent, did, claims) + } + + @When("{actor} sends the verification proof") + fun `Edge Agent sends the verification proof`(edgeAgent: Actor) { + edgeAgentWorkflow.waitForProofRequest(edgeAgent) + edgeAgentWorkflow.presentProof(edgeAgent) + } + @Then("{actor} should receive the credential") fun `Edge Agent should receive the credential`(edgeAgent: Actor) { edgeAgentWorkflow.waitForCredentialOffer(edgeAgent, 1) @@ -124,12 +190,20 @@ class EdgeAgentSteps { } @Then("{actor} should have {} credentials") - fun `Edge Agent should have N credential`(actor: Actor, numberOfCredentials: Int) { - //edgeAgentWorkflow.creden + fun `Edge Agent should have expected credentials`(actor: Actor, numberOfCredentials: Int) { + actor.attemptsTo( + UseWalletSdk.execute { + val credentials = it.sdk.getAllCredentials().first() + assertThat(credentials.size).isEqualTo(numberOfCredentials) + } + ) } @Then("{actor} waits to receive the revocation notifications from {actor}") - fun `Edge Agent waits to receive the revocation notifications from Cloud Agent`(edgeAgent: Actor, cloudAgent: Actor) { + fun `Edge Agent waits to receive the revocation notifications from Cloud Agent`( + edgeAgent: Actor, + cloudAgent: Actor + ) { val revokedRecordIdList = cloudAgent.recall>("revokedRecordIdList") edgeAgentWorkflow.waitForCredentialRevocationMessage(edgeAgent, revokedRecordIdList.size) } @@ -140,10 +214,43 @@ class EdgeAgentSteps { edgeAgentWorkflow.waitUntilCredentialIsRevoked(edgeAgent, revokedRecordIdList) } - @After - fun stopAgent() { - OnStage.theActor("Edge Agent").attemptsTo( - UseWalletSdk.stop() - ) + @Then("a new SDK can be restored from {actor}") + fun `A new SDK can be restored from Edge Agent`(edgeAgent: Actor) { + edgeAgentWorkflow.createANewWalletFromBackup(edgeAgent) + } + + @Then("a new SDK cannot be restored from {actor} with wrong seed") + fun `A new SDK cannot be restored from Edge Agent with wrong seed`(edgeAgent: Actor) { + edgeAgentWorkflow.createNewWalletFromBackupWithWrongSeed(edgeAgent) + } + + @Then("a new {actor} is restored from {actor}") + fun `A new Agent is restored from Edge Agent`(newAgent: Actor, originalAgent: Actor) { + edgeAgentWorkflow.backupAndRestoreToNewAgent(newAgent, originalAgent) + } + + @Then("{actor} should have the expected values from {actor}") + fun `Restored Agent should have the expected values from Original Edge Agent`( + restoredEdgeAgent: Actor, + originalEdgeAgent: Actor + ) { + edgeAgentWorkflow.copyAgentShouldMatchOriginalAgent(restoredEdgeAgent, originalEdgeAgent) + } + + @Then("{actor} is dismissed") + fun `Edge Agent is dismissed`(edgeAgent: Actor) { + edgeAgent.wrapUp() + } + + @Then("{actor} should see the verification proof is verified") + fun `Verifier Edge Agent should see the verification proof is verified`(verifierEdgeAgent: Actor) { + edgeAgentWorkflow.waitForPresentationMessage(verifierEdgeAgent) + edgeAgentWorkflow.verifyPresentation(verifierEdgeAgent) + } + + @Then("{actor} should see the verification proof was not verified due revocation") + fun `Verifier Edge Agent should see the verification proof was not verified`(verifierEdgeAgent: Actor) { + edgeAgentWorkflow.waitForPresentationMessage(verifierEdgeAgent) + edgeAgentWorkflow.verifyPresentation(verifierEdgeAgent, expected = false, shouldBeRevoked = true) } } diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/CloudAgentWorkflow.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/CloudAgentWorkflow.kt index 48c13f221..0aa379ce9 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/CloudAgentWorkflow.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/CloudAgentWorkflow.kt @@ -7,20 +7,20 @@ import io.iohk.atala.automation.serenity.ensure.Ensure import io.iohk.atala.automation.serenity.interactions.PollingWait import io.iohk.atala.automation.serenity.questions.HttpRequest import org.hyperledger.identus.walletsdk.configuration.Environment -import io.iohk.atala.prism.models.AnoncredPresentationRequestV1 -import io.iohk.atala.prism.models.AnoncredRequestedAttributeV1 -import io.iohk.atala.prism.models.AnoncredRequestedPredicateV1 -import io.iohk.atala.prism.models.CreateConnectionRequest -import io.iohk.atala.prism.models.CreateIssueCredentialRecordRequest -import io.iohk.atala.prism.models.Options -import io.iohk.atala.prism.models.ProofRequestAux -import io.iohk.atala.prism.models.RequestPresentationInput import org.hyperledger.identus.walletsdk.utils.Utils import net.serenitybdd.rest.SerenityRest.lastResponse import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.rest.interactions.Patch import net.serenitybdd.screenplay.rest.interactions.Post import org.apache.http.HttpStatus +import org.hyperledger.identus.client.models.AnoncredPresentationRequestV1 +import org.hyperledger.identus.client.models.AnoncredRequestedAttributeV1 +import org.hyperledger.identus.client.models.AnoncredRequestedPredicateV1 +import org.hyperledger.identus.client.models.CreateConnectionRequest +import org.hyperledger.identus.client.models.CreateIssueCredentialRecordRequest +import org.hyperledger.identus.client.models.Options +import org.hyperledger.identus.client.models.ProofRequestAux +import org.hyperledger.identus.client.models.RequestPresentationInput import java.util.UUID class CloudAgentWorkflow { @@ -58,7 +58,7 @@ class CloudAgentWorkflow { fun offerJwtCredential(cloudAgent: Actor) { val connectionId = cloudAgent.recall("connectionId") val credential = CreateIssueCredentialRecordRequest( - claims = mapOf(Pair("automation-required", UUID.randomUUID())), + claims = mapOf(Pair("automation-required", "required value")), issuingDID = Environment.publishedDid, connectionId = UUID.fromString(connectionId), schemaId = "${Environment.agentUrl}/schema-registry/schemas/${Environment.jwtSchemaGuid}" @@ -162,6 +162,43 @@ class CloudAgentWorkflow { cloudAgent.remember("presentationId", lastResponse().get("presentationId")) } + fun askForPresentProofForAnoncredWithUnexpectedAttributes(cloudAgent: Actor) { + val credentialDefinitionId = Environment.agentUrl + + "/credential-definition-registry/definitions/" + + Environment.anoncredDefinitionId + + "/definition" + val anoncredsPresentationRequestV1 = AnoncredPresentationRequestV1( + requestedAttributes = mapOf( + "name" to AnoncredRequestedAttributeV1( + name = "name", + restrictions = listOf( + mapOf( + "attr::name::value" to "Automation", + "cred_def_id" to credentialDefinitionId + ) + ) + ) + ), + requestedPredicates = mapOf(), + name = "proof_req_1", + nonce = Utils.generateNonce(25), + version = "0.1" + ) + + val presentProofRequest = RequestPresentationInput( + connectionId = UUID.fromString(cloudAgent.recall("connectionId")), + credentialFormat = "AnonCreds", + anoncredPresentationRequest = anoncredsPresentationRequestV1, + proofs = emptyList() + ) + + cloudAgent.attemptsTo( + Post.to("/present-proof/presentations").body(presentProofRequest), + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED) + ) + cloudAgent.remember("presentationId", lastResponse().get("presentationId")) + } + fun verifyCredentialState(cloudAgent: Actor, recordId: String, state: String) { cloudAgent.attemptsTo( PollingWait.until( diff --git a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/EdgeAgentWorkflow.kt b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/EdgeAgentWorkflow.kt index af911f195..67c80e206 100644 --- a/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/EdgeAgentWorkflow.kt +++ b/tests/end-to-end/src/test/kotlin/org/hyperledger/identus/walletsdk/workflow/EdgeAgentWorkflow.kt @@ -3,18 +3,33 @@ package org.hyperledger.identus.walletsdk.workflow import com.google.gson.GsonBuilder import io.iohk.atala.automation.serenity.interactions.PollingWait import io.iohk.atala.automation.utils.Logger -import org.hyperledger.identus.walletsdk.abilities.UseWalletSdk +import io.ktor.util.reflect.* import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking import net.serenitybdd.screenplay.Actor -import net.serenitybdd.screenplay.rest.interactions.Ensure +import net.serenitybdd.screenplay.rest.abilities.CallAnApi import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.fail import org.hamcrest.CoreMatchers.equalTo +import org.hyperledger.identus.walletsdk.abilities.SdkContext +import org.hyperledger.identus.walletsdk.abilities.UseWalletSdk +import org.hyperledger.identus.walletsdk.apollo.ApolloImpl +import org.hyperledger.identus.walletsdk.configuration.Environment import org.hyperledger.identus.walletsdk.domain.models.CastorError +import org.hyperledger.identus.walletsdk.domain.models.CredentialType +import org.hyperledger.identus.walletsdk.domain.models.DID +import org.hyperledger.identus.walletsdk.domain.models.PolluxError +import org.hyperledger.identus.walletsdk.domain.models.PresentationClaims +import org.hyperledger.identus.walletsdk.domain.models.ProvableCredential +import org.hyperledger.identus.walletsdk.domain.models.Seed import org.hyperledger.identus.walletsdk.edgeagent.protocols.issueCredential.IssueCredential import org.hyperledger.identus.walletsdk.edgeagent.protocols.issueCredential.OfferCredential import org.hyperledger.identus.walletsdk.edgeagent.protocols.outOfBand.OutOfBandInvitation import org.hyperledger.identus.walletsdk.edgeagent.protocols.proofOfPresentation.RequestPresentation -import java.util.function.Consumer +import org.hyperledger.identus.walletsdk.pluto.PlutoBackupTask +import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes class EdgeAgentWorkflow { private val logger = Logger.get() @@ -53,7 +68,6 @@ class EdgeAgentWorkflow { UseWalletSdk.proofOfRequestStackSize(), equalTo(1) ) - ) } @@ -62,9 +76,12 @@ class EdgeAgentWorkflow { UseWalletSdk.execute { val credentials = it.sdk.getAllCredentials().first() val credential = credentials.first() - val requestPresentationMessage = RequestPresentation.fromMessage(it.proofRequestStack.removeFirst()) - val presentation = it.sdk.preparePresentationForRequestProof(requestPresentationMessage, credential) - it.sdk.sendMessage(presentation.makeMessage()) + assertThat(credential).instanceOf(ProvableCredential::class) + if (credential is ProvableCredential) { + val requestPresentationMessage = RequestPresentation.fromMessage(it.proofRequestStack.removeFirst()) + val presentation = it.sdk.preparePresentationForRequestProof(requestPresentationMessage, credential) + it.sdk.sendMessage(presentation.makeMessage()) + } } ) } @@ -121,7 +138,7 @@ class EdgeAgentWorkflow { fun waitForCredentialRevocationMessage(edgeAgent: Actor, numberOfRevocation: Int) { edgeAgent.attemptsTo( - PollingWait.until( + PollingWait.with(2.minutes, 500.milliseconds).until( UseWalletSdk.revocationStackSize(), equalTo(numberOfRevocation) ) @@ -139,14 +156,133 @@ class EdgeAgentWorkflow { val revokedCredentials = credentials.filter { credential -> credential.revoked == true && revokedIdList.contains(credential.id) } - edgeAgent.attemptsTo( - Ensure.that( - "The number of revoked credentials matches the expected number", - Consumer { context -> - assertThat(revokedCredentials.size).isEqualTo(revokedRecordIdList.size) - } - ) - ) + assertThat(revokedCredentials.size).isEqualTo(revokedRecordIdList.size) + } + ) + } + + fun createBackup(edgeAgent: Actor) { + edgeAgent.attemptsTo( + UseWalletSdk.execute { sdkContext: SdkContext -> + val backup = sdkContext.sdk.backupWallet(PlutoBackupTask(sdkContext.sdk.pluto)) + val seed = sdkContext.sdk.seed + edgeAgent.remember("backup", backup) + edgeAgent.remember("seed", seed) + } + ) + } + + fun createANewWalletFromBackup(edgeAgent: Actor) { + val backup = edgeAgent.recall("backup") + val seed = edgeAgent.recall("seed") + val walletSdk = UseWalletSdk() + walletSdk.recoverWallet(seed, backup) + runBlocking { + walletSdk.tearDown() + } + } + + fun createNewWalletFromBackupWithWrongSeed(edgeAgent: Actor) { + val backup = edgeAgent.recall("backup") + val seed = ApolloImpl().createRandomSeed().seed + val walletSdk = UseWalletSdk() + runBlocking { + try { + walletSdk.recoverWallet(seed, backup) + fail("SDK should not be able to restore with wrong seed phrase.") + } catch (e: Exception) { + assertThat(e).isNotNull() + } + } + } + + fun createPeerDids(edgeAgent: Actor, numberOfDids: Int) { + edgeAgent.attemptsTo( + UseWalletSdk.execute { sdkContext -> + repeat(numberOfDids) { + val did = sdkContext.sdk.createNewPeerDID(updateMediator = true) + edgeAgent.remember("did", did) + } + } + ) + } + + fun createPrismDids(edgeAgent: Actor, numberOfDids: Int) { + edgeAgent.attemptsTo( + UseWalletSdk.execute { sdkContext -> + repeat(numberOfDids) { + sdkContext.sdk.createNewPrismDID() + } + } + ) + } + + fun backupAndRestoreToNewAgent(newAgent: Actor, originalAgent: Actor) { + val backup = originalAgent.recall("backup") + val seed = originalAgent.recall("seed") + val walletSdk = UseWalletSdk() + walletSdk.recoverWallet(seed, backup) + newAgent.whoCan(walletSdk).whoCan(CallAnApi.at(Environment.mediatorOobUrl)) + } + + fun copyAgentShouldMatchOriginalAgent(restoredEdgeAgent: Actor, originalEdgeAgent: Actor) { + val expectedCredentials = mutableListOf() + val expectedPeerDids = mutableListOf() + val expectedPrismDids = mutableListOf() + + originalEdgeAgent.attemptsTo( + UseWalletSdk.execute { sdkContext -> + expectedCredentials.addAll(sdkContext.sdk.getAllCredentials().first().map { it.id }) + expectedPeerDids.addAll(sdkContext.sdk.pluto.getAllPeerDIDs().first().map { it.did.toString() }) + expectedPrismDids.addAll(sdkContext.sdk.pluto.getAllPrismDIDs().first().map { it.did.toString() }) + } + ) + + restoredEdgeAgent.attemptsTo( + UseWalletSdk.execute { sdkContext -> + val actualCredentials = sdkContext.sdk.getAllCredentials().first().map { it.id } + val actualPeerDids = sdkContext.sdk.pluto.getAllPeerDIDs().first().map { it.did.toString() } + val actualPrismDids = sdkContext.sdk.pluto.getAllPrismDIDs().first().map { it.did.toString() } + + assertThat(actualCredentials.size).isEqualTo(expectedCredentials.size) + assertThat(actualCredentials.containsAll(expectedCredentials)).isTrue() + assertThat(actualPeerDids.size).isEqualTo(expectedPeerDids.size) + assertThat(actualPeerDids.containsAll(expectedPeerDids)).isTrue() + assertThat(actualPrismDids.size).isEqualTo(expectedPrismDids.size) + assertThat(actualPrismDids.containsAll(expectedPrismDids)).isTrue() + } + ) + } + + fun initiatePresentationRequest(type: CredentialType, edgeAgent: Actor, did: DID, claims: PresentationClaims) { + edgeAgent.attemptsTo( + UseWalletSdk.execute { + it.sdk.initiatePresentationRequest(type, did, claims, "", UUID.randomUUID().toString()) + } + ) + } + + fun waitForPresentationMessage(edgeAgent: Actor, numberOfMessages: Int = 1) { + edgeAgent.attemptsTo( + PollingWait.until( + UseWalletSdk.presentationStackSize(), + equalTo(numberOfMessages) + ) + ) + } + + fun verifyPresentation(edgeAgent: Actor, expected: Boolean = true, shouldBeRevoked: Boolean = false) { + edgeAgent.attemptsTo( + UseWalletSdk.execute { + val message = it.presentationStack.removeFirst() + try { + val isVerified = it.sdk.handlePresentation(message) + assertThat(isVerified).isEqualTo(expected) + } catch (e: PolluxError.VerificationUnsuccessful) { + assertThat(expected).isFalse() + assertThat(shouldBeRevoked).isTrue() + assertThat(e.message).contains("Provided credential is revoked") + } } ) } diff --git a/tests/end-to-end/src/test/resources/features/backup/Backup.feature b/tests/end-to-end/src/test/resources/features/backup/Backup.feature new file mode 100644 index 000000000..137873739 --- /dev/null +++ b/tests/end-to-end/src/test/resources/features/backup/Backup.feature @@ -0,0 +1,25 @@ +@backup +Feature: Backup + The Edge Agent should be able to create and restore a backup + + Scenario: Create and restore a backup + Given Edge Agent has created a backup + Then a new SDK can be restored from Edge Agent + + Scenario: Agent without a seed should not be able to restore the backup + Given Edge Agent has created a backup + Then a new SDK cannot be restored from Edge Agent with wrong seed + + Scenario: Restored backup should be functional + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' jwt credentials issued by Cloud Agent + And Edge Agent creates '5' peer DIDs + And Edge Agent creates '3' prism DIDs + And Edge Agent has created a backup + Then a new Restored Agent is restored from Edge Agent + And Restored Agent should have the expected values from Edge Agent + And Edge Agent is dismissed + Given Cloud Agent is connected to Restored Agent + And Cloud Agent asks for present-proof + And Restored Agent sends the present-proof + Then Cloud Agent should see the present-proof is verified diff --git a/tests/end-to-end/src/test/resources/features/present-proof/AnoncredPresentProof.feature b/tests/end-to-end/src/test/resources/features/credential/anoncred/PresentProof.feature similarity index 54% rename from tests/end-to-end/src/test/resources/features/present-proof/AnoncredPresentProof.feature rename to tests/end-to-end/src/test/resources/features/credential/anoncred/PresentProof.feature index abd1149f6..a6735873f 100644 --- a/tests/end-to-end/src/test/resources/features/present-proof/AnoncredPresentProof.feature +++ b/tests/end-to-end/src/test/resources/features/credential/anoncred/PresentProof.feature @@ -8,3 +8,11 @@ Feature: Respond to anoncred request proof When Cloud Agent asks for present-proof for anoncred And Edge Agent sends the present-proof Then Cloud Agent should see the present-proof is verified + + @disabled + Scenario: Respond to a present request with a wrong credential + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' anonymous credentials issued by Cloud Agent + When Cloud Agent asks for present-proof for anoncred with unexpected attributes + And Edge Agent sends the present-proof + Then Cloud Agent should see the present-proof is not verified diff --git a/tests/end-to-end/src/test/resources/features/credential/Anoncred.feature b/tests/end-to-end/src/test/resources/features/credential/anoncred/Receive.feature similarity index 100% rename from tests/end-to-end/src/test/resources/features/credential/Anoncred.feature rename to tests/end-to-end/src/test/resources/features/credential/anoncred/Receive.feature diff --git a/tests/end-to-end/src/test/resources/features/credential/anoncred/Verify.feature b/tests/end-to-end/src/test/resources/features/credential/anoncred/Verify.feature new file mode 100644 index 000000000..19a37ab63 --- /dev/null +++ b/tests/end-to-end/src/test/resources/features/credential/anoncred/Verify.feature @@ -0,0 +1,10 @@ +@anoncred @verification +Feature: Verify Anoncreds presentation + The Edge Agent should be able to receive a verifiable credential from Cloud Agent and then send a presentation to another edge agent who will verify it + + Scenario: Verify Anoncreds + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' anonymous credentials issued by Cloud Agent + When Verifier Edge Agent request Edge Agent to verify the anonymous credential + When Edge Agent sends the verification proof + Then Verifier Edge Agent should see the verification proof is verified diff --git a/tests/end-to-end/src/test/resources/features/present-proof/JWTPresentProof.feature b/tests/end-to-end/src/test/resources/features/credential/jwt/PresentProof.feature similarity index 100% rename from tests/end-to-end/src/test/resources/features/present-proof/JWTPresentProof.feature rename to tests/end-to-end/src/test/resources/features/credential/jwt/PresentProof.feature diff --git a/tests/end-to-end/src/test/resources/features/credential/Credential.feature b/tests/end-to-end/src/test/resources/features/credential/jwt/Receive.feature similarity index 88% rename from tests/end-to-end/src/test/resources/features/credential/Credential.feature rename to tests/end-to-end/src/test/resources/features/credential/jwt/Receive.feature index 9754ac10e..58aee9e74 100644 --- a/tests/end-to-end/src/test/resources/features/credential/Credential.feature +++ b/tests/end-to-end/src/test/resources/features/credential/jwt/Receive.feature @@ -14,14 +14,14 @@ Feature: Receive verifiable credential Scenario: Receive multiple verifiable credentials sequentially Given Cloud Agent is connected to Edge Agent - When Edge Agent accepts 3 credential offer sequentially from Cloud Agent + When Edge Agent accepts 3 jwt credential offers sequentially from Cloud Agent Then Cloud Agent should see all credentials were accepted And Edge Agent wait to receive 3 issued credentials And Edge Agent process 3 issued credentials Scenario: Receive multiple verifiable credentials at once Given Cloud Agent is connected to Edge Agent - When Edge Agent accepts 3 credentials offer at once from Cloud Agent + When Edge Agent accepts 3 jwt credential offers at once from Cloud Agent Then Cloud Agent should see all credentials were accepted And Edge Agent wait to receive 3 issued credentials And Edge Agent process 3 issued credentials diff --git a/tests/end-to-end/src/test/resources/features/credential/RevokeJWT.feature b/tests/end-to-end/src/test/resources/features/credential/jwt/Revoke.feature similarity index 100% rename from tests/end-to-end/src/test/resources/features/credential/RevokeJWT.feature rename to tests/end-to-end/src/test/resources/features/credential/jwt/Revoke.feature diff --git a/tests/end-to-end/src/test/resources/features/credential/jwt/Verify.feature b/tests/end-to-end/src/test/resources/features/credential/jwt/Verify.feature new file mode 100644 index 000000000..685a44927 --- /dev/null +++ b/tests/end-to-end/src/test/resources/features/credential/jwt/Verify.feature @@ -0,0 +1,18 @@ +@jwt @verification +Feature: Verify JWT presentation + The Edge Agent should be able to receive a verifiable credential from Cloud Agent and then send a presentation to another edge agent who will verify it + + Scenario: Verify valid jwt credential + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' jwt credentials issued by Cloud Agent + When Verifier Edge Agent request Edge Agent to verify the JWT credential + And Edge Agent sends the verification proof + Then Verifier Edge Agent should see the verification proof is verified + + Scenario: Verify revoked jwt credential + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' jwt credentials issued by Cloud Agent + When Cloud Agent revokes '1' credentials + Then Verifier Edge Agent request Edge Agent to verify the JWT credential + When Edge Agent sends the verification proof + Then Verifier Edge Agent should see the verification proof was not verified due revocation