Swift Package for Multiformat VC

Overview

Swift package for creating Verifiable Credentials (VCs) in multiple formats

Support for mulitple data types for disclosed values including

  • String
  • Int
  • Boolean
  • Array of Strings - [String]
  • Dictionary of String keys and String values - [String:String]

License

Apache License, Version 2.0

Getting Started

Swift Package Manager

Go to File > Swift Packages > Add Package Dependency and add the following URL:

https://github.com/pingidentity/MultiformatVC-iOS

SDJWT

Creating SD JWT format VC

The SDJWT_Writer class is used to create an SD JWT format Verifiable Credential. To instantiate the class you simply provide the signing algorithm and the Key ID

    let vcWriter = SDJWT_Creator(algorithm: "EdDSA", keyId: UUID().uuidString)

You can directly access the payload for defining other claims for the VC

      vcWriter.payload.jwtId = UUID().uuidString
      vcWriter.payload.subject = "did:jwk:ewogICAgImt0eSI6IC...iIKfQo"
      vcWriter.payload.issuer = "did:jwk:ewogICAgImt0eSI6IC...SIKfQo"

Note: issuer and subject edited for brevity

You can specify a credentialSubject for the Verifiable Credential. The credentialSubject is a dictionary with String keys. The values of the dictionary can be of following types

  • String
  • Int
  • Bool
  • Array of Strings - [String]
  • Dictionary of String keys and String values - [String:String]
        vcWriter.setCredentialSubject([
            "name" : "John" ,
            "age": 25,
            "gender" : "male",
            "over_21" : true,
            "roles" : ["user", "reports_admin"]
        ])

You can also provide date claims like expiry, issued at and not before

        vcWriter.issuedAt.expiry = UInt64(Date().timeIntervalSince1970)
        vcWriter.payload.expiry = UInt64(futureDateByAddingMonths(1).timeIntervalSince1970)

Once the instance of the VCJWT_Writer class has been setup you can call the getEncodedVC method to get the SD JWT encoded VC

    try vcWriter.getEncodedVC()

You can provide an optional hashAlgorithm parameter to the getEncodedVC method. The hashAlgorithm parameter accepts an enum HashAlgorithm. This is used to request the getEncodedVC method to use one of the three supported hash algorithms for creating the digests in the _sd claim.

  • sha-256 (default value)
  • sha-384
  • sha-512
    try vcWriter.getEncodedVC(hashAlgorithm: HashAlgorithm.SHA_512)

You can also provide an optional decoyDigests parameter to the getEncodedVC method. The decoyDigests parameter accepts an Integer. This value is used to determine how many extra decoy digests will be created in the _sd claim.

Note: default value is 0

    try vcWriter.getEncodedVC(decoyDigests: 5)

Once the Encoded JWT is created it can be shared with the holder. An example encoded SD JWT based on the above calls is shared here:

eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IjQyMzk3NkQ5LTczQUQtNEJFMC05NjNFLURCRDFDQzI4Qjc0MCJ9.eyJfc2QiOlsiMk5RYzExZjR0V3pNUHg3SGhMRzVaU2NndUlwVExiTTNzZ2x5dWstSEZQdyIsImd6V0p2bEtYdldCQnoyclNtdGZnUjlvYjVFYUhMeE1SdEQ0UmJrcTh6RU0iLCJHUWN2WVIxZFFKa1R4eUJRdThZcE9BZVE2N0RiTmVQOEhTempENS1OY3pzIiwiallidENCa3YxSzR1V1loV1dNSVNmbXYxWHRyTm92aXhtcW5femxzU29xayIsIkV4X0pDRS1jem5oV2VYNGpWcGdLVUw1aU4tYk9DOGxrMFpVcDAtOTE3cDgiXSwiaXNzIjoiZGlkOmp3azpzZGtzYWRma2phc2xma2phc2RmbGtqYWRsZmsiLCJleHAiOjE3MDQ2NTI3MTgsInN1YiI6ImRpZDpqd2s6bGRrZmpzYWxkZmtqc2FkbGZramFzZGZsa2oiLCJfc2RfYWxnIjoic2hhLTI1NiJ9.khYZCYUc1ebfVIq8MyUigxmB~WyJDWkNJT1MwZmtOWmxYNjdIV1BjVjlxeUd1eUlFSXVDdWllT3JnWXRLcS1jIiwiZ2VuZGVyIiwibWFsZSJd~WyJZMjdqVkJLRVFkUTF5OUVXS3FkTEVjbWVmMWNwVWNOQmJvUDFORThuZ0ZjIiwib3Zlcl8yMSIsdHJ1ZV0~WyIzampKNHRSdlNXd3F1ZGpueVJKX2V3MjJKakkxc2Vaa243NEZuMnBkMHc0IiwibmFtZSIsIkpvaG4iXQ~WyJIRUpjWWFUOC1XRnB4aFRUT0t1U2lMblVkdGRaMnc1N0JHQVVPUWZPYllvIiwicm9sZXMiLFsidXNlciIsInJlcG9ydHNfYWRtaW4iXV0~WyItNjUtcXpDbGcyLXJtVFEteWdobnh0aUxXWU12R25HWlhtUVU4ZURMSV9nIiwiYWdlIiwyNV0

To create a signed SD JWT you need to provide the signer method to the SDJWT_Creator instance. Here is an example

    let privateKey = Curve25519.Signing.PrivateKey()
    let signer:(Data) throws -> Data = {(data) in
        return try privateKey.signature(for: data)
    }

When a signer function is provided the output of the getEncodedVC() will look slightly different

eyJ0eXAiOiJ2YytzZC1qd3QiLCJraWQiOiJDNzY1QzZCQi0xOUI0LTQ2QTYtQTcxNC03NjBGRkJEQjJBMTIiLCJhbGciOiJFZERTQSJ9.eyJzdWIiOiJkaWQ6andrOmxka2Zqc2FsZGZranNhZGxma2phc2RmbGtqIiwiaXNzIjoiZGlkOmp3azpzZGtzYWRma2phc2xma2phc2RmbGtqYWRsZmsiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJLU3RmYUNsZTZDMUgxMU56ZW9vTWRaeXBXeU56VXVSSmpubXk5RlFnVGlBIiwiUS1BU3BwTGwwZXpXUTdubGRBUmpSY09IM1BNemJGRUVkMC1UQ0pFMVlJayIsInBRUFJfelVaVDdKY3NlODhEbWpXbnFZZGQ5OUNGb3VtbGR3WXY5cEYyZVUiLCJibGI3QmlWeHpoTmdtMkExMXA0bVItWUc3d2hoNEd0WGQzZWVlSFpwbUNVIiwiRGhWVjZ1TzNHQ190TUF3ajZxNGpqdEo4eUljUHFYQnoteGZac2ptWFhCMCJdLCJleHAiOjE3MDQ0NzY0Njh9.2zeB_qy5uJj1tZbPKIhlOfoO5ECKgTghCFBOVfnY4yWa3HkbbIBEppJwgPLVsFfy8qjjT_p-wyHjPMWJI4bBBQ~WyJKUEwwSnVTOG5FeU1BNWR5ZmthZ2lCYUU1NmhzTnlZcElkdmlRNTVINy0wIiwiZ2VuZGVyIiwibWFsZSJd~WyJHTmZ2WWZtYktCVTVjeEhJYTNRQUV5bl9Da3ZLUVRnUE1ubXo0SmpqdU9vIiwiYWdlIiwyNV0~WyJHeVFDdVZna1pZdGdyV1lUTXRVM0ZfVDBGczRqT2pIVVp4Wk1mZnFILTdvIiwicm9sZXMiLFsidXNlciIsInJlcG9ydHNfYWRtaW4iXV0~WyJteGxxQXE3ZWJDY29sNjZoRU9ya2p3eEIzTl91cnZWcmVPUzdCNTQ5SnlnIiwib3Zlcl8yMSIsdHJ1ZV0~WyJkaHRGaGpVUjhjb1hUcDdpaGJaLU1qMmVvblR4bXFhT3pJakpHVXNXQTk4IiwibmFtZSIsIkpvaG4iXQ

Reading an encoded SD JWT format VC

The SDJWT class is used to read an SD JWT format Verifiable Credential. To instantiate the class you simply encoded SD JWT

    let sdjwt = "eyJ0eXAiOiJ2YytzZC1qd3QiLCJraWQiOiJDNzY1QzZCQi0xOUI0LTQ2QTYtQTcxNC03NjBGRkJEQjJBMTIiLCJhbGciOiJFZERTQSJ9.eyJzdWIiOiJkaWQ6andrOmxka2Zqc2FsZGZranNhZGxma2phc2RmbGtqIiwiaXNzIjoiZGlkOmp3azpzZGtzYWRma2phc2xma2phc2RmbGtqYWRsZmsiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJLU3RmYUNsZTZDMUgxMU56ZW9vTWRaeXBXeU56VXVSSmpubXk5RlFnVGlBIiwiUS1BU3BwTGwwZXpXUTdubGRBUmpSY09IM1BNemJGRUVkMC1UQ0pFMVlJayIsInBRUFJfelVaVDdKY3NlODhEbWpXbnFZZGQ5OUNGb3VtbGR3WXY5cEYyZVUiLCJibGI3QmlWeHpoTmdtMkExMXA0bVItWUc3d2hoNEd0WGQzZWVlSFpwbUNVIiwiRGhWVjZ1TzNHQ190TUF3ajZxNGpqdEo4eUljUHFYQnoteGZac2ptWFhCMCJdLCJleHAiOjE3MDQ0NzY0Njh9.2zeB_qy5uJj1tZbPKIhlOfoO5ECKgTghCFBOVfnY4yWa3HkbbIBEppJwgPLVsFfy8qjjT_p-wyHjPMWJI4bBBQ~WyJKUEwwSnVTOG5FeU1BNWR5ZmthZ2lCYUU1NmhzTnlZcElkdmlRNTVINy0wIiwiZ2VuZGVyIiwibWFsZSJd~WyJHTmZ2WWZtYktCVTVjeEhJYTNRQUV5bl9Da3ZLUVRnUE1ubXo0SmpqdU9vIiwiYWdlIiwyNV0~WyJHeVFDdVZna1pZdGdyV1lUTXRVM0ZfVDBGczRqT2pIVVp4Wk1mZnFILTdvIiwicm9sZXMiLFsidXNlciIsInJlcG9ydHNfYWRtaW4iXV0~WyJteGxxQXE3ZWJDY29sNjZoRU9ya2p3eEIzTl91cnZWcmVPUzdCNTQ5SnlnIiwib3Zlcl8yMSIsdHJ1ZV0~WyJkaHRGaGpVUjhjb1hUcDdpaGJaLU1qMmVvblR4bXFhT3pJakpHVXNXQTk4IiwibmFtZSIsIkpvaG4iXQ"
    let vc = try SDJWT(from: sdjwt)

If the SDJWT constructor successfully returns the class allows access to the disclosures array.

    let disclosures:[String:Any] = vc.disclosures

You can call the verifySignature methods to validate the signature of a signed SD-JWT by calling the verifySignature method of the SDJWT instance with a function to verify the signature

    let privateKey =  Curve25519.Signing.PrivateKey()
    if try vc.verifySignature(verifier: {(signature:Data, data: Data) in
            return keyPair.1.isValidSignature(signature, for: data)
        }) {
          ... // Signature verified successfully
        }

VCJWT

Creating JWT formatted VC

The VCJWT_Writer class is used to create an JWT formatted Verifiable Credential. To instantiate the class you simply provide the signing algorithm and the Key ID

    let vcWriter = VCJWT_Creator(algorithm: "EdDSA", keyId: UUID().uuidString)

You can directly access the payload for defining other claims for the VC

      vcWriter.payload.jwtId = UUID().uuidString
      vcWriter.payload.subject = "did:jwk:ewogICAgImt0eSI6IC...iIKfQo"
      vcWriter.payload.issuer = "did:jwk:ewogICAgImt0eSI6IC...SIKfQo"

Note: issuer and subject edited for brevity

You can specify a credentialSubject for the Verifiable Credential. The credentialSubject is a dictionary with String keys. The values of the dictionary can be of following types

  • String
  • Int
  • Bool
  • Array of Strings - [String]
  • Dictionary of String keys and String values - [String:String]
    vcWriter.setCredentialSubject([
      "name" : "John" ,
      "age": 25,
      "gender" : "male",
      "over_21" : true,
      "roles" : ["user", "reports_admin"]
    ])

You can also provide date claims like expiry, issued at and not before

        vcWriter.payload.issuedAt = UInt64(Date().timeIntervalSince1970)
        vcWriter.payload.expiry = UInt64(futureDateByAddingMonths(1).timeIntervalSince1970)

Once the instance of the SDJWT_Writer class has been setup you can call the getEncodedVC method to get the SD JWT encoded VC

    try vcWriter.getEncodedVC()

Once the Encoded JWT is created it can be shared with the holder. An example encoded SD JWT based on the above calls is shared here:

eyJraWQiOiIxMEYxOEZDRS00QjhGLTQ4RjQtOTIxOS0zRkI0MDg1NjkyMjciLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0IiwianRpIjoiMTgwMzhBODUtM0MxRS00RDQyLTk1RDctOEMzQURFMENFMTc4IiwidmMiOnsidHlwZSI6WyJWZXJpZmllZEVtcGxveWVlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7Im5hbWUiOiJKb2huIiwicm9sZXMiOlsidXNlciIsInJlcG9ydHNfYWRtaW4iXSwiZ2VuZGVyIjoibWFsZSIsImFnZSI6MjUsIm92ZXJfMjEiOnRydWV9fX0.N5125_lpu1uCpd8H2STWEPpBsY6huRzAyVZ-a-Eh6Bwxuh6Wqd7vx6XxjKzR6_YMhJdGuHypRWYD-gzf4jc8CQ

To create a signed SD JWT you need to provide the signer method to the SDJWT_Creator instance. Here is an example

    let privateKey = Curve25519.Signing.PrivateKey()
    let signer:(Data) throws -> Data = {(data) in
        return try privateKey.signature(for: data)
    }

When a signer function is provided the output of the getEncodedVC() will look slightly different

eyJraWQiOiIxMEYxOEZDRS00QjhGLTQ4RjQtOTIxOS0zRkI0MDg1NjkyMjciLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpc3N1ZXIiLCJzdWIiOiJzdWJqZWN0IiwianRpIjoiMTgwMzhBODUtM0MxRS00RDQyLTk1RDctOEMzQURFMENFMTc4IiwidmMiOnsidHlwZSI6WyJWZXJpZmllZEVtcGxveWVlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7Im5hbWUiOiJKb2huIiwicm9sZXMiOlsidXNlciIsInJlcG9ydHNfYWRtaW4iXSwiZ2VuZGVyIjoibWFsZSIsImFnZSI6MjUsIm92ZXJfMjEiOnRydWV9fX0

Reading an encoded JWT format VC

The VCJWT class is used to read an JWT formatted Verifiable Credential. To instantiate the class you simply encoded SD JWT

    let vcjwt = "eyJ0eXAiOiJ2YytzZC1qd3QiLCJraWQiOiJDNzY1QzZCQi0xOUI0LTQ2QTYtQTcxNC03NjBGRkJEQjJBMTIiLCJhbGciOiJFZERTQSJ9.eyJzdWIiOiJkaWQ6andrOmxka2Zqc2FsZGZranNhZGxma2phc2RmbGtqIiwiaXNzIjoiZGlkOmp3azpzZGtzYWRma2phc2xma2phc2RmbGtqYWRsZmsiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJLU3RmYUNsZTZDMUgxMU56ZW9vTWRaeXBXeU56VXVSSmpubXk5RlFnVGlBIiwiUS1BU3BwTGwwZXpXUTdubGRBUmpSY09IM1BNemJGRUVkMC1UQ0pFMVlJayIsInBRUFJfelVaVDdKY3NlODhEbWpXbnFZZGQ5OUNGb3VtbGR3WXY5cEYyZVUiLCJibGI3QmlWeHpoTmdtMkExMXA0bVItWUc3d2hoNEd0WGQzZWVlSFpwbUNVIiwiRGhWVjZ1TzNHQ190TUF3ajZxNGpqdEo4eUljUHFYQnoteGZac2ptWFhCMCJdLCJleHAiOjE3MDQ0NzY0Njh9.2zeB_qy5uJj1tZbPKIhlOfoO5ECKgTghCFBOVfnY4yWa3HkbbIBEppJwgPLVsFfy8qjjT_p-wyHjPMWJI4bBBQ~WyJKUEwwSnVTOG5FeU1BNWR5ZmthZ2lCYUU1NmhzTnlZcElkdmlRNTVINy0wIiwiZ2VuZGVyIiwibWFsZSJd~WyJHTmZ2WWZtYktCVTVjeEhJYTNRQUV5bl9Da3ZLUVRnUE1ubXo0SmpqdU9vIiwiYWdlIiwyNV0~WyJHeVFDdVZna1pZdGdyV1lUTXRVM0ZfVDBGczRqT2pIVVp4Wk1mZnFILTdvIiwicm9sZXMiLFsidXNlciIsInJlcG9ydHNfYWRtaW4iXV0~WyJteGxxQXE3ZWJDY29sNjZoRU9ya2p3eEIzTl91cnZWcmVPUzdCNTQ5SnlnIiwib3Zlcl8yMSIsdHJ1ZV0~WyJkaHRGaGpVUjhjb1hUcDdpaGJaLU1qMmVvblR4bXFhT3pJakpHVXNXQTk4IiwibmFtZSIsIkpvaG4iXQ"
    let vc = try VCJWT(from: sdjwt)

If the VCJWT constructor successfully you can access the credentialSubject from the decoded payload

    let credentialSubject:[String:Any] = vc.getCredentialSubject()

You can call the verifySignature methods to validate the signature of a signed JWT by calling the verifySignature method of the VDJWT instance with a function to verify the signature

    let privateKey =  Curve25519.Signing.PrivateKey()
    if try vc.verifySignature(verifier: {(signature:Data, data: Data) in
            return keyPair.1.isValidSignature(signature, for: data)
        }) {
          ... // Signature verified successfully
        }