About

The Sovrin DID method specification conforms to the requirements specified in the DID specification currently published by the W3C Credentials Community Group. For more information about DIDs and DID method specifications, please see the DID Primer and DID Spec.

Sovrin is a public ledger designed specifically and only for privacy-preserving self-sovereign identity. The Sovrin Ledger is governed by the international non-profit Sovrin Foundation. As the only public ledger designed exclusively for self-sovereign identity, Sovrin is optimized for DIDs and DID Documents. DIDs are created, stored, and used with verifiable claims. This specification covers how these DIDs are managed. It also describes related features of Sovrin of particular interest to DID owners, guardians, and developers.

 

Sovrin DID Method

The namestring that shall identify this DID method is: sov

A DID that uses this method MUST begin with the following prefix: did:sov. Per the DID specification, this string MUST be in lowercase. The remainder of the DID, after the prefix, is the NSI specified below.

Target System(s)

This DID method applies to the Sovrin network in all its incarnations, beginning with Sovrin's provisional launch on 31 July 2017.

Namespace Specific Identifier (NSI)

The Sovrin DID scheme is defined by the following ABNF:

sovrin-did = "did:sov:idstring" *(":" subnamespace)
idstring = 21*22(char)
subnamespace = ALPHA *(ALPHA / DIGIT / "_" / "-")
char = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "A" / "B" / "C"
    / "D" / "E" / "F" / "G" / "H" / "J" / "K" / "L" / "M" / "N" / "P" / "Q"
    / "R" / "S" / "T" / "U" / "V" / "W" / "X" / "Y" / "Z" / "a" / "b" / "c"
    / "d" / "e" / "f" / "g" / "h" / "i" / "j" / "k" / "m" / "n" / "o" / "p"
    / "q" / "r" / "s" / "t" / "u" / "v" / "w" / "x" / "y" / "z"

All Sovrin DIDs are base58 encoded using the Bitcoin/IPFS alphabets of a 16-byte uuid. The encoding uses most alphas and digits, omitting 0OIl to avoid readability problems. This gives an NSI length of either 21 or 22 characters, and it means that DIDs are case-sensitive and may not be case-normalized, even though the prefix is always lower-case.

Optional one or more sub namespaces may be specified to indicate which Sovrin system the DID should reference.

Namestring Generation Method

The 16-byte uuid underlying a Sovrin DID can be generated in various ways--using standard uuid methods, for example, or by selecting the first 16 bytes of a 256 bit Ed25519 verification key (the public portion of the key pair). In the latter case, there may or may not be an active verification key for the identity owner; that information is only available in the key descriptions in the owner section of the DID Document.

A convenient regex to match sov DIDs is:

^[1-9A-HJ-NP-Za-km-z]{21,22}$

A convenient regex to match the entire did string is:

^did:sov:[1-9A-HJ-NP-Za-km-z]{21,22}(?<namespace>(?::\w[-\w]*)*)$

Examples

A valid sov DID might be: did:sov:2wJPyULfLLnYTEFYzByfUR.

JSON-LD Context Definition

On Sovrin, public DIDs and their corresponding documents are stored on the ledger.

TODO

CRUD Operation Definitions

Create (Register)

The DID method specification must specify how a client creates a DID record—the combination of a DID and its associated document—on the target system, including all cryptographic operations necessary to establish proof of ownership. The Sovrin spec adds a new entry under publicKey called authorizations. This is used to indicate possible authorisations the public key is supposed to have. The keyword ALL means that the public key can make whatever change it wants to the DID doc. While creating a new DID doc, the field authorizations cannot be left empty. It has to either contain the keyword ALL or be ommitted, in that case it is assumed that the key has all authorisations.

For example:

To create a DID, you must submit a transaction that looks like this:

                {
                    "submitterId": <Trust Anchor DID>,
                    "signature": <signature over this transaction from the Trust Anchor>,
                    "reqId": <A nonce for this transaction>,
                    "operation": {
                        "type": "NYM",
                        "did": <new DID that is being registered>,
                        "document": {
                            "publicKey": [{
                                "id": <a valid unique identifier>,
                                "type": <ed25519, defined in appendix>,
                                "publicKeyBase58": <Key material encoded>,
                                "authorizations": ["ALL"]
                            }],
                            "authentication": [{
                                "type": <type of DID authentication>,
                                "publicKey": "<reference to a publicKey object>",
                            }],
                            "service": [{
                              "type": <agentService, emailService, apiService, dnsService, etc>,
                              "serviceEndpoint": <A URI for the endpoint>
                            }],
                            "role":<Optional; Enumeration for roles, an integer; if left empty then no special role is assigned>
                        }
                    }
                }

                Example
                {
                    "submitterId": "did:sov:29wksjcn38djfh47ruqrtcd5",
                    "signature": "1qaz2wsx3edc4rfv5tgb6yhn7ujm8iklop==",
                    "reqId": "okn987yhbgFtErDsCXsw",
                    "operation": {
                        "type": "NYM",
                        "did": "did:sov:mnjkl98uipsndg2hdjdjuf7",
                        "document": {
                            "publicKey": [{
                                "id": "key1"
                                "type": "ED25519SignatureVerification",
                                "publicKeyBase58": "..."
                            }],
                            "authentication": [{
                                "type": "ED25519SigningAuthentication",
                                "publicKey": "key1"
                            }],
                            "service": [{
                              "type": "agentService",
                              "serviceEndpoint":"https://www.sovrin.org/agents"
                            }]
                        }
                    }
                }
            
The transaction must be signed by a Trust Anchor and must provide an un-registered DID and a document of data about that DID. Possible outcomes from the create operation include:

The DID document is rendered as follows. The exact data structure persisted on the ledger does not have to match the following and can vary for each node.

            {
              "id": "did:sov:mnjkl98uipsndg2hdjdjuf7",
              "publicKey": [{
                  "id": "key1"
                  "type": "ED25519SignatureVerification",
                  "publicKeyBase58": "...",
                  "authorizations": ["all"]
                }],
              "authentication": [{
                  "type": "ED25519SigningAuthentication",
                  "publicKey": "key1"
                }],
              "service": [{
                  "type": "agentService",
                  "serviceEndpoint":"https://www.sovrin.org/agents"
                }]
            }
          

Read (Resolve)

A Sovrin DID record can be looked up directly by the DID using a standard Sovrin GET_NYM transaction that simply takes the DID and will return the DID record. The client trusts the DID record because it either receives the same DID record from sufficient number of validators or it receives a proof of the DID record from a single validator that the DID record is part of a merkle tree whose root has an aggregate signature from sufficient number of validators.

This is how the DID record query looks like

                {
                    "submitterId": <Optional; DID of the author of this query>,
                    "reqId": <Optional; a nonce for this query>,
                    "operation": {
                        "did": <DID to be queried>,
                        "type": "GET_NYM"
                    }
                }
        

Anyone can query a DID record, by sending the above request. The response contains the DID document.

        

Update (Replace)

To replace the DID document, the owner or guardian (guardianship ends once ownership begins) of the DID should send the following transaction using a key referenced in the authentication property.

                  {
                    "submitterId": <DID of the author of this query>,
                    "signature": [<signature over this transaction from the author>,]
                    "reqId": <a nonce for this query>,
                    "operation": {
                        "type": "NYM",
                        "did": <DID whose record needs to be updated>,
                        "document": {
                            "publicKey": [{
                                "id": <a valid unique identifier>
                                "type": <ED25519>
                                "publicKeyBase58": <Key material encoded in format>,
                                "authorizations": ["all"]
                            },{
                                "id": <a valid unique identifier>
                                "type": <ED25519>
                                "publicKeyBase58": <Key material encoded in format>
                            },{
                                "id": <a valid unique identifier>
                                "type": <ED25519>
                                "publicKeyBase58": <Key material encoded in format>
                            }],
                            "authentication": [{
                                "type": "ED25519SigningAuthenticationThreshold",
                                "threshold": <an integer>,
                                "publicKey": [<key references>]
                            }],
                            "service": [{
                              "type": <agentService, emailService, apiService, dnsService, authenticationService, etc>,
                              "serviceEndpoint": <A URI for the endpoint>
                            }]
                        }
                      }
                  }
                

This example shows how to update the DID document to require multiple signatures for authentication:
                {
                    "submitterId": "did:sov:qazWSX3erfcY459iLMh7",
                    "signature": ["1qaz2wsx3eeciguytGGVDR990ik=="]
                    "reqId": "897432983746n43g"
                    "operation": {
                        "type": "NYM",
                        "did": "did:sov:c432cDSGUASJN987jnJKb",
                        "document": {
                            "publicKey": [{
                                "id": "#key1",
                                "type": "ED25519VerificationKey",
                                "publicKeyBase58": ...
                            },{
                                "id": "#key2",
                                "type": "ED25519VerificationKey",
                                "publicKeyBase58": ...
                            },{
                                "id": "#key3",
                                "type": "ED25519VerificationKey",
                                "publicKeyBase58": ...
                            }],
                            "authentication": [{
                                "type": "ED25519SigningAuthenticationThreshold",
                                "threshold": 2,
                                "publicKey": ["#key1","#key2","#key3"]
                            }],
                            "service": [{
                              "type": "agentService",
                              "serviceEndpoint": "https://www.sovrin.org/agents"
                            }]
                        }
                      }
                  }
                

Delete (Revoke)

Deleting or revoking a verification key is not to be confused with temporary suspension or rotation. Deletion sets an identity's verification key to null; this permanently terminates the identity's ability to operate on the network because there is no key that the identity can use to authenticate itself--even to submit a new key rotation request. It is irreversible.

Revocation may be appropriate when a person dies or a business is legally dissolved. It does not remove any record or history of the identity--it simply prevents any new history from accruing. This guarantees that no malicious actor can recover and reactivate an identity that's dead.

The scope of deletion in Sovrin is one DID only. This does not prevent an entity from creating new DIDs; it simply prevents an entity from reusing the old DID that has been terminated.

To revoke the document of the DID, the owner of the DID should send the following transaction.

                {
                    "submitterId": <DID of the owner>,
                    "signature": <signature over this transaction from the DID in identifier>,
                    "reqId": <a nonce for this transaction>,
                    "operation": {
                        "type": "NYM",
                        "did": <DID whose key is being revoked>,
                        "document": null
                    }
                }
            

Security Considerations

Attacks

Secure communication to the Sovrin Ledger uses CurveZMQ to mitigate attacks like eavesdropping, replay, message insertion, deletion, modification, impersonation, and man-in-the-middle. Any vulnerabilities in that protocol will apply to Sovrin.

Sovrin uses a modified version of the Redundant Byzantinne Fault Tolerace protocol called Plenum to reach consensus.

Sovrin currently stores only the following data on the ledger:

All confidential information such as cryptographic private keys are stored with end consumers of Sovrin.

Residual Risks

We don't believe to have any residual risks at this time.

Recovery From Key Compromise

This section MUST provide integrity protection and update authentication for all operations required by Section 7 of this specification (DID Operations).

Confused Deputy Problem when attempting separation of writeAuthorization from authenticationCredential.

Other sections from DID spec to incorporate into the outline:

Privacy Considerations

Assume psuedonymous DID. What Sovrin is doing about privacy and addresses each of these.

Considering all four types of privacy, explore issues from these sections of the DID spec:

            10. Privacy Considerations
            10.1 Requirements of DID Method Specifications
            10.2 Keep Personally-Identifiable Information (PII) Off-Ledger
            10.3 DID Correlation Risks and Pseudonymous DIDs
            10.4 Document Correlation Risks
            10.5 Herd Privacy
            

Reference Implementations

The code at https://github.com/hyperledger/indy-node, stable and https://github.com/hyperledger/indy-sdk, master constitute a canonical implementation of Sovrin DIDs, and should be treated like an oracle.

Note that the code includes a test suite; any other implementations should ensure that all tests pass before they claim compatibility.

Resources

Many developers maintaining the code and spec tend to hang out in RocketChat at chat.hyperledger.org, #indy and #indy-sdk. You might also connect with us in Hyperledger and W3C working groups or at the semi-annual Internet Identity Workshop conferences.

Appendix A: Key Algorithm Types

This is extracted from RFC 5480 and 5639:
The NIST-named curves are:

-- Note that in [X9.62] the curves are referred to as 'ansiX9' as
-- opposed to 'sec'. For example, secp192r1 is the same curve as
-- ansix9p192r1.

-- Note that in [PKI-ALG] the secp192r1 curve was referred to as
-- prime192v1 and the secp256r1 curve was referred to as
-- prime256v1.

-- Note that [FIPS186-3] refers to secp192r1 as P-192, secp224r1 as
-- P-224, secp256r1 as P-256, secp384r1 as P-384, and secp521r1 as
-- P-521.

secp192r1 ... same as nistp192, prime192v1
secp224r1 ... same as nistp224, prime224v1
secp256k1 ... used by Bitcoin
secp256r1 ... same as nistp256, prime256v1
secp384r1 ... same as nistp384, prime384v1
secp521r1 ... same as nistp521, prime521v1
Curves from http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
nistp192 ... same as secp192r1, prime192v1
nistp224 ... same as secp224r1, prime224v1
nistp256 ... same as secp256r1, prime256v1
nistp384 ... same as secp384r1, prime384v1
nistp521 ... same as secp521r1, prime521v1
Curves from ANS X9.62
prime192v1 ... same as nistp192, secp192r1
prime192v2
prime192v3
prime224v1 ...same as nistp224, secp224r1
prime239v1
prime239v2
prime239v3
prime256v1 ... same as nistp256, secp256r1
prime384v1 … same as nistp384, secp384r1
prime521v1 ... same as nistp521, secp521r1

Barreto Naehring curves are defined at ISO/IEC 15946-5, DevScoDah2007, and FIDO ECDAA Section 4

The following are valid values for publicKey/type

Key Type
rsa
Twisted Edwards/Montgomery Curves
m-221, e-222, ed1174, ed25519, ed383187, ed41417, e-382, m-383, ed448, e-521, m-511
BrainPool Curves
brainpoolp160r1, brainpoolp192r1, brainpoolp224r1, brainpoolp256r1, brainpoolp320r1, brainpoolp384r1, brainpoolp512r1
SEC2 Curves
secp112r1, secp128r1, secp160r1, secp192r1, secp224r1, secp256r1, secp384r1, secp521r1
secp160k1, secp163k1, secp192k1, secp224k1, secp256k1
sect163r2, sect233r1, sect239r1, sect283r1, sect409r1, sect571r1
Barreto Naehrig Curves
fp224bn, fp254bna, fp254bnb, fp256bn, fp384bn, fp512bn, fp638bn

Appendix B: Ed25519 Signature Suite

A description to the Ed25519 Signature Suite can be found here.

Appendix C: Public Key Authorizations

The following authorizations are supported:
  • ADD_KEY: Add new key to the DID doc. The new key can only have ADD_KEY authorization.
  • REM_KEY: Remove existing keys from the DID doc.
  • ALL: All possible authorizations. The key submitted during creation of DID doc has this authorisations. The key with this authorization can add keys with ADD_KEY, REM_KEY and ALL authorization