UNPKG

covid-certificate-checker

Version:

Web-Library for Reading and Verifying Covid Vaccination Certificates

124 lines 11.2 kB
import { __awaiter } from "tslib"; import * as b45 from "base45-ts"; import * as pako from 'pako'; import * as cbor from 'cbor-web'; import * as x509 from "@peculiar/x509"; import { toBase64String } from "./utils"; import { CertMapperUtil } from "./CertMapper.util"; //See: https://github.com/ehn-dcc-development/ehn-sign-verify-javascript-trivial/blob/main/cose_verify.js export default class ElectronicHealthCertificateChecker { static decode(certificate) { try { const decodedCertificate = this.decodeCBOR(this.inflateZlib(this.decodeBase45(this.removeHC1Header(certificate)))); return decodedCertificate.hcertCertClaim; } catch (e) { console.error("Error has happened at decoding Certificate", e); throw e; } } static verify(certificate, dscKey) { return __awaiter(this, void 0, void 0, function* () { try { return ElectronicHealthCertificateChecker.verifyCertificate(certificate, dscKey); } catch (e) { console.error(e); throw e; } }); } static verifyWithTrustList(certificate, trustList) { return __awaiter(this, void 0, void 0, function* () { try { return ElectronicHealthCertificateChecker.verifyCertificate(certificate, undefined, trustList); } catch (e) { console.error(e); throw e; } }); } static verifyCertificate(certificate, publicKey, trustList) { return __awaiter(this, void 0, void 0, function* () { if (!trustList && !publicKey) { throw new DOMException("Please submit either a Public-Key or a Trust-List for verifying!"); } if (!this.supportsWebCryptoApi()) { throw new DOMException("Your Browser doesn't support WebCrypto-Api. Please provide a Polyfill!"); } const decodedCertificate = this.decodeCBOR(this.inflateZlib(this.decodeBase45(this.removeHC1Header(certificate)))); let filteredCertificates = []; if (trustList && trustList.certificates) { filteredCertificates = trustList.certificates.filter(certificate => certificate.kid == decodedCertificate.kid); if (filteredCertificates.length < 1) { console.error("No Certificate in Trust-List with specified kid: " + decodedCertificate.kid); } } const key = filteredCertificates.length > 0 ? filteredCertificates[0].rawData : publicKey; if (!key) { return { healthCertificateClaim: decodedCertificate.hcertCertClaim, isVerified: false, }; } const isVerified = yield this.verifyCoseMessage(decodedCertificate.coseMessage, key); return { healthCertificateClaim: decodedCertificate.hcertCertClaim, isVerified }; }); } static removeHC1Header(certificate) { if (certificate.startsWith("HC1:")) { return certificate.substring(4); } console.warn("No HC1:-Prefix"); return certificate; } static decodeBase45(encodedMessage) { return b45.decode(encodedMessage); } static inflateZlib(compressedString) { return pako.inflate(compressedString); } static decodeCBOR(cborByteArray) { const data = cbor.decode(cborByteArray); const [protectHeader, unprotectHeader, payload, sig] = data.value; const coseMessage = { protectHeader, unprotectHeader, payload, sig, }; const decodedHeaders = cbor.decode(protectHeader); let kidUintArray; if (decodedHeaders && decodedHeaders.has(4)) { kidUintArray = decodedHeaders.get(4); } else if (unprotectHeader && unprotectHeader.has(4)) { kidUintArray = unprotectHeader.get(4); } const kid = toBase64String(kidUintArray); const decodedPaylod = cbor.decode(payload); const hcertCertClaim = CertMapperUtil.payloadToHCERTClaim(decodedPaylod); return { coseMessage, hcertCertClaim, kid }; } static supportsWebCryptoApi() { return !!crypto && 'subtle' in crypto && 'importKey' in crypto.subtle && 'verify' in crypto.subtle; } static verifyCoseMessage(coseMessage, publicKey) { return __awaiter(this, void 0, void 0, function* () { const cert = new x509.X509Certificate(publicKey); const rawData = cert.publicKey.rawData; //See: https://github.com/btielen/covid-certificate/blob/7f8631a2e0bdc5debf5c9877858d71ffd8ade329/src/cose/ECDS256SignatureVerifier.ts#L6 const pk = yield crypto.subtle.importKey("spki", rawData, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]); const verified = yield crypto.subtle.verify({ name: "ECDSA", hash: "sha-256" }, pk, coseMessage.sig, cbor.encode(["Signature1", coseMessage.protectHeader, (new Uint8Array(0)).buffer, coseMessage.payload])); return verified; }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlY3Ryb25pY0hlYWx0aENlcnRpZmljYXRlQ2hlY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvRWxlY3Ryb25pY0hlYWx0aENlcnRpZmljYXRlQ2hlY2tlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLLEdBQUcsTUFBTSxXQUFXLENBQUM7QUFDakMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLElBQUksTUFBTSxVQUFVLENBQUM7QUFDakMsT0FBTyxLQUFLLElBQUksTUFBTSxnQkFBZ0IsQ0FBQztBQUN2QyxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sU0FBUyxDQUFDO0FBQ3ZDLE9BQU8sRUFBQyxjQUFjLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQU1qRCx5R0FBeUc7QUFDekcsTUFBTSxDQUFDLE9BQU8sT0FBTyxrQ0FBa0M7SUFFbkQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFtQjtRQUM3QixJQUFJO1lBQ0EsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ25ILE9BQU8sa0JBQWtCLENBQUMsY0FBYyxDQUFDO1NBQzVDO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDUixPQUFPLENBQUMsS0FBSyxDQUFDLDRDQUE0QyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQy9ELE1BQU0sQ0FBQyxDQUFDO1NBQ1g7SUFDTCxDQUFDO0lBRUQsTUFBTSxDQUFPLE1BQU0sQ0FDZixXQUFtQixFQUNuQixNQUFjOztZQU1kLElBQUk7Z0JBQ0EsT0FBTyxrQ0FBa0MsQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDLENBQUM7YUFDcEY7WUFBQyxPQUFPLENBQUMsRUFB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