From 87f72bd285bebe1828ec0ca3a7799de7abee9294 Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Fri, 17 May 2024 11:50:49 +0100 Subject: [PATCH] SD JWT moduel is done Signed-off-by: FabioPinheiro --- .../identus/pollux/sdjwt/SDJWT.scala | 72 +++++++++----- .../identus/pollux/sdjwt/SDJWTSpec.scala | 93 +++++++++---------- 2 files changed, 95 insertions(+), 70 deletions(-) diff --git a/pollux/sd-jwt/src/main/scala/org/hyperledger/identus/pollux/sdjwt/SDJWT.scala b/pollux/sd-jwt/src/main/scala/org/hyperledger/identus/pollux/sdjwt/SDJWT.scala index 0fcd49e7db..defb59aed6 100644 --- a/pollux/sd-jwt/src/main/scala/org/hyperledger/identus/pollux/sdjwt/SDJWT.scala +++ b/pollux/sd-jwt/src/main/scala/org/hyperledger/identus/pollux/sdjwt/SDJWT.scala @@ -6,6 +6,10 @@ import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters import org.bouncycastle.crypto.util.PrivateKeyInfoFactory import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory +import scala.util.Try +import scala.util.Failure +import scala.util.Success +import org.bouncycastle.cert.path.CertPathValidationResult // TODO move to apollo private[sdjwt] object Utils { @@ -160,7 +164,7 @@ object SDJWT { def createPresentation( sdjwt: CredentialJson, claimsToDisclose: String, - signAlg: String // FIXME + // signAlg: String // FIXME ): PresentationJson = { val holder = SdjwtHolderWrapper(sdjwt.value, SdjwtSerializationFormat.JSON) val presentation = holder.createPresentation( @@ -168,7 +172,7 @@ object SDJWT { null, // nonce null, // aud null, // holder_key - signAlg, // sign_alg + null, // signAlg, // sign_alg ) PresentationJson(presentation) } @@ -201,16 +205,34 @@ object SDJWT { PresentationJson(presentation) } - def verifyPresentation(key: IssuerPublicKey, presentation: PresentationJson, 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 + sealed trait ValidationResult + case object Valid extends ValidationResult + sealed trait Invalid extends ValidationResult + case object InvalidSignature extends Invalid { def error = "Fail due to invalid input: InvalidSignature" } + case object InvalidClaims extends Invalid { def error = "Fail to Veridy the claims" } + case class InvalidError(error: String) extends Invalid + + def verifyPresentation( + key: IssuerPublicKey, + presentation: PresentationJson, + claims: String + ): ValidationResult = { + Try { + val verifier = SdjwtVerifierWrapper( + presentation.value, // sd_jwt_presentation + key.pem, // public_key + null, // expected_aud + null, // expected_nonce + SdjwtSerializationFormat.JSON // serialization_format + ) + verifier.verify(claims) + } match { + case Failure(ex: SdjwtException.Unspecified) if ex.getMessage() == "invalid input: InvalidSignature" => + InvalidSignature + case Failure(ex) => InvalidError(ex.getMessage()) + case Success(true) => Valid + case Success(false) => InvalidClaims + } } /** Verify Presentation with challenge @@ -231,15 +253,21 @@ object SDJWT { expectedNonce: String, expectedAud: String, // holderKey: HolderPrivateKey - ) = { - val verifier = SdjwtVerifierWrapper( - presentation.value, // sd_jwt_presentation - key.pem, // public_key - expectedAud, // expected_aud - expectedNonce, // expected_nonce - SdjwtSerializationFormat.JSON // serialization_format - ) - val result: Boolean = verifier.verify(claims) - result + ): ValidationResult = { + Try { + val verifier = SdjwtVerifierWrapper( + presentation.value, // sd_jwt_presentation + key.pem, // public_key + expectedAud, // expected_aud + expectedNonce, // expected_nonce + SdjwtSerializationFormat.JSON // serialization_format + ) + verifier.verify(claims) + } match { + case Failure(ex: SdjwtException.Unspecified) if ex.getMessage() == "invalid input: InvalidSignature" => + InvalidSignature + case Failure(ex) => InvalidError(ex.getMessage()) + case Success(presentation) => Valid + } } } diff --git a/pollux/sd-jwt/src/test/scala/org/hyperledger/identus/pollux/sdjwt/SDJWTSpec.scala b/pollux/sd-jwt/src/test/scala/org/hyperledger/identus/pollux/sdjwt/SDJWTSpec.scala index fbba4614b5..b96af06937 100644 --- a/pollux/sd-jwt/src/test/scala/org/hyperledger/identus/pollux/sdjwt/SDJWTSpec.scala +++ b/pollux/sd-jwt/src/test/scala/org/hyperledger/identus/pollux/sdjwt/SDJWTSpec.scala @@ -2,6 +2,7 @@ package org.hyperledger.identus.pollux.sdjwt import zio.* import zio.test.* +import zio.test.Assertion.* import org.hyperledger.identus.pollux.sdjwt.* import org.hyperledger.identus.shared.crypto.* @@ -9,7 +10,6 @@ import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters import java.security.SecureRandom -import java.util.Base64 def ISSUER_KEY = IssuerPrivateKey.fromEcPem( """-----BEGIN PRIVATE KEY----- @@ -108,23 +108,20 @@ object SDJWTSpec extends ZIOSpecDefault { }, test("make presentation") { val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS) - val fixme = ISSUER_KEY.signAlg - val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED, fixme) + val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED) assertTrue(!presentation.value.isEmpty()) }, test("verify presentation") { val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS) - val fixme = ISSUER_KEY.signAlg - val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED, fixme) + val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED) val ret = SDJWT.verifyPresentation(ISSUER_KEY_PUBLIC, presentation, CLAIMS_PRESENTED) - assertTrue(ret) + assertTrue(ret == SDJWT.Valid) }, test("fail to verify false claimes presentation") { val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS) - val fixme = ISSUER_KEY.signAlg - val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED, fixme) + val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED) val ret = SDJWT.verifyPresentation(ISSUER_KEY_PUBLIC, presentation, FAlSE_CLAIMS_PRESENTED) - assertTrue(!ret) + assertTrue(ret == SDJWT.InvalidClaims) }, // presentation challenge test("make presentation with holder presentation challenge") { @@ -136,7 +133,8 @@ object SDJWTSpec extends ZIOSpecDefault { "did:example:verifier", HOLDER_KEY ) - assertTrue(!presentation.value.isEmpty()) + assert(presentation.value)(isNonEmptyString) + // Assertion { TestArrow.make[PresentationJson, String] { a => TestTrace.succeed(a.value) } >>> isEmptyString.arrow } }, test("verify presentation with holder presentation challenge") { val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS, HOLDER_KEY_JWK_PUBLIC) @@ -154,7 +152,7 @@ object SDJWTSpec extends ZIOSpecDefault { expectedNonce = "nonce123456789", expectedAud = "did:example:verifier", ) - assertTrue(ret) + assertTrue(ret == SDJWT.Valid) }, test("fail to verify presentation with holder presentation challenge") { val credential = SDJWT.issueCredential(ISSUER_KEY, CLAIMS) @@ -165,8 +163,12 @@ object SDJWTSpec extends ZIOSpecDefault { aud = "did:example:verifier", holderKey = HOLDER_KEY ) - val ret = SDJWT.verifyPresentation(ISSUER_KEY_PUBLIC, presentation, FAlSE_CLAIMS_PRESENTED) - assertTrue(!ret) + val ret = SDJWT.verifyPresentation( + ISSUER_KEY_PUBLIC, + presentation, + FAlSE_CLAIMS_PRESENTED, + ) + assertTrue(ret == SDJWT.InvalidClaims) }, test("IssuerPrivateKey from a key type ED25519") { val ed25519KeyPair = KmpEd25519KeyOps.generateKeyPair @@ -192,13 +194,9 @@ object SDJWTSpec extends ZIOSpecDefault { val issuerPublicKey = IssuerPublicKey(ed25519KeyPair.publicKey) val credential = SDJWT.issueCredential(issuerKey, CLAIMS) - val presentation = SDJWT.createPresentation( - credential, - CLAIMS_PRESENTED, - issuerKey.signAlg // TODO signAlg should be metadata - ) + val presentation = SDJWT.createPresentation(credential, CLAIMS_PRESENTED) val ret = SDJWT.verifyPresentation(issuerPublicKey, presentation, CLAIMS_PRESENTED) - assertTrue(ret) + assertTrue(ret == SDJWT.Valid) }, test("Flow with key type ED25519 with presentation challenge") { val ed25519KeyPair = KmpEd25519KeyOps.generateKeyPair @@ -218,7 +216,6 @@ object SDJWTSpec extends ZIOSpecDefault { aud = "did:example:verifier", holderKey = holderKey ) - println(presentation) val ret = SDJWT.verifyPresentation( key = issuerPublicKey, presentation = presentation, @@ -226,38 +223,38 @@ object SDJWTSpec extends ZIOSpecDefault { expectedNonce = "nonce123456789", expectedAud = "did:example:verifier" ) - - assertTrue(ret) + assertTrue(ret == SDJWT.Valid) }, - // test("Flow with key type ED25519 with presentation challenge fail") { - // val ed25519KeyPair = KmpEd25519KeyOps.generateKeyPair - // val privateKey = ed25519KeyPair.privateKey - // val issuerKey = IssuerPrivateKey(privateKey) - // val issuerPublicKey = IssuerPublicKey(ed25519KeyPair.publicKey) + test("Flow with key type ED25519 with presentation challenge fail") { + val ed25519KeyPair = KmpEd25519KeyOps.generateKeyPair + val privateKey = ed25519KeyPair.privateKey + val issuerKey = IssuerPrivateKey(privateKey) + val issuerPublicKey = IssuerPublicKey(ed25519KeyPair.publicKey) - // val holderEd25519KeyPair = KmpEd25519KeyOps.generateKeyPair - // val holderPrivateKey = holderEd25519KeyPair.privateKey - // val holderKey = HolderPrivateKey(holderPrivateKey) + val holderEd25519KeyPair = KmpEd25519KeyOps.generateKeyPair + // val holderKey = HolderPrivateKey(holderEd25519KeyPair.privateKey) + val holderKeyPublic = HolderPublicKey(holderEd25519KeyPair.publicKey) + val credential = SDJWT.issueCredential(issuerKey, CLAIMS, holderKeyPublic) - // val credential = SDJWT.issueCredential(issuerKey, CLAIMS) + val failHolderEd25519KeyPair = KmpEd25519KeyOps.generateKeyPair + val failHolderKey = HolderPrivateKey(failHolderEd25519KeyPair.privateKey) - // val failHolderKey = HolderPrivateKey(holderPrivateKey) - // val presentation = SDJWT.createPresentation( - // sdjwt = credential, - // claimsToDisclose = CLAIMS_PRESENTED, - // nonce = "nonce123456789", - // aud = "did:example:verifier", - // holderKey = holderKey - // ) - // val ret = SDJWT.verifyPresentation( - // key = issuerPublicKey, - // presentation = presentation, - // claims = CLAIMS_PRESENTED, - // expectedNonce = "nonce123456789", - // expectedAud = failHolderKey - // ) - // assertTrue(ret) - // }, + val presentation = SDJWT.createPresentation( + sdjwt = credential, + claimsToDisclose = CLAIMS_PRESENTED, + nonce = "nonce123456789", + aud = "did:example:verifier", + holderKey = failHolderKey + ) + val ret = SDJWT.verifyPresentation( + key = issuerPublicKey, + presentation = presentation, + claims = CLAIMS_PRESENTED, + expectedNonce = "nonce123456789", + expectedAud = "did:example:verifier" + ) + assertTrue(ret == SDJWT.InvalidSignature) + }, ) }