a-sit-plus / kmm-vc-library

Kotlin Multiplatform library implementing W3C VC Data Model and ISO 18013-5 mDL
https://a-sit-plus.github.io/kmm-vc-library/
Apache License 2.0
15 stars 1 forks source link

KMM VC Library

A-SIT Plus Official GitHub license Kotlin Kotlin Java Maven Central

This library implements verifiable credentials to support several use cases, i.e. issuing of credentials, presentation of credentials and validation thereof. This library may be shared between backend services issuing credentials, wallet apps holding credentials, and verifier apps validating them.

Credentials may be represented in the W3C VC Data Model or as mobile driving licences from ISO/IEC 18013-5:2021. Issuing may happen according to ARIES RFC 0453 Issue Credential V2 or with OpenID for Verifiable Credential Issuance. Presentation of credentials may happen according to ARIES RFC 0454 Present Proof V2 or with Self-Issued OpenID Provider v2.

Architecture

This library was built with Kotlin Multiplatform and Multiplatform Mobile in mind. Its primary targets are JVM, Android and iOS. In order to achieve smooth usage especially under iOS, there have been some notable design decisions:

Notable features for multiplatform are:

The main entry point for applications is an instance of HolderAgent, VerifierAgent or IssuerAgent, according to the nomenclature from the W3C VC Data Model.

Many classes define several constructor parameters, some of them with default values, to enable a simple form of dependency injection. Implementers are advised to specify the parameter names of arguments passed to increase readability and prepare for future extensions.

Aries

A single run of an ARIES protocol (for issuing or presentation) is implemented by the *Protocol classes, whereas the *Messenger classes should be used by applications to manage several runs of a protocol. These classes reside in the artifact vclib-aries.

OpenId

For SIOPv2 see OidcSiopProtocol, and for OpenId4VCI see at.asitplus.wallet.lib.oidvci.WalletService. Most code resides in the artifact/subdirectory vclib-openid. Both protocols are able to transport W3C credentials (any form) and ISO credentials (mobile driving licence).

Limitations

iOS Implementation

The DefaultCryptoService for iOS should not be used in production as it does not implement encryption, decryption, key agreement and message digests correctly. See the Swift Package for details on a more correct iOS implementation.

Credentials

A single credential itself is an instance of CredentialSubject and has no special meaning attached to it. This library implements AtomicAttribute2023 as a trivial sample of a custom credential.

Other libraries using this library may subclass CredentialSubject and call LibraryInitializer.registerExtensionLibrary() to register that extension with this library:

@kotlinx.serialization.Serializable
@kotlinx.serialization.SerialName("YourCredential2023")
class YourCredential : at.asitplus.wallet.lib.data.CredentialSubject {
    // custom properties
    @SerialName("firstname")
    val firstname: String

    constructor(id: String, firstname: String) : super(id = id) {
        this.firstname = firstname
    }

    override fun toString(): String {
        return "YourCredential(id='$id', firstname='$firstname')"
    }
}

at.asitplus.wallet.lib.LibraryInitializer.registerExtensionLibrary(
    at.asitplus.wallet.lib.LibraryInitializer.ExtensionLibraryInfo(
        credentialScheme = object : at.asitplus.wallet.lib.data.ConstantIndex.CredentialScheme {
            override val schemaUri: String = "https://example.com/schemas/1.0.0/yourcredential.json"
            override val vcType: String = "YourCredential2023"
            override val isoNamespace: String = "com.example.your-credential"
            override val isoDocType: String = "com.example.your-credential.iso"
        },
        serializersModule = kotlinx.serialization.modules.SerializersModule {
            kotlinx.serialization.modules.polymorphic(CredentialSubject::class) {
                kotlinx.serialization.modules.subclass(YourCredential::class)
            }
        },
    )
)

Representation

Credentials in the form of the W3C VC Data Model may be represented as a plain JWT (with simple ECDSA signatures in it), or as a Selective Disclosure JWT. See ConstantIndex.CredentialRepresentation for the enum class in this library.

SD-JWT

To transport the information of SD-JWTs across our several protocols, we came up with the string jwt_vc_sd for use in OpenId protocols, see implementation CredentialFormatEnum.

We also attach revocation information to the SD-JWT by adding a member called credentialStatus to it, same as for a VC represented as a plain JWT.

There are several limitations with our implementation of this early draft of SD-JWT:

Example from AgentSdJwtTest, where a simple credential with name, value and mime-type (meaning three disclosures) is issued:

eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6bUVwRGVlRmc1eXc0cWI0VHA4ek1QN2JlWXBWS2lUMkk2VTZ3TWlYNjl2S0VRWHV0cUx3NXVZaEVMdXhxVVVTRDhNZFR6cEN6emRtS0hoWG5COGZ5Y2tlSUgiLCJ0eXAiOiJqd3QifQ
.eyJzdWIiOiJkaWQ6a2V5Om1FcENmR0E2NVprMCtHS2pOQUNweGdVOVhSNitza2tpeEdOLzV6RUpjUU0wVkVXbTlFRkJVK0l2dnE1bTdYNHJyKytIa3pqT1Q2N0NreTNZSk42TVA2bEM2IiwibmJmIjoxNjk4MDgyMjEyLCJpc3MiOiJkaWQ6a2V5Om1FcERlZUZnNXl3NHFiNFRwOHpNUDdiZVlwVktpVDJJNlU2d01pWDY5dktFUVh1dHFMdzV1WWhFTHV4cVVVU0Q4TWRUenBDenpkbUtIaFhuQjhmeWNrZUlIIiwiZXhwIjoxNjk4MDgyMjcyLCJqdGkiOiJ1cm46dXVpZDo0YjJjMWNjZC0zYTRhLTQ0OTItODZlZi1hZDM3YWE2MWM2OTYiLCJfc2QiOlsieTFfU3p4NGg0aEh2TG5TMS1pRGtKZ3hOV0x1Z1pFRF93ejhkUER1S2hlUT0iLCJfV0pVSE5EZlIxWkI3YjdJbFVsNl95dkxURlJ5V2JlRENpdDAzNTlXaHlvPSIsIjRNQ192Q1M5WDhpM2UzZzBWN2hpRTk0VHBPTlVoZWdydDJ2Y0VSQ3hzd289Il0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJBdG9taWNBdHRyaWJ1dGUyMDIzIl0sIl9zZF9hbGciOiJzaGEtMjU2In0
.782vK3v64-RWD2NLLRiXkmKTwvcup6-Sph7FLMCjXE6c41E9bWsBTI1ICFo1gC9Igud1mus7Zdphmm5lpxalDw
~WyJvell4TXVLbkVOZHlHR1MxOThqUDFobWdaUjlMVXhnUHRJb2NUa0xKcG9vIiwibmFtZSIsImdpdmVuLW5hbWUiXQ
~WyJtY0lMNkxBTmZJbVdIb2FrNFQxNG5PRnV1SUxXY0ZicTJLblo3QmhwMmM4IiwidmFsdWUiLCJTdXNhbm5lIl0
~WyIxQ1pfTXBFbEVjeFRwaEYtNmxLOHZvZmgtSnJuWkwzS0NWdkxDQXl0eTB3IiwibWltZS10eXBlIiwiYXBwbGljYXRpb24vdGV4dCJd

The JWT payload of the VC has no visible attributes, only the _sd entry (parsed from the JWT printed above):

{
  "sub": "did:key:mEpCfGA65Zk0+GKjNACpxgU9XR6+skkixGN/5zEJcQM0VEWm9EFBU+Ivvq5m7X4rr++HkzjOT67Cky3YJN6MP6lC6",
  "nbf": 1698082212,
  "iss": "did:key:mEpDeeFg5yw4qb4Tp8zMP7beYpVKiT2I6U6wMiX69vKEQXutqLw5uYhELuxqUUSD8MdTzpCzzdmKHhXnB8fyckeIH",
  "exp": 1698082272,
  "jti": "urn:uuid:4b2c1ccd-3a4a-4492-86ef-ad37aa61c696",
  "_sd": [
    "y1_Szx4h4hHvLnS1-iDkJgxNWLugZED_wz8dPDuKheQ=",
    "_WJUHNDfR1ZB7b7IlUl6_yvLTFRyWbeDCit0359Whyo=",
    "4MC_vCS9X8i3e3g0V7hiE94TpONUhegrt2vcERCxswo="
  ],
  "type": [
    "VerifiableCredential",
    "AtomicAttribute2023"
  ],
  "_sd_alg": "sha-256"
}

The disclosures are stored by the holder to reveal them later on when requested, i.e. for name (parsed from the first disclosure printed above):

[
  "tC93k39JMbjrmJOfQUXhoQpz7Xv7NPjCHOws6dQwrtU", // salt
  "name",                                        // key
  "given-name"                                   // value
]

The presentation from the holder to the verifier, disclosing the item name to have the value given-name is the following: the JWT is the same as issued, one disclosure is appended and a key binding JWT to prove possession of the holder key:

eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6bUVwRGVlRmc1eXc0cWI0VHA4ek1QN2JlWXBWS2lUMkk2VTZ3TWlYNjl2S0VRWHV0cUx3NXVZaEVMdXhxVVVTRDhNZFR6cEN6emRtS0hoWG5COGZ5Y2tlSUgiLCJ0eXAiOiJqd3QifQ
.eyJzdWIiOiJkaWQ6a2V5Om1FcENmR0E2NVprMCtHS2pOQUNweGdVOVhSNitza2tpeEdOLzV6RUpjUU0wVkVXbTlFRkJVK0l2dnE1bTdYNHJyKytIa3pqT1Q2N0NreTNZSk42TVA2bEM2IiwibmJmIjoxNjk4MDgyMjEyLCJpc3MiOiJkaWQ6a2V5Om1FcERlZUZnNXl3NHFiNFRwOHpNUDdiZVlwVktpVDJJNlU2d01pWDY5dktFUVh1dHFMdzV1WWhFTHV4cVVVU0Q4TWRUenBDenpkbUtIaFhuQjhmeWNrZUlIIiwiZXhwIjoxNjk4MDgyMjcyLCJqdGkiOiJ1cm46dXVpZDo0YjJjMWNjZC0zYTRhLTQ0OTItODZlZi1hZDM3YWE2MWM2OTYiLCJfc2QiOlsieTFfU3p4NGg0aEh2TG5TMS1pRGtKZ3hOV0x1Z1pFRF93ejhkUER1S2hlUT0iLCJfV0pVSE5EZlIxWkI3YjdJbFVsNl95dkxURlJ5V2JlRENpdDAzNTlXaHlvPSIsIjRNQ192Q1M5WDhpM2UzZzBWN2hpRTk0VHBPTlVoZWdydDJ2Y0VSQ3hzd289Il0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJBdG9taWNBdHRyaWJ1dGUyMDIzIl0sIl9zZF9hbGciOiJzaGEtMjU2In0
.782vK3v64-RWD2NLLRiXkmKTwvcup6-Sph7FLMCjXE6c41E9bWsBTI1ICFo1gC9Igud1mus7Zdphmm5lpxalDw
~WyJvell4TXVLbkVOZHlHR1MxOThqUDFobWdaUjlMVXhnUHRJb2NUa0xKcG9vIiwibmFtZSIsImdpdmVuLW5hbWUiXQ
~eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6bUVwQ2ZHQTY1WmswK0dLak5BQ3B4Z1U5WFI2K3Nra2l4R04vNXpFSmNRTTBWRVdtOUVGQlUrSXZ2cTVtN1g0cnIrK0hrempPVDY3Q2t5M1lKTjZNUDZsQzYiLCJ0eXAiOiJrYitqd3QifQ
.eyJpYXQiOjE2OTgwODIyMTMsImF1ZCI6ImRpZDprZXk6bUVwREcwQy9IOUpRRE1za0hreDZ1SW1wajkwRWpEaDlTWkQ0byt1bHRFK3pOeGRpTHc3QzRJSWJsd1ppWlN1Tnl3ekdQOElWR3N6Yk1SNjREMlFRa2dyN2oiLCJub25jZSI6Ijg3YmViMjJjLTNiYzUtNGI1ZC1hZDIwLTFhMTZkY2ViNTZiZiJ9
.JJ5Y0ZAj44dyPRxbt4K3ws_PKFchcsZUukLRQWbx22KuQUEUQf12r3rgYqsGV3yVmXO-D-NnsaP-1iAmgNy4GQ



This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 959072.

EU flag

The Apache License does not apply to the A-SIT logo, as it is the sole property of A-SIT/A-SIT Plus GmbH and may not be used without explicit permission!