decentralized-identity / trustdidweb

Trust DID Web (did:tdw)
https://identity.foundation/trustdidweb/
Other
19 stars 9 forks source link

How to authenticate a DID rotation #3

Closed brianorwhatever closed 5 months ago

brianorwhatever commented 6 months ago

The method must only allow rotations of DID documents by the controller.

Controller is a bit of a loaded term but my assumption is that this is the controller property of the DID doc. If so, the first document will be lacking this property won't it? What do we expect a controller property to look like? It can be a set of keys but we would have to further specify (see #2)

andrewwhitehead commented 6 months ago

The DID spec is light on the details of how controllers should be interpreted, leaving the explanation to specific DID methods it seems. A good number of the open issues have to do with the semantics of controllers: https://github.com/w3c/did-core/issues?q=is%3Aissue+is%3Aopen+controller

We can say that the top-level controller field is optional, and can be a DID string or set of DID strings identifying other subjects. If provided, then only the set of controllers are allowed to make changes to the DID document (and presumably, should authorize version 0 as well). If left out then we would want the DID subject to be authorizing changes on their own behalf, as opposed to them not requiring any authorization.

Looking at did:cheqd as an example, it seems reasonable to require signatures from ALL controllers on an update, from keys present in the authentication section of their respective DID documents (although we can consider capabilityInvocation instead).

swcurran commented 6 months ago

After thinking through this and texting with Drummond and @peacekeeper, I think this is the best we can do if we want to enforce that only an authorized key (or keys) can update the DIDDoc of a DID. The first part of this covers the single key use case of a verificationMethod, and lower down in the comment I propose adding in multi-sig via verifiableCondition.

For multi-sig, we define that a verifiableCondition is used equivalently to a verificationMethod for the purposes of updating the DIDDoc of a DID:

brianorwhatever commented 6 months ago

A thought I've had after reading the did:plc spec was that maybe the self referencing controller property CAN be a version or specific point in time reference of the DID. To do this we can have the method specify that the controller property isn't included in the document but injected after using a new SCID. So the id of a DID would remain as the inception SCID but the controller would be updated every rotation to identify the current state. If we also want to include a way to have other DIDs sign off on changes this would still be possible if we say that a controller property can be included but the current SCID value is added onto the list.

I also like that they have a ranking of recovery keys in their list but I don't like that they allow you to make changes with a higher authority for 72 hours so I'm not sure we can do anything with that idea..

did:plc also separates out the recovery keys from the DID Document keys entirely and I think that separation of concerns is interesting but probably unnecessary.

verifiableCondition seems like a good approach to multisig 👍

andrewwhitehead commented 6 months ago

I agree that the keys that are allowed to update the DID document should be separate from the keys used for other purposes, like signing VCs. Based on my reading of the DID Core spec, capabilityInvocation is intended primarily for authenticating with external services (like a VDR) and so maybe not appropriate here. For VCs, the assertionMethod proof method is used. The default option, authentication does seem to make sense for authorizing DID rotations, as the text says:

A particular DID method could decide that authenticating as a DID controller is sufficient to, for example, update or delete the DID document.

It gets a little complicated for me when considering external controllers:

swcurran commented 6 months ago

@andrewwhitehead and I talked about this and we think we have a generalized approach, with some “details” that we can adjust based on feedback. Basically:

andrewwhitehead commented 6 months ago

^ One caveat here is that an external controller probably wants to separate the keys they use to update their own DID Document versus the keys used to update (authorize an update to) someone else's DID Document.

swcurran commented 6 months ago

Arrgghh…turtles all the way down. Yes — I think I momentarily thought about that and decided to suppress the idea.

Ah…does the controller item on the key type come into play here? What is it for? Of course, if so, the chicken and egg SCID becomes even more fun.

andrewwhitehead commented 6 months ago

In regards to the controller field on a verification method, the spec says:

Since a key can't control itself, and the key controller cannot be inferred from the DID document, it is necessary to explicitly express the identity of the controller of the key.

Essentially it is meant to point to the entity that controls the key data. Manu adds some explanation here: (https://github.com/w3c/did-core/issues/802#issuecomment-986073342). Looking at the different combinations of key IDs and controllers, with an example DID document did:example:child:

  1. The document could have a signing key with ID did:example:child#key-1 and the controller set to did:example:child, this is the common case. I don't think we need to expand on it.

  2. It could have a signing key with ID did:example:child#key-1 and the controller set to did:example:parent. In a signature using this key, the verification method would be set to did:example:child#key-1 which would be resolvable via the current document. As I understand the semantics, the verifier would need to then look up the DID document for the controller did:example:parent (based on the verification method it resolved) to see if it listed the same key for the same proof purpose. That document would need to use the same key ID. If additional properties were found alongside the reference, then presumably those would have to match the definition in did:example:child.

  3. It could have a signing key with ID did:example:parent#key-1 and the controller set to did:example:parent. Verifying the signature on a proof would mean looking up the DID document for did:example:parent, resolving the key, and seeing whether it is listed for this proof purpose. If the proof is being used to authenticate did:example:child (which depends on the context and interpretation the proof), then the verifier would have to check that the key ID is also listed in the did:example:child document for this proof purpose. The same matching rules would apply as above.

  4. The document could have a simple reference to an external key such as did:example:parent#key-1 with no other details, listed for the appropriate purpose (ie. authentication). In this case the original key definition must be resolved via did:example:parent to see who the controller is, and it must be listed for the same proof purpose. Now the controller on this external key definition could be another DID subject, meaning that a third DID resolution would need to be performed (!) and the key definitions again compared for equality.

andrewwhitehead commented 6 months ago

I believe that given the RDF interpretation of controller it does make sense to only require one signature, not signatures from all controllers. For a document with two controller entries listed (such as did:example:A and did:example:B), we have two independent rules asserting that did:example:A controls did:example:child and did:example:B controls did:example:child. For other cases the two controllers can either create a new shared DID to control did:example:child (what the spec calls group control), or they can add verifiable conditions rules to the current document.

swcurran commented 6 months ago

In regards to the controller field on a verification method, the spec says:

Wow…that seems very unhelpful. The “same key” stuff seems very unhelpful.

Do you understand that well enough to know what we need to do? The issue seems to be that there is no key type (e.g. verificationMethod, authentication, etc) that is explicitly for controlling the DID — which is where we got to the other day. We need to pursue that with the “DID Folks” (Manu et. al.) on what they recommend, and/or we push for a key type that we use. Either way it is just a “rule” that we enforce, so not a big impact on the code — much more of a standards/policy/people issue.

andrewwhitehead commented 6 months ago

I would suggest for self-signed documents, use the authentication proof purpose. The text is consistent with that being a viable option.

For external controllers, we can't predict the DID method that will be used, and so we can't assume they are able to add new proof purposes. For this reason, one of the existing purposes should be chosen. authentication could be used, but it seems like it may be good practice to reserve that for the 'self-signed' case. For this reason I would suggest using the assertionMethod proof purpose when an external controller is authorizing a document update. This is the same as when they sign a VC, but presumably with a different key. There's a closed issue from Daniel Hardman in regards to limiting the scope of assertion methods that could apply here (it could also apply if authentication was used) but that would likely require a new extension.

swcurran commented 6 months ago

Hmmm…ok I guess, but weird that we would use a differnt type depending on whether it is an internal vs. an external controller DID. Not my favourite and it does make the code more complicated.

swcurran commented 5 months ago

Planned approach:

For signing, the DID controller would indicate what key reference(s) are to be used, and provide an endpoint for a signing service. The DI proof would contain the references to the key(s) used.

For verifying, the verifier would confirm that the reference(s) to the keys are in the authorized list (with the threshold met if using verifiableCondition), retrieve the public keys via the references, and verify the signature(s).

andrewwhitehead commented 5 months ago

I think in the interest of keeping validation efficient, we should only look at verification methods (authentication keys) in the current document and NOT require resolution of any controller DIDs. Every proof would have to reference a key within the previous version of the document.

The verification methods in the document may have an external controller value set, which would have to be listed in the DID doc controllers as well, but I suggest we should not take the step of resolving that controller and checking that the key is also present there, at least when used for the purpose of DID document updates. If the key is used for other purposes then of course verifiers are capable of doing that extra check.

swcurran commented 5 months ago

I think you are indicating a difference in what should be in the spec. vs. an implementation. I think the spec. should indicate that anything valid is…well, valid. So if the DID wants to put external DIDs into the controller property, it can do so.

For verification, the key reference being used in the proof is in the proof itself (right?). A verifier only needs to resolve an external DID if the key reference is to an external DID and that DID is listed in the DID controller property. Further, if it can’t resolve the external DID (e.g., unsupported DID Method) that is the result of the resolution.

For the controller signing an update, it is up to the DID Controller to decide what key reference is to be used for the DID. It can decide to only use internal keys, to use an external DID or even to list multiple DIDs and then use whichever one it needs.

swcurran commented 5 months ago

New proposal:

The reason I think the first is important is the typical Multi-Sig use case -- such as when a Board of Directors control the DID of an entity. In that case, it is likely all of the DIDs are of the same type, and so resolvable. It does create the possibility of turtles all the way down, but...

@andrewwhitehead -- acceptable?

brianorwhatever commented 5 months ago

This creates an external dependency such that another DID is able to break the history of my DID. For example If an external did:web document were to disappear the log of my DID would be unresolvable. But that is likely a caveat of external controllers no matter what we do.. it scares me though

andrewwhitehead commented 5 months ago

I feel like it should be up to the client to resolve an external controller for extra verification if that is what they want to do. The resolution process is just returning the current state of the DID document and checking the internal consistency of the history.

swcurran commented 5 months ago

This creates an external dependency such that another DID is able to break the history of my DID. For example If an external did:web document were to disappear the log of my DID would be unresolvable. But that is likely a caveat of external controllers no matter what we do.. it scares me though

I disagree. It’s not your DID, it’s “their” DID, since they are a controller. That’s the point of a controller.

I feel like it should be up to the client to resolve an external controller for extra verification if that is what they want to do. The resolution process is just returning the current state of the DID document and checking the internal consistency of the history.

The problem with this is that we’re saying — “We’re resolved the DID and verified the history. Well, most of it. Over to you.”. I don’t see a problem with resolving DIDs that we know we can (at least try) to resolve. Agree we should not include all DIDs, hence the limit to the DIDs we know.

But you do raise the interesting case of resolving the DID that we can’t full verify, and (somehow…) communicating to the caller that we did that. AFAIK — there is no way the client to a resolver can know that the resolution is only partially complete. They only get a DIDDoc from the resolution — they don’t get the log.

brianorwhatever commented 5 months ago

I disagree. It’s not your DID, it’s “their” DID, since they are a controller. That’s the point of a controller.

yes fair, but I mostly mean it creates a technical external dependency. So the resolution of a DID that has a did:web external controller brings in all the baggage of that DID (ie DNS resolution, server availability, latency, key availability (no history for did:web). We just need to make it clear the tradeoffs this introduces

swcurran commented 5 months ago

Solution that we're going to put in the first draft:

We recommend that the DID controller put a copy of the verificationMethod into the DID itself.

brianorwhatever commented 5 months ago

Interestingly, the digital bazaar libraries require a controller property on a Verification Method. Things blow up if it doesn't see that. This makes me think pulling in verification methods from other DIDs isn't all that strange of an idea. My original concern about "what if the external controller revokes there key externally but forgets to revoke it here" still scares me.. This external controller thing sure creates a lot of fun edge cases 😄

brianorwhatever commented 5 months ago

Ok, I think I have removed my fear of that. The main controller can revoke the key themselves.. so they can be watching the other controllers did doc themselves and then revoke it themselves should there be any funny business

brianorwhatever commented 5 months ago

To update a DID Document a key MUST be listed as a verificationMethod and included in the authentication key list. The controller of that key MUST be listed as a controller of the DID Document.

brianorwhatever commented 5 months ago

See implementation https://github.com/bcgov/didwebnext/pull/18/files#diff-0e3a67056bbdff08ce99ced931577a25243c45d664116e4f8f4878069efc259eR152-R173

swcurran commented 5 months ago

Questions about the implementation:

brianorwhatever commented 5 months ago

Yeah right now I am requiring authentication type. This is actually something I realized is wrong about my mental model of DIDs. I used to think keys always had to be in one of the relationship lists and then keys were listed in verificationMethod so that they could be repeated with multiple relationships. But it sounds like most people treat the verificationMethod list as a catch all so keys in there are valid for any purpose. I'll have to update both my mental model and the implementation 😅

Unfortunately I haven't had a chance to investigate verifiableCondition enough so I can't answer that second question

andrewwhitehead commented 5 months ago

I don't think that keys in verificationMethod should be acceptable for just any purpose, but it seems to be preferred to put the full key definition there and only place references in the other properties.