diff --git a/package.json b/package.json index 0a8fbb7..9825ae1 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@types/node-fetch": "^2.5.6", "@typescript-eslint/eslint-plugin": "^4.19.0", "@typescript-eslint/parser": "^4.19.0", - "cbor": "^6.0.1", + "cbor": "^8.1.0", "did-resolver": "^3.1.3", "elliptic": "^6.5.4", "esbuild": "^0.11.11", diff --git a/patches/cbor+6.0.1.patch b/patches/cbor+6.0.1.patch deleted file mode 100644 index e4b8a3a..0000000 --- a/patches/cbor+6.0.1.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/node_modules/cbor/lib/constants.js b/node_modules/cbor/lib/constants.js -index 932e1c5..9ca7538 100644 ---- a/node_modules/cbor/lib/constants.js -+++ b/node_modules/cbor/lib/constants.js -@@ -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 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/patches/cbor+8.1.0.patch b/patches/cbor+8.1.0.patch new file mode 100644 index 0000000..1c8b7db --- /dev/null +++ b/patches/cbor+8.1.0.patch @@ -0,0 +1,14 @@ +diff --git a/node_modules/cbor/lib/encoder.js b/node_modules/cbor/lib/encoder.js +index 6327959..288c692 100644 +--- a/node_modules/cbor/lib/encoder.js ++++ b/node_modules/cbor/lib/encoder.js +@@ -509,6 +509,9 @@ Call removeLoopDetectors before resuming.`) + if (typeof f === 'function') { + return f.call(obj, this) + } ++ if (Buffer.isBuffer(obj)) { ++ return this.semanticTypes[Buffer.name].call(obj, this, obj) ++ } + const converter = this.semanticTypes[obj.constructor.name] + if (converter) { + return converter.call(obj, this, obj) diff --git a/src/cbor.test.ts b/src/cbor.test.ts index 834a444..2e6a578 100644 --- a/src/cbor.test.ts +++ b/src/cbor.test.ts @@ -1,14 +1,16 @@ import { base32 } from "rfc4648"; -import cbor from "cbor"; +import { decodeCBOR } from "./cbor"; test("CBOR library works", async () => { const res = base32.parse( "2KCEVIQEIVVWK6JNGEASNICZAEP2KALYDZSGSZB2O5SWEOTOPJRXALTDN53GSZBRHEXGQZLBNR2GQLTOPICRUYMBTIFAIGTUKBAAUYTWMOSGQQDDN5XHIZLYOSBHQJTIOR2HA4Z2F4XXO53XFZ3TGLTPOJTS6MRQGE4C6Y3SMVSGK3TUNFQWY4ZPOYYXQKTIOR2HA4Z2F4XW46TDOAXGG33WNFSDCOJONBSWC3DUNAXG46RPMNXW45DFPB2HGL3WGFTXMZLSONUW63TFGEXDALRQMR2HS4DFQJ2FMZLSNFTGSYLCNRSUG4TFMRSW45DJMFWG6UDVMJWGSY2DN53GSZCQMFZXG4LDOJSWIZLOORUWC3CTOVRGUZLDOSRWSZ3JOZSW4TTBNVSWISTBMNVWUZTBNVUWY6KOMFWWKZ2TOBQXE4TPO5RWI33CNIYTSNRQFUYDILJRGYDVAYFE6VGU4MCDGK7DHLLYWHVPUS2YIDJOA6Y524TD3AZRM263WTY2BE4DPKIF27WKF3UDNNVSVWRDYIYVJ65IRJJJ6Z25M2DO4YZLBHWFQGVQR5ZLIWEQJOZTS3IQ7JTNCFDX" ); - const cborobj = cbor.decode(res); + const cborobj = decodeCBOR(res); expect(cborobj.err).toEqual(undefined); expect(cborobj.tag).toEqual(18); expect(cborobj.value.length).toEqual(4); }); + +// TODO: test for cbor library encodes diff --git a/src/cbor.ts b/src/cbor.ts new file mode 100644 index 0000000..328eb2e --- /dev/null +++ b/src/cbor.ts @@ -0,0 +1,18 @@ +// centralized place where cbor is included, in case we need to patch it + +import util from 'util'; +// @ts-ignore +global.TextDecoder = util.TextDecoder; +// @ts-ignore +global.TextEncoder = util.TextEncoder; + +import cbor from "cbor"; + + +export const encodeCBOR = (obj: any): Buffer => { + return cbor.encode(obj); +} + +export const decodeCBOR = (buf: Buffer | Uint8Array): any => { + return cbor.decode(buf); +} diff --git a/src/crypto.ts b/src/crypto.ts index 50a071c..1e51ae2 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,7 +1,7 @@ -import cbor from "cbor"; import { sha256 } from "js-sha256"; import elliptic from "elliptic"; import { DecodedCOSEStructure } from "./coseTypes"; +import { encodeCBOR } from "./cbor"; const EC = elliptic.ec; const ec = new EC("p256"); @@ -22,7 +22,7 @@ export function validateCOSESignature( const yBuf = Buffer.from(publicKeyJwt.y, "base64"); // 1) '04' + hex string of x + hex string of y - const publicKeyHex = `04${xBuf.toString("hex")}${yBuf.toString("hex")}}`; + const publicKeyHex = `04${xBuf.toString("hex")}${yBuf.toString("hex")}`; const key = ec.keyFromPublic(publicKeyHex, "hex"); // Sig_structure = [ // context : "Signature" / "Signature1" / "CounterSignature", @@ -31,8 +31,12 @@ export function validateCOSESignature( // external_aad : bstr, // payload : bstr // ] - const SigStructure = ["Signature1", Buffer.from(protected_ as Buffer), Buffer.alloc(0), Buffer.from(payload_ as Buffer)]; - const ToBeSigned = cbor.encode(SigStructure); + const bufferProtected_ = Buffer.from(protected_ as Buffer) + const buffer0 = Buffer.alloc(0) + const bufferPayload_ = Buffer.from(payload_ as Buffer) + const SigStructure = ["Signature1", bufferProtected_, buffer0, bufferPayload_]; + + const ToBeSigned = encodeCBOR(SigStructure); const messageHash = sha256.digest(ToBeSigned); const signature = { r: signature_.slice(0, signature_.length / 2), diff --git a/src/main.ts b/src/main.ts index fceb171..94bbdc2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,10 +1,10 @@ import { base32 } from "rfc4648"; -import cbor from "cbor"; import did from "./did"; import { addBase32Padding } from "./util"; import { validateCOSESignature } from "./crypto"; import { parseCWTClaims, parseCWTHeaders, validateCWTClaims } from "./cwt"; import { VerificationResult } from "./generalTypes"; +import { decodeCBOR } from "./cbor"; // The function below implements v1 of NZ COVID Pass - Technical Specification // https://nzcp.covid19.health.nz/ @@ -118,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 = decodeCBOR(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. @@ -128,7 +128,7 @@ export const verifyPassURIWithTrustedIssuers = async ( // 6b65792d31 -- {Val:0}, 6b65792d31 // 01 -- {Key:1}, 1 // 26 -- {Val:1}, -7 - const decodedCWTProtectedHeaders = cbor.decode( + const decodedCWTProtectedHeaders = decodeCBOR( decodedCOSEStructure.value[0] ) as Map; @@ -167,7 +167,7 @@ export const verifyPassURIWithTrustedIssuers = async ( }; } - const rawCWTClaims = cbor.decode(decodedCOSEStructure.value[2]) as Map< + const rawCWTClaims = decodeCBOR(decodedCOSEStructure.value[2]) as Map< number | string, string | number | Buffer | unknown >; diff --git a/yarn.lock b/yarn.lock index baf6266..901ad24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -927,10 +927,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bignumber.js@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" - bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -1125,12 +1121,12 @@ capture-exit@^2.0.0: dependencies: rsvp "^4.8.4" -cbor@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-6.0.1.tgz#f559abb1b986f54fb9cb1a6855085847bcc1cd61" +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== dependencies: - bignumber.js "^9.0.1" - nofilter "^1.0.4" + nofilter "^3.1.0" chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" @@ -3259,9 +3255,10 @@ node-releases@^1.1.76: version "1.1.77" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" -nofilter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== normalize-package-data@^2.5.0: version "2.5.0"