Skip to content

Commit e67859a

Browse files
committed
New Error Handling
1 parent 6ec53ff commit e67859a

File tree

2 files changed

+87
-59
lines changed

2 files changed

+87
-59
lines changed

lib/shc.js

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ import { resolveKey, addCachedKeys } from './resolver';
55

66
const URI_SCHEMA = 'shc';
77

8+
const NOT_SUPPORTED = "not_supported"; // QR Standard not supported by this algorithm
9+
const INVALID_ENCODING = "invalid_encoding"; // could not decode Base45 for DCC, Base10 for SHC
10+
const INVALID_COMPRESSION = "invalid_compression"; // could not decompress the byte array
11+
const INVALID_SIGNING_FORMAT = "invalid_signing_format";// invalid COSE, JOSE, W3C VC Payload
12+
const KID_NOT_INCLUDED = "kid_not_included"; // unable to resolve the issuer ID
13+
const ISSUER_NOT_TRUSTED = "issuer_not_trusted"; // issuer is not found in the registry
14+
const TERMINATED_KEYS = "terminated_keys"; // issuer was terminated by the registry
15+
const EXPIRED_KEYS = "expired_keys"; // keys expired
16+
const REVOKED_KEYS = "revoked_keys"; // keys were revoked by the issuer
17+
const INVALID_SIGNATURE = "invalid_signature"; // signature doesn't match
18+
const VERIFIED = "verified"; // Verified content.
19+
820
/*
921
* I am not sure if I should build this by hand.
1022
*/
@@ -34,60 +46,71 @@ export async function sign(payload, privateKey) {
3446
const fields = { zip: 'DEF' };
3547
const body = pako.deflateRaw(bodyString);
3648

37-
const signed = await JWS.createSign({ format: 'compact', fields }, signingKey)
49+
return await JWS.createSign({ format: 'compact', fields }, signingKey)
3850
.update(Buffer.from(body))
3951
.final();
40-
41-
return signed;
4252
}
4353

44-
async function jwsParts(token) {
54+
function jwsParse(token) {
4555
let [headerEnc, bodyEnc, signatureEnc] = token.split(".");
46-
const header = JSON.parse(base64url.decode(headerEnc));
56+
const headerRaw = base64url.decode(headerEnc);
4757
const signature = base64url.decode(signatureEnc);
48-
let bodyRaw = base64url.toBuffer(bodyEnc);
49-
if (header.zip == 'DEF') {
50-
bodyRaw = Buffer.from(pako.inflateRaw(bodyRaw));
51-
}
52-
let body = JSON.parse(bodyRaw.toString());
53-
return { header, body, signature };
58+
const bodyRaw = base64url.toBuffer(bodyEnc);
59+
return { headerRaw, bodyRaw, signature };
5460
}
5561

56-
async function getVerificationKeyForJws(jws) {
57-
let parsedJwsUnsafe = await jwsParts(jws);
58-
let kid = parsedJwsUnsafe.header.kid;
59-
let iss = parsedJwsUnsafe.body.iss;
60-
return await resolveKey(iss, kid);
62+
function jwsInflate(jwsRaw) {
63+
jwsRaw.header = JSON.parse(jwsRaw.headerRaw)
64+
if (jwsRaw.header.zip == 'DEF') {
65+
jwsRaw.body = JSON.parse(Buffer.from(pako.inflateRaw(jwsRaw.bodyRaw)).toString());
66+
}
67+
return jwsRaw;
6168
}
6269

63-
async function verifyAndReturnPayload(jws, publicKeyArray) {
70+
export async function verify(jws, publicKeyArray) {
6471
if (publicKeyArray) {
6572
addCachedKeys(publicKeyArray);
6673
}
74+
75+
let rawPayload;
76+
try {
77+
rawPayload = jwsParse(jws);
78+
} catch (err) {
79+
console.log(err);
80+
return { status: INVALID_SIGNING_FORMAT}
81+
}
6782

68-
let issuer = await getVerificationKeyForJws(jws);
83+
try {
84+
rawPayload = jwsInflate(rawPayload);
85+
} catch (err) {
86+
console.log(err);
87+
return { status: INVALID_COMPRESSION, raw: rawPayload }
88+
}
6989

70-
const verified = await JWS.createVerify(await JWK.asKey(issuer.didDocument)).verify(jws)
90+
if (!rawPayload.body.iss || !rawPayload.header.kid) return { status: KID_NOT_INCLUDED, contents: rawPayload.body, raw: rawPayload }
7191

72-
let body = verified.payload;
73-
if ((verified.header).zip === 'DEF') {
74-
body = Buffer.from(pako.inflateRaw(body));
75-
}
92+
let issuer = await resolveKey(rawPayload.body.iss, rawPayload.header.kid);
7693

77-
return {
78-
credential: JSON.parse(body.toString()),
79-
issuer: issuer,
80-
raw: await jwsParts(jws)
81-
};
82-
}
94+
if (!issuer) return { status: ISSUER_NOT_TRUSTED, contents: rawPayload.body, raw: rawPayload }
95+
96+
switch (issuer.status) {
97+
case "revoked": return { status: REVOKED_KEYS, contents: rawPayload.body, issuer: issuer, raw: rawPayload }
98+
case "terminated": return { status: TERMINATED_KEYS, contents: rawPayload.body, issuer: issuer, raw: rawPayload }
99+
case "expired": return { status: EXPIRED_KEYS, contents: rawPayload.body, issuer: issuer, raw: rawPayload }
100+
}
83101

84-
export async function verify(jws, pubkeyKey) {
85102
try {
86-
await verifyAndReturnPayload(jws, pubkeyKey);
87-
return true;
103+
const verified = await JWS.createVerify(await JWK.asKey(issuer.didDocument)).verify(jws)
104+
105+
let body = verified.payload;
106+
if ((verified.header).zip === 'DEF') {
107+
body = Buffer.from(pako.inflateRaw(body));
108+
}
109+
110+
return { status: VERIFIED, contents: JSON.parse(body.toString()), issuer: issuer, raw: rawPayload };
88111
} catch (err) {
89112
console.log(err);
90-
return false;
113+
return { status: INVALID_SIGNATURE, contents: rawPayload.body, issuer: issuer, raw: rawPayload }
91114
}
92115
}
93116

@@ -122,24 +145,31 @@ export async function unpack(uri) {
122145
}
123146
}
124147

125-
return await fromBase10(data);
148+
try {
149+
return await fromBase10(data);
150+
} catch (err) {
151+
console.log(err);
152+
return undefined
153+
}
126154
}
127155

128156
export async function pack(payload) {
129157
return URI_SCHEMA + ':/' + await toBase10(payload);
130158
}
131159

132160
export async function debug(uri) {
133-
return await jwsParts(await unpack(uri));
161+
return jwsInflate(jwsParse(await unpack(uri)));
134162
}
135163

136164
export async function unpackAndVerify(uri, publicKeyArray) {
137-
try {
138-
return await verifyAndReturnPayload(await unpack(uri), publicKeyArray);
139-
} catch (err) {
140-
console.log(err);
141-
return undefined;
165+
const jws = await unpack(uri);
166+
167+
if (!jws) {
168+
return { status: INVALID_ENCODING, qr: uri };
142169
}
170+
171+
const verified = await verify(jws, publicKeyArray);
172+
return {...verified, qr: uri};
143173
}
144174
export async function signAndPack(payload, publicKeyPem, privateKeyP8) {
145175
return await pack(await sign(payload, publicKeyPem, privateKeyP8));

test/sign-verify.spec.js

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ describe('JWS Crypto', function() {
110110

111111
it('should verify the package', async function() {
112112
const result = await verify(SIGNED_TEST_PAYLOAD);
113-
expect(result).to.be.true;
113+
expect(result.status).to.be.eq("verified");
114114
});
115115

116116
it('should sign and verify the package', async function() {
117117
const signed = await sign(await makeJWT(TEST_PAYLOAD, "https://pcf.pw"), PRIVATE_KEY);
118118
const result = await verify(signed);
119-
expect(result).to.be.true;
119+
expect(result.status).to.be.eq("verified");
120120
});
121121
});
122122

@@ -132,21 +132,23 @@ describe('Sign And Pack, UnpackAndverify', function() {
132132
it('should pack And unpack', async function() {
133133
const packed = await signAndPack(await makeJWT(TEST_PAYLOAD, "https://pcf.pw"), GENERATED_PRIVATE_KEY);
134134
const result = await unpackAndVerify(packed, CACHED_KEYS);
135-
expect(result.credential.vc).to.eql(TEST_PAYLOAD);
136-
expect(result.credential.iss).to.eql("https://pcf.pw");
137-
expect(result.credential.nbf).to.be.undefined;
138-
expect(result.credential.exp).to.be.undefined;
135+
expect(result.status).to.be.eq("verified");
136+
expect(result.contents.vc).to.eql(TEST_PAYLOAD);
137+
expect(result.contents.iss).to.eql("https://pcf.pw");
138+
expect(result.contents.nbf).to.be.undefined;
139+
expect(result.contents.exp).to.be.undefined;
139140
});
140141

141142
it('should pack And unpack with dates', async function() {
142143
let nbf = new Date(2021, 11, 02);
143144
let exp = new Date(2022, 11, 02);
144145
const packed = await signAndPack(await makeJWT(TEST_PAYLOAD, "https://pcf.pw", nbf, exp), GENERATED_PRIVATE_KEY);
145146
const result = await unpackAndVerify(packed, CACHED_KEYS);
146-
expect(result.credential.vc).to.eql(TEST_PAYLOAD);
147-
expect(result.credential.iss).to.eql("https://pcf.pw");
148-
expect(new Date(result.credential.nbf*1000)).to.eql(nbf);
149-
expect(new Date(result.credential.exp*1000)).to.eql(exp);
147+
expect(result.status).to.be.eq("verified");
148+
expect(result.contents.vc).to.eql(TEST_PAYLOAD);
149+
expect(result.contents.iss).to.eql("https://pcf.pw");
150+
expect(new Date(result.contents.nbf*1000)).to.eql(nbf);
151+
expect(new Date(result.contents.exp*1000)).to.eql(exp);
150152
});
151153
});
152154

@@ -248,7 +250,7 @@ const EXAMPLE2_OBJECT = {
248250
describe('Testing SmartCard Examples', function() {
249251
it('should verify example 1', async function() {
250252
const result = await verify(EXAMPLE1_SIGNED);
251-
expect(result).to.be.true;
253+
expect(result.status).to.be.eq("verified");
252254
});
253255
it('should pack example 1', async function() {
254256
const packed = await pack(EXAMPLE1_SIGNED);
@@ -260,7 +262,7 @@ describe('Testing SmartCard Examples', function() {
260262
});
261263
it('should unpack and verify example 2', async function() {
262264
const json = await unpackAndVerify(EXAMPLE2_PACKED);
263-
expect(json.credential).to.eql(EXAMPLE2_OBJECT);
265+
expect(json.contents).to.eql(EXAMPLE2_OBJECT);
264266
expect(json.issuer).to.eql({
265267
displayName: { en: 'Untrusted Issuer: spec.smarthealth.cards/examples/issuer' },
266268
entityType: 'issuer',
@@ -309,23 +311,19 @@ const GENERATED_PUBLIC_KEY = {
309311
}
310312

311313
const CACHED_KEYS = {
312-
"https://pcf.pw": {
313-
keys: [GENERATED_PUBLIC_KEY]
314-
}
314+
"https://pcf.pw": { keys: [GENERATED_PUBLIC_KEY] }
315315
}
316316

317317
describe('JWS Crypto w/ New Keys', function() {
318318
it('should sign and verify the package', async function() {
319319
const signed = await sign(await makeJWT(TEST_PAYLOAD, "https://pcf.pw"), GENERATED_PRIVATE_KEY);
320320
const result = await verify(signed, CACHED_KEYS);
321-
expect(result).to.be.true;
321+
expect(result.status).to.be.eq("verified");
322322
});
323-
});
324323

325-
describe('JWS Crypto w/ New Keys', function() {
326324
it('should sign and verify the package w/ Not Before Date', async function() {
327325
const signed = await sign(await makeJWT(TEST_PAYLOAD, "https://pcf.pw", new Date()), GENERATED_PRIVATE_KEY);
328326
const result = await verify(signed, CACHED_KEYS);
329-
expect(result).to.be.true;
327+
expect(result.status).to.be.eq("verified");
330328
});
331329
});

0 commit comments

Comments
 (0)