Skip to content

Commit

Permalink
fix: status list credential issuer did (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplotean committed Nov 13, 2023
1 parent f748423 commit d524fd4
Show file tree
Hide file tree
Showing 22 changed files with 358 additions and 249 deletions.
3 changes: 3 additions & 0 deletions service-matrix.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ id.walt.signatory.Signatory=id.walt.signatory.WaltIdSignatory:config/signatory.c
id.walt.custodian.Custodian=id.walt.custodian.WaltIdCustodian
id.walt.auditor.Auditor=id.walt.auditor.WaltIdAuditor
id.walt.services.ecosystems.gaiax.GaiaxService=id.walt.services.ecosystems.gaiax.WaltIdGaiaxService
id.walt.signatory.revocation.statuslist2021.storage.StatusListCredentialStorageService=id.walt.signatory.revocation.statuslist2021.storage.WaltIdStatusListCredentialStorageService
id.walt.signatory.revocation.statuslist2021.index.IndexingStrategy=id.walt.signatory.revocation.statuslist2021.index.IncrementalIndexingStrategy
id.walt.signatory.revocation.statuslist2021.index.StatusListIndexService=id.walt.signatory.revocation.statuslist2021.index.WaltIdStatusListIndexService
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import id.walt.credentials.w3c.VerifiableCredential
import id.walt.credentials.w3c.VerifiablePresentation
import id.walt.model.credential.status.CredentialStatus
import id.walt.signatory.revocation.CredentialStatusCredential
import id.walt.signatory.revocation.RevocationClientService
import id.walt.signatory.revocation.CredentialStatusClientService
import id.walt.signatory.revocation.RevocationStatus
import id.walt.signatory.revocation.TokenRevocationStatus
import java.text.SimpleDateFormat
Expand Down Expand Up @@ -53,7 +53,7 @@ class CredentialStatusPolicy : SimpleVerificationPolicy() {
override fun doVerify(vc: VerifiableCredential): VerificationPolicyResult {
val maybeCredentialStatus = Klaxon().parse<CredentialStatusCredential>(vc.toJson())!!.credentialStatus
return maybeCredentialStatus?.let { cs -> runCatching {
RevocationClientService.check(vc).let {
CredentialStatusClientService.check(vc).let {
if (!it.isRevoked) VerificationPolicyResult.success()
else failResult(it, cs)
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/id/walt/cli/VcCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import id.walt.signatory.ProofConfig
import id.walt.signatory.ProofType
import id.walt.signatory.Signatory
import id.walt.signatory.dataproviders.CLIDataProvider
import id.walt.signatory.revocation.RevocationClientService
import id.walt.signatory.revocation.CredentialStatusClientService
import io.ktor.util.date.*
import io.ktor.util.reflect.*
import mu.KotlinLogging
Expand Down Expand Up @@ -561,7 +561,7 @@ class VcRevocationCheckCommand : CliktCommand(
override fun run() = vcFile.takeIf { it.exists() }?.run {
println("Checking revocation status for credential stored at: ${vcFile.absolutePath}")
runWithErrorHandling(
runner = { RevocationClientService.check(this.readText().toVerifiableCredential()) },
runner = { CredentialStatusClientService.check(this.readText().toVerifiableCredential()) },
onSuccess = {
println("Revocation status:")
println(Klaxon().toJsonString(it).prettyPrint())
Expand All @@ -577,7 +577,7 @@ class VcRevocationRevokeCommand: CliktCommand(
override fun run() = vcFile.takeIf { it.exists() }?.run {
println("Revoking credential stored at: ${vcFile.absolutePath}")
runWithErrorHandling(
runner = { RevocationClientService.revoke(this.readText().toVerifiableCredential()) },
runner = { CredentialStatusClientService.revoke(this.readText().toVerifiableCredential()) },
onSuccess = {
println("Revocation result:")
println(Klaxon().toJsonString(it).prettyPrint())
Expand Down
5 changes: 4 additions & 1 deletion src/main/kotlin/id/walt/common/CommonUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ fun buildRawBitString(bitSet: BitSet): ByteArray{
return builder.toString().toByteArray()
}

fun createEncodedBitString(bitSet: BitSet): ByteArray = Base64.getEncoder().encode(compressGzip(buildRawBitString(bitSet)))
fun createEncodedBitString(bitSet: BitSet = BitSet(16 * 1024 * 8)): ByteArray =
Base64.getEncoder().encode(compressGzip(buildRawBitString(bitSet)))

fun decodeBitSet(bitString: String): BitSet = uncompressGzip(Base64.getDecoder().decode(bitString)).toBitSet(16 * 1024 * 8)

fun createBaseToken() = UUID.randomUUID().toString() + UUID.randomUUID().toString()
fun deriveRevocationToken(baseToken: String): String = Base32.toBase32String(DigestUtils.sha256(baseToken)).replace("=", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import id.walt.model.credential.status.CredentialStatus
import id.walt.signatory.ProofConfig
import id.walt.signatory.Signatory
import id.walt.signatory.SignatoryConfig
import id.walt.signatory.revocation.SimpleCredentialStatusFactory
import id.walt.signatory.revocation.CredentialStatusFactory
import id.walt.signatory.revocation.SimpleStatusFactoryParameter
import id.walt.signatory.revocation.StatusListEntryFactory
import id.walt.signatory.revocation.StatusListEntryFactoryParameter
import id.walt.signatory.revocation.statuslist2021.StatusListCredentialStorageService
import id.walt.signatory.revocation.statuslist2021.StatusListIndexService
import io.ktor.http.*
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
Expand All @@ -24,34 +21,35 @@ import java.time.format.DateTimeFormatterBuilder
class W3CCredentialBuilderWithCredentialStatus<C : VerifiableCredential, B : AbstractW3CCredentialBuilder<C, B>>(
private val builder: AbstractW3CCredentialBuilder<C, B>,
private val proofConfig: ProofConfig,
):AbstractW3CCredentialBuilder<VerifiableCredential, W3CCredentialBuilder>(builder.type, VerifiableCredential){
) : AbstractW3CCredentialBuilder<VerifiableCredential, W3CCredentialBuilder>(builder.type, VerifiableCredential) {

private val statusListEntryFactory = StatusListEntryFactory(
StatusListIndexService.getService(),
StatusListCredentialStorageService.getService(),
)
private val simpleStatusFactory = SimpleCredentialStatusFactory()
private val signatoryConfig = Signatory.getService().configuration as? SignatoryConfig

override fun build(): C = builder.apply {
getStatusProperty(
issuer = proofConfig.issuerDid,
type = proofConfig.statusType!!,
purpose = proofConfig.statusPurpose,
credentialUrl = proofConfig.credentialsEndpoint ?: signatoryConfig?.proofConfig?.credentialsEndpoint ?: ""
credentialUrl = proofConfig.credentialsEndpoint ?: signatoryConfig?.proofConfig?.credentialsEndpoint
?: proofConfig.statusPurpose
)?.let { this.setProperty("credentialStatus", it) }
}.build()

private fun getStatusProperty(type: CredentialStatus.Types, purpose: String, credentialUrl: String) = when (type) {
CredentialStatus.Types.SimpleCredentialStatus2022 -> simpleStatusFactory.create(SimpleStatusFactoryParameter(
id = URLBuilder().takeFrom(credentialUrl).appendPathSegments("token", createBaseToken()).buildString(),
)).asMap()
CredentialStatus.Types.StatusList2021Entry -> statusListEntryFactory.create(StatusListEntryFactoryParameter(
purpose = purpose,
credentialUrl = URLBuilder().takeFrom(credentialUrl).appendPathSegments("status", purpose).buildString(),
)).asMap()
}.takeIf {
it.isNotEmpty()
}
private fun getStatusProperty(issuer: String, type: CredentialStatus.Types, purpose: String, credentialUrl: String) =
when (type) {
CredentialStatus.Types.SimpleCredentialStatus2022 -> SimpleStatusFactoryParameter(
id = URLBuilder().takeFrom(credentialUrl).appendPathSegments("token", createBaseToken()).buildString(),
)
CredentialStatus.Types.StatusList2021Entry -> StatusListEntryFactoryParameter(
purpose = purpose,
credentialUrl = credentialUrl,
issuer = issuer,
)
}.let {
CredentialStatusFactory.create(it).asMap()
}.takeIf {
it.isNotEmpty()
}
}

class W3CCredentialBuilder(type: List<String> = listOf("VerifiableCredential")) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsonLdCredentialService>()

open fun sign(jsonCred: String, config: ProofConfig): String = implementation.sign(jsonCred, config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ 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 documentLoaderService: JsonLdDocumentLoaderService get() = JsonLdDocumentLoaderService.getService()
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import id.walt.signatory.ProofConfig
import id.walt.signatory.ProofType
import id.walt.signatory.Signatory
import id.walt.signatory.dataproviders.MergingDataProvider
import id.walt.signatory.revocation.RevocationClientService
import id.walt.signatory.revocation.CredentialStatusClientService
import id.walt.signatory.revocation.RevocationStatus
import id.walt.signatory.revocation.TokenRevocationStatus
import id.walt.signatory.revocation.simplestatus2022.SimpleCredentialStatus2022StorageService
import id.walt.signatory.revocation.statuslist2021.StatusListCredentialStorageService
import id.walt.signatory.revocation.statuslist2021.storage.StatusListCredentialStorageService
import io.javalin.http.BadRequestResponse
import io.javalin.http.ContentType
import io.javalin.http.Context
Expand Down Expand Up @@ -178,7 +178,7 @@ object SignatoryController {
}.json<RevocationStatus>("200")

fun checkRevoked(ctx: Context) = runCatching {
RevocationClientService.check(ctx.body().toVerifiableCredential())
CredentialStatusClientService.check(ctx.body().toVerifiableCredential())
}.onSuccess {
ctx.json(it)
}.onFailure {
Expand All @@ -193,7 +193,7 @@ object SignatoryController {
}.json<String>("201")

fun revoke(ctx: Context) = runCatching {
RevocationClientService.revoke(ctx.body().toVerifiableCredential())
CredentialStatusClientService.revoke(ctx.body().toVerifiableCredential())
}.onSuccess {
ctx.status(if (it.succeed) HttpCode.OK else HttpCode.NOT_FOUND).json(it.message)
}.onFailure { ctx.status(HttpCode.NOT_FOUND).json(it.localizedMessage) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import id.walt.signatory.revocation.simplestatus2022.SimpleCredentialClientServi
import id.walt.signatory.revocation.statuslist2021.StatusList2021EntryClientService
import kotlinx.serialization.Serializable

interface RevocationClientService {
interface CredentialStatusClientService {
fun checkRevocation(parameter: RevocationCheckParameter): RevocationStatus
fun revoke(parameter: RevocationConfig)
fun create(parameter: CredentialStatusFactoryParameter): CredentialStatus

companion object {
fun revoke(vc: VerifiableCredential): RevocationResult =
Expand All @@ -39,7 +40,7 @@ interface RevocationClientService {
is StatusList2021EntryCredentialStatus -> StatusListRevocationCheckParameter(credentialStatus = credentialStatus)
}

private fun getClient(credentialStatus: CredentialStatus): RevocationClientService = when (credentialStatus) {
private fun getClient(credentialStatus: CredentialStatus): CredentialStatusClientService = when (credentialStatus) {
is SimpleCredentialStatus2022 -> SimpleCredentialClientService()
is StatusList2021EntryCredentialStatus -> StatusList2021EntryClientService()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,16 @@
package id.walt.signatory.revocation

import id.walt.common.createEncodedBitString
import id.walt.model.credential.status.CredentialStatus
import id.walt.model.credential.status.SimpleCredentialStatus2022
import id.walt.model.credential.status.StatusList2021EntryCredentialStatus
import id.walt.signatory.revocation.statuslist2021.StatusListCredentialStorageService
import id.walt.signatory.revocation.statuslist2021.StatusListIndex
import id.walt.signatory.revocation.statuslist2021.StatusListIndexService
import java.util.*
import id.walt.signatory.revocation.simplestatus2022.SimpleCredentialClientService
import id.walt.signatory.revocation.statuslist2021.StatusList2021EntryClientService

interface CredentialStatusFactory {
fun create(parameter: CredentialStatusFactoryParameter): CredentialStatus
}

class SimpleCredentialStatusFactory : CredentialStatusFactory {
override fun create(parameter: CredentialStatusFactoryParameter) = SimpleCredentialStatus2022(
id = (parameter as? SimpleStatusFactoryParameter)?.id ?: ""
)
}

class StatusListEntryFactory(
private val indexService: StatusListIndexService,
private val storageService: StatusListCredentialStorageService,
) : CredentialStatusFactory {
override fun create(parameter: CredentialStatusFactoryParameter) = let {
indexService.read() ?: indexService.create()
}.let {
val statusParameter = parameter as StatusListEntryFactoryParameter
// update index
indexService.update(StatusListIndex(
index = ((it.index.toIntOrNull() ?: 0) + 1).toString()
))
// verify status-credential exists and create one
storageService.fetch(statusParameter.credentialUrl) ?: run {
storageService.store(
statusParameter.credentialUrl,
statusParameter.purpose,
String(createEncodedBitString(BitSet(16 * 1024 * 8)))
)
}
StatusList2021EntryCredentialStatus(
id = statusParameter.credentialUrl + "#${it.index}",
statusPurpose = statusParameter.purpose,
statusListIndex = it.index,
statusListCredential = statusParameter.credentialUrl,
)
object CredentialStatusFactory {
private val simpleStatus = SimpleCredentialClientService()
private val statusList2021 = StatusList2021EntryClientService()
fun create(parameter: CredentialStatusFactoryParameter): CredentialStatus = when (parameter) {
is SimpleStatusFactoryParameter -> simpleStatus.create(parameter)
is StatusListEntryFactoryParameter -> statusList2021.create(parameter)
else -> throw IllegalArgumentException("Status type not supported: ${parameter.javaClass.simpleName}")
}
}

Expand All @@ -54,5 +20,6 @@ data class SimpleStatusFactoryParameter(
) : CredentialStatusFactoryParameter
data class StatusListEntryFactoryParameter(
val credentialUrl: String,
val purpose: String
val purpose: String,
val issuer: String,
) : CredentialStatusFactoryParameter
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package id.walt.signatory.revocation.simplestatus2022

import id.walt.model.credential.status.CredentialStatus
import id.walt.model.credential.status.SimpleCredentialStatus2022
import id.walt.services.WaltIdServices
import id.walt.signatory.revocation.*
import io.ktor.client.*
Expand All @@ -13,7 +15,7 @@ import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging

class SimpleCredentialClientService: RevocationClientService {
class SimpleCredentialClientService: CredentialStatusClientService {

private val logger = KotlinLogging.logger("WaltIdRevocationClientService")
private val credentialStorage = SimpleCredentialStatus2022StorageService
Expand Down Expand Up @@ -50,4 +52,8 @@ class SimpleCredentialClientService: RevocationClientService {
logger.debug { "Revoking at $baseTokenUrl" }
credentialStorage.revokeToken(baseToken)
}

override fun create(parameter: CredentialStatusFactoryParameter): CredentialStatus = SimpleCredentialStatus2022(
id = (parameter as? SimpleStatusFactoryParameter)?.id ?: ""
)
}

0 comments on commit d524fd4

Please sign in to comment.