const fs = require('fs'); const zlib = require('zlib'); const Jimp = require('jimp'); const jsQR = require('jsqr'); const base45 = require('base45'); const cbor = require('cbor'); const cose = require('cose-js'); const rs = require('jsrsasign'); const { verify, webcrypto, SignatureMismatchError } = require('cosette/build/sign'); class DCC { static async fromRaw(certificateRaw) { const dcc = new DCC(); dcc._raw = certificateRaw; const base45Data = base45.decode(certificateRaw.slice(4)); dcc._coseRaw = zlib.inflateSync(base45Data); const cborPayload = cbor.decodeFirstSync(dcc._coseRaw).value[2]; const jsonCBOR = cbor.decodeFirstSync(cborPayload); dcc._payload = jsonCBOR.get(-260).get(1); return dcc; } static async fromImage(certificateImagePath) { const buffer = fs.readFileSync(certificateImagePath); const image = await Jimp.read(buffer); const code = jsQR(image.bitmap.data, image.bitmap.width, image.bitmap.height); return DCC.fromRaw(code.data); } get raw() { return this._raw; } get payload() { return this._payload; } async checkSignature(signatureKey) { const verifier = { key: signatureKey, }; return cose.sign.verify(this._coseRaw, verifier); } async checkSignatureWithCertificate(certificate) { const key = rs.KEYUTIL.getKey(certificate); let verifier; if (key.type === 'EC') { verifier = key.getPublicKeyXYHex(); } else if (key.type === 'RSA') { const jwk = rs.KEYUTIL.getJWKFromKey(key); verifier = { n: Buffer.from(jwk.n, 'base64'), e: Buffer.from(jwk.e, 'base64'), }; } else { throw new Error('Certificate not supported'); } return cose.sign.verify(this._coseRaw, { key: verifier }); } /** * * @param keys associative array of keys, [kid => {key}] * @returns {Promise} return a promise, if the certificate is validly signed with * a listed key, return the key with infos about authority that signed it. If the certificate * is not validly signed, return false. If the keys is not in list, throw Error. */ async checkSignatureWithKeysList(keys) { let cert; try { await verify(this._coseRaw, async (kid) => { cert = keys[kid.toString('base64')]; return { key: await webcrypto.subtle.importKey( 'spki', Buffer.from(cert.publicKeyPem, 'base64'), cert.publicKeyAlgorithm, true, ['verify'], ), }; }); return cert; } catch (e) { if (e instanceof SignatureMismatchError) { return false; } if (typeof cert === 'undefined') { throw new Error('Cannot verify signature: the key that signed the certificate is not listed', { cause: e }); } throw e; } } } module.exports = DCC;