1
0
mirror of https://github.com/covidpass-org/covidpass.git synced 2025-02-22 06:27:49 +01:00
This commit is contained in:
Jason Liu 2021-10-15 10:32:53 -04:00
commit 8befe6af76
13 changed files with 34 additions and 335 deletions

View File

@ -27,18 +27,17 @@ function urlParse(text: string, links: link[]): JSX.Element[] {
function Faq(): JSX.Element {
const { t } = useTranslation(['common', 'index', 'faq']);
const questionList = [
{description: 'Which version of iOS does this support?', answer: 'iOS 13.7 is the minimum at the moment. We are looking for adjustments for older iOS versions, but it will take a bit of time.'},
{description: 'I\'m having issues with adding it to my iPhone 6.', answer: 'Unfortunately, the iPhone 6 supports up to iOS 12 while the minimum requirement for our app is iOS 13.7 however, we are looking for ways around this to make it more accessible. In the meantime you can try it on a computer or another device and save it as either a wallet card or a photo - if you save it as a card, you can then email it to your iPhone and you will be able to import it into Apple Wallet that way.'},
{description: 'Which version of iOS does this support?', answer: 'The new enhanced QR code requires iOS 15+.'},
{description: 'I\'m having issues with adding it to my iPhone 6.', answer: 'Unfortunately, the minimum requirement for the new QR code is iOS 15 which runs on iPhone 6s and newer devices.'},
{description: "What are the supported browsers?", answer: 'For iPhones, only Safari is supported for importing to your Apple Wallet. For any other devices, we recommend that you save it as photo using your browser of choice. Browsers built internally into mobile apps (e.g. Facebook, Twitter, Instagram) are known to have issues.'},
{description: "How is my private information handled?", answer: 'Your proof-of-vaccination PDF (and most of the information in it) never leaves your device, and does NOT get sent to our server - the only information we send and store is non-personally-identifiable information such as vaccine type, date, and which organization gave you the vaccine. We share your concern about personal data being stored and lost, which is why we chose not to store or send any of it to our servers so there is no chance of it being lost or leaked.'},
{description: 'Do you have plans for Android support?', answer: 'Yes. We are working with Google to gain access to the APIs required. Meanwhile, you can also use this tool to download an Apple Wallet pass and import that into Google Pay Wallet using apps such as Pass2Pay or simply save it as a photo.'},
{description: 'I have a Red/White OHIP card. Can I still use this tool?', answer: 'Yes you can! Just call the Provincial Vaccine Contact Centre at 1-833-943-3900. The call centre agent can email you a copy of the receipt.'},
{description: 'I do not have a health card. Can I still use this tool?', answer: 'First contact your local public health unit to verify your identity and receive a COVIDcovid ID/Personal Access Code. You can then call the Provincial Vaccine Contact Centre at 1-833-943-3900 to get an email copy of your receipt.'},
{description: 'I\'m seeing an error message saying “Failed byte range verification." What do I do?', answer: 'If you see this error then please try re-downloading your receipt from the provincial proof-of-vaccination portal and trying again. We have received reports from some people that this has resolved the problem for them.'},
{description: 'What does the colour of the Apple Wallet pass mean?', answer: 'Dose 1 is shown as Orange; dose 2+ in green for easy differentiation without reading the text. For the Janssen (Johnson & Johnson) vaccine, dose 1 is shown as green.'},
{description: 'Should I use the official provincial apps when they come out on 22nd October?', answer: 'YES. Once the official QR code from the province is available, please come back to this site and you will be able to generate a new Apple Wallet pass which contains that new QR code'},
{description: 'Why isn\'t the new Apple Wallet pass green/orange?', answer: 'The official verifier from Ontario will decide which one is accepted. Color coding is no longer used for our Apple Wallet tool.'},
{description: 'How is the data on my vaccination receipt processed?', answer: 'Inside your local web browser, it checks the receipt for a digital signature from the provincial proof-of-vaccination system. If present, the receipt data is converted into Apple\'s format and then added into your iOS Wallet app.'},
{description: 'How can organizations validate this QR code?', answer: 'You can use our verifier app at verifier.vaccine-ontario.ca to verify these passes quickly if you are a business - you should also be able to use any normal QR code scanner to scan this code and it will take you to a verification site which tells you whether the receipt is valid or not'},
{description: 'How can organizations validate this QR code?', answer: 'Verify Ontario app is your official tool. For devices that cannot run the official tool, you can also use our web-based tool at verifier.vaccine-ontario.ca'},
{description: 'Can I use the same iPhone to store passes for my entire family?', answer: 'Yes.'},
{description: 'Is this free and non-commercial?', answer: 'Similar to VaxHuntersCanada, there are no commercial interests. Just volunteers trying to do our part to help the community.'},
{description: 'How about support for other provinces?', answer: 'We will be investigating BC and Québec support shortly. If you are interested in contributing, please email us at grassroots@vaccine-ontario.ca'},

View File

@ -1 +0,0 @@
nb

View File

@ -1,6 +0,0 @@
title: CovidPass
subtitle: Legg til EUs digitale COVID -sertifikater i favorittlommebokappene dine.
privacyPolicy: Privacy Policy
donate: Sponsor
gitHub: GitHub
imprint: Avtrykk

View File

@ -1,20 +0,0 @@
noFileOrQrCode: Vennligst skann en QR-kode, eller velg en fil
signatureFailed: Feil under signering av pass på server
decodingFailed: Kunne ikke dekode nyttelast for QR-kode
invalidColor: Ugyldig farge
certificateData: Kunne ikke lese sertifikatdata
nameMissing: Kunne ikke lese navnet
dobMissing: Kunne ikke lese fødselsdatoen
invalidMedicalProduct: Ugyldig medisinsk produkt
invalidCountryCode: Ugyldig landskode
invalidManufacturer: Ugyldig produsent
invalidFileType: Ugyldig filtype
couldNotDecode: Kunne ikke dekode QR-koden fra filen
couldNotFindQrCode: Kunne ikke finne QR-koden i den medfølgende filen
invalidQrCode: Ugyldig QR-kode
certificateType: Fant ingen gyldig sertifikattype
invalidTestResult: Ugyldig testresultat
invalidTestType: Ugyldig testtype
noCameraAccess: Fikk ikke tilgang til kameraet. Kontroller tillatelser under Innstillinger > Safari > Kamera.
noCameraFound: Fant ikke kamera.
safariSupportOnly: På iOS, vennligst bruk Safari nettleseren.

View File

@ -1,27 +0,0 @@
heading: Informasjon i henhold til § 5 TMG
contact: Kontakt
euDisputeResolution: EU Konfliktløsning
euDisputeResolutionParagraph: |
Europakommisjonen tilbyr en plattform for online konfliktløsning (OS) https://ec.europa.eu/consumers/odr.
Du finner e-postadressen vår i avtrykket ovenfor.
consumerDisputeResolution: Forbrukerkonfliktløsning
consumerDisputeResolutionParagraph: Vi er ikke villige eller forpliktet til å delta i konfliktløsningssaker for et forbrukernemnd.
liabilityForContents: Ansvar for innholdet
liabilityForContentsParagraph: |
Som tjenesteleverandør er vi ansvarlig for vårt eget innhold på disse sidene i henhold til § 7 avsnitt 1 TMG under de generelle lovene.
I henhold til §§ 8 til 10 TMG er vi ikke forpliktet til å overvåke overført eller lagret informasjon eller å undersøke omstendigheter som indikerer ulovlig aktivitet.
Forpliktelser til å fjerne eller blokkere bruk av informasjon i henhold til de generelle lovene forblir upåvirket.
Ansvar i denne forbindelse er imidlertid bare mulig fra det tidspunkt et konkret brudd på loven blir kjent.
Hvis vi blir klar over slike overtredelser, fjerner vi det relevante innholdet umiddelbart.
liabilityForLinks: Ansvar for lenker
liabilityForLinksParagraph: |
Vårt tilbud inneholder lenker til eksterne nettsteder til tredjeparter, hvis innhold vi ikke har innflytelse på.
Derfor kan vi ikke påta oss noe ansvar for dette eksterne innholdet.
Den respektive leverandøren eller operatøren av nettstedene er alltid ansvarlig for innholdet på de koblede nettstedene.
De koblede sidene ble sjekket for mulige lovbrudd på tidspunktet for koblingen.
Ulovlig innhold var ikke gjenkjennelig på tidspunktet for koblingen.
En permanent kontroll av innholdet på de lenkete sidene er imidlertid ikke rimelig uten konkrete bevis på brudd på loven.
Hvis vi blir klar over brudd, vil vi fjerne slike lenker umiddelbart.
credits: Kreditere
creditsSource: Med utdrag fra https://www.e-recht24.de/impressum-generator.html
creditsTranslation: Oversatt med https://www.DeepL.com/Translator (free version)

View File

@ -1,29 +0,0 @@
iosHint: På iOS, vennligst bruk Safari nettleseren.
errorClose: Lukk
selectCertificate: Velg Sertifikat
selectCertificateDescription: |
Skann QR-koden på sertifikatet ditt, eller velg et skjermbilde eller en PDF med QR-koden.
Vær oppmerksom på at det ikke støttes å velge en fil direkte fra kameraet.
stopCamera: Stopp Kamera
startCamera: Start Kamera
openFile: Velg Fil
foundQrCode: Fant QR-kode!
pickColor: Velg en farge
pickColorDescription: Velg en bakgrunnsfarge for passet ditt.
colorWhite: hvit
colorBlack: svart
colorGrey: grå
colorGreen: grønn
colorIndigo: mørkeblå
colorBlue: blå
colorPurple: lilla
colorTeal: blågrønn
addToWallet: Legg til i Lommebok
dataPrivacyDescription: |
Personvern er av spesiell betydning ved behandling av helserelaterte data.
For å ta en informert beslutning, vennligst les
iAcceptThe: Jeg godtar
privacyPolicy: Personvernerklæring
createdOnDevice: Laget på enheten din
openSourceTransparent: Åpen kildekode og gjennomsiktig
hostedInEU: Driftet i EU

View File

@ -1,57 +0,0 @@
gdprNotice: |
Personvernerklæringen vår er basert på vilkårene som brukes av den europeiske lovgiveren
for vedtakelsen av General Data Protection Regulation (GDPR).
generalInfo: Generell informasjon
generalInfoProcess: |
Hele prosessen med å generere passfilen skjer lokalt i nettleseren din.
For signeringstrinnet sendes bare en hash representasjon av dataene dine til serveren.
generalInfoStoring: Dine data lagres ikke utover den aktive nettlesersessionen, og nettsten bruker ikke informasjonskapsler.
generalInfoThirdParties: Ingen data blir sendt til tredjeparter.
generalInfoHttps: Vi overfører dataene dine sikkert over https.
generalInfoLocation: Serveren vår ligger i Nuremberg, Germany.
generalInfoGitHub: Kildekoden til dette nettstedet er tilgjengelig på
generalInfoLockScreen: Som standard er Apple Wallet pass tilgjengelig fra låseskjermen. Dette kan endres i
settings: innstillinger
generalInfoProvider: |
Serverleverandøren behandler data for å tilby dette nettstedet.
For å bedre forstå hvilke tiltak de tar for å beskytte dataene dine, vennligst les også deres
privacyPolicy: personvernerklæring
andThe: og
dataPrivacyFaq: vanlige spørsmål om personvern
contact: Kontakt
email: Epost
website: Webside
process: Forenklet forklaring av prosessen
processFirst: Først skjer følgende trinn lokalt i nettleseren din
processSecond: Så skjer følgende trinn på serveren vår
processThird: Til slutt skjer følgende trinn lokalt i nettleseren din
processRecognizing: Gjenkjenne og trekke ut QR-kodedataene fra det valgte sertifikatet
processDecoding: Dekoding av dine personlige og helserelaterte data fra QR-kode
processAssembling: Montering av en ufullstendig pass-fil ut av dataene dine
processGenerating: Generere en fil som inneholder hashes av dataene som er lagret i passfilen
processSending: Sender bare filen som inneholder hasjene til serveren vår
processReceiving: Motta og sjekke hashene som ble generert lokalt
processSigning: Signerer filen som inneholder hasjene
processSendingBack: Sender signaturen tilbake
processCompleting: Montering av den signerte passfilen fra den ufullstendige filen generert lokalt og signaturen
processSaving: Lagrer filen på enheten din
locallyProcessedData: Lokalt behandlede data
the:
schema: Skjema for digitalt Covid -sertifikat
specification: inneholder en detaljert spesifikasjon av hvilke data som kan finnes i QR -koden og vil bli behandlet i nettleseren din.
serverProvider: Serverleverandør
serverProviderIs: Serverleverandøren vår er
logFiles: Følgende data kan samles inn og lagres i serverloggfilene
logFilesBrowser: Nettlesertypene og versjonene som brukes
logFilesOs: Operativsystemet som brukes av tilgangssystemet
logFilesReferrer: Nettstedet som et tilgangssystem kommer til nettstedet vårt fra (såkalte henvisninger)
logFilesTime: Dato og klokkeslett for tilgang
logFilesIpAddress: De pseudonymiserte IP-adressene
rights: Dine rettigheter
rightsGranted: I samsvar med GDPR har du følgende rettigheter
rightsAccess: Rett til tilgang til dataene dine; Du har rett til å vite hvilke data som er samlet om deg og hvordan de ble behandlet.
rightsErasure: Rett til å bli glemt; Sletting av dine personlige data.
rightsRectification: Rett til å rette opp; Du har rett til å korrigere unøyaktige data.
rightsPortability: Rett til dataportabilitet; Du har rett til å overføre dataene dine fra et behandlingssystem til et annet.
thirdParties: Tredjeparter knyttet til
appleSync: Apple kan synkronisere passene dine via iCloud

View File

@ -1 +0,0 @@
nb

View File

@ -1 +0,0 @@
no

View File

@ -147,7 +147,9 @@ export class PassData {
// Update our pass name if this is an SHC pass
if (payload.rawData.length > 0) {
const newPassTitle = `${Constants.NAME}, ${payload.shcReceipt.cardOrigin}`;
// const newPassTitle = `${Constants.NAME}, ${payload.shcReceipt.cardOrigin}`;
const newPassTitle = `${Constants.NAME}`; // hot patch for production for now... string too long to fit in pass
this.logoText = newPassTitle;
this.organizationName = newPassTitle;
this.description = newPassTitle;

View File

@ -109,6 +109,7 @@ export class PassPhotoCommon {
// validation since SHCs are self-validating. This entire registration process could
// be turned off for SHCs and there would be no harm to the card creation process
registrationPayload = generateSHCRegisterPayload(payloadBody.shcReceipt);
registrationPayload.serialNumber = payload.serialNumber; // serial number is needed as it's the firestore document id
} else {

View File

@ -193,9 +193,26 @@ function processSHCReceipt(receipt: SHCReceipt, generic: PassDictionary) {
generic.primaryFields.push(
{
key: "name",
label: "Name",
value: receipt.name
label: "",
value: `${receipt.name} (${receipt.dateOfBirth})`
}
)
);
}
for (let i = 0; i < receipt.vaccinations.length; i++) {
generic.secondaryFields.push(
{
key: "vaccine",
label: "Vaccine",
value: receipt.vaccinations[i].vaccineName
},
{
key: "dov",
label: "Vacc. Date",
value: receipt.vaccinations[i].vaccinationDate
}
);
}
}

View File

@ -1,12 +1,11 @@
import {PayloadBody, Receipt, HashTable} from "./payload";
import {PayloadBody} from "./payload";
import * as PdfJS from 'pdfjs-dist/legacy/build/pdf'
import {QRCode} from "jsqr";
import { getCertificatesInfoFromPDF } from "@ninja-labs/verify-pdf"; // ES6
import * as Sentry from '@sentry/react';
import * as Decode from './decode';
import {getScannedJWS, verifyJWS, decodeJWS} from "./shc";
import { PDFPageProxy, TextItem } from 'pdfjs-dist/types/src/display/api';
import { PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
// import {PNG} from 'pngjs'
// import {decodeData} from "./decode";
@ -22,19 +21,9 @@ export async function getPayloadBodyFromFile(file: File): Promise<PayloadBody> {
switch (file.type) {
case 'application/pdf':
///////// DELETE THE FOLLOWING CODE ON OCT 22ND //////////////////////
const receiptType = await detectReceiptType(fileBuffer);
console.log(`receiptType = ${receiptType}`);
// receipt type is needed to decide if digital signature checking is needed
if (receiptType == 'ON') {
// Bail out immediately, special case
const receipts = await loadPDF(fileBuffer);
return {receipts, rawData: '', shcReceipt: null};
} else {
///////// END OCT 22ND DELETE BLOCK //////////////////////
imageData = await getImageDataFromPdf(fileBuffer);
}
imageData = await getImageDataFromPdf(fileBuffer);
break;
case 'image/png':
case 'image/jpeg':
case 'image/webp':
@ -42,6 +31,7 @@ export async function getPayloadBodyFromFile(file: File): Promise<PayloadBody> {
console.log(`image ${file.type}`);
imageData = [await getImageDataFromImage(file)];
break;
default:
throw Error('invalidFileType')
}
@ -50,174 +40,6 @@ export async function getPayloadBodyFromFile(file: File): Promise<PayloadBody> {
return processSHC(imageData);
}
async function detectReceiptType(fileBuffer : ArrayBuffer): Promise<string> {
console.log('detectPDFTypeAndProcess');
const typedArray = new Uint8Array(fileBuffer);
let loadingTask = PdfJS.getDocument(typedArray);
const pdfDocument = await loadingTask.promise;
const docMetadata = await pdfDocument.getMetadata();
// Explicitly try to detect an ON PDF based on the headers in the PDF
//console.log(`PDF details: metadata=${JSON.stringify(docMetadata)}`);
// The Ontario proof-of-vaccination receipts have several fixed unchanging pieces of metadata that we use for detection
if (docMetadata.info['IsSignaturesPresent'] &&
(docMetadata.info['Producer'] == 'PDFKit') &&
(docMetadata.info['PDFFormatVersion'] == '1.7') &&
(docMetadata.info['Title'] == 'COVID-19 vaccination receipt / Récépissé de vaccination contre la COVID-19')
) {
return Promise.resolve('ON');
} else {
// If it's not an exact match to an ON proof-of-vaccination PDF, it will never pass validation anyways so treat it as an SHC PDF
return Promise.resolve('SHC');
}
}
async function loadPDF(fileBuffer : ArrayBuffer): Promise<HashTable<Receipt>> {
try {
const certs = getCertificatesInfoFromPDF(fileBuffer);
const result = certs[0];
const refcert = '-----BEGIN CERTIFICATE-----\r\n'+
'MIIHNTCCBh2gAwIBAgIQanhJa+fBXT8GQ8QG/t9p4TANBgkqhkiG9w0BAQsFADCB\r\n'+
'ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT\r\n'+
'H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy\r\n'+
'MDE0IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG\r\n'+
'A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxTTAeFw0y\r\n'+
'MTA1MjAxMzQxNTBaFw0yMjA2MTkxMzQxNDlaMIHTMQswCQYDVQQGEwJDQTEQMA4G\r\n'+
'A1UECBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzETMBEGCysGAQQBgjc8AgED\r\n'+
'EwJDQTEYMBYGCysGAQQBgjc8AgECEwdPbnRhcmlvMRcwFQYDVQQKEw5PbnRhcmlv\r\n'+
'IEhlYWx0aDEaMBgGA1UEDxMRR292ZXJubWVudCBFbnRpdHkxEzARBgNVBAUTCjE4\r\n'+
'LTA0LTIwMTkxJzAlBgNVBAMTHmNvdmlkMTlzaWduZXIub250YXJpb2hlYWx0aC5j\r\n'+
'YTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2bD+Ng1RNYCNVVtEQ3\r\n'+
'zg8JKFvRWFFPIF/UTXGg3iArK1tKr1xtjx6OdFtwosHyo+3ksPRicc4KeuV6/QMF\r\n'+
'qiVJ5IOy9TSVImJsmONgFyEiak0dGYG5SeHiWwyaUvkniWd7U3wWEl4nOZuLAYu4\r\n'+
'8ZLot8p8Q/UaNvAoNsRDv6YDGjL2yGHaXxi3Bb6XTQTLcevuEQeM6g1LtKyisZfB\r\n'+
'Q8TKThBq99EojwHfXIhddxbPKLeXvWJgK1TcL17UFIwx6ig74s0LyYqEPm8Oa8qR\r\n'+
'+IesFUT9Liv7xhV+tU52wmNfDi4znmLvs5Cmh/vmcHKyhEbxhYqciWJocACth5ij\r\n'+
'E3kCAwEAAaOCAxowggMWMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFoW3zt+jaHS\r\n'+
'pm1EV5hU4XD+mwO5MB8GA1UdIwQYMBaAFMP30LUqMK2vDZEhcDlU3byJcMc6MGgG\r\n'+
'CCsGAQUFBwEBBFwwWjAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5u\r\n'+
'ZXQwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9haWEuZW50cnVzdC5uZXQvbDFtLWNoYWlu\r\n'+
'MjU2LmNlcjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1c3QubmV0\r\n'+
'L2xldmVsMW0uY3JsMCkGA1UdEQQiMCCCHmNvdmlkMTlzaWduZXIub250YXJpb2hl\r\n'+
'YWx0aC5jYTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG\r\n'+
'AQUFBwMCMEsGA1UdIAREMEIwNwYKYIZIAYb6bAoBAjApMCcGCCsGAQUFBwIBFhto\r\n'+
'dHRwczovL3d3dy5lbnRydXN0Lm5ldC9ycGEwBwYFZ4EMAQEwggF+BgorBgEEAdZ5\r\n'+
'AgQCBIIBbgSCAWoBaAB3AFYUBpov18Ls0/XhvUSyPsdGdrm8mRFcwO+UmFXWidDd\r\n'+
'AAABeYoCz+MAAAQDAEgwRgIhAKGKAoZMzwkh/3sZXq6vtEYhoYHfZzsjh9jqZvfS\r\n'+
'xQVZAiEAmJu/ftbkNFBr8751Z9wA2dpI0Qt+LoeL1TJQ833Kdg4AdQDfpV6raIJP\r\n'+
'H2yt7rhfTj5a6s2iEqRqXo47EsAgRFwqcwAAAXmKAs/cAAAEAwBGMEQCICsD/Vj+\r\n'+
'ypZeHhesMyv/TkS5ftQjqyIaAFTL/02Gtem4AiBcWdPQspH3vfzZr4LO9z4u5jTg\r\n'+
'Psfm5PZr66tI7yASrAB2AEalVet1+pEgMLWiiWn0830RLEF0vv1JuIWr8vxw/m1H\r\n'+
'AAABeYoC0WkAAAQDAEcwRQIgTL5F11+7KhQ60jnODm9AkyvXRLY32Mj6tgudRAXO\r\n'+
'y7UCIQDd/dU+Ax1y15yiAA5xM+bWJ7T+Ztd99SD1lw/o8fEmOjANBgkqhkiG9w0B\r\n'+
'AQsFAAOCAQEAlpV3RoNvnhDgd2iFSF39wytf1R6/0u5FdL7eIkYNfnkqXu9Ux9cO\r\n'+
'/OeaGAFMSzaDPA8Xt9A0HqkEbh1pr7UmZVqBwDr4a7gczvt7+HFJRn//Q2fwhmaw\r\n'+
'vXTLLxcAPQF00G6ySsc9MUbsArh6AVhMf9tSXgNaTDj3X3UyYDfR+G8H9eVG/LPp\r\n'+
'34QV/8uvPUFXGj6MjdQysx6YG+K3mae0GEVpODEl4MiceEFZ7v4CPA6pFNadijRF\r\n'+
'6tdXky2psuo7VXfnE2WIlahKr56x+8R6To5pcWglKTywTqvCbnKRRVZhXXYo3Awd\r\n'+
'8h9+TbL3ACHDqA4fi5sAbZ7nMXp8RK4o5A==\r\n'+
'-----END CERTIFICATE-----';
const pdfCert = result.pemCertificate.trim();
//const pdfOrg = result.issuedBy.organizationName;
const issuedpemCertificate = (pdfCert == refcert.trim());
//console.log(`pdf is signed by this cert ${result.pemCertificate.trim()}`);
//console.log(issuedpemCertificate);
//console.log(`PDF is signed by ${result.issuedBy.organizationName}, issued to ${result.issuedTo.commonName}`);
if (issuedpemCertificate) {
//console.log('getting receipt details inside PDF');
const receipt = await getPdfDetails(fileBuffer);
// console.log(JSON.stringify(receipt, null, 2));
return Promise.resolve(receipt);
} else {
/* We don't need to track these anymore - this is all going away in a week anyways
// According to the Sentry docs, this can be up to 8KB in size
// https://develop.sentry.dev/sdk/data-handling/#variable-size
Sentry.setContext("certificate", {
pdfCert: pdfCert,
pdfOrg: pdfOrg,
});
Sentry.captureMessage('Certificate validation failed');
*/
console.error('invalid certificate');
return Promise.reject(`invalid certificate + ${JSON.stringify(result)}`);
}
} catch (e) {
if (e.message.includes('Failed to locate ByteRange') ||
e.message.includes(' ASN.1') ||
e.message.includes('Failed byte range verification') ||
e.message.includes('parse DER') ||
e.message.includes('8, 16, 24, or 32 bits')) {
e.message = 'Sorry. Selected PDF file is not digitally signed. Please download official copy from Step 1 and retry. Thanks.'
} else {
if (!e.message.includes('cancelled')) {
console.error(e);
Sentry.captureException(e);
}
}
return Promise.reject(e);
}
}
async function getPdfDetails(fileBuffer: ArrayBuffer): Promise<HashTable<Receipt>> {
try {
const typedArray = new Uint8Array(fileBuffer);
let loadingTask = PdfJS.getDocument(typedArray);
const pdfDocument = await loadingTask.promise;
// Load all dose numbers
const receiptObj = {};
for (let pages = 1; pages <= pdfDocument.numPages; pages++){
const pdfPage = await pdfDocument.getPage(pages);
const content = await pdfPage.getTextContent();
const numItems = content.items.length;
let name, vaccinationDate, vaccineName, dateOfBirth, numDoses, organization;
for (let i = 0; i < numItems; i++) {
let item = content.items[i] as TextItem;
const value = item.str;
if (value.includes('Name / Nom'))
name = (content.items[i+1] as TextItem).str;
if (value.includes('Date:')) {
vaccinationDate = (content.items[i+1] as TextItem).str;
vaccinationDate = vaccinationDate.split(',')[0];
}
if (value.includes('Product name')) {
vaccineName = (content.items[i+1] as TextItem).str;
}
if (value.includes('Date of birth'))
dateOfBirth = (content.items[i+1] as TextItem).str;
if (value.includes('Authorized organization'))
organization = (content.items[i+1] as TextItem).str;
if (value.includes('You have received'))
numDoses = Number(value.split(' ')[3]);
}
receiptObj[numDoses] = new Receipt(name, vaccinationDate, vaccineName, dateOfBirth, numDoses, organization);
//console.log(receiptObj[numDoses]);
}
return Promise.resolve(receiptObj);
} catch (e) {
if (e && e.message && !e.message.includes('cancelled')) {
Sentry.captureException(e);
}
return Promise.reject(e);
}
}
async function getImageDataFromPdfPage(pdfPage: PDFPageProxy): Promise<ImageData> {
const pdfScale = 2;
@ -350,7 +172,7 @@ async function processSHC(allImageData : ImageData[]) : Promise<PayloadBody> {
// If we got here, no SHC was detected and successfully decoded.
// The vast majority of our processed things right now are ON proof-of-vaccination PDFs, not SHC docs, so assume anything
// that blew up here was a malformed ON proof-of-vaccination and create an appropriate error message for that
return Promise.reject(new Error('No valid ON proof-of-vaccination digital signature found! Please make sure you download the PDF directly from covid19.ontariohealth.ca, Save as Files on your iPhone, and do NOT save/print it as a PDF!'));
return Promise.reject(new Error('No SHC QR code found! Please try taking another picture of the SHC you wish to import'));
} catch (e) {
Sentry.captureException(e);