{"id":22006361,"url":"https://github.com/a-sit-plus/swift-package-kmm-vc-library","last_synced_at":"2026-05-06T05:38:59.931Z","repository":{"id":181775958,"uuid":"607585338","full_name":"a-sit-plus/swift-package-kmm-vc-library","owner":"a-sit-plus","description":"Swift Package for the Kotlin Multiplatform library implementing the W3C VC Data Model ","archived":false,"fork":false,"pushed_at":"2023-11-20T09:32:55.000Z","size":6,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-23T06:43:54.676Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/a-sit-plus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-28T09:17:34.000Z","updated_at":"2024-08-27T12:21:36.000Z","dependencies_parsed_at":"2023-07-17T08:45:06.712Z","dependency_job_id":"a5a62aba-7df8-41a0-a513-94d56f9d931f","html_url":"https://github.com/a-sit-plus/swift-package-kmm-vc-library","commit_stats":null,"previous_names":["a-sit-plus/swift-package-kmm-vc-library"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/a-sit-plus/swift-package-kmm-vc-library","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fswift-package-kmm-vc-library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fswift-package-kmm-vc-library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fswift-package-kmm-vc-library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fswift-package-kmm-vc-library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a-sit-plus","download_url":"https://codeload.github.com/a-sit-plus/swift-package-kmm-vc-library/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-sit-plus%2Fswift-package-kmm-vc-library/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32680889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T02:33:58.958Z","status":"ssl_error","status_checked_at":"2026-05-06T02:33:39.611Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-30T01:11:45.665Z","updated_at":"2026-05-06T05:38:59.913Z","avatar_url":"https://github.com/a-sit-plus.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KMM VC Library Swift Package\n\nThis [Kotlin Mulitplatform](https://kotlinlang.org/docs/multiplatform.html) library implements the [W3C VC Data Model](https://w3c.github.io/vc-data-model/) to support several use cases of verifiable credentials, verifiable presentations, and validation thereof. This library may be shared between Wallet Apps, Verifier Apps and a Backend Service issuing credentials.\n\nThis repository is a Swift Package (see [documentation](https://developer.apple.com/documentation/swift_packages)) to wrap the [kmm-vc-library](https://github.com/a-sit-plus/kmm-vc-library). \n\nThe [Package.swift](Package.swift) contains a remote binary target, which is the framework zipped attached to a Github release, in the form of \u003chttps://github.com/a-sit-plus/kmm-vc-library/releases/download/3.0.0/VcLibKMM-release.xcframework.zip\u003e.\n\nWe'll recommend a few extensions in Swift to make using Kotlin code easier:\n\n```Swift\nimport VcLibKMM\n\n// to avoid nameclash with CyptoKit.Digest when importing both\n// CryptoKit, and VcLibKMM (in other files)\npublic typealias VcLibKMMDigest = Digest\n\nextension Data {\n    public var kotlinByteArray : KotlinByteArray {\n        let bytes = self.bytes\n        let kotlinByteArray = KotlinByteArray(size: Int32(self.count))\n        for index in 0..\u003cbytes.count {\n            kotlinByteArray.set(index: Int32(index), value: bytes[index])\n        }\n        return kotlinByteArray\n    }\n\n    var bytes: [Int8] {\n        return self.map { Int8(bitPattern: $0)}\n    }\n}\n\nextension Int8 {\n    var kotlinByte : KotlinByte {\n        return KotlinByte(value: self)\n    }\n}\n\nextension KotlinByteArray {\n    public var data : Data {\n        var bytes = [UInt8]()\n        for index in 0..\u003cself.size {\n            bytes.append(UInt8(bitPattern: self.get(index: index)))\n        }\n        return Data(bytes)\n    }\n}\n\nfunc KmmResultFailure\u003cT\u003e(_ error: KotlinThrowable) -\u003e KmmResult\u003cT\u003e where T: AnyObject {\n    return KmmResult(failure: error) as! KmmResult\u003cT\u003e\n}\n\nfunc KmmResultSuccess\u003cT\u003e(_ value: T) -\u003e KmmResult\u003cT\u003e where T: AnyObject {\n    return KmmResult(value: value) as! KmmResult\u003cT\u003e\n}\n```\n\nThe `DefaultCryptoService` for iOS should not be used in production as it does not implement encryption, decryption, key agreement and message digests correctly.\n\nA more correct implementation in Swift, using [Apple CryptoKit](https://developer.apple.com/documentation/cryptokit/) would be:\n\n```Swift\nimport Foundation\nimport CryptoKit\n\n// KeyChainService.loadPrivateKey() provides a SecureEnclave.P256.Signing.PrivateKey?\n\npublic class VcLibCryptoServiceCryptoKit: CryptoService {\n\n    public var identifier: String\n    public var jwsAlgorithm: JwsAlgorithm\n    public var coseAlgorithm: CoseAlgorithm\n    public var certificate: KotlinByteArray?\n    private let cryptoPublicKey: CryptoPublicKey\n    private let keyChainService: KeyChainService\n\n    public init?(keyChainService: KeyChainService) {\n        guard let privateKey = keyChainService.loadPrivateKey() else {\n            return nil\n        }\n        self.keyChainService = keyChainService\n        self.cryptoPublicKey = CryptoPublicKey.Ec.companion.fromAnsiX963Bytes(curve: .secp256R1, it: privateKey.publicKey.x963Representation.kotlinByteArray)!\n        self.identifier = cryptoPublicKey.toJsonWebKey().identifier\n        self.jwsAlgorithm = .es256\n        self.coseAlgorithm = .es256\n        self.certificate = nil\n    }\n\n    public func decrypt(key: KotlinByteArray, iv: KotlinByteArray, aad: KotlinByteArray, input: KotlinByteArray, authTag: KotlinByteArray, algorithm: JweEncryption) async throws -\u003e KmmResult\u003cKotlinByteArray\u003e {\n        switch algorithm {\n        case .a256gcm:\n            let key = SymmetricKey(data: key.data)\n            guard let nonce = try? AES.GCM.Nonce(data: iv.data),\n                  let sealedBox = try? AES.GCM.SealedBox(nonce: nonce, ciphertext: input.data, tag: authTag.data),\n                  let decryptedData = try? AES.GCM.open(sealedBox, using: key, authenticating: aad.data) else {\n                return KmmResultFailure(KotlinThrowable(message: \"Error in AES.GCM.open\"))\n            }\n            return KmmResultSuccess(decryptedData.kotlinByteArray)\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"Algorithm unknown \\(algorithm)\"))\n        }\n    }\n\n    public func encrypt(key: KotlinByteArray, iv: KotlinByteArray, aad: KotlinByteArray, input: KotlinByteArray, algorithm: JweEncryption) -\u003e KmmResult\u003cAuthenticatedCiphertext\u003e {\n        switch algorithm {\n        case .a256gcm:\n            let key = SymmetricKey(data: key.data)\n            guard let nonce = try? AES.GCM.Nonce(data: iv.data),\n                  let encryptedData = try? AES.GCM.seal(input.data, using: key, nonce: nonce, authenticating: aad.data) else {\n                return KmmResultFailure(KotlinThrowable(message: \"Error in AES.GCM.seal\"))\n            }\n            let ac = AuthenticatedCiphertext(ciphertext: encryptedData.ciphertext.kotlinByteArray, authtag: encryptedData.tag.kotlinByteArray)\n            return KmmResultSuccess(ac)\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"Algorithm unknown \\(algorithm)\"))\n        }\n    }\n\n    public func generateEphemeralKeyPair(ecCurve: EcCurve) -\u003e KmmResult\u003cEphemeralKeyHolder\u003e {\n        switch ecCurve {\n        case .secp256R1:\n            return KmmResultSuccess(VcLibEphemeralKeyHolder())\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"ecCurve unknown \\(ecCurve)\"))\n        }\n    }\n\n    public func messageDigest(input: KotlinByteArray, digest: VcLibKMMDigest) -\u003e KmmResult\u003cKotlinByteArray\u003e {\n        switch digest {\n        case .sha256:\n            let digest = SHA256.hash(data: input.data)\n            let data = Data(digest.compactMap { $0 })\n            return KmmResultSuccess(data.kotlinByteArray)\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"Digest unknown \\(digest)\"))\n        }\n    }\n\n    public func performKeyAgreement(ephemeralKey: EphemeralKeyHolder, recipientKey: JsonWebKey, algorithm: JweAlgorithm) -\u003e KmmResult\u003cKotlinByteArray\u003e {\n        switch algorithm {\n        case .ecdhEs:\n            let recipientKeyBytes = recipientKey.toAnsiX963ByteArray()\n            if let throwable = recipientKeyBytes.exceptionOrNull() {\n                return KmmResultFailure(throwable)\n            }\n            guard let recipientKeyBytesValue = recipientKeyBytes.getOrNull(),\n                  let recipientKey = try? P256.KeyAgreement.PublicKey(x963Representation: recipientKeyBytesValue.data),\n                  let ephemeralKey = ephemeralKey as? VcLibEphemeralKeyHolder,\n                  let sharedSecret = try? ephemeralKey.privateKey.sharedSecretFromKeyAgreement(with: recipientKey) else {\n                return KmmResultFailure(KotlinThrowable(message: \"Error in KeyAgreement\"))\n            }\n            let data = sharedSecret.withUnsafeBytes {\n                return Data(Array($0))\n            }\n            return KmmResultSuccess(data.kotlinByteArray)\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"Algorithm unknown \\(algorithm)\"))\n        }\n    }\n\n    public func performKeyAgreement(ephemeralKey: JsonWebKey, algorithm: JweAlgorithm) -\u003e KmmResult\u003cKotlinByteArray\u003e {\n        switch algorithm {\n        case .ecdhEs:\n            guard let privateKey = keyChainService.loadPrivateKey() else {\n                return KmmResultFailure(KotlinThrowable(message: \"Could not load private key\"))\n            }\n            let ephemeralKeyBytes = ephemeralKey.toAnsiX963ByteArray()\n            if let throwable = ephemeralKeyBytes.exceptionOrNull() {\n                return KmmResultFailure(throwable)\n            }\n            guard let recipientKeyBytesValue = ephemeralKeyBytes.getOrNull(),\n                  let recipientKey = try? P256.KeyAgreement.PublicKey(x963Representation: recipientKeyBytesValue.data),\n                  let privateAgreementKey = try? SecureEnclave.P256.KeyAgreement.PrivateKey(dataRepresentation: privateKey.dataRepresentation),\n                  let sharedSecret = try? privateAgreementKey.sharedSecretFromKeyAgreement(with: recipientKey) else {\n                return KmmResultFailure(KotlinThrowable(message: \"Error in KeyAgreement\"))\n            }\n            let data = sharedSecret.withUnsafeBytes {\n                return Data($0)\n            }\n            return KmmResultSuccess(data.kotlinByteArray)\n        default:\n            return KmmResultFailure(KotlinThrowable(message: \"Algorithm unknown \\(algorithm)\"))\n        }\n    }\n\n    public func sign(input: KotlinByteArray) async throws -\u003e KmmResult\u003cKotlinByteArray\u003e {\n        guard let privateKey = keyChainService.loadPrivateKey() else {\n            return KmmResultFailure(KotlinThrowable(message: \"Could not load private key\"))\n        }\n        guard let signature = try? privateKey.signature(for: input.data) else {\n            return KmmResultFailure(KotlinThrowable(message: \"Signature error\"))\n        }\n        return KmmResultSuccess(signature.derRepresentation.kotlinByteArray)\n    }\n\n    public func toPublicKey() -\u003e CryptoPublicKey {\n        return cryptoPublicKey\n    }\n    \n}\n\npublic class VcLibVerifierCryptoService : VerifierCryptoService {\n    \n    public func verify(input: KotlinByteArray, signature: KotlinByteArray, algorithm: JwsAlgorithm, publicKey: CryptoPublicKey) -\u003e KmmResult\u003cKotlinBoolean\u003e {\n        if algorithm != .es256 {\n            return KmmResultFailure(KotlinThrowable(message: \"Can not verify algorithm \\(algorithm.name)\"))\n        }\n        if !(publicKey is CryptoPublicKey.Ec) {\n            return KmmResultFailure(KotlinThrowable(message: \"Public key is not an EC key \\(publicKey)\"))\n        }\n        let ansiX963Result = publicKey.toAnsiX963ByteArray()\n        if let throwable = ansiX963Result.exceptionOrNull() {\n            return KmmResultFailure(throwable)\n        }\n        guard let publicKeyBytes = ansiX963Result.getOrNull(),\n            let cryptoKitPublicKey = try? P256.Signing.PublicKey(x963Representation: publicKeyBytes.data) else {\n            return KmmResultFailure(KotlinThrowable(message: \"Can not create CryptoKit key\")) \n        }\n        if let cryptoKitSignature = try? P256.Signing.ECDSASignature(derRepresentation: signature.data) {\n            let valid = cryptoKitPublicKey.isValidSignature(cryptoKitSignature, for: input.data)\n            return KmmResultSuccess(KotlinBoolean(value: valid))\n        } else if let cryptoKitSignature = try? P256.Signing.ECDSASignature(rawRepresentation: signature.data) {\n            let valid = cryptoKitPublicKey.isValidSignature(cryptoKitSignature, for: input.data)\n            return KmmResultSuccess(KotlinBoolean(value: valid))\n        } else {\n            return KmmResultFailure(KotlinThrowable(message: \"Can not read signature\"))\n        }\n    }\n    \n    public func extractPublicKeyFromX509Cert(it: KotlinByteArray) -\u003e JsonWebKey? {\n        guard let certificate = SecCertificateCreateWithData(nil, it.data as CFData),\n              let publicKey = SecCertificateCopyKey(certificate),\n              let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as? Data else {\n            return nil\n        }\n        return JsonWebKey.companion.fromAnsiX963Bytes(type: .ec, curve: .secp256R1, it: publicKeyData.kotlinByteArray)\n    }\n    \n}\n\npublic class VcLibEphemeralKeyHolder : EphemeralKeyHolder {\n    \n    let privateKey: P256.KeyAgreement.PrivateKey\n    let publicKey: P256.KeyAgreement.PublicKey\n    let jsonWebKey: JsonWebKey\n    \n    public init() {\n        self.privateKey = P256.KeyAgreement.PrivateKey()\n        self.publicKey = privateKey.publicKey\n        self.jsonWebKey = JsonWebKey.companion.fromAnsiX963Bytes(type: .ec, curve: .secp256R1, it: publicKey.x963Representation.kotlinByteArray)!\n    }\n    \n    public func toPublicJsonWebKey() -\u003e JsonWebKey {\n        return jsonWebKey\n    }\n    \n}\n```\n\n\nAn example for the implementation of `loadPrivateKey()` would be:\n\n```Swift\n\nextension String: Error {}\n\n    public func loadPrivateKey() -\u003e SecureEnclave.P256.Signing.PrivateKey throws {\n        let flags: SecAccessControlCreateFlags = [.privateKeyUsage]\n        var error: Unmanaged\u003cCFError\u003e?\n        guard let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, flags, \u0026error) else {\n            throw \"cannot create key access flags\"\n        }\n        guard let privateKey = try? SecureEnclave.P256.Signing.PrivateKey(compactRepresentable: true, accessControl: access, authenticationContext: nil) else {\n            throw \"Can not create SecureEnclave key\"\n        }\n        // SecureEnclave keys from CryptoKit shall be stored as \"passwords\"\n        // (their data representation is an encrypted blob)\n        let query = [kSecClass: kSecClassGenericPassword,\n                     kSecAttrAccessible: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,\n                     kSecUseDataProtectionKeychain: true,\n                     kSecAttrLabel: RealKeyChainService.KEY_PAIR_ALIAS,\n                     kSecAttrAccount: RealKeyChainService.ACCOUNT,\n                     kSecValueData: privateKey.dataRepresentation] as [String: Any]\n\n        let status = SecItemAdd(query as CFDictionary, nil)\n        guard status == errSecSuccess else {\n            throw \"unable to store item: \\(status)\"\n        }\n        return privateKey\n    }\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-sit-plus%2Fswift-package-kmm-vc-library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa-sit-plus%2Fswift-package-kmm-vc-library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-sit-plus%2Fswift-package-kmm-vc-library/lists"}