diff --git a/service-matrix.properties b/service-matrix.properties index 56ddac7e..5af5d726 100644 --- a/service-matrix.properties +++ b/service-matrix.properties @@ -1,6 +1,7 @@ id.walt.services.ecosystems.essif.didebsi.DidEbsiService=id.walt.services.ecosystems.essif.didebsi.WaltIdDidEbsiService id.walt.services.ecosystems.essif.jsonrpc.JsonRpcService=id.walt.services.ecosystems.essif.jsonrpc.WaltIdJsonRpcService id.walt.services.vc.JsonLdCredentialService=id.walt.services.vc.WaltIdJsonLdCredentialService +id.walt.credentials.jsonld.JsonLdDocumentLoaderService=id.walt.credentials.jsonld.LocalJsonLdDocumentLoaderService id.walt.services.vc.JwtCredentialService=id.walt.services.vc.WaltIdJwtCredentialService id.walt.services.crypto.CryptoService=id.walt.services.crypto.SunCryptoService id.walt.services.keystore.KeyStoreService=id.walt.services.keystore.SqlKeyStoreService diff --git a/src/main/kotlin/id/walt/credentials/jsonld/JsonLdDocumentLoaderService.kt b/src/main/kotlin/id/walt/credentials/jsonld/JsonLdDocumentLoaderService.kt new file mode 100644 index 00000000..81d06fc4 --- /dev/null +++ b/src/main/kotlin/id/walt/credentials/jsonld/JsonLdDocumentLoaderService.kt @@ -0,0 +1,19 @@ +package id.walt.credentials.jsonld + +import com.apicatalog.jsonld.loader.DocumentLoader +import id.walt.servicematrix.ServiceProvider +import id.walt.servicematrix.ServiceRegistry +import id.walt.services.WaltIdService + +abstract class JsonLdDocumentLoaderService: WaltIdService() { + + override val implementation: JsonLdDocumentLoaderService get() = serviceImplementation() + + abstract val documentLoader: DocumentLoader + + companion object : ServiceProvider { + override fun getService() = ServiceRegistry.getService(JsonLdDocumentLoaderService::class) + override fun defaultImplementation() = LocalJsonLdDocumentLoaderService() + + } +} diff --git a/src/main/kotlin/id/walt/credentials/jsonld/LocalJsonLdDocumentLoaderService.kt b/src/main/kotlin/id/walt/credentials/jsonld/LocalJsonLdDocumentLoaderService.kt new file mode 100644 index 00000000..7156215f --- /dev/null +++ b/src/main/kotlin/id/walt/credentials/jsonld/LocalJsonLdDocumentLoaderService.kt @@ -0,0 +1,8 @@ +package id.walt.credentials.jsonld + +import com.apicatalog.jsonld.loader.DocumentLoader + +class LocalJsonLdDocumentLoaderService : JsonLdDocumentLoaderService() { + override val documentLoader: DocumentLoader + get() = VerifiableCredentialContexts.DOCUMENT_LOADER +} diff --git a/src/main/kotlin/id/walt/credentials/jsonld/RemoteJsonLdDocumentLoaderService.kt b/src/main/kotlin/id/walt/credentials/jsonld/RemoteJsonLdDocumentLoaderService.kt new file mode 100644 index 00000000..1c36b552 --- /dev/null +++ b/src/main/kotlin/id/walt/credentials/jsonld/RemoteJsonLdDocumentLoaderService.kt @@ -0,0 +1,9 @@ +package id.walt.credentials.jsonld + +import com.apicatalog.jsonld.loader.DocumentLoader +import info.weboftrust.ldsignatures.jsonld.LDSecurityContexts + +class RemoteJsonLdDocumentLoaderService : JsonLdDocumentLoaderService() { + override val documentLoader: DocumentLoader + get() = LDSecurityContexts.DOCUMENT_LOADER +} diff --git a/src/main/kotlin/id/walt/credentials/jsonld/VerifiableCredentialContexts.kt b/src/main/kotlin/id/walt/credentials/jsonld/VerifiableCredentialContexts.kt new file mode 100644 index 00000000..005e1433 --- /dev/null +++ b/src/main/kotlin/id/walt/credentials/jsonld/VerifiableCredentialContexts.kt @@ -0,0 +1,32 @@ +package id.walt.credentials.jsonld + +import com.apicatalog.jsonld.document.JsonDocument +import com.apicatalog.jsonld.http.media.MediaType +import com.apicatalog.jsonld.loader.DocumentLoader +import foundation.identity.jsonld.ConfigurableDocumentLoader +import info.weboftrust.ldsignatures.jsonld.LDSecurityContexts +import java.net.URI +import java.util.* + +object VerifiableCredentialContexts { + val DOCUMENT_LOADER: DocumentLoader by lazy { ConfigurableDocumentLoader(CONTEXTS) } + private val CONTEXTS: Map by lazy { + val map = LDSecurityContexts.CONTEXTS + runCatching { loadContextFiles() }.onSuccess { + map.putAll(it) + } + for ((key, value) in map) { + value.documentUrl = key + } + map + } + private val JSONLD_CONTEXT_W3C_2018_CREDENTIALS_V1 = URI.create("https://www.w3.org/2018/credentials/v1") + + private fun loadContextFiles() = mapOf( + JSONLD_CONTEXT_W3C_2018_CREDENTIALS_V1 to JsonDocument.of( + MediaType.JSON_LD, Objects.requireNonNull( + VerifiableCredentialContexts::class.java.classLoader.getResourceAsStream("credentials-v1.jsonld") + ) + ) + ) +} diff --git a/src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt b/src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt index b0665b42..01ddafa9 100644 --- a/src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt +++ b/src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt @@ -19,7 +19,7 @@ enum class VerificationType { @Serializable data class VerificationResult(val verified: Boolean, val verificationType: VerificationType) -abstract class JsonLdCredentialService : WaltIdService() { +abstract class JsonLdCredentialService() : WaltIdService() { override val implementation get() = serviceImplementation() open fun sign(jsonCred: String, config: ProofConfig): String = implementation.sign(jsonCred, config) diff --git a/src/main/kotlin/id/walt/services/vc/WaltIdJsonLdCredentialService.kt b/src/main/kotlin/id/walt/services/vc/WaltIdJsonLdCredentialService.kt index 162e9e1d..51315e4a 100644 --- a/src/main/kotlin/id/walt/services/vc/WaltIdJsonLdCredentialService.kt +++ b/src/main/kotlin/id/walt/services/vc/WaltIdJsonLdCredentialService.kt @@ -7,6 +7,7 @@ import foundation.identity.jsonld.ConfigurableDocumentLoader import foundation.identity.jsonld.JsonLDException import foundation.identity.jsonld.JsonLDObject import id.walt.auditor.VerificationPolicyResult +import id.walt.credentials.jsonld.JsonLdDocumentLoaderService import id.walt.credentials.w3c.* import id.walt.credentials.w3c.schema.SchemaValidatorFactory import id.walt.crypto.Key @@ -19,7 +20,6 @@ import id.walt.services.keystore.KeyStoreService import id.walt.signatory.ProofConfig import id.walt.signatory.ProofType import info.weboftrust.ldsignatures.LdProof -import info.weboftrust.ldsignatures.jsonld.LDSecurityContexts import info.weboftrust.ldsignatures.verifier.LdVerifier import mu.KotlinLogging import org.json.JSONObject @@ -31,10 +31,10 @@ import java.util.* private val log = KotlinLogging.logger {} -open class WaltIdJsonLdCredentialService : JsonLdCredentialService() { +open class WaltIdJsonLdCredentialService() : JsonLdCredentialService() { - private val keyStore: KeyStoreService - get() = ContextManager.keyStore + private val keyStore: KeyStoreService get() = ContextManager.keyStore + private val documentLoaderService: JsonLdDocumentLoaderService get() = JsonLdDocumentLoaderService.getService() init { Ed25519Provider.set(TinkEd25519Provider()) @@ -109,13 +109,13 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() { log.debug { "Signing jsonLd object with: issuerDid (${config.issuerDid}), domain (${config.domain}), nonce (${config.nonce}" } val jsonLdObject: JsonLDObject = JsonLDObject.fromJson(jsonCred) - val confLoader = LDSecurityContexts.DOCUMENT_LOADER as ConfigurableDocumentLoader + val confLoader = documentLoaderService.documentLoader as ConfigurableDocumentLoader confLoader.isEnableHttp = true confLoader.isEnableHttps = true confLoader.isEnableFile = true confLoader.isEnableLocalCache = true - jsonLdObject.documentLoader = LDSecurityContexts.DOCUMENT_LOADER + jsonLdObject.documentLoader = documentLoaderService.documentLoader val vm = config.issuerVerificationMethod ?: config.issuerDid val key = keyStore.load(vm) @@ -182,7 +182,7 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() { log.debug { "Verification key for: $vm is: $publicKey" } - val confLoader = LDSecurityContexts.DOCUMENT_LOADER as ConfigurableDocumentLoader + val confLoader = documentLoaderService.documentLoader as ConfigurableDocumentLoader confLoader.isEnableHttp = true confLoader.isEnableHttps = true @@ -192,7 +192,7 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() { log.debug { "Document loader config: isEnableHttp (${confLoader.isEnableHttp}), isEnableHttps (${confLoader.isEnableHttps}), isEnableFile (${confLoader.isEnableFile}), isEnableLocalCache (${confLoader.isEnableLocalCache})" } val jsonLdObject = JsonLDObject.fromJson(vcOrVp) - jsonLdObject.documentLoader = LDSecurityContexts.DOCUMENT_LOADER + jsonLdObject.documentLoader = documentLoaderService.documentLoader log.debug { "Decoded Json LD object: $jsonLdObject" } val ldProof = LdProof.getFromJsonLDObject(jsonLdObject) diff --git a/src/main/resources/credentials-v1.jsonld b/src/main/resources/credentials-v1.jsonld new file mode 100644 index 00000000..0124a3c4 --- /dev/null +++ b/src/main/resources/credentials-v1.jsonld @@ -0,0 +1,237 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "VerifiableCredential": { + "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "credentialSchema": { + "@id": "cred:credentialSchema", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + + "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" + } + }, + "credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"}, + "credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"}, + "evidence": {"@id": "cred:evidence", "@type": "@id"}, + "expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"}, + "holder": {"@id": "cred:holder", "@type": "@id"}, + "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, + "issuer": {"@id": "cred:issuer", "@type": "@id"}, + "issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "refreshService": { + "@id": "cred:refreshService", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + + "ManualRefreshService2018": "cred:ManualRefreshService2018" + } + }, + "termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"}, + "validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"}, + "validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"} + } + }, + + "VerifiablePresentation": { + "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + + "holder": {"@id": "cred:holder", "@type": "@id"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"} + } + }, + + "EcdsaSecp256k1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "EcdsaSecp256r1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "Ed25519Signature2018": { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "RsaSignature2018": { + "@id": "https://w3id.org/security#RsaSignature2018", + "@context": { + "@version": 1.1, + "@protected": true, + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"} + } +}