© 2024 Blockchain Commons
Authors: Wolf McNally, Christopher Allen
Date: December 6, 2024
This document defines the XID (“eXtensible IDentifier”), a unique 32-byte identifier for a subject entity. A subject may be a person, an organization, a device, or any other entity, real or abstract. XIDs may be extended to XID documents, which can contain keys, endpoints, permissions, delegation of capabilities, and other information.
This document is a work in progress, with a partial reference implementation in the bc-xid
crate.
XID documents described herein are encoded using Gordian Envelope, which is a recursive data structure having the form:
<subject> [
<predicate>: <object>
<predicate>: <object>
...
]
The term subject as used in Gordian Envelope documentation and literature derives from the domain of knowledge representation such as the Semantic Web and Resource Description Framework (RDF) where subject-predicate-object triples are the basic unit of relational knowledge. As such, the "subject" of an envelope can be any entity, real or abstract, and the predicate-object pairs are called assertions.
This particular use of the term subject is not to be confused with the same word used in the Verifiable Credential (VC) domain to mean, "the entity about whom claims are being made," which is typically a real-world entity such as a person or organization. Furthermore, envelope assertions are not to be confused with Verifiable Credential claims.
Since Envelopes consist of recursively-nested envelopes, most "subjects" in a typical envelope will not be "subjects" as construed in Verifiable Credentials.
<subject> [
<predicate>: <object>
<predicate>: <subject> [
<predicate>: <object>
<predicate>: <object>
...
]
...
]
Nonetheless, a XID as the top-level subject of a XID document envelope may very well be such an identifier of a real-world entity, and hence be a subject in both senses. In specific circumstances, it is also possible for envelope assertions to represent verifiable claims.
A XID (“eXtensible IDentifier”, IPA pronunciation /zɪd/) is a unique 32-byte identifier for a subject entity. A subject may be a person, an organization, a device, or any other entity, real or abstract:
XID(71274df133169a0e2d2ffb11cbc7917732acafa31989f685cca6cb69d473b93c)
XIDs are encoded using CBOR, and are a 32-length CBOR byte string tagged with the CBOR tag #6.40024.
NOTE: This tag has not yet been assigned in the IANA registry of CBOR tags.
So the XID above, in CBOR diagnostic notation, is:
40024(h'71274df133169a0e2d2ffb11cbc7917732acafa31989f685cca6cb69d473b93c')
And when converted to binary is:
D9 9C58 # tag(40024)
58 20 # bytes(32)
71274DF133169A0E2D2FFB11CBC7917732ACAFA31989F685CCA6CB69D473B93C
We will frequently identify XIDs by the first four bytes, sufficient for quick recognition by humans:
XID(71274df1)
These first four bytes can be translated to a sequence of Bytemoji or Bytewords prefixed by 🅧
U+1F167 (NEGATIVE CIRCLED LATIN CAPITAL LETTER X) for quick and memorable identification of a XID in any context:
🅧 🌊 😹 🌽 🐞
🅧 JUGS DELI GIFT WHEN
🅧 71 27 4d f1
For additional recognizability and security, a LifeHash may also be used, which is created using the full tagged CBOR encoding of the XID:
To encode a XID as a URI, we encode it as a Uniform Resource (UR) using the UR type xid
to take the place of the tag:
ur:xid/hdcxjsdigtwneocmnybadpdlzobysbstmekteypspeotcfldynlpsfolsbintyjkrhfnvsbyrdfw
❇️ Note: None of these alternate encodings change the characteristic bytes (and hence Bytewords) used to quickly identify the XID itself. Comparison of XIDs by machines always uses all 32 bytes.
To gain access to the information to which a XID refers, it must undergo resolution. A XID alone does not specify a resolution method. It would be desirable for a future XID standard to define as part of its ecosystem a decentralized universal XID resolver that would be useful for many purposes. However, some applications of XIDs will require that one or more alternate or required resolution methods be specified along with the basic XID.
To accomplish this, we can create a minimal XID Document (see below) which only includes one or more resolution methods. This is a Gordian Envelope with the XID as its subject and one or more dereferenceVia
assertions describing recommended resolution methods:
XID(71274df1) [
'dereferenceVia': URI(btcr:01234567)
'dereferenceVia': URI(https://resolver.example.com)
]
We then wrap the CBOR encoding of this envelope in the XID tag.
40024(200(<XID envelope contents>))
If we want to handle this as a UR, we can use the xid
UR type, just like with a bare XID. As with all URs, the UR type (xid
) replaces the outer tag:
200(<XID envelope contents>)
So handline a XID document as a UR looks just like a bare XID, just longer:
ur:xid/tpsplstpsotanshdhdcxjsdigtwneocmnybadpdlzobysbstmekteypspeotcfldynlpsfolsbintyjkrhfnoyastpsotpcxksceisjyjyjojkftdldljpihjkjljzkoihjpdmihkshsjnjojzihdmiajljnoyastpsotpcxjnidjyiajpftdyeheyeoeeecenemmtcwrogm
Therefore, when parsing a ur:xid
, if the encapsulated CBOR is a byte string, then it is a bare XID. If it begins with tag #6.200, then it is a Gordian Envelope, and hence a XID document.
A XID is generated from the SHA-256 hash of the CBOR representation of a specific PublicSigningKey
structure called the inception key.
Since the CBOR representation of a PublicSigningKey
is self-describing, including the type of algorithm (Ed25519, BIP-340 Schnorr, etc.) the hash is unique to both the key material and the algorithm used.
Therefore, when parsing a ur:xid
, if the encapsulated CBOR is a byte string, then it is a bare XID. If it begins with tag #6.200, then it is a Gordian Envelope, and hence a XID document.
A XID is generated from the SHA-256 hash of the CBOR representation of a specific PublicSigningKey
structure called the inception key.
Since the CBOR representation of a PublicSigningKey
is self-describing, including the type of algorithm (Ed25519, BIP-340 Schnorr, etc.) the hash is unique to both the key material and the algorithm used.
SHA256(PublicSigningKey(Ed25519))
⇒
71274df133169a0e2d2ffb11cbc7917732acafa31989f685cca6cb69d473b93c
⇒
XID(71274df1)
The private half of the inception key is held by the document's inception controller.
The fact that at inception a XID is tied to the cryptographic material of a specific key provides simplicity of lookup and initial authentication, also ensuring that a XID is never generated from arbitrarily chosen numbers, but rather from a unique private key verified to be possessed by the inception controller.
However, a XID's verification keys (which initially include the inception key) can be rotated, including removing (revoking) the inception key, without needing to change the XID itself. This provides for using the XID as a stable identifier throughout its lifetime.
One implication of uniquely tying a XID to a key pair at inception is that a new key pair must be generated and managed for each new XID created. The primary challenge with this is key management when multiple XIDs are managed, while the primary benefit is key isolation in the event of compromise.
A XID document is the result of resolving a XID, and provides the foundational information needed to:
- authenticate the subject entity,
- communicate with it,
- delegate one or more controllers of the entity's functions.
- declare endpoints controllers may use to carry out actions on the subject's behalf.
The format of a XID document makes it easy to:
- delegate all authority to the subject (in which case it is a self-sovereign XID)
- share some authority, or
- completely transfer some or all authority.
Whether function access is granted or denied must be enforced by the verifiers of the rules in the XID document, including XID resolution mechanisms.
A XID document is encoded as a Gordian Envelope, with the XID itself as the envelope subject, and containing a number of assertions about the XID.
A primary role of a XID document is to declare public keys and their associated attributes, including specific operations they are permitted to perform (their permissions), and (optionally) the endpoints through which they may perform these functions. Public keys are declared in a XID document using the key assertion:
XID(71274df1) [
'key': PublicKeyBase [
'allow': 'All'
'endpoint': URI(https://example.com/endpoint)
]
]
Key assertions have:
- The
PublicKeyBase
as their object, which includes both a verfier key and an encryption key. - One or more
allow
ordeny
assertions, which specify the operations the key is permitted to perform. - One or more
endpoint
assertions, which specify the endpoints through which the key may perform these operations. - An optional
PrivateKeyBase
assertion, which includes the private key corresponding to the public key. This key SHOULD be omitted or elided when the XID document is published.
Keys declared at the top level of a XID document are controlled by the inception controller (the key that generated the XID).
The Inception key may be replaced. This must be done by submitting an updated XID document to the XID resolver, which will verify that the signing key has permission to update the XID document in the resolver's database before making the change.
XID(2d9296d0) [
'key': PublicKeyBase [ // Not the inception key
'allow': 'All'
'endpoint': URI(https://example.com/endpoint)
]
]
For publication, a XID document envelope must first be wrapped and then signed with one of the declared keys with the Verify
permission. If more than one key has the Verify
permission, either key may be used. Any key with the All
permission implicitly has the Verify
permission and may be used to sign the envelope.
{
XID(71274df1) [
'key': PublicKeyBase [
'allow': 'All'
'endpoint': URI(https://example.com/endpoint)
]
]
} [
'signed': Signature
]
As mentioned above, XIDs may include non-default resolution methods. A XID document may also include special-purpose resolution mechanisms for a given XID in exactly the same way:
XID(71274df1) [
'dereferenceVia': "https://resolver.example.com"
'dereferenceVia': "btcr:01234567"
'key': PublicKeyBase [
'allow': 'All'
'endpoint': URI(https://example.com/endpoint)
]
]
Delegates are entities that are granted some level of control over a XID. Delegates are declared in a XID document using the delegate assertion. XIDs declared as delegates may also declare non-default resolution mechanisms. In this example, the delegate is granted the ability to sign and encrypt, while the inception key retains all permissions:
XID(71274df1) [
'delegate': {
XID(7c30cafe) [
'dereferenceVia': "https://resolver.example.com"
]
} [
'allow': 'Encrypt'
'allow': 'Sign'
'deny': 'All'
]
'key': PublicKeyBase [
'allow': 'All'
]
]
After resolution, the delegate's keys(s), endpoints, etc. are available:
XID(71274df1) [
'delegate': {
XID(7c30cafe) [
'key': PublicKeyBase [
'allow': 'All'
'dereferenceVia': "https://resolver.example.com"
'endpoint': URI(https://example.com/endpoint)
]
]
} [
'allow': 'Encrypt'
'allow': 'Sign'
'deny': 'All'
]
'key': PublicKeyBase [
'allow': 'All'
]
]
Delegates are assigned a set of permissions that are a subset of the permissions of their parent XID. The delegate may also have its own delegates, and so on. Even if a delegate's XID document declares it has All
permissions, it is still subject to the permissions of its parent XID, meaning permissions can only narrow as you go down the delegation tree.
If policy requires multiple parties to sign an updated XID document:
- Multiple
'signed': Signature
assertions along with averificationThreshold
rule assertion may be used:
{
XID(2d9296d0) [
'key': PublicKeyBase [
'allow': 'Verify'
]
'key': PublicKeyBase [
'allow': 'Sign'
]
'key': PublicKeyBase [
'allow': 'Encrypt'
]
'verificationThreshold': 2 // 2 of 3 signatures required
]
} [
'verifiedBy': Signature // Signed by any two of the above
'verifiedBy': Signature // verifiers
]
- Or, a multiparty threshold signature scheme like FROST could be used where a single XID may be controlled by a quorum of group members, who cooperatively produce a single valid signature:
{
XID(9699ea9b) [
'key': PublicKeyBase [ // Corresponding FROST private key
'allow': 'Verify' // never known by individual group members.
]
]
} [
'verifiedBy': Signature // Multiparty signature indistinguishable
] // from a single-party signature.
Functions are various actions that may be executed on behalf of the XID subject. The exercise of functions often occurs through one or more endpoints, which serve as APIs enabling the associated operations.
❇️ *Note: The list of functions here is provisional and non-exhaustive.
Operational functions relate to interacting with services in the day-to-day usage of the XID, where authentication, signing, and accessing resources are the main activities.
Auth
: Authenticate as the subject (e.g., log into services)Sign
: Sign digital communications as the subjectEncrypt
: Encrypt messages from the subjectElide
: Elide data under the subject's controlIssue
: Issue or revoke verifiable credentials on the subject's authorityAccess
: Access resources allocated to the subject
Management functions affect a XID’s lifecycle and governance. These actions typically require a higher level of trust and authority.
Delegate
: Delegate function access to third partiesVerify
: Verify (update) the XID documentUpdate
: Update service endpointsTransfer
: Remove the inception keyElect
: Add or remove other verifiers (rotate keys)Burn
: Transition to a new provenance mark chain (see below)Revoke
: Revoke the XID entirely
To accept a revised XID document for publication, the resolver network must validate that changes being made are signed by a verifier that has permission to make those changes in the previous version of the XID document.
❇️ Note: Future work could involve defining a system for refining the granularity of permissions by adding assertions to given functions.
A function may exist, but to use it an entity must be granted permission to do so.
XID documents have a deny-first permission structure, so any and all permissions are denied unless specifically granted with an allow
assertion, and a deny
assertion overrides any conflicting allow
. The universal permission All
may be granted, and then additional deny
rules can be added to remove specific permissions.
Delegates may be assigned a subset of the permissions of their parent XID, so permissions can only narrow as you go down the delegation tree.
A minimal, completely self-sovereign XID document that grants all permissions to its inception controller might look like this:
XID(2d9296d0) [
'key': PublicKeyBase [
'allow': 'All'
]
]
In this example, the XID document declares that all management functions are exclusively held by the inception controller, but operational functions are shared between the inception controller and another controller:
XID(2d9296d0) [
'delegate': {
XID(7c30cafe) [
'key': PublicKeyBase [
'allow': 'All'
]
]
} [
'allow': 'auth'
'allow': 'sign'
'allow': 'elide'
'allow': 'issue'
'allow': 'access'
'allow': 'encrypt'
'allow': 'decrypt'
]
'key': PublicKeyBase [
'allow': 'All'
]
]
In this example the inception controller retains all authority, and grants a group of three keys access various operational functions. One of the keys is also nor granted the auth
and access
functions:
XID(2d9296d0) [
'delegate': {
XID(0f7b34f2) [
'key': PublicKeyBase [
'allow': 'All'
]
]
} [
'allow': 'auth'
'allow': 'sign'
'allow': 'elide'
'allow': 'issue'
'allow': 'access'
'allow': 'encrypt'
'allow': 'decrypt'
]
'delegate': {
XID(f520960d) [
'key': PublicKeyBase [
'allow': 'All'
]
]
} [
'allow': 'auth'
'allow': 'sign'
'allow': 'elide'
'allow': 'issue'
'allow': 'access'
'allow': 'encrypt'
'allow': 'decrypt'
]
'delegate': {
XID(763a2eba) [
'key': PublicKeyBase [
'allow': 'All'
]
]
} [
'allow': 'sign'
'allow': 'elide'
'allow': 'issue'
'allow': 'encrypt'
'allow': 'decrypt'
]
'key': PublicKeyBase [
'allow': 'All'
]
]
Although a XID remains stable throughout its lifecycle, the key(s) it vends, the permissions it grants, and the endpoints it uses may change considerably over time. Valid XIDs MUST be created from a inception key, which gives a XID its identity, including verifying that the XID is not formed from arbitrarily chosen numbers.
As long as the inception key remains in the XID document as a verifier, then revisions to the document signed by the same key can be considered authentic.
But control of a XID may be entirely transferred at some point, including entirely removing the inception key. This may be due to possible key compromise, or because policy may simply dictate periodic key rotation.
In addition, there are challenges around the sequencing of published revisions to XID documents in a decentralized, distributed, and concurrent environment. Between two revisions, which is more current? Timestamps can be spoofed.
So, since a XID document may routinely become detached from the cryptographic material used to incept it, how do we authenticate a given version of a XID document we may receive from some source?
Part of that authentication depends on resolving the XIDs of the document's current controller(s), and deciding whether to trust those entities.
However, an additional and complementary path to authentication becomes available if we provide a mechanism for establishing the provenance of a XID document; that is, whether we can establish a XID document as a verifiable part of a chain of revisions going back to its inception.
Therefore, XID documents support the inclusion of a provenance mark. This means our minimal, fully self-sovereign XID document signed and ready for publication now looks like this:
{
XID(2d9296d0) [
'key': PublicKeyBase [
'allow': 'all'
]
'provenance': ProvenanceMark
]
} [
'verifiedBy': Signature
]
A provenance mark is a small binary structure that, like a block of transactions on the Bitcoin blockchain, links a given revision of a XID document to all previous revisions and makes the history of a XID increasingly hard to alter.
However, provenance marks have several additional functions that multiply a XID's security and verifiability. The provenance mark:
- confirms the cryptographic commitment from the previous revision
- "seals" the history of previous revision(s),
- proves that the revision belongs to a specific chain,
- contains a monotonically increasing integer revision number,
- contains a timestamp which must never decrease,
- provides a cryptographic pre-commitment to the next revision.
When first published with a provenance mark (recommended, perhaps mandatory depending on policy and the resolver method) every successive revision to a XID document MUST contain the next mark in the chain. This allows resolvers to independently verify its place in the timeline of the XID's lifecycle as well as detect anomalies that may be the result of data corruption, loss, or attempted forgery.
The technical prerequisite for supporting provenance marks is the generation and secure storage of a 32-byte cryptographic seed. This seed is generated at XID inception along with the inception key, but is not cryptographically linked to it in any way. This means it can be stored together with the inception key or managed separately from it. It can be given to every verifier or held by a completely separate agent whose function is generating the next provenance mark when needed.
To publish a revised document, the seed is used to generate the next provenance mark, which is included in the revised document, and then the entire document is signed by a key with verifier permission. Before being accepted for publication, resolver networks must validate the XID's provenance chain as well as the other XIDs the document references and the verifier's permissions.
Having a new piece of cryptographic material to manage raises the question of how to rotate it, if and when the need arises.
The solution is to publish a XID document revision with a "burn mark" that definitively marks the end of the current chain, and links it to the start of a new chain. The process for creating a provenance mark with a burn mark is described in detail in the Provenance Mark whitepaper.
Before accepting a XID document with a burn mark, resolver networks MUST verify that the signer of the document has the burn
permission, which is deliberately separate from the verify
permission.
When the next revision of the XID document appears, it will contain the genesis mark of the new chain, now cryptographically linked to the burn mark in the previous revision.
This document requests the assignment of a new CBOR tag for XIDs: #6.40024.