digital-covid-certificate-lib
Version:
A library to parse and verify Digital Covid Certificates (DCC)
528 lines (526 loc) • 24.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DccHcertFactory = void 0;
const Country2LetterISO3166Codes_1 = require("../../data/Country2LetterISO3166Codes");
const DiseaseAgentTargeted_1 = require("../../data/DiseaseAgentTargeted");
const HscCommonRecogintionRat_1 = require("../../data/HscCommonRecogintionRat");
const TestResult_1 = require("../../data/TestResult");
const TestType_1 = require("../../data/TestType");
const VaccineManufacturer_1 = require("../../data/VaccineManufacturer");
const VaccineMedicalProduct_1 = require("../../data/VaccineMedicalProduct");
const VaccineProphylaxis_1 = require("../../data/VaccineProphylaxis");
const ValueSets_1 = require("../../data/ValueSets");
const RecoveryCertificate_1 = require("./RecoveryCertificate");
const TestCertifiacte_1 = require("./TestCertifiacte");
const VaccinationCertifiacte_1 = require("./VaccinationCertifiacte");
const ChPayloadKeys = {
ISSUER: 1,
SUBJECT: 2,
AUDIENCE: 3,
EXPIRATION: 4,
NOT_BEFORE: 5,
ISSUED_AT: 6,
CWT_ID: 7,
HCERT: -260,
LIGHT: -250
};
class DccHcertFactory {
static create(dccCose) {
const version = DccHcertFactory.getValue(dccCose)("ver");
if (version) {
// could later be used to dereference different version, for now we just ignore it
return DccHcertFactory.dereferenceV_1_3_0(dccCose);
}
}
/**
* https://ec.europa.eu/health/sites/default/files/ehealth/docs/covid-certificate_json_specification_en.pdf
* @param dccCose
*/
static dereferenceV_1_3_0(dccCose) {
const getMetadataValue = DccHcertFactory.getMetaDataValue(dccCose);
const getValue = DccHcertFactory.getValue(dccCose);
/**
* MUST match the identifier of the schema version used for
* producing the EUDCC.
* Example:
* "ver": "1.3.0"
*/
const version = getValue("ver");
/**
* Surname(s), such as family name(s), of the holder.
* Exactly 1 (one) non-empty field MUST be provided, with
* all surnames included in it. In case of multiple surnames,
* these MUST be separated by a space. Combination names
* including hyphens or similar characters must however
* stay the same.
* Examples:
* "fn": "Musterfrau-Gößinger"
* "fn": "Musterfrau-Gößinger Müller"
*/
const familyName = getValue("nam")["fn"];
/**
Surname(s) of the holder transliterated using the same
convention as the one used in the holder’s machine
readable travel documents (such as the rules defined in
ICAO Doc 9303 Part 3).
Exactly 1 (one) non-empty field MUST be provided, only
including characters A-Z and <. Maximum length: 80
characters (as per ICAO 9303 specification).
Examples:
"fnt": "MUSTERFRAU<GOESSINGER"
"fnt": "MUSTERFRAU<GOESSINGER<MUELLER"
*/
const familyNameStandardised = getValue("nam")["fnt"];
/**
* Forename(s), such as given name(s), of the holder.
If the holder has no forenames, the field MUST be
skipped.
In all other cases, exactly 1 (one) non-empty field MUST
be provided, with all forenames included in it. In case of
multiple forenames, these MUST be separated by a space.
Example:
"gn": "Isolde Erika"
*/
const givenName = getValue("nam")["gn"];
/**
Forename(s) of the holder transliterated using the same
convention as the one used in the holder’s machine
readable travel documents (such as the rules defined in
ICAO Doc 9303 Part 3).
If the holder has no forenames, the field MUST be
skipped.
In all other cases, exactly 1 (one) non-empty field MUST
be provided, only including characters A-Z and <.
Maximum length: 80 characters.
Example:
"gnt": "ISOLDE<ERIKA"
*/
const givenNameStandardised = getValue("nam")["gnt"];
/**
* Date of birth of the DCC holder.
Complete or partial date without time restricted to the
range from 1900-01-01 to 2099-12-31.
Exactly 1 (one) non-empty field MUST be provided if the
complete or partial date of birth is known. If the date of
birth is not known even partially, the field MUST be set to
an empty string "". This should match the information as
provided on travel documents.
One of the following ISO 8601 formats MUST be used if
information on date of birth is available. Other options are
not supported.
YYYY-MM-DD
YYYY-MM
YYYY
(The verifier app may show missing parts of the date of
birth using the XX convention as the one used in machine-
readable travel documents, e.g. 1990-XX-XX.)
Examples:
"dob": "1979-04-14"
"dob": "1901-08"
"dob": "1939"
"dob": ""
*/
const dateOfBirth = getValue("dob");
const person = {
givenName, givenNameStandardised,
familyName, familyNameStandardised,
dateOfBirth
};
if (getValue("v")) {
const groupInformation = DccHcertFactory.getVaccinationGroupInformation(dccCose);
return new VaccinationCertifiacte_1.VaccinationCertificate(version, person, groupInformation);
}
else if (getValue("t")) {
const groupInformation = DccHcertFactory.getTestGroupInformation(dccCose);
return new TestCertifiacte_1.TestCertificate(version, person, groupInformation);
}
else if (getValue("r")) {
const groupInformation = DccHcertFactory.getRecoveryGroupInformation(dccCose);
return new RecoveryCertificate_1.RecoveryCertificate(version, person, groupInformation);
}
throw new Error("...");
}
static getVaccinationGroupInformation(dccCose) {
const getValue = DccHcertFactory.getValue(dccCose);
/**
A coded value from the value set
disease-agent-targeted.json.
This value set has a single entry 840539006, which is the code for COVID-
19 from SNOMED CT (GPS).
Exactly 1 (one) non-empty field MUST be provided.
Example:
"tg": "840539006"
*/
const diseaseId = getValue("v")[0]["tg"];
const disease = ValueSets_1.getValueSetValue(DiseaseAgentTargeted_1.VSD_DISEASE_AGENT_TARGETED)(diseaseId, 'en');
/**
Type of the vaccine or prophylaxis used.
A coded value from the value set
vaccine-prophylaxis.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) non-empty field MUST be provided.
Example:
"vp": "1119349007" (a SARS-CoV-2 mRNA vaccine)
*/
const vaccineOrProphylaxisId = getValue("v")[0]["vp"];
const vaccineOrProphylaxis = ValueSets_1.getValueSetValue(VaccineProphylaxis_1.VSD_VACCINE_PROPHYLAXIS)(vaccineOrProphylaxisId, 'en');
/**
Medicinal product used for this specific dose of vaccination. A coded value
from the value set
vaccine-medicinal-product.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) non-empty field MUST be provided. Example:
"mp": "EU/1/20/1528" (Comirnaty)
*/
const vaccineProductId = getValue("v")[0]["mp"];
const vaccineProduct = ValueSets_1.getValueSetValue(VaccineMedicalProduct_1.VSD_VACCINE_MEDICAL_PRODUCT)(vaccineProductId, 'en');
/**
Marketing authorisation holder or manufacturer, if no marketing authorization
holder is present. A coded value from the value set
vaccine-mah-manf.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) non-empty field MUST be provided. Example:
"ma": "ORG-100030215" (Biontech Manufacturing GmbH)
*/
const vaccineManufacturerId = getValue("v")[0]["ma"];
const vaccineManufacturer = ValueSets_1.getValueSetValue(VaccineManufacturer_1.VSD_VACCINE_MANUFACTURER)(vaccineManufacturerId, 'en');
/**
Sequence number (positive integer) of the dose given during this vaccination
event. 1 for the first dose, 2 for the second dose etc.
Exactly 1 (one) non-empty field MUST be provided.
Examples:
"dn": "1" (first dose in a series)
"dn": "2" (second dose in a series)
"dn": "3" (third dose in a series, such as in case of a booster)
*/
const doseNumber = getValue("v")[0]["dn"];
/**
Total number of doses (positive integer) in a complete vaccination series
according to the used vaccination protocol. The protocol is not in all cases
directly defined by the vaccine product, as in some countries only one dose of
normally two-dose vaccines is delivered to people recovered from COVID-
19. In these cases, the value of the field should be set to 1.
Exactly 1 (one) non-empty field MUST be provided.
Examples:
"sd": "1" (for all 1-dose vaccination schedules)
"sd": "2" (for 2-dose vaccination schedules)
"sd": "3" (in case of a booster)
*/
const overallDoseNumber = getValue("v")[0]["sd"];
/**
The date when the described dose was received, in the format YYYY-MM-DD
(full date without time). Other formats are not supported.
Exactly 1 (one) non-empty field MUST be provided. Example:
"dt": "2021-03-28"
*/
const vaccinationDate = getValue("v")[0]["dt"];
/**
Country expressed as a 2-letter ISO3166 code (RECOMMENDED) or a
reference to an international organisation responsible for the vaccination event
(such as UNHCR or WHO). A coded value from the value set
country-2-codes.json.
The value set will be distributed from the EUDCC Gateway starting
with the gateway version 1.1.
Exactly 1 (one) field MUST be provided.
Example:
"co": "CZ"
"co": "UNHCR"
*/
// We only handle the 2 letter code.
const vaccinationCountry = Country2LetterISO3166Codes_1.COUNTRY_2_LETTER_ISO3166_CODES[getValue("v")[0]["co"]];
/**
Name of the organisation that issued the certificate. Identifiers are allowed as
part of the name, but not recommended to be used individually without the
name as a text. Max 80 UTF-8 characters.
Exactly 1 (one) non-empty field MUST be provided. Example:
"is": "Ministry of Health of the Czech Republic"
"is": "Vaccination Centre South District 3" */
const certificateIssuer = getValue("v")[0]["is"];
/**
Unique certificate identifier (UVCI) as specified in the vaccination-
proof_interoperability-guidelines_en.pdf (europa.eu)
The inclusion of the checksum is optional. The prefix "URN:UVCI:" may be
added.
Exactly 1 (one) non-empty field MUST be provided.
Examples:
"ci": "URN:UVCI:01:NL:187/37512422923"
"ci": "URN:UVCI:01:AT:10807843F94AEE0EE5093FBC254BD813#B" */
const uniqueCertificateIdentifier = getValue("v")[0]["ci"];
return {
disease, vaccineOrProphylaxis, vaccineProduct, vaccineManufacturer,
doseNumber, overallDoseNumber, vaccinationDate, vaccinationCountry,
certificateIssuer, uniqueCertificateIdentifier
};
}
/**
* A utility function to access the metadata values.
*
* Metadata (payload as json) internal data model:
* {
* 1: ...
* 2: ...
* -260: ...
* }
* @param dccCose
* @private
*/
static getMetaDataValue(dccCose) {
return (key) => dccCose.getPayloadAsJson().get(key);
}
/**
* A utility function to access the hcert values.
*
* Hcert (payload as json) internal data model:
* {
* -260: {
* 1: {
* v: ...
* dob: ...
* name: ...
* ver: ...
* }
* }
* }
* @param dccHcert
* @private
*/
static getValue(dccCose) {
// @ts-ignore
return (key) => dccCose.getPayloadAsJson().get(ChPayloadKeys.HCERT).get(1)[key];
}
static getTestGroupInformation(dccCose) {
const testGroup = DccHcertFactory.getValue(dccCose)("t")[0];
/**
A coded value from the value set
disease-agent-targeted.json.
This value set has a single entry 840539006, which is the code for COVID-
19 from SNOMED CT (GPS).
Exactly 1 (one) non-empty field MUST be provided.
Example:
"tg": "840539006"
*/
const diseaseId = testGroup["tg"];
const disease = ValueSets_1.getValueSetValue(DiseaseAgentTargeted_1.VSD_DISEASE_AGENT_TARGETED)(diseaseId, 'en');
/**
The type of the test used, based on the material targeted by the test. A coded
value from the value set
test-type.json
(based on LOINC). Values outside of the value set are not allowed.
Exactly 1 (one) non-empty field MUST be provided.
Example:
"tt": "LP6464-4" (Nucleic acid amplification with probe
detection)
"tt": "LP217198-3" (Rapid immunoassay)
*/
const testTypeId = testGroup["tt"];
const testType = ValueSets_1.getValueSetValue(TestType_1.VSD_TEST_TYPE)(testTypeId, 'en');
/**
The name of the nucleic acid amplification test (NAAT) used. The name
should include the name of the test manufacturer and the commercial name of
the test, separated by a comma.
For NAAT: the field is optional.
For RAT: the field SHOULD NOT be used, as the name of the test is supplied
indirectly through the test device identifier (t/ma).
When supplied, the field MUST NOT be empty.
Example:
"nm": "ELITechGroup, SARS-CoV-2 ELITe MGB® Kit"
*/
const testName = testGroup["nm"];
/**
Rapid antigen test (RAT) device identifier from the JRC database. Value set
(HSC common list):
All Rapid antigen tests in HSC common list (human readable).
https://covid-19-diagnostics.jrc.ec.europa.eu/devices/hsc-common-
recognition-rat (machine-readable, values of the field id_device
included on the list form the value set).
In EU/EEA countries, issuers MUST only issue certificates for tests belonging
to the currently valid value set. The value set MUST be updated every 24
hours.
Values outside of the value set MAY be used in certificates issued by third
countries, however the identifiers MUST still be from the JRC database. The
use of other identifiers such as those provided directly by test manufacturers is
not permitted.
Verifiers MUST detect values not belonging to the up to date value set and
render certificates bearing these as invalid. If an identifier is removed from
the value set, certificates including it MAY be accepted for a maximum of 72
hours after the removal.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
For RAT: exactly 1 (one) non-empty field MUST be provided.
For NAAT: the field MUST NOT be used, even if the NAA test identifier is
available in the JRC database.
Example:
"ma": "344" (SD BIOSENSOR Inc, STANDARD F COVID-19 Ag
FIA)
*/
const testDevice = HscCommonRecogintionRat_1.HSC_COMMON_RECONGINTION_RAT.deviceList.filter(d => testGroup["ma"] === d.id_device).pop();
/**
* The date and time when the test sample was collected. The time MUST
include information on the time zone. The value MUST NOT denote the time
when the test result was produced.
Exactly 1 (one) non-empty field MUST be provided.
One of the following ISO 8601 formats MUST be used. Other options are not
supported.
YYYY-MM-DDThh:mm:ssZ
YYYY-MM-DDThh:mm:ss[+-]hh
YYYY-MM-DDThh:mm:ss[+-]hhmm
YYYY-MM-DDThh:mm:ss[+-]hh:mm
Examples:
"sc": "2021-08-20T10:03:12Z" (UTC time)
"sc": "2021-08-20T12:03:12+02" (CEST time)
"sc": "2021-08-20T12:03:12+0200" (CEST time)
"sc": "2021-08-20T12:03:12+02:00" (CEST time)
*/
const testDate = new Date(testGroup["sc"]);
/**
The result of the test. A coded value from the value set
test-result.json (based on SNOMED CT, GPS).
Exactly 1 (one) non-empty field MUST be provided.
Example:
"tr": "260415000" (Not detected)
*/
const testResultId = testGroup["tr"];
const testResult = ValueSets_1.getValueSetValue(TestResult_1.VSD_TEST_RESULTS)(testResultId, 'en');
/**
* Name of the actor that conducted the test. Identifiers are allowed as part of the
name, but not recommended to be used individually without the name as a
text. Max 80 UTF-8 characters. Any extra characters should be truncated. The
name is not designed for automated verification.
For NAAT tests: exactly 1 (one) non-empty field MUST be provided.
For RAT tests: the field is optional. If provided, MUST NOT be empty.
Example:
"tc": "Test centre west region 245"
*/
const testingCentre = testGroup["tc"];
/**
Country expressed as a 2-letter ISO3166 code (RECOMMENDED) or a
reference to an international organisation responsible for carrying out the test
(such as UNHCR or WHO). This MUST be a coded value from the value set
country-2-codes.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) field MUST be provided.
Examples:
"co": "CZ"
"co": "UNHCR"
*/
const testingCountry = Country2LetterISO3166Codes_1.COUNTRY_2_LETTER_ISO3166_CODES[testGroup["co"]];
/**
Country expressed as a 2-letter ISO3166 code (RECOMMENDED) or a
reference to an international organisation responsible for carrying out the test
(such as UNHCR or WHO). This MUST be a coded value from the value set
country-2-codes.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) field MUST be provided.
Examples:
"co": "CZ"
"co": "UNHCR"
*/
const certificateIssuer = testGroup["is"];
/**
Unique certificate identifier (UVCI) as specified in the vaccination-
proof_interoperability-guidelines_en.pdf (europa.eu)
The inclusion of the checksum is optional. The prefix "URN:UVCI:" may be
added.
Exactly 1 (one) non-empty field MUST be provided.
Examples:
"ci": "URN:UVCI:01:NL:187/37512422923"
"ci": "URN:UVCI:01:AT:10807843F94AEE0EE5093FBC254BD813#B" */
const uniqueCertificateIdentifier = testGroup["ci"];
return {
disease, testType, testName, testDevice, testDate, testResult,
testingCentre, testingCountry,
certificateIssuer, uniqueCertificateIdentifier
};
}
static getRecoveryGroupInformation(dccCose) {
const recoveryGroup = DccHcertFactory.getValue(dccCose)("r")[0];
/**
A coded value from the value set
disease-agent-targeted.json.
This value set has a single entry 840539006, which is the code for COVID-
19 from SNOMED CT (GPS).
Exactly 1 (one) non-empty field MUST be provided.
Example:
"tg": "840539006"
*/
const diseaseId = recoveryGroup["tg"];
const disease = ValueSets_1.getValueSetValue(DiseaseAgentTargeted_1.VSD_DISEASE_AGENT_TARGETED)(diseaseId, 'en');
/**
The date when a sample for the NAAT test producing a positive result was
collected, in the format YYYY-MM-DD (complete date without time). Other
formats are not supported.
Exactly 1 (one) non-empty field MUST be provided.
Example:
"fr": "2021-05-18"
*/
const firstPositiveTestDate = recoveryGroup["fr"];
/**
Country expressed as a 2-letter ISO3166 code (RECOMMENDED) or a
reference to an international organisation responsible for carrying out the test
(such as UNHCR or WHO). This MUST be a coded value from the value set
country-2-codes.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) field MUST be provided.
Examples:
"co": "CZ"
"co": "UNHCR"
*/
const testingCountry = Country2LetterISO3166Codes_1.COUNTRY_2_LETTER_ISO3166_CODES[recoveryGroup["co"]];
/**
Country expressed as a 2-letter ISO3166 code (RECOMMENDED) or a
reference to an international organisation responsible for carrying out the test
(such as UNHCR or WHO). This MUST be a coded value from the value set
country-2-codes.json.
The value set will be distributed from the EUDCC Gateway starting with the
gateway version 1.1.
Exactly 1 (one) field MUST be provided.
Examples:
"co": "CZ"
"co": "UNHCR"
*/
const certificateIssuer = recoveryGroup["is"];
/**
The first date on which the certificate is considered to be valid. The date
MUST NOT be earlier than the date calculated as r/fr + 11 days.
The date MUST be provided in the format YYYY-MM-DD (complete date
without time). Other formats are not supported.
Exactly 1 (one) non-empty field MUST be provided.
Example:
"df": "2021-05-29"
*/
const certificateValidFrom = recoveryGroup["df"];
/**
The last date on which the certificate is considered to be valid, assigned by the
certificate issuer. The date MUST NOT be after the date calculated as r/fr +
180 days.
The date MUST be provided in the format YYYY-MM-DD (complete date
without time). Other formats are not supported.
Exactly 1 (one) non-empty field MUST be provided.
Example:
"du": "2021-11-14"
*/
const certificateValidUntil = recoveryGroup["du"];
/**
Unique certificate identifier (UVCI) as specified in the vaccination-
proof_interoperability-guidelines_en.pdf (europa.eu)
The inclusion of the checksum is optional. The prefix "URN:UVCI:" may be
added.
Exactly 1 (one) non-empty field MUST be provided.
Examples:
"ci": "URN:UVCI:01:NL:187/37512422923"
"ci": "URN:UVCI:01:AT:10807843F94AEE0EE5093FBC254BD813#B" */
const uniqueCertificateIdentifier = recoveryGroup["ci"];
return { disease, certificateIssuer, testingCountry,
uniqueCertificateIdentifier, firstPositiveTestDate,
certificateValidFrom, certificateValidUntil
};
}
}
exports.DccHcertFactory = DccHcertFactory;
//# sourceMappingURL=EudccHcertFactory.js.map