nostr-protocol / nips

Nostr Implementation Possibilities
2.39k stars 578 forks source link

Key rotation verified through root key attestation #116

Open kevinsmith opened 1 year ago

kevinsmith commented 1 year ago

The purpose here is to provide users with a mechanism to use revocable "hot" keys in less secure application-level contexts while keeping the root key that defines their identity in cold storage, only used for signing the attestation event described below. This NIP would not replace or attempt to improve upon NIP-26. That's an entirely different use case.

I can imagine a simple key management scheme where a root key just broadcasts a replaceable, NIP-03 timestamped event that declares "these are the pubkeys I use for routine event broadcasting along with all the old ones and when they were revoked, consider messages signed by them as if they were signed by me".

These listed keys have the same privilege and authority as the root key with the sole exception that only root keys can sign the attestation event. If a listed key is also the root of another attestation event with its own listed keys, clients must ignore the attestation. The top of the hierarchy prevails.

Each listed pubkey must include:

When these keys broadcast any event, the root's pubkey should be in the pubkey field of the event, the signer's pubkey should be included in a new field, and clients should use the attestation event above to validate accordingly. This NIP would not impose any requirements on relays.

For DMs, it may make sense to be able to declare a single one of the current listed keys suitable for DMs. Clients can choose to store old DMs to maintain history.

It would require clients to build in support, otherwise any user adopting this scheme would be broadcasting events that couldn't be verified. But it doesn't require changing the ID format. Doesn't require generating a new root key pair and thus cause people to lose their "identity". The existing network protocol continues to work.

Originally posted as a comment under https://github.com/nostr-protocol/nostr/issues/45, moved over here to focus comments on this particular draft proposal.

kevinsmith commented 1 year ago

Broadcasting a revocation on nostr doesn’t provide any sort of guarantee that a relevant client will see it. Each user should of course attempt to broadcast their own attestation to as many relevant public relays as possible, but we shouldn’t rely on that for this mechanism to work. @gkbrk suggested a variant of the gossip protocol from client to relays to maximize distribution of the attestation.

So if I see a revocation from someone I follow.

  1. Immediatly set that pubkey as bad, never consider events from that pubkey valid again.
  2. Re-post that revoke event to all public relays I am connected to.

Given the typical efficiency of the gossip protocol and the fact that nostr relays are more efficient than standard p2p communication, this should be more than enough to ensure a user’s current attestation can be found on any relay that’s relevant to their audience.

fiatjaf commented 1 year ago

This is not a bad idea, but if I understand it correctly it was considered before and the general conclusion was that the implementation was really hard, full of state and hidden complexities. Doesn't mean it can't be done and that its future isn't bright though.

rot13maxi commented 1 year ago

Adding the NIP-03 timestamp is a good call. if a client gets a bunch of these events, it can either establish partial ordering, or throw out all but the latest (depending on the actual semantics of the message).

kevinsmith commented 1 year ago

Yeah, my thought was to have one authoritative attestation and allow clients to throw out all the old ones.

vitorpamplona commented 1 year ago

I think it is a good idea to separate the concept of "authorized key payload" from "expired/revoked keys". The key attestation event simply refers to the keys that can act on behalf of a root of trust. Adding or removing keys to the list is just like adding employees to a payroll. They might still exist, fully valid, by themselves, before, during and after they were added to the list. The only difference is that during their period in the list, clients that are following the root will see them as trusted entities to speak on behalf of the root key.

The beauty of this idea is that clients don't need to know if keys are coming from different people or just different devices. Relays can't assume keys are expired or stolen. Relays must continue to accept any payload coming with a valid signature. It's the role of the client to differentiate moments when a key was part of a bigger group and moments it is not.

kevinsmith commented 1 year ago

How does separating key attestation from revocation accomplish that? Either way the relays can still act as dumb pipes or choose to block messages from revoked keys, and either way the clients need to determine when keys are considered valid as the signer for a root key.

vitorpamplona commented 1 year ago

It's just semantics. So devs don't get confused. The sentence:

"these are the pubkeys I use for routine event broadcasting along with all the old ones and when they were revoked, consider messages signed by them as if they were signed by me"

Removing a key from the list doesn't mean the key is not being used elsewhere. When combined with:

"Relays can choose to reject all new events signed by revoked keys if they wish"

Creates semantically diverging relays (one relay thinks revoked keys are fine, the other does not), making it harder for users to understand what's going on and who to trust.

Needless to say, divergent behaviors should not be encouraged to help with the simplicity of the protocol.

kevinsmith commented 1 year ago

Removing a key from the list doesn't mean the key is not being used elsewhere.

I’m assuming you mean marking the key as revoked. And I’d argue that marking it revoked does mean that this key is burned and shouldn’t be used elsewhere. If the key holder keeps using it… they shouldn’t? Not sure what else to say there. Burn a key, can’t use it anymore.

vitorpamplona commented 1 year ago

It's revoked in the sense that it cannot speak on behalf of the root key. It can still speak for itself.

That not only guarantee that there is no change to relay operations, but it also avoids the protocol having to deal with other types of attacks, for instance: somebody else could add my key to their root (with or without my approval) and then revoking it and killing my personal data/access to the protocol.

kevinsmith commented 1 year ago

The root has to provide a signature from the listed key to prove that they control it. So you can’t just list other people’s keys and then mark them revoked.

vitorpamplona commented 1 year ago

That's even more complicated. Now we either have to design a secure way to send private keys around in order to assemble the payload (think multi-sig setup) or simply let users send their private keys in regular unsecured communications.

And it doesn't solve the problem because users may give access to their private keys willingly just to be rug pulled by the root key owner later.

In another use case, what happens if the same key is added to two roots and one root decides to revoke that key? Should relays accept or reject messages from that key? Should they know when the key is trying to speak on behalf of which root and then block when it does not have access to that particular root?

What if the key is another root of keys? Will relays build a recursive on-behalf-of field validation?

What if the key is revoked for 2 days and added back after that? Should replays go back and approve those past messages? Should they wait for them to be broadcasted again?

Things get very complicated very quickly when revoking status (expanded meaning) and authorized keys (more specific meaning) are blended together without care.

vitorpamplona commented 1 year ago

My proposal is to keep it simple. Relays always accept valid payloads. They don't check root keys at all. They don't even know they exist.

The client app may follow a root key, which will follow all individual keys. In that case, when the root key changes its list, the client app will stop receiving events from the removed keys and start receiving events from the added keys.

The root key can then be an off-line private key (cold keys) and all active usages are coming from authorized keys (hot keys).

Super simple.

kevinsmith commented 1 year ago

To be clear, this isn’t meant to replace NIP-26. Completely different use case.

kevinsmith commented 1 year ago

And it doesn't solve the problem because users may give access to their private keys willingly just to be rug pulled by the root key owner later.

There are no multiple users here. It’s the same user controlling all the keys.

vitorpamplona commented 1 year ago

There are no multiple users here. It’s the same user controlling all the keys.

That's another semantic mix-up. There is no way to know and subsequently control, at the protocol level, what's a user, what's an identity of the user, and what's a singing device of an identity, per key. The protocol will be better off if designed for things it can control.

kevinsmith commented 1 year ago

If you hand over your private key to someone, they now fully control it. No protocol can change that.

The point of this proposal is to limit the downside of handing your private key over to an app like damus or even an extension like nos2x. This way, it's not your root key.

vitorpamplona commented 1 year ago

The point of this proposal is to limit the downside of handing your private key over to an app like damus or even an extension like nos2x. This way, it's not your root key.

Great, then don't require private keys to assemble the authorized key list. Otherwise, you are incentivizing the exact thing you are fighting against.

kevinsmith commented 1 year ago

Great, then don't require private keys to assemble the authorized key list. Otherwise, you are incentivizing the exact thing you are fighting against.

The root must prove that these other keys are owned by the same author.

vitorpamplona commented 1 year ago

Great, then don't require private keys to assemble the authorized key list. Otherwise, you are incentivizing the exact thing you are fighting against.

The root must prove that these other keys are owned by the same author.

Why? It doesn't need to.

kevinsmith commented 1 year ago

How do you know a root key is being truthful when they claim "this other pubkey is me, too"?

vitorpamplona commented 1 year ago

There is a major difference between "on behalf of myself" (which is your initial proposal) and "is me".

Cryptography will never be able to prove a "is me" statement. You have to rely on trust.

kevinsmith commented 1 year ago

"On behalf of" wasn't the initial proposal.

Cryptography will never be able to prove a "is me" statement.

Right, that's why that language wasn't in the proposal. (It's also not formalized but trying to use clear, plain language for the sake of discussion.) But a signed attestation saying "these other keys have the same privilege and authority as me" could be cryptographically validated. No trust required. No outside information needed.

Whether it's literally the same user is irrelevant to the protocol. "Give this other key the same privileges you give 'me' until revoked, and attribute all their events to me" are clear rules that a protocol can easily follow.

That's why that's what's outlined in the initial proposal. If that's unclear, I'm happy to update and clarify.

vitorpamplona commented 1 year ago

"these other keys have the same privilege and authority as me"

This is also different. We need to come to a clear meaning here. Having the "same privilege and authority" doesn't mean speaking "on behalf of" and also doesn't mean "it's me". It's a different thing. Apps that rely on the "same privilege" (think same OS user group, all developers in a software company) and apps that can be built upon "on behalf of" (think parents signing on behalf of their kids, marketing accounts that speak on behalf of companies, etc) are different.

if they have "same privilege and authority" as the root, can all keys change the list of keys? Can they include themselves in the list?

I would rephrase the original post to use the same wording in all situations and avoid confusion. On behalf of is probably the best wording. It makes the syntax work without specifying semantics, which are left to client apps:

The base protocol doesn't need to know which one is which.

kevinsmith commented 1 year ago

"On behalf of" is not best because it means something entirely different. NIP-26 already exists.

vitorpamplona commented 1 year ago

Yes, but NIP26 is not allowing root keys to expire their delegation tokens, right? There are no delegation tokens here. This NIP would allow different use cases than NIP 26 and much smaller payloads.

vitorpamplona commented 1 year ago

To the best of my knowledge, NIP 26 doesn't allow key rotation. Once delegated keys have leaked, the root account is also leaked (because delegated accounts can always speak on behalf of the root account)

kevinsmith commented 1 year ago

At this point, I'm wondering if you're intentionally being obtuse. You keep bringing up irrelevant use cases that are outside the scope of what this scheme attempts to solve, and you've objected to several aspects of it as if they're obviously unworkable when they're also features of DID, which you've been championing. (e.g. including cryptographic proof that a listed key is controlled by the root, explicitly listing revoked keys)

I've updated the original post for clarity. Everyone is welcome to contribute to the discussion and critique, even and especially reasons this concept might not work. But I'm going to start ignoring comments that change the subject.

vitorpamplona commented 1 year ago

I have lived through those "irrelevant use cases" Sorry if they bug you, but they are very real.

I am not being obtuse, I am being very clear. In fact, I have been trying to clarify most of the initial text. Changing the relay behavior as initially proposed is problematic and brings more problems than solutions. The rest of the idea (list of keys) is good.

If you want to change relay behavior to account for identity and not only public keys, there is a lot more work to be done and keep it cohesive.

I don't understand why simple solutions can't be discussed here.

kevinsmith commented 1 year ago

Changing the relay behavior as initially proposed is problematic and brings more problems than solutions.

There was no change in relay behavior in the original post or the updated post. This is what I'm talking about. It feels like you're not reading what was written.

Simple solutions are being discussed here. You're overcomplicating things.

vitorpamplona commented 1 year ago

Changing the relay behavior as initially proposed is problematic and brings more problems than solutions.

There was no change in relay behavior in the original post or the updated post. This is what I'm talking about. It feels like you're not reading what was written.

? There was this text in the original version:

"Relays can choose to reject all new events signed by revoked keys if they wish"

I appreciate that you removed it. But don't pretend it wasn't there.

The new text is good.

The extra field to have two public keys in the event in is not a requirement to make this proposal work. But if people are ok with the added field, it's a nice to have.

kevinsmith commented 1 year ago

"Relays can choose to reject all new events signed by revoked keys if they wish"

I appreciate that you removed it. But don't pretend it wasn't there.

It was a comment suggesting possible optimizations, but nothing about this requires relays to do anything. As always, relays can do whatever they want if they wish. There was no required change in behavior.

vitorpamplona commented 1 year ago

On this one:

signature proving the root key holder controls the corresponding private key

I still think this is unnecessary. I have never seen the need to prove a root key has access to private keys. It might actually be a bad practice.

Neither DIDs, nor x509 PKDs offer this.

I would remove the signature to make it simpler.

kevinsmith commented 1 year ago

Without it, a root key could usurp the identity of any pubkey on the network.

vitorpamplona commented 1 year ago

But the root key cannot speak on behalf of the leaf keys. The spec is not bidirectional. Has any client app requested this bidirectionality?

If the leaf keys want to make the root speak on their behalf, they can add their own key attestation where they are the ones signing the payload. In that way, you avoid the signature field, and the private key copy paste everywhere (making it simpler), and you also increase freedom to the keys, which can now decide by themselves when to add and remove the root to their authority list.

The root adding pub keys to a list should not mean that the root can speak on behalf of the keys.

kevinsmith commented 1 year ago

The hot keys are signing something whether it's included in the attestation described here or their own attestation. I have it all in the root-published event right now to make it easy for clients, but each hot key could publish its own attestation. Does seem like that would make it slightly more work for the client though. What's the benefit?

vitorpamplona commented 1 year ago

Let's make a list.

Benefits of making it one-directional:

  1. Avoid the signature field. One less thing to specify/implement/find inconsistencies/debate about.
  2. Avoid having to get signatures from keys to include them in the payload, which is an additional UX flow to be designed/explained.
  3. In the lack of 2, avoids the root getting access to the private keys themselves (added security risk).
  4. The root can authorize any pubkey to speak on root's behalf. If the root authorizes random people, so be it. It's the root's choice/freedom to do so.
  5. When keys post their own authorized key list including the root, they can at their own will revoke the roots permission to talk on their behalf, increasing freedom for each of the key members. Keys are not slaves to the wishes of the root.

Benefits of making it bi-directional:

  1. Two events in one: Small space saved by relays. One less request to the relay.
  2. ?
kevinsmith commented 1 year ago

I think you still misunderstand the purpose of this scheme.

vitorpamplona commented 1 year ago

So, no other benefits to bidirectionality?

I think you still misunderstand the purpose of this scheme.

You tell me. I think the purpose is key rotation as it clearly says in the title and first sentence. PERIOD. Key rotation doesn't necessarily need bi-directionality of attestations. If the purpose is something else, better write it out. I can't read your mind.

kevinsmith commented 1 year ago

It makes no sense for a root to be able to claim the identity of another pubkey without proof. Full stop.

vitorpamplona commented 1 year ago

But the root is not claiming the identity of anybody. That's not the purpose of this thread. I can create a list right now with your keys and post it. That doesn't mean I am claiming your identity.

kevinsmith commented 1 year ago

Yes that is the purpose of the proposed scheme. The hot keys do not have their own identity. They are additional ways of signing events that are attributed to the root.

vitorpamplona commented 1 year ago

Then write that in the title. You don't want to solve key rotation. You want to assign the same identity to all keys. Those are VERY DIFFERENT THINGS.

kevinsmith commented 1 year ago

Do you actually think you're helping?

vitorpamplona commented 1 year ago

I do. Because clearly, I am the only one providing feedback in this thread and you clearly took in and modified the text.

I am still waiting for your answers on why anybody needs bidirectional support in key rotation. Otherwise, it looks like this idea is not needed at all.

vitorpamplona commented 1 year ago

I have added a new Issue to discuss a simpler and more general idea than this one: https://github.com/nostr-protocol/nips/issues/123

kevinsmith commented 1 year ago

Godspeed.

fix commented 1 year ago

Reading the discussion.The idea of an independent ID management tool seeding applications with keys looks like the way to go. Identity in the end cannot be simply tied to only one app, it should be able to provide application keys to nostr, signal, whatsapp, bitcoin wallet, etc... in such way that the people you are in contact with can discover them without difficulties

kevinsmith commented 1 year ago

Identity in the end cannot be simply tied to only one app, it should be able to provide application keys to nostr, signal, whatsapp, bitcoin wallet, etc... in such way that the people you are in contact with can discover them without difficulties

Nostr isn't an app, it's a protocol. Maybe someone will implement a multi-protocol identity system with a nostr prototype and it'll catch on, but personally I don't see much benefit. People use Google and Facebook single sign-on everywhere because it's easy to login to new websites, not because they necessarily want to have a unified identity across the whole Internet. (That is Google's and Facebook's desire though.)

fix commented 1 year ago