did:cow, a proposal for an ID resolution method with most of the convenience of did:plc/did:web and the robustness of a public blockchain
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

remove the hash part and just stick everything in the did

+31 -120
+31 -120
spec.md
··· 5 5 6 6 ## Abstract 7 7 8 - The `did:cow` method (Consensus Origin Wrapper) provides persistent wrappers around other DID methods, enabling rotation and migration without breaking existing references. Uses Ethereum for state transitions, content-addressable storage for zero-cost creation. 8 + The `did:cow` method (Consensus Ownership Wrapper) provides persistent wrappers around other DID methods, enabling rotation and migration without breaking existing references. Uses Ethereum for state transitions, content-addressable storage for zero-cost creation. 9 9 10 10 ## Status of This Document 11 11 ··· 19 19 - **did:key** - No rotation or recovery 20 20 - **did:web** - Domain dependency 21 21 - **did:plc** - Centralized sequencer (Bluesky's PLC server) 22 + - **did:ethr** - Gas costs for all updates 22 23 23 24 Migrating between methods breaks all existing references. `did:cow` provides a stable wrapper. 24 25 25 - **Key advantage:** Ethereum sequencing eliminates did:plc's centralized reorg risk: 26 - - **Reorg protection** - Ethereum finality prevents operation log rewrites 27 - - **Operational independence** - Rotate away from did:plc without permission 28 - - **Censorship resistance** - Bonus: no single entity can block updates 29 - 30 - **Solving the did:plc Centralization Problem:** 31 - 32 - The primary centralization risk in did:plc is that the PLC directory controls operation sequencing and can censor updates. By wrapping a did:plc in did:cow, key rotation and recovery operations are secured by Ethereum's blockchain instead of the PLC directory: 33 - 34 - - **Without did:cow:** `did:plc:abc` rotation requires PLC directory to sequence and publish updates 35 - - **With did:cow:** `did:cow:xyz → did:plc:abc` rotation happens via Ethereum transaction, creating a new did:plc:def and updating the wrapper atomically 36 - 37 - If the PLC directory becomes unavailable, censors updates, or acts maliciously, the wrapper controller can rotate to a different DID method entirely (did:web, did:key, or another did:plc instance) without losing their persistent identifier. 38 - 39 26 ### 1.2 Design Goals 40 27 41 28 1. **Persistent** - Wrapper DID never changes ··· 45 32 5. **Decentralized** - No central registry dependency 46 33 6. **Transferable** - Controller can be changed 47 34 7. **Rotation Independence** - Key rotation secured by Ethereum, not the wrapped DID's infrastructure 35 + 8. **Composible Control** - Automatic compatibility with multisig and decentralized organization tooling such as Gnosis Safe. 48 36 49 37 ## 2. DID Method Name 50 38 51 - Method name: `cow` (Consensus Origin Wrapper) 39 + Method name: `cow` (Consensus Ownership Wrapper) 52 40 53 41 DID prefix: `did:cow:` (lowercase) 54 42 55 43 ## 3. Method Specific Identifier 56 44 57 - Format: `did:cow:<hash>` 45 + Format: `did:cow:<controller_address>:<initial_wrapped_did>` 58 46 59 - Hash computation: 60 - ``` 61 - hash = SHA256(controller_address || wrapped_did) 62 - ``` 63 47 64 48 **Parameters:** 65 49 - `controller_address` - Ethereum address (20 bytes, no "0x" prefix) 66 - - `wrapped_did` - UTF-8 encoded DID string 67 - - `||` - Binary concatenation 50 + - `initial_wrapped_did` - UTF-8 encoded DID string 68 51 69 52 ### 3.1 Example 70 53 71 54 ``` 72 - controller_address = "742d35Cc6634C0532925a3b844Bc9e7595f0bEb" (20 bytes, no 0x prefix) 55 + controller_address = "8BC101ABF5BcF8b6209FaaAD4D761C1ED14999Be" (20 bytes, no 0x prefix) 73 56 wrapped_did = "did:web:example.com" 74 57 75 - preimage = bytes.fromhex("742d35Cc6634C0532925a3b844Bc9e7595f0bEb") + 76 - "did:web:example.com".encode('utf-8') 77 - 78 - hash = SHA256(preimage) 79 - = "8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52" 80 - 81 - DID = did:cow:8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52 58 + DID = did:cow:8BC101ABF5BcF8b6209FaaAD4D761C1ED14999Be:web:example.com 82 59 ``` 83 - 84 - **Parsing:** 85 - 1. First 20 bytes → controller_address 86 - 2. Remaining bytes (UTF-8) → wrapped_did 87 - 3. Verify: SHA256(controller_address || wrapped_did) == hash 88 60 89 61 ## 5. Blockchain Transaction Model 90 62 ··· 103 75 pragma solidity ^0.8.0; 104 76 105 77 contract COWRegistry { 106 - struct State { 78 + struct COW { 107 79 address controller; 108 - bytes wrappedDid; 109 80 bool deactivated; 110 - uint256 lastUpdated; 81 + bytes wrappedDid; 111 82 } 112 83 113 - mapping(bytes32 => State) public didStates; 84 + mapping(bytes32 => State) public cows; 114 85 115 86 event DIDUpdated(bytes32 indexed didHash, address controller, bytes wrappedDid); 116 87 event DIDDeactivated(bytes32 indexed didHash); 88 + 89 + modifier onlyOwner() { 90 + } 91 + 92 + function updateWrappedDID( 93 + bytes32 didHash, 94 + bytes calldata newWrappedDid 95 + ) external onlyOwner { 96 + cows[didHash].wrappedDid = newWrappedDid; 97 + } 117 98 118 - function update( 99 + function updateController( 119 100 bytes32 didHash, 120 101 address newController, 121 - bytes calldata newWrappedDid 122 102 ) external { 123 103 State storage state = didStates[didHash]; 124 104 ··· 174 154 175 155 **Process:** 176 156 1. Choose `wrapped_did` and `controller_address` 177 - 2. Compute `hash = SHA256(controller_address || wrapped_did)` 178 - 3. Construct DID: `did:cow:<hash>` 179 - 4. Create initial state: `[20 bytes: controller][N bytes: wrapped_did]` 180 - 5. Store at content-addressable location (IPFS, Arweave, etc.) 157 + 2. Construct DID: `did:cow:<controller_address>:wrapped_did` 181 158 182 159 **Verification:** Anyone can fetch the state, recompute hash, confirm it matches the DID. 183 160 ··· 224 201 225 202 ## 7. Security Considerations 226 203 227 - ### 7.1 Hash Collision Resistance 228 - 229 - SHA-256 provides 256-bit collision resistance. Accidental collision is negligible. Preimage attacks are computationally infeasible. 230 - 231 - ### 7.2 Controller Key Security 204 + ### 7.1 Controller Key Security 232 205 233 206 Security depends on: 234 207 1. Controller's Ethereum private key (secp256k1) 235 208 2. Wrapped DID's keys 236 209 237 - **Authorization:** Ethereum transactions validated by smart contract (`msg.sender == controller`). Replay protection via nonces. Gas costs prevent spam. 238 - 239 210 Compromised controller key → attacker can update wrapper. 240 211 Compromised wrapped DID keys → attacker can impersonate within that method. 241 212 242 - **Mitigation:** Hardware wallets, multisig contracts (Gnosis Safe), account abstraction (ERC-4337). 243 - 244 - ### 7.3 Wrapped DID Dependence 213 + ### 7.2 Wrapped DID Dependence 245 214 246 - Inherits ALL security properties of wrapped DID: 215 + Inherits ALL security properties of wrapped DID, but is recoverable: 247 216 - did:web → DNS hijacking risk 248 217 - did:key → no rotation 249 218 - did:plc → trust in Bluesky's directory 250 219 251 - **Wrapper only provides portability, not security.** 252 - 253 - **did:plc special case:** Wrapping did:plc adds exit capability. Day-to-day trusts PLC directory; crisis allows rotation via Ethereum. Primary concern is subtle reorgs, not censorship. Ethereum anchoring makes canonical state independent of PLC's operation log. 254 - 255 - **Exception - did:plc Rotation:** 256 - 257 - While daily operations with a wrapped did:plc still depend on the PLC directory for resolution, **key rotation and recovery are secured by Ethereum instead of PLC**. This means: 258 - 259 - - PLC directory can censor or fail → rotate to new DID method via Ethereum 260 - - PLC directory compromised → maintain control through Ethereum-based wrapper updates 261 - - No dependency on PLC for the critical security operation of key rotation 262 - 263 - This significantly reduces the centralization risk compared to using did:plc directly. 264 - 265 - ### 7.4 Registration Race Conditions 266 - 267 - Multiple parties can create wrappers with same `wrapped_did` but different `controller_address` (different hashes, different DIDs). 268 - 269 - Not a security issue: 270 - - Each wrapper independently controlled 271 - - Wrapper doesn't grant authority over wrapped DID 272 - - Social/technical adoption determines authoritative wrapper 273 - - Identity verified through wrapped DID's cryptographic proofs 274 - 275 - ### 7.5 Blockchain Dependencies 220 + ### 7.3 Blockchain Dependencies 276 221 277 222 Uses Ethereum mainnet for state transitions, immutable audit trail, authorization, and spam prevention (gas costs). 278 223 279 224 **Why Ethereum:** High security, established ecosystem, ECDSA signing, native smart contracts, deterministic finality. 280 225 281 - **Tradeoffs:** Gas costs (~50-100k gas per update), ~12 second confirmation, MEV/front-running (mitigated by validation), L2 not supported in v0.1. 226 + **Tradeoffs:** Gas costs (~50-100k gas per update), ~12 second confirmation 282 227 283 228 ## 8. Privacy Considerations 284 229 285 - ### 8.1 Correlation Risk 286 - 287 - `wrapped_did` visible in initial state, on-chain transactions, and resolution responses. Same wrapped DID in multiple contexts enables trivial correlation. 288 - 289 - **Mitigation:** Use pairwise wrapped DIDs. 290 - 291 - ### 8.2 Controller Address Linkability 230 + ### 8.1 Controller Address Linkability 292 231 293 232 `controller_address` appears in hash computation and on-chain. Reusing controller links all DIDs. 294 233 295 234 **Mitigation:** Different controllers per context or privacy-preserving addresses. 296 235 297 - ### 8.3 On-Chain Metadata 236 + ### 8.2 On-Chain Metadata 298 237 299 238 All updates permanently public with timestamps. Creates audit trail of updates, previous/new wrapped DIDs, and controller history. 300 - 301 - Unavoidable in current design. 302 239 303 240 ## 9. Reference Implementation 304 241 ··· 317 254 318 255 Given: 319 256 ``` 320 - did:cow:8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52 321 - ``` 322 - 323 - Created from binary state: 324 - ``` 325 - [742d35Cc6634C0532925a3b844Bc9e7595f0bEb (20 bytes)] 326 - [did:web:example.com (UTF-8)] 257 + did:cow:8BC101ABF5BcF8b6209FaaAD4D761C1ED14999Be:web:example.com 327 258 ``` 328 259 329 260 Wrapping: ··· 432 363 ### 12.5 Multi-Identity Aggregation 433 364 434 365 Rotate between multiple DIDs for different contexts while maintaining one persistent identifier. 435 - 436 - ## 13. Open Questions 437 - 438 - ### 13.1 Content-Addressable Storage 439 - 440 - IPFS only? Any system? Multiple fallbacks? 441 - 442 - Current: Any system with hash-based retrieval. 443 - 444 - ### 13.2 Smart Contract Deployment 445 - 446 - Canonical contract address? Multiple deployments? ENS registration? 447 - 448 - Current: Single canonical contract at well-known address. 449 - 450 - ### 13.3 Layer 2 Scaling 451 - 452 - Support Optimism/Arbitrum? zkSync/StarkNet? Cross-L2 resolution? 453 - 454 - Current: Ethereum mainnet only for v0.1. 455 366 456 367 ## 14. References 457 368