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

Fix/ebsi jwt verifiable presentation #101

Merged
merged 4 commits into from
Jan 26, 2022
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
2 changes: 1 addition & 1 deletion src/main/kotlin/id/walt/cli/VcCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class PresentVcCommand : CliktCommand(
val vcStrList = vcSources.values.toList()

// Creating the Verifiable Presentation
val vp = Custodian.getService().createPresentation(vcStrList, holderDid, verifierDid, domain, challenge)
val vp = Custodian.getService().createPresentation(vcStrList, holderDid, verifierDid, domain, challenge, null)

log.debug { "Presentation created:\n$vp" }

Expand Down
34 changes: 25 additions & 9 deletions src/main/kotlin/id/walt/custodian/Custodian.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import id.walt.services.key.KeyService
import id.walt.services.vc.JsonLdCredentialService
import id.walt.services.vc.JwtCredentialService
import id.walt.vclib.model.VerifiableCredential
import id.walt.vclib.model.VerifiableCredential.Companion.fromString
import java.time.Instant

abstract class Custodian : WaltIdService() {
override val implementation get() = serviceImplementation<Custodian>()
Expand All @@ -27,8 +29,13 @@ abstract class Custodian : WaltIdService() {
open fun deleteCredential(alias: String): Boolean = implementation.deleteCredential(alias)

open fun createPresentation(
vcs: List<String>, holderDid: String, verifierDid: String? = null, domain: String? = null, challenge: String? = null
): String = implementation.createPresentation(vcs, holderDid, verifierDid, domain, challenge)
vcs: List<String>,
holderDid: String,
verifierDid: String? = null,
domain: String? = null,
challenge: String? = null
, expirationDate: Instant?
): String = implementation.createPresentation(vcs, holderDid, verifierDid, domain, challenge, expirationDate)

companion object : ServiceProvider {
override fun getService() = object : Custodian() {}
Expand All @@ -42,7 +49,9 @@ open class WaltIdCustodian : Custodian() {
private val jwtCredentialService = JwtCredentialService.getService()
private val jsonLdCredentialService = JsonLdCredentialService.getService()

override fun generateKey(keyAlgorithm: KeyAlgorithm): Key = ContextManager.keyStore.load(keyService.generate(keyAlgorithm).id)
override fun generateKey(keyAlgorithm: KeyAlgorithm): Key =
ContextManager.keyStore.load(keyService.generate(keyAlgorithm).id)

override fun getKey(alias: String): Key = ContextManager.keyStore.load(alias)
override fun listKeys(): List<Key> = ContextManager.keyStore.listKeys()
override fun storeKey(key: Key) = ContextManager.keyStore.store(key)
Expand All @@ -51,15 +60,22 @@ open class WaltIdCustodian : Custodian() {
override fun getCredential(id: String) = ContextManager.vcStore.getCredential(id, VC_GROUP)
override fun listCredentials(): List<VerifiableCredential> = ContextManager.vcStore.listCredentials(VC_GROUP)
override fun listCredentialIds(): List<String> = ContextManager.vcStore.listCredentialIds(VC_GROUP)
override fun storeCredential(alias: String, vc: VerifiableCredential) = ContextManager.vcStore.storeCredential(alias, vc, VC_GROUP)
override fun storeCredential(alias: String, vc: VerifiableCredential) =
ContextManager.vcStore.storeCredential(alias, vc, VC_GROUP)

override fun deleteCredential(alias: String) = ContextManager.vcStore.deleteCredential(alias, VC_GROUP)

override fun createPresentation(
vcs: List<String>, holderDid: String, verifierDid: String?, domain: String?, challenge: String?
): String = when {
vcs.stream().allMatch { VerifiableCredential.isJWT(it) } -> jwtCredentialService.present(vcs, holderDid, verifierDid, challenge)
vcs.stream().noneMatch { VerifiableCredential.isJWT(it) } -> jsonLdCredentialService.present(vcs, holderDid, domain, challenge)
vcs: List<String>,
holderDid: String,
verifierDid: String?,
domain: String?,
challenge: String?,
expirationDate: Instant?
) = when {
vcs.stream().allMatch { VerifiableCredential.isJWT(it) } -> jwtCredentialService.present(vcs, holderDid, verifierDid, challenge, expirationDate)
vcs.stream().noneMatch { VerifiableCredential.isJWT(it) } -> jsonLdCredentialService.present(vcs, holderDid, domain, challenge, expirationDate)
else -> throw IllegalStateException("All verifiable credentials must be of the same proof type.")
}
}.also { fromString(it).also { vp -> storeCredential(vp.id!!, vp) } }
}

3 changes: 2 additions & 1 deletion src/main/kotlin/id/walt/rest/core/VcController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ object VcController {
listOf(presentVcReq.vc),
presentVcReq.holderDid,
presentVcReq.domain,
presentVcReq.challenge
presentVcReq.challenge,
null
)
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/id/walt/rest/custodian/CustodianController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ object CustodianController {

fun presentCredentials(ctx: Context) {
val req = ctx.bodyAsClass<PresentCredentialsRequest>()
ctx.result(custodian.createPresentation(req.vcs, req.holderDid, req.verifierDid, req.domain, req.challenge))
ctx.result(custodian.createPresentation(req.vcs, req.holderDid, req.verifierDid, req.domain, req.challenge, null))
}

fun presentCredentialIdsDocs() = document()
Expand All @@ -202,7 +202,7 @@ object CustodianController {

val ids = req.vcIds.map { custodian.getCredential(it)!!.encode() }

ctx.result(custodian.createPresentation(ids, req.holderDid, req.verifierDid, req.domain, req.challenge))
ctx.result(custodian.createPresentation(ids, req.holderDid, req.verifierDid, req.domain, req.challenge, null))
}

}
11 changes: 9 additions & 2 deletions src/main/kotlin/id/walt/services/vc/JsonLdCredentialService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import id.walt.signatory.ProofConfig
import id.walt.vclib.model.VerifiableCredential
import info.weboftrust.ldsignatures.LdProof
import kotlinx.serialization.Serializable
import java.time.Instant

enum class VerificationType {
VERIFIABLE_CREDENTIAL,
Expand All @@ -26,8 +27,14 @@ abstract class JsonLdCredentialService : WaltIdService() {
open fun verifyVc(vcJson: String): Boolean = implementation.verifyVc(vcJson)
open fun verifyVp(vpJson: String): Boolean = implementation.verifyVp(vpJson)

open fun present(vcs: List<String>, holderDid: String, domain: String?, challenge: String?): String =
implementation.present(vcs, holderDid, domain, challenge)
open fun present(
vcs: List<String>,
holderDid: String,
domain: String?,
challenge: String?,
expirationDate: Instant?
): String =
implementation.present(vcs, holderDid, domain, challenge, expirationDate)

open fun listVCs(): List<String> = implementation.listVCs()

Expand Down
10 changes: 8 additions & 2 deletions src/main/kotlin/id/walt/services/vc/JwtCredentialService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import id.walt.services.WaltIdService
import id.walt.signatory.ProofConfig
import id.walt.vclib.model.VerifiableCredential
import info.weboftrust.ldsignatures.LdProof
import java.time.Instant


abstract class JwtCredentialService : WaltIdService() {
Expand All @@ -19,8 +20,13 @@ abstract class JwtCredentialService : WaltIdService() {
open fun verifyVc(vc: String): Boolean = implementation.verifyVc(vc)
open fun verifyVp(vp: String): Boolean = implementation.verifyVp(vp)

open fun present(vcs: List<String>, holderDid: String, verifierDid: String? = null, challenge: String? = null): String =
implementation.present(vcs, holderDid, verifierDid, challenge)
open fun present(
vcs: List<String>,
holderDid: String,
verifierDid: String? = null,
challenge: String? = null,
expirationDate: Instant? = null
): String = implementation.present(vcs, holderDid, verifierDid, challenge, expirationDate)

open fun listVCs(): List<String> = implementation.listVCs()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import java.net.URI
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.time.Instant
import java.util.*

private val log = KotlinLogging.logger {}
Expand Down Expand Up @@ -270,7 +271,13 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() {
// return verifier.verify(jsonLdObject)
// }

override fun present(vcs: List<String>, holderDid: String, domain: String?, challenge: String?): String {
override fun present(
vcs: List<String>,
holderDid: String,
domain: String?,
challenge: String?,
expirationDate: Instant?
): String {
log.debug { "Creating a presentation for VCs:\n$vcs" }

val id = "urn:uuid:${UUID.randomUUID()}"
Expand All @@ -281,7 +288,8 @@ open class WaltIdJsonLdCredentialService : JsonLdCredentialService() {
proofType = ProofType.LD_PROOF,
domain = domain,
nonce = challenge,
credentialId = id
credentialId = id,
expirationDate = expirationDate
)
val vpReqStr =
VerifiablePresentation(id = id, holder = holderDid, verifiableCredential = vcs.map { it.toCredential() }).encode()
Expand Down
19 changes: 14 additions & 5 deletions src/main/kotlin/id/walt/services/vc/WaltIdJwtCredentialService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ open class WaltIdJwtCredentialService : JwtCredentialService() {
override fun sign(jsonCred: String, config: ProofConfig): String {
log.debug { "Signing JWT object with config: $config" }

val crd = jsonCred.toCredential()
val issuerDid = config.issuerDid
val issueDate = config.issueDate ?: Instant.now()
val validDate = config.validDate ?: Instant.now()
val jwtClaimsSet = JWTClaimsSet.Builder()
.jwtID(config.credentialId)
.issuer(issuerDid)
.subject(config.subjectDid)
.issueTime(Date.from(issueDate))
.notBeforeTime(Date.from(validDate))

Expand All @@ -44,11 +46,10 @@ open class WaltIdJwtCredentialService : JwtCredentialService() {
config.verifierDid?.let { jwtClaimsSet.audience(config.verifierDid) }
config.nonce?.let { jwtClaimsSet.claim("nonce", config.nonce) }

when (val crd = jsonCred.toCredential()) {
when (crd) {
is VerifiablePresentation -> jwtClaimsSet
.claim(JWT_VP_CLAIM, crd.toMap())
else -> jwtClaimsSet
.subject(config.subjectDid)
.claim(JWT_VC_CLAIM, crd.toMap())
}

Expand Down Expand Up @@ -80,18 +81,26 @@ open class WaltIdJwtCredentialService : JwtCredentialService() {
override fun verifyVp(vp: String): Boolean =
verifyVc(vp)

override fun present(vcs: List<String>, holderDid: String, verifierDid: String?, challenge: String?): String {
override fun present(
vcs: List<String>,
holderDid: String,
verifierDid: String?,
challenge: String?,
expirationDate: Instant?
): String {
log.debug { "Creating a presentation for VCs:\n$vcs" }

val id = "urn:uuid:${UUID.randomUUID()}"
val config = ProofConfig(
issuerDid = holderDid,
subjectDid = holderDid,
verifierDid = verifierDid,
proofType = ProofType.JWT,
nonce = challenge,
credentialId = id
credentialId = id,
expirationDate = expirationDate
)
val vpReqStr = VerifiablePresentation(verifiableCredential = vcs.map { it.toCredential() }).encode()
val vpReqStr = VerifiablePresentation(holder = holderDid, verifiableCredential = vcs.map { it.toCredential() }).encode()

log.trace { "VP request: $vpReqStr" }
log.trace { "Proof config: $$config" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package id.walt.services.vcstore
import id.walt.vclib.model.VerifiableCredential
import id.walt.vclib.model.toCredential
import java.io.File
import java.net.URLEncoder
import java.nio.charset.StandardCharsets

open class FileSystemVcStoreService : VcStoreService() {

Expand All @@ -25,7 +27,10 @@ open class FileSystemVcStoreService : VcStoreService() {

override fun listCredentialIds(group: String): List<String> = getGroupDir(group).listFiles()!!.map { it.nameWithoutExtension }

override fun storeCredential(alias: String, vc: VerifiableCredential, group: String) = getFileById(alias, group).writeText(vc.encode())
override fun storeCredential(alias: String, vc: VerifiableCredential, group: String) =
getFileById(
URLEncoder.encode(alias, StandardCharsets.UTF_8), group
).writeText(vc.encode())

override fun deleteCredential(alias: String, group: String) = getFileById(alias, group).delete()
}
4 changes: 2 additions & 2 deletions src/test/kotlin/id/walt/auditor/AuditorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class AuditorCommandTest : StringSpec() {
)

vpStr =
custodian.createPresentation(listOf(vcStr), did, did, "https://api.preprod.ebsi.eu", "d04442d3-661f-411e-a80f-42f19f594c9d")
custodian.createPresentation(listOf(vcStr), did, did, "https://api.preprod.ebsi.eu", "d04442d3-661f-411e-a80f-42f19f594c9d", null)

vcJwt = signatory.issue(
"VerifiableDiploma", ProofConfig(
Expand All @@ -58,7 +58,7 @@ class AuditorCommandTest : StringSpec() {
DummySignatoryDataProvider()
)

vpJwt = custodian.createPresentation(listOf(vcJwt), did, did, null, "abcd")
vpJwt = custodian.createPresentation(listOf(vcJwt), did, did, null, "abcd", null)
}

init {
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/id/walt/cli/VcVerifyCommandTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class VcVerifyCommandTest : StringSpec({
val vcStr = Signatory.getService().issue(
"VerifiableDiploma", ProofConfig(issuerDid = did, subjectDid = did, issuerVerificationMethod = "Ed25519Signature2018")
)
val vpStr = JsonLdCredentialService.getService().present(listOf(vcStr), did, "https://api.preprod.ebsi.eu", "d04442d3-661f-411e-a80f-42f19f594c9d")
val vpStr = JsonLdCredentialService.getService().present(listOf(vcStr), did, "https://api.preprod.ebsi.eu", "d04442d3-661f-411e-a80f-42f19f594c9d", null)
val vpFile = File.createTempFile("vpr", ".json")
try {
vpFile.writeText(vpStr)
Expand All @@ -52,7 +52,7 @@ class VcVerifyCommandTest : StringSpec({
val vcJwt = Signatory.getService().issue(
"VerifiableDiploma", ProofConfig(issuerDid = did, subjectDid = did, issuerVerificationMethod = "Ed25519Signature2018", proofType = ProofType.JWT)
)
val vpJwt = Custodian.getService().createPresentation(listOf(vcJwt), did, did, null ,"abcd")
val vpJwt = Custodian.getService().createPresentation(listOf(vcJwt), did, did, null ,"abcd", null)
val vpFile = File.createTempFile("vpr", ".jwt")
try {
vpFile.writeText(vpJwt)
Expand Down
10 changes: 5 additions & 5 deletions src/test/kotlin/id/walt/custodian/CustodianPresentTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class CustodianPresentTest : StringSpec() {

init {
"Json ld presentation" {
val presStr = Custodian.getService().createPresentation(listOf(vcJsonLd), did, did, null, null)
val presStr = Custodian.getService().createPresentation(listOf(vcJsonLd), did, did, null, null, null)
println("Created VP: $presStr")

val pres = presStr.toCredential()
Expand All @@ -58,7 +58,7 @@ class CustodianPresentTest : StringSpec() {
}

"Jwt presentation" {
val presStr = Custodian.getService().createPresentation(listOf(vcJwt), did, did, null, "abcd")
val presStr = Custodian.getService().createPresentation(listOf(vcJwt), did, did, null, "abcd", null)
println("Created VP: $presStr")

checkVerifiablePresentation(presStr)
Expand All @@ -67,7 +67,7 @@ class CustodianPresentTest : StringSpec() {
"Jwt presentation without audience or nonce" {
val presStr = Custodian
.getService()
.createPresentation(listOf(vcJwt), did, null, null, "abcd")
.createPresentation(listOf(vcJwt), did, null, null, "abcd", null)
println("Created VP: $presStr")

checkVerifiablePresentation(presStr)
Expand All @@ -76,7 +76,7 @@ class CustodianPresentTest : StringSpec() {
"Jwt presentation without nonce" {
val presStr = Custodian
.getService()
.createPresentation(listOf(vcJwt), did, did, null)
.createPresentation(listOf(vcJwt), did, did, null, null, null)
println("Created VP: $presStr")

checkVerifiablePresentation(presStr)
Expand All @@ -86,7 +86,7 @@ class CustodianPresentTest : StringSpec() {
assertThrows<IllegalStateException> {
Custodian
.getService()
.createPresentation(listOf(vcJsonLd, vcJwt), did, did, null, "abcd")
.createPresentation(listOf(vcJsonLd, vcJwt), did, did, null, "abcd", null)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/id/walt/essif/EssifIntTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class EssifIntTest() : StringSpec({
val vcStrList = src.stream().map { vc -> vc.readText() }.collect(Collectors.toList())

// Creating the Verifiable Presentation
val vp = Custodian.getService().createPresentation(vcStrList, holderDid, null, null, null)
val vp = Custodian.getService().createPresentation(vcStrList, holderDid, null, null, null, null)

println("Verifiable presentation generated for holder DID: \"$holderDid\"")
println("Verifiable presentation document (below, JSON):\n\n$vp")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class WaltIdJsonLdCredentialServiceTest : AnnotationSpec() {
vcVerified.verificationType shouldBe VerificationType.VERIFIABLE_CREDENTIAL

val holderDid = vc.subject!!
val vpStr = credentialService.present(listOf(vcStr), holderDid, "domain.com", "nonce")
val vpStr = credentialService.present(listOf(vcStr), holderDid, "domain.com", "nonce", null)
println("Presentation generated: $vpStr")

val vp = vpStr.toCredential() as VerifiablePresentation
Expand Down Expand Up @@ -109,7 +109,7 @@ class WaltIdJsonLdCredentialServiceTest : AnnotationSpec() {
fun presentVa() {
val vaStr = File("$VC_PATH/vc-ebsi-verifiable-authorisation.json").readText()

val vp = credentialService.present(listOf(vaStr), vaStr.toCredential().subject!!, null, null)
val vp = credentialService.present(listOf(vaStr), vaStr.toCredential().subject!!, null, null, null)

println(vp)
}
Expand All @@ -131,7 +131,7 @@ class WaltIdJsonLdCredentialServiceTest : AnnotationSpec() {
val vcSigned = vc.toCredential()
println(vcSigned.toString())

val vp = credentialService.present(listOf(vc), vcSigned.subject!!, domain, challenge)
val vp = credentialService.present(listOf(vc), vcSigned.subject!!, domain, challenge, null)
println("Presentation generated: $vp")

val vpVerified = credentialService.verifyVp(vp)
Expand Down
Loading