Skip to content

Commit e37ef0e

Browse files
committed
Migrating lib to the new spec for verified documents.
1 parent 8b7666f commit e37ef0e

File tree

3 files changed

+101
-20
lines changed

3 files changed

+101
-20
lines changed

lib/resolver.js

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fetch from 'cross-fetch';
22

3-
var localPubKeyDB = {};
3+
let TRUST_REGISTRY = {}
4+
let LAST_FETCH = undefined;
5+
const ONE_DAY_IN_MSECONDS = 86400000;
46

57
async function getJSON(url) {
68
const res = await fetch(url);
@@ -13,26 +15,72 @@ async function getJSON(url) {
1315
return await res.json();
1416
}
1517

16-
export async function getKeyId(kid, iss) {
18+
async function getKeyId(iss, kid) {
1719
// Download pubkey to verify
18-
if (localPubKeyDB[kid]) {
19-
return localPubKeyDB[kid];
20-
}
21-
2220
let url = `${iss}/.well-known/jwks.json`;
2321

2422
try {
2523
// Try to download as a file.
2624
const newKeyset = await getJSON(url);
2725

2826
newKeyset.keys.forEach(key => {
29-
localPubKeyDB[key.kid] = { type: "URL", key: key, debugPath: url };
27+
TRUST_REGISTRY[iss + "#" + key.kid] = {
28+
"displayName": { "en": "Untrusted Issuer: " + iss.replace("https://", "") },
29+
"entityType": "issuer",
30+
"status": "untrusted",
31+
"validFromDT": "2021-01-01T01:00:00.000Z",
32+
"didDocument": key,
33+
"credentialType": [
34+
"https://smarthealth.cards#immunization"
35+
]
36+
}
3037
});
3138

32-
return localPubKeyDB[kid];
39+
return TRUST_REGISTRY[iss + "#" + kid];
3340
} catch(err) {
3441
console.warn(url, err);
3542
}
3643

3744
return undefined;
3845
}
46+
47+
export function addCachedKeys(newIssuerSet) {
48+
for (const [iss, value] of Object.entries(newIssuerSet)) {
49+
value.keys.forEach(key => {
50+
TRUST_REGISTRY[iss + "#" + key.kid] = {
51+
"displayName": { "en": "Untrusted Issuer: " + iss.replace("https://", "") },
52+
"entityType": "issuer",
53+
"status": "untrusted",
54+
"validFromDT": "2021-01-01T01:00:00.000Z",
55+
"didDocument": key,
56+
"credentialType": [
57+
"https://smarthealth.cards#immunization"
58+
]
59+
}
60+
});
61+
}
62+
}
63+
64+
export async function resolveKey(iss, kID) {
65+
const kidIndex = iss + "#" + kID;
66+
if (!TRUST_REGISTRY[kidIndex] && (!LAST_FETCH || new Date().getTime() > LAST_FETCH.getTime() + ONE_DAY_IN_MSECONDS )) {
67+
// Loading PathCheck Registry
68+
console.log('KeyID not found: ', kidIndex, ' fetching certificates from PathCheck\'s Trust Registry')
69+
70+
try {
71+
const res = await fetch('https://raw.githubusercontent.com/Path-Check/trust-registry/main/registry.json', {method: 'GET', mode: 'no-cors'})
72+
const data = await res.text()
73+
TRUST_REGISTRY = JSON.parse(data)["SmartHealthCards"];
74+
} catch (e) {
75+
console.log(e);
76+
}
77+
78+
LAST_FETCH = new Date();
79+
}
80+
81+
if (TRUST_REGISTRY[kidIndex] && TRUST_REGISTRY[kidIndex].status == "current") {
82+
return TRUST_REGISTRY[kidIndex];
83+
}
84+
85+
return await getKeyId(iss, kID);
86+
}

lib/shc.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import jose, { JWK } from 'node-jose';
22
import pako from 'pako';
33
import base64url from 'base64url';
4-
import { getKeyId } from './resolver';
4+
import { resolveKey, addCachedKeys } from './resolver';
55

66
const URI_SCHEMA = 'shc';
77

@@ -60,21 +60,28 @@ async function getVerificationKeyForJws(jws) {
6060
let parsedJwsUnsafe = await jwsParts(jws);
6161
let kid = parsedJwsUnsafe.header.kid;
6262
let iss = parsedJwsUnsafe.body.iss;
63-
return await getKeyId(kid, iss);
63+
return await resolveKey(iss, kid);
6464
}
6565

66-
async function verifyAndReturnPayload(jws, signingKey) {
67-
if (!signingKey)
68-
signingKey = (await getVerificationKeyForJws(jws)).key;
66+
async function verifyAndReturnPayload(jws, publicKeyArray) {
67+
if (publicKeyArray) {
68+
addCachedKeys(publicKeyArray);
69+
}
70+
71+
let issuer = await getVerificationKeyForJws(jws);
6972

70-
const verified = await jose.JWS.createVerify(await JWK.asKey(signingKey)).verify(jws)
73+
const verified = await jose.JWS.createVerify(await JWK.asKey(issuer.didDocument)).verify(jws)
7174

7275
let body = verified.payload;
7376
if ((verified.header).zip === 'DEF') {
7477
body = Buffer.from(pako.inflateRaw(body));
7578
}
7679

77-
return JSON.parse(body.toString());
80+
return {
81+
credential: JSON.parse(body.toString()),
82+
issuer: issuer,
83+
raw: await jwsParts(jws)
84+
};
7885
}
7986

8087
export async function verify(jws, pubkeyKey) {
@@ -129,9 +136,9 @@ export async function debug(uri) {
129136
return await jwsParts(await unpack(uri));
130137
}
131138

132-
export async function unpackAndVerify(uri, publicKeyPem) {
139+
export async function unpackAndVerify(uri, publicKeyArray) {
133140
try {
134-
return await verifyAndReturnPayload(await unpack(uri), publicKeyPem);
141+
return await verifyAndReturnPayload(await unpack(uri), publicKeyArray);
135142
} catch (err) {
136143
console.log(err);
137144
return undefined;

test/sign-verify.spec.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,28 @@ describe('Testing SmartCard Examples', function() {
242242
});
243243
it('should unpack and verify example 2', async function() {
244244
const json = await unpackAndVerify(EXAMPLE2_PACKED);
245-
expect(json).to.eql(EXAMPLE2_OBJECT);
245+
expect(json.credential).to.eql(EXAMPLE2_OBJECT);
246+
expect(json.issuer).to.eql({
247+
displayName: { en: 'Untrusted Issuer: spec.smarthealth.cards/examples/issuer' },
248+
entityType: 'issuer',
249+
status: 'untrusted',
250+
validFromDT: '2021-01-01T01:00:00.000Z',
251+
didDocument: {
252+
kty: 'EC',
253+
kid: 'EBKOr72QQDcTBUuVzAzkfBTGew0ZA16GuWty64nS-sw',
254+
use: 'sig',
255+
alg: 'ES256',
256+
x5c: [
257+
"MIICDDCCAZGgAwIBAgIUVJEUcO5ckx9MA7ZPjlsXYGv+98wwCgYIKoZIzj0EAwMwJzElMCMGA1UEAwwcU01BUlQgSGVhbHRoIENhcmQgRXhhbXBsZSBDQTAeFw0yMTA2MDExNTUwMDlaFw0yMjA2MDExNTUwMDlaMCsxKTAnBgNVBAMMIFNNQVJUIEhlYWx0aCBDYXJkIEV4YW1wbGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQHApUWm94mflvswQgAnfHlETMwJFqjUVSs7WU6LQy7uaPwg77xXlVmMNtFWwkg0L9GrlqLkIOEVfXxx5GwtZKOBljCBkzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDA5BgNVHREEMjAwhi5odHRwczovL3NwZWMuc21hcnRoZWFsdGguY2FyZHMvZXhhbXBsZXMvaXNzdWVyMB0GA1UdDgQWBBTGqQP/SGBzOjWWcDdk/U7bQFhu+DAfBgNVHSMEGDAWgBQ4uufUcLGAmR55HWQWi+6PN9HJcTAKBggqhkjOPQQDAwNpADBmAjEAlZ9TR2TJnhumSUmtmgsWPpcp3xDYUtcXtxHs2xuHU6HqoaBfWDdUJKO8tWljGSVWAjEApesQltBP8ddWIn1BgBpldJ1pq9zukqfwRjwoCH1SRQXyuhGNfovvQMl/lw8MLIyO",
258+
"MIICBzCCAWigAwIBAgIUK9wvDGYJ5S9DKzs/MY+IiTa0CP0wCgYIKoZIzj0EAwQwLDEqMCgGA1UEAwwhU01BUlQgSGVhbHRoIENhcmQgRXhhbXBsZSBSb290IENBMB4XDTIxMDYwMTE1NTAwOVoXDTI2MDUzMTE1NTAwOVowJzElMCMGA1UEAwwcU01BUlQgSGVhbHRoIENhcmQgRXhhbXBsZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABF2eAAAAGv0/isod1xpgaLX0DASxCDs0+JbCt12CTdQhB7os9m9H8c0nLyaNb8lM9IXkBRZLoLly/ZRaRjU8vq3bt6l5m9Cc6OY+xwmADKvNdNm94dsCC5CiB+JQu6WgWKNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUOLrn1HCxgJkeeR1kFovujzfRyXEwHwYDVR0jBBgwFoAUJo6aEvlKNnmPfQaKVkOXIDY87/8wCgYIKoZIzj0EAwQDgYwAMIGIAkIBq9tT76Qzv1wH6nB0/sKPN4xPUScJeDv4+u2Zncv4ySWn5BR3DxYxEdJsVk4Aczw8uBipnYS90XNiogXMmN7JbRQCQgEYLzjOB1BdWIzjBlLF0onqnsAQijr6VX+2tfd94FNgMxHtaU864vgD/b3b0jr/Qf4dUkvF7K9WM1+vbcd0WDP4gQ==",
259+
"MIICMjCCAZOgAwIBAgIUadiyU9sUFV6H40ZB5pCyc+gOikgwCgYIKoZIzj0EAwQwLDEqMCgGA1UEAwwhU01BUlQgSGVhbHRoIENhcmQgRXhhbXBsZSBSb290IENBMB4XDTIxMDYwMTE1NTAwOFoXDTMxMDUzMDE1NTAwOFowLDEqMCgGA1UEAwwhU01BUlQgSGVhbHRoIENhcmQgRXhhbXBsZSBSb290IENBMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB/XU90B0DMB6GKbfNKz6MeEIZ2o6qCX76GGiwhPYZyDLgB4+njRHUA7l7KSrv8THtzXSn8FwDmubAZdbU3lwNRGcAQJVY/9Bq9TY5Utp8ttbVnXcHQ5pumzMgIkkrIzERg+iCZLtjgPYjUMgeLWpqQMG3VBNN6LXN4wM6DiJiZeeBId6jUDBOMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFCaOmhL5SjZ5j30GilZDlyA2PO//MB8GA1UdIwQYMBaAFCaOmhL5SjZ5j30GilZDlyA2PO//MAoGCCqGSM49BAMEA4GMADCBiAJCAe/u808fhGLVpgXyg3h/miSnqxGBx7Gav5Xf3iscdZkF9G5SH1G6UPvIS0tvP/2x9xHh2Vsx82OCZH64uPmKPqmkAkIBcUed8q/dQMgUmsB+jT7A7hKz0rh3CvmhW8b4djD3NesKW3M9qXqpRihd+7KqmTjUxhqUckiPBVLVm5wenaj08Ys="
260+
],
261+
crv: 'P-256',
262+
x: 'PQHApUWm94mflvswQgAnfHlETMwJFqjUVSs7WU6LQy4',
263+
y: '7mj8IO-8V5VZjDbRVsJINC_Rq5ai5CDhFX18ceRsLWQ'
264+
},
265+
credentialType: [ 'https://smarthealth.cards#immunization' ]
266+
});
246267
});
247268
});
248269

@@ -269,19 +290,24 @@ const GENERATED_PUBLIC_KEY = {
269290
"y": "B_VFOyQ0Rpek9nFqNu5anXT43A--m0MYaPfZ4iCR1xI"
270291
}
271292

293+
const CACHED_KEYS = {
294+
"https://pcf.pw": {
295+
keys: [GENERATED_PUBLIC_KEY]
296+
}
297+
}
272298

273299
describe('JWS Crypto w/ New Keys', function() {
274300
it('should sign and verify the package', async function() {
275301
const signed = await sign(await makeJWT(TEST_PAYLOAD, 48, "https://pcf.pw"), GENERATED_PRIVATE_KEY);
276-
const result = await verify(signed, GENERATED_PUBLIC_KEY);
302+
const result = await verify(signed, CACHED_KEYS);
277303
expect(result).to.be.true;
278304
});
279305
});
280306

281307
describe('JWS Crypto w/ New Keys', function() {
282308
it('should sign and verify the package w/ Not Before Date', async function() {
283309
const signed = await sign(await makeJWT(TEST_PAYLOAD, 48, "https://pcf.pw", new Date()), GENERATED_PRIVATE_KEY);
284-
const result = await verify(signed, GENERATED_PUBLIC_KEY);
310+
const result = await verify(signed, CACHED_KEYS);
285311
expect(result).to.be.true;
286312
});
287313
});

0 commit comments

Comments
 (0)