From eed357275162a4c12994ac4de34de96cac8a5c61 Mon Sep 17 00:00:00 2001 From: mikeplotean <101570226+mikeplotean@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:15:00 +0300 Subject: [PATCH] refactor: update sd-jwt lib (#318) Co-authored-by: severinstampler --- build.gradle.kts | 2 +- src/main/kotlin/id/walt/common/OidcUtil.kt | 4 +- .../id/walt/common/SerializationUtils.kt | 49 +++++++++++--- .../credentials/w3c/VerifiableCredential.kt | 6 +- .../credentials/w3c/VerifiablePresentation.kt | 8 +-- src/main/kotlin/id/walt/model/oidc/IDToken.kt | 2 +- .../id/walt/model/oidc/SelfIssuedIDToken.kt | 2 +- .../EnterpriseWalletService.kt | 2 +- .../essif/userwallet/UserWalletService.kt | 2 +- .../kotlin/id/walt/services/jwt/JwtService.kt | 3 +- .../id/walt/services/jwt/WaltIdJwtService.kt | 5 +- .../services/vc/WaltIdJwtCredentialService.kt | 2 +- .../kotlin/id/walt/signatory/Signatory.kt | 3 +- .../signatory/rest/SignatoryController.kt | 6 +- .../id/walt/services/jwt/JwtServiceTest.kt | 6 +- .../id/walt/services/oidc/OIDC4VCTest.kt | 2 +- .../vc/WaltIdJwtCredentialServiceTest.kt | 2 +- .../id/walt/signatory/SignatoryApiTest.kt | 65 +++++++++++++++++++ .../id/walt/signatory/SignatoryServiceTest.kt | 4 +- 19 files changed, 136 insertions(+), 39 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7e37d10e3..09d66669c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,7 +38,7 @@ dependencies { implementation("com.microsoft.azure:azure-client-authentication:1.7.14") implementation("com.nimbusds:nimbus-jose-jwt:9.30.2") implementation("com.nimbusds:oauth2-oidc-sdk:10.7") - implementation("id.walt:waltid-sd-jwt-jvm:1.2306071206.0") + implementation("id.walt:waltid-sd-jwt-jvm:1.2306160840.0") implementation("org.bouncycastle:bcprov-jdk15to18:1.72") implementation("org.bouncycastle:bcpkix-jdk15to18:1.72") diff --git a/src/main/kotlin/id/walt/common/OidcUtil.kt b/src/main/kotlin/id/walt/common/OidcUtil.kt index f0e391295..34076d4d2 100644 --- a/src/main/kotlin/id/walt/common/OidcUtil.kt +++ b/src/main/kotlin/id/walt/common/OidcUtil.kt @@ -103,7 +103,7 @@ object OidcUtil { val scope = pm["scope"]!! val authRequestJwt = pm["request"]!! - if (jwtService.verify(authRequestJwt)) { + if (jwtService.verify(authRequestJwt).verified) { log.debug { "Successfully verified signature of JWT" } } else { throw IllegalArgumentException("Could not verify JWT $authRequestJwt") @@ -163,7 +163,7 @@ object OidcUtil { val jwt = jwtService.sign(did, payload) - jwtService.verify(jwt).let { if (!it) throw IllegalStateException("Generated JWK not valid") } + jwtService.verify(jwt).let { if (!it.verified) throw IllegalStateException("Generated JWK not valid") } return jwt } diff --git a/src/main/kotlin/id/walt/common/SerializationUtils.kt b/src/main/kotlin/id/walt/common/SerializationUtils.kt index b7f7809f0..c38d26a69 100644 --- a/src/main/kotlin/id/walt/common/SerializationUtils.kt +++ b/src/main/kotlin/id/walt/common/SerializationUtils.kt @@ -4,6 +4,9 @@ import com.beust.klaxon.* import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.toVerifiableCredential import id.walt.model.VerificationMethod +import id.walt.sdjwt.SDMap +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject @Target(AnnotationTarget.FIELD) annotation class VCList @@ -26,12 +29,19 @@ annotation class SingleVCObject @Target(AnnotationTarget.FIELD) annotation class JsonObjectField + +@Target(AnnotationTarget.FIELD) +annotation class KotlinxJsonObjectField + @Target(AnnotationTarget.FIELD) annotation class ListOrSingleValue @Target(AnnotationTarget.FIELD) annotation class DidVerificationRelationships +@Target(AnnotationTarget.FIELD) +annotation class SDMapProperty + class VcConverter(private val singleVC: Boolean, private val singleIfOne: Boolean, private val toVcObject: Boolean) : Converter { override fun canConvert(cls: Class<*>) = singleVC && cls == VerifiableCredential::class.java || cls == List::class.java @@ -87,6 +97,19 @@ val jsonObjectFieldConverter = object : Converter { } } +val kotlinxJsonObjectFieldConverter = object : Converter { + override fun canConvert(cls: Class<*>) = cls == kotlinx.serialization.json.JsonObject::class.java + + override fun fromJson(jv: JsonValue): Any? { + return jv.obj?.toJsonString()?.let { Json.parseToJsonElement(it) }?.jsonObject + } + + override fun toJson(value: Any): String { + return (value as kotlinx.serialization.json.JsonObject).toString() + } + +} + val listOrSingleValueConverter = object : Converter { override fun canConvert(cls: Class<*>) = cls == List::class.java @@ -126,6 +149,19 @@ val didVerificationRelationshipsConverter = object : Converter { } } +val sdMapConverter = object : Converter { + override fun canConvert(cls: Class<*>) = cls == SDMap::class.java + + override fun fromJson(jv: JsonValue): Any? { + return jv.obj?.toJsonString()?.let { SDMap.fromJSON(it) } + } + + override fun toJson(value: Any): String { + return (value as SDMap).toJSON().toString() + } + +} + fun KlaxonWithConverters() = Klaxon() .fieldConverter(VCList::class, VcConverter(singleVC = false, singleIfOne = false, toVcObject = false)) .fieldConverter(VCObjectList::class, VcConverter(singleVC = false, singleIfOne = false, toVcObject = true)) @@ -136,15 +172,8 @@ fun KlaxonWithConverters() = Klaxon() .fieldConverter(ListOrSingleValue::class, listOrSingleValueConverter) .fieldConverter(JsonObjectField::class, jsonObjectFieldConverter) .fieldConverter(DidVerificationRelationships::class, didVerificationRelationshipsConverter) + .fieldConverter(SDMapProperty::class, sdMapConverter) + .fieldConverter(KotlinxJsonObjectField::class, kotlinxJsonObjectFieldConverter) @Deprecated("Use KlaxonWithConverters()") -val KlaxonWithConverters = Klaxon() - .fieldConverter(VCList::class, VcConverter(singleVC = false, singleIfOne = false, toVcObject = false)) - .fieldConverter(VCObjectList::class, VcConverter(singleVC = false, singleIfOne = false, toVcObject = true)) - .fieldConverter(ListOrSingleVC::class, VcConverter(singleVC = false, singleIfOne = true, toVcObject = false)) - .fieldConverter(ListOrSingleVCObject::class, VcConverter(singleVC = false, singleIfOne = true, toVcObject = true)) - .fieldConverter(SingleVC::class, VcConverter(singleVC = true, singleIfOne = false, toVcObject = false)) - .fieldConverter(SingleVCObject::class, VcConverter(singleVC = true, singleIfOne = false, toVcObject = true)) - .fieldConverter(ListOrSingleValue::class, listOrSingleValueConverter) - .fieldConverter(JsonObjectField::class, jsonObjectFieldConverter) - .fieldConverter(DidVerificationRelationships::class, didVerificationRelationshipsConverter) +val KlaxonWithConverters = KlaxonWithConverters() diff --git a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt index 4b3bed185..1f2400462 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt @@ -54,7 +54,7 @@ open class VerifiableCredential internal constructor( open val challenge get() = when (this.sdJwt) { null -> this.proof?.nonce - else -> sdJwt!!.sdPayload.undisclosedPayload["nonce"]?.jsonPrimitive?.contentOrNull + else -> sdJwt!!.undisclosedPayload["nonce"]?.jsonPrimitive?.contentOrNull } fun toJsonObject() = buildJsonObject { @@ -120,11 +120,11 @@ open class VerifiableCredential internal constructor( fun isSDJwt(data: String) = SDJwt.isSDJwt(data) private fun fromSdJwt(sdJwt: SDJwt): VerifiableCredential { - val resolvedObject = sdJwt.sdPayload.fullPayload + val resolvedObject = sdJwt.fullPayload val claimKey = possibleClaimKeys.first { it in resolvedObject.keys } return fromJsonObject(resolvedObject[claimKey]!!.jsonObject).apply { this.sdJwt = sdJwt - this.selectiveDisclosure = sdJwt.sdPayload.sdMap[claimKey]?.children + this.selectiveDisclosure = sdJwt.sdMap[claimKey]?.children } } diff --git a/src/main/kotlin/id/walt/credentials/w3c/VerifiablePresentation.kt b/src/main/kotlin/id/walt/credentials/w3c/VerifiablePresentation.kt index be3a092fa..2883ca9e9 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/VerifiablePresentation.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/VerifiablePresentation.kt @@ -2,8 +2,8 @@ package id.walt.credentials.w3c import id.walt.credentials.w3c.builder.AbstractW3CCredentialBuilder import id.walt.credentials.w3c.builder.CredentialFactory -import id.walt.sdjwt.SDField import id.walt.sdjwt.SDMap +import id.walt.sdjwt.SDMapBuilder import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive @@ -56,13 +56,11 @@ data class PresentableCredential( ) { fun toJsonElement() = if(verifiableCredential.sdJwt != null) { - val claimKey = VerifiableCredential.possibleClaimKeys.first { it in verifiableCredential.sdJwt!!.sdPayload.undisclosedPayload.keys } + val claimKey = VerifiableCredential.possibleClaimKeys.first { it in verifiableCredential.sdJwt!!.undisclosedPayload.keys } val presentedJwt = if(discloseAll) { verifiableCredential.sdJwt!!.present(discloseAll) } else { - verifiableCredential.sdJwt!!.present(selectiveDisclosure?.let { mapOf( - claimKey to SDField(true, it) - )}) + verifiableCredential.sdJwt!!.present(selectiveDisclosure?.let { SDMapBuilder().addField(claimKey, false, it).build() }) } JsonPrimitive(presentedJwt.toString(formatForPresentation = true)) } else verifiableCredential.toJsonElement() diff --git a/src/main/kotlin/id/walt/model/oidc/IDToken.kt b/src/main/kotlin/id/walt/model/oidc/IDToken.kt index c08a8fb26..1de6de372 100644 --- a/src/main/kotlin/id/walt/model/oidc/IDToken.kt +++ b/src/main/kotlin/id/walt/model/oidc/IDToken.kt @@ -29,7 +29,7 @@ data class IDToken( if (KeyStoreService.getService().getKeyId(subject) == null) { DidService.importKeys(subject) } - return JwtService.getService().verify(jwt!!) + return JwtService.getService().verify(jwt!!).verified } return false } diff --git a/src/main/kotlin/id/walt/model/oidc/SelfIssuedIDToken.kt b/src/main/kotlin/id/walt/model/oidc/SelfIssuedIDToken.kt index 38bfa6328..6d22ad0c1 100644 --- a/src/main/kotlin/id/walt/model/oidc/SelfIssuedIDToken.kt +++ b/src/main/kotlin/id/walt/model/oidc/SelfIssuedIDToken.kt @@ -48,7 +48,7 @@ data class SelfIssuedIDToken( if (KeyStoreService.getService().getKeyId(parsedToken.subject) == null) { DidService.importKeys(parsedToken.subject) } - return JwtService.getService().verify(jwt) + return JwtService.getService().verify(jwt).verified } } } diff --git a/src/main/kotlin/id/walt/services/ecosystems/essif/enterprisewallet/EnterpriseWalletService.kt b/src/main/kotlin/id/walt/services/ecosystems/essif/enterprisewallet/EnterpriseWalletService.kt index 365359d53..1d1f3f98c 100644 --- a/src/main/kotlin/id/walt/services/ecosystems/essif/enterprisewallet/EnterpriseWalletService.kt +++ b/src/main/kotlin/id/walt/services/ecosystems/essif/enterprisewallet/EnterpriseWalletService.kt @@ -51,7 +51,7 @@ open class EnterpriseWalletService : WaltIdService() { log.debug { jwtToVerify.header } log.debug { jwtToVerify.payload } - jwtService.verify(jwt).let { if (!it) throw IllegalStateException("Generated JWK not valid") } + jwtService.verify(jwt).let { if (!it.verified) throw IllegalStateException("Generated JWK not valid") } log.debug { "AuthResponse JWT: $jwt" } return jwt diff --git a/src/main/kotlin/id/walt/services/ecosystems/essif/userwallet/UserWalletService.kt b/src/main/kotlin/id/walt/services/ecosystems/essif/userwallet/UserWalletService.kt index 76e1b1e65..98e005f97 100644 --- a/src/main/kotlin/id/walt/services/ecosystems/essif/userwallet/UserWalletService.kt +++ b/src/main/kotlin/id/walt/services/ecosystems/essif/userwallet/UserWalletService.kt @@ -427,7 +427,7 @@ object UserWalletService { log.debug { "Siop Response JWT:\n$jwt" } - jwtService.verify(jwt).let { if (!it) throw IllegalStateException("Generated JWK not valid") } + jwtService.verify(jwt).let { if (!it.verified) throw IllegalStateException("Generated JWK not valid") } return jwt } diff --git a/src/main/kotlin/id/walt/services/jwt/JwtService.kt b/src/main/kotlin/id/walt/services/jwt/JwtService.kt index d3aa7b3f9..c24c58f69 100644 --- a/src/main/kotlin/id/walt/services/jwt/JwtService.kt +++ b/src/main/kotlin/id/walt/services/jwt/JwtService.kt @@ -2,6 +2,7 @@ package id.walt.services.jwt import com.nimbusds.jose.jwk.OctetKeyPair import id.walt.sdjwt.JWTCryptoProvider +import id.walt.sdjwt.JwtVerificationResult import id.walt.servicematrix.ServiceProvider import id.walt.services.WaltIdService import kotlinx.serialization.json.JsonObject @@ -35,7 +36,7 @@ open class JwtService : WaltIdService(), JWTCryptoProvider { return sign(keyID, payload.toString()) } - override fun verify(token: String): Boolean = implementation.verify(token) + override fun verify(token: String): JwtVerificationResult = implementation.verify(token) open fun parseClaims(token: String): MutableMap? = implementation.parseClaims(token) diff --git a/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt b/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt index 743a7af4f..df35734a1 100644 --- a/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt +++ b/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt @@ -10,6 +10,7 @@ import com.nimbusds.jwt.JWTClaimsSet import com.nimbusds.jwt.SignedJWT import id.walt.crypto.* import id.walt.model.DidUrl +import id.walt.sdjwt.JwtVerificationResult import id.walt.services.did.DidService import id.walt.services.key.KeyService import mu.KotlinLogging @@ -147,7 +148,7 @@ open class WaltIdJwtService : JwtService() { return serializedSignedJwt } - override fun verify(token: String): Boolean { + override fun verify(token: String): JwtVerificationResult { log.debug { "Verifying token: $token" } val jwt = SignedJWT.parse(token) val issuer = jwt.jwtClaimsSet.issuer @@ -183,7 +184,7 @@ open class WaltIdJwtService : JwtService() { } log.debug { "JWT verified returned: $res" } - return res + return JwtVerificationResult(res) } override fun parseClaims(token: String): MutableMap? { diff --git a/src/main/kotlin/id/walt/services/vc/WaltIdJwtCredentialService.kt b/src/main/kotlin/id/walt/services/vc/WaltIdJwtCredentialService.kt index 408a8f5fd..2ffe5a71a 100644 --- a/src/main/kotlin/id/walt/services/vc/WaltIdJwtCredentialService.kt +++ b/src/main/kotlin/id/walt/services/vc/WaltIdJwtCredentialService.kt @@ -81,7 +81,7 @@ open class WaltIdJwtCredentialService : JwtCredentialService() { override fun verifyVc(vc: String): Boolean { log.debug { "Verifying vc: $vc" } - return SDJwt.parse(vc).verify(jwtService) + return SDJwt.parse(vc).verify(jwtService).verified } override fun verifyVp(vp: String): Boolean = diff --git a/src/main/kotlin/id/walt/signatory/Signatory.kt b/src/main/kotlin/id/walt/signatory/Signatory.kt index 0529e43b4..99b7b22e3 100644 --- a/src/main/kotlin/id/walt/signatory/Signatory.kt +++ b/src/main/kotlin/id/walt/signatory/Signatory.kt @@ -1,6 +1,7 @@ package id.walt.signatory import com.beust.klaxon.Json +import id.walt.common.SDMapProperty import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.W3CIssuer import id.walt.credentials.w3c.builder.AbstractW3CCredentialBuilder @@ -48,7 +49,7 @@ data class ProofConfig( @Json(serializeNull = false) val statusType: CredentialStatus.Types? = null, @Json(serializeNull = false) val statusPurpose: String = "revocation", @Json(serializeNull = false) val credentialsEndpoint: String? = null, - @Json(serializeNull = false) val selectiveDisclosure: SDMap? = null + @Json(serializeNull = false) @SDMapProperty val selectiveDisclosure: SDMap? = null ) data class SignatoryConfig( diff --git a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt index 6104544a5..8422139e0 100644 --- a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt +++ b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt @@ -1,6 +1,7 @@ package id.walt.signatory.rest import id.walt.common.KlaxonWithConverters +import id.walt.common.KotlinxJsonObjectField import id.walt.credentials.w3c.JsonConverter import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.builder.W3CCredentialBuilder @@ -20,12 +21,13 @@ import io.javalin.http.ContentType import io.javalin.http.Context import io.javalin.http.HttpCode import io.javalin.plugin.openapi.dsl.document +import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonObject data class IssueCredentialRequest( val templateId: String?, val config: ProofConfig, - val credentialData: Map? = null + @KotlinxJsonObjectField val credentialData: JsonObject? = null ) object SignatoryController { @@ -75,7 +77,7 @@ object SignatoryController { }.pathParam("id").result("200") fun issueCredential(ctx: Context) { - val req = ctx.bodyAsClass() + val req = KlaxonWithConverters().parse(ctx.body()) ?: throw BadRequestResponse("Cannot parse IssueCredentialRequest body") if (req.templateId != null && !signatory.hasTemplateId(req.templateId)) { throw BadRequestResponse("Template with supplied id does not exist.") } diff --git a/src/test/kotlin/id/walt/services/jwt/JwtServiceTest.kt b/src/test/kotlin/id/walt/services/jwt/JwtServiceTest.kt index e1b6f3472..4a1eddfc7 100644 --- a/src/test/kotlin/id/walt/services/jwt/JwtServiceTest.kt +++ b/src/test/kotlin/id/walt/services/jwt/JwtServiceTest.kt @@ -38,7 +38,7 @@ class JwtServiceTest : AnnotationSpec() { "https://walt.id" shouldBe signedJwt.jwtClaimsSet.claims["iss"] val res1 = jwtService.verify(jwt) - res1 shouldBe true + res1.verified shouldBe true } @Test @@ -53,7 +53,7 @@ class JwtServiceTest : AnnotationSpec() { "https://walt.id" shouldBe signedJwt.jwtClaimsSet.claims["iss"] val res1 = jwtService.verify(jwt) - res1 shouldBe true + res1.verified shouldBe true } @Test @@ -80,7 +80,7 @@ class JwtServiceTest : AnnotationSpec() { "https://self-issued.me" shouldBe jwt.jwtClaimsSet.claims["iss"] thumbprint shouldBe jwt.jwtClaimsSet.claims["sub"] - jwtService.verify(jwtStr) shouldBe true + jwtService.verify(jwtStr).verified shouldBe true } } diff --git a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt index 9301fe0ff..e7eafb016 100644 --- a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt +++ b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt @@ -156,7 +156,7 @@ class OIDC4VCTest : AnnotationSpec() { fun testCrosswordCyberSecurityDidProof() { val proof = JwtProof("eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpqd2s6ZXlKNUlqb2lNako0Umw5NVRsUjZkVzVSVEU1dmExWXlNMTlwTFhGS2EwOU5NVlptVUdZNFRFeFhOM1JHZDBkcWF5SXNJbU55ZGlJNklsQXRNalUySWl3aWVDSTZJbVJKTmtoMGVrMDJXSEZZUkhCd1MzTkJiR0kzWDNkd1FuWlpjRTVZV0c5UldHcHFjRUpGVURWaE5tOGlMQ0poYkdjaU9pSkZVekkxTmlJc0ltdDBlU0k2SWtWREluMCJ9.eyJleHAiOjE2NjYwMjM0NjYsImp0aSI6IjVVT0xnR29keXZ1R2dZVTZcLzF2Tk9kd0pVUUpkM085VWlCTHg1Z2VSSlpBPSIsImlzcyI6ImRpZDpqd2s6ZXlKNUlqb2lNako0Umw5NVRsUjZkVzVSVEU1dmExWXlNMTlwTFhGS2EwOU5NVlptVUdZNFRFeFhOM1JHZDBkcWF5SXNJbU55ZGlJNklsQXRNalUySWl3aWVDSTZJbVJKTmtoMGVrMDJXSEZZUkhCd1MzTkJiR0kzWDNkd1FuWlpjRTVZV0c5UldHcHFjRUpGVURWaE5tOGlMQ0poYkdjaU9pSkZVekkxTmlJc0ltdDBlU0k2SWtWREluMCIsImF1ZCI6Imh0dHBzOlwvXC9pc3N1ZXIud2FsdC10ZXN0LmNsb3VkXC9pc3N1ZXItYXBpXC9vaWRjXC8iLCJpYXQiOjE2NjYwMjM0MDZ9.oZd5hfs1iLQMiaL3VUvxG93B5CjKsVmH45WPdXGeeSnqBm5QF7eHNlakZ4u8KGlrq0gqXLc5lXP-u0rMzbNZkw") - JwtService.getService().verify(proof.jwt) shouldBe true + JwtService.getService().verify(proof.jwt).verified shouldBe true } @Test diff --git a/src/test/kotlin/id/walt/services/vc/WaltIdJwtCredentialServiceTest.kt b/src/test/kotlin/id/walt/services/vc/WaltIdJwtCredentialServiceTest.kt index 07875a9c7..193a54484 100644 --- a/src/test/kotlin/id/walt/services/vc/WaltIdJwtCredentialServiceTest.kt +++ b/src/test/kotlin/id/walt/services/vc/WaltIdJwtCredentialServiceTest.kt @@ -206,7 +206,7 @@ class WaltIdJwtCredentialServiceTest : AnnotationSpec() { val parsedSdJwt = SDJwt.parse(issuedVID) parsedSdJwt.disclosures shouldHaveSize 3 - parsedSdJwt.sdPayload.sDisclosures.map { sd -> sd.key } shouldContainAll setOf("credentialSubject", "firstName", "dateOfBirth") + parsedSdJwt.disclosureObjects.map { sd -> sd.key } shouldContainAll setOf("credentialSubject", "firstName", "dateOfBirth") Auditor.getService().verify(issuedVID, listOf(SignaturePolicy())).result shouldBe true diff --git a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt index 8b4176a7b..b867c2807 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt @@ -6,6 +6,8 @@ import id.walt.credentials.w3c.templates.VcTemplate import id.walt.credentials.w3c.templates.VcTemplateService import id.walt.credentials.w3c.toVerifiableCredential import id.walt.model.DidMethod +import id.walt.sdjwt.SDJwt +import id.walt.sdjwt.SDMapBuilder import id.walt.servicematrix.ServiceMatrix import id.walt.services.WaltIdServices import id.walt.services.did.DidService @@ -14,6 +16,9 @@ import id.walt.signatory.rest.SignatoryRestAPI import id.walt.test.RESOURCES_PATH import io.kotest.core.spec.style.AnnotationSpec import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.maps.shouldContain +import io.kotest.matchers.maps.shouldNotContainKey import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldStartWith @@ -25,6 +30,8 @@ import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.runBlocking +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import java.net.URL class SignatoryApiTest : AnnotationSpec() { @@ -314,4 +321,62 @@ class SignatoryApiTest : AnnotationSpec() { vcParsed.subjectId shouldBe did vcParsed.credentialSubject!!.properties["foo"] shouldBe "bar" } + + @Test + fun testIssueRequestSDMap() { + val did = DidService.create(DidMethod.key) + val reqObj = IssueCredentialRequest( + templateId = "VerifiableId", + config = ProofConfig( + issuerDid = did, + subjectDid = did, + proofType = ProofType.SD_JWT, + selectiveDisclosure = SDMapBuilder() + .addField("credentialSubject", true, + SDMapBuilder().addField("firstName", true).build() + ).build() + ), + credentialData = buildJsonObject { + put("credentialSubject", buildJsonObject { + put("firstName", "Severin") + }) + } + ) + val request = "{\n" + + " \"templateId\": \"VerifiableId\",\n" + + " \"config\": {\n" + + " \"issuerDid\": \"$did\",\n" + + " \"subjectDid\": \"$did\",\n" + + " \"proofType\": \"SD_JWT\",\n" + + " \"selectiveDisclosure\": {\n" + + " \"fields\": {\n" + + " \"credentialSubject\": {\n" + + " \"sd\": true,\n" + + " \"children\": {\n" + + " \"fields\": {\n" + + " \"firstName\": {\n" + + " \"sd\": true\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " },\n" + + " \"credentialData\": {\n" + + " \"credentialSubject\": {\n" + + " \"firstName\": \"Severin\"\n" + + " }\n" + + " }\n" + + "}" + val vc = httpPost("/v1/credentials/issue", KlaxonWithConverters().toJsonString(reqObj)) + println(vc) + val parsedVc = vc?.toVerifiableCredential() + parsedVc shouldNotBe null + parsedVc!!.credentialSubject!!.properties shouldContain Pair("firstName", "Severin") + + val parsedSdJwt = SDJwt.parse(vc) + parsedSdJwt.undisclosedPayload shouldNotContainKey "credentialSubject" + parsedSdJwt.disclosureObjects.map { it.key }.toSet() shouldContainAll setOf("credentialSubject", "firstName") + } } diff --git a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt index e628bba9b..d839aab15 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt @@ -78,7 +78,7 @@ class SignatoryServiceTest : StringSpec({ did shouldBe jwt.jwtClaimsSet.claims["sub"] println("VERIFYING VC") - JwtService.getService().verify(jwtStr) shouldBe true + JwtService.getService().verify(jwtStr).verified shouldBe true } "Issue and verify: VerifiableDiploma (LD-Proof)" { @@ -126,7 +126,7 @@ class SignatoryServiceTest : StringSpec({ did shouldBe jwt.jwtClaimsSet.claims["sub"] println("VERIFYING VC") - JwtService.getService().verify(jwtStr) shouldBe true + JwtService.getService().verify(jwtStr).verified shouldBe true } "vc storage test" {