Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use the compact format in SD-JWT #1169

Merged
merged 5 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import org.hyperledger.identus.pollux.core.model.error.{CredentialServiceError,
import org.hyperledger.identus.pollux.core.model.error.PresentationError.*
import org.hyperledger.identus.pollux.core.service.{CredentialService, PresentationService}
import org.hyperledger.identus.pollux.core.service.serdes.AnoncredCredentialProofsV1
import org.hyperledger.identus.pollux.sdjwt.{IssuerPublicKey, PresentationJson, SDJWT}
import org.hyperledger.identus.pollux.sdjwt.{IssuerPublicKey, PresentationCompact, SDJWT}
import org.hyperledger.identus.pollux.vc.jwt.{DidResolver as JwtDidResolver, JWT, JwtPresentation}
import org.hyperledger.identus.resolvers.DIDResolver
import org.hyperledger.identus.shared.http.*
Expand Down Expand Up @@ -682,7 +682,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
didResolverService <- ZIO.service[JwtDidResolver]
credentialsClaimsValidationResult <- 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.getUrlDecoder.decode(data))
val maybePresentationOptions: Either[PresentationError, Option[
org.hyperledger.identus.pollux.core.model.presentation.Options
]] =
Expand Down Expand Up @@ -832,26 +832,17 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
didResolverService <- ZIO.service[JwtDidResolver]
credentialsClaimsValidationResult <- 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.getUrlDecoder.decode(data))
val verifiedClaims = for {
presentation <- ZIO.succeed(PresentationJson(base64Decoded))
presentation <- ZIO.succeed(PresentationCompact.unsafeFromCompact(base64Decoded))
iss <- ZIO.fromEither(presentation.iss)
ed25519PublicKey <- resolveToEd25519PublicKey(iss)
verifiedClaims = SDJWT.getVerifiedClaims(
ret = SDJWT.getVerifiedClaims(
IssuerPublicKey(ed25519PublicKey),
presentation
)
_ <- ZIO.logInfo(s"ClaimsValidationResult: $verifiedClaims")
result: SDJWT.ClaimsValidationResult =
verifiedClaims match {
case validClaims: SDJWT.ValidClaims =>
validClaims.verifyDiscoseClaims(
Json.Obj()
)
case validAnyMatch: SDJWT.ValidAnyMatch.type => validAnyMatch
case invalid: SDJWT.Invalid => invalid
}
} yield result
_ <- ZIO.logInfo(s"ClaimsValidationResult: $ret")
} yield ret
verifiedClaims
.mapError(error =>
UnexpectedError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ object PresentationStatus {
case Some(p) =>
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.getUrlDecoder.decode(data))
Seq(base64Decoded)
case any => ???
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ private class CredentialServiceImpl(
.find(_.format.contains(IssueCredentialOfferFormat.Anoncred.name))
.map(_.data)
.flatMap {
case Base64(value) => Some(new String(java.util.Base64.getDecoder.decode(value)))
case Base64(value) => Some(new String(java.util.Base64.getUrlDecoder.decode(value)))
case _ => None
}
)
Expand Down Expand Up @@ -1141,7 +1141,7 @@ private class CredentialServiceImpl(
fromDID = issue.from,
toDID = issue.to,
thid = issue.thid,
credentials = Seq(IssueCredentialIssuedFormat.SDJWT -> credential.value.getBytes)
credentials = Seq(IssueCredentialIssuedFormat.SDJWT -> credential.compact.getBytes)
)
// End custom

Expand Down Expand Up @@ -1293,7 +1293,7 @@ private class CredentialServiceImpl(
jwt <- attachmentDescriptor.data match
case Base64(b64) =>
ZIO.succeed {
val base64Decoded = new String(java.util.Base64.getDecoder.decode(b64))
val base64Decoded = new String(java.util.Base64.getUrlDecoder.decode(b64))
JWT(base64Decoded)
}
case _ => ZIO.dieMessage(s"Attachment does not contain Base64Data: ${record.id}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import org.hyperledger.identus.mercury.protocol.presentproof.{
import org.hyperledger.identus.pollux.anoncreds.AnoncredPresentation
import org.hyperledger.identus.pollux.core.model.{DidCommID, PresentationRecord}
import org.hyperledger.identus.pollux.core.model.error.PresentationError
import org.hyperledger.identus.pollux.core.model.presentation.{Options, SdJwtPresentationPayload}
import org.hyperledger.identus.pollux.core.model.presentation.Options
import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import org.hyperledger.identus.pollux.sdjwt.PresentationJson
import org.hyperledger.identus.pollux.sdjwt.PresentationCompact
import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
import org.hyperledger.identus.shared.models.WalletAccessContext
import zio.{mock, IO, URLayer, ZIO, ZLayer}
Expand Down Expand Up @@ -208,10 +208,10 @@ object MockPresentationService extends Mock[PresentationService] {
issuanceDate: Instant
): IO[PresentationError, PresentationPayload] = ???

override def createSDJwtPresentationPayloadFromRecord(
override def createPresentationFromRecord(
record: DidCommID,
issuer: Issuer,
): IO[PresentationError, PresentationJson] = ???
): IO[PresentationError, PresentationCompact] = ???

def createSDJwtPresentation(
recordId: DidCommID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.hyperledger.identus.pollux.core.model.*
import org.hyperledger.identus.pollux.core.model.error.PresentationError
import org.hyperledger.identus.pollux.core.model.presentation.*
import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import org.hyperledger.identus.pollux.sdjwt.PresentationJson
import org.hyperledger.identus.pollux.sdjwt.PresentationCompact
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.shared.models.WalletAccessContext
import zio.*
Expand Down Expand Up @@ -57,10 +57,10 @@ trait PresentationService {
issuanceDate: Instant
): ZIO[WalletAccessContext, PresentationError, PresentationPayload]

def createSDJwtPresentationPayloadFromRecord(
def createPresentationFromRecord(
record: DidCommID,
issuer: Issuer,
): ZIO[WalletAccessContext, PresentationError, PresentationJson]
): ZIO[WalletAccessContext, PresentationError, PresentationCompact]

def createSDJwtPresentation(
recordId: DidCommID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import org.hyperledger.identus.pollux.anoncreds.*
import org.hyperledger.identus.pollux.core.model.*
import org.hyperledger.identus.pollux.core.model.error.PresentationError
import org.hyperledger.identus.pollux.core.model.error.PresentationError.*
import org.hyperledger.identus.pollux.core.model.presentation.{SdJwtPresentationPayload, *}
import org.hyperledger.identus.pollux.core.model.presentation.*
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
import org.hyperledger.identus.pollux.core.repository.{CredentialRepository, PresentationRepository}
import org.hyperledger.identus.pollux.core.service.serdes.*
import org.hyperledger.identus.pollux.sdjwt.{CredentialJson, PresentationJson, SDJWT}
import org.hyperledger.identus.pollux.sdjwt.{CredentialCompact, PresentationCompact, SDJWT}
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.shared.models.WalletAccessContext
import org.hyperledger.identus.shared.utils.aspects.CustomMetricsAspect
Expand Down Expand Up @@ -108,10 +108,10 @@ private class PresentationServiceImpl(
} yield presentationPayload
}

override def createSDJwtPresentationPayloadFromRecord(
override def createPresentationFromRecord(
recordId: DidCommID,
prover: Issuer
): ZIO[WalletAccessContext, PresentationError, PresentationJson] = {
): ZIO[WalletAccessContext, PresentationError, PresentationCompact] = {

for {
maybeRecord <- presentationRepository
Expand Down Expand Up @@ -142,22 +142,16 @@ private class PresentationServiceImpl(
)
)
)
// return presentationJson
presentationJson <- createSDJwtPresentationPayloadFromCredential(

presentationCompact <- createPresentationFromRecord(
issuedCredentials,
sdJwtClaimsToDisclose,
requestPresentation,
prover
)
presentationPayload <- ZIO.succeed(
SdJwtPresentationPayload(
claimsToDisclose = sdJwtClaimsToDisclose,
presentation = presentationJson,
options = None
)
)
presentationPayload <- ZIO.succeed(presentationCompact)

} yield presentationJson
} yield presentationCompact
}

override def createSDJwtPresentation(
Expand All @@ -166,7 +160,7 @@ private class PresentationServiceImpl(
prover: Issuer,
): ZIO[WalletAccessContext, PresentationError, Presentation] = {
for {
presentationPayload <- createSDJwtPresentationPayloadFromRecord(recordId, prover)
presentationPayload <- createPresentationFromRecord(recordId, prover)
presentation <- ZIO.succeed(
Presentation(
body = Presentation.Body(
Expand All @@ -176,7 +170,7 @@ private class PresentationServiceImpl(
attachments = Seq(
AttachmentDescriptor
.buildBase64Attachment(
payload = presentationPayload.value.getBytes,
payload = presentationPayload.compact.getBytes,
mediaType = Some(PresentCredentialFormat.SDJWT.name)
)
),
Expand Down Expand Up @@ -506,26 +500,34 @@ private class PresentationServiceImpl(
} yield record
}

private def createSDJwtPresentationPayloadFromCredential(
private def createPresentationFromRecord(
issuedCredentials: Seq[String],
claimsToDisclose: SdJwtCredentialToDisclose,
requestPresentation: RequestPresentation,
prover: Issuer
): IO[PresentationError, PresentationJson] = {
): IO[PresentationError, PresentationCompact] = {

val verifiableCredentials: Either[
PresentationError.PresentationDecodingError,
Seq[CredentialJson]
Seq[CredentialCompact]
] = issuedCredentials.map { signedCredential =>
println("******signedCredential***********")
println(signedCredential)
println("*******signedCredential**********")
decode[org.hyperledger.identus.mercury.model.Base64](signedCredential)
.flatMap(x => Right(CredentialJson(new String(java.util.Base64.getDecoder.decode(x.base64)))))
.flatMap(x =>
Right(CredentialCompact.unsafeFromCompact(new String(java.util.Base64.getUrlDecoder.decode(x.base64))))
)
.left
.map(err => PresentationDecodingError(s"JsonData decoding error: $err"))
}.sequence

import io.circe.parser.decode
import io.circe.syntax._
import java.util.Base64
println("*****************")
println(requestPresentation.attachments.head)
println("*****************")

val result: Either[PresentationDecodingError, SDJwtPresentation] =
requestPresentation.attachments.headOption
Expand Down Expand Up @@ -575,7 +577,7 @@ private class PresentationServiceImpl(
] =
issuedCredentials.map { signedCredential =>
decode[org.hyperledger.identus.mercury.model.Base64](signedCredential)
.flatMap(x => Right(new String(java.util.Base64.getDecoder.decode(x.base64))))
.flatMap(x => Right(new String(java.util.Base64.getUrlDecoder.decode(x.base64))))
.flatMap(x => Right(JwtVerifiableCredentialPayload(JWT(x))))
.left
.map(err => PresentationDecodingError(s"JsonData decoding error: $err"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import org.hyperledger.identus.mercury.protocol.presentproof.{
import org.hyperledger.identus.pollux.anoncreds.AnoncredPresentation
import org.hyperledger.identus.pollux.core.model.{DidCommID, PresentationRecord}
import org.hyperledger.identus.pollux.core.model.error.PresentationError
import org.hyperledger.identus.pollux.core.model.presentation.{Options, SdJwtPresentationPayload}
import org.hyperledger.identus.pollux.core.model.presentation.Options
import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import org.hyperledger.identus.pollux.sdjwt.PresentationJson
import org.hyperledger.identus.pollux.sdjwt.PresentationCompact
import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
import org.hyperledger.identus.shared.models.WalletAccessContext
import zio.{IO, URLayer, ZIO, ZLayer}
Expand Down Expand Up @@ -202,11 +202,11 @@ class PresentationServiceNotifier(
): ZIO[WalletAccessContext, PresentationError, PresentationPayload] =
svc.createJwtPresentationPayloadFromRecord(record, issuer, issuanceDate)

override def createSDJwtPresentationPayloadFromRecord(
override def createPresentationFromRecord(
record: DidCommID,
issuer: Issuer
): ZIO[WalletAccessContext, PresentationError, PresentationJson] =
svc.createSDJwtPresentationPayloadFromRecord(record, issuer)
): ZIO[WalletAccessContext, PresentationError, PresentationCompact] =
svc.createPresentationFromRecord(record, issuer)

override def createSDJwtPresentation(
record: DidCommID,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.hyperledger.identus.pollux.sdjwt

import zio.json.*

type Header = String
type Payload = String
type Signature = String
type Disclosure = String
type KBJWT = (String, String, String)

case class CredentialCompact(
override val jwtHeader: Header,
override val jwtPayload: Payload,
override val jwtSignature: Signature,
override val disclosures: Seq[Disclosure],
) extends ModelsExtensionMethods {
override def kbJWT: Option[KBJWT] = None // MUST not have the KB-JWT part
}

object CredentialCompact {
given decoder: JsonDecoder[CredentialCompact] = JsonDecoder.string.map(CredentialCompact.unsafeFromCompact(_))
given encoder: JsonEncoder[CredentialCompact] = JsonEncoder.string.contramap[CredentialCompact](_.compact)

import scala.util.matching.Regex
val patternCompact =
new Regex("(^[\\w-]*)\\.([\\w-]*)\\.([\\w-]*)((?:~(?:[\\w-]*))*)~$") // MUST not have the KB-JWT part
val patternDisclosure = new Regex("(~([\\w]+))")

/** <Issuer-signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Disclosure N>~<optional KB-JWT>
*/
def unsafeFromCompact(str: String): CredentialCompact = {
val patternCompact(h, p, s, disclosuresStr) = str: @unchecked // TODO make error type
CredentialCompact(
jwtHeader = h,
jwtPayload = p,
jwtSignature = s,
disclosures = patternDisclosure.findAllIn(disclosuresStr).toSeq.map(_.drop(1)),
)
}
}

case class PresentationCompact(
override val jwtHeader: Header,
override val jwtPayload: Payload,
override val jwtSignature: Signature,
override val disclosures: Seq[Disclosure],
override val kbJWT: Option[KBJWT]
) extends ModelsExtensionMethods

object PresentationCompact {
given decoder: JsonDecoder[PresentationCompact] = JsonDecoder.string.map(PresentationCompact.unsafeFromCompact(_))
given encoder: JsonEncoder[PresentationCompact] = JsonEncoder.string.contramap[PresentationCompact](_.compact)

import scala.util.matching.Regex
val patternCompact =
new Regex("^([\\w-]*)\\.([\\w-]*)\\.([\\w-]*)((?:~(?:[\\w-]*))*)~(?:([\\w-]*)\\.([\\w-]*)\\.([\\w-]*))?$")
val patternDisclosure = new Regex("(~([\\w]+))")

/** <Issuer-signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Disclosure N>~<optional KB-JWT>
*/
def unsafeFromCompact(str: String): PresentationCompact = {
val patternCompact(h, p, s, disclosuresStr, kb_h, kb_p, kb_s) = str: @unchecked // TODO make error type
val kbJWT: Option[KBJWT] = (
Option(kb_h).filterNot(_.isBlank()),
Option(kb_p).filterNot(_.isBlank()),
Option(kb_s).filterNot(_.isBlank())
) match
case (None, None, None) => None
case (Some(h), Some(p), Some(s)) => Some((h, p, s))
case _ => None // TODO make error type

PresentationCompact(
jwtHeader = h,
jwtPayload = p,
jwtSignature = s,
disclosures = patternDisclosure.findAllIn(disclosuresStr).toSeq.map(_.drop(1)),
kbJWT = kbJWT,
)
}
}
Loading
Loading