Closed OR13 closed 4 years ago
@msporny @selfissued heads up, I have changed my view of being more restrictive of publicKeyJwk
based on experience working with JOSE and the VC Data Model. IMO VC-JWT is dead on arrival as a usable credential format for DIDS without the change outlined in this issue...
For example, when trying to create VC-JWT Domain Linkage Credential for the Well Known DID Configuration Spec (DIF / MS / Transmute / Mattr)... this is what we are looking at:
const jose = require("jose");
const key = {
id: "did:web:identity.foundation#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
type: "JwsVerificationKey2020",
controller: "did:web:identity.foundation",
publicKeyJwk: {
crv: "Ed25519",
x: "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ",
kty: "OKP",
kid:
"did:web:identity.foundation#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
},
privateKeyJwk: {
crv: "Ed25519",
x: "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ",
d: "tP7VWE16yMQWUO2G250yvoevfbfxY25GjHglTP3ZOyU",
kty: "OKP",
kid:
"did:web:identity.foundation#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
},
};
const credentialSubject = require("./credentialSubject.json");
// const credentialSubject = {
// id: "did:web:identity.foundation",
// domain: "identity.foundation",
// };
describe("jwt-domain-linkage-credential", () => {
it("sign and verify JWT", () => {
const jwtPayload = {
sub: credentialSubject.id,
iss: credentialSubject.id,
nbf: 1541493724,
iat: 1541493724,
exp: 2573029723,
nonce: "660!6345FSer",
vc: {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
],
type: ["VerifiableCredential", "DomainLinkageCredential"],
credentialSubject,
},
};
const jwt = jose.JWT.sign(jwtPayload, jose.JWK.asKey(key.privateKeyJwk), {
iat: false, // do not overrite iat
kid: true, // pushes kid to JWT Header,
// beware that kid can anything...
// an incredibily terrible property of jose...
});
const { header, payload, signature } = jose.JWT.verify(
jwt,
jose.JWK.asKey(key.publicKeyJwk),
{
complete: true,
}
);
expect(header).toEqual({
kid:
"did:web:identity.foundation#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
alg: "EdDSA",
});
expect(payload).toEqual(jwtPayload);
expect(signature).toEqual(
"ly4pu1jJbLShaSkKgS68nPBSZjzkA-3dlfuOI6_GV8CGDx6-pYg2npiBNi3DqKelRndUM6e25agp36-63el2DA"
);
});
});
I suppose we could double down on the ambiguity, and say that kid
can be whatever, and just leave the VC Data Model fixing for another WG....
its possible to resolve a key for a JWT if iss
is a did
and kid
is a publicKey.id
... and since there is no normative language in vc data model about his, maybe its best that we just explicitly note that kid
can be anything, and it might be totally useless property, or contain meta data or pii, or other harmful values... per the JOSE spec.
Related to your latest reply, @OR13, I would rather just accept the kid
optionality as it is described in the JWK/JWT spec, and make a specific note about it in the DID spec so people realize that the publicKey.id
is the overarching construct that covers all variants.
That sounds good to me... I think the did core spec has to mention what happend in VC-JWT though, because it provides examples that people are building implementations from...
Something informational to the effect of:
kid
can be anything, publicKey.id
is often used as the kid
value in JWS/JWT/JWE... however there are also cases where kid
is not prefixed with a DID. In such cases, implementers need to know how to resolve the kid
identifier on a case by case basis:
example using iss
example using sidetree jws
example from well-known did configuration
This will provide clarity, while not actually reducing developer burden, and we can try again when we get VC Data Model 2.0
Suggest we close this issue, we have spec language that addressed this.
The spec currently says:
The value of the id property MAY be structured as a compound key. This is especially useful for integrating with existing key management systems and key formats such as JWK. It is RECOMMENDED that JWK kid values are set to the public key fingerprint. It is RECOMMENDED that verification methods that use JWKs to represent their public keys utilize the value of kid as their fragment identifier.
Agree with @OR13, we can close this.
No comments raising objections to closing this issue
There are many JWS/JWT implementations out there that don't have any
kid
... so we needpublicKey.id
to be a separate thing... I think we have established consensus on this topic, thats not what this issue is about.For DID Documents that contain
verificationMethod
s that usepublicKeyJwk
For example:The following language in did core would be exceedingly helpful / fix a major problem linking JWTs and DIDs...
If
kid
is present inpublicKeyJwk
its value MUST equal${verificationMethod.controller}#${rfc7638(verificationMethod.publicKeyJwk)}
.https://tools.ietf.org/html/rfc7638
per jose https://tools.ietf.org/html/rfc7517#section-4.5
kid
can be anything... so this is legal... if we don't prefix thekid
with the controller... we have no guarantee of a resolvable verificationMethod for JWT/JWS... which cripples them from a VC perspective.Why?
https://www.w3.org/TR/vc-data-model/#jwt-encoding
When this language is combined with https://tools.ietf.org/html/rfc7517#section-4.5
The result is potentially valid VC-JWT with no way to lookup the key used to sign it.... because JOSE allows
kid
to be anything....This section of text is non-normative:
https://www.w3.org/TR/vc-data-model/#example-27-jwt-header-of-a-jwt-based-verifiable-credential-using-jws-as-a-proof-non-normative
So basically, the VC-JWT format is not defined, because key discovery was out of scope for the VC Data Model... yet, key discovery for LD-Proofs is defined, because of the use of
assertionMethod
.https://www.w3.org/TR/vc-data-model/#proofs-signatures-0
The fact that similar language was not added for VC-JWT is imo, the its greatest failing w.r.t. use with DIDs....
VC-JWT offers this confusing non normative and completely incompatible example:
https://www.w3.org/TR/vc-data-model/#example-28-jwt-payload-of-a-jwt-based-verifiable-credential-using-jws-as-a-proof-non-normative
This implies that VC-JWTs are issued by keys, whereas LD-Proofs are issued by identifiers.... there is no well-known open id configuration, no jwks url to check for
kid
s that could be anything... this differs from oidc where issuer is a domain, not a reference to a specific key... also...https://example.com/keys/foo.jwk -> jwk
did:web:identity.foundation#Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY-tA4A -> did document...
One uses a fragment, the other does not.... they point to completely different resources...
The mapping for VC-JWT is not defined.... and IMO, VC JWT is totally broken because of this.
This proposal would allow jose libraries to produce valid VC JWTs, with no modification...if they normally support
kid
it will be inserted correctly.Some objections:
There are many implementations of JWT/JWS in the wild today that use publicKey.id for kid.... so either we duplicate things like this, or did core says that UPort / Mattr / Transmute / anyone using non JWK encoded keys to produce JWS / JWT are wrong and we make JWK the only key format for DIDs.... this will never happen, please recognize that it will never happen, and that this duplication is what allows those implementations to be valid / continue to work, while resolving ambiguity for the people who come later and only support JWK.
Per https://tools.ietf.org/html/rfc7517#section-4.5
kid
can be whatever anybody wants... so we can be more restrictive here if we want to... there are a million reasons why its a good idea to be more restrictive here... allowingkid
to be "whatever" was a huge mistake by the jose spec authors IMO.This is one of the largest problems with it... its so bad, I would say that this issue makes the VC-JWT representation NOT USABLE.... this kind of ambiguity is interop destroying... since we can't fix it in the vc data model... we should consider fixing it here....
Alternatives:
VC JWT could have mandated that
iss
not be used to identify a specific key... and then could have added language similar to open id / oauth... for looking up a key used for a JWT by usingiss
andkid
and well known URIs...VC JWT could have mandated that if
iss
is a did, thenkid
is a fragment or that ifkid
contains a did, theniss
is that did.VC JWT could have mandated what I'm proposing in this issue.