w3c / did-resolution

RELEASED DRAFT: Decentralized Identifier Resolution (DID Resolution) 0.2 Specification
https://w3c.github.io/did-resolution/
Other
14 stars 9 forks source link

Discuss how to treat deactivated DIDs #5

Open peacekeeper opened 6 years ago

peacekeeper commented 6 years ago

See this thread: https://lists.w3.org/Archives/Public/public-credentials/2018Jun/0078.html

Should DID resolution distinguish between a "DID that does not exist because it has never been created" and a "DID that has been revoked"? What results should be returned in each case by a DID resolver?

cboscolo commented 6 years ago

I think it would be better to be explicit between "DID does not exist" and "DID has been revoked". In fact, I think it would be beneficial to specify what should be required in a DID Document for a DID that has been revoked and provide an example. (Sorry if I missed that in the spec.)

@ChristopherA responded to the above email thread with how BTCR handles this, which highlights the three (really two) revocation scenarios we should describe. I formatted it for clarity.

With BTCR, the resolver always can construct a DID document. But it must check to see if the “tip” bitcoin address has been spent — if it has, the DID document has been revoked (or rotated) as of the date of the address being spent. The tip transaction can contain:

  • no information (default revoke)
  • revocation information (revoke with details why)
  • link to updated DID document (rotation)
peacekeeper commented 6 years ago

+1 I also agree it makes sense to distinguish between a DID that has been revoked, and one that doesn't exist. (At least if a method supports this, I could also imagine methods where a revoked DID is actually completely deleted everywhere - I think this should also be allowed).

As for locating the "latest version", yes I am certain that in a simple "resolve" request, the latest DID document must be returned. That's the meaning of "update" - it updates what the latest version is. So I think a BTCR resolver must follow all the tip transactions to the end in order to locate the latest version.

Returning earlier versions of a DID document could be an additional optional feature, if both the DID method and the resolver implementation support it.

cboscolo commented 6 years ago

@peacekeeper, regarding:

(... I could also imagine methods where a revoked DID is actually completely deleted everywhere - I think this should also be allowed)

I think this question deserves a little more discussion. Could allowing this inadvertently create a situation where compromised private keys might be reused?

Regarding the versioning issue, if we don't plan on formally requiring a "request version history" on a DID (I don't think we should), we could loosely agree to add it as meta information to the DID Document in the same way.

peacekeeper commented 6 years ago

Good points, I agree with both. We can have a warning in the spec about security implications of "revoked" vs "deleted", and we could add versioning information to the DID document in a similar way as revocation information.

(But resolver implementations should also try as much as possible to protect developers from accidentally using a revoked/outdated DID document).

TomCJones commented 5 years ago

there was another discussion on m/l that a DID that was valid on a given date needs to be continue to be valid on that date to allow validation of signature. I feel strongly that a subject should not be permitted to revoke a commitment made (ie a signature) at a earlier date. That capability would invalidate the entire chain, including key rolling and even the revocation itself. That means that requests to the resolver need to allow the request to include the date on which the DID is being queried. In general it would be most useful for the resolver to always included status, which could be (for example) that this DID was revoked on this date. Anyway, that is how i am constructing a resolver. If a method does not support that, i would simply ignore the method.

peacekeeper commented 5 years ago

Good points, versioning (and the ability to resolve earlier versions of a DID Document) is definitely one of the features that needs to be addressed in the DID Resolution spec. I just created an issue for this: https://github.com/w3c-ccg/did-resolution/issues/12

agropper commented 5 years ago

I'm concerned about revocation vs. spending for a prescription. A prescription can be coded as a Verifiable Credential and it's subject to revocation and spending. (1) Does the prescription itself have a DID or is the VC being revoked or spent? (2) Revocation can be done only by the issuer. Spending must be done by the subject and verifier together. How?

ankurdotb commented 1 year ago

@peacekeeper Resurrecting this thread since the DID-Linked Resources use case might signal that this question should be treated in a different fashion than it is currently...

Why I think deactivated DIDs should still be resolvable

My view is that a deactivated DID can still be read and should be resolved. It’s up to the client app/API request sender to then decide what to do with this. For example:

Relation to DID Core specification

I’m going with the spirit of what DID Core’s production specification implies:

If a DID has been deactivated, DID document metadata MUST include this property with the boolean value true. If a DID has not been deactivated, this property is OPTIONAL, but if included, MUST have the boolean value false.

This does not state a DID production shouldn't be made. If it was the case, this would be entirely unique out of all the other DIDDoc metadata in the effect that it has on production. Also, since it's optional/may be set to false, the opposite scenario implies an answer should be returned with the value set to true.

Proposed solution

Based on the above, I'd suggest that deactivated DIDDocs should return a resolution response. The change I'd make is to set didResolutionMetadata to return a different value instead:

Resolution metadata

Current specification

If the input DID has been deactivated, return the following result: didResolutionMetadata: «[ ]» didDocument: null didDocumentMetadata: «[ "deactivated" → true ]»

Proposed modification

DIDs may be deactivated with a replacement DID created, or without a replacement DID created. If the DID method supports it (perhaps via the alsoKnownAs property, perhaps the DID subject could also specify the new replacement DID. In this case, the resolution specification could also support:

HTTP Status code

The current HTTP status code of 410 implies "Gone", so returning an actual response is syntactically weird. I propose that status codes should be:

peacekeeper commented 1 year ago

@ankurdotb Thanks for sharing detailed thoughts on this! Personally I don't have a strong opinion here and could live with either approach. I think DID Core supports your arguments, since it says that if the resolution was successful, a didDocument must be returned. At some point in the past, "deactivated" was an error code, but then we changed that, since a deactivated DID isn't really an error, so we just made it another metadata property (see https://github.com/w3c/did-core/pull/691). And you're right that it would be unique as a metadata property if the didDocument was null. It also feels strange to return DID document metadata without returning a DID document :)

Having said that, let me present some arguments for the current approach:

Here are two potential ideas for a middle ground:

  1. If a DID is deactivated, we could return an empty DID document with only "id" (to be compliant with DID Core), and we could introduce a resolution option "resolveDeactivated=true" (or similar) that would return the actual deactivated DID document. So it's something you have to explicitly request from the resolver, in a similar way as you can request earlier versions of a DID document (using "versionId" and "versionTime") after an update.
  2. Or, we could do exactly what you propose, but add a strong recommendation somewhere that controllers should consider removing services, verification methods, Linked Resources, etc. from their DID before deactivating it, to ensure it won't be used anymore.

What do you think? In any case, it feels like we have to clarify more what deactivating a DID actually means.

peacekeeper commented 1 year ago

The change I'd make is to set didResolutionMetadata to return a different value instead.

didResolutionMetadata: "didDeactivated"

I don't really understand what you mean here. According to DID Core, didResolutionMetadata must be a map, we can't change it to a single string. Maybe I misunderstood.

peacekeeper commented 1 year ago

Also pinging @fabianekc since he also had some thoughts about DID rotation, forwarding, redirecting.

ankurdotb commented 1 year ago

The change I'd make is to set didResolutionMetadata to return a different value instead.

didResolutionMetadata: "didDeactivated"

I don't really understand what you mean here. According to DID Core, didResolutionMetadata must be a map, we can't change it to a single string. Maybe I misunderstood.

I don't mean the DID Document Metadata, which is defined in DID Core specification, but the values returned under DID Resolution Metadata - which as far as I understand is purely in the DID Resolution specification and not in DID Core. For example, the other values mentioned here are notFound, invalidDidUrl etc. Am I correct that these are purely in DID Resolution?

The argument for calling it "deactivate" was mostly a technical one, since many DID methods were and are based on blockchain/ledger technology where you can't really delete anything.

I agree with you that non-ledger VDRs such as did:key, did:dns etc could certainly actually delete rather than deactivate. Personally, I wouldn't assume whether underling VDR had a way of deactivating the information or actually deleting it. (I would also argue that if we get really philosophical, even ledger-based VDRs could stop existing given a long-enough time horizon, which is technically a delete operation. E.g., if did:terra was an actual DID method, it would have stopped existing in mid-2022, even though some people may have retained archival copies.)

The university should rotate the keys in its DID document, not rotate its DID. If an entity is aquired by another entity, both DIDs should continue to exist (not deactivated), and they could point to each other using the alsoKnownAs property.

While I do agree that in an ideal scenario either replacements should be created or new as well as legacy kept alive, in reality, I suspect that this will be hard to achieve. Sometimes, companies/entities just straight up go bankrupt and not acquired, or just plain not interested in maintaining "legacy" technology. I would consider verifying a credential linked to a deactivated DID as being interpreted as "It is a cryptographically verifiable fact that this credential is untampered, but it's issued by a defunct entity". E.g., think of a KYC credential issued by Silicon Valley Bank or FTX, utility bill credentials issued by a bankrupt energy provider in the UK etc. More importantly, with paper/plastic credentials issued by defunct entities, or in defunct formats, I can still use them and are considered valid documents. (My birth certificate is on paper filled in with pen, which is no longer a valid format where I was born, but that doesn't make it any less of a "genuine" document although whoever I may show it to could ask me to show other documents stating my birthplace since they don't trust this legacy format as much.)

Also, I suspect legacy/un-maintained service endpoints are only truly critical in some DID methods, but perhaps not all. A JWT VC is pretty standalone.

I'm a bit worried that if "deactivated" is merely metadata, many implementations will simply ignore it, and all VCs, service endpoints, Linked Resources, etc. will just continue to work normally even though the DID has been deactivated by the controller.

I do agree strongly with you on this point. Logically, I think this is how it should work but I suspect a lot of people wouldn't, and this could become a real issue if this best practice isn't followed.

...we could introduce a resolution option "resolveDeactivated=true" (or similar) that would return the actual deactivated DID document...

I really like this idea and I think this is the ideal answer. By asking the requester to explicitly acknowledge and re-send the request a deactivated DID, it's more likely that they'll understand and account for it in their logic. It's like a dialog prompt going "Are you REALLY sure?" before continuing. Also, if an underlying VDR like did:web or did:dns which could actually delete isn't able to provide an answer, this could also be gracefully handled with something in the response that then returns an error status/code. More likely they'll just return a 404 rather than a 410 because unless they are separately tracking all deactivated or historical DIDs, it'll be very hard for those VDRs to distinguish between a "not found" vs a "gone".

So maybe the steps could be:

  1. Requester asks for a DID. The DID is deactivated.
    1. The VDR doesn't support fetching deactivated DIDs and/or doesn't keep a historical log. (did:dns would be a fantastic example of this.) In this scenario, it returns a 404 not found.
    2. The VDR does support fetching deactivated DIDs, say, a ledger-based VDR like cheqd. In this case:
      1. Option 1: Assume the client app understands the semantics of how to behave with HTTP status codes correctly
        1. The resolver driver returns a 300 Multiples Choices HTTP status code
        2. Location header pointing to resolvable DID URL (only one value allowed), Link header pointing to resolvable URL (301 status code) or (404 - non-resolvable URL, error code)
        3. This may be a bit obscure, since most developers I know only expect 301/302/307 and all of those automatically cause many agents to get the redirect URL. In effect, it behaves exactly like returning a DID Doc with metadata that says deactivated: true. I worry that this solution is a bit too clever for real-world usage.
      2. Option 2: Don't assume client apps/developers understand obscure HTTP status codes
        1. Return a 200 OK status code
        2. No DID Document body, but do return DID document metadata and DID Resolution metadata
  2. Based on the response, either the client app hasn't been designed to fetch deactivated DIDs and fails, or the app understands that when it only gets metadata back with deactivated: true, it has to actively go and fetch the DID by appending a parameter.

What do you think?

alenhorvat commented 1 year ago

Should DID resolution distinguish between a "DID that does not exist because it has never been created" and a "DID that has been revoked"? What results should be returned in each case by a DID resolver?

Yes. First, DIDs and DID keys are part of a decentralised PKI. Second, DID keys are used for e-signing/e-sealing, and authentication. DID keys can essentially have three states

The deactivated state can be due to

In most VDR, DID keys are self-managed, meaning that the keys can be self-suspended or self-revoked. Suspension and revocation of keys by 3rd parties are usually done using VCs + using a credential status framework.

Depending on the use case, deactivation may require to signal

All this is independent of whether the DID R. supports the resolution of historical information or not.

Why is the distinction between non-existent and deactivated important? There's a difference between whether a key has been revoked/suspended or does not belong to the DID controller. First signals that a given DID issues the VC, but the DID has been revoked/suspended for some reason, second signals that the VC has not been issued by the DID, which is an attack.

ankurdotb commented 1 year ago

Why is the distinction between non-existent and deactivated important? There's a difference between whether a key has been revoked/suspended or does not belong to the DID controller. First signals that a given DID issues the VC, but the DID has been revoked/suspended for some reason, second signals that the VC has not been issued by the DID, which is an attack.

Glad you agree @alenhorvat! (BTW, everything that follows is me agreeing with you, rather than disputing any of your points.)

This is the reason why I think there should be some way of resolving deactivated DIDs vs DIDs that never existed/don't exist, even if it's by using an additional query parameter to make the client app developer 'opt-in' if necessary to make sure they truly understand what they're doing.

The deactivated state can be due to

key expiration suspension (temporary deactivated) revocation key rolling (registering a new key + revoking an existing key) - we're using key rolling instead of key rotation since key rotation definition is very ambiguous

The scenarios you've mentioned are excellent examples of intentional DID/key deactivation. I also add the following scenarios for why a DID/key may be in deactivated state, as unintentional or non-recoverable scenarios:

  1. Lost control of keys / DID controller doesn't want to maintain it any more. (I really don't think this is a far-fetched scenario, since people/companies lose passwords all the time.) To be fair, most ledger-based VDRs won't allow this for single keys as they'd always expect a deactivate operation be carried out using the controller keys. But I can foresee this being possible for m-of-n multisig keys if a ledger-based VDR supports it (and we want to allow this as well). Other, non-ledger VDRs such as did:web or did:dns could very easily allow some form of break-glass access to deactivate the entry in case they've lost control of the key without needing the operation to be signed by controller keys.
  2. An entire VDR/DID method is sunset: I can foresee situations where a DID method or a VDR (say, did:ethr) is entirely sunset because the network/VDR cannot be run any more (due to economic challenges, legal challenges, sunsetting legacy products etc). The developers of this VDR are the ones who set the rules for what's allowed to authorise a transaction, and could choose, using a software update to mark all DIDs as deactivated (and perhaps available only in a read-only format/archive).

These non-recoverable scenarios, IMO, should not cause a holder to lose the ability to use their credentials entirely.