Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ebsi vc and vp verifiactions #48

Merged
merged 14 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ dependencies {
implementation("com.google.guava:guava:31.0.1-jre")

// VC
implementation("id.walt:waltid-ssikit-vclib:1.4.9")
implementation("id.walt:waltid-ssikit-vclib:1.5.0")

// JSON
implementation("org.json:json:20210307")
implementation("com.beust:klaxon:5.5")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.0")
implementation("net.pwall.json:json-kotlin-schema:0.23")
//implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2")

// DB
Expand Down
74 changes: 59 additions & 15 deletions src/main/kotlin/id/walt/auditor/Auditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import id.walt.services.essif.TrustedIssuerClient
import id.walt.services.vc.JsonLdCredentialService
import id.walt.services.vc.JwtCredentialService
import id.walt.services.vc.VcUtils
import id.walt.vclib.Helpers.encode
import id.walt.vclib.Helpers.toCredential
import id.walt.vclib.model.VerifiableCredential
import id.walt.vclib.vclist.VerifiablePresentation
import kotlinx.serialization.Serializable
import mu.KotlinLogging
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.LinkedHashMap

// the following validation policies can be applied
// - SIGNATURE
Expand All @@ -26,6 +30,11 @@ import mu.KotlinLogging

private val log = KotlinLogging.logger {}

private val jsonLdCredentialService = JsonLdCredentialService.getService()
private val jwtCredentialService = JwtCredentialService.getService()
private val dateFormatter =
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").also { it.timeZone = TimeZone.getTimeZone("UTC") }

@Serializable
data class VerificationPolicyMetadata(val description: String, val id: String)

Expand All @@ -37,10 +46,7 @@ interface VerificationPolicy {
}

class SignaturePolicy : VerificationPolicy {
private val jsonLdCredentialService = JsonLdCredentialService.getService()
private val jwtCredentialService = JwtCredentialService.getService()
override val description: String = "Verify by signature"

override fun verify(vc: VerifiableCredential): Boolean {
return try {
log.debug { "is jwt: ${vc.jwt != null}" }
Expand All @@ -56,19 +62,20 @@ class SignaturePolicy : VerificationPolicy {
}
}

class JsonSchemaPolicy : VerificationPolicy { // Schema already validated by json-ld?
class JsonSchemaPolicy : VerificationPolicy {
override val description: String = "Verify by JSON schema"
override fun verify(vc: VerifiableCredential) = true // TODO validate policy
override fun verify(vc: VerifiableCredential): Boolean {
return when (vc.jwt) {
null -> jsonLdCredentialService.validateSchema(vc.encode()) // Schema already validated by json-ld?
else -> jwtCredentialService.validateSchema(vc.encode())
}
}
}

class TrustedIssuerDidPolicy : VerificationPolicy {
override val description: String = "Verify by trusted issuer did"
override fun verify(vc: VerifiableCredential): Boolean {

return when (vc) {
is VerifiablePresentation -> true
else -> DidService.loadOrResolveAnyDid(VcUtils.getIssuer(vc)) != null
}
return DidService.loadOrResolveAnyDid(VcUtils.getIssuer(vc)) != null
}
}

Expand Down Expand Up @@ -120,15 +127,49 @@ class TrustedIssuerRegistryPolicy : VerificationPolicy {
class TrustedSubjectDidPolicy : VerificationPolicy {
override val description: String = "Verify by trusted subject did"
override fun verify(vc: VerifiableCredential): Boolean {
return VcUtils.getSubject(vc).let {
if (it.isEmpty()) true
else DidService.loadOrResolveAnyDid(it) != null
}
}
}

//TODO complete PoC implementation
class IssuanceDateBeforePolicy : VerificationPolicy {
override val description: String = "Verify by issuance date"
override fun verify(vc: VerifiableCredential): Boolean {
return when (vc) {
is VerifiablePresentation -> true
else -> DidService.loadOrResolveAnyDid(VcUtils.getHolder(vc)) != null
else -> parseDate(VcUtils.getIssuanceDate(vc)).let { it != null && it.before(Date()) }
}
}
}

class ValidFromBeforePolicy : VerificationPolicy {
override val description: String = "Verify by valid from"
override fun verify(vc: VerifiableCredential): Boolean {
return when (vc) {
is VerifiablePresentation -> true
else -> parseDate(VcUtils.getValidFrom(vc)).let { it != null && it.before(Date()) }
}
}
}

class ExpirationDateAfterPolicy : VerificationPolicy {
override val description: String = "Verify by expiration date"
override fun verify(vc: VerifiableCredential): Boolean {
return when (vc) {
is VerifiablePresentation -> true
else -> parseDate(VcUtils.getExpirationDate(vc)).let { it == null || it.after(Date()) }
}
}
}

private fun parseDate(date: String?) = try {
dateFormatter.parse(date)
} catch (e: Exception) {
null
}

object PolicyRegistry {
private val policies = LinkedHashMap<String, VerificationPolicy>()
val defaultPolicyId: String
Expand All @@ -143,9 +184,12 @@ object PolicyRegistry {
defaultPolicyId = sigPol.id
register(sigPol)
register(JsonSchemaPolicy())
register(TrustedSubjectDidPolicy())
register(TrustedIssuerDidPolicy())
register(TrustedIssuerRegistryPolicy())
register(TrustedSubjectDidPolicy())
register(IssuanceDateBeforePolicy())
register(ValidFromBeforePolicy())
register(ExpirationDateAfterPolicy())
}
}

Expand All @@ -161,8 +205,8 @@ interface IAuditor {

fun verify(vcJson: String, policies: List<VerificationPolicy>): VerificationResult

// fun verifyVc(vc: String, config: AuditorConfig) = VerificationStatus(true)
// fun verifyVp(vp: String, config: AuditorConfig) = VerificationStatus(true)
// fun verifyVc(vc: String, config: AuditorConfig) = VerificationStatus(true)
// fun verifyVp(vp: String, config: AuditorConfig) = VerificationStatus(true)
}

object Auditor : IAuditor {
Expand Down
10 changes: 4 additions & 6 deletions src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ data class VerificationResult(val verified: Boolean, val verificationType: Verif
abstract class JsonLdCredentialService : WaltIdService() {
override val implementation get() = ServiceRegistry.getService<JsonLdCredentialService>()

open fun sign(
jsonCred: String,
config: ProofConfig
): String = implementation.sign(jsonCred, config)
open fun sign(jsonCred: String, config: ProofConfig): String = implementation.sign(jsonCred, config)

open fun verify(vcOrVp: String): VerificationResult = implementation.verify(vcOrVp)
open fun verifyVc(issuerDid: String, vc: String): Boolean = implementation.verifyVc(issuerDid, vc)
Expand All @@ -36,8 +33,9 @@ abstract class JsonLdCredentialService : WaltIdService() {

open fun defaultVcTemplate(): VerifiableCredential = implementation.defaultVcTemplate()

open fun addProof(credMap: Map<String, String>, ldProof: LdProof): String =
implementation.addProof(credMap, ldProof)
open fun addProof(credMap: Map<String, String>, ldProof: LdProof): String = implementation.addProof(credMap, ldProof)

open fun validateSchema(vc: String): Boolean = implementation.validateSchema(vc)

companion object : ServiceProvider {
override fun getService() = object : JsonLdCredentialService() {}
Expand Down
10 changes: 4 additions & 6 deletions src/main/kotlin/id/walt/services/vc/JwtCredentialService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import info.weboftrust.ldsignatures.LdProof
abstract class JwtCredentialService : WaltIdService() {
override val implementation get() = ServiceRegistry.getService<JwtCredentialService>()

open fun sign(
jsonCred: String,
config: ProofConfig
): String = implementation.sign(jsonCred, config)
open fun sign(jsonCred: String, config: ProofConfig): String = implementation.sign(jsonCred, config)

open fun verify(vcOrVp: String): VerificationResult = implementation.verify(vcOrVp)
open fun verifyVc(issuerDid: String, vc: String): Boolean = implementation.verifyVc(issuerDid, vc)
Expand All @@ -28,8 +25,9 @@ abstract class JwtCredentialService : WaltIdService() {

open fun defaultVcTemplate(): VerifiableCredential = implementation.defaultVcTemplate()

open fun addProof(credMap: Map<String, String>, ldProof: LdProof): String =
implementation.addProof(credMap, ldProof)
open fun addProof(credMap: Map<String, String>, ldProof: LdProof): String = implementation.addProof(credMap, ldProof)

open fun validateSchema(vc: String): Boolean = implementation.validateSchema(vc)

companion object : ServiceProvider {
override fun getService() = object : JwtCredentialService() {}
Expand Down
49 changes: 48 additions & 1 deletion src/main/kotlin/id/walt/services/vc/VcUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object VcUtils {
}
}

fun getHolder(vcObj: VerifiableCredential): String = when (vcObj) {
fun getSubject(vcObj: VerifiableCredential): String = when (vcObj) {
is Europass -> vcObj.credentialSubject!!.id!!
is VerifiableId -> vcObj.credentialSubject!!.id!!
is VerifiableDiploma -> vcObj.credentialSubject!!.id!!
Expand All @@ -44,4 +44,51 @@ object VcUtils {
}
}

fun getIssuanceDate(vc: VerifiableCredential) = when (vc) {
is Europass -> vc.issuanceDate
is VerifiableId -> vc.issuanceDate
is VerifiableDiploma -> vc.issuanceDate
is UniversityDegree -> vc.issuanceDate
is VerifiableAttestation -> vc.issuanceDate
is VerifiableAuthorization -> vc.issuanceDate
else -> {
log.warn { "No getIssuanceDate for ${vc.type.last()}!" }
""
}
}

fun getValidFrom(vc: VerifiableCredential) = when (vc) {
is Europass -> vc.validFrom
is VerifiableId -> vc.validFrom
is VerifiableDiploma -> vc.validFrom
is VerifiableAttestation -> vc.validFrom
is VerifiableAuthorization -> vc.validFrom
else -> {
log.warn { "No getValidFrom for ${vc.type.last()}!" }
""
}
}

fun getExpirationDate(vc: VerifiableCredential) = when (vc) {
is Europass -> vc.expirationDate
is VerifiableId -> vc.expirationDate
is VerifiableDiploma -> vc.expirationDate
is VerifiableAuthorization -> vc.expirationDate
else -> {
log.warn { "No getExpirationDate for ${vc.type.last()}!" }
""
}
}

fun getCredentialSchema(vc: VerifiableCredential) = when (vc) {
is Europass -> vc.credentialSchema
is VerifiableId -> vc.credentialSchema
is VerifiableDiploma -> vc.credentialSchema
is VerifiableAttestation -> vc.credentialSchema
is VerifiableAuthorization -> vc.credentialSchema
else -> {
log.warn { "No getCredentialSchema for ${vc.type.last()}!" }
null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ import info.weboftrust.ldsignatures.jsonld.LDSecurityContexts
import mu.KotlinLogging
import org.json.JSONObject
import java.net.URI
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import net.pwall.json.schema.JSONSchema

private val log = KotlinLogging.logger {}

Expand All @@ -43,10 +45,7 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() {
Ed25519Provider.set(TinkEd25519Provider())
}

override fun sign(
jsonCred: String,
config: ProofConfig
): String {
override fun sign(jsonCred: String, config: ProofConfig): String {
log.debug { "Signing jsonLd object with: issuerDid (${config.issuerDid}), domain (${config.domain}), nonce (${config.nonce}" }

val jsonLdObject: JsonLDObject = JsonLDObject.fromJson(jsonCred)
Expand Down Expand Up @@ -354,6 +353,16 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() {
)
}

override fun validateSchema(vc: String) = try {
vc.toCredential().let {
val credentialSchema = VcUtils.getCredentialSchema(it) ?: return true
val schema = JSONSchema.parse(URL(credentialSchema.id).readText())
return schema.validateBasic(it.json!!).valid
}
} catch (e: Exception) {
false
}

/*override fun listTemplates(): List<String> {
return Files.walk(Path.of("templates"))
.filter { Files.isRegularFile(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import id.walt.vclib.model.VerifiableCredential
import id.walt.vclib.vclist.VerifiablePresentation
import info.weboftrust.ldsignatures.LdProof
import mu.KotlinLogging
import net.pwall.json.schema.JSONSchema
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.time.LocalDateTime
Expand All @@ -33,7 +35,7 @@ open class WaltIdJwtCredentialService : JwtCredentialService() {

val issuerDid = config.issuerDid
val issueDate = config.issueDate ?: LocalDateTime.now()
val validDate = config.validDate ?: LocalDateTime.now().plusDays(5)
val validDate = config.validDate ?: LocalDateTime.now()
val jwtClaimsSet = JWTClaimsSet.Builder()
.jwtID(config.credentialId)
.issuer(issuerDid)
Expand Down Expand Up @@ -112,4 +114,15 @@ open class WaltIdJwtCredentialService : JwtCredentialService() {

override fun defaultVcTemplate(): VerifiableCredential =
TODO("Not implemented yet.")

override fun validateSchema(vc: String) = try {
vc.toCredential().let {
val credentialSchema = VcUtils.getCredentialSchema(it) ?: return true
val schema = JSONSchema.parse(URL(credentialSchema.id).readText())
return schema.validateBasic(it.json!!).valid
}
} catch (e: Exception) {
e.printStackTrace()
false
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/id/walt/signatory/Signatory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class WaltSignatory(configurationPath: String) : Signatory() {
proofPurpose = config.proofPurpose,
config.credentialId ?: "identity#${templateId}#${UUID.randomUUID()}",
issueDate = config.issueDate ?: LocalDateTime.now(),
validDate = config.validDate ?: LocalDateTime.MAX,
validDate = config.validDate ?: LocalDateTime.now(),
expirationDate = config.expirationDate,
dataProviderIdentifier = config.dataProviderIdentifier
)
Expand Down
Loading