hyperledger / aries-rfcs

Hyperledger Aries is infrastructure for blockchain-rooted, peer-to-peer interactions
https://hyperledger.github.io/aries-rfcs/
Apache License 2.0
325 stars 218 forks source link

RFC 0019: Updating to JWE compliant data model #133

Closed kdenhartog closed 4 years ago

kdenhartog commented 5 years ago

Recently there's been discussion to update this work to be fully JWE compliant. One of the proposed solutions that's come up is to generate the following format:

{
    "protected": base64url({
        "typ": "prs.hyperledger.aries-auth-message",
        "alg": "ECDH+XC20PKW",
        "enc":"XC20P"
    }),
    "recipients": [
        {
            "encrypted_key": "base64url(encrypted CEK)",
            "header": {
                "kid": "base58(Recipient vk)",
                "iv": "base64url(CEK encryption IV)",
                "pk": "compactJWE(Sender PK)"
            }
        },
        {
            "encrypted_key": "base64url(encrypted CEK)",
            "header": {
                "kid": "base58(Recipient vk)",
                "iv": "base64url(CEK encryption IV)",
                "pk": "compactJWE(Sender PK)"
            }
        }
    ],
    "aad": "base64url(sha256(concat('.',sort([recipients[0].kid, recipients[n].kid]))))",
    "iv": "base64url(content encryption IV)",
    "ciphertext": "base64url(ciphertext)",
    "tag": "base64url(AEAD Authentication Tag)"
}

Exploded example of compactJWE(Sender PK):
{
    "protected": base64url({
        "iv": "base64url(CEK encryption IV)",
        "epk": "Ephemeral JWK",
        "typ": "jose",
        "cty": "jwk+json",
        "alg": "ECDH-ES+XC20PKW",
        "enc":"XC20P"
    }),
    "encrypted_key": "base64url(encrypted CEK)",
    "iv": "base64url(content encryption IV)",
    "ciphertext": "base64url(Encrypted Sender vk as JWK)",
    "tag": "base64url(AEAD Authentication Tag)"
}

Here's a provided example of what this would look like:

{
    "protected": "eyJ0eXAiOiJwcnMuaHlwZXJsZWRnZXIuYXJpZXMtYXV0aC1tZXNzYWdlIiwiYWxnIjoiRUNESCtYQzIwUEtXIiwiZW5jIjoiWEMyMFAifQ",
    "recipients": [
        {
            "encrypted_key": "whpkJkvHRP0XX-EqxUOHhHIfuW8i5EMuR3Kxlg5NNIU",
            "header": {
                "kid": "5jMonJACEPcLfqVaz8jpqBLXHHKYgCE71XYBmFXhjZVX",
                "iv": "tjGLK6uChZatAyACFzGmFR4V9othKN8S",
                "tag": "ma9pIjkQuzaqvq_5y5vUlQ",
                "pk": "eyJpdiI6IldoVGptNi1DX2xiTlQ4Q2RzN2dfNjdMZzZKSEF3NU5BIiwiZXBrIjp7Imt0eSI6Ik9LUCIsImNydiI6IlgyNTUxOSIsIngiOiJnNjRfblJSSFQyYk1JX1hjT1dHRTdJOGdQcU1VWTF4aUNub2J0LVhDUkNZIn0sInR5cCI6Impvc2UiLCJjdHkiOiJqd2sranNvbiIsImFsZyI6IkVDREgtRVMrWEMyMFBLVyIsImVuYyI6IlhDMjBQIn0.4zUt5tOOlcQWskJqxfMi0tNsfUCAzb5_PDfPqQ1h0Vw.xYkeEXV1_cSYFEd6UBMIfl8MWQfHaDex.XSNKTRXye5-iSXQ-aS_vQVZNEgFE6iA9X_KgSRMzihQBMoI1j4WM3o-9dMT9TeSyMvdq3gXt1NpvLdZHpJplahhk3mxMZL-vawm5Prtf.H7a5N-dggwdesjHyJCl06w"
            }
        },
        {
            "encrypted_key": "dDHydlp_wlGt_zwR-yUvESx9fXuO-GRJFGtaw2u6CEw",
            "header": {
                "kid": "TfVVqzPT1FQHdq1CUDe9XYcg6Wu2QMusWKhGBXEZsosg",
                "iv": "7SFlGTxQ4Q2l02D9HRNdFeYQnwntyctb",
                "tag": "9-O6djpNAizix-ZnjAx-Fg",
                "pk": "eyJpdiI6IkV6UjBFaVRLazJCT19oc05qOVRxeU9PVmVLRFFPYVp1IiwiZXBrIjp7Imt0eSI6Ik9LUCIsImNydiI6IlgyNTUxOSIsIngiOiJoU1g1NGt5ZTdsd0pBdjlMaUplTmh4eFhaV1N0M3hLSDBXUmh6T1NOb1c0In0sInR5cCI6Impvc2UiLCJjdHkiOiJqd2sranNvbiIsImFsZyI6IkVDREgtRVMrWEMyMFBLVyIsImVuYyI6IlhDMjBQIn0.qKmU5xO8Z1ZtRBWEjEMixb5VZG0mrY0LnjUGjLqktwg.EG-VOZSC2vLdoO5l2_A37IYvdXCckLZp.D4kgD6bYL1YfXyApk5ESKE2sc8TUiO-QGBtY-M5hcV_F88JPZdsi53Qofxk02ZxPHJZK-abDy45pIMH6-KUMDfE6WKhW3nPQhydPYutv.0SO4VjM8sDH-wGHcEpinTg"
            }
        }
    ],
    "aad": "OGY5ZDIxMDE3YTQ4MTc4YWE5MTk0MWQyOGJmYjQ1ZmZmMTYzYTE3ZjUxYjc4YjA3YTlmY2FlMmMwOTFlMjBhZg",
    "ciphertext": "x1lnQq_pZLgU2ZC4",
    "tag": "2JgOe9SRjJXddT9TyIjqrg",
    "iv": "fDGEXswlWXOBx6FxPC_u6qIuhADnOrW1"
}

I'm curious what other's opinions are on this approach. I personally am not a fan of the bloat the compact JWE adds in order to encrypt the sender's public key. However, I've not found another approach yet that seems satisfactory. If anyone has some suggestions it would be appreciated.

llorllale commented 4 years ago

@dhh1128 you are correct with your https://github.com/hyperledger/aries-rfcs/issues/133#issuecomment-535691424 in that putting a reference to the Sender's key inside the spk at the moment of sending does not solve the timing problem.

The thing is I'm not trying to solve that problem - I'm attempting to solve the "reverse lookup" problem although in a sloppy way I admit. But now I went back to one of your comments on #104 and see that you had actually proposed to insert this claim as sid ("sender ID"). I'm fine with sid.

Moopli commented 4 years ago

@dhh1128 about onion routing bloat: from what I can tell the main cause of bloat is base64 encoding ciphertext for packing, then encrypting and b64encoding it again. Each encode is a 4/3 multiplier on ciphertext size. The rest of the bloat is from the (effectively constant) size of the additional JSON wrapping, headers, etc, and most of that bloat remains with a header encryption chain, but that's definitely much more manageable (linear with onion layer depth rather than exponential). An ascii-safe encoding for binary data like the ciphertext is important for transmission, but an alternative solution would be:

  1. Given an envelope P as payload to wrap inside another envelope E
  2. Edit P by base64 decoding the ciphertext
  3. encrypt P
  4. base64 encode the resulting ciphertext
  5. build E around that

Then when decrypting, the recipient will base64 decode and decrypt the ciphertext as usual, and then will see if it's an envelope to send on, in which case it base64 encodes just the ciphertext of this envelope.

You could split this between packing/unpacking and sending/receiving: when packing, don't b64 encode the ciphertext, and only b64 encode the ciphertext of the final envelope once all envelope layers have been packed. When receiving, b64 decode the ciphertext of the received envelope, and then unpack multiple layers if possible. (This split dodges the extra work of base64 encoding then immediately decoding the ciphertext when creating an onion envelope or peeling multiple layers in one agent)

dhh1128 commented 4 years ago

@lovesh , you may want to comment on @Moopli 's idea. (Lovesh had previously proposed a solution to this problem.)

llorllale commented 4 years ago

@dhh1128 @Baha-sk @kdenhartog @Moopli @lovesh

I think it's a good idea to reduce the size of the pack() output. However, can we consider solving the "runaway bloat" problem related specifically for routing in a separate RFC? I think it would be cleaner if the re-encryption problem is addressed in the outer envelope (the Forward message). Yes, we would be re-encrypting the output of pack() but hopefully only once, and hopefully the metadata on this outer envelope is editable such that we can add hops as we please without having to touch the ciphertext again.

Just my 2 c.

Moopli commented 4 years ago

I should note that I'm not trying to propose this as an alternative - I want to point out that the repeated base64 encoding (really, any binary -> ascii-safe encoding, if repeated on data) is the source of the exponential bloat. In fact it occurs to me that my scheme runs into problems when decoding, since arbitrary binary data (the payload) could contain unescaped string-terminating characters and in general break json parsing (if your payload is an envelope, which you must parse as JSON to forward to the next recipient, and by my scheme the ciphertext within that envelope is binary rather than for example base64).

@llorllale fair enough

lovesh commented 4 years ago

@Moopli As you pointed out in your last comment, having the ciphertext of the inner envelope as bytes is not safe (you could represent the bytes as integers but that will increase the size as well). Secondly, decoding the inner envelope from base64 each time before re-encoding the outer envelope is expensive.

kdenhartog commented 4 years ago

@lovesh can you referencing me to some documentation that shows that re-encrypting is not safe? I was not aware that this was a problem and would like to read up on it so I'm more aware of the problems it presents.

Moopli commented 4 years ago

Here's a concern we've been running into recently with aries-framework-go. The current envelope format (I checked ACA-Py and Indy SDK) uses signing keys everywhere, converting them to encryption keys (as you can do with ed25519/curve25519) solely for encryption. Note that this extends to packing signing keys in the envelope, with the recipient doing conversion as well. @Baha-sk's current Go implementation of this draft spec packs the encryption keys, and since it's interoperable with the PHP example I'd assume @gamringer is doing the same.

There's a hole here:

Now, for RSA, for example, this should not be an issue, verification keys are encryption keys and could technically be used for both purposes, but for Curve25519, where we'd want to use Ed25519 for EdDSA, we do need to convert.

Personally I'd prefer that we generate signing and encryption keypairs as distinct keypairs, and develop a DIDexchange protocol that will exchange both, no matter whether the asymmetric crypto algorithm in use allows us to use the same keys or derive one from the other.

dhh1128 commented 4 years ago

Agreed. I think @kdenhartog was planning to go this direction.

On Fri, Oct 11, 2019, 10:57 AM Filip Burlacu notifications@github.com wrote:

Here https://github.com/hyperledger/aries-framework-go/issues/454's a concern we've been running into recently with aries-framework-go. The current envelope format uses signing keys everywhere, converting them to encryption keys (as you can do with ed25519/curve25519) solely for encryption. Note that this extends to packing signing keys in the envelope, with the recipient doing conversion as well. @Baha-sk https://github.com/Baha-sk's current Go implementation of this draft spec packs the encryption keys, and since it's interoperable with the PHP example I'd assume @gamringer https://github.com/gamringer is doing the same.

There's a hole here:

  • DIDexchange does not exchange encryption pubkeys in addition to verification keys
  • This spec does not mandate that signing keys be used to create encryption keys, or that the keys included as part of the envelope be verification keys

Now, for RSA, for example, this should not be an issue, verification keys are encryption keys and could technically be used for both purposes, but for Curve25519, where we'd want to use Ed25519 for EdDSA, we do need to convert.

Personally I'd prefer that we generate signing and encryption keypairs as distinct keypairs, and develop a DIDexchange protocol that will exchange both, no matter whether the asymmetric crypto algorithm in use allows us to use the same keys or derive one from the other.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hyperledger/aries-rfcs/issues/133?email_source=notifications&email_token=AAQ3JCFWIMMRVDXBGNVC433QOCV6DA5CNFSM4IE6JH2KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBATA4Y#issuecomment-541143155, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQ3JCE2I3SDIBMACGNPVZLQOCV6DANCNFSM4IE6JH2A .

llorllale commented 4 years ago

@Moopli @dhh1128 @kdenhartog would it make sense to bake encryption keys into the ~service decorator and DIDDoc service attribute by adding a note in the didcomm conventions RFC?

kdenhartog commented 4 years ago

Here's a concern we've been running into recently with aries-framework-go. The current envelope format (I checked ACA-Py and Indy SDK) uses signing keys everywhere, converting them to encryption keys (as you can do with ed25519/curve25519) solely for encryption. Note that this extends to packing signing keys in the envelope, with the recipient doing conversion as well. @Baha-sk's current Go implementation of this draft spec packs the encryption keys, and since it's interoperable with the PHP example I'd assume @gamringer is doing the same.

There's a hole here:

* DIDexchange does not exchange encryption pubkeys in addition to verification keys

* This spec does not mandate that signing keys be used to create encryption keys, or that the keys included as part of the envelope be verification keys

Now, for RSA, for example, this should not be an issue, verification keys are encryption keys and could technically be used for both purposes, but for Curve25519, where we'd want to use Ed25519 for EdDSA, we do need to convert.

Personally I'd prefer that we generate signing and encryption keypairs as distinct keypairs, and develop a DIDexchange protocol that will exchange both, no matter whether the asymmetric crypto algorithm in use allows us to use the same keys or derive one from the other.

What's being done in both the IndySDK implementations, the ACA-py implementation, and DIDComm-js is a conversion between an ed25519 key to an x25519 key. This is made possible by birational equivalence between Montgomery curves and twisted Edwards curves.

See here for the math: https://en.wikipedia.org/wiki/Montgomery_curve#Equivalence_with_twisted_Edwards_curves

As for whether this is secure or not is another question. In the use of RSA keys, it's been proven that reuse of an RSA key for both signing and encryption is insecure when using textbook RSA. However, with elliptic curves, there's been no one to produce proof showing it's secure or insecure to reuse keys for DHKE and signing operations. With that said, I tend to agree that the use of independent keys is a better practice and would suggest that's the direction we go.

However, I will point out that doing this will introduce new complexity around how we're managing keys and would need some new did doc conventions to list these keys in the DID Doc.

@Moopli @dhh1128 @kdenhartog would it make sense to bake encryption keys into the ~service decorator and DIDDoc service attribute by adding a note in the didcomm conventions RFC?

This is another option to consider. It could be useful to do this, but I'm of the opinion that stuffing more of the cryptography into the semantic layer is an anti-pattern because it requires developers who want to focus on business logic and protocols to now have to learn about proper key rotation, key reuse, and the different tradeoffs between key types. Essentially, they have to learn enough crypto to be dangerous. Rather it's of my opinion that we contain the cryptography to the secure channel protocol aka DIDComm Crypto layer. Especially if we expect the use of multiple parties in the communication.

baha-ai commented 4 years ago

@kdenhartog thanks for the detailed reply. I believe the envelope exchange is only used to encrypt/decrypt the packed payload, therefore it would make sense for the exchanged keys to be crypto keys and the payload should contain signing keys needed to verify its content.

So the message flow in an agent would look like this:

This way, steps 3 and 4 above don't handle signing keys at the Envelope layer while the payload has all it needs for signing/verification.

The PHP example and Go example I posted earlier (here) is using encryption keys only (no signing keys) in the envelope. This should be in concordance with your opinion.

By the way, I liked the video about the Noise protocol from the YouTube video you posted. We do implement some of the features in the framework in our example, but stick to JWE compliance (with ephemeral keys + nonce, the Noise framework states nonces are not needed with ephemeral keys, that's another topic)

kdenhartog commented 4 years ago

@kdenhartog thanks for the detailed reply. I believe the envelope exchange is only used to encrypt/decrypt the packed payload, therefore it would make sense for the exchanged keys to be crypto keys and the payload should contain signing keys needed to verify its content.

So the message flow in an agent would look like this:

* The sending agent will:

1. Sign a payload and embed its public verification (signing) key in the payload (as a DID document)

2. Call Pack(payload above + encryption keys), no signing keys involved for Pack

* The receiving agent will:

1. Call Unpack(received envelope + their encryption key), no signing key involved here

2. Verify payload extracted from Unpack above using the embedded corresponding recipient verification(signing) from the extracted payload (the receiving agent can fetch from their wallet a matching sender verification key).

This way, steps 3 and 4 above don't handle signing keys at the Envelope layer while the payload has all it needs for signing/verification.

The PHP example and Go example I posted earlier (here) is using encryption keys only (no signing keys) in the envelope. This should be in concordance with your opinion.

By the way, I liked the video about the Noise protocol from the YouTube video you posted. We do implement some of the features in the framework in our example, but stick to JWE compliance (with ephemeral keys + nonce, the Noise framework states nonces are not needed with ephemeral keys, that's another topic)

This is a good idea. In the JWS, we can provide a reference to a different key than the one referenced in the outer JWE layer. This raises the question though, what happens when the key used to sign the JWS is in a different DID Doc than the one in the JWE? There's some weird edge cases we should be accounting for here, but in total - I agree this is a good solution.

dhh1128 commented 4 years ago

I want to re-emphasize something about key references that I've said before (possibly on different discussion threads): crypto can't depend on DID resolution. We cannot be looking up key references in DID documents to do either of the following operations:

The value of the kid field in JW structures is not valid input to either of these operations. We could* use a key reference to confirm that the key we're seeing has proper standing with respect to the party that's purporting to communicate with us--but that is not the same thing as the cryptographic operation of validating a signature.

Are we aligned on this, or did I just raise eyebrows?

baha-ai commented 4 years ago

@dhh1128 that's what I'm trying to get at, JWE must not have any verification key stored in its headers. The keys stored in the envelope should be for encryption/decryption of the JWE only. This is one of the reasons why we should push the RFC to use the new JWE format I was referring to in my previous post.

dhh1128 commented 4 years ago

@Baha-sk : I am content with the idea that we should split verification keys and encryption keys apart; the conflation of these was an accident of us using Ed25519. And I get that you're advocating the removal of the verkeys from the JWE. That's all fine. But what I don't know if we're aligned on is this: I want an encryption key value--NOT a reference to an encryption key named in a DID doc--to be named in the JWE, and I want a verkey value--NOT a reference to a verkey in a DID doc--as the field that a JWS uses to verify.

baha-ai commented 4 years ago

@dhh1128 the encryption keys in the JWE example are real public key values. They are not reference IDs nor are they stored in the DID doc.

troyronda commented 4 years ago

Saw this update on the DIF issue tracker: https://github.com/decentralized-identity/DIDComm-js/issues/8#issuecomment-545125297

(referring to https://github.com/digitalbazaar/minimal-cipher and https://github.com/digitalbazaar/encrypted-data-vaults/issues/5)

troyronda commented 4 years ago

The above noted effort allows:

Also note the kid format:

${did}#${keyAgreementKey.fingerprint()}

dhh1128 commented 4 years ago

The kid format is fatally flawed unless it includes a versionstamp or (less optimally) a timestamp. How can we get it upgraded?

troyronda commented 4 years ago

It would be nice to have alignment across communities on these topics.

I was also going to mention that it would be nice to have overlapping algorithm support in the envelope. Note that they have ECDH-ES+A256KW and additionally allow for 256-bit AES-GCM. The rationale for the latter also seems like a good idea (allowing for FIPS).

tplooker commented 4 years ago

How is the kid fatally flawed? In an encrypted envelope the kid is just suppose to act as an alias to a private key for the recipient, DID resolution isn't even required. The kid can literally be anything, as long as it is meaningful to the recipient, it really comes down to what the recipient indexes the keys in their kms against.

tplooker commented 4 years ago

I think we need to be clearer around the intention of the kid in different circumstances.

When we are talking about encryption/decryption, the kid is really acting as an alias to a private key the recipient should have, whereas when we are verifying a signature, the kid should allow the audience of the signature to obtain a public key to perform verification.

dhh1128 commented 4 years ago

I agree that we need to be clearer about intention. However, that doesn't affect my assertion that the kid format of ${did}#${keyAgreementKey.fingerprint()} is fatally flawed.

Understand: I'm not saying that the idea of kid is fatally flawed. I'm staying that the specific kid format is.

It's flawed because without a versionstamp or timestamp, it can't be dereferenced -- either for encryption or for signature validation.

Analogy: you want an email address, and I give you "@example.com" as the answer. I claim that answer is fatally flawed, because it doesn't include the mailbox identifier.

A kid without a versionstamp or timestamp might be dereferencable, sometimes, if no key rotations have ever happened. And people who are building trivial, non-production systems get away with using such a kid because they ignore this. But to really use a kid to solve production problems, you can't ignore it. If you do, your kid is fatally flawed.

tplooker commented 4 years ago

I understand your reasoning, but this key format proposed I dont think is fundamentally flawed in the way you are describing for encryption and decryption, which is the context it is being applied in, because the fragment portion is a fingerprint. Which implies an immutable and unique reference to a single key.

Say you rotate a key but some sender continues to send you encrypted messages to your old key, provided you still have the old key indexed in your kms by this kid format, you should still be able to decrypt the message. And safely decrypt messages from new keys linked to the same did.

I do understand that because identifiers for keys in did docs are not enforced as immutable by default (e.g not every key featured in a did doc has an id corresponding to the keys unique fingerprint) this is not a universal rule for all did methods. However, this as a kid format if it were universally supported I believe would work without major issue for encryption/decryption.

Essentially when it comes to a kid for a signature, the easiest option to preserve verification is to make the kid the public key, no dereferencing required. However when it is an encrypted message the kid must be some meaningful alias to the recipient, there really is no need to have a versionstamp or timestamp, either the alias means something to me as a recipient or it doesn't.

dhh1128 commented 4 years ago

I dont think is fundamentally flawed in the way you are describing for encryption and decryption, which is the context it is being applied in, because the fragment portion is a fingerprint. Which implies an immutable and unique reference to a single key.

The problem is that the key value is immutable, but the key's meaning with respect to the DID doc -- the context in which that key is related to the DID -- is not. That key value might have been authorized to encrypt on behalf of the DID at point A, and not authorized to encrypt at point B, and you wouldn't know that. So you could do naked decryption with just the key value, but you can't do the semantic validation that should follow naked decryption, without the additional context.

However when it is an encrypted message the kid must be some meaningful alias to the recipient, there really is no need to have a versionstamp or timestamp, either the alias means something to me as a recipient or it doesn't.

I disagree. You are assuming that all decryption happens at the time of transmission, which is also the time of reception. If you separate those timeframes, the "either it means something to me or it doesn't" unravels. It might not mean something to me at the time I receive it, but it might be important that it means something to me later. Or vice versa.

selfissued commented 4 years ago

A kid value is used to match a particular key. No less and no more. They're assigned by the party that creates the key and they're simply strings that are intended to uniquely identify that key. They work great for that purpose and are in widespread use.

dhh1128 commented 4 years ago

@selfissued : Right. 100% agree. I think kids are great.

My concern is that the intersection between kids and DIDs is suffering because of a design decision in the DID spec (which I vehemently disagreed with but failed to change) that the relationship between a key identifier and a key value inside a DID doc is mutable. It is illegal to assume that did:foo:123#identifier always refers to the same thing.

Now, fans of JW* in DID circles have suggested eliminating id as a required property on a key, claiming that kid is an equivalent. I don't think they realize the mess they're signing up for. An id has meaning in the larger context of the rest of the doc, and one of those meanings, according to the DID spec, is that you can use it to construct DID URLs with particular semantics. Among those semantics is the idea of mutability--that the data behind the identifier can be rotated. So if kid is an equivalent of id, then it follows that a kid that's a DID URL composed of a DID doc plus an identifier is not allowed to be assumed to be stable with respect to time. This is in direct conflict with the intention of kid, which is the simple and stable mapping you articulated.

Possible solution 1: we stop thinking that kid and id mean the same thing. If someone wants a key to have a mutable relationship between its identifier and its value, they use id; if they want immutability, they use kid. (Personally, I would never use 'id'...) We don't confuse them. A DID URL in the format did:foo:123#xyz is an id-based URL, NOT a kid-based URL. If we want a kid-based URL, we use something different, like maybe did:foo:123;kid=xyz.

Possible solution 2: we like kid = id, and we accept that this means DID URLs that reference keys by kid must have a versionstamp or timestamp to disambiguate. We stop kidding ourselves that did:foo:123#identifier is enough to accomplish what kid does.

tplooker commented 4 years ago

IMO the case you describe @dhh1128 should be offloaded for the recipient software to deal with, e.g if recipient software rotates a key and the same kid is used in a did doc, what does the recipient software do with the old key? If its deleted then you can't decrypt anyway regardless of having a timestamp. If the software keeps the key, then how is its indexed in the kms? Could you not preserve its association to the kid it use to represent? Then if you receive a message that is encrypted to a kid where there have been multiple keys represented by that kid over time, you try each one until you can successfully decrypt.

tplooker commented 4 years ago

Alternatively if you don't want to traverse all previous keys associated to a kid, we could add an optional JOSE header field that describes when the message was encrypted, that would then give the recipient further clue as to which key they need to use to decrypt.

tplooker commented 4 years ago

I do however disagree with trying to pack more meaning into the kid field, like a timestamp/versionstamp

andrewwhitehead commented 4 years ago

The DID spec provides a format for identifying a DID Doc at a specific time using either the version-id or version-time parameters, doesn't that satisfy the need to refer to a specific key? The hl property (hash) can also be used to ensure the document integrity.

"kid": "did:method:ident;version-id=GJdXXRq7Kxu7uKQCb77ik3;hl= zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e#key-ident"

tplooker commented 4 years ago

It does, but doing this adds a lot of complexity in parsing this field. kid is simply meant to be a string alias or a hint to a key, by making the requirement in DIDComm that this is a full URL we now need a url parser to extract this information and will need to evolve this syntax as DID resolution continues to evolve.

If there is a problem with kid reuse for multiple keys over time, I think the simple choice is for the audience or consumer of the message to iterate through the keys associated to one kid or for there to be a seperate field in the kid header that hints at timing information and therefore allows them to limit the possible keys the kid is referring too.

kdenhartog commented 4 years ago

IIRC, I heard there was discussion about this during the connectathon. Would anyone be able to provide details about that discussion here for historical context?

dhh1128 commented 4 years ago

My memory of the conversation was that we got to a stalemate. @tplooker , please confirm. The comment thread here boils down to 2 basic positions, which also summarize the connectathon discussion:

  1. (Tobias) It's onerous and ugly to make a DID key reference in kid include a timestamp or version, so let's not do that.
  2. (me) If we use a DID key reference as the value of kid, it must include a timestamp or version. (The assertion that we could have an ambiguous kid that can be disambiguated by iterating over candidates until we find a match strikes me as worse than a code smell--it feels like a security vulnerability.)

FWIW, the ticket about this issue in the DID spec repo includes a comment from one of the JWE spec authors (Mike Jones) that suggests the intent of kid is to be unambiguous--else its use in caching is impossible.

tplooker commented 4 years ago

@dhh1128, yes your summary is correct.

TelegramSam commented 4 years ago

Continue issue here: https://github.com/decentralized-identity/didcomm-messaging/issues/22