Skip to content

Commit

Permalink
Merge pull request #810 from walt-id/98-refactor-openid4vc-library-2
Browse files Browse the repository at this point in the history
provide w3c jwt/sd-jwt credential generation via OpenID4VCI
  • Loading branch information
severinstampler authored Oct 29, 2024
2 parents 48c6b8c + a3def12 commit 3f64fa5
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package id.walt.oid4vc

import cbor.Cbor
import id.walt.credentials.issuance.Issuer.mergingJwtIssue
import id.walt.credentials.issuance.Issuer.mergingSdJwtIssue
import id.walt.credentials.issuance.dataFunctions
import id.walt.credentials.utils.CredentialDataMergeUtils.mergeSDJwtVCPayloadWithMapping
import id.walt.credentials.utils.VCFormat
import id.walt.credentials.vc.vcs.W3CVC
import id.walt.crypto.keys.Key
import id.walt.crypto.keys.jwk.JWKKey
import id.walt.crypto.utils.Base64Utils.base64UrlDecode
Expand Down Expand Up @@ -384,7 +387,7 @@ object OpenID4VCI {
credentialData: JsonObject, dataMapping: JsonObject?,
selectiveDisclosure: SDMap?, vct: String,
issuerDid: String?, issuerKid: String?, x5Chain: List<String>?,
issuerKey: Key): SDJwtVC {
issuerKey: Key): String {
val proofHeader = credentialRequest.proof?.jwt?.let { JwtUtils.parseJWTHeader(it) } ?: throw CredentialError(
credentialRequest, CredentialErrorCode.invalid_or_missing_proof, message = "Proof must be JWT proof"
)
Expand Down Expand Up @@ -430,7 +433,40 @@ object OpenID4VCI {

val jwt = issuerKey.signJws(finalSdPayload.undisclosedPayload.toString().encodeToByteArray(),
headers.mapValues { it.value.toJsonElement() })
return SDJwtVC(SDJwt.createFromSignedJwt(jwt, finalSdPayload))
return SDJwtVC(SDJwt.createFromSignedJwt(jwt, finalSdPayload)).toString()
}

suspend fun generateW3CJwtVC(credentialRequest: CredentialRequest,
credentialData: JsonObject, dataMapping: JsonObject?,
selectiveDisclosure: SDMap?, issuerDid: String?, issuerKid: String?,
x5Chain: List<String>?, issuerKey: Key): String {
val proofHeader = credentialRequest.proof?.jwt?.let { JwtUtils.parseJWTHeader(it) } ?: throw CredentialError(
credentialRequest, CredentialErrorCode.invalid_or_missing_proof, message = "Proof must be JWT proof"
)
val holderKid = proofHeader[JWTClaims.Header.keyID]?.jsonPrimitive?.content
val holderDid = if (!holderKid.isNullOrEmpty() && DidUtils.isDidUrl(holderKid)) holderKid.substringBefore("#") else null
val additionalJwtHeaders = x5Chain?.let {
mapOf("x5c" to JsonArray(it.map { cert -> cert.toJsonElement() }))
} ?: mapOf()
return W3CVC(credentialData).let { vc -> when(selectiveDisclosure.isNullOrEmpty()) {
true -> vc.mergingJwtIssue(
issuerKey = issuerKey,
issuerDid = issuerDid,
issuerKid = issuerKid,
subjectDid = holderDid ?: "",
mappings = dataMapping ?: JsonObject(emptyMap()),
additionalJwtHeader = additionalJwtHeaders,
additionalJwtOptions = emptyMap()
)
else -> vc.mergingSdJwtIssue(
issuerKey = issuerKey,
issuerDid = issuerDid,
subjectDid = holderDid ?: "",
mappings = dataMapping ?: JsonObject(emptyMap()),
additionalJwtHeaders = additionalJwtHeaders,
additionalJwtOptions = emptyMap(),
disclosureMap = selectiveDisclosure
)
}}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@
"fields": {
"name": {
"sd": true
},
"credentialSubject": {
"sd": false,
"children": {
"fields": {
"achievement": {
"sd": false,
"children": {
"fields": {
"name": {
"sd": true
}
},
"decoyMode": "NONE",
"decoys": 0
}
}
},
"decoyMode": "NONE",
"decoys": 0
}
}
},
"decoyMode": "NONE",
"decoys": 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ open class CIProvider(
request.selectiveDisclosure, vct = metadata.credentialConfigurationsSupported?.get(request.credentialConfigurationId)?.vct ?: throw ConfigurationException(
ConfigException("No vct configured for given credential configuration id: ${request.credentialConfigurationId}")
), issuerDid, issuerKid, request.x5Chain, data.issuerKey.key).toString()
else -> w3cSdJwtVc(W3CVC(vc), issuerKid, holderDid, holderKey)
else -> OpenID4VCI.generateW3CJwtVC(credentialRequest, vc, request.mapping, request.selectiveDisclosure,
issuerDid, issuerKid, request.x5Chain, data.issuerKey.key)
}
}.also { log.debug { "Respond VC: $it" } }
}))
Expand Down Expand Up @@ -460,36 +461,6 @@ open class CIProvider(
}
}

private suspend fun IssuanceSessionData.w3cSdJwtVc(
vc: W3CVC,
issuerKid: String,
holderDid: String?,
holderKey: JsonObject?
) = when(request.selectiveDisclosure.isNullOrEmpty()) {
true -> vc.mergingJwtIssue(
issuerKey = issuerKey.key,
issuerDid = issuerDid,
issuerKid = issuerKid,
subjectDid = holderDid ?: "",
mappings = request.mapping ?: JsonObject(emptyMap()),
additionalJwtHeader = emptyMap(),
additionalJwtOptions = emptyMap()
)
else -> vc.mergingSdJwtIssue(
issuerKey = issuerKey.key,
issuerDid = issuerDid,
subjectDid = holderDid ?: "",
mappings = request.mapping ?: JsonObject(emptyMap()),
additionalJwtHeaders = emptyMap(),
additionalJwtOptions = emptyMap(),
disclosureMap = request.selectiveDisclosure
)
}.also {
sendCallback("jwt_issue", buildJsonObject {
put("jwt", it)
})
}

suspend fun getJwksSessions() : JsonObject{
var jwksList = buildJsonObject {
put("keys", buildJsonArray { add(buildJsonObject {
Expand Down

0 comments on commit 3f64fa5

Please sign in to comment.