From f115da4193894e551409d348a03215015ce15322 Mon Sep 17 00:00:00 2001 From: Bassam Date: Wed, 3 Jan 2024 14:09:14 -0500 Subject: [PATCH] feat: validate presentation (#815) Signed-off-by: Bassam Riman --- .../atala/pollux/anoncreds/AnoncredLib.scala | 58 ++--- .../iohk/atala/pollux/anoncreds/Models.scala | 208 +++++++++--------- .../atala/pollux/anoncreds/PoCNewLib.scala | 14 +- .../core/model/IssueCredentialRecord.scala | 4 +- .../core/model/error/PresentationError.scala | 3 +- .../model/schema/CredentialDefinition.scala | 14 +- .../model/schema/validator/SchemaSerDes.scala | 6 +- .../repository/CredentialRepository.scala | 4 +- .../CredentialRepositoryInMemory.scala | 4 +- .../CredentialDefinitionServiceImpl.scala | 4 +- .../core/service/CredentialServiceImpl.scala | 107 +++++---- .../core/service/LinkSecretService.scala | 4 +- .../core/service/LinkSecretServiceImpl.scala | 20 +- .../service/MockPresentationService.scala | 17 ++ .../core/service/PresentationService.scala | 14 ++ .../service/PresentationServiceImpl.scala | 140 +++++++++++- .../service/PresentationServiceNotifier.scala | 18 ++ .../AnoncredPresentationRequestV1.scala | 69 ++---- .../anoncred-presentation-schema-example.json | 2 +- .../service/CredentialServiceImplSpec.scala | 4 +- .../service/LinkSecretServiceImplSpec.scala | 4 +- .../service/PresentationServiceSpec.scala | 148 ++++++++++++- .../PresentationServiceSpecHelper.scala | 16 +- .../AnoncredPresentationRequestSpec.scala | 58 +++-- .../serdes/AnoncredPresentationSpec.scala | 2 +- ...CredentialDefinitionSchemaSerDesSpec.scala | 4 +- .../repository/JdbcCredentialRepository.scala | 10 +- .../vc/jwt/VerifiableCredentialPayload.scala | 2 - .../server/jobs/PresentBackgroundJobs.scala | 152 ++++++++++--- .../controller/PresentProofController.scala | 4 + .../http/RequestPresentationInput.scala | 4 - .../controller/IssueControllerTestTools.scala | 4 +- .../CredentialDefinitionBasicSpec.scala | 16 +- 33 files changed, 769 insertions(+), 369 deletions(-) diff --git a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala index 775c7987b1..5d6b3c7cf4 100644 --- a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala +++ b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala @@ -18,16 +18,16 @@ object AnoncredLib { version: String, // SCHEMA_Version attr_names: AttributeNames, issuer_id: IssuerId, // ISSUER_DID - ): SchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id) + ): AnoncredSchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id) // issuer def createCredDefinition( issuer_id: String, - schema: SchemaDef, + schema: AnoncredSchemaDef, tag: String, supportRevocation: Boolean, signature_type: uniffi.anoncreds_wrapper.SignatureType.CL.type = uniffi.anoncreds_wrapper.SignatureType.CL - ) = { + ): AnoncredCreateCredentialDefinition = { val credentialDefinition: uniffi.anoncreds_wrapper.IssuerCreateCredentialDefinitionReturn = uniffi.anoncreds_wrapper .Issuer() @@ -40,7 +40,7 @@ object AnoncredLib { uniffi.anoncreds_wrapper.CredentialDefinitionConfig(supportRevocation) ) - CreateCredentialDefinition( + AnoncredCreateCredentialDefinition( credentialDefinition.getCredentialDefinition(), credentialDefinition.getCredentialDefinitionPrivate(), credentialDefinition.getCredentialKeyCorrectnessProof() @@ -49,9 +49,9 @@ object AnoncredLib { // issuer def createOffer( - credentialDefinition: CreateCredentialDefinition, + credentialDefinition: AnoncredCreateCredentialDefinition, credentialDefinitionId: String - ): CredentialOffer = + ): AnoncredCredentialOffer = uniffi.anoncreds_wrapper .Issuer() .createCredentialOffer( @@ -62,15 +62,15 @@ object AnoncredLib { // holder def createCredentialRequest( - linkSecret: LinkSecretWithId, - credentialDefinition: CredentialDefinition, - credentialOffer: CredentialOffer, + linkSecret: AnoncredLinkSecretWithId, + credentialDefinition: AnoncredCredentialDefinition, + credentialOffer: AnoncredCredentialOffer, entropy: String = { val tmp = scala.util.Random() tmp.setSeed(java.security.SecureRandom.getInstanceStrong().nextLong()) tmp.nextString(80) } - ): CreateCrendentialRequest = { + ): AnoncredCreateCrendentialRequest = { val credentialRequest = uniffi.anoncreds_wrapper .Prover() @@ -83,16 +83,16 @@ object AnoncredLib { credentialOffer, // CredentialOffer credential_offer ) - CreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata()) + AnoncredCreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata()) } // holder def processCredential( - credential: Credential, - metadata: CredentialRequestMetadata, - linkSecret: LinkSecretWithId, - credentialDefinition: CredentialDefinition, - ): Credential = { + credential: AnoncredCredential, + metadata: AnoncredCredentialRequestMetadata, + linkSecret: AnoncredLinkSecretWithId, + credentialDefinition: AnoncredCredentialDefinition, + ): AnoncredCredential = { uniffi.anoncreds_wrapper .Prover() .processCredential( @@ -106,16 +106,16 @@ object AnoncredLib { // issuer def createCredential( - credentialDefinition: CredentialDefinition, - credentialDefinitionPrivate: CredentialDefinitionPrivate, - credentialOffer: CredentialOffer, - credentialRequest: CredentialRequest, + credentialDefinition: AnoncredCredentialDefinition, + credentialDefinitionPrivate: AnoncredCredentialDefinitionPrivate, + credentialOffer: AnoncredCredentialOffer, + credentialRequest: AnoncredCredentialRequest, attributeValues: Seq[(String, String)] // java.util.List[AttributeValues] : java.util.List[AttributeValues] // revocationRegistryId : String // revocationStatusList : RevocationStatusList // credentialRevocationConfig : CredentialRevocationConfig - ): Credential = { + ): AnoncredCredential = { uniffi.anoncreds_wrapper .Issuer() .createCredential( @@ -140,12 +140,12 @@ object AnoncredLib { // [info] Caused by: Predicate is not satisfied def createPresentation( - presentationRequest: PresentationRequest, - credentialRequests: Seq[CredentialRequests], + presentationRequest: AnoncredPresentationRequest, + credentialRequests: Seq[AnoncredCredentialRequests], selfAttested: Map[String, String], - linkSecret: LinkSecret, - schemas: Map[SchemaId, SchemaDef], - credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition], + linkSecret: AnoncredLinkSecret, + schemas: Map[SchemaId, AnoncredSchemaDef], + credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition], ): Either[uniffi.anoncreds_wrapper.AnoncredsException.CreatePresentationException, AnoncredPresentation] = { try { Right( @@ -182,9 +182,9 @@ object AnoncredLib { // FIXME its always return false .... def verifyPresentation( presentation: AnoncredPresentation, - presentationRequest: PresentationRequest, - schemas: Map[SchemaId, SchemaDef], - credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition], + presentationRequest: AnoncredPresentationRequest, + schemas: Map[SchemaId, AnoncredSchemaDef], + credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition], ): Boolean = { uniffi.anoncreds_wrapper .Verifier() diff --git a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala index ee3e24ced9..38f9d4014e 100644 --- a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala +++ b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala @@ -21,41 +21,42 @@ import scala.jdk.CollectionConverters.* type AttributeNames = Set[String] type IssuerId = String -case class LinkSecretWithId(id: String, secret: LinkSecret) { def data = secret.data } -object LinkSecretWithId { - def apply(id: String): LinkSecretWithId = LinkSecretWithId(id, LinkSecret()) +case class AnoncredLinkSecretWithId(id: String, secret: AnoncredLinkSecret) { def data = secret.data } +object AnoncredLinkSecretWithId { + def apply(id: String): AnoncredLinkSecretWithId = AnoncredLinkSecretWithId(id, AnoncredLinkSecret()) } -case class LinkSecret(data: String) -object LinkSecret { +case class AnoncredLinkSecret(data: String) +object AnoncredLinkSecret { - def apply(): LinkSecret = LinkSecret.given_Conversion_UniffiLinkSecret_LinkSecret(UniffiLinkSecret()) + def apply(): AnoncredLinkSecret = + AnoncredLinkSecret.given_Conversion_UniffiLinkSecret_AnoncredLinkSecret(UniffiLinkSecret()) - given Conversion[LinkSecret, UniffiLinkSecret] with { - def apply(linkSecret: LinkSecret): UniffiLinkSecret = + given Conversion[AnoncredLinkSecret, UniffiLinkSecret] with { + def apply(linkSecret: AnoncredLinkSecret): UniffiLinkSecret = UniffiLinkSecret.Companion.newFromValue(linkSecret.data) } - given Conversion[UniffiLinkSecret, LinkSecret] with { - def apply(uniffiLinkSecret: UniffiLinkSecret): LinkSecret = - LinkSecret.apply(uniffiLinkSecret.getValue()) + given Conversion[UniffiLinkSecret, AnoncredLinkSecret] with { + def apply(uniffiLinkSecret: UniffiLinkSecret): AnoncredLinkSecret = + AnoncredLinkSecret.apply(uniffiLinkSecret.getValue()) } } //FIXME use same names as in https://hyperledger.github.io/anoncreds-spec/#term:schemas -case class SchemaDef( +case class AnoncredSchemaDef( name: String, // SCHEMA_ID version: String, // SCHEMA_Version attributes: AttributeNames, issuer_id: IssuerId, // ISSUER_DID ) -object SchemaDef { +object AnoncredSchemaDef { - given Conversion[SchemaDef, UniffiSchema] with { - def apply(schemaDef: SchemaDef): UniffiSchema = + given Conversion[AnoncredSchemaDef, UniffiSchema] with { + def apply(schemaDef: AnoncredSchemaDef): UniffiSchema = UniffiSchema.apply( schemaDef.name, schemaDef.version, @@ -65,9 +66,9 @@ object SchemaDef { } - given Conversion[UniffiSchema, SchemaDef] with { - def apply(schema: UniffiSchema): SchemaDef = - SchemaDef.apply( + given Conversion[UniffiSchema, AnoncredSchemaDef] with { + def apply(schema: UniffiSchema): AnoncredSchemaDef = + AnoncredSchemaDef.apply( name = schema.getName(), version = schema.getVersion(), attributes = schema.getAttrNames().asScala.toSet, @@ -106,20 +107,20 @@ object SchemaDef { // value: String, // issuerId: String, // ) -case class CredentialDefinition(data: String) { // TODO - def schemaId = CredentialDefinition - .given_Conversion_CredentialDefinition_UniffiCredentialDefinition(this) +case class AnoncredCredentialDefinition(data: String) { // TODO + def schemaId = AnoncredCredentialDefinition + .given_Conversion_AnoncredCredentialDefinition_UniffiCredentialDefinition(this) .getSchemaId() } -object CredentialDefinition { - given Conversion[CredentialDefinition, UniffiCredentialDefinition] with { - def apply(credentialDefinition: CredentialDefinition): UniffiCredentialDefinition = +object AnoncredCredentialDefinition { + given Conversion[AnoncredCredentialDefinition, UniffiCredentialDefinition] with { + def apply(credentialDefinition: AnoncredCredentialDefinition): UniffiCredentialDefinition = UniffiCredentialDefinition(credentialDefinition.data) } - given Conversion[UniffiCredentialDefinition, CredentialDefinition] with { - def apply(credentialDefinition: UniffiCredentialDefinition): CredentialDefinition = - CredentialDefinition(credentialDefinition.getJson()) + given Conversion[UniffiCredentialDefinition, AnoncredCredentialDefinition] with { + def apply(credentialDefinition: UniffiCredentialDefinition): AnoncredCredentialDefinition = + AnoncredCredentialDefinition(credentialDefinition.getJson()) } } @@ -134,90 +135,94 @@ object CredentialDefinition { // "r_key": null // } // } -case class CredentialDefinitionPrivate(data: String) -object CredentialDefinitionPrivate { - given Conversion[CredentialDefinitionPrivate, UniffiCredentialDefinitionPrivate] with { - def apply(credentialDefinitionPrivate: CredentialDefinitionPrivate): UniffiCredentialDefinitionPrivate = +case class AnoncredCredentialDefinitionPrivate(data: String) +object AnoncredCredentialDefinitionPrivate { + given Conversion[AnoncredCredentialDefinitionPrivate, UniffiCredentialDefinitionPrivate] with { + def apply(credentialDefinitionPrivate: AnoncredCredentialDefinitionPrivate): UniffiCredentialDefinitionPrivate = UniffiCredentialDefinitionPrivate(credentialDefinitionPrivate.data) } - given Conversion[UniffiCredentialDefinitionPrivate, CredentialDefinitionPrivate] with { - def apply(credentialDefinitionPrivate: UniffiCredentialDefinitionPrivate): CredentialDefinitionPrivate = - CredentialDefinitionPrivate(credentialDefinitionPrivate.getJson()) + given Conversion[UniffiCredentialDefinitionPrivate, AnoncredCredentialDefinitionPrivate] with { + def apply(credentialDefinitionPrivate: UniffiCredentialDefinitionPrivate): AnoncredCredentialDefinitionPrivate = + AnoncredCredentialDefinitionPrivate(credentialDefinitionPrivate.getJson()) } } // **************************************************************************** -case class CredentialKeyCorrectnessProof(data: String) -object CredentialKeyCorrectnessProof { - given Conversion[CredentialKeyCorrectnessProof, UniffiCredentialKeyCorrectnessProof] with { - def apply(credentialKeyCorrectnessProof: CredentialKeyCorrectnessProof): UniffiCredentialKeyCorrectnessProof = +case class AnoncredCredentialKeyCorrectnessProof(data: String) +object AnoncredCredentialKeyCorrectnessProof { + given Conversion[AnoncredCredentialKeyCorrectnessProof, UniffiCredentialKeyCorrectnessProof] with { + def apply( + credentialKeyCorrectnessProof: AnoncredCredentialKeyCorrectnessProof + ): UniffiCredentialKeyCorrectnessProof = UniffiCredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.data) } - given Conversion[UniffiCredentialKeyCorrectnessProof, CredentialKeyCorrectnessProof] with { - def apply(credentialKeyCorrectnessProof: UniffiCredentialKeyCorrectnessProof): CredentialKeyCorrectnessProof = - CredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.getJson()) + given Conversion[UniffiCredentialKeyCorrectnessProof, AnoncredCredentialKeyCorrectnessProof] with { + def apply( + credentialKeyCorrectnessProof: UniffiCredentialKeyCorrectnessProof + ): AnoncredCredentialKeyCorrectnessProof = + AnoncredCredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.getJson()) } } -case class CreateCredentialDefinition( - cd: CredentialDefinition, - cdPrivate: CredentialDefinitionPrivate, - proofKey: CredentialKeyCorrectnessProof, +case class AnoncredCreateCredentialDefinition( + cd: AnoncredCredentialDefinition, + cdPrivate: AnoncredCredentialDefinitionPrivate, + proofKey: AnoncredCredentialKeyCorrectnessProof, ) // **************************************************************************** -case class CredentialOffer(data: String) { - lazy val schemaId = CredentialOffer - .given_Conversion_CredentialOffer_UniffiCredentialOffer(this) +case class AnoncredCredentialOffer(data: String) { + lazy val schemaId = AnoncredCredentialOffer + .given_Conversion_AnoncredCredentialOffer_UniffiCredentialOffer(this) .getSchemaId() - lazy val credDefId = CredentialOffer - .given_Conversion_CredentialOffer_UniffiCredentialOffer(this) + lazy val credDefId = AnoncredCredentialOffer + .given_Conversion_AnoncredCredentialOffer_UniffiCredentialOffer(this) .getCredDefId() } -object CredentialOffer { - given Conversion[CredentialOffer, UniffiCredentialOffer] with { - def apply(credentialOffer: CredentialOffer): UniffiCredentialOffer = +object AnoncredCredentialOffer { + given Conversion[AnoncredCredentialOffer, UniffiCredentialOffer] with { + def apply(credentialOffer: AnoncredCredentialOffer): UniffiCredentialOffer = UniffiCredentialOffer(credentialOffer.data) } - given Conversion[UniffiCredentialOffer, CredentialOffer] with { - def apply(credentialOffer: UniffiCredentialOffer): CredentialOffer = - CredentialOffer(credentialOffer.getJson()) + given Conversion[UniffiCredentialOffer, AnoncredCredentialOffer] with { + def apply(credentialOffer: UniffiCredentialOffer): AnoncredCredentialOffer = + AnoncredCredentialOffer(credentialOffer.getJson()) } } // **************************************************************************** -case class CreateCrendentialRequest( - request: CredentialRequest, - metadata: CredentialRequestMetadata +case class AnoncredCreateCrendentialRequest( + request: AnoncredCredentialRequest, + metadata: AnoncredCredentialRequestMetadata ) -case class CredentialRequest(data: String) -object CredentialRequest { +case class AnoncredCredentialRequest(data: String) +object AnoncredCredentialRequest { - given Conversion[CredentialRequest, UniffiCredentialRequest] with { - def apply(credentialRequest: CredentialRequest): UniffiCredentialRequest = + given Conversion[AnoncredCredentialRequest, UniffiCredentialRequest] with { + def apply(credentialRequest: AnoncredCredentialRequest): UniffiCredentialRequest = UniffiCredentialRequest(credentialRequest.data) } - given Conversion[UniffiCredentialRequest, CredentialRequest] with { - def apply(credentialRequest: UniffiCredentialRequest): CredentialRequest = - CredentialRequest(credentialRequest.getJson()) + given Conversion[UniffiCredentialRequest, AnoncredCredentialRequest] with { + def apply(credentialRequest: UniffiCredentialRequest): AnoncredCredentialRequest = + AnoncredCredentialRequest(credentialRequest.getJson()) } } -case class CredentialRequestMetadata( +case class AnoncredCredentialRequestMetadata( linkSecretBlinding: String, nonce: String, linkSecretName: String, ) -object CredentialRequestMetadata { - given Conversion[CredentialRequestMetadata, UniffiCredentialRequestMetadata] with { - def apply(credentialRequestMetadata: CredentialRequestMetadata): UniffiCredentialRequestMetadata = +object AnoncredCredentialRequestMetadata { + given Conversion[AnoncredCredentialRequestMetadata, UniffiCredentialRequestMetadata] with { + def apply(credentialRequestMetadata: AnoncredCredentialRequestMetadata): UniffiCredentialRequestMetadata = UniffiCredentialRequestMetadata( /*link_secret_blinding_data*/ credentialRequestMetadata.linkSecretBlinding, /*nonce*/ Nonce.Companion.newFromValue(credentialRequestMetadata.nonce), @@ -225,62 +230,63 @@ object CredentialRequestMetadata { ) } - given Conversion[UniffiCredentialRequestMetadata, CredentialRequestMetadata] with { - def apply(credentialRequestMetadata: UniffiCredentialRequestMetadata): CredentialRequestMetadata = - CredentialRequestMetadata( + given Conversion[UniffiCredentialRequestMetadata, AnoncredCredentialRequestMetadata] with { + def apply(credentialRequestMetadata: UniffiCredentialRequestMetadata): AnoncredCredentialRequestMetadata = + AnoncredCredentialRequestMetadata( linkSecretBlinding = credentialRequestMetadata.getLinkSecretBlindingData(), nonce = credentialRequestMetadata.getNonce().getValue(), linkSecretName = credentialRequestMetadata.getLinkSecretName(), ) } - given JsonDecoder[CredentialRequestMetadata] = DeriveJsonDecoder.gen[CredentialRequestMetadata] - given JsonEncoder[CredentialRequestMetadata] = DeriveJsonEncoder.gen[CredentialRequestMetadata] + given JsonDecoder[AnoncredCredentialRequestMetadata] = DeriveJsonDecoder.gen[AnoncredCredentialRequestMetadata] + given JsonEncoder[AnoncredCredentialRequestMetadata] = DeriveJsonEncoder.gen[AnoncredCredentialRequestMetadata] } // **************************************************************************** //Credential -case class Credential(data: String) { - lazy val credDefId: String = Credential - .given_Conversion_Credential_UniffiCredential(this) +case class AnoncredCredential(data: String) { + lazy val credDefId: String = AnoncredCredential + .given_Conversion_AnoncredCredential_UniffiCredential(this) .getCredDefId } -object Credential { - given Conversion[Credential, UniffiCredential] with { - def apply(credential: Credential): UniffiCredential = +object AnoncredCredential { + given Conversion[AnoncredCredential, UniffiCredential] with { + def apply(credential: AnoncredCredential): UniffiCredential = UniffiCredential(credential.data) } - given Conversion[UniffiCredential, Credential] with { - def apply(credential: UniffiCredential): Credential = - Credential(credential.getJson()) + given Conversion[UniffiCredential, AnoncredCredential] with { + def apply(credential: UniffiCredential): AnoncredCredential = + AnoncredCredential(credential.getJson()) } } // **************************************************************************** -case class CredentialRequests( - credential: Credential, +case class AnoncredCredentialRequests( + credential: AnoncredCredential, requestedAttribute: Seq[String], requestedPredicate: Seq[String], ) -object CredentialRequests { - given Conversion[CredentialRequests, UniffiCredentialRequests] with { +object AnoncredCredentialRequests { + given Conversion[AnoncredCredentialRequests, UniffiCredentialRequests] with { import uniffi.anoncreds_wrapper.RequestedAttribute import uniffi.anoncreds_wrapper.RequestedPredicate - def apply(credentialRequests: CredentialRequests): UniffiCredentialRequests = { - val credential = Credential.given_Conversion_Credential_UniffiCredential(credentialRequests.credential) + def apply(credentialRequests: AnoncredCredentialRequests): UniffiCredentialRequests = { + val credential = + AnoncredCredential.given_Conversion_AnoncredCredential_UniffiCredential(credentialRequests.credential) val requestedAttributes = credentialRequests.requestedAttribute.map(a => RequestedAttribute(a, true)) val requestedPredicates = credentialRequests.requestedPredicate.map(p => RequestedPredicate(p)) UniffiCredentialRequests(credential, requestedAttributes.asJava, requestedPredicates.asJava) } } - given Conversion[UniffiCredentialRequests, CredentialRequests] with { - def apply(credentialRequests: UniffiCredentialRequests): CredentialRequests = { - CredentialRequests( - Credential.given_Conversion_UniffiCredential_Credential(credentialRequests.getCredential()), + given Conversion[UniffiCredentialRequests, AnoncredCredentialRequests] with { + def apply(credentialRequests: UniffiCredentialRequests): AnoncredCredentialRequests = { + AnoncredCredentialRequests( + AnoncredCredential.given_Conversion_UniffiCredential_AnoncredCredential(credentialRequests.getCredential()), credentialRequests .getRequestedAttribute() .asScala @@ -301,16 +307,16 @@ object CredentialRequests { // **************************************************************************** -case class PresentationRequest(data: String) -object PresentationRequest { - given Conversion[PresentationRequest, UniffiPresentationRequest] with { - def apply(presentationRequest: PresentationRequest): UniffiPresentationRequest = +case class AnoncredPresentationRequest(data: String) +object AnoncredPresentationRequest { + given Conversion[AnoncredPresentationRequest, UniffiPresentationRequest] with { + def apply(presentationRequest: AnoncredPresentationRequest): UniffiPresentationRequest = UniffiPresentationRequest(presentationRequest.data) } - given Conversion[UniffiPresentationRequest, PresentationRequest] with { - def apply(presentationRequest: UniffiPresentationRequest): PresentationRequest = - PresentationRequest(presentationRequest.getJson()) + given Conversion[UniffiPresentationRequest, AnoncredPresentationRequest] with { + def apply(presentationRequest: UniffiPresentationRequest): AnoncredPresentationRequest = + AnoncredPresentationRequest(presentationRequest.getJson()) } } diff --git a/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala b/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala index 6e6a3c1d5a..fd75b61a4b 100644 --- a/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala +++ b/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala @@ -15,13 +15,13 @@ class PoCNewLib extends AnyFlatSpec { "LinkSecret" should "be able to parse back to the anoncreds lib" in { import scala.language.implicitConversions - val ls1 = LinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890") + val ls1 = AnoncredLinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890") val ls1p = ls1: uniffi.anoncreds_wrapper.LinkSecret assert(ls1p.getValue() == "65965334953670062552662719679603258895632947953618378932199361160021795698890") - val ls0 = LinkSecret() + val ls0 = AnoncredLinkSecret() val ls0p = ls0: uniffi.anoncreds_wrapper.LinkSecret - val ls0_ = ls0p: LinkSecret + val ls0_ = ls0p: AnoncredLinkSecret assert(ls0.data == ls0_.data) } @@ -58,8 +58,8 @@ class PoCNewLib extends AnyFlatSpec { // ############## println("*** holder " + ("*" * 100)) - val ls1 = LinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890") - val linkSecret = LinkSecretWithId("ID_of_some_secret_1", ls1) + val ls1 = AnoncredLinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890") + val linkSecret = AnoncredLinkSecretWithId("ID_of_some_secret_1", ls1) val credentialRequest = AnoncredLib.createCredentialRequest(linkSecret, credentialDefinition.cd, credentialOffer) println("*" * 100) @@ -89,7 +89,7 @@ class PoCNewLib extends AnyFlatSpec { // ############## // TODO READ about PresentationRequest https://hyperledger.github.io/anoncreds-spec/#create-presentation-request - val presentationRequest = PresentationRequest( + val presentationRequest = AnoncredPresentationRequest( s"""{ "nonce": "1103253414365527824079144", "name":"proof_req_1", @@ -109,7 +109,7 @@ class PoCNewLib extends AnyFlatSpec { val presentation = AnoncredLib.createPresentation( presentationRequest, // : PresentationRequest, Seq( - CredentialRequests(processedCredential, Seq("sex"), Seq("age")) + AnoncredCredentialRequests(processedCredential, Seq("sex"), Seq("age")) ), // credentials: Seq[Credential], Map(), // selfAttested: Map[String, String], linkSecret.secret, // linkSecret: LinkSecret, diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala index 7ec80dcde7..1551c85f53 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala @@ -9,7 +9,7 @@ import io.iohk.atala.mercury.protocol.issuecredential.{ OfferCredential, RequestCredential } -import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata +import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata import io.iohk.atala.pollux.core.model.IssueCredentialRecord.* import java.time.Instant @@ -31,7 +31,7 @@ final case class IssueCredentialRecord( protocolState: ProtocolState, offerCredentialData: Option[OfferCredential], requestCredentialData: Option[RequestCredential], - anonCredsRequestMetadata: Option[CredentialRequestMetadata], + anonCredsRequestMetadata: Option[AnoncredCredentialRequestMetadata], issueCredentialData: Option[IssueCredential], issuedCredentialRaw: Option[String], issuingDID: Option[CanonicalPrismDID], diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala index 7009ad0021..257116b21e 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala @@ -19,7 +19,8 @@ object PresentationError { object MissingCredentialFormat extends PresentationError final case class UnsupportedCredentialFormat(vcFormat: String) extends PresentationError final case class InvalidAnoncredPresentationRequest(error: String) extends PresentationError + final case class InvalidAnoncredPresentation(error: String) extends PresentationError final case class MissingAnoncredPresentationRequest(error: String) extends PresentationError - final case class AnoncredPresentationCreationError(cause: Throwable) extends PresentationError + final case class AnoncredPresentationVerificationError(cause: Throwable) extends PresentationError } diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala index 554d36aab0..98823ed3f5 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala @@ -8,6 +8,7 @@ import zio.json.* import java.time.OffsetDateTime import java.time.ZoneOffset import java.util.UUID +import scala.util.Try type Definition = zio.json.ast.Json type CorrectnessProof = zio.json.ast.Json @@ -58,7 +59,7 @@ case class CredentialDefinition( signatureType: String, supportRevocation: Boolean ) { - def longId = CredentialDefinition.makeLongId(author, id, version) + def longId = CredentialDefinition.makeLongId(author, guid, version) } object CredentialDefinition { @@ -69,6 +70,17 @@ object CredentialDefinition { def makeGUID(author: String, id: UUID, version: String) = UUID.nameUUIDFromBytes(makeLongId(author, id, version).getBytes) + def extractGUID(longId: String): Option[UUID] = { + longId.split("/") match { + case Array(_, idWithVersion) => + idWithVersion.split("\\?") match { + case Array(id, _) => Try(UUID.fromString(id)).toOption + case _ => None + } + case _ => None + } + } + def make( in: Input, definitionSchemaId: String, diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala index e3cfb4a239..365f8a0251 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala @@ -48,12 +48,12 @@ class SchemaSerDes[S](jsonSchemaSchemaStr: String) { } yield json } - def validate(jsonString: String): IO[JsonSchemaError, Boolean] = { + def validate(jsonString: String): IO[JsonSchemaError, Unit] = { for { jsonSchemaSchema <- JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr) schemaValidator = JsonSchemaValidatorImpl(jsonSchemaSchema) - _ <- schemaValidator.validate(jsonString) - } yield true + result <- schemaValidator.validate(jsonString) + } yield result } } diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala index 186bc0276c..2f2b9615e5 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala @@ -1,7 +1,7 @@ package io.iohk.atala.pollux.core.repository import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, RequestCredential} -import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata +import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState import io.iohk.atala.shared.models.WalletAccessContext @@ -53,7 +53,7 @@ trait CredentialRepository { def updateWithAnonCredsRequestCredential( recordId: DidCommID, request: RequestCredential, - metadata: CredentialRequestMetadata, + metadata: AnoncredCredentialRequestMetadata, protocolState: ProtocolState ): RIO[WalletAccessContext, Int] diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala index 5b2ba4dbea..c329a2548a 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala @@ -1,7 +1,7 @@ package io.iohk.atala.pollux.core.repository import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, RequestCredential} -import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata +import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError.* @@ -315,7 +315,7 @@ class CredentialRepositoryInMemory( override def updateWithAnonCredsRequestCredential( recordId: DidCommID, request: RequestCredential, - metadata: CredentialRequestMetadata, + metadata: AnoncredCredentialRequestMetadata, protocolState: ProtocolState ): RIO[WalletAccessContext, RuntimeFlags] = { for { diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala index 7aca15f038..601207b260 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala @@ -2,7 +2,7 @@ package io.iohk.atala.pollux.core.service import io.iohk.atala.agent.walletapi.storage import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage -import io.iohk.atala.pollux.anoncreds.{AnoncredLib, SchemaDef} +import io.iohk.atala.pollux.anoncreds.{AnoncredLib, AnoncredSchemaDef} import io.iohk.atala.pollux.core.model.error.CredentialSchemaError import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.URISyntaxError import io.iohk.atala.pollux.core.model.schema.CredentialDefinition.{Filter, FilteredEntries} @@ -39,7 +39,7 @@ class CredentialDefinitionServiceImpl( vcSchema <- parseCredentialSchema(content) anoncredSchema <- AnoncredSchemaSerDesV1.schemaSerDes.deserialize(vcSchema.schema) anoncredLibSchema = - SchemaDef( + AnoncredSchemaDef( in.schemaId, anoncredSchema.version, anoncredSchema.attrNames, diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala index 2550e62047..ffddf3856d 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala @@ -10,7 +10,12 @@ import io.iohk.atala.castor.core.service.DIDService import io.iohk.atala.mercury.model.* import io.iohk.atala.mercury.protocol.issuecredential.* import io.iohk.atala.pollux.* -import io.iohk.atala.pollux.anoncreds.{AnoncredLib, CreateCredentialDefinition, CredentialOffer} +import io.iohk.atala.pollux.anoncreds.{ + AnoncredCreateCredentialDefinition, + AnoncredCredential, + AnoncredCredentialOffer, + AnoncredLib +} import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.CredentialFormat.AnonCreds import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState.OfferReceived @@ -318,7 +323,7 @@ private class CredentialServiceImpl( case Base64(value) => for { _ <- ZIO - .attempt(CredentialOffer(value)) + .attempt(AnoncredCredentialOffer(value)) .mapError(e => CredentialServiceError.UnexpectedError( s"Unexpected error parsing credential offer attachment: ${e.toString}" @@ -536,12 +541,12 @@ private class CredentialServiceImpl( } ) .mapError(_ => InvalidFlowStateError(s"No AnonCreds offer attachment found")) - credentialOffer = anoncreds.CredentialOffer(attachmentData) + credentialOffer = anoncreds.AnoncredCredentialOffer(attachmentData) _ <- ZIO.logInfo(s"Cred def ID => ${credentialOffer.getCredDefId}") credDefContent <- uriDereferencer .dereference(new URI(credentialOffer.getCredDefId)) .mapError(err => UnexpectedError(err.toString)) - credentialDefinition = anoncreds.CredentialDefinition(credDefContent) + credentialDefinition = anoncreds.AnoncredCredentialDefinition(credDefContent) linkSecret <- linkSecretService .fetchOrCreate() .mapError(e => CredentialServiceError.LinkSecretError.apply(e.cause)) @@ -600,34 +605,48 @@ private class CredentialServiceImpl( override def receiveCredentialIssue( issueCredential: IssueCredential ): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] = { - // TODO We can get rid of this 'raw' representation stored in DB, because it is not used. - val rawIssuedCredential = issueCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???") for { // TODO Move this type of generic/reusable code to a helper trait - attachmentFormatAndData <- ZIO.succeed { - import IssueCredentialIssuedFormat.{Anoncred, JWT} - issueCredential.attachments - .collectFirst { - case AttachmentDescriptor(_, _, Base64(v), Some(JWT.name), _, _, _, _) => (JWT, v) - case AttachmentDescriptor(_, _, Base64(v), Some(Anoncred.name), _, _, _, _) => (Anoncred, v) - } - .map { case (f, v) => (f, java.util.Base64.getUrlDecoder.decode(v)) } - } record <- getRecordFromThreadIdWithState( issueCredential.thid.map(DidCommID(_)), ignoreWithZeroRetries = true, ProtocolState.RequestPending, ProtocolState.RequestSent ) - _ <- attachmentFormatAndData match - case Some(IssueCredentialIssuedFormat.JWT, _) => ZIO.succeed(()) - case Some(IssueCredentialIssuedFormat.Anoncred, ba) => processAnonCredsCredential(record, ba) - case _ => ZIO.fail(UnexpectedError("No AnonCreds or JWT credential attachment found")) + processedAttachments <- { + import IssueCredentialIssuedFormat.Anoncred + ZIO.collectAll( + issueCredential.attachments + .map { + case AttachmentDescriptor( + id, + media_type, + Base64(v), + Some(Anoncred.name), + _, + _, + _, + _ + ) => + processAnonCredsCredential(record, java.util.Base64.getUrlDecoder.decode(v)) + .map(processedCredential => + AttachmentDescriptor.buildBase64Attachment( + id = id, + mediaType = media_type, + format = Some(IssueCredentialIssuedFormat.Anoncred.name), + payload = processedCredential + ) + ) + case attachment => ZIO.succeed(attachment) + } + ) + } + processedIssuedCredential = issueCredential.copy(attachments = processedAttachments) _ <- credentialRepository .updateWithIssuedRawCredential( record.id, - issueCredential, - rawIssuedCredential, + processedIssuedCredential, + processedIssuedCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???"), ProtocolState.CredentialReceived ) .flatMap { @@ -642,30 +661,33 @@ private class CredentialServiceImpl( } yield record } - private[this] def processAnonCredsCredential(record: IssueCredentialRecord, credentialBytes: Array[Byte]) = { + private[this] def processAnonCredsCredential( + record: IssueCredentialRecord, + credentialBytes: Array[Byte] + ): ZIO[WalletAccessContext, CredentialServiceError, Array[Byte]] = { for { - credential <- ZIO.succeed(anoncreds.Credential(new String(credentialBytes))) + credential <- ZIO.succeed(anoncreds.AnoncredCredential(new String(credentialBytes))) credDefContent <- uriDereferencer .dereference(new URI(credential.getCredDefId)) .mapError(err => UnexpectedError(err.toString)) - credentialDefinition = anoncreds.CredentialDefinition(credDefContent) + credentialDefinition = anoncreds.AnoncredCredentialDefinition(credDefContent) metadata <- ZIO .fromOption(record.anonCredsRequestMetadata) .mapError(_ => CredentialServiceError.UnexpectedError(s"No request metadata Id found un record: ${record.id}")) linkSecret <- linkSecretService .fetchOrCreate() .mapError(e => CredentialServiceError.LinkSecretError.apply(e.cause)) - _ <- ZIO + credential <- ZIO .attempt( AnoncredLib.processCredential( - anoncreds.Credential(new String(credentialBytes)), + anoncreds.AnoncredCredential(new String(credentialBytes)), metadata, linkSecret, credentialDefinition ) ) .mapError(error => UnexpectedError(s"AnonCreds credential processing error: ${error.getMessage}")) - } yield () + } yield credential.data.getBytes() } override def markOfferSent( @@ -856,16 +878,16 @@ private class CredentialServiceImpl( credentialDefinition <- credentialDefinitionService .getByGUID(credentialDefinitionGUID) .mapError(e => CredentialServiceError.UnexpectedError(e.toString)) - cd = anoncreds.CredentialDefinition(credentialDefinition.definition.toString) - kcp = anoncreds.CredentialKeyCorrectnessProof(credentialDefinition.keyCorrectnessProof.toString) + cd = anoncreds.AnoncredCredentialDefinition(credentialDefinition.definition.toString) + kcp = anoncreds.AnoncredCredentialKeyCorrectnessProof(credentialDefinition.keyCorrectnessProof.toString) maybeCredentialDefinitionSecret <- genericSecretStorage .get[UUID, CredentialDefinitionSecret](credentialDefinition.guid) .orDie credentialDefinitionSecret <- ZIO .fromOption(maybeCredentialDefinitionSecret) .mapError(_ => CredentialServiceError.CredentialDefinitionPrivatePartNotFound(credentialDefinition.guid)) - cdp = anoncreds.CredentialDefinitionPrivate(credentialDefinitionSecret.json.toString) - createCredentialDefinition = CreateCredentialDefinition(cd, cdp, kcp) + cdp = anoncreds.AnoncredCredentialDefinitionPrivate(credentialDefinitionSecret.json.toString) + createCredentialDefinition = AnoncredCreateCredentialDefinition(cd, cdp, kcp) offer = AnoncredLib.createOffer(createCredentialDefinition, credentialDefinitionId) } yield offer @@ -1069,7 +1091,7 @@ private class CredentialServiceImpl( credentialDefinition <- credentialDefinitionService .getByGUID(credentialDefinitionId) .mapError(e => CredentialServiceError.UnexpectedError(e.toString)) - cd = anoncreds.CredentialDefinition(credentialDefinition.definition.toString) + cd = anoncreds.AnoncredCredentialDefinition(credentialDefinition.definition.toString) offerCredential <- ZIO .fromOption(record.offerCredentialData) .mapError(_ => InvalidFlowStateError(s"No offer found for this record: ${record.id}")) @@ -1084,7 +1106,7 @@ private class CredentialServiceImpl( } ) .mapError(_ => InvalidFlowStateError(s"No AnonCreds offer attachment found")) - credentialOffer = anoncreds.CredentialOffer(offerCredentialAttachmentData) + credentialOffer = anoncreds.AnoncredCredentialOffer(offerCredentialAttachmentData) requestCredential <- ZIO .fromOption(record.requestCredentialData) .mapError(_ => InvalidFlowStateError(s"No request found for this record: ${record.id}")) @@ -1099,7 +1121,7 @@ private class CredentialServiceImpl( } ) .mapError(_ => InvalidFlowStateError(s"No AnonCreds request attachment found")) - credentialRequest = anoncreds.CredentialRequest(requestCredentialAttachmentData) + credentialRequest = anoncreds.AnoncredCredentialRequest(requestCredentialAttachmentData) attrValues = offerCredential.body.credential_preview.body.attributes.map { attr => (attr.name, attr.value) } @@ -1109,14 +1131,15 @@ private class CredentialServiceImpl( credentialDefinitionSecret <- ZIO .fromOption(maybeCredentialDefinitionSecret) .mapError(_ => CredentialServiceError.CredentialDefinitionPrivatePartNotFound(credentialDefinition.guid)) - cdp = anoncreds.CredentialDefinitionPrivate(credentialDefinitionSecret.json.toString) - credential = AnoncredLib.createCredential( - cd, - cdp, - credentialOffer, - credentialRequest, - attrValues - ) + cdp = anoncreds.AnoncredCredentialDefinitionPrivate(credentialDefinitionSecret.json.toString) + credential = + AnoncredLib.createCredential( + cd, + cdp, + credentialOffer, + credentialRequest, + attrValues + ) } yield credential } diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala index c87c63e6ed..f0d6aa2448 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala @@ -1,6 +1,6 @@ package io.iohk.atala.pollux.core.service -import io.iohk.atala.pollux.anoncreds.LinkSecretWithId +import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecretWithId import io.iohk.atala.pollux.core.model.error.LinkSecretError import io.iohk.atala.shared.models.WalletAccessContext import zio.ZIO @@ -8,5 +8,5 @@ import zio.ZIO trait LinkSecretService { type Result[T] = ZIO[WalletAccessContext, LinkSecretError, T] - def fetchOrCreate(): Result[LinkSecretWithId] + def fetchOrCreate(): Result[AnoncredLinkSecretWithId] } diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala index 4219d2bbd0..88e51199be 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala @@ -1,7 +1,7 @@ package io.iohk.atala.pollux.core.service import io.iohk.atala.agent.walletapi.storage.{GenericSecret, GenericSecretStorage} -import io.iohk.atala.pollux.anoncreds.{LinkSecret, LinkSecretWithId} +import io.iohk.atala.pollux.anoncreds.{AnoncredLinkSecret, AnoncredLinkSecretWithId} import io.iohk.atala.pollux.core.model.error.LinkSecretError import io.iohk.atala.shared.models.WalletAccessContext import zio.* @@ -15,18 +15,18 @@ class LinkSecretServiceImpl(genericSecretStorage: GenericSecretStorage) extends type Result[T] = ZIO[WalletAccessContext, LinkSecretError, T] - override def fetchOrCreate(): Result[LinkSecretWithId] = { + override def fetchOrCreate(): Result[AnoncredLinkSecretWithId] = { genericSecretStorage - .get[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId) + .get[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId) .flatMap { case Some(secret) => ZIO.succeed(secret) case None => - val linkSecret = LinkSecret() + val linkSecret = AnoncredLinkSecret() genericSecretStorage - .set[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId, linkSecret) + .set[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId, linkSecret) .as(linkSecret) } - .map(linkSecret => LinkSecretWithId(LinkSecretServiceImpl.defaultLinkSecretId, linkSecret)) + .map(linkSecret => AnoncredLinkSecretWithId(LinkSecretServiceImpl.defaultLinkSecretId, linkSecret)) .mapError(LinkSecretError.apply) } } @@ -40,13 +40,13 @@ object LinkSecretServiceImpl { ] = ZLayer.fromFunction(LinkSecretServiceImpl(_)) - given GenericSecret[String, LinkSecret] = new { + given GenericSecret[String, AnoncredLinkSecret] = new { override def keyPath(id: String): String = s"link-secret/${id.toString}" - override def encodeValue(secret: LinkSecret): Json = Json.Str(secret.data) + override def encodeValue(secret: AnoncredLinkSecret): Json = Json.Str(secret.data) - override def decodeValue(json: Json): Try[LinkSecret] = json match { - case Json.Str(data) => Try(LinkSecret(data)) + override def decodeValue(json: Json): Try[AnoncredLinkSecret] = json match { + case Json.Str(data) => Try(AnoncredLinkSecret(data)) case _ => scala.util.Failure(new Exception("Invalid JSON format for LinkSecret")) } } diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala index 736f0ca451..6049790a99 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala @@ -43,6 +43,8 @@ object MockPresentationService extends Mock[PresentationService] { object MarkPresentationVerificationFailed extends Effect[DidCommID, PresentationError, PresentationRecord] + object VerifyAnoncredPresentation extends Effect[DidCommID, PresentationError, PresentationRecord] + object AcceptRequestPresentation extends Effect[(DidCommID, Seq[String]), PresentationError, PresentationRecord] object AcceptAnoncredRequestPresentation @@ -138,6 +140,13 @@ object MockPresentationService extends Mock[PresentationService] { override def markPresentationVerificationFailed(recordId: DidCommID): IO[PresentationError, PresentationRecord] = proxy(MarkPresentationVerificationFailed, recordId) + override def verifyAnoncredPresentation( + presentation: Presentation, + requestPresentation: RequestPresentation, + recordId: DidCommID + ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = + proxy(VerifyAnoncredPresentation, recordId) + override def receiveRequestPresentation( connectionId: Option[String], request: RequestPresentation @@ -169,6 +178,14 @@ object MockPresentationService extends Mock[PresentationService] { issuanceDate: Instant ): IO[PresentationError, AnoncredPresentation] = ??? + override def createAnoncredPresentation( + requestPresentation: RequestPresentation, + recordId: DidCommID, + prover: Issuer, + anoncredCredentialProof: AnoncredCredentialProofsV1, + issuanceDate: Instant + ): ZIO[WalletAccessContext, PresentationError, Presentation] = ??? + override def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, limit: Int, diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala index 88e5a0759a..9727983870 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala @@ -52,6 +52,14 @@ trait PresentationService { issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] + def createAnoncredPresentation( + requestPresentation: RequestPresentation, + recordId: DidCommID, + prover: Issuer, + anoncredCredentialProof: AnoncredCredentialProofsV1, + issuanceDate: Instant + ): ZIO[WalletAccessContext, PresentationError, Presentation] + def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, limit: Int, @@ -126,6 +134,12 @@ trait PresentationService { recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] + def verifyAnoncredPresentation( + presentation: Presentation, + requestPresentation: RequestPresentation, + recordId: DidCommID + ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] + def reportProcessingFailure( recordId: DidCommID, failReason: Option[String] diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala index c7b866c45c..6ac79b7b17 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala @@ -13,13 +13,15 @@ import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.error.PresentationError import io.iohk.atala.pollux.core.model.error.PresentationError.* import io.iohk.atala.pollux.core.model.presentation.* +import io.iohk.atala.pollux.core.model.schema.CredentialDefinition import io.iohk.atala.pollux.core.model.schema.CredentialSchema.parseCredentialSchema import io.iohk.atala.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1 import io.iohk.atala.pollux.core.repository.{CredentialRepository, PresentationRepository} import io.iohk.atala.pollux.core.service.serdes.{ AnoncredCredentialProofV1, AnoncredCredentialProofsV1, - AnoncredPresentationRequestV1 + AnoncredPresentationRequestV1, + AnoncredPresentationV1 } import io.iohk.atala.pollux.vc.jwt.* import io.iohk.atala.shared.models.WalletAccessContext @@ -31,6 +33,7 @@ import java.rmi.UnexpectedException import java.time.Instant import java.util as ju import java.util.{UUID, Base64 as JBase64} +import scala.util.Try private class PresentationServiceImpl( credentialDefinitionService: CredentialDefinitionService, @@ -153,6 +156,43 @@ private class PresentationServiceImpl( } yield presentationPayload } + def createAnoncredPresentation( + requestPresentation: RequestPresentation, + recordId: DidCommID, + prover: Issuer, + anoncredCredentialProof: AnoncredCredentialProofsV1, + issuanceDate: Instant + ): ZIO[WalletAccessContext, PresentationError, Presentation] = { + for { + presentationPayload <- + createAnoncredPresentationPayloadFromRecord( + recordId, + prover, + anoncredCredentialProof, + issuanceDate + ) + presentation <- ZIO.succeed( + Presentation( + body = Presentation.Body( + goal_code = requestPresentation.body.goal_code, + comment = requestPresentation.body.comment + ), + attachments = Seq( + AttachmentDescriptor + .buildBase64Attachment( + payload = presentationPayload.data.getBytes(), + mediaType = Some(PresentCredentialFormat.Anoncred.name), + format = Some(PresentCredentialFormat.Anoncred.name), + ) + ), + thid = requestPresentation.thid.orElse(Some(requestPresentation.id)), + from = requestPresentation.to, + to = requestPresentation.from + ) + ) + } yield presentation + } + override def extractIdFromCredential(credential: W3cCredentialPayload): Option[UUID] = credential.maybeId.map(_.split("/").last).map(UUID.fromString) @@ -479,7 +519,10 @@ private class PresentationServiceImpl( credentialDefinition <- credentialDefinitionService .getByGUID(credentialDefinitionId) .mapError(e => UnexpectedError(e.toString)) - } yield (credentialDefinition.longId, CredentialDefinition(credentialDefinition.definition.toString)) + } yield ( + credentialDefinition.longId, + AnoncredCredentialDefinition(credentialDefinition.definition.toString) + ) }) .map(_.toMap) credentialProofsMap = credentialProofs.map(credentialProof => (credentialProof.credential, credentialProof)).toMap @@ -525,18 +568,20 @@ private class PresentationServiceImpl( .fetchOrCreate() .map(_.secret) .mapError(t => AnoncredPresentationCreationError(t.cause)) + credentialRequest = + verifiableCredentials.map(verifiableCredential => + AnoncredCredentialRequests( + AnoncredCredential(verifiableCredential.credential), + verifiableCredential.requestedAttribute, + verifiableCredential.requestedPredicate + ) + ) presentation <- ZIO .fromEither( AnoncredLib.createPresentation( - PresentationRequest(presentationRequestData), - verifiableCredentials.map(verifiableCredential => - CredentialRequests( - Credential(verifiableCredential.credential), - verifiableCredential.requestedAttribute, - verifiableCredential.requestedPredicate - ) - ), + AnoncredPresentationRequest(presentationRequestData), + credentialRequest, Map.empty, // TO FIX linkSecret, schemaMap, @@ -547,7 +592,7 @@ private class PresentationServiceImpl( } yield presentation } - private def resolveSchema(schemaId: String): IO[UnexpectedError, (String, SchemaDef)] = { + private def resolveSchema(schemaId: String): IO[UnexpectedError, (String, AnoncredSchemaDef)] = { for { uri <- ZIO.attempt(new URI(schemaId)).mapError(e => UnexpectedError(e.getMessage)) content <- uriDereferencer.dereference(uri).mapError(e => UnexpectedError(e.error)) @@ -556,7 +601,7 @@ private class PresentationServiceImpl( .deserialize(vcSchema.schema) .mapError(e => UnexpectedError(e.error)) anoncredLibSchema = - SchemaDef( + AnoncredSchemaDef( schemaId, anoncredSchema.version, anoncredSchema.attrNames, @@ -882,6 +927,77 @@ private class PresentationServiceImpl( PresentationRecord.ProtocolState.PresentationVerificationFailed ) + override def verifyAnoncredPresentation( + presentation: Presentation, + requestPresentation: RequestPresentation, + recordId: DidCommID + ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { + for { + serializedPresentation <- presentation.attachments.head.data match { + case Base64(data) => ZIO.succeed(AnoncredPresentation(new String(JBase64.getUrlDecoder.decode(data)))) + case _ => ZIO.fail(InvalidAnoncredPresentation("Expecting Base64-encoded data")) + } + deserializedPresentation <- + AnoncredPresentationV1.schemaSerDes + .deserialize(serializedPresentation.data) + .mapError(error => PresentationError.UnexpectedError(error.error)) + schemaIds = deserializedPresentation.identifiers.map(_.schema_id) + schemaMap <- + ZIO + .collectAll(schemaIds.map { schemaId => + resolveSchema(schemaId) + }) + .map(_.toMap) + credentialDefinitionIds = deserializedPresentation.identifiers.map(_.cred_def_id) + credentialDefinitionMap <- + ZIO + .collectAll(credentialDefinitionIds.map { credentialDefinitionId => + for { + guid <- + ZIO + .fromOption(CredentialDefinition.extractGUID(credentialDefinitionId)) + .mapError(_ => + InvalidAnoncredPresentation(s"CredentialDefinitionId format invalid ${credentialDefinitionId}") + ) + credentialDefinition <- + credentialDefinitionService + .getByGUID(guid) + .mapError(e => UnexpectedError(e.toString)) + } yield ( + credentialDefinition.longId, + AnoncredCredentialDefinition(credentialDefinition.definition.toString) + ) + }) + .map(_.toMap) + serializedPresentationRequest <- requestPresentation.attachments.head.data match { + case Base64(data) => ZIO.succeed(AnoncredPresentationRequest(new String(JBase64.getUrlDecoder.decode(data)))) + case _ => ZIO.fail(InvalidAnoncredPresentationRequest("Expecting Base64-encoded data")) + } + isValid <- + ZIO + .fromTry( + Try( + AnoncredLib.verifyPresentation( + serializedPresentation, + serializedPresentationRequest, + schemaMap, + credentialDefinitionMap + ) + ) + ) + .mapError((t: Throwable) => AnoncredPresentationVerificationError(t)) + .flatMapError(e => + for { + _ <- markPresentationVerificationFailed(recordId) + } yield () + ZIO.succeed(e) + ) + result <- + if isValid then markPresentationVerified(recordId) + else markPresentationVerificationFailed(recordId) + } yield result + } + def reportProcessingFailure( recordId: DidCommID, failReason: Option[String] diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala index 894e53858c..59afcefd9c 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala @@ -118,6 +118,15 @@ class PresentationServiceNotifier( ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = notifyOnSuccess(svc.markPresentationVerificationFailed(recordId)) + override def verifyAnoncredPresentation( + presentation: Presentation, + requestPresentation: RequestPresentation, + recordId: DidCommID + ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = + notifyOnSuccess( + svc.verifyAnoncredPresentation(presentation, requestPresentation, recordId) + ) + override def acceptPresentation( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = @@ -166,6 +175,15 @@ class PresentationServiceNotifier( ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = svc.createAnoncredPresentationPayloadFromRecord(record, issuer, anoncredCredentialProof, issuanceDate) + override def createAnoncredPresentation( + requestPresentation: RequestPresentation, + recordId: DidCommID, + prover: Issuer, + anoncredCredentialProof: AnoncredCredentialProofsV1, + issuanceDate: Instant + ): ZIO[WalletAccessContext, PresentationError, Presentation] = + svc.createAnoncredPresentation(requestPresentation, recordId, prover, anoncredCredentialProof, issuanceDate) + override def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, limit: Int, diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala index c4063533eb..e16fe79fff 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala @@ -13,24 +13,17 @@ case class AnoncredPresentationRequestV1( non_revoked: Option[AnoncredNonRevokedIntervalV1] ) -case class AnoncredRequestedAttributeV1(name: String, restrictions: List[AnoncredAttributeRestrictionV1]) +case class AnoncredRequestedAttributeV1( + name: String, + restrictions: List[Map[String, String]], + non_revoked: Option[AnoncredNonRevokedIntervalV1] +) case class AnoncredRequestedPredicateV1( name: String, p_type: String, p_value: Int, - restrictions: List[AnoncredPredicateRestrictionV1] -) - -case class AnoncredAttributeRestrictionV1( - schema_id: Option[String], - cred_def_id: Option[String], - non_revoked: Option[AnoncredNonRevokedIntervalV1] -) - -case class AnoncredPredicateRestrictionV1( - schema_id: Option[String], - cred_def_id: Option[String], + restrictions: List[Map[String, String]], non_revoked: Option[AnoncredNonRevokedIntervalV1] ) @@ -54,20 +47,16 @@ object AnoncredPresentationRequestV1 { | "restrictions": { | "type": "array", | "items": { - | "type": "object", - | "properties": { - | "schema_id": { "type": "string" }, - | "cred_def_id": { "type": "string" }, - | "non_revoked": { - | "type": "object", - | "properties": { - | "from": { "type": "integer" }, - | "to": { "type": "integer" } - | } - | } - | } + | "type": "object" | } - | } + | }, + | "non_revoked": { + | "type": "object", + | "properties": { + | "from": { "type": "integer" }, + | "to": { "type": "integer" } + | } + | } | }, | "required": ["name", "restrictions"] | } @@ -83,19 +72,15 @@ object AnoncredPresentationRequestV1 { | "restrictions": { | "type": "array", | "items": { - | "type": "object", - | "properties": { - | "schema_id": { "type": "string" }, - | "cred_def_id": { "type": "string" }, - | "non_revoked": { - | "type": "object", - | "properties": { + | "type": "object" + | } + | }, + | "non_revoked": { + | "type": "object", + | "properties": { | "from": { "type": "integer" }, | "to": { "type": "integer" } - | } - | } | } - | } | } | }, | "required": ["name", "p_type", "p_value", "restrictions"] @@ -131,24 +116,12 @@ object AnoncredPresentationRequestV1 { given JsonEncoder[AnoncredRequestedPredicateV1] = DeriveJsonEncoder.gen[AnoncredRequestedPredicateV1] - given JsonDecoder[AnoncredAttributeRestrictionV1] = - DeriveJsonDecoder.gen[AnoncredAttributeRestrictionV1] - given JsonEncoder[AnoncredNonRevokedIntervalV1] = DeriveJsonEncoder.gen[AnoncredNonRevokedIntervalV1] given JsonDecoder[AnoncredNonRevokedIntervalV1] = DeriveJsonDecoder.gen[AnoncredNonRevokedIntervalV1] - given JsonEncoder[AnoncredAttributeRestrictionV1] = - DeriveJsonEncoder.gen[AnoncredAttributeRestrictionV1] - - given JsonDecoder[AnoncredPredicateRestrictionV1] = - DeriveJsonDecoder.gen[AnoncredPredicateRestrictionV1] - - given JsonEncoder[AnoncredPredicateRestrictionV1] = - DeriveJsonEncoder.gen[AnoncredPredicateRestrictionV1] - given JsonDecoder[AnoncredPresentationRequestV1] = DeriveJsonDecoder.gen[AnoncredPresentationRequestV1] diff --git a/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json b/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json index 3d1f765fa9..14b1ffcfbe 100644 --- a/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json +++ b/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json @@ -10,7 +10,7 @@ "description": "Simple credential schema for the driving licence verifiable credential.", "type": "AnoncredSchemaV1", "schema": { - "name": "schema:uri2", + "name": "resource:///anoncred-presentation-schema-example.json", "version": "1.0", "attrNames": [ "name", diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala index 1be703bf0f..5b26f677d1 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala @@ -9,7 +9,7 @@ import io.iohk.atala.castor.core.service.MockDIDService import io.iohk.atala.mercury.model import io.iohk.atala.mercury.model.* import io.iohk.atala.mercury.protocol.issuecredential.* -import io.iohk.atala.pollux.anoncreds.Credential +import io.iohk.atala.pollux.anoncreds.AnoncredCredential import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.IssueCredentialRecord.{ProtocolState, Role} import io.iohk.atala.pollux.core.model.error.CredentialServiceError @@ -602,7 +602,7 @@ object CredentialServiceImplSpec extends MockSpecDefault with CredentialServiceS assertTrue(record.issueCredentialData.get.attachments.head.data match case model.Base64(value) => val ba = new String(Base64.getUrlDecoder.decode(value)) - Credential(ba).credDefId == credDefId + AnoncredCredential(ba).credDefId == credDefId case _ => false ) } diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala index c6ea4e6b49..c65219bc37 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala @@ -2,7 +2,7 @@ package io.iohk.atala.pollux.core.service import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage -import io.iohk.atala.pollux.anoncreds.LinkSecret +import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecret import io.iohk.atala.shared.models.WalletId.* import io.iohk.atala.shared.models.{WalletAccessContext, WalletId} import zio.* @@ -30,7 +30,7 @@ object LinkSecretServiceImplSpec extends ZIOSpecDefault { record1 <- svc.fetchOrCreate() storage <- ZIO.service[GenericSecretStorage] maybeDidSecret <- storage - .get[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId) + .get[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId) } yield { assertTrue(record.id == LinkSecretServiceImpl.defaultLinkSecretId) assertTrue(record == record1) diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala index 09e89abae4..befbbdde40 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala @@ -2,10 +2,11 @@ package io.iohk.atala.pollux.core.service import io.circe.parser.decode import io.circe.syntax.* +import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage import io.iohk.atala.mercury.model.{AttachmentDescriptor, Base64, DidId} import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, IssueCredentialIssuedFormat} import io.iohk.atala.mercury.protocol.presentproof.* -import io.iohk.atala.pollux.anoncreds.AnoncredLib +import io.iohk.atala.pollux.anoncreds.* import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.IssueCredentialRecord.* import io.iohk.atala.pollux.core.model.PresentationRecord.* @@ -13,6 +14,7 @@ import io.iohk.atala.pollux.core.model.error.PresentationError import io.iohk.atala.pollux.core.model.error.PresentationError.* import io.iohk.atala.pollux.core.model.presentation.Options import io.iohk.atala.pollux.core.model.schema.CredentialDefinition.Input +import io.iohk.atala.pollux.core.model.secret.CredentialDefinitionSecret import io.iohk.atala.pollux.core.repository.{CredentialRepository, PresentationRepository} import io.iohk.atala.pollux.core.service.serdes.{ AnoncredCredentialProofV1, @@ -27,12 +29,14 @@ import zio.test.* import zio.test.Assertion.* import java.time.{Instant, OffsetDateTime} -import java.util.Base64 as JBase64 +import java.util.{UUID, Base64 as JBase64} object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSpecHelper { override def spec = - suite("PresentationService")(singleWalletSpec, multiWalletSpec).provide(presentationServiceLayer) + suite("PresentationService")(singleWalletSpec, multiWalletSpec).provide( + presentationServiceLayer ++ genericSecretStorageLayer + ) private val singleWalletSpec = suite("singleWalletSpec")( @@ -358,12 +362,148 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp validation <- AnoncredPresentationV1.schemaSerDes.validate(aPresentationPayload.data) presentation <- AnoncredPresentationV1.schemaSerDes.deserialize(aPresentationPayload.data) } yield { - assertTrue(validation) + assert(validation)(isUnit) assert( presentation.proof.proofs.headOption.flatMap(_.primary_proof.eq_proof.revealed_attrs.headOption.map(_._1)) )(isSome(equalTo("sex"))) } }, + test("verify anoncred presentation") { + for { + credentialDefinitionService <- ZIO.service[CredentialDefinitionService] + issuerId = "did:prism:issuer" + holderID = "did:prism:holder" + schemaId = "resource:///anoncred-presentation-schema-example.json" + credentialDefinitionDb <- credentialDefinitionService.create( + Input( + name = "Credential Definition Name", + description = "Credential Definition Description", + version = "1.2", + signatureType = "CL", + tag = "tag", + author = issuerId, + authored = Some(OffsetDateTime.parse("2022-03-10T12:00:00Z")), + schemaId = schemaId, + supportRevocation = false + ) + ) + credentialDefinitionDb <- credentialDefinitionService + .getByGUID(credentialDefinitionDb.guid) + repo <- ZIO.service[CredentialRepository] + linkSecretService <- ZIO.service[LinkSecretService] + linkSecret <- linkSecretService.fetchOrCreate() + cenericSecretStorage <- ZIO.service[GenericSecretStorage] + maybeCredentialDefintionPrivate <- + cenericSecretStorage + .get[UUID, CredentialDefinitionSecret](credentialDefinitionDb.guid) + credentialDefinition = AnoncredCreateCredentialDefinition( + AnoncredCredentialDefinition(credentialDefinitionDb.definition.toString()), + AnoncredCredentialDefinitionPrivate(maybeCredentialDefintionPrivate.get.json.toString()), + AnoncredCredentialKeyCorrectnessProof(credentialDefinitionDb.keyCorrectnessProof.toString()) + ) + credentialOffer = AnoncredLib.createOffer(credentialDefinition, credentialDefinitionDb.longId) + credentialRequest = AnoncredLib.createCredentialRequest(linkSecret, credentialDefinition.cd, credentialOffer) + processedCredential = + AnoncredLib.processCredential( + AnoncredLib + .createCredential( + credentialDefinition.cd, + credentialDefinition.cdPrivate, + credentialOffer, + credentialRequest.request, + Seq( + ("name", "Miguel"), + ("sex", "M"), + ("age", "31"), + ) + ), + credentialRequest.metadata, + linkSecret, + credentialDefinition.cd + ) + issueCredential = + IssueCredential( + from = DidId(issuerId), + to = DidId(holderID), + body = IssueCredential.Body(), + attachments = Seq( + AttachmentDescriptor.buildBase64Attachment( + mediaType = Some("application/json"), + format = Some(IssueCredentialIssuedFormat.Anoncred.name), + payload = processedCredential.data.getBytes() + ) + ) + ) + aIssueCredentialRecord = + IssueCredentialRecord( + id = DidCommID(), + createdAt = Instant.now, + updatedAt = None, + thid = DidCommID(), + schemaId = Some(schemaId), + credentialDefinitionId = Some(credentialDefinitionDb.guid), + credentialFormat = CredentialFormat.AnonCreds, + role = IssueCredentialRecord.Role.Issuer, + subjectId = None, + validityPeriod = None, + automaticIssuance = None, + protocolState = IssueCredentialRecord.ProtocolState.CredentialReceived, + offerCredentialData = None, + requestCredentialData = None, + anonCredsRequestMetadata = None, + issueCredentialData = Some(issueCredential), + issuedCredentialRaw = + Some(issueCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???")), + issuingDID = None, + metaRetries = 5, + metaNextRetry = Some(Instant.now()), + metaLastFailure = None, + ) + _ <- repo.createIssueCredentialRecord(aIssueCredentialRecord) + svc <- ZIO.service[PresentationService] + aRecord <- svc.createAnoncredRecord( + credentialDefinitionId = credentialDefinitionDb.longId + ) + repo <- ZIO.service[PresentationRepository] + credentialsToUse = + AnoncredCredentialProofsV1( + List( + AnoncredCredentialProofV1( + aIssueCredentialRecord.id.value, + Seq("sex"), + Seq("age") + ) + ) + ) + credentialsToUseJson <- ZIO.fromEither( + AnoncredCredentialProofsV1.schemaSerDes.serialize(credentialsToUse) + ) + _ <- + repo.updateAnoncredPresentationWithCredentialsToUse( + aRecord.id, + Some(AnoncredPresentationV1.version), + Some(credentialsToUseJson), + PresentationRecord.ProtocolState.RequestPending + ) + issuer = createIssuer(DID("did:prism:issuer")) + presentation <- svc.createAnoncredPresentation( + aRecord.requestPresentationData.get, + aRecord.id, + issuer, + credentialsToUse, + Instant.now() + ) + _ <- svc.receivePresentation(presentation) + validateRecord <- + svc.verifyAnoncredPresentation( + presentation, + aRecord.requestPresentationData.get, + aRecord.id + ) + } yield { + assert(validateRecord.protocolState)(equalTo(PresentationRecord.ProtocolState.PresentationVerified)) + } + }, test("markRequestPresentationSent returns updated PresentationRecord") { for { svc <- ZIO.service[PresentationService] diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala index 7e20c41bd9..d50f4376b5 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala @@ -171,6 +171,7 @@ trait PresentationServiceSpecHelper { } def createAnoncredRecord( + credentialDefinitionId: String = "$CRED_DEF_ID", pairwiseVerifierDID: DidId = DidId("did:prism:issuer"), pairwiseProverDID: DidId = DidId("did:prism:prover-pairwise"), thid: DidCommID = DidCommID() @@ -180,12 +181,12 @@ trait PresentationServiceSpecHelper { "sex" -> AnoncredRequestedAttributeV1( name = "sex", restrictions = List( - AnoncredAttributeRestrictionV1( - schema_id = None, - cred_def_id = Some("$CRED_DEF_ID"), - non_revoked = None + Map( + ("attr::sex::value" -> "M"), + ("cred_def_id" -> credentialDefinitionId) ) - ) + ), + non_revoked = None ) ), requested_predicates = Map( @@ -193,13 +194,14 @@ trait PresentationServiceSpecHelper { name = "age", p_type = ">=", p_value = 18, - restrictions = List.empty + restrictions = List.empty, + non_revoked = None ) ), name = "proof_req_1", nonce = "1103253414365527824079144", version = "0.1", - non_revoked = Some(AnoncredNonRevokedIntervalV1(from = Some(1), to = Some(4))) + non_revoked = None ) svc.createAnoncredPresentationRecord( thid = thid, diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala index 35bd8416c7..85a18f3204 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala @@ -13,13 +13,13 @@ object AnoncredPresentationRequestSpec extends ZIOSpecDefault { | "name": "Attribute 1", | "restrictions": [ | { - | "cred_def_id": "credential_definition_id_of_attribute1", - | "non_revoked": { - | "from": 1635734400, - | "to": 1735734400 - | } + | "cred_def_id": "credential_definition_id_of_attribute1" | } - | ] + | ], + | "non_revoked": { + | "from": 1635734400, + | "to": 1735734400 + | } | } | }, | "requested_predicates": { @@ -29,12 +29,12 @@ object AnoncredPresentationRequestSpec extends ZIOSpecDefault { | "p_value": 18, | "restrictions": [ | { - | "schema_id": "schema_id_of_predicate1", - | "non_revoked": { - | "from": 1635734400 - | } + | "schema_id": "schema_id_of_predicate1" | } - | ] + | ], + | "non_revoked": { + | "from": 1635734400 + | } | } | }, | "name": "Example Presentation Request", @@ -45,7 +45,7 @@ object AnoncredPresentationRequestSpec extends ZIOSpecDefault { override def spec: Spec[TestEnvironment with Scope, Any] = suite("AnoncredPresentationRequestSerDes")( test("should validate a correct schema") { - assertZIO(AnoncredPresentationRequestV1.schemaSerDes.validate(json))(isTrue) + assertZIO(AnoncredPresentationRequestV1.schemaSerDes.validate(json))(isUnit) }, test("should deserialize correctly") { val expectedPresentationRequest = @@ -54,15 +54,14 @@ object AnoncredPresentationRequestSpec extends ZIOSpecDefault { "attribute1" -> AnoncredRequestedAttributeV1( "Attribute 1", List( - AnoncredAttributeRestrictionV1( - None, - Some("credential_definition_id_of_attribute1"), - Some( - AnoncredNonRevokedIntervalV1( - Some(1635734400), - Some(1735734400) - ) - ) + Map( + "cred_def_id" -> "credential_definition_id_of_attribute1" + ) + ), + Some( + AnoncredNonRevokedIntervalV1( + Some(1635734400), + Some(1735734400) ) ) ) @@ -74,15 +73,14 @@ object AnoncredPresentationRequestSpec extends ZIOSpecDefault { ">=", 18, List( - AnoncredPredicateRestrictionV1( - Some("schema_id_of_predicate1"), - None, - Some( - AnoncredNonRevokedIntervalV1( - Some(1635734400), - None - ) - ) + Map( + "schema_id" -> "schema_id_of_predicate1" + ) + ), + Some( + AnoncredNonRevokedIntervalV1( + Some(1635734400), + None ) ) ) diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala index 88e3fc8620..6517c6bd03 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala @@ -104,7 +104,7 @@ object AnoncredPresentationSpec extends ZIOSpecDefault { override def spec: Spec[TestEnvironment with Scope, Any] = suite("AnoncredPresentationRequestSerDes")( test("should validate a correct schema") { - assertZIO(AnoncredPresentationV1.schemaSerDes.validate(json))(isTrue) + assertZIO(AnoncredPresentationV1.schemaSerDes.validate(json))(isUnit) }, test("should deserialize correctly") { val predicate = AnoncredPredicateV1( diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala index aebb68bea8..aa10648519 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala @@ -1,8 +1,8 @@ package io.iohk.atala.pollux.core.service.serdes import zio.* -import zio.test.Assertion.* import zio.test.* +import zio.test.Assertion.* object PublicCredentialDefinitionSchemaSerDesSpec extends ZIOSpecDefault { val json: String = @@ -46,7 +46,7 @@ object PublicCredentialDefinitionSchemaSerDesSpec extends ZIOSpecDefault { override def spec: Spec[TestEnvironment with Scope, Any] = suite("PublicCredentialDefinitionSerDes")( test("should validate a correct schema") { - assertZIO(PublicCredentialDefinitionSerDesV1.schemaSerDes.validate(json))(isTrue) + assertZIO(PublicCredentialDefinitionSerDesV1.schemaSerDes.validate(json))(isUnit) }, test("should deserialise") { val primary = PublicCredentialPrimaryPublicKeyV1( diff --git a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala index c56e3c10ac..3ce67f30bd 100644 --- a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala +++ b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala @@ -9,7 +9,7 @@ import io.circe.parser.* import io.circe.syntax.* import io.iohk.atala.castor.core.model.did.* import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, OfferCredential, RequestCredential} -import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata +import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata import io.iohk.atala.pollux.core.model.* import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError.* @@ -51,9 +51,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ given requestCredentialGet: Get[RequestCredential] = Get[String].map(decode[RequestCredential](_).getOrElse(???)) given requestCredentialPut: Put[RequestCredential] = Put[String].contramap(_.asJson.toString) - given acRequestMetadataGet: Get[CredentialRequestMetadata] = - Get[String].map(_.fromJson[CredentialRequestMetadata].getOrElse(???)) - given acRequestMetadataPut: Put[CredentialRequestMetadata] = Put[String].contramap(_.toJson) + given acRequestMetadataGet: Get[AnoncredCredentialRequestMetadata] = + Get[String].map(_.fromJson[AnoncredCredentialRequestMetadata].getOrElse(???)) + given acRequestMetadataPut: Put[AnoncredCredentialRequestMetadata] = Put[String].contramap(_.toJson) given issueCredentialGet: Get[IssueCredential] = Get[String].map(decode[IssueCredential](_).getOrElse(???)) given issueCredentialPut: Put[IssueCredential] = Put[String].contramap(_.asJson.toString) @@ -384,7 +384,7 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ override def updateWithAnonCredsRequestCredential( recordId: DidCommID, request: RequestCredential, - metadata: CredentialRequestMetadata, + metadata: AnoncredCredentialRequestMetadata, protocolState: ProtocolState ): RIO[WalletAccessContext, Int] = { val cxnIO = diff --git a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/VerifiableCredentialPayload.scala b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/VerifiableCredentialPayload.scala index b22493dd06..4002c87b57 100644 --- a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/VerifiableCredentialPayload.scala +++ b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/VerifiableCredentialPayload.scala @@ -34,8 +34,6 @@ case class W3cVerifiableCredentialPayload(payload: W3cCredentialPayload, proof: case class JwtVerifiableCredentialPayload(jwt: JWT) extends VerifiableCredentialPayload -case class AnoncredVerifiableCredentialPayload(json: String) extends VerifiableCredentialPayload //FIXME json type - case class CredentialStatus( id: String, `type`: String diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala index 988e235d11..836be0556d 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala @@ -197,7 +197,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { case None => ZIO.fail(InvalidState("PresentationRecord 'RequestPending' with no Record")) case Some(record) => val verifierReqPendingToSentFlow = for { - _ <- ZIO.log(s"PresentationRecord: RequestPending (Send Massage)") + _ <- ZIO.log(s"PresentationRecord: RequestPending (Send Message)") walletAccessContext <- buildWalletAccessContextLayer(record.from) result <- (for { didOps <- ZIO.service[DidOps] @@ -378,36 +378,15 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { anoncredCredentialProofs.credentialProofs.map(_.credential) ).provideSomeLayer(ZLayer.succeed(walletAccessContext)) presentation <- - for { - presentationPayload <- - presentationService - .createAnoncredPresentationPayloadFromRecord( - id, - prover, - anoncredCredentialProofs, - Instant.now() - ) - .provideSomeLayer(ZLayer.succeed(walletAccessContext)) - presentation <- ZIO.succeed( - Presentation( - body = Presentation.Body( - goal_code = requestPresentation.body.goal_code, - comment = requestPresentation.body.comment - ), - attachments = Seq( - AttachmentDescriptor - .buildBase64Attachment( - payload = presentationPayload.data.getBytes(), - mediaType = Some(PresentCredentialFormat.Anoncred.name), - format = Some(PresentCredentialFormat.Anoncred.name), - ) - ), - thid = requestPresentation.thid.orElse(Some(requestPresentation.id)), - from = requestPresentation.to, - to = requestPresentation.from - ) + presentationService + .createAnoncredPresentation( + requestPresentation, + id, + prover, + anoncredCredentialProofs, + Instant.now() ) - } yield presentation + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) _ <- presentationService .markPresentationGenerated(id, presentation) .provideSomeLayer(ZLayer.succeed(walletAccessContext)) @@ -495,7 +474,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { _, _, PresentationReceived, - _, + CredentialFormat.JWT, mayBeRequestPresentation, _, presentation, @@ -518,7 +497,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { didResolverService <- ZIO.service[JwtDidResolver] credentialsValidationResult <- p.attachments.head.data match { case Base64(data) => - val base64Decoded = new String(java.util.Base64.getDecoder().decode(data)) + val base64Decoded = new String(java.util.Base64.getDecoder.decode(data)) val maybePresentationOptions : Either[PresentationError, Option[io.iohk.atala.pollux.core.model.presentation.Options]] = mayBeRequestPresentation @@ -614,7 +593,114 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { "present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_flow_ms_gauge" ) .trackDurationWith(_.toMetricsSeconds) - + case PresentationRecord( + _, + _, + _, + _, + _, + _, + _, + _, + PresentationReceived, + CredentialFormat.AnonCreds, + None, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + ZIO.fail(InvalidState("PresentationRecord in 'PresentationReceived' with no Presentation Request")) + case PresentationRecord( + _, + _, + _, + _, + _, + _, + _, + _, + PresentationReceived, + CredentialFormat.AnonCreds, + _, + _, + None, + _, + _, + _, + _, + _, + _ + ) => + ZIO.fail(InvalidState("PresentationRecord in 'PresentationReceived' with no Presentation")) + case PresentationRecord( + id, + _, + _, + _, + _, + _, + _, + _, + PresentationReceived, + CredentialFormat.AnonCreds, + Some(requestPresentation), + _, + Some(presentation), + _, + _, + _, + _, + _, + _ + ) => // Verifier + ZIO.logDebug("PresentationRecord: PresentationReceived") *> ZIO.unit + val verifierPresentationReceivedToProcessed = + for { + walletAccessContext <- buildWalletAccessContextLayer(presentation.to) + presReceivedToProcessedAspect = CustomMetricsAspect.endRecordingTime( + s"${record.id}_present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge", + "present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge" + ) + result <- (for { + service <- ZIO.service[PresentationService] + _ <- (service + .verifyAnoncredPresentation(presentation, requestPresentation, id) + .provideSomeLayer(ZLayer.succeed(walletAccessContext)) @@ presReceivedToProcessedAspect) + .flatMapError(e => + for { + didCommAgent <- buildDIDCommAgent(presentation.from).provideSomeLayer( + ZLayer.succeed(walletAccessContext) + ) + reportproblem = ReportProblem.build( + fromDID = presentation.to, + toDID = presentation.from, + pthid = presentation.thid.getOrElse(presentation.id), + code = ProblemCode("e.p.presentation-verification-failed"), + comment = Some(e.toString) + ) + _ <- MessagingService + .send(reportproblem.toMessage) + .provideSomeLayer(didCommAgent) + _ <- ZIO.log(s"CredentialsValidationResult: ${e.toString}") + } yield () + ZIO.succeed(e) + ) + } yield ()).mapError(e => (walletAccessContext, handlePresentationErrors(e))) + } yield result + verifierPresentationReceivedToProcessed + @@ VerifierPresentationReceivedToProcessedSuccess.trackSuccess + @@ VerifierPresentationReceivedToProcessedFailed.trackError + @@ VerifierPresentationReceivedToProcessed + @@ Metric + .gauge( + "present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_flow_ms_gauge" + ) + .trackDurationWith(_.toMetricsSeconds) case PresentationRecord( id, _, diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala index 5a6056b3f7..12b4c74c09 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala @@ -49,8 +49,12 @@ object PresentProofController { ErrorResponse.badRequest(title = "Missing Anoncred Presentation Request", detail = Some(msg)) case PresentationError.AnoncredPresentationCreationError(cause) => ErrorResponse.badRequest(title = "Error Creating Anoncred Presentation", detail = Some(cause.toString)) + case PresentationError.AnoncredPresentationVerificationError(cause) => + ErrorResponse.badRequest(title = "Error Verifying Prensetation", detail = Some(cause.toString)) case PresentationError.InvalidAnoncredPresentationRequest(msg) => ErrorResponse.badRequest(title = "Invalid Anoncred Presentation Request", detail = Some(msg)) + case PresentationError.InvalidAnoncredPresentation(msg) => + ErrorResponse.badRequest(title = "Invalid Anoncred Presentation", detail = Some(msg)) case PresentationError.NotMatchingPresentationCredentialFormat(cause) => ErrorResponse.badRequest( title = "Presentation and Credential Format Not Matching", diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala index 4f50e50f30..dd460c4c25 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala @@ -75,9 +75,5 @@ object RequestPresentationInput { given Schema[AnoncredNonRevokedIntervalV1] = Schema.derived - given Schema[AnoncredAttributeRestrictionV1] = Schema.derived - - given Schema[AnoncredPredicateRestrictionV1] = Schema.derived - given schema: Schema[RequestPresentationInput] = Schema.derived } diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala index e8c9a52135..b5fd42f818 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala @@ -16,7 +16,7 @@ import io.iohk.atala.issue.controller.http.{ IssueCredentialRecord, IssueCredentialRecordPage } -import io.iohk.atala.pollux.anoncreds.LinkSecretWithId +import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecretWithId import io.iohk.atala.pollux.core.model.CredentialFormat import io.iohk.atala.pollux.core.repository.{CredentialDefinitionRepositoryInMemory, CredentialRepositoryInMemory} import io.iohk.atala.pollux.core.service.* @@ -81,7 +81,7 @@ trait IssueControllerTestTools extends PostgresTestContainerSupport { didResolverLayer >+> ResourceURIDereferencerImpl.layer >+> CredentialRepositoryInMemory.layer >+> - ZLayer.succeed(LinkSecretWithId("Unused Linked Secret ID")) >+> + ZLayer.succeed(AnoncredLinkSecretWithId("Unused Linked Secret ID")) >+> MockDIDService.empty >+> MockManagedDIDService.empty >+> CredentialServiceImpl.layer >+> diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala index 4b53450a27..ffabec1c77 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala @@ -1,7 +1,6 @@ package io.iohk.atala.pollux.credentialdefinition -import io.iohk.atala.agent.walletapi.model.BaseEntity -import io.iohk.atala.agent.walletapi.model.Entity +import io.iohk.atala.agent.walletapi.model.{BaseEntity, Entity} import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage import io.iohk.atala.api.http.ErrorResponse import io.iohk.atala.container.util.MigrationAspects.* @@ -112,25 +111,22 @@ object CredentialDefinitionBasicSpec extends ZIOSpecDefault with CredentialDefin maybeValidPublicDefinition <- PublicCredentialDefinitionSerDesV1.schemaSerDes.validate( fetchedCredentialDefinition.definition.toString() ) - assertValidPublicDefinition = assert(maybeValidPublicDefinition)(Assertion.isTrue) + assertValidPublicDefinition = assert(maybeValidPublicDefinition)(Assertion.isUnit) maybeValidKeyCorrectnessProof <- ProofKeyCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate( fetchedCredentialDefinition.keyCorrectnessProof.toString() ) - assertValidKeyCorrectnessProof = assert(maybeValidKeyCorrectnessProof)(Assertion.isTrue) + assertValidKeyCorrectnessProof = assert(maybeValidKeyCorrectnessProof)(Assertion.isUnit) storage <- ZIO.service[GenericSecretStorage] maybeDidSecret <- storage .get[UUID, CredentialDefinitionSecret](fetchedCredentialDefinition.guid) .provideSomeLayer(Entity.Default.wacLayer) maybeValidPrivateDefinitionZIO = maybeDidSecret match { case Some(didSecret) => - val validPrivateDefinition = - PrivateCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(didSecret.json.toString()) - validPrivateDefinition - case None => - ZIO.succeed(false) + PrivateCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(didSecret.json.toString()) + case None => ZIO.unit } maybeValidPrivateDefinition <- maybeValidPrivateDefinitionZIO - assertValidPrivateDefinition = assert(maybeValidPrivateDefinition)(Assertion.isTrue) + assertValidPrivateDefinition = assert(maybeValidPrivateDefinition)(Assertion.isUnit) } yield statusCodeIs201 && credentialDefinitionIsCreated && credentialDefinitionIsFetched &&