mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-11-10 06:09:08 -05:00
285 lines
18 KiB
Markdown
285 lines
18 KiB
Markdown
NIP-41
|
|
======
|
|
|
|
Identity management
|
|
--------------------
|
|
|
|
`draft` `optional`
|
|
|
|
|
|
This NIP introduces a reliable and verifiable solution for users to manage their identities, along with standard procedures for dealing with catastrophic cases. It also outlines the differences between simple and secure identities.
|
|
|
|
# Basic concepts
|
|
* **Secured identities**
|
|
* These identities are composed of a **master keypair** and an **active subkey**.
|
|
* The master key pair is used to derive, secure and back up subkeys, and acts as a trusted source of truth for announcements related to subkeys.
|
|
* To derive a subkey it is recommended, but not mandatory, to use bip-32 as described in [NIP-06](https://github.com/nostr-protocol/nips/blob/master/06.md) with the derivation path `m/44'/1237'/<account>'/`. This gives us HD addresses. Other derivation paths can be used if the user knows what its doing. No derivation scheme needs to be used, users can just generate random keypairs and use them, this gives users less control over subkeys.
|
|
* The active subkey should be considered the current account in use for a master keypair, and there should only be one current subkey in use per master keypair.
|
|
* Both the master keypair and active subkey should maintain a list of relays in common.
|
|
* Users should follow the master keypair to stay up to date with announcements related to the active subkey or revocation certificates. However, it is not mandatory as the described conventions ensure backward compatibility. Subkeys can provide all necessary information to stay uptadate.
|
|
* *Secured identities* have two possible catastrophe cases, a **minor catastrophe case** if a subkey is leaked, and a **major catastrophe case** if a master keypair is leaked.
|
|
* Master keypairs can be stored in cold storage.
|
|
* **Simple identities**
|
|
* They are regular nostr identities that are not backed by a master keypair.
|
|
* A simple identity can be transformed in a *Secured identity* by following the conventions described below.
|
|
* *Secured identities* and *simple identities* differ from each other because *secured identities* master keypairs have a secure checkpoint created (event `kind:1775`) and a current subkey in use (event `kind:1776`). A subkey represents its role by adding a `p` tag to its kind-0, pointing to the public key of the master keypair.
|
|
* *Simple identities* and master keypairs don't contain a `p` tag in their kind-0 to indicate that they don't belong to or are associated with other accounts.
|
|
* *Simple Identities* doesn't have a published secure checkpoint event.
|
|
* **`kind:1775` Secure checkpoint**
|
|
* The master key pair of *Secured Identities* publishes this event to provide a timestamped point at which the account can be trusted, ideally at its creation.
|
|
* This event contains a hashed secret in its `content`.
|
|
* With this secured checkpoint, and by revealing the preimage of the hash in the event of a future catastrophe case, we can establish a verifiable link between the entity that created the checkpoint and the revocation certificate.
|
|
* This allows users to avoid having to define a new identity in advance.
|
|
* *Simple identities* don't need these events, and if they create them, they can be considered as part of the transformation into a *secured identity*.
|
|
* **`kind:1776` Key rotation announce/whitelist event.**
|
|
* *Secured Identities* uses these events to define the subkey it is rotating and the active subkey in use. This is considered a minor catastrophe case.
|
|
* *Simple identities* uses these events to whitelist pubkeys.
|
|
* If `kind:1776`, its published by a *simple identity* MUST be opentimestamped and SHOULD be accompanied by a `kind:1777` event.
|
|
* If `kind:1776` is published by a *secured identity*, it needn't be opentimestamped, but it SHOULD be published by the master keypair and the subkey from which it is rotating, and the event published by the subkey SHOULD point to the event published by the master keypair using an `e` tag.
|
|
* **`kind:1777` Key migration/Revocation certificate.**
|
|
* Revocation certificates or key migration are events that contain proofs that should be used to validate how trustworthy a new purpose identity is.
|
|
* When a *Simple Identity* publishes this event, a 60-day period begins during which a user may publish a competing migration event pointing to an earlier `kind:1776` event. After this period, clients SHOULD automatically update the user's follower list to the new pubkey.
|
|
* When the master key pair of a *secured identity* publishes a revocation certificate, clients can check to validate the following proofs.
|
|
* Checkpoint Proof: The client checks if a provided preimage and a secure checkpoint hash match.
|
|
* Social proof(optional): the event contains a list of people that the master keypair attaches to the revocation certificate as `p` tags. These pubkeys are designated as trusted identities that can judge/signal if the new proposed identity is trustworthy or not.
|
|
* If no social proof is required, clients can simply check the proofs provided, and if they are valid, they SHOULD automatically update the user's follower list to the new pubkey.
|
|
* If social proof is required, a 30-day period begins during which designated users can signal whether the new identity announced in the revocation certificate is trustworthy by reacting to the revocation certificate event with a `kind:7` reaction event.
|
|
* After this period, clients SHOULD automatically update the user's follower list to the new pubkey if there is sufficient evidence to validate the trustworthiness of the new identity.
|
|
* `kind:1775` and `kind:1777` MUST always be open timestamped. `kind:1776` SHOULD only be timestamped if it is published by a *simple identity*.
|
|
* Relays SHOULD NOT delete `kind:1040`, `kind:1775`, `kind:1776`, nor `kind:1777` events from their database upon receiving a `kind:5` event.
|
|
|
|
# Flow
|
|
## Creating a secured identity
|
|
Every *secured identity* starts as a *simple identity*. Then, if a *simple identity* wants to become a *secured identity*, it basically needs to publish a secure checkpoint event and then announce the subkey it's going to use.
|
|
- Secure checkpoint event `kind:1775`:
|
|
- This is an event, ideally created at the beginning when the account is created, and contains a hash of a secret that the user should define.
|
|
- The hashing algorithm can be Argon2 or Bcrypt as they are the most used for hashing and storing passwords (TBD).
|
|
- This event should be opentimestamped.
|
|
- This hashed secret can be different things:
|
|
- A user defined passphrase (best UX)
|
|
- A user defined passphrase + salt (good UX and improved security)
|
|
- A 12-word seed phrase (not best UX), as it can be confused with the account seed phrase.
|
|
- Anything the user can remember or securely store to prove they were the creator of this event.
|
|
- Checkpoint event example:
|
|
```json
|
|
{
|
|
"pubkey": "master-pubkey-A",
|
|
"kind": 1775,
|
|
"content": "hash of the secret",
|
|
//...
|
|
"tags": [
|
|
[ "proof", "<kind:1040-event-id>" ],
|
|
[ "alt", "secure checkpoint" ]
|
|
]
|
|
}
|
|
```
|
|
- `pubkey` is the pubkey of the master keypair is creating the checkpoint.
|
|
- `kind` MUST be `1775`
|
|
- `content` MUST be the hashed secret.
|
|
- `proof` tag MUST list the kind:1040 event that provides the Opentimestamp data.
|
|
|
|
- Subkey announce `kind:1776`:
|
|
- This event defines the subkey that will be in use.
|
|
- Subkey announce event example
|
|
```json
|
|
{
|
|
"pubkey": "master-pubkey-A",
|
|
"kind": 1776,
|
|
"content": "",
|
|
"tags": [
|
|
[ "p", "new-subkey-pubkey" ],
|
|
[ "alt", "subkey announce/rotation event" ]
|
|
]
|
|
}
|
|
```
|
|
- `pubkey` is the pubkey of the master key pair that is announcing its active subkey.
|
|
- `kind` MUST be `1776`
|
|
- `content` should be ignored
|
|
- `p` tag is used to define the active subkey
|
|
|
|
## Rotating/whitelisting a pubkey
|
|
### Secured identity
|
|
The user's master keypair (e.g. *master-pubkey A*) and active subkey (e.g. *subkey A*) each publish a `kind:1776` event, using a `p` tag to announce the subkey it's going to rotate to (e.g. *subkey B*).
|
|
|
|
The `kind:1776` event published by the master keypair SHOULD be published first, and the event published by the active subkey SHOULD point to it using an `e` tag.
|
|
|
|
The implementation can be done directly on regular clients or microapps to handle this type of thing could be implemented as well.
|
|
|
|
```json
|
|
// Master keypair kind:1776 example
|
|
{
|
|
"pubkey": "master-pubkey-A",
|
|
"kind": 1776,
|
|
"id" : "master-keypair-rotation-event-id",
|
|
"content": "",
|
|
"tags": [
|
|
[ "p", "subkey-B" ],
|
|
[ "alt", "subkey rotation event" ]
|
|
]
|
|
}
|
|
|
|
// Active subkey kind:1776 example
|
|
{
|
|
"pubkey": "subkey-A",
|
|
"kind": 1776,
|
|
"content": "",
|
|
"tags": [
|
|
[ "p", "subkey-B" ],
|
|
[ "e", "master-keypair-rotation-event-id"]
|
|
[ "alt", "subkey rotation event" ]
|
|
]
|
|
}
|
|
```
|
|
* `.content` SHOULD be ignored.
|
|
* The events MUST have a single `p` tag listing the new subkey (*subkey B*) that the user is rotating to.
|
|
* The event published by the old subkey (*subkey A*) MUST contain an `e` tag pointing to the event id of the master key pair rotation event.
|
|
|
|
Multiple `kind:1776` events will eventually exist, and they will represent the historical record of a subkey used by a *secured identity*.
|
|
|
|
Relays SHOULD NOT delete `kind:1776` events upon receiving a `kind:5` event.
|
|
### Simple identity
|
|
The user's active pubkey (e.g. *pubkey A*) publish an event `kind:1776` whitelisting a pubkey (e.g *pubkey* B) that can be used to migrate an identity to.
|
|
|
|
This should be done ahead of time, perhaps after a user has used Nostr enough for a few days. Clients can choose to prompt the user to "save a recovery kit" or different UXs when they see the user doesn't currently have a `kind:1776` published.
|
|
|
|
The implementation can be done directly on regular clients or microapps to handle this type of thing could be implemented as well.
|
|
|
|
```json
|
|
{
|
|
"pubkey": "pubkey-A",
|
|
"kind": 1776,
|
|
"content": "",
|
|
"tags": [
|
|
[ "p", "pubkey-B" ],
|
|
[ "proof", "<kind:1040-event-id>" ],
|
|
[ "alt", "pubkey whitelisting event" ]
|
|
]
|
|
}
|
|
```
|
|
|
|
* `content` SHOULD be ignored. Users might choose to use it to leave a base64 symmetrically-encrypted message of where they left the new key or anything else.
|
|
* The event MUST have a single `p` tag listing the whitelisted pubkey.
|
|
|
|
Multiple `kind:1776` events can exist. All `kind:1776` MUST be opentimestamped following [NIP-3](https://github.com/nostr-protocol/nips/blob/master/03.md).
|
|
|
|
Relays SHOULD NOT delete `kind:1040` nor `kind:1776` events upon receiving a `kind:5` event.
|
|
## Migrating to a pubkey
|
|
### Secured identity
|
|
If the user needs to rotate to a new subkey, they simply follow the conventions described in `Rotating/whitelisting a pubkey` above. This is considered a **minor catastrophe case**
|
|
|
|
By having a master keypair as the source of truth, users don't need to timestamp events, and clients can perform the switch to the new subkey as soon as they validate the `kind:1776` events published by the master keypair and the active subkey.
|
|
|
|
Checks to validate the migration to a new subkey:
|
|
* Verify that the `kind:1776` event published by the active subkey correctly points to the master keypair, and the event can be found.
|
|
* Verify the `kind:1776` events published by the master keypair and the active subkey to validate that the new proposed subkey matches.
|
|
|
|
Purpose flow for clients:
|
|
- Client discovers a new `kind:1776` event published by a subkey.
|
|
- Looks for the one published by the master keypair by using the `e` tag of the discovered event.
|
|
- Checks if the new purposed subkey matches
|
|
|
|
#### Following the new subkey
|
|
Once all these checks have been validated, clients MAY choose to display a warning to the user that the identity is migrating to a new subkey. Then offer an option to replace the old subkey in the user's follower list with the new one. Or automatically update the user's follower list.
|
|
### Simple identity
|
|
When the user needs to change keys they sign an event `kind:1777` with the new key and creates a NIP-03 attestation.
|
|
|
|
```json
|
|
{
|
|
"pubkey": "pubkey-B",
|
|
"kind": 1777,
|
|
"content": "I rolled my key using libbitcoin; moving to a new one just in case",
|
|
"tags": [
|
|
[ "p", "pubkey-A" ],
|
|
[ "e", "<kind:1776-event-id>" ],
|
|
[ "proof", "<kind:1040-event-id>" ],
|
|
[ "alt", "pubkey migration event" ],
|
|
[ "relays", "relay1", "relay2" ]
|
|
]
|
|
}
|
|
```
|
|
|
|
* `p` tag MUST list the previous pubkey
|
|
* `e` tag MUST list the `kind:1776` event that whitelisted the new pubkey.
|
|
* `proof` tag MUST list the `kind:1040` event that provides the Opentimestamp data of the `kind:1776` event.
|
|
* `relays` tag SHOULD list relays where both `kind:1776` and `kind:1040` events can be found.
|
|
* `.content` SHOULD be ignored; users can optionally write a message explaining the migration.
|
|
#### Following the new pubkey
|
|
Upon seeing this event, the client MAY choose to display a warning to the user that the identity is migrating to a new key. The client should not take any automated action at this point since the migration could be an attack, but the user could communicate out of band with the user to confirm the migration.
|
|
|
|
After 60 days of seeing the `kind:1777` event, the client SHOULD automatically update the user's follow list to the new pubkey after some verifications:
|
|
When users who follow the old pubkey see a `kind:1777` event they SHOULD:
|
|
|
|
* check `kind:1776` and `kind:1777` event signatures
|
|
* check `kind:1777`'s `pubkey` matches `kind:1776`'s `p` tag
|
|
* check `kind:1777` is more than 60 days old
|
|
* check that no competing 1777 event exists pointing to an event with an older valid OTS proof
|
|
|
|
After validating all these checks clients SHOULD replace the old pubkey in the user's follow list with the new one.
|
|
|
|
## Migrating to a master keypair
|
|
|
|
> This can only be done by *secured identities*, as *simple identities* doesn't use a master key pair.
|
|
|
|
If the user needs to migrate to a new master keypair, a new secure checkpoint must first be created with the new master keypair to be used (e.g. *master-keypair B*).
|
|
|
|
Then the user's active master keypair (e.g. *master-keypair A*) signs and timestamps a `kind:1777` event containing a set of proofs as described above and announces the new master keypair (*master-keypair B*) to which it is migrating.
|
|
|
|
This should be done at the moment a user notices the attack, and the revocation certificate MUST contain the checkpoint proof and an `i` tag containing the new pubkey of the master keypair it will use (*master-keypair B*), and the `eventid` of the checkpoint event created by the new master keypair.
|
|
|
|
The active subkey SHOULD also publish a `kind:1776` event without a `p` tag, pointing to this `kind:1777` event using the `e` tag for backward compatibility.
|
|
|
|
Public revocation certificate event example:
|
|
```json
|
|
{
|
|
"pubkey": "master-pubkey-A",
|
|
"kind": 1777,
|
|
// Preimage for proof of checkpoint
|
|
"content": "<preimage of the checkpoint event>",
|
|
"tags": [
|
|
// Checkpoint event id
|
|
[ "e", "<master-pubkey-A-checkpoint-event-id>" ],
|
|
// Declaring new master public key and id of checkpoint id
|
|
[ "i", "nostr:master-pubkey-B", "<master-pubkey-B cpid>" ],
|
|
// (optional) List of designated people to provide social proof
|
|
["p", "witness1-pubkey"],
|
|
["p", "witness2-pubkey"],
|
|
["p", "witness3-pubkey"],
|
|
// OTS
|
|
["proof", "<kind:1040-event-id>"]
|
|
// Relays where to look for checkpoints
|
|
[ "relays", "relay1", "relay2" ],
|
|
[ "alt", "revocation announce event" ]
|
|
]
|
|
}
|
|
```
|
|
|
|
* `.content` MUST contain the preimage of the hash contained in the checkpoint event defined in the `e` tag
|
|
* `e` tag MUST point to the `kind:1775` checkpoint event that `.content` should match .
|
|
* `i` tag MUST list the pubkey and `event-id` of the new master keypair's checkpoint.
|
|
* `p` tags SHOULD list designated people who should be trusted to judge how trustworthy the new purposed identity is.
|
|
* `relays` tag SHOULD list relays where both `kind:1775` and `kind:1040` events can be found.
|
|
|
|
At the moment the revocation certificate becomes public, clients can validate the proofs provided and, if social proof is required, a 30-day period will start during which the designated people can judge how trustworthy the new proposed identity is.
|
|
|
|
After this period, clients SHOULD prompt the user with the results or automatically update the user's follower list if the result is more than 51% of the designated people agree.
|
|
|
|
If no social proof was required and the provided proofs succeed, clients SHOULD prompt the user to switch and follow the new master key pair (*master-keypair* B).
|
|
|
|
# Notes:
|
|
### *Secured identities* relays:
|
|
Secured Identities Master Keypair and Subkeys SHOULD maintain the same list of relays. Subkeys can add relays, but should not delete those shared with their master keypair.
|
|
### Paid relays/services:
|
|
Paid relays or services shouldn't care about master keypairs or subkeys (It is up to them if they take care of it). Active master keypairs or subkeys should subscribe to the relay/service indifferently if they want to use it.
|
|
### Account search and discover-ability:
|
|
If a user searches for a master key pair, the search results will probably show the subkeys, as they have the master key pair's pubkey in their `kind:0`. And vice versa, if a user searches for a subkey, the search result will probably show the master keypair.
|
|
### Leaked subkeys:
|
|
A leaked subkey can be any subkey that is not present in the last `kind:1776` event of a master key pair. Or any subkey that has a published `kind:1776` event.
|
|
### Rational behind the 30/60 days delay
|
|
#### 30 day delay
|
|
This gives designated users enough time to review and then signal/judge whether they think a revocation event `kind:1777` should be considered trustworthy or not.
|
|
#### 60 day delay
|
|
This allows enough time for a user to notice a migration request published by an attacker, and for the attacker to publish a competing migration request pointing to an earlier `kind:1776` whitelisting event.
|
|
### Preventing unpublished evil `kind:1777` attack
|
|
Clients should keep track of when a `kind:1777` event should take into effect. This is to prevent an attacker creating an evil `kind:1776`, its attestation, and a `kind:1777` event with its attestation and not publishing them until the 60 days of the attestation have elapsed.
|
|
### Preventing poorly-distributed evil `kind:1777` attack
|
|
Additionally, clients SHOULD broadcast the `kind:1777` events to the relays it normally writes to. This is to prevent an attacker from creating a short-lived NIP-65 relay list where only a subset of users will see an evil `kind:1777` event but not widespread enough for the real owner to notice it. |