<!doctype html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<link rel="stylesheet" href="css/style.verify.css">
|
|
<link rel="stylesheet" href="css/topnav.css">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="shortcut icon" href="https://www.pathcheck.org/hubfs/Favicon.png">
|
|
<title>Certificate Verifier</title>
|
|
|
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DTDMHW3NV6"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', 'G-DTDMHW3NV6');
|
|
</script>
|
|
|
|
<script src="js/qrcode.min.js"></script>
|
|
|
|
<script src="js/pcf-utils.js"></script>
|
|
<script src="js/ui-utils.js"></script>
|
|
|
|
<script src="js/cred-sdk.min.js"></script>
|
|
<script src="js/divoc-sdk.min.js"></script>
|
|
<script src="js/dcc-sdk.min.js"></script>
|
|
<script src="js/shc-sdk.min.js"></script>
|
|
|
|
<script src="js/bbs-cbld-sdk.min.js"></script>
|
|
<script src="js/bbs-jxt-sdk.min.js"></script>
|
|
<script src="js/eddsa-jxt-sdk.min.js"></script>
|
|
|
|
<script src="js/vds-sdk.min.js"></script>
|
|
|
|
<script type="text/javascript" src="js/instascan.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div class="topnav">
|
|
<div class="topnavContainer">
|
|
<a href="index.html">Signers</a>
|
|
<a class="active" href="verify.html"><span class="xs-hidden">Universal </span>Verifier</a>
|
|
<a class="xs-hidden" href="debug.html">QR Debugger</a>
|
|
<a class="xs-hidden" href="https://github.com/Path-Check/paper-cred-demo">Source Code</a>
|
|
<a href="https://github.com/Path-Check/paper-cred"><span class="xs-hidden"> QR </span>Specs</a>
|
|
<a href="http://vaccine-docs.pathcheck.org"><span class="xs-hidden">Vaccine </span>Docs</a>
|
|
<a class="xs-hidden" href="http://pathcheck.org">About PathCheck</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="center">
|
|
<div class="full-div">
|
|
<div id="pre-verify-section">
|
|
<h1>Certificate Verifier</h1>
|
|
|
|
<div class="select" tabindex="1" id="cameras"></div>
|
|
<button class="camera-btn" id='camera-btn' onclick="toggleCamera()">Go</button>
|
|
<br><br>
|
|
|
|
<video id="preview" ></video>
|
|
|
|
<h2>Or Paste the QR Code here:</h2>
|
|
<textarea id="qr-verify" rows="5" placeholder="cred:type:version:signature:pubkey:payload"></textarea>
|
|
<br><br>
|
|
|
|
<div class="align-center">
|
|
<button class="qr-btn" onclick="verifyQRCode()">Verify</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="post-verify-section" style="display: none;">
|
|
<div class="auto-margin">
|
|
<div class="card align-center">
|
|
<h2 id="qr-verify-title" style="text-align: center;"></h1>
|
|
<canvas id="qr"></canvas><br/>
|
|
<h2 id="qr-verify-name" style="text-align: center;"></h1>
|
|
<h3 id="qr-verify-verified" style="text-align: center;"></h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="position:fixed;top:calc(50% - 250px);right:0;transition:width 300ms ease-out;width:0;" data-qa="side_panel"> <a class="typeform-share button" href="https://form.typeform.com/to/cGJGC6zl?typeform-medium=embed-snippet" data-mode="side_panel" style="box-sizing:border-box;position:absolute;top:300px;width:200px;height:48px;padding:0 20px;margin:0;cursor:pointer;background:#0F69BE;border-radius:4px 4px 0px 0px;box-shadow:0px 2px 12px rgba(0, 0, 0, 0.06), 0px 2px 4px rgba(0, 0, 0, 0.08);display:flex;align-items:center;justify-content:flex-start;transform:rotate(-90deg);transform-origin:bottom left;color:white;text-decoration:none;z-index:9999;" data-width="320" data-height="500" target="_blank"> <span class="icon" style="width:32px;position:relative;text-align:center;transform:rotate(90deg) scale(0.85);left:-8px;"> <svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg' style="margin-top:10px;"> <path d='M21 0H0V9L10.5743 24V16.5H21C22.6567 16.5 24 15.1567 24 13.5V3C24 1.34325 22.6567 0 21 0ZM7.5 9.75C6.672 9.75 6 9.07875 6 8.25C6 7.42125 6.672 6.75 7.5 6.75C8.328 6.75 9 7.42125 9 8.25C9 9.07875 8.328 9.75 7.5 9.75ZM12.75 9.75C11.922 9.75 11.25 9.07875 11.25 8.25C11.25 7.42125 11.922 6.75 12.75 6.75C13.578 6.75 14.25 7.42125 14.25 8.25C14.25 9.07875 13.578 9.75 12.75 9.75ZM18 9.75C17.172 9.75 16.5 9.07875 16.5 8.25C16.5 7.42125 17.172 6.75 18 6.75C18.828 6.75 19.5 7.42125 19.5 8.25C19.5 9.07875 18.828 9.75 18 9.75Z' fill='white' /> </svg> </span> <span style="text-decoration:none;font-size:18px;font-family:Helvetica,Arial,sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;"> Suggestions? </span> </a> </div> <script> (function() { var qs,js,q,s,d=document, gi=d.getElementById, ce=d.createElement, gt=d.getElementsByTagName, id="typef_orm_share", b="https://embed.typeform.com/"; if(!gi.call(d,id)){ js=ce.call(d,"script"); js.id=id; js.src=b+"embed.js"; q=gt.call(d,"script")[0]; q.parentNode.insertBefore(js,q) } })() </script>
|
|
|
|
|
|
<div class="footer">
|
|
<div class="footerContainer">
|
|
<img src="https://www.pathcheck.org/hubfs/pathcheck-foundation-logo-white.svg" alt="pathcheck-foundation-logo-white" width="264" style="width: 264px; max-width: 264px; margin: 0px 0px 5px;">
|
|
<span style="font-size: 14px;">
|
|
<br>©2021. PathCheck Foundation – a 501(c)(3) nonprofit.
|
|
<br>Content is distributed under the Creative Commons CC-BY License unless otherwise stated. PathCheck is a trademark of PathCheck Foundation.
|
|
</span>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function e(elem) { return document.getElementById(elem); }
|
|
|
|
function qrParams(elem) { return {margin:0, width:Math.min(e(elem).scrollWidth,500)-20, errorCorrectionLevel: 'Q', color: {dark: '#3654DD' }}; }
|
|
|
|
var scanner = new Instascan.Scanner({ video: document.getElementById('preview'), mirror: false });
|
|
scanner.addListener('scan', function (content) {
|
|
onScanSuccess(content);
|
|
});
|
|
scanner.addListener('active', function (content) {
|
|
onCameraOn(content);
|
|
})
|
|
scanner.addListener('inactive', function (content) {
|
|
onCameraOff(content);
|
|
})
|
|
|
|
const monthNames = [ " ", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
|
|
|
|
function parseDate(str) {
|
|
console.log(str)
|
|
if(!/^(\d){8}$/.test(str)) return "invalid date";
|
|
var y = str.substr(0,4),
|
|
m = str.substr(4,2),
|
|
d = str.substr(6,2);
|
|
return monthNames[parseInt(m)] + ' ' + d + ', ' + y;
|
|
}
|
|
|
|
// Date in seconds from epoch
|
|
function parseDInt(str) {
|
|
const d = new Date(parseInt(str) * 1000);
|
|
return monthNames[parseInt(d.getMonth()+1)] + ' ' + d.getDate() + ', ' + d.getFullYear();
|
|
}
|
|
|
|
function base32ToNumber(base32String) {
|
|
return parseInt(base32String, 32);
|
|
}
|
|
|
|
function parseISODate(str) {
|
|
const d = new Date(str);
|
|
return monthNames[parseInt(d.getMonth()+1)] + ' ' + d.getDate() + ', ' + d.getFullYear();
|
|
}
|
|
|
|
function short(hash) {
|
|
return hash.substr(0,10) + ".." + hash.substr(hash.length-10,10)
|
|
}
|
|
|
|
function verifyQRCode(data) {
|
|
gtag('event', 'universalverifyQR');
|
|
if (!data)
|
|
data = e("qr-verify").value;
|
|
|
|
console.log(data);
|
|
|
|
// Update the page's address without causing a reload
|
|
window.location.hash = '#processed'
|
|
certView();
|
|
|
|
if (data === "" || data == null) {
|
|
displayCertificateNotFound(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("CRED:")) {
|
|
verifyDisplayCRED(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("CBLD:")) {
|
|
verifyDisplayCBOR(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("HC1:")) {
|
|
verifyDisplayEU(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("JXT:")) {
|
|
verifyDisplayJXT(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("shc:")) {
|
|
verifyDisplaySHC(data);
|
|
} else if (data.startsWith("PK")) {
|
|
unZipVerifyDisplayDIVOC(data);
|
|
} else if (typeof(data) === 'string' && data.startsWith("{")) {
|
|
if (data.includes("did:india")) {
|
|
verifyDisplayDIVOC(data);
|
|
} else if (data.includes("icao")) {
|
|
verifyDisplayVDS(data);
|
|
} else {
|
|
verifyDisplayIBM(data);
|
|
}
|
|
} else {
|
|
displayCertificateUnsupported(data);
|
|
}
|
|
}
|
|
|
|
function displayCertificateUnsupported(data) {
|
|
e('qr-verify-title').innerHTML = '';
|
|
e('qr-verify-name').innerHTML = '';
|
|
let canvas = e('qr');
|
|
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
|
|
e('qr-verify-verified').innerHTML = "Certificate not found.";
|
|
}
|
|
|
|
function displayCertificateUnsupported(data) {
|
|
e('qr-verify-title').innerHTML = '';
|
|
e('qr-verify-name').innerHTML = '';
|
|
let canvas = e('qr');
|
|
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
|
|
e('qr-verify-verified').innerHTML = "Unsupported Certificate.";
|
|
}
|
|
|
|
async function verifyDisplayCRED(data) {
|
|
let uri = data.substring(data.indexOf("CRED:"));
|
|
|
|
const [schema, type, version, signatureBase32NoPad, pubKeyLink, payloadStr] = await CRED.unpack(uri);
|
|
const payload = await CRED.unpackAndVerify(uri);
|
|
const fields = await CRED.mapHeaders(payload, type, version);
|
|
|
|
QRCode.toCanvas(e('qr'), uri, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr',payload);
|
|
});
|
|
|
|
if (payload) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + pubKeyLink + (fields['date'] || fields['issuanceDate'] ? ' on ' + parseDate(fields['date'] ? fields['date'] : fields['issuanceDate']) : '');
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid or Not Supported";
|
|
}
|
|
|
|
if (type == 'BADGE') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = (fields['name'] ? fields['name'] : short(fields['passkey'])) + "<br>";
|
|
e('qr-verify-name').innerHTML += fields['dob'] ? parseDate(fields['dob']) : "" + "<br>";
|
|
} else if (type == 'COUPON') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Coupon"
|
|
e('qr-verify-name').innerHTML = "Coupon " + fields['number'] + "/" + fields['total'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "<small>Phase " + fields['phase'] + " in " + fields['city'] + "</small><br>";
|
|
} else if (type == 'STATUS') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Pass"
|
|
e('qr-verify-name').innerHTML = "Status: " + (fields['status'] === '2' ? "Vaccinated" : fields['status'] === '1' ? "One Dose" : "Not Vaccinated") + "<br />";
|
|
e('qr-verify-name').innerHTML += (fields['initials'] ? "Initials: " + fields['initials'] : "Key: " + short(fields['passkey'])) + "<br>";
|
|
} else if (type == 'PASSKEY') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 PassKey"
|
|
e('qr-verify-name').innerHTML = "Name: " + fields['name'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDate(fields['dob']);
|
|
} else if (type == 'LIBERTY') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Pass"
|
|
e('qr-verify-name').innerHTML = fields['credentialSubject.subject.name.given'] + " " + fields['credentialSubject.subject.name.family'] + "<br>";
|
|
e('qr-verify-name').innerHTML += parseDate(fields['credentialSubject.subject.birthDate']);
|
|
} else if (type == 'COWIN') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = fields['credentialSubject.name'] + "<br>";
|
|
e('qr-verify-name').innerHTML += fields['credentialSubject.age'] + "yrs old";
|
|
} else if (type == 'DGC') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Certificate"
|
|
e('qr-verify-name').innerHTML = fields['nam.gn'] + " " + fields['nam.fn'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDInt(base32ToNumber(fields['iat']) + base32ToNumber(fields['dob'])*60*60*24);
|
|
} else if (type == 'DGC.VAX') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Certificate"
|
|
e('qr-verify-name').innerHTML = fields['nam.gn'] + " " + fields['nam.fn'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDInt(fields['dob']);
|
|
} else if (type == 'DGC.TEST') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Test Certificate"
|
|
e('qr-verify-name').innerHTML = fields['nam.gn'] + " " + fields['nam.fn'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDInt(fields['dob']);
|
|
} else if (type == 'DGC.RECV') {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Recovery Certificate"
|
|
e('qr-verify-name').innerHTML = fields['nam.gn'] + " " + fields['nam.fn'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDInt(fields['dob']);
|
|
} else if (type == 'VIAL') {
|
|
e('qr-verify-title').innerHTML = "Vaccine Vial"
|
|
e('qr-verify-name').innerHTML = fields['manuf'] + " " + fields['product'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "Lot: #" + fields['lot'];
|
|
} else if (type == 'US.MA.ID') {
|
|
e('qr-verify-title').innerHTML = "Mass ID (demo)"
|
|
e('qr-verify-name').innerHTML = fields['dad'] + " " + fields['dct'] + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + parseDate(fields['dbc']);
|
|
} else if (type == 'BANKNOTE') {
|
|
e('qr-verify-title').innerHTML = "Bank Note (demo)"
|
|
e('qr-verify-name').innerHTML = fields['denomination'] + " " + fields['curency-name'] + "<br>";
|
|
e('qr-verify-name').innerHTML += fields['country'];
|
|
} else {
|
|
e('qr-verify-title').innerHTML = "Unsupported Credential"
|
|
e('qr-verify-name').innerHTML = "Credential Type " + type + " undefined";
|
|
}
|
|
}
|
|
|
|
function verifyDisplayEU(data) {
|
|
DCC.unpackAndVerify(data).then(obj => {
|
|
let result = obj;
|
|
|
|
QRCode.toCanvas(e('qr'), data, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr',result);
|
|
});
|
|
|
|
if (result == null) {
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
} else if (result) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + obj.get(1) + ' on ' + parseDInt(obj.get(6));
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
|
|
let certificate = DCC.parseCWT(obj).then(certificate => {
|
|
if (certificate.v) {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Certificate"
|
|
} else if (certificate.t) {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Test Certificate"
|
|
} else if (certificate.r) {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Recovery Certificate"
|
|
} else {
|
|
// Uruguay
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Certificate"
|
|
}
|
|
|
|
if (certificate.nam) {
|
|
// EU and UK
|
|
e('qr-verify-name').innerHTML = certificate.nam.gn + " " + certificate.nam.fn + "<br>";
|
|
e('qr-verify-name').innerHTML += "DOB: " + certificate.dob ;
|
|
} else {
|
|
console.log(certificate);
|
|
// URUGUAY
|
|
e('qr-verify-name').innerHTML = certificate.Name + "<br>";
|
|
e('qr-verify-name').innerHTML += "Doc: " + certificate.DocumentType + " " + certificate.DocumentNumber ;
|
|
}
|
|
|
|
});
|
|
});
|
|
}
|
|
|
|
function unZipVerifyDisplayDIVOC(data) {
|
|
DIVOC.unpack(data).then(function (contents) {
|
|
console.log("json", contents);
|
|
verifyDisplayDIVOC(contents);
|
|
});
|
|
}
|
|
|
|
function verifyDisplayDIVOC(json) {
|
|
DIVOC.verify(json).then(result => {
|
|
DIVOC.pack(json).then(function (bin) {
|
|
QRCode.toCanvas(e('qr'), bin, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr',result);
|
|
});
|
|
});
|
|
|
|
if (result == null) {
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
} else if (result) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + json.evidence[0].facility.name + ' on ' + parseISODate(json.issuanceDate);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = json.credentialSubject.name + "<br>";
|
|
e('qr-verify-name').innerHTML += json.credentialSubject.age + "yrs old";
|
|
});
|
|
}
|
|
|
|
function verifyDisplayVDS(json) {
|
|
VDS.unpackAndVerify(json).then(result => {
|
|
VDS.pack(result).then(function (bin) {
|
|
QRCode.toCanvas(e('qr'), bin, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr',result);
|
|
});
|
|
});
|
|
|
|
if (result == null) {
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
} else if (result) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + result.data.hdr.is;
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = result.data.msg.pid.n + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + result.data.msg.pid.dob;
|
|
});
|
|
}
|
|
|
|
function verifyDisplayCBOR(uri) {
|
|
BBS_CBLD.unpackAndVerify(uri).then(json => {
|
|
QRCode.toCanvas(e('qr'), uri, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr', json);
|
|
});
|
|
if (json == null) {
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
} else if (json) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + json.issuer + ' on ' + parseISODate(json.issuanceDate);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = json.credentialSubject.personalInformation.givenName + " " + json.credentialSubject.personalInformation.familyName + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + json.credentialSubject.personalInformation.birthDate;
|
|
});
|
|
}
|
|
|
|
function verifyDisplaySHC(uri) {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
|
|
SHC.unpackAndVerify(uri).then(json => {
|
|
if (json == null) return;
|
|
|
|
QRCode.toCanvas(e('qr'), uri, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr', json);
|
|
});
|
|
|
|
let iss = json.iss;
|
|
if (iss === 'https://myvaccinerecord.cdph.ca.gov/creds')
|
|
iss = "State of California"
|
|
|
|
if (json) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + iss + ' on ' + parseDInt(json.nbf);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
let pat = json.vc.credentialSubject.fhirBundle.entry.filter(entry => entry.resource.resourceType === "Patient");
|
|
let name = pat[0].resource.name[0].given.join(' ') + " " + pat[0].resource.name[0].family;
|
|
let dob = pat[0].resource.birthDate;
|
|
|
|
e('qr-verify-name').innerHTML = name + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + dob;
|
|
});
|
|
}
|
|
|
|
function verifyDisplayJXT(uri) {
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
|
|
BBS_JXT.unpackAndVerify(uri).then(json => {
|
|
if (json == null) return;
|
|
|
|
QRCode.toCanvas(e('qr'), uri, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr', json);
|
|
});
|
|
|
|
if (json) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + json.issuer + ' on ' + parseISODate(json.issuanceDate);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-name').innerHTML = json.credentialSubject.personalInformation.givenName + " " + json.credentialSubject.personalInformation.familyName + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + json.credentialSubject.personalInformation.birthDate;
|
|
});
|
|
|
|
EDDSA_JXT.unpackAndVerify(uri).then(json => {
|
|
if (json == null) return;
|
|
|
|
QRCode.toCanvas(e('qr'), uri, qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr', json);
|
|
});
|
|
|
|
if (json) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + json.issuer + ' on ' + parseISODate(json.issuanceDate);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Record"
|
|
e('qr-verify-name').innerHTML = json.credentialSubject.personalInformation.givenName + " " + json.credentialSubject.personalInformation.familyName + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + json.credentialSubject.personalInformation.birthDate;
|
|
});
|
|
}
|
|
|
|
function verifyDisplayIBM(json) {
|
|
//IBM.verify(json).then(result => {
|
|
let result = null;
|
|
|
|
//IBM.pack(json).then(function (bin) {
|
|
QRCode.toCanvas(e('qr'), JSON.stringify(json), qrParams('qr'), function (error) {
|
|
UIUtils.drawVerifiedSymbol('qr',result);
|
|
});
|
|
//});
|
|
|
|
if (result == null) {
|
|
e('qr-verify-verified').innerHTML = "Unable to Verify";
|
|
} else if (result) {
|
|
e('qr-verify-verified').innerHTML = 'Signed by ' + json.evidence[0].facility.name + ' on ' + parseISODate(json.issuanceDate);
|
|
} else {
|
|
e('qr-verify-verified').innerHTML = "Credential Invalid";
|
|
}
|
|
|
|
e('qr-verify-title').innerHTML = "COVID-19 Vaccine Pass"
|
|
e('qr-verify-name').innerHTML = json.credentialSubject.subject.name.given + " " + json.credentialSubject.subject.name.family + "<br>";
|
|
e('qr-verify-name').innerHTML += "DoB: " + json.credentialSubject.subject.birthDate;
|
|
//});
|
|
}
|
|
|
|
function onScanSuccess(qrMessage) {
|
|
stopCamera();
|
|
|
|
if (qrMessage !== "" && qrMessage != null) {
|
|
e("qr-verify").value = qrMessage;
|
|
verifyQRCode(qrMessage);
|
|
}
|
|
}
|
|
|
|
function onScanFailure(error) {
|
|
console.warn(`QR error = ${error}`);
|
|
}
|
|
|
|
function getQueryVariable(variable) {
|
|
var query = window.location.search.substring(1);
|
|
var vars = query.split('&');
|
|
for (var i = 0; i < vars.length; i++) {
|
|
var pair = vars[i].split('=');
|
|
if (pair[0] == variable) {
|
|
return pair[1];
|
|
}
|
|
}
|
|
console.log('Query variable %s not found', variable);
|
|
}
|
|
|
|
function toggleCamera() {
|
|
if (e('camera-btn').innerHTML === "Go") {
|
|
startCamera();
|
|
} else {
|
|
stopCamera();
|
|
}
|
|
}
|
|
|
|
function onCameraOn() {
|
|
e('camera-btn').innerHTML = "Stop";
|
|
}
|
|
function onCameraOff() {
|
|
e('camera-btn').innerHTML = "Go";
|
|
}
|
|
|
|
function startCamera() {
|
|
Instascan.Camera.getCameras().then(function (cameras) {
|
|
if (cameras.length > 0) {
|
|
scanner.start(cameras[document.querySelector('input[name="camera"]:checked').id.substr(3)]);
|
|
} else {
|
|
console.error('No cameras found.');
|
|
}
|
|
}).catch(function (e) {
|
|
console.error(e);
|
|
});
|
|
}
|
|
|
|
function stopCamera() {
|
|
scanner.stop();
|
|
e('preview').pause();
|
|
e('preview').removeAttribute('src'); // empty source
|
|
e('preview').load();
|
|
}
|
|
|
|
function homeView() {
|
|
e("pre-verify-section").style.display = '';
|
|
e("post-verify-section").style.display = 'none';
|
|
}
|
|
|
|
function certView() {
|
|
e("pre-verify-section").style.display = 'none';
|
|
e("post-verify-section").style.display = '';
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
// Loading URI from qr parameter.
|
|
const queryString = window.location.search;
|
|
|
|
const qr = getQueryVariable("qr")
|
|
|
|
if (qr !== "" && qr != null) {
|
|
console.log(qr);
|
|
e("qr-verify").value = qr;
|
|
verifyQRCode(qr);
|
|
}
|
|
|
|
Instascan.Camera.getCameras().then(function (cameras) {
|
|
let selectedCamIndex = 0;
|
|
cameras.forEach(function(camera, index) {
|
|
if (camera.name) {
|
|
if (camera.name.toLowerCase().indexOf('back') != -1) {
|
|
selectedCamIndex = index;
|
|
}
|
|
if (camera.name.toLowerCase().indexOf('usb') != -1) {
|
|
selectedCamIndex = index;
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log("Camera Index", selectedCamIndex);
|
|
|
|
cameras.forEach(function(camera, index) {
|
|
let newInput = document.createElement("input");
|
|
newInput.setAttribute('class',"selectopt");
|
|
newInput.setAttribute('name',"camera");
|
|
newInput.setAttribute('type',"radio");
|
|
newInput.setAttribute('id',"opt"+index);
|
|
|
|
console.log(camera);
|
|
|
|
if (index == selectedCamIndex)
|
|
newInput.setAttribute('checked',true);
|
|
|
|
e('cameras').appendChild(newInput);
|
|
|
|
newInput = document.createElement("label");
|
|
newInput.setAttribute('for',"opt"+index);
|
|
newInput.setAttribute('class',"option");
|
|
newInput.innerHTML = camera.name;
|
|
|
|
e('cameras').appendChild(newInput);
|
|
});
|
|
});
|
|
|
|
window.onpopstate = function() {
|
|
switch(location.hash) {
|
|
case '#processed':
|
|
certView();
|
|
break
|
|
default:
|
|
homeView();
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|
|
|