Skip to content

Commit

Permalink
feat: integrate SD JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
FabioPinheiro committed May 6, 2024
1 parent cf156d9 commit b8d2469
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 4 deletions.
20 changes: 16 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ lazy val D = new {
val mockito: ModuleID = "org.scalatestplus" %% "mockito-4-11" % V.mockito % Test
val monocle: ModuleID = "dev.optics" %% "monocle-core" % V.monocle % Test
val monocleMacro: ModuleID = "dev.optics" %% "monocle-macro" % V.monocle % Test
val scalaTest = "org.scalatest" %% "scalatest" % "3.2.16" % Test

val apollo = "io.iohk.atala.prism.apollo" % "apollo-jvm" % V.apollo
// We have to exclude bouncycastle since for some reason bitcoinj depends on bouncycastle jdk15to18
Expand Down Expand Up @@ -329,12 +330,10 @@ lazy val D_Pollux_VC_JWT = new {
val zioTestSbt = "dev.zio" %% "zio-test-sbt" % V.zio % Test
val zioTestMagnolia = "dev.zio" %% "zio-test-magnolia" % V.zio % Test

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.16" % Test

// Dependency Modules
val zioDependencies: Seq[ModuleID] = Seq(zio, zioPrelude, zioTest, zioTestSbt, zioTestMagnolia)
val baseDependencies: Seq[ModuleID] =
zioDependencies :+ D.jwtCirce :+ circeJsonSchema :+ networkntJsonSchemaValidator :+ D.nimbusJwt :+ scalaTest
zioDependencies :+ D.jwtCirce :+ circeJsonSchema :+ networkntJsonSchemaValidator :+ D.nimbusJwt :+ D.scalaTest

// Project Dependencies
lazy val polluxVcJwtDependencies: Seq[ModuleID] = baseDependencies
Expand Down Expand Up @@ -434,6 +433,7 @@ publish / skip := true

val commonSetttings = Seq(
testFrameworks ++= Seq(new TestFramework("zio.test.sbt.ZTestFramework")),
libraryDependencies ++= Seq(D.zioTest, D.zioTestSbt, D.zioTestMagnolia),
// Needed for Kotlin coroutines that support new memory management mode
resolvers += "JetBrains Space Maven Repository" at "https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven",
resolvers += "jitpack" at "https://jitpack.io",
Expand Down Expand Up @@ -776,9 +776,20 @@ lazy val polluxAnoncreds = project

lazy val polluxAnoncredsTest = project
.in(file("pollux/anoncredsTest"))
.settings(libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "3.2.15" % Test))
.settings(libraryDependencies += D.scalaTest)
.dependsOn(polluxAnoncreds % "compile->test")

lazy val polluxSDJWT = project
.in(file("pollux/sd-jwt"))
.settings(commonSetttings)
.settings(
name := "pollux-sd-jwt",
resolvers += Resolver.mavenLocal, // TODO REMOVE
libraryDependencies += "io.iohk" % "sd-jwt-kmp-jvm" % "1.0-SNAPSHOT",
// libraryDependencies ++= Seq(D.zio, D.zioTest)
)
.dependsOn(sharedCrypto)

// #####################
// ##### connect #####
// #####################
Expand Down Expand Up @@ -919,6 +930,7 @@ lazy val aggregatedProjects: Seq[ProjectReference] = Seq(
polluxDoobie,
polluxAnoncreds,
polluxAnoncredsTest,
polluxSDJWT,
connectCore,
connectDoobie,
agentWalletAPI,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.hyperledger.identus.pollux.sdjwt

import sd_jwt_rs.*

opaque type SDJWTPublicKey = String
object SDJWTPublicKey:
def fromPem(pemKey: String): SDJWTPublicKey = pemKey
extension (pemKey: SDJWTPublicKey)
def value: String = pemKey
def pem: String = pemKey

opaque type SDJWTPrivateKey = String
object SDJWTPrivateKey:
def fromPem(pemKey: String): SDJWTPrivateKey = pemKey
// def makeNew = {
// // val pr = org.hyperledger.identus.shared.crypto.Apollo.default.ed25519.generateKeyPair
// }
extension (keyPem: SDJWTPrivateKey)
def value: String = keyPem
def encodingKey = new sd_jwt_rs.EncodingKeyValue(keyPem.value)

opaque type SDJWTCredentialJson = String
object SDJWTCredentialJson:
def apply(value: String): SDJWTCredentialJson = value
extension (c: SDJWTCredentialJson)
def value: String = c
//

opaque type SDJWTPresentationJson = String
object SDJWTPresentationJson:
def apply(value: String): SDJWTPresentationJson = value
extension (c: SDJWTPresentationJson)
def value: String = c
//

object SDJWT {

def issueCredential(key: SDJWTPrivateKey, claims: String): SDJWTCredentialJson = {
val issuer = new SdjwtIssuerWrapper(key.encodingKey, "ES256") // null)
val sdjwt = issuer.issueSdJwtAllLevel(
claims, // user_claims
null, // holder_key
false, // add_decoy_claims
SdjwtSerializationFormat.JSON // COMPACT // serialization_format
)
SDJWTCredentialJson(sdjwt)
}
def createPresentation(sdjwt: SDJWTCredentialJson, claimsToDisclose: String): SDJWTPresentationJson = {
val holder = SdjwtHolderWrapper(sdjwt.value, SdjwtSerializationFormat.JSON)
val presentation = holder.createPresentation(
claimsToDisclose,
null, // nonce
null, // aud
null, // holder_key
null, // sign_alg
)
SDJWTPresentationJson(presentation)
}

def verifiePresentation(key: SDJWTPublicKey, presentation: SDJWTPresentationJson, claims: String) = {
val verifier = SdjwtVerifierWrapper(
presentation.value, // sd_jwt_presentation
key.pem, // public_key
null, // expected_aud
null, // expected_nonce
SdjwtSerializationFormat.JSON // serialization_format
)
val result: Boolean = verifier.verify(claims)
result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.hyperledger.identus.pollux.sdjwt

import sd_jwt_rs.*

import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.pollux.sdjwt.*

val ISSUER_KEY = SDJWTPrivateKey.fromPem(
"""-----BEGIN PRIVATE KEY-----
|MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgUr2bNKuBPOrAaxsR
|nbSH6hIhmNTxSGXshDSUD1a1y7ihRANCAARvbx3gzBkyPDz7TQIbjF+ef1IsxUwz
|X1KWpmlVv+421F7+c1sLqGk4HUuoVeN8iOoAcE547pJhUEJyf5Asc6pP
|-----END PRIVATE KEY-----
|""".stripMargin
)

val ISSUER_KEY_PUBLIC = SDJWTPublicKey.fromPem(
"""-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb28d4MwZMjw8+00CG4xfnn9SLMVM
M19SlqZpVb/uNtRe/nNbC6hpOB1LqFXjfIjqAHBOeO6SYVBCcn+QLHOqTw==
-----END PUBLIC KEY-----
|""".stripMargin
)

val CLAIMS =
"""{
| "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
| "iss": "https://example.com/issuer",
| "iat": 1683000000,
| "exp": 1883000000,
| "address": {
| "street_address": "Schulstr. 12",
| "locality": "Schulpforta",
| "region": "Sachsen-Anhalt",
| "country": "DE"
| }
|}""".stripMargin

val CLAIMS_PRESENTED =
"""{
| "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
| "iss": "https://example.com/issuer",
| "iat": 1683000000,
| "exp": 1883000000,
| "address": {
| "region": "Sachsen-Anhalt",
| "country": "DE"
| }
|}""".stripMargin

val FAlSE_CLAIMS_PRESENTED =
"""{
| "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
| "iss": "https://example.com/issuer",
| "iat": 1683000000,
| "exp": 1883000000,
| "address": {
| "region": "Sachsen-Anhalt",
| "country": "PT"
| }
|}""".stripMargin

object SDJWTSpec extends ZIOSpecDefault {

override def spec = suite("SDJWTRawSpec")(
test("issue credential") {
val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS)
assertTrue(!credential.value.isEmpty())
},
test("make presentation") {
val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS)
val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED)
assertTrue(!presentation.value.isEmpty())
},
test("verifie presentation") {
val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS)
val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED)
val ret = SDJWT.verifiePresentation(ISSUER_KEY_PUBLIC, presentation, CLAIMS_PRESENTED)
assertTrue(ret)
},
test("fail to verifie false claimes presentation") {
val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS)
val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED)
val ret = SDJWT.verifiePresentation(ISSUER_KEY_PUBLIC, presentation, FAlSE_CLAIMS_PRESENTED)
assertTrue(!ret)
},
)

}

0 comments on commit b8d2469

Please sign in to comment.