diff --git a/.npmignore b/.npmignore
index 2da1930..0a0671f 100644
--- a/.npmignore
+++ b/.npmignore
@@ -11,7 +11,12 @@ coverage
.gitlab-ci.yml
package-lock.json
+build-and-publish
+build.ts
+jest.config.js
/*.tgz
/tmp*
/mnt/
/package/
+/src/
+/patches/
diff --git a/README.md b/README.md
index c68464b..d324056 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-# NZCP.js
+# NZCP.js   
A JavaScript implementation of [NZ COVID Pass](https://github.com/minhealthnz/nzcovidpass-spec) verification, New Zealand's proof of COVID-19 vaccination solution, written in TypeScript. All contributions welcome 🥳
We also have a [Rust implementation](https://github.com/vaxxnz/nzcp-rust/) available.
-> This library can be used for both Client and Server-side implementations.
+> This library can be used for both in browser and Node.js.
## Install
diff --git a/browser-test.html b/browser-test.html
deleted file mode 100644
index b9f0a0b..0000000
--- a/browser-test.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
- Open the devtools 👇
-
-
diff --git a/publish b/build-and-publish
similarity index 91%
rename from publish
rename to build-and-publish
index 8c527f9..56a5559 100755
--- a/publish
+++ b/build-and-publish
@@ -1,5 +1,6 @@
#!/bin/bash
+yarn build-all
echo '@vaxxnz:registry=https://registry.npmjs.org' > .npmrc
npm publish
echo '@vaxxnz:registry=https://npm.pkg.github.com' > .npmrc
diff --git a/build.ts b/build.ts
index 8213109..b3ec163 100644
--- a/build.ts
+++ b/build.ts
@@ -12,7 +12,7 @@ async function main() {
format: "cjs",
target: "es6"
})
- console.log(resultBrowser)
+ console.log('resultBrowser', resultBrowser)
const resultNode = await build({
entryPoints: ['src/node.ts'],
@@ -24,6 +24,6 @@ async function main() {
format: "cjs",
target: "es6"
})
- console.log(resultNode)
+ console.log('resultNode', resultNode)
}
main()
diff --git a/package.json b/package.json
index a7c8cb3..f275316 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"test-watch": "jest --watch",
"clean": "rm -rf dist build package",
"docs": "typedoc --entryPoints src/main.ts",
- "build-all": "yarn clean && patch-package && tsc -p tsconfig.json && yarn ts-node build.ts"
+ "build-all": "yarn clean && patch-package && tsc --emitDeclarationOnly -p tsconfig.json && yarn ts-node build.ts",
+ "build-and-publish": "./build-and-publish"
},
"devDependencies": {
"@types/elliptic": "^6.4.14",
diff --git a/patches/cbor+6.0.1.patch b/patches/cbor+6.0.1.patch
index d9c8d98..e4b8a3a 100644
--- a/patches/cbor+6.0.1.patch
+++ b/patches/cbor+6.0.1.patch
@@ -1,13 +1,16 @@
diff --git a/node_modules/cbor/lib/constants.js b/node_modules/cbor/lib/constants.js
-index 932e1c5..9b4502c 100644
+index 932e1c5..9ca7538 100644
--- a/node_modules/cbor/lib/constants.js
+++ b/node_modules/cbor/lib/constants.js
-@@ -2,7 +2,7 @@
+@@ -2,7 +2,10 @@
// Let's get consistent first, then we can think about feature testing
// for BigNumber support
-const {BigNumber} = require('bignumber.js')
-+const {BigNumber} = require('bignumber.js/bignumber.mjs')
++const isNode = typeof process !== 'undefined'
++ && process.versions != null
++ && process.versions.node != null
++const {BigNumber} = isNode ? require('bignumber.js') : require('bignumber.js/bignumber.mjs')
exports.BigNumber = BigNumber
diff --git a/src/base32.test.ts b/src/base32.test.ts
new file mode 100644
index 0000000..197ddda
--- /dev/null
+++ b/src/base32.test.ts
@@ -0,0 +1,11 @@
+import { base32 } from "rfc4648";
+
+test("Base32 library works", async () => {
+ const base32Example =
+ "2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAYFE6VGU4MCDGK7DHLLYWHVPUS2YIDJOA6Y524TD3AZRM263WTY2BE4DPKIF27WKF3UDNNVSVWRDYIYVJ65IRJJJ6Z25M2DO4YZLBHWFQGVQR5ZLIWEQJOZTS3IQ7JTNCFDX";
+
+ const res = base32.parse(base32Example);
+
+ const base32String = base32.stringify(res);
+ expect(base32String).toBe(base32Example);
+});
diff --git a/src/browser.ts b/src/browser.ts
index 6d31900..cbc2700 100644
--- a/src/browser.ts
+++ b/src/browser.ts
@@ -2,5 +2,5 @@
* This file is the entrypoint of browser builds.
* The code executes when loaded in a browser.
*/
-import { verifyPassURI, verifyPassURIWithTrustedIssuers } from './main'
-export { verifyPassURI, verifyPassURIWithTrustedIssuers }
+import { verifyPassURI, verifyPassURIWithTrustedIssuers } from "./main";
+export { verifyPassURI, verifyPassURIWithTrustedIssuers };
diff --git a/src/cbor.test.ts b/src/cbor.test.ts
new file mode 100644
index 0000000..834a444
--- /dev/null
+++ b/src/cbor.test.ts
@@ -0,0 +1,14 @@
+import { base32 } from "rfc4648";
+import cbor from "cbor";
+
+test("CBOR library works", async () => {
+ const res = base32.parse(
+ "2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAYFE6VGU4MCDGK7DHLLYWHVPUS2YIDJOA6Y524TD3AZRM263WTY2BE4DPKIF27WKF3UDNNVSVWRDYIYVJ65IRJJJ6Z25M2DO4YZLBHWFQGVQR5ZLIWEQJOZTS3IQ7JTNCFDX"
+ );
+
+ const cborobj = cbor.decode(res);
+
+ expect(cborobj.err).toEqual(undefined);
+ expect(cborobj.tag).toEqual(18);
+ expect(cborobj.value.length).toEqual(4);
+});
diff --git a/src/coseTypes.ts b/src/coseTypes.ts
index a0c03a9..3cd09a0 100644
--- a/src/coseTypes.ts
+++ b/src/coseTypes.ts
@@ -1,8 +1,7 @@
-
-type DecodedCOSEValue = (Buffer | Record)[]
+type DecodedCOSEValue = (Buffer | Record)[];
export interface DecodedCOSEStructure {
- tag: number,
- value: DecodedCOSEValue,
- err?: Error
+ tag: number;
+ value: DecodedCOSEValue;
+ err?: Error;
}
diff --git a/src/crypto.ts b/src/crypto.ts
index 62b4e75..eef325c 100644
--- a/src/crypto.ts
+++ b/src/crypto.ts
@@ -33,7 +33,7 @@ export function validateCOSESignature(
// ]
const SigStructure = ["Signature1", protected_, Buffer.alloc(0), payload_];
const ToBeSigned = cbor.encode(SigStructure);
- const messageHash = sha256.digest(ToBeSigned)
+ const messageHash = sha256.digest(ToBeSigned);
const signature = {
r: signature_.slice(0, signature_.length / 2),
s: signature_.slice(signature_.length / 2),
diff --git a/src/cwt.ts b/src/cwt.ts
index 6fedf78..f641428 100644
--- a/src/cwt.ts
+++ b/src/cwt.ts
@@ -9,7 +9,6 @@ import { CWTClaimsResult } from "./generalTypes";
import { decodeCtiToJti } from "./jtiCti";
import { currentTimestamp } from "./util";
-
export function parseCWTClaims(
rawCWTClaims: RawCWTClaims
): UnvalidatedCWTClaims {
@@ -110,7 +109,8 @@ export function validateCWTClaims(
return {
success: false,
violates: {
- message: "Not Before claim MUST be present and MUST be a timestamp encoded as an integer in the NumericDate format (as specified in [RFC8392] section 2)",
+ message:
+ "Not Before claim MUST be present and MUST be a timestamp encoded as an integer in the NumericDate format (as specified in [RFC8392] section 2)",
section: "2.1.0.3.1",
link: "https://nzcp.covid19.health.nz/#cwt-claims",
},
@@ -126,7 +126,8 @@ export function validateCWTClaims(
return {
success: false,
violates: {
- message: "Not Before claim MUST be present and MUST be a timestamp encoded as an integer in the NumericDate format (as specified in [RFC8392] section 2)",
+ message:
+ "Not Before claim MUST be present and MUST be a timestamp encoded as an integer in the NumericDate format (as specified in [RFC8392] section 2)",
section: "2.1.0.4.1",
link: "https://nzcp.covid19.health.nz/#cwt-claims",
},
@@ -165,7 +166,6 @@ export function validateCWTClaims(
};
}
-
// Section 2.1.0.5.3
// The vc claim is currrently unregistered and therefore MUST be encoded as a Major Type 3 string as defined by [RFC7049].
if (cwtClaims.vc) {
@@ -292,7 +292,13 @@ export function validateCWTClaims(
return {
success: true,
- cwtClaims: { jti: cwtClaims.jti, iss: cwtClaims.iss, nbf: cwtClaims.nbf, exp: cwtClaims.exp, vc: cwtClaims.vc },
+ cwtClaims: {
+ jti: cwtClaims.jti,
+ iss: cwtClaims.iss,
+ nbf: cwtClaims.nbf,
+ exp: cwtClaims.exp,
+ vc: cwtClaims.vc,
+ },
violates: null,
};
}
diff --git a/src/cwtTypes.ts b/src/cwtTypes.ts
index 8e41457..0faca0f 100644
--- a/src/cwtTypes.ts
+++ b/src/cwtTypes.ts
@@ -5,7 +5,7 @@ export interface CredentialSubject {
}
export interface VC {
- '@context': string[];
+ "@context": string[];
version: string;
type: string[];
credentialSubject: CredentialSubject;
@@ -19,17 +19,18 @@ export interface CWTClaims {
jti: string;
}
-export type UnvalidatedCWTClaims = Partial
-
-
+export type UnvalidatedCWTClaims = Partial;
export type RawCWTHeaders = Map;
-export type RawCWTClaims = Map;
+export type RawCWTClaims = Map<
+ number | string,
+ string | number | Buffer | unknown
+>;
interface CWTHeaders {
- kid: string
- alg: string
+ kid: string;
+ alg: string;
}
-export type UnvalidatedCWTHeaders = Partial
+export type UnvalidatedCWTHeaders = Partial;
diff --git a/src/did.test.ts b/src/did.test.ts
new file mode 100644
index 0000000..3ff662e
--- /dev/null
+++ b/src/did.test.ts
@@ -0,0 +1,17 @@
+import { Resolver } from "did-resolver";
+import { getResolver } from "web-did-resolver";
+
+const webResolver = getResolver();
+
+const didResolver = new Resolver({
+ ...webResolver,
+});
+
+test("CBOR library works", async () => {
+ const doc = await didResolver.resolve("did:web:nzcp.covid19.health.nz");
+ expect(doc).toBeTruthy();
+ expect(doc.didDocument).toBeTruthy();
+ expect(doc.didDocument?.id).toBe("did:web:nzcp.covid19.health.nz");
+ expect(doc.didDocumentMetadata).toBeTruthy();
+ expect(doc.didResolutionMetadata).toBeTruthy();
+});
diff --git a/src/elliptic.test.ts b/src/elliptic.test.ts
new file mode 100644
index 0000000..dada568
--- /dev/null
+++ b/src/elliptic.test.ts
@@ -0,0 +1,28 @@
+import elliptic from "elliptic";
+
+const fromHexString = (hexString: string) =>
+ new Uint8Array(
+ (hexString.match(/.{1,2}/g) ?? []).map((byte) => parseInt(byte, 16))
+ );
+
+test("elliptic library works", async () => {
+ const publicKeyHex =
+ "04cd147e5c6b02a75d95bdb82e8b80c3e8ee9caa685f3ee5cc862d4ec4f97cefad22fe5253a16e5be4d1621e7f18eac995c57f82917f1a9150842383f0b4a4dd3d";
+ const messageHashHex =
+ "0513bb48e77bcfa51209a78d3224b0b2f1a29a9b9c0eff2263b6d08156aee72a";
+ const signatureRHex =
+ "f6a9a841a390a40bd5cee4434cccdb7499d9461840f5c8dff436cba0698b1ab2";
+ const signatureSHex =
+ "4dca052720b9f581200bebac2fff1afa159ce42aeb38d558df9413899db48271";
+
+ const signature = {
+ r: fromHexString(signatureRHex),
+ s: fromHexString(signatureSHex),
+ };
+
+ const EC = elliptic.ec;
+ const ec = new EC("p256");
+ const key = ec.keyFromPublic(publicKeyHex, "hex");
+ const result = key.verify(fromHexString(messageHashHex), signature);
+ expect(result).toBe(true);
+});
diff --git a/src/generalTypes.ts b/src/generalTypes.ts
index 8041791..8f437b5 100644
--- a/src/generalTypes.ts
+++ b/src/generalTypes.ts
@@ -7,8 +7,8 @@ export interface Violates {
}
export type VerificationResult =
- | { success: true; violates: null, credentialSubject: CredentialSubject }
- | { success: false; violates: Violates, credentialSubject: null };
+ | { success: true; violates: null; credentialSubject: CredentialSubject }
+ | { success: false; violates: Violates; credentialSubject: null };
export type CWTClaimsResult =
| { success: true; violates: null; cwtClaims: CWTClaims }
diff --git a/src/main.test.ts b/src/main.test.ts
index 53198b9..e430925 100644
--- a/src/main.test.ts
+++ b/src/main.test.ts
@@ -2,13 +2,16 @@ import { verifyPassURIWithTrustedIssuers } from "./main";
// This is the list of trusted issuers which work with the examples specified in v1 of NZ COVID Pass - Technical Specification
// https://nzcp.covid19.health.nz/
-const nzcpExamplesTrustedIssuers = ["did:web:nzcp.covid19.health.nz"]
+const nzcpExamplesTrustedIssuers = ["did:web:nzcp.covid19.health.nz"];
// https://nzcp.covid19.health.nz/#valid-worked-example
const validPass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAYFE6VGU4MCDGK7DHLLYWHVPUS2YIDJOA6Y524TD3AZRM263WTY2BE4DPKIF27WKF3UDNNVSVWRDYIYVJ65IRJJJ6Z25M2DO4YZLBHWFQGVQR5ZLIWEQJOZTS3IQ7JTNCFDX";
test("Valid pass is successful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(validPass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ validPass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(true);
expect(result.credentialSubject?.givenName).toBe("Jack");
expect(result.credentialSubject?.familyName).toBe("Sparrow");
@@ -19,7 +22,10 @@ test("Valid pass is successful", async () => {
const badPublicKeyPass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAY73U6TCQ3KF5KFML5LRCS5D3PCYIB2D3EOIIZRPXPUA2OR3NIYCBMGYRZUMBNBDMIA5BUOZKVOMSVFS246AMU7ADZXWBYP7N4QSKNQ4TETIF4VIRGLHOXWYMR4HGQ7KYHHU";
test("Bad Public Key pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(badPublicKeyPass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ badPublicKeyPass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("3");
});
@@ -28,7 +34,10 @@ test("Bad Public Key pass is unsuccessful", async () => {
const publicKeyNotFoundPass =
"NZCP:/1/2KCEVIQEIVVWK6JNGIASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVBMP3LEDMB4CLBS2I7IOYJZW46U2YIBCSOFZMQADVQGM3JKJBLCY7ATASDTUYWIP4RX3SH3IFBJ3QWPQ7FJE6RNT5MU3JHCCGKJISOLIMY3OWH5H5JFUEZKBF27OMB37H5AHF";
test("Public Key Not Found pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(publicKeyNotFoundPass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ publicKeyNotFoundPass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("5.1.1");
});
@@ -37,7 +46,10 @@ test("Public Key Not Found pass is unsuccessful", async () => {
const modifiedSignaturePass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAYFE6VGU4MCDGK7DHLLYWHVPUS2YIAAAAAAAAAAAAAAAAC63WTY2BE4DPKIF27WKF3UDNNVSVWRDYIYVJ65IRJJJ6Z25M2DO4YZLBHWFQGVQR5ZLIWEQJOZTS3IQ7JTNCFDX";
test("Modified Signature pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(modifiedSignaturePass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ modifiedSignaturePass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("3");
});
@@ -46,7 +58,10 @@ test("Modified Signature pass is unsuccessful", async () => {
const modifiedPayloadPass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEOKKALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWKU3UMV3GK2TGMFWWS3DZJZQW2ZLDIRXWKY3EN5RGUMJZGYYC2MBUFUYTMB2QMCSPKTKOGBBTFPRTVV4LD2X2JNMEAAAAAAAAAAAAAAAABPN3J4NASOBXVEC5P3FC52BWW2ZK3IR4EMKU7OUIUUU7M5OWNBXOMMVQT3CYDKYI64VULCIEXMZZNUIPUZWRCR3Q";
test("Modified Payload pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(modifiedPayloadPass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ modifiedPayloadPass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("3");
});
@@ -55,7 +70,10 @@ test("Modified Payload pass is unsuccessful", async () => {
const expiredPass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUX5AM2FQIGTBPBPYWYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVA56TNJCCUN2NVK5NGAYOZ6VIWACYIBM3QXW7SLCMD2WTJ3GSEI5JH7RXAEURGATOHAHXC2O6BEJKBSVI25ICTBR5SFYUDSVLB2F6SJ63LWJ6Z3FWNHOXF6A2QLJNUFRQNTRU";
test("Expired Pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(expiredPass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ expiredPass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("2.1.0.4.3");
});
@@ -64,17 +82,22 @@ test("Expired Pass is unsuccessful", async () => {
const notActivePass =
"NZCP:/1/2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRU2XI5UFQIGTMZIQIWYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVA27NR3GFF4CCGWF66QGMJSJIF3KYID3KTKCBUOIKIC6VZ3SEGTGM3N2JTWKGDBAPLSG76Q3MXIDJRMNLETOKAUTSBOPVQEQAX25MF77RV6QVTTSCV2ZY2VMN7FATRGO3JATR";
test("Not Active pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(notActivePass, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ notActivePass,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("2.1.0.3.3");
});
-
// Custom Test: non base-32 string in the payload
-const notBase32 = "NZCP:/1/asdfghasSDFGHFDSADFGHFDSADFGHGFSDADFGBHFSADFGHFDSFGHFDDS0123456789"
+const notBase32 =
+ "NZCP:/1/asdfghasSDFGHFDSADFGHFDSADFGHGFSDADFGBHFSADFGHFDSFGHFDDS0123456789";
test("Non base-32 string in the payload Pass is unsuccessful", async () => {
- const result = await verifyPassURIWithTrustedIssuers(notBase32, nzcpExamplesTrustedIssuers);
+ const result = await verifyPassURIWithTrustedIssuers(
+ notBase32,
+ nzcpExamplesTrustedIssuers
+ );
expect(result.success).toBe(false);
expect(result.violates?.section).toBe("4.7");
});
-
diff --git a/src/main.ts b/src/main.ts
index b8c9dbe..fceb171 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -13,13 +13,15 @@ import { VerificationResult } from "./generalTypes";
// The following is a list of trusted issuer identifiers for New Zealand Covid Passes.
const nzcpTrustedIssuers = ["did:web:nzcp.identity.health.nz"];
-export const verifyPassURI = async (payload: string): Promise => {
- return verifyPassURIWithTrustedIssuers(payload, nzcpTrustedIssuers);
-}
+export const verifyPassURI = async (
+ uri: string
+): Promise => {
+ return verifyPassURIWithTrustedIssuers(uri, nzcpTrustedIssuers);
+};
// TODO: add tests for every error path
export const verifyPassURIWithTrustedIssuers = async (
- payload: string,
+ uri: string,
trustedIssuers: string[]
): Promise => {
// Section 4: 2D Barcode Encoding
@@ -29,7 +31,7 @@ export const verifyPassURIWithTrustedIssuers = async (
// Section 4.4
// Parse the form of QR Code payload
const payloadRegex = /(NZCP:\/)(\d+)\/([A-Za-z2-7=]+)/;
- const payloadMatch = payload.match(payloadRegex);
+ const payloadMatch = uri.match(payloadRegex);
if (!payloadMatch) {
return {
success: false,
@@ -116,7 +118,7 @@ export const verifyPassURIWithTrustedIssuers = async (
// a501781e6469643a7765623a6e7a63702e636f76696431392e6865616c74682e6e7a051a61819a0a041a7450400a627663a46840636f6e7465787482782668747470733a2f2f7777772e77332e6f72672f323031382f63726564656e7469616c732f7631782a68747470733a2f2f6e7a63702e636f76696431392e6865616c74682e6e7a2f636f6e74657874732f76316776657273696f6e65312e302e306474797065827456657269666961626c6543726564656e7469616c6f5075626c6963436f766964506173737163726564656e7469616c5375626a656374a369676976656e4e616d65644a61636b6a66616d696c794e616d656753706172726f7763646f626a313936302d30342d3136075060a4f54d4e304332be33ad78b1eafa4b -- [2], a501781e6469643a7765623a6e7a63702e636f76696431392e6865616c74682e6e7a051a61819a0a041a7450400a627663a46840636f6e7465787482782668747470733a2f2f7777772e77332e6f72672f323031382f63726564656e7469616c732f7631782a68747470733a2f2f6e7a63702e636f76696431392e6865616c74682e6e7a2f636f6e74657874732f76316776657273696f6e65312e302e306474797065827456657269666961626c6543726564656e7469616c6f5075626c6963436f766964506173737163726564656e7469616c5375626a656374a369676976656e4e616d65644a61636b6a66616d696c794e616d656753706172726f7763646f626a313936302d30342d3136075060a4f54d4e304332be33ad78b1eafa4b
// 58 -- Bytes, length next 1 byte
// 40 -- Bytes, length: 64
- const decodedCOSEStructure = cbor.decode(uint8array)
+ const decodedCOSEStructure = cbor.decode(uint8array);
// Decoding the byte string present in the first element of the Decoded COSE structure, as a CBOR structure and rendering it via the expanded form yields the following.
// Let this result be known as the Decoded CWT protected headers.
diff --git a/src/node.ts b/src/node.ts
index 132ace0..190d786 100644
--- a/src/node.ts
+++ b/src/node.ts
@@ -2,5 +2,5 @@
* This file is the entrypoint of node builds.
* The code executes when loaded in a node.
*/
-import { verifyPassURI, verifyPassURIWithTrustedIssuers } from './main'
-export { verifyPassURI, verifyPassURIWithTrustedIssuers }
+import { verifyPassURI, verifyPassURIWithTrustedIssuers } from "./main";
+export { verifyPassURI, verifyPassURIWithTrustedIssuers };
diff --git a/src/util.ts b/src/util.ts
index 73f8e7b..7d09459 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,4 +1,3 @@
-
// convert js timestamp to unix timestamp
export function currentTimestamp(): number {
return Date.now() / 1000;
@@ -7,8 +6,8 @@ export function currentTimestamp(): number {
// from https://nzcp.covid19.health.nz/#adding-base32-padding
export function addBase32Padding(base32InputNoPadding: string): string {
let result = base32InputNoPadding;
- while ((result.length % 8) !== 0) {
- result += '='
+ while (result.length % 8 !== 0) {
+ result += "=";
}
return result;
}