Skip to content
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

NIP-0b - On-Behalf of (Simple Sub-Key Management) #1482

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

ice-orestes
Copy link

@ice-orestes ice-orestes commented Sep 6, 2024

Prior art:

  • NIP-26: Multiple key delegation and no revocation (just time bound)
  • NIP-41: More complete single key delegation and revocation (complex)

This NIP defines a simple way for an Identity Sub-Key management system, including active, inactive and revoked public keys, for publishing On-Behalf of a master identity.

Why?

Each of these methods has their own pros and cons, but there's a very common use case that is not covered by any of these methods completely, which is having a master identity that is secure (possibly in cold storage), that can whitelist and blacklist multiple public keys (one for each device or app, for example), that can be used individually (less common but interesting for media management companies) and can publish on-behalf of one or several master identities.

Using NIP-26 for this purpose doesn't allow for blacklisting (revocation), or alternatively requires time bound delegation, with new delegation signing at every expiry. On the other hand NIP-41 is a more complete and complex identity management solution, but only allows for one active sub-key at any point in time, and that sub-key's only purpose is bound to the main identity.

Copy link
Collaborator

@vitorpamplona vitorpamplona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments. In general, this does create significant burdens for signers and verifiers as well as broken user experiences between clients and relays that do and don't implement it. The only way to solve the UX is to make this mandatory for all of Nostr, which significantly increases the startup time of any Nostr project. Complexity goes through the roof.

It also does not solve how to deal with encryption and shared secrets for event kinds that need it, like DMs and private group chats. Meaning that, subkeys don't see what the main account and other subkeys are encrypting/decrypting even when authorized.

More importantly, this type of delegation changes the indexing of replaceable events. Since subkeys can change replaceables of the main key, queries by a tag must be broken down to also search by changes from authorized b keys. Relays that keep only the last event per pubkey in the 10000-20000 range must now also account for subkeys and those subkeys can be dynamically changed at any time, past and future (by rebroadcasting past kind 10100 events or deleting them, reverting to a previous state). A replaceable that has a b tag cannot affect the indexing of the sub key's own replaceables and should not be returned when a client requests the replaceables of the subkey.

One can also create an elaborate attack by using a past kind 10100 when the stolen subkey was still authorized. The attacker gets the past version, sends it to a relay the attacker controls, and doesn't allow that event to be replaced by newer versions. Now new posts by the sub key can make clients think the attacker's relay is the main account's relay, which will make users see the stolen key as still valid.

or

```
"revoked:<timestamp>"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of the revoked timestamp if it is going to invalidate everything?

It's easier to just delete the key from the list.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list has to keep all history to keep track of revocations, exactly to avoid replay attacks of past events

"active:<timestamp>:<kinds comma separated list, optional>"
```

Last `:` is only required if an optional kinds list is present. Without a kinds list, the attestation is valid for all event kinds besides `kind 10100`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinds MUST be required, because otherwise subkeys can delete posts of the main account and can also change kind:10100 of the main account.

There are many kinds that don't work well with delegation or that the delegation gets ambiguous. For instance, what should kinds that use encryption, like DMs, do? Encrypt to the main account or keep working as if there is no delegation?

Can subkeys have their own subkeys ad infinitum by having multiple bs to build the stack? And if so, do verifiers need to verify the complete chain?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinds MUST be required, because otherwise subkeys can delete posts of the main account and can also change kind:10100 of the main account.

Not against sub-keys deleting posts by master or any other sub-account. The kind 10100 is not allowed to be signed by any delegation mechanism, only the master account can sign those events.

There are many kinds that don't work well with delegation or that the delegation gets ambiguous. For instance, what should kinds that use encryption, like DMs, do? Encrypt to the main account or keep working as if there is no delegation?

Those kinds shouldn't take b tag into account... see my reply below about DM's etc.

Can subkeys have their own subkeys ad infinitum by having multiple bs to build the stack? And if so, do verifiers need to verify the complete chain?

Don't like this idea, I think that only one level is enough. Maybe that should be more explicit in the NIP?

0b.md Outdated Show resolved Hide resolved
The **Inactive** or **Revoked Attestations** should be a string in the following formats:

```
"inactive:<timestamp>"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be a timestamp as the inactive key can still post in the past, during the active period. It should contain a bloomfilter with all the valid event_ids made during the time the key was active.

The main account gathers all valid events and merges their IDs in a bloom filter and posts it, likely as a separate event because this will get big very quicky (Imagine corporate accounts)

Verification will check if the event id is in the list or not.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of the inactive attestation is for sub-accounts that were not compromised, just inactivated. Imagine the use case of an iPhone that you want to replace, you request to the master account to inactivate that sub-account as the key is removed from the device, effectively destroying the key. If the key was leaked by any means, than the revocation is needed, not the inactivation.

### Relay & Client Support

Relays should answer requests such as `["REQ", "", {"authors": ["A"]}]` by querying both the `pubkey` and on-behalf `#b` tags `[1]` value.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIP-42 MUST allow the use of the subkey to login if authorized.

Copy link
Author

@ice-orestes ice-orestes Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool. So you mean that kind 22242 ephemeral event on the AUTH message could use the b tag to reference the master account, and the relays should authenticate the main account when such b tag is present?

@fiatjaf
Copy link
Member

fiatjaf commented Sep 6, 2024

Vitor said it all.

@ice-orestes
Copy link
Author

In general, this does create significant burdens for signers and verifiers as well as broken user experiences between clients and relays that do and don't implement it. The only way to solve the UX is to make this mandatory for all of Nostr, which significantly increases the startup time of any Nostr project. Complexity goes through the roof.

First of all, thanks a lot for your review @vitorpamplona . Since there's a lot to unpack there, let's start with this first...

The way we imagined this was that:

  • relays that don't implement this NIP - will just ignore the events with b tag to the master account when queried for master account events
  • clients that don't implement this NIP - events with b tag behave like any other event by the signer account, and don't appear in the master account

This should be similar to NIP-26 for relays and clients that don't implement it. But maybe we're missing something, so please advise...

@vitorpamplona
Copy link
Collaborator

This should be similar to NIP-26

Yeah. Some/many of these issues are also on NIP-26 and that's why no one implements NIP-26 and why I proposed to deprecate it entirely #1051

@ice-orestes
Copy link
Author

I do agree with the fact that NIP-26 has many issues, but there's a clear need for having account delegation, and possibly the revocation part that we include in our proposal can open this feature to more usage. At least in our use case we need it because our main account will be a blockchain key pair, and it would be costly to sign all events with it... that way we need the feature of having sub-accounts that can reside on multiple devices to sign events on-behalf of the main account. We also require those sub-accounts to be able to rotate and even be revoked in case of compromised or lost devices, for instance.
In sum, we think this is not a very uncommon use case, and security wise it is a good practice to implement such an architecture, so there's a real need for a account delegation and revocation in nostr, NIP-26 didn't cover it so we propose some alternative that can even substitute NIP-26 (although that wasn't the initial thought), as it requires less signature verifications and includes a rotation and revocation mechanism as well.

@ice-orestes
Copy link
Author

Addressing the other points of @vitorpamplona initial comment:

It also does not solve how to deal with encryption and shared secrets for event kinds that need it, like DMs and private group chats. Meaning that, subkeys don't see what the main account and other subkeys are encrypting/decrypting even when authorized.

True for all ECDH Key Exchange based functionality, as they rely on participant accounts private keys to compute the shared secret. This limitation is also valid to any sub-account mechanism in nostr. But we had a few attempts like PR #59, or PR #580, to have a single shared secret effectively shared, by DM or replaceable event, encrypted to each participant. Something alike would solve this problem and allow sub-accounts to coexist peacefully with DMs and private group chats in nostr.

More importantly, this type of delegation changes the indexing of replaceable events. Since subkeys can change replaceables of the main key, queries by a tag must be broken down to also search by changes from authorized b keys.

True. Relays should consider a replaceable with a valid b tag as effectively coming from the master account, that way this problem is mitigated.

Relays that keep only the last event per pubkey in the 10000-20000 range must now also account for subkeys and those subkeys can be dynamically changed at any time, past and future (by rebroadcasting past kind 10100 events or deleting them, reverting to a previous state).

This is why we have these requirements for kind 10100:

  • This list is owned by the master account, and can only be updated by an event signed by the master account itself
  • This list is ever growing, so each update has to include all previous attestations unchanged, and add one or more attestations to the list, otherwise they should be considered invalid and ignored by clients and relays.

A replaceable that has a b tag cannot affect the indexing of the sub key's own replaceables and should not be returned when a client requests the replaceables of the subkey.

Exactly, relays that receive a replaceable event with a valid b tag should apply it to the master account and ignore it on the sub-account level. Maybe this should be added to the last section of the NIP?

One can also create an elaborate attack by using a past kind 10100 when the stolen subkey was still authorized. The attacker gets the past version, sends it to a relay the attacker controls, and doesn't allow that event to be replaced by newer versions. Now new posts by the sub key can make clients think the attacker's relay is the main account's relay, which will make users see the stolen key as still valid.

Not sure what you mean by and doesn't allow that event to be replaced by newer versions, as I am not aware of a way for a single relay to block other relays from accepting new versions, or make clients think the attacker's relay is the main account's relay, as I don't know what that means, do you mean NIP-65 relay list metadata?

Thanks again for your comments and looking forward to improve this NIP with all the input...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants