Skip to content

Commit

Permalink
[ATL-1932] feat(pollux): added Verifiable Presentation Demo (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
CryptoKnightIOG authored Oct 20, 2022
1 parent 431b657 commit 4bbe6bc
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,17 @@ object JWT {
extension (jwt: JWT) {
def value: String = jwt
}

object Implicits {
implicit val jwtEncoder: Encoder[JWT] =
(jwt: JWT) => jwt.value.asJson
}
}


case class JWTHeader(typ: String = "JWT", alg: Option[String])

case class JWTPayload(
iss: Option[String],
sub: Option[String],
aud: Vector[String],
iat: Option[Instant],
nbf: Option[Instant],
exp: Option[Instant],
iss: Option[String],
sub: Option[String],
aud: Vector[String],
iat: Option[Instant],
nbf: Option[Instant],
exp: Option[Instant],
rexp: Option[Instant]
)
trait JWTVerified(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ import io.circe.syntax.*

trait Verifiable(proof: Proof)

case class Proof(`type`: String, other: Json)
case class Proof(`type`: String, jwt: JWT)

object Proof {

object Implicits {
implicit val proofEncoder: Encoder[Proof] =
(proof: Proof) => proof.other.deepMerge(Map("type" -> proof.`type`).asJson)
(proof: Proof) =>
Json
.obj(
("type", proof.`type`.asJson),
("jwt", proof.jwt.value.asJson)
)
implicit val proofDecoder: Decoder[Proof] =
(c: HCursor) =>
for {
`type` <- c.downField("type").as[String]
other <- c.downField("type").delete.up.as[Json]
jwt <- c.downField("jwt").as[String]
} yield {
Proof(
`type` = `type`,
other = other
jwt = JWT(jwt)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@ case class Issuer(did: DID, signer: Signer, publicKey: PublicKey)
sealed trait VerifiableCredentialPayload

case class W3cVerifiableCredentialPayload(payload: W3cCredentialPayload, proof: Proof)
extends Verifiable(proof),
VerifiableCredentialPayload
extends Verifiable(proof),
VerifiableCredentialPayload

case class JwtVerifiableCredentialPayload(jwt: JWT) extends VerifiableCredentialPayload

case class CredentialStatus(
id: String,
`type`: String
)
id: String,
`type`: String
)

case class RefreshService(
id: String,
`type`: String
)
id: String,
`type`: String
)

case class CredentialSchema(
id: String,
Expand Down Expand Up @@ -142,42 +142,41 @@ case class JwtCredentialPayload(
)

case class W3cCredentialPayload(
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
maybeId: Option[String],
issuer: DID,
issuanceDate: Instant,
maybeExpirationDate: Option[Instant],
maybeCredentialSchema: Option[CredentialSchema],
credentialSubject: Json,
maybeCredentialStatus: Option[CredentialStatus],
maybeRefreshService: Option[RefreshService],
maybeEvidence: Option[Json],
maybeTermsOfUse: Option[Json],

/** Not part of W3C Credential but included to preserve in case of conversion from JWT. */
aud: IndexedSeq[String] = IndexedSeq.empty
) extends CredentialPayload(
maybeSub = credentialSubject.hcursor.downField("id").as[String].toOption,
`@context` = `@context`.distinct,
`type` = `type`.distinct,
maybeJti = maybeId,
maybeNbf = Some(issuanceDate),
aud = aud,
maybeExp = maybeExpirationDate,
maybeIss = Some(issuer.value),
maybeConnectionStatus = maybeCredentialStatus,
maybeRefreshService = maybeRefreshService,
maybeEvidence = maybeEvidence,
maybeTermsOfUse = maybeTermsOfUse,
maybeCredentialSchema = maybeCredentialSchema,
credentialSubject = credentialSubject
)
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
maybeId: Option[String],
issuer: DID,
issuanceDate: Instant,
maybeExpirationDate: Option[Instant],
maybeCredentialSchema: Option[CredentialSchema],
credentialSubject: Json,
maybeCredentialStatus: Option[CredentialStatus],
maybeRefreshService: Option[RefreshService],
maybeEvidence: Option[Json],
maybeTermsOfUse: Option[Json],

/** Not part of W3C Credential but included to preserve in case of conversion from JWT. */
aud: IndexedSeq[String] = IndexedSeq.empty
) extends CredentialPayload(
maybeSub = credentialSubject.hcursor.downField("id").as[String].toOption,
`@context` = `@context`.distinct,
`type` = `type`.distinct,
maybeJti = maybeId,
maybeNbf = Some(issuanceDate),
aud = aud,
maybeExp = maybeExpirationDate,
maybeIss = Some(issuer.value),
maybeConnectionStatus = maybeCredentialStatus,
maybeRefreshService = maybeRefreshService,
maybeEvidence = maybeEvidence,
maybeTermsOfUse = maybeTermsOfUse,
maybeCredentialSchema = maybeCredentialSchema,
credentialSubject = credentialSubject
)

object CredentialPayload {
object Implicits {

import JWT.Implicits.*

import Proof.Implicits.*

implicit val didEncoder: Encoder[DID] =
Expand Down Expand Up @@ -264,7 +263,7 @@ object CredentialPayload {
.deepMerge(Map("proof" -> w3cVerifiableCredentialPayload.proof).asJson)

implicit val jwtVerifiableCredentialPayloadEncoder: Encoder[JwtVerifiableCredentialPayload] =
(jwtVerifiableCredentialPayload: JwtVerifiableCredentialPayload) => jwtVerifiableCredentialPayload.jwt.asJson
(jwtVerifiableCredentialPayload: JwtVerifiableCredentialPayload) => jwtVerifiableCredentialPayload.jwt.value.asJson

implicit val verifiableCredentialPayloadEncoder: Encoder[VerifiableCredentialPayload] = {
case (w3cVerifiableCredentialPayload: W3cVerifiableCredentialPayload) => w3cVerifiableCredentialPayload.asJson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ import scala.util.{Failure, Success, Try}
sealed trait VerifiablePresentationPayload

case class W3cVerifiablePresentationPayload(payload: W3cPresentationPayload, proof: Proof)
extends Verifiable(proof),
VerifiablePresentationPayload
extends Verifiable(proof),
VerifiablePresentationPayload

case class JwtVerifiablePresentationPayload(jwt: JWT) extends VerifiablePresentationPayload

sealed trait PresentationPayload(
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload],
maybeIss: Option[String],
maybeNbf: Option[Instant],
aud: IndexedSeq[String],
maybeExp: Option[Instant],
maybeJti: Option[String],
maybeNonce: Option[String]
) {
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload],
maybeIss: Option[String],
maybeNbf: Option[Instant],
aud: IndexedSeq[String],
maybeExp: Option[Instant],
maybeJti: Option[String],
maybeNonce: Option[String]
) {
def toJwtPresentationPayload: JwtPresentationPayload =
JwtPresentationPayload(
maybeIss = maybeIss,
Expand Down Expand Up @@ -66,64 +66,62 @@ sealed trait PresentationPayload(
}

case class W3cPresentationPayload(
`@context`: IndexedSeq[String],
maybeId: Option[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload],
holder: String,
verifier: IndexedSeq[String],
issuanceDate: Instant,
maybeExpirationDate: Option[Instant],
`@context`: IndexedSeq[String],
maybeId: Option[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload],
holder: String,
verifier: IndexedSeq[String],
issuanceDate: Instant,
maybeExpirationDate: Option[Instant],

/** Not part of W3C Presentation but included to preserve in case of conversion from JWT. */
maybeNonce: Option[String] = Option.empty
) extends PresentationPayload(
`@context` = `@context`.distinct,
`type` = `type`.distinct,
maybeJti = maybeId,
verifiableCredential = verifiableCredential,
aud = verifier,
maybeIss = Some(holder),
maybeNbf = Some(issuanceDate),
maybeExp = maybeExpirationDate,
maybeNonce = maybeNonce
)
/** Not part of W3C Presentation but included to preserve in case of conversion from JWT. */
maybeNonce: Option[String] = Option.empty
) extends PresentationPayload(
`@context` = `@context`.distinct,
`type` = `type`.distinct,
maybeJti = maybeId,
verifiableCredential = verifiableCredential,
aud = verifier,
maybeIss = Some(holder),
maybeNbf = Some(issuanceDate),
maybeExp = maybeExpirationDate,
maybeNonce = maybeNonce
)

case class JwtVp(
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload]
)
`@context`: IndexedSeq[String],
`type`: IndexedSeq[String],
verifiableCredential: IndexedSeq[VerifiableCredentialPayload]
)

case class JwtPresentationPayload(
maybeIss: Option[String],
vp: JwtVp,
maybeNbf: Option[Instant],
aud: IndexedSeq[String],
maybeExp: Option[Instant],
maybeJti: Option[String],
maybeNonce: Option[String]
) extends PresentationPayload(
maybeIss = maybeIss,
`@context` = vp.`@context`,
`type` = vp.`type`,
verifiableCredential = vp.verifiableCredential,
maybeNbf = maybeNbf,
aud = aud,
maybeExp = maybeExp,
maybeJti = maybeJti,
maybeNonce = maybeNonce
)
maybeIss: Option[String],
vp: JwtVp,
maybeNbf: Option[Instant],
aud: IndexedSeq[String],
maybeExp: Option[Instant],
maybeJti: Option[String],
maybeNonce: Option[String]
) extends PresentationPayload(
maybeIss = maybeIss,
`@context` = vp.`@context`,
`type` = vp.`type`,
verifiableCredential = vp.verifiableCredential,
maybeNbf = maybeNbf,
aud = aud,
maybeExp = maybeExp,
maybeJti = maybeJti,
maybeNonce = maybeNonce
)

object PresentationPayload {

object Implicits {

import CredentialPayload.Implicits.*
import JWT.Implicits.*
import Proof.Implicits.*


implicit val w3cPresentationPayloadEncoder: Encoder[W3cPresentationPayload] =
(w3cPresentationPayload: W3cPresentationPayload) =>
Json
Expand Down Expand Up @@ -156,7 +154,7 @@ object PresentationPayload {
Json
.obj(
("iss", jwtPresentationPayload.maybeIss.asJson),
("vc", jwtPresentationPayload.vp.asJson),
("vp", jwtPresentationPayload.vp.asJson),
("nbf", jwtPresentationPayload.maybeNbf.asJson),
("aud", jwtPresentationPayload.aud.asJson),
("exp", jwtPresentationPayload.maybeExp.asJson),
Expand All @@ -166,7 +164,7 @@ object PresentationPayload {
.deepDropNullValues
.dropEmptyValues

implicit val w3cPresentationPayload: Decoder[W3cPresentationPayload] =
implicit val w3cPresentationPayloadDecoder: Decoder[W3cPresentationPayload] =
(c: HCursor) =>
for {
`@context` <- c
Expand Down Expand Up @@ -286,3 +284,19 @@ object PresentationPayload {
}
}

object JwtPresentation {

import PresentationPayload.Implicits.*

def encodeJwt(payload: JwtPresentationPayload, issuer: Issuer): JWT = issuer.signer.encode(payload.asJson)

def toEncodedJwt(payload: W3cPresentationPayload, issuer: Issuer): JWT =
encodeJwt(payload.toJwtPresentationPayload, issuer)

def decodeJwt(jwt: JWT, publicKey: PublicKey): Try[JwtPresentationPayload] = {
JwtCirce.decodeRaw(jwt.value, publicKey).flatMap(decode[JwtPresentationPayload](_).toTry)
}

def validateEncodedJwt(jwt: JWT, publicKey: PublicKey): Boolean =
JwtCirce.isValid(jwt.value, publicKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import java.time.Instant
val privateKey = keyPair.getPrivate
val publicKey = keyPair.getPublic

val Right(claimJson) : Right[io.circe.ParsingFailure, io.circe.Json] @unchecked = jawn.parse(s"""{"expires":${Instant.now.getEpochSecond}}""")
val Right(claimJson): Right[io.circe.ParsingFailure, io.circe.Json] @unchecked =
jawn.parse(s"""{"expires":${Instant.now.getEpochSecond}}""")

val jwt = JwtCirce.encode(claimJson, privateKey, JwtAlgorithm.ES256)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import java.security.*
import java.security.spec.*
import java.time.{Instant, ZonedDateTime}

@main def JwtCredentialEncondingDemo(): Unit =
@main def JwtCredentialEncodingDemo(): Unit =

println("")
println("==================")
Expand Down
Loading

0 comments on commit 4bbe6bc

Please sign in to comment.