TBD54566975/did-dht

Clarification on JWK `alg` property during DID Document Reconstruction

frankhinek opened this issue · 11 comments

Context

Test Vector 1 and Test Vector 2 include 'alg' properties in the example DID Document even though this information is not encoded in the Pkarr / DNS packet:

Screenshot 2024-02-05 at 9 56 24 AM

Question

Should alg be omitted from verification method JWKs in the DID Documents produced by DID DHT Resolvers and Clients?

The alg property is optional in JWKs and primarily becomes relevant in the context of using the verification method material with a specific cryptographic algorithm. While in the case of Ed25519 keys, they are commonly used with the EdDSA algorithm, but that need not always be the case. In the case of secp256k1 and secp256r1, ECDSA is the most commonly used algorithm, but others such as ECDH or ECIES cannot be ruled out.

It seems we ought to omit alg from the DID DHT specification and any examples to minimize the confusion that implementers should be automatically assuming an algorithm when reconstructing a DID Document.

It is helpful to specify alg and I believe we should require it. Assuming you agree with that we have two possible avenues to explore:

  1. Bake it into the registry

The simplest way to do this would be to add a column to https://did-dht.com/registry/#key-type-index where we specify the associated alg in accordance with the verification relationship type. I believe that this reduces implementation/usage risk.

For example, if you are using an Ed25519 for authentication or assertion then EDDSA makes sense.
If you are doing key agreement then ECDH makes sense, but you should probably specify X25519 (related: #47).

  1. Allow it to be explicitly specified in the DNS packet representation

This removes the need to maintain a registry and gives implementers full control over specifying what they wish to. The risk here is ambiguity if it is unspecified, so we may need to make it a required property.


Open to other ideas as well!

cc: @selfissued it would be great to have your input here

If there's any chance of algorithm ambiguity, it's better to include "alg" in the the JWK than not.

@selfissued @decentralgabe It may be that we're saying the same thing or perhaps that there's a nuance that we should explore to work towards alignment.

The concern raised above is that there are cases where it is not possible to safely assume the alg based solely on the JWK's kty and crv.

Example 1

Let's start with an example where it is safe to assume the alg given that a DID DHT document contains a verification method with the following public key material:

"publicKeyJwk": {
  "kty": "OKP",
  "crv": "Ed25519",
  "x": "375SKiRFNsqZkAxD1pp4y-uWVFPG0jUsLQOe3inWPnU"
}

In practical usage, the alg could be safely assumed to be EdDSA (or Ed25519 given draft-ietf-jose-fully-specified-algorithms-00) based solely on the curve property Ed25519.

An implementation of DID DHT could be written to automatically include alg: EdDSA (or alg: Ed25519) when expanding a DID with this verification to a DID document (i.e., read operation / resolve function).

Example 2

IMHO it is not safe to assume any specific alg based solely on this public key:

"publicKeyJwk": {
  "kty": "EC",
  "crv": "secp256k1",
  "x": "qTH-IrSngh6_rmWdT9itRYmmmdbaVkhJlhu_8I6rzDk",
  "y": "t2tpsM53facozLz9MmOrd7hzorveQ7-vclyFnZkthtk"
}

While it is possible/likely that the alg is ES256K it could very well be intended for use in key agreement with a variant of ECDH or perhaps used for ECIES. There are algorithms that such a key could be used with that don't yet have JOSE / COSE registered algorithm names (e.g., Schnorr signatures). As. consequence, It does not seem like the DID DHT specification should advise that implementations automatically assume ECDSA usage whenever crv: secp256k1 (or secp256r1) is present in a verification method's material.

We could consider attempting to deduce alg values based on some combination of what is present in the JWK and whether the method appears in a particular verification relationship, but even then, you can't always deterministically select the alg.

@frankhinek there is some nuance, so thanks for writing this out. I agree with you that it's not safe to assume alg based solely on the kty and cty. That's separate from the statement we should always specify alg.

So how about a new proposal: alg must be specified for all key types?

To reduce the burden on implementers we could have suggested default types that are implied unless overwritten (this would also save bytes), but this does add some complexity. I am also fine to just always include the alg property.

Good suggestion.

One open question that raises is what implementers should do in the event that the alg value they wish to use is not yet registered? Presumably we should encourage use of registered algorithms but permit usage of any public alg values given that they are "understood and processed by implementations".

There are a number of alg values that have been in draft for an extended period of time. For example:

alg values from those expired drafts are in use by practitioners today and yet still are not listed in the IANA JOSE registry. Several alg values have found their way to COSE but have yet to be registered in JOSE (some but not all of the below).

Screenshot 2024-02-20 at 3 16 32 PM Screenshot 2024-02-20 at 3 17 16 PM Screenshot 2024-02-20 at 3 17 19 PM

One additional reason to NOT require that alg be specified in the DID document for all key types:

  • A key listed as a verification method in a DID Document may be used with multiple alg values over its lifetime. Implementers would either have to reject otherwise valid messages due to an alg mismatch OR would have to ignore the alg in the DID document (which calls into question, why even have it specified?)

For example, let's say this you control this DID:

{
  "id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
  "controller": "did:example:abcd",
  "alsoKnownAs": [
    "did:example:efgh",
    "did:example:ijkl"
  ],
  "verificationMethod": [
    {
      "id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0",
      "type": "JsonWebKey",
      "controller": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
      "publicKeyJwk": {
        "kty": "OKP",
        "crv": "Ed25519",
        "x": "YCcHYL2sYNPDlKaALcEmll2HHyT968M4UWbr-9CFGWE",
        "alg": "EdDSA",
        "kid": "0"
      }
    },
    {
      "id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#example",
      "type": "JsonWebKey",
      "controller": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
      "publicKeyJwk": {
        "kty": "EC",
        "crv": "P-256",
        "x": "j3cCDZFyaC-EucYxsrt3Wen42S1uWV8W_pZyuHrboRk",
        "y": "QqDEUuwZhrSE6zECuzxjRNw0erMsksvPT6uegReh2Vs"
      }
    }
  ],
  "authentication": [
    "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
  ],
  "assertionMethod": [
    "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
  ],
  "capabilityInvocation": [
    "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
  ],
  "capabilityDelegation": [
    "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
  ],
  "keyAgreement": [
    "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#example"
  ],
  "service": [
    {
      "id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#service-1",
      "type": "TestService",
      "serviceEndpoint": [
        "https://test-service.com/1",
        "https://test-service.com/2"
      ]
    }
  ]
}

Initially, an app using DIDComm messaging uses the P-256 key identified by #example for anonymous message encryption. All JWE's use an alg value of ECDH-ES and all recipients of said messages can verify the alg matches the value present in the DID's document.

Then the app needs to transmit a message using sender authenticated encryption which mandates using the alg value ECDH-1PU in the JWE. What happens? Does the DID document need to be updated and re-published to the DHT so that the recipient can verify the message? Or does the recipient ignore the alg mismatch between the JWE they received and what is in the DID document?

The next message needs to use anonymous encryption with an alg of ECDH-ES. Same question arises: update the DID document again or should other entities ignore the alg mismatch?

Some days later another entity needs to send this DID a wrapped key and wants to encrypt it using a JWE with an alg of ECDH-ES+A256KW. What happens in this case? Can the sender use the #example verification method key for this purpose or does it fail because the recipient doesn't have any verification methods in their DID document that support ECDH-ES+A256KW?

This issue would repeat with every variant of alg that is compatible with a given JWK (e.g., ECDH-1PU+A256KW, etc.)

I believe this is one of the reasons that alg is NOT a required property of a JWK but is a required property of both JWS and JWE. Mandating that implementers always include an alg in the verification material of DID DHT documents may create more potential for issues / ambiguity rather than diminishing it.

Even with the case of Ed25519 keys that are, in practice, only ever used with EdDSA: What happens when the new Ed25519 algorithm value starts being used if draft-ietf-jose-fully-specified-algorithms is adopted? There will be a period of months/years when libraries that developers use to work with JWKs, JWS, and JWE will only support an alg of EdDSA while others will be updated to use Ed25519. What should implementations due as a result of these alg mismatches? In other words, even if DID DHT specific libs were updated immediately that doesn't mean the ecosystem of many other libs across a dozen languages is going to snap to the new standard overnight.

One solution (albeit impractical to adopt) is for the alg property to have been specified as an array to identify the algorithm(s) with which the key is intended to be used, much like was done for key_ops. I'd imagine there was vigorous debate at the time when RFCs 7515-7520 were being drafted and perhaps revisiting why alg remained an optional property of JWK could be helpful context.

because alg is a string the array property wouldn't quite work, though that was my initial thought too.

this is clearly a complex problem, as you have outlined. my gut says - if you want multiple algorithms for a key specify that key multiple times with unique KIDs and algorithms. of course that will conflict with our guidance on using the thumbprint, and further complicates things.

what I'm still going back and forth on is whether it's better for implementers to specify the alg they're using in the JWK itself or to leave it empty intentionally. perhaps we allow it as an option, but ... then we should have some language around scenarios where the same key is used with multiple algorithms.

let's find some time to discuss with @selfissued to hear his expert perspective here

A key listed as a verification method in a DID Document may be used with multiple alg values over its lifetime.

Use of keys for multiple purposes and especially with different algorithms is a known bad security practice. Our design should explicitly forbid that.

There are a number of alg values that have been in draft for an extended period of time. For example:
https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04#section-2
https://datatracker.ietf.org/doc/html/draft-amringer-jose-chacha-02

alg values from those expired drafts are in use by practitioners today and yet still are not listed in the IANA JOSE registry.

There's nothing magic about getting algorithms registered with IANA. It requires creating/finishing a draft that does so. If there's business need to use some of the algorithms, as opposed to the already registered ones, drafts could be created to do that. That said, if there's not a compelling need for them, there's not a good reason to do that work. We could discuss particulars, as appropriate.

Several alg values have found their way to COSE but have yet to be registered in JOSE (some but not all of the below).

As above, if there's a business reason to use these in JOSE, drafts can be created to register them. That's particularly easy now, with the JOSE working group being active again. Again, I'd be glad to discuss motivations for using particular algorithms.

What happens when the new Ed25519 algorithm value starts being used if draft-ietf-jose-fully-specified-algorithms is adopted?

It has been adopted and is now https://datatracker.ietf.org/doc/draft-ietf-jose-fully-specified-algorithms/. Feedback on the specification is welcomed.