Self-sovereign identity on Corda
Introduction
This blog aims to introduce the reader to the concept of natively implementing self-sovereign identity on Corda networks. The implementation of this work is open-source and freely available on the R3 marketplace for anyone to use or build upon.
Self-sovereign identity (SSI) is a model for managing digital identities in which individuals have sole ownership over their identity and the ability to control their personal data. Most of today’s identity management systems are either siloed or federated. In the siloed model, users authenticate themselves to a service with a username and a password that is created during a registration step with the service. The username and hash of a user’s password are saved into a database on the server side. Each application has its own database with usernames and passwords. The siloed identity is only valid for that application and with the entity with which the username was created. Therefore, the user must create a new username and password for each application or website that he interacts with. In this type of system, it is difficult for users to remember all the usernames/passwords used across different applications. Many users therefore use the same username and password for multiple applications. If a data breach occurs in any application, it can lead to users’ identities being compromised, thereby allowing attackers to consequently gain control of multiple applications with the same username and password combination.
A federated identity system overcomes the difficulty of creating separate usernames and passwords for each website as needed with the siloed authentication model. The federated model allows users to use a single username and password across multiple applications and websites via a federated identity provider. This involves an Identity provider (IDP) that manages the identities of the users centrally inside its database. A third-party application can determine the identity of the user by redirecting him to an IDP login. Once authenticated by the IDP, the user may proceed with using the services of the application. The above approach saves the user the need to have multiple usernames and passwords. The problem with this approach is that the IDP saves all the user information. If an IDP is hacked, all associated accounts may also be compromised. The Identity provider can also track which applications the user interacts with. If the identity provider decides to delete/block a user’s account he will not be able to login and interact with applications using the federated identity.
Self-sovereign identity tries to solve the problems with the above approaches. To understand how self-sovereign identity works, we must first understand the concept of a PKI (public key infrastructure) as the self-sovereign identity systems makes use of PKI for identifying parties on the network.
PKI system uses cryptographic keypairs (a private key and a public key). A message encrypted with a public key can only be decrypted with the corresponding private key and a message signed with private key can only be verified using the corresponding public key. The PKI system has Certificate Authorities (CAs) to identify the owner of a public/private keypair. This Certificate Authority is centralized and if it mistakenly issued a certificate or is hacked, it can lead to major problems. For example, a fraudulent website can obtain a certificate from a compromised CA and masquerade as a legitimate website.
A self-sovereign identity system is built on a blockchain which acts like a decentralized Certificate Authority, thus reducing dependence on large central entities. In this ecosystem, the data about the user’s identity is stored only on his device and controlled solely by him using a private key. The user generates a cryptographic keypair on his device (preferably a mobile device) along with Decentralized Identifier (DID) which will be used to uniquely identify the user. The DID and the public key are then registered on the blockchain network. Any party that wishes to find the public key for the DID can simply query the blockchain and retrieve the public key. Please refer this link for more information.
Corda DID Method Overview
The Corda DID method is an implementation of the self-sovereign identity on the Corda ledger. It allows for creation, update, lookup and deletion of decentralized identities. The Corda DID method follows the w3 spec for decentralized identifiers v1.0 guidelines in implementation of these methods.
An identity registry is used to maintain a list of public keys belonging to a particular DID. Whenever a user wants to verify a signature of a party or encrypt a message to be sent to a party, he can do so by fetching the public key associated with that party’s DID.
The Corda DID method provides a REST interface to enable communication between the user’s device/wallet and the Corda self-sovereign identity solution. The wallet can use the REST APIs to create their identity, read, replace or add keys to their identity and delete identity.
Architecture
Corda DID method defines “witness nodes” which act as Trust Anchors on the network, only the witness nodes can create, update and delete identities from the network. Other parties that wish to interact with the witness nodes must do so using REST APIs to the webserver of the witness nodes.
Figure 1 shows the architecture of the Corda DID method and workflow for a create identity use case. To create an identity, the client first generates a keypair locally on his device .The device also generates a RFC4122 based unique identifier which is then combined with a DID prefix and used as an identifier for the user.
Example did:corda:org-pvt:3df6b0a1–6b02–4053–8900–8c36b6d35fa1
A DID document (DDO) is created and the public key that was generated earlier along with the DID is embedded inside the document. An instruction payload is created that contain the necessary commands for the Corda backend to create an identity. The DID document (DDO) containing the public key is signed using a private key and embedded in the instruction.
The document and instruction are then posted to a REST API, the API will invoke a Corda flow for creating the identity .The Corda flow sends the transaction to all the witness nodes on the network and a Corda smart contract verifies signature on the document using the public key embedded inside the document and the entry linking the DID to the public key is made to the ledger.
The signatures for the REST APIs are shown in a later section below.
DID
The DID or decentralized identifier is used to uniquely identify a user (owner of a keypair) on the network. Since there is no centralized database to keep track of all the identities, we need to ensure that identifiers are unique when generated in a decentralized way. For this the Corda DID method makes use of UUID with the standard RFC4122.
The DID has a format did:<platform>:<network>:<unique_identifier>
A Corda DID would look like the following:
did:corda:livenet:3df6b0a1–6b02–4053–8900–8c36b6d35fa1
The “did” part specifies that it is a decentralized identifier.
The “corda” part specifies that it can be resolved using Corda DID method.
Corda DID method allows you to run in the setup in a custom network. In the example above it is Corda test network and signified by the string “livenet”.
The unique identifier is the RFC4122 based UUID.
DID Document
The DID document used in the Corda DID method looks like the following
The primary purpose of the document object is to present the public key(s) associated with a DID. In the example above we can see that the public key being stored in this document is an ECDSA key and it is encoded using base58 encoding. The public key data structure is an array indicates the user can have multiple keys associated with a single DID. The DID document is stored on the ledger.
Instruction
The wallet/holder will communicate with the Corda DID layer via a REST API and as such it is vital to ensure the identity of the user that is invoking these APIs. To achieve this and still retain decentralization, we do make use of the instruction object.
Take the case of identity creation
1. The Holder generates a keypair(public key and private key).
2. The Holder creates the document as shown in the section above.
3. The Holder will then create an instruction object and include a signature on the document.
4. This would then be posted to the API server and then passed on to the Corda flow and ultimately to the Corda contract which then validate the signature on the instruction against the public key in the document. If the verification is successful, the identity is created.
In the case of update or delete, current document is queried from the ledger to fetch public key to match signatures on instruction.
Example:
{
“action” : “create” ,
“signatures” : [
{ “id” : “ did:corda:tcn:2ce7cef6–6948–4d71-a6ab-dbd8096050fb#keys-1” ,
“type” : “Ed25519Signature2018” ,
“signatureBase58” :“2M12aBn5ijmmUyHtTf56NTJsUEUbpbqbAgpsvxsfMa2KrL5MR5rGb4dP37QoyRWp94kqreDMV9P4K3QHfE6 7ypTD”
}
]
}
In the example above we see the instruction object which contains an action and an array of signatures. The signatures include type of signature and the encoding used on the signatures as well as the encoded signature.
API
The API follows the standard REST format. There are APIs for create, read update and delete. Below are a few examples.
CREATE
curl -X PUT \ http://example.org/did:corda:tcn:a609bcc0-a3a8-11e9-b949-fb002eb572a5 \
-H ‘content-type: multipart/form-data’ \
-F instruction=’{
“action”: “create”,
“signatures”: [
{
“id”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys1”,
“type”: “Ed25519Signature2018”,
“signatureBase58”: “2M12aBn5ijmmUyHtTf56NTJsUEUbpbqbAgpsvxsfMa2KrL5MR5rGb4dP37QoyRWp94kqreDMV9P4K3QHfE6 7ypTD”
}
]
}’ \
-F document’= {
“@context”: “https://w3id.org/did/v1",
“id”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5”,
“created”:”2019–07–11T10:27:27.326Z”,
“publicKey”: [
{
“id”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-1”,
“type”: “Ed25519VerificationKey2018”,
“controller”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5”,
“publicKeyBase58”: “GfHq2tTVk9z4eXgyNRg7ikjUaaP1fuE4Ted3d6eBaYSTxq9iokAwcd16hu8v”
}
]
}’
Response:
● The API will respond with status 200 for a request with a well-formed instruction and a well-formed document and valid signature(s) and an unused ID.
● The API will respond with status 400 for a request with a deformed instruction or a deformed document or at least one invalid signature.
● The API will respond with status 409 for a request with an DID that is already taken.
UPDATE
curl -X POST \ http://example.org/did:corda:tcn:a609bcc0-a3a8-11e9-b949-fb002eb572a5 \
-H ‘content-type: multipart/form-data’ \
-F instruction=’{
“action”: “update”,
“signatures”: [
{
“id”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-1",
“type”:”Ed25519Signature2018",
“signatureBase58”: ”57HQXkem7pXpfHnP3DPTyLqSQB9NuZNj7V4hS61kbkQA28hCuYtSmFQCABj8HBX2Am Dss13iDkNY2H3zqRZsYnD4"
},
{
“id”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-2",
“type”:”Ed25519Signature2018",
“signatureBase58”: ”26kkhZbQLSNvEKbPvx18GRfSoVMu2bDXutvnWcQQyrGxqz5VKijkFV2GohbkbafPa2WqV ad7wnyLwx1zxjvVfvSa”
}
]
}’ \
-F document’= {
“@context”: “https://w3id.org/did/v1",
“id”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5”,
“created”:”2019–07–11T10:27:27.326Z”,
“updated”:”2019–07–11T10:29:15.116Z”,
“publicKey”: [
{
“id”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-2”,
“type”: “Ed25519VerificationKey2018”,
“controller”: “did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5”,
“publicKeyBase58”: “GfHq2tTVk9z4eXgyHhSTmTRf4NFuTv7afqFroA8QQFXKm9fJcBtMRctowK33”
}
]
}’
Response:
· The API will respond with status 200 if update is successful.
· The API will respond with status 404 for a request with an unknown ID.
· The API will respond with status 400 for other cases of incorrect payload (mismatched signatures, malformed document, instruction etc.).
READ
curl -X GET http://example.org/did:corda:tcn:a609bcc0-a3a8-11e9-b949-fb002eb572a5Response:{
“@context”:”https://w3id.org/did/v1",
“id”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5",
“created”:”2019–07–11T10:27:27.326Z”,
“publicKey”:[
{
”id”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-1",
”type”:”Ed25519VerificationKey2018",
”controller”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5",
”publicKeyBase58":”GfHq2tTVk9z4eXgyNRg7ikjUaaP1fuE4Ted3d6eBaYSTxq9iokAwcd16hu8v”
}
]
}
Response:
● The API will respond with status 200 for a request with a known ID.
● The API will respond with status 404 for a request with an unknown ID.
● The API will respond with status 400 for a request with an ID with incorrect format.
DELETE
curl -X DELETE \ http://example.org/did:corda:tcn:a609bcc0-a3a8-11e9-b949-fb002eb572a5 \
-H ‘content-type: multipart/form-data’ \
-F instruction=’{
“action”: “delete”,
“signatures”: [
{
“id”:”did:corda:tcn:a609bcc0-a3a8–11e9-b949-fb002eb572a5#keys-2",
“type”:”Ed25519Signature2018",
“signatureBase58”:
”26kkhZbQLSNvEKbPvx18GRfSoVMu2bDXutvnWcQQyrGxqz5VKijkFV2GohbkbafPa2WqV ad7wnyLwx1zxjvVfvSa”
}
]
}’
Response
● The API will respond with status 200 if delete is successful.
● The API will respond with status 404 for a request with an unknown ID.
● The API will respond with status 400 for other cases of incorrect payload (mismatched signatures, malformed instruction etc.).
Potential applications
Corda DID method implementation can be run as a service on Corda business networks. Developers can experiment with DIDs and incorporate them into Corda business applications thereby enabling new functionality, such as client-side signing and verification. End-users can be represented as a DID on Corda ledger which opens new possibilities for applications that depends on end-user’s interaction with Corda. Smart contract can have a check to verify digital signatures over some payload coming from corresponding private key held by user’s wallet.