DID Method Mechanics (key, jwk, web, webvh 1.0)
2. DID Method Implementation Mechanics
Section titled “2. DID Method Implementation Mechanics”2.1 did:key (W3C CCG v0.9)
Section titled “2.1 did:key (W3C CCG v0.9)”The did:key method derives the entire DID document deterministically from the public key encoded within the method-specific identifier.
did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK │└─────────────────────────────────────────────┘ │ Base58BTC Encoded Payload ▼ Multibase Prefix 'z' │ ▼ (Base58BTC Decode) ┌────────────────────────┬───────────────────────────────────────────┐ │ Varint Multicodec │ Raw Public Key Bytes │ │ (e.g., 0xed = Ed25519) │ (32 bytes Ed25519 / 33 bytes compressed) │ └────────────────────────┴───────────────────────────────────────────┘2.1.1 Multicodec Table & Varint Decoding
Section titled “2.1.1 Multicodec Table & Varint Decoding”Decoding is executed via unsigned_varint::decode::u64 against the raw byte buffer obtained after stripping the leading base58btc 'z' prefix.
| Codec Identifier | Hex Codec | Varint Wire Bytes | Raw Key Length | Cryptographic Curve / Type | Key Purpose Mapping |
|---|---|---|---|---|---|
MC_ED25519_PUB |
0xed |
[0xed, 0x01] |
32 bytes | Edwards Ed25519 | authentication, assertionMethod, capabilityInvocation, capabilityDelegation |
MC_X25519_PUB |
0xec |
[0xec, 0x01] |
32 bytes | Montgomery X25519 | keyAgreement (ONLY) |
MC_P256_PUB |
0x1200 |
[0x80, 0x24] |
33 bytes | NIST P-256 (secp256r1) | authentication, assertionMethod, capabilityInvocation, capabilityDelegation |
MC_SECP256K1_PUB |
0xe7 |
[0xe7, 0x01] |
33 bytes | SECG secp256k1 | authentication, assertionMethod, capabilityInvocation, capabilityDelegation |
MC_P384_PUB |
0x1201 |
[0x81, 0x24] |
49 bytes | NIST P-384 | Recognized as unsupported; document derived without key validation. |
MC_P521_PUB |
0x1202 |
[0x82, 0x24] |
67 bytes | NIST P-521 | Recognized as unsupported; document derived without key validation. |
MC_BLS12381_G2_PUB |
0xeb |
[0xeb, 0x01] |
96 bytes | BLS12-381 G2 | Recognized as unsupported; document derived without key validation. |
MC_RSA_PUB |
0x1205 |
[0x85, 0x24] |
Variable | RSA 2048/4096 | Recognized as unsupported; document derived without key validation. |
2.1.2 Curve Point Validation & DoS Defenses
Section titled “2.1.2 Curve Point Validation & DoS Defenses”- Length Enforcement:
- Ed25519 and X25519 raw byte slices MUST measure exactly 32 bytes.
- P-256 and secp256k1 raw byte slices MUST measure exactly 33 bytes representing a compressed SEC1 elliptic curve point.
- Point Validation:
- Ed25519: Decoded via
ed25519_dalek::VerifyingKey::from_bytes. Signature verification uses RFC 8032verify_strictto prevent signature malleability and non-canonical point attacks. - P-256: Decoded via
p256::ecdsa::VerifyingKey::from_sec1_bytes. SEC1 decompression validates that the point satisfies the Weierstrass equation $y^2 \equiv x^3 - 3x + b \pmod p$. - secp256k1: Decoded via
k256::ecdsa::VerifyingKey::from_sec1_bytes. Point decompression validates that the coordinates lie on $y^2 \equiv x^3 + 7 \pmod p$. - Non-canonical encodings, coordinates exceeding field size, or points off curve return
CryptoError::Malformedand abort resolution withResolverError::InvalidDid.
- Ed25519: Decoded via
- Key Agreement Isolation:
- X25519 keys (
0xec) populate solely thekeyAgreementverification relationship. They are prohibited from asserting identity (authentication) or signing credentials (assertionMethod). Any signature verification attempted against an X25519 key fails withCryptoError::Malformed("X25519 is a key-agreement key and cannot verify signatures").
- X25519 keys (
2.2 did:jwk (DIF Specification)
Section titled “2.2 did:jwk (DIF Specification)”The did:jwk method packages a public JSON Web Key (RFC 7517) within a base64url-encoded string (did:jwk:<base64url-jwk>).
2.2.1 Parsing & DoS Constraints
Section titled “2.2.1 Parsing & DoS Constraints”- Length Constraint: The method-specific identifier string length is strictly capped at
MAX_JWK_ENCODED_LEN = 4096. Identifiers exceeding this bound fail immediately withResolverError::InvalidDid("did:jwk value is too long"). - Duplicate Key Guard: Raw bytes are decoded using
strict_json::from_slice(&bytes)to reject duplicate JSON keys at all nesting levels before deserialization.
2.2.2 Mandatory Private-Key Member Rejection
Section titled “2.2.2 Mandatory Private-Key Member Rejection”To prevent private key leakage through public identifier reflection, DID.is enforces an absolute rejection policy. If any of the following private key members are present in the parsed JWK, resolution fails:
const PRIVATE_JWK_MEMBERS: &[&str] = &["d", "p", "q", "dp", "dq", "qi", "oth", "k"];Private keys are never silently stripped or ignored. The presence of any member in PRIVATE_JWK_MEMBERS aborts resolution immediately with:
ResolverError::InvalidDid("did:jwk embeds private key material ('<member>'); refusing to resolve")2.2.3 Point De-serialization & SEC1 Uncompressed Assembly
Section titled “2.2.3 Point De-serialization & SEC1 Uncompressed Assembly”For EC keys (P-256 and secp256k1), x and y members (each exactly 32 base64url-decoded bytes) are parsed and concatenated into a 65-byte uncompressed SEC1 octet sequence:
$$\text{SEC1} = \mathtt{0x04} \parallel x \parallel y$$
The point is then validated against the curve via from_sec1_bytes.
2.2.4 Usage Mapping
Section titled “2.2.4 Usage Mapping”- If
use == "enc", the verification method is bound exclusively tokeyAgreement. - If
use == "sig", the verification method is bound toassertionMethod,authentication,capabilityInvocation, andcapabilityDelegation. - If
useis omitted, the method is bound to all relationships.
2.3 did:web (W3C CCG did:web)
Section titled “2.3 did:web (W3C CCG did:web)”The did:web method maps a domain and optional path segments to a well-known HTTPS URL.
2.3.1 Transformation Rules
Section titled “2.3.1 Transformation Rules”did:web:example.com$\to$https://example.com/.well-known/did.jsondid:web:example.com:user:alice$\to$https://example.com/user/alice/did.jsondid:web:example.com%3A8443$\to$https://example.com:8443/.well-known/did.json
2.3.2 Parsing Hardening & Anti-Evasion Rules
Section titled “2.3.2 Parsing Hardening & Anti-Evasion Rules”The transformation pipeline strictly enforces:
- Single Percent-Decoding: Segment splitting occurs prior to percent-decoding the port component. Double percent-encoding (e.g.
%253A) is rejected. - IP Literal Prohibition: Numeric IP addresses (
did:web:127.0.0.1,did:web:169.254.169.254), hexadecimal IPs (did:web:0x7f000001), octal representations (did:web:017700000001), and integer IP literals (did:web:2130706433) are rejected at the parser level withResolverError::InvalidDid. - Path Traversal & Ambiguity Guards: Colon segments containing dot segments (
:..:,:%2E%2E:,:%2F..%2F) or userinfo markers (@,%40) are rejected. - Strict Document ID Matching: Per W3C DID Resolution v1.0, the document retrieved from the remote host MUST contain a top-level
idproperty that exactly equals the requested DID: $$\text{doc.id} \equiv \text{requested_did}$$ Ifdoc.id != requested_did, resolution fails closed withResolverError::InvalidDidDocument.
2.3.3 DIF Domain Linkage Verification
Section titled “2.3.3 DIF Domain Linkage Verification”When resolving did:web, resolver-core validates domain authenticity via DIF Well Known DID Configuration (/.well-known/did-configuration.json):
- Fetches configuration from
https://<origin>/.well-known/did-configuration.json. - Inspects
linked_dids[]array (up toMAX_LINKED_DIDS = 50). - For each
DomainLinkageCredential:- Validates that
credentialSubject.id == issuer == did. - Normalizes and compares
credentialSubject.originwith the request origin: $$\mathtt{normalize_origin(credentialSubject.origin)} \equiv \mathtt{normalize_origin(origin)}$$ - Verifies the credential signature using a verification method declared in the DID document’s
assertionMethod. - Validates temporal validity:
validFrom/issuanceDate$\le \text{now} \le$validUntil/expirationDate.
- Validates that
2.4 did:webvh 1.0 (DIF did:webvh v1.0)
Section titled “2.4 did:webvh 1.0 (DIF did:webvh v1.0)”did:webvh (“DID Web + Verifiable History”) transforms mutable did:web infrastructure into a tamper-evident, cryptographically verifiable log of state transitions (did.jsonl).
Entry 1 (Genesis / Version 1): SCID Calculation: multihash(sha2-256, JCS(Entry 1 with versionId="{SCID}", proof stripped)) Entry Hash: SCID Proof: eddsa-jcs-2022 by updateKey Commitment: nextKeyHashes = [hash(k2)] │ ▼ Linked by predecessor versionId & proofEntry 2 (Update / Version 2): Entry Hash: multihash(sha2-256, JCS(Entry 2 with versionId=Entry 1.versionId, proof stripped)) Proof: eddsa-jcs-2022 by k2 (satisfying nextKeyHashes) Commitment: nextKeyHashes = [hash(k3)]2.4.1 Multihash Base58BTC Formatting
Section titled “2.4.1 Multihash Base58BTC Formatting”Entry digests use Base58BTC-encoded multihash format without a multibase prefix (starting with Qm):
$$\text{multihash_b58}(D) = \mathtt{base58btc}([0x12, 0x20] \parallel \text{SHA-256}(D))$$
where 0x12 represents the sha2-256 multicodec and 0x20 specifies 32 bytes length.
2.4.2 SCID Derivation Formula
Section titled “2.4.2 SCID Derivation Formula”The Self-Certifying Identifier (SCID) is deterministically generated from the genesis entry (entry 1):
- Strip the
proofobject from entry 1. - Set
versionId = "{SCID}". - In the serialized JSON string, substitute all occurrences of the prospective SCID string with
"{SCID}". - Canonicalize using RFC 8785 (JCS).
- Compute: $$\text{SCID} = \text{multihash_b58}(\text{JCS}(\text{entry}_1[\text{versionId}\leftarrow\text{“{SCID}”}, \text{proof}\leftarrow\emptyset]))$$
2.4.3 Entry-Hash Chain Invariant
Section titled “2.4.3 Entry-Hash Chain Invariant”For every subsequent entry $i > 1$:
- Strip the
proofobject from $\text{entry}_i$. - Set
versionIdequal to the predecessor’s version identifier ($\text{versionId}_{i-1}$). - Canonicalize using RFC 8785 (JCS).
- Compute: $$\text{entry_hash}_i = \text{multihash_b58}(\text{JCS}(\text{entry}i[\text{versionId}\leftarrow\text{versionId}{i-1}, \text{proof}\leftarrow\emptyset]))$$
- Invariants enforced:
- Version numbers MUST increment sequentially by exactly 1 ($v_{i} = v_{i-1} + 1$).
versionTimeMUST be strictly monotonically increasing ($t_i > t_{i-1}$).- Log entries MUST NOT exceed
MAX_ENTRIES = 2000. - Total log stream size MUST NOT exceed
MAX_LOG_BYTES = 4 \times 1024 \times 1024(4 MiB).
2.4.4 Pre-Rotation Key Commitments
Section titled “2.4.4 Pre-Rotation Key Commitments”To defend against server-compromise state substitutions:
- Entry $i$ declares
nextKeyHashes = [H_1, H_2, \dots]where: $$H_k = \text{multihash_b58}(\text{MultikeyBytes})$$ - In entry $i+1$, the update key used to sign the state transition MUST satisfy: $$\text{multihash_b58}(\text{signer_multikey}) \in \text{nextKeyHashes}_i$$ Any update signed by a key not committed in the immediately preceding entry fails resolution.
2.4.5 Witness Threshold Verification
Section titled “2.4.5 Witness Threshold Verification”When a witness policy is active:
- Entry defines
witness: { threshold: M, witnesses: [{ id: "did:key:..." }, ...] }. - Condition: $1 \le M \le N$ where $N$ is the witness count.
- Witnesses publish detached Data Integrity proofs in
/.well-known/did-witness.json(bounded toMAX_WITNESS_BYTES = 1024 \times 1024bytes). - At least $M$ distinct witnesses MUST sign the entry hash. If fewer than $M$ valid signatures are present, resolution fails.