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: status list credential issuer did #357

Merged
merged 3 commits into from
Nov 13, 2023
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: 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 ?: ""
)
}
Loading