-
Notifications
You must be signed in to change notification settings - Fork 720
NIP-102: Subkey Attestation #1450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ynniv
wants to merge
3
commits into
nostr-protocol:master
Choose a base branch
from
ynniv:nip-100/subkeys
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| NIP-102 | ||
| ======= | ||
|
|
||
| Subkey Attestation and Management | ||
| ------------------------------ | ||
|
|
||
| `draft` `optional` | ||
|
|
||
| This NIP defines a way to separate identity from authentication using subkey attestations. This allows the use of one keypair to issue independent keypairs for different apps. If any subkey key is compromised, the root keypair can publish an event revoking the key. | ||
|
|
||
| ## Attestation | ||
|
|
||
| A subkey attestation is created by signing the message: | ||
|
|
||
| ``` | ||
| nip102:<hex-subkey1-pubkey> | ||
| ``` | ||
|
|
||
| This signature is then added to events to demonstrate authorization to post on behalf of the original key. | ||
|
|
||
| The parent key can publish a `Kind 10102` event for subkey management. This event lists attestations that have been revoked, as well as those that should be used to encrypt messages so that they can be read using preferred clients. These declarations are only a preference, as they may not be available in a timely manner. | ||
|
|
||
| Content: | ||
| ```json | ||
| { | ||
| "inbox_keys": ["<hex-subkey1-pubkey>", "<hex-subkey2-pubkey>"], | ||
| "revoked_subkeys": ["<hex-subkey3-pubkey>"] | ||
| } | ||
| ``` | ||
|
|
||
| ## Event Signing | ||
|
|
||
| When signing events using a subkey, the account pubkey will be included as a well-known attribute. The event will also include an attestation of the subkey's authorization signed by the main key. Relays and clients should validate this attestation, and discard messages that make invalid claims. | ||
|
|
||
| ```json | ||
| { | ||
| "pubkey": "<hex-subkey1-pubkey>", | ||
| "kind": 1, | ||
| "tags": [ | ||
| ["I", "<hex-account0-pubkey>"], | ||
| ["Ia", "<subkey1-attestation>"] | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ## Event Validation | ||
|
|
||
| An implementing client's subscriptions will be slightly different. For each subscription using `authors`, a second subscription will be registered specifying `#I` instead of `authors`. This will collect all events published by subkeys of the authors, each of which will be checked for a valid attestation in `Ia`. This can be done at the same time the event signature is validated. As the client finds new values of `I`, it will request `Kind 10102` events with an author of `I` in order to check for revocations. When a `Kind 10102` event with `revoked_attestations` is received, use this index to find events that are no longer valid. | ||
|
|
||
| After validation, the client will treat an event as if it were signed by the pubkey specified in `I`. This includes honoring NIP-9 deletion requests for events authored by `account0` as well as those having an `I` of `account0` (eg, a subkey acting on behalf of `account0`). | ||
|
|
||
| If `subkey1` publishes an event without an `I` attribute, it should be treated as having an author of `subkey1` and NOT `account0`. `subkey1` may have its own profile, etc, separate from `account0` that is used in these circumstances. | ||
|
|
||
| ## Destructive Events | ||
|
|
||
| `NIP-09` deletion requests should be honored for events authored by `account0` or having an `I` of `account0`. | ||
|
|
||
| ## Adoption | ||
|
|
||
| ### Stage 1: | ||
|
|
||
| Client support is required for basic functionality, so we start when the first client implements this spec. | ||
|
|
||
| The client will be able to sign messages for the keypair `account0`. A new keypair `subkey1` is generated, and an attestation that `subkey1` is authorized to post on behalf of `account0` is signed by `account0`. For brevity we will say this is a key rotation, and the client now switches to signing messages using `subkey1` with `subkey1-attestation`. The client publishes the message: | ||
|
|
||
| ```json | ||
| { | ||
| "kind": 1, | ||
| "content": "GM", | ||
| "I": "<hex-account0-pubkey>", | ||
| "Ia": "<subkey1-attestation1>" | ||
| } | ||
| ``` | ||
|
|
||
| All instances of the client will create secondary subscriptions with `#I` instead of `authors`. Valid events will be merged with those authored by the key in `I`, though destructive events (deletions / replacements) will be deferred until a `Kind 10102` has been retrieved for the pubkey `I`. | ||
|
|
||
| Clients that do not support this NIP will continue to treat `subkey1` and `account0` as different accounts. | ||
|
|
||
| ### Stage 2: | ||
|
|
||
| Relays should ensure that `I` is indexed, and may drop events with invalid `Ia` attestations or have been revoked with the expectation that these will be discarded by clients anyway. Replacement events should ignore `I` and maintained per signing key until there is sufficient client support. In the interim conforming clients will merge these locally. | ||
|
|
||
| ## Reference Code | ||
|
|
||
| ```python | ||
| import secp256k1 | ||
|
|
||
| def create_attestation(account_privkey, subkey_pubkey): | ||
| privkey = secp256k1.PrivateKey(bytes.fromhex(account_privkey)) | ||
| signature = privkey.ecdsa_sign(bytes.fromhex(subkey_pubkey)) | ||
| return privkey.ecdsa_serialize(signature).hex() | ||
|
|
||
| def verify_attestation(account_pubkey, subkey_pubkey, attestation): | ||
| pubkey = secp256k1.PublicKey(bytes.fromhex(account_pubkey), raw=True) | ||
| signature = pubkey.ecdsa_deserialize(bytes.fromhex(attestation)) | ||
| return pubkey.ecdsa_verify(bytes.fromhex(subkey_pubkey), signature) | ||
|
|
||
| # Example usage | ||
| account_privkey = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" | ||
| account_pubkey = secp256k1.PrivateKey(bytes.fromhex(account_privkey)).pubkey.serialize().hex() | ||
| subkey_pubkey = "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321" | ||
|
|
||
| attestation = create_attestation(account_privkey, subkey_pubkey) | ||
| # attestation is "30440220198c94e388c3a5d7eed7f66ea83dd60a0156ba612c1d5067286ace5c641cbb600220739ca9cd3f3780f28c3a98df954736e323d3f23905bfea4482365055b2fb9fe5" | ||
|
|
||
| print(f"Attestation is valid: {verify_attestation(account_pubkey, subkey_pubkey, attestation)}") | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is correct because we won't search for the attestation. If there are other reasons to use single characters I was considering
J.