TBD54566975 / did-nostr

35 stars 8 forks source link

did:nostr

Thought experiment around bringing dids to nostr.

Why?

I have no idea whether this will work, or if it's a good idea. What has me interested is:

Brain Dump

DIDs

DIDs, like URLs are fully qualified URIs. Similar to a URL, DIDs can be resolved. resolving a URL like https://snort.social/ returns html. So what does resolving a DID return? A DID document

DID Documents

What is a DID document? it's a JSON object that contains information about the DID e.g.

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
  ],
  "id": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48",
  "verificationMethod": [
    {
      "id": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48#nip06-0",
      "type": "SchnorrSecp256k1VerificationKey2019",
      "controller": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48",
      "publicKeyHex": "e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48"
    }
  ],
  "service": [
    {
      "id": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48#nostr-relays",
      "type": "Relay",
      "serviceEndpoint": ["wss://relay.damus.io", "wss://relay.nostr.info"]
    },
    {
      "id": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48#ln",
      "type": "LightningNode",
      "serviceEndpoint": "ip://024bfaf0cabe7f874fd33ebf7c6f4e5385971fc504ef3f492432e9e3ec77e1b5cf@52.1.72.207:9735"
    }
  ],
  "keyAgreement": [
    {
      "id": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48#keyagreement",
      "type": "X25519KeyAgreement2023",
      "controller": "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48",
      "publicKeyHex": "75d92cea4ab8ef28a0a14acf103d6b8a2bb026120d62d1817fa5a4b11f534038"
    }
  ]
}

💡 TODO: think of more service examples

💡 TODO: figure out id property for keyAgreement

so what do the properties in the DID document mean? property description notes
verificationMethod includes crypto keys that can be used for various purposes, such as to verify digital signatures the key shown in the example above happens to be the decoded nostr pubkey
service lists services that can be used to interact with a DID the example includes the nostr relays that this DID publishes to. Could also include any other service e.g. a lightning node
keyAgreement lists keys that can be used to generate shared keys for encryption/decryption purposes

💡 there are several other properties that can exist on DID documents. more info on that here

NIP-9325

⚠️ WIP ⚠️

This NIP proposes the following:

DID generation

did:nostr:<nostr_hex_pubkey>

  1. generate a new (or use an existing) nostr pubkey according to BIP340 as mentioned in nip01. nostr_hex_pubkey is represented as 32-bytes lowercase hex-encoded public key
  2. prefix the nostr_hex_pubkey with did:nostr:

Example

did:nostr:41e791de6a6f6f0b3c820c2db179c0679e2c228ae6ecb9583cb48b3e1ff354b6

💡 reference implementation

💡 if desired nostr dids can be displayed using the bech32 encoded npub format described in nip19 e.g. did:nostr:npub1u2.... As stated in nip19: The bech32 encodings should not be used to represent a did in nostr events.

Deriving base DID Document

the base DID document can be derived without sending a publish message to a relay. It's not all that useful in and of itself, but forms the foundation of DID resolution.

💡 reference implementation

💡 TODO: write out steps to derive base DID Document by reading reference implementation

// from example.ts in reference implementation
const did = pubKeyToDid(kp.public);
console.log(deriveDidDoc(did));

Output

{
  "id": "did:nostr:41e791de6a6f6f0b3c820c2db179c0679e2c228ae6ecb9583cb48b3e1ff354b6",
  "verificationMethod": [
    {
      "id": "did:nostr:41e791de6a6f6f0b3c820c2db179c0679e2c228ae6ecb9583cb48b3e1ff354b6#nip06-0",
      "type": "SchnorrSecp256k1VerificationKey2019",
      "controller": "did:nostr:41e791de6a6f6f0b3c820c2db179c0679e2c228ae6ecb9583cb48b3e1ff354b6",
      "publicKeyHex": "41e791de6a6f6f0b3c820c2db179c0679e2c228ae6ecb9583cb48b3e1ff354b6"
    }
  ]
}

💡 the verificationMethod in the example above is a schnorr pubkey. SchnorrSecp256k1VerificationKey2019 is used to describe the type of verification method. Honestly not entirely sure what the motiviation is behind including the year (aka 2019).

DID Event

image

This event can be used to publish, patch, and recover a DID. every 9325 message should contain the o (e.g op) and d (e.g. did) tags

{
  "kind": 9325,
  "tags": [
    ["o", "publish | patch | recover"],
    ["d", "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48"]
  ]
}

publish

Event

{
  "kind": 9325,
  "pubkey": "e3932a8cd4ec81e1e9a41467470ec9db817accf21e1e8b939525e84da6786d9a",
  "created_at": 1676957260,
  "tags": [
    ["d","did:nostr:e3932a8cd4ec81e1e9a41467470ec9db817accf21e1e8b939525e84da6786d9a"],
    ["o","publish"]
  ],
  "content": "{\"r\":\"683c867bf3dc5e7993fdd0715771d36eb6f00b8a8645795c332246ea5f19c7d5\",\"patches\":[{\"op\":\"add\",\"path\":\"/service\",\"value\":[{\"id\":\"did:nostr:e3932a8cd4ec81e1e9a41467470ec9db817accf21e1e8b939525e84da6786d9a#nostr-relays\",\"type\":\"NostrRelay\",\"serviceEndpoint\":[\"wss://relay.damus.io\"]}]}]}",
  "sig": "b1bae7a64d84ce63e40dfa24d299892116694ae71215f357ddad0a0091455e0b985b45ccca6f9354223dc3f0243a9eabcb6d874ff85fac7051493fee8aab2ef9"
}
Property Description
r double hashed (sha256) encoded recovery pubkey
patches an array of JSON patch ops that are applied on top of the base DID Document

Integrity Checks

recover

Event

{
  "content": "{\"r\":\"0df32bb15f48f60c5d7e035bdfbc67507a8576109a422dd860ad1a7b4935b3e6\"}",
  "created_at": 1676957711,
  "kind": 9325,
  "pubkey": "b424a3e0ea03a1450feef4cdba3dd74892c3e18726f079f89db42d597dfceb4c",
  "tags": [
    ["d","did:nostr:89abca8adb7d5db8a4b4e90b0c0eb97dc6f3a957fc219a83bf925fb9c7bd4331"],
    ["o", "recover"],
    ["e", "bd513634c49754383d9d4110ebeaa1433467c5b70da7ae2d2873cabcd5445451", "wss://relay.damus.io", "reply"]
  ],
  "sig": "1f3fdaf79dcf9fb7ab51834c80b28abc5e6a3850f0185bb5b616f4a5e133f188dc609be10462e551ea984fe403a32a4b0b8b492d0c9c45f63d4fdd4d38d8c46d",
}
Property Description
r new double hashed (sha256) recovery key

💡 Note: any r should not be used more than once. Recovering should always include a new r that can be used for subsequent recoveries

Integrity Checks

  • sha256(sha256(event.pubkey)) should match JSON.parse(previousEvent.content).r of the most recent recover event if one exists or the initial publish event if no other recover events exist

patch

Event

{
  "kind": 9325,
  "tags": [
    ["d", "did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48"]
    ["e", "event id of most recent 9325 event for did"]
    ["o", "patch"],
  ],
  "content": "{\"patches\":[{\"op\":\"add\",\"path\":\"/service\",\"value\":[{\"id\":\"did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48#nostr-relays\",\"type\":\"NostrRelay\",\"serviceEndpoint\":[\"wss://relay.damus.io\"]}]}]}"
}
Property Description
patches an array of JSON patch ops

Resolving

💡 WIP reference implementation


Given a DID (e.g. did:nostr:e2bdaa90e96a4fafb9f1c36f9b378e4bbd6fea26e5d47063e7b30aa15de37d48):

Considerations