You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

366 lines
17 KiB

<!doctype html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/style.v2.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>NY Liberty Pass with PCF Certificates Generator</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>
</head>
<body>
<div class="topnav">
<div class="topnavContainer">
<a class="active" href="index.html">Signers</a>
<a 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="subnav">
<div class="subnavContainer">
<a href="index.v5.html">PCF<span class="xs-hidden">'s 4 QRs</span></a>
<a href="eu.dgc.html">EU<span class="xs-hidden"> Green Pass</span></a>
<a href="icao.html">ICAO<span class="xs-hidden"> Seals</span></a>
<a href="cowin.html">DIVOC<span class="xs-hidden">/India</span></a>
<a class="active" href="liberty.html">IBM/NY<span class="xs-hidden"> Excelsior</span></a>
<a class="xs-hidden" href="opencerta.html"><span class="xs-hidden">Open</span>Certa</a>
<a href="vial.html">Vial<span class="xs-hidden"> Label</span></a>
<a href="us.ma.id.html"><span class="xs-hidden">Mass </span>ID</a>
<a href="banknote.html">Cash<span class="xs-hidden"> Bills</span></a>
</div>
</div>
<div class="center">
<h1>IBM/NY Excelsior Pass</h1>
<div class="full-div">
<div class="two-quarter">
<h4>VC Overhead</h4>
<table>
<tr><td>Context</td><td><input id="qr-head-context" type="text" placeholder="https://www.w3.org/2018/credentials/v1"/></td></tr>
<tr><td>Type</td><td><input id="qr-head-type" type="text" placeholder="VerifiableCredential"/></td></tr>
<tr><td>Issue Date</td><td><input id="qr-head-issuance-date" type="text" placeholder="YYYYMMDD"/></td></tr>
<tr><td>Expiration</td><td><input id="qr-head-expiration-date" type="text" placeholder="YYYYMMDD"/></td></tr>
<tr><td>Issuer</td><td><input id="qr-head-issuer" type="text" placeholder="did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac"/></td></tr>
<tr><td>ID</td><td><input id="qr-head-id" type="text" placeholder="did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac#vc-[UUID]"/></td></tr>
</table>
<h4>Credential Schema</h4>
<table>
<tr><td>Type</td><td><input id="qr-credschema-type" type="text" placeholder="JsonSchemaValidator2018"/></td></tr>
<tr><td>Schema Id</td><td><input id="qr-credschema-id" type="text" placeholder="did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac;id=libertyhealthpass;version=0.1"></td></tr>
</table>
</div>
<div class="two-quarter">
<h4>Subject</h4>
<table>
<tr><td>FirstName</td><td><input id="qr-pat-firstname" type="text" placeholder="First Name ..."/></td></tr>
<tr><td>LastName</td><td><input id="qr-pat-lastname" type="text" placeholder="Last Name ..."/></td></tr>
<tr><td>Date</td><td><input id="qr-pat-dob" type="text" placeholder="YYYYMMDD"/></td></tr>
</table>
<h4>Credential</h4>
<table>
<tr><td>Type</td><td><input id="qr-cred-type" type="text" placeholder="Liberty HealthPass"/></td></tr>
<tr><td>Display</td><td><input id="qr-cred-display" type="text" placeholder="#99999E"/></td></tr>
<tr><td>PassType</td><td><input id="qr-cred-passtype" type="text" placeholder="COVID-19 Vaccination"></td></tr>
</table>
</div>
<div class="quarter">
<h4>Credentials</h4>
<label for="privkey">Private Key</label><br/>
<textarea id="privkey" rows="10" cols="30">-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIPWKbSezZMY1gCpvN42yaVv76Lo47FvSsVZpQl0a5lWRoAcGBSuBBAAK
oUQDQgAE6DeIun4EgMBLUmbtjQw7DilMJ82YIvOR2jz/IK0R/F7/zXY1z+gqvFXf
DcJqR5clbAYlO9lHmvb4lsPLZHjugQ==
-----END EC PRIVATE KEY-----</textarea>
<br><br>
<label for="pubkey">Public Key</label><br/>
<textarea id="qr-link" rows="1" cols="30">1A9.PCF.PW</textarea>
<br><label for="pubkey">When ready, change to: <span class='pub-key'>ISSUER#key-1</span></label><br/>
</div>
</div>
<div class="full-div"></div>
<div class="four-quarter">
<br>
<button class="qr-btn" onclick="generateQRCodes()">Generate Credentials</button>
<br>
</div>
<div class="full-div">
<div class="two-quarter">
<h4>PCF's QR Format</h4>
<canvas id="qr-ibm-code"></canvas>
<h4 id="qr-ibm-pdf-label" style="display: none;">PDF 417 Format</h4>
<canvas id="qr-ibm-pdf"></canvas>
<pre id="qr-ibm-result"></pre>
<pre id="qr-ibm-bytes" class="xs-hidden"></pre>
</div>
<div class="two-quarter">
<h4>Original IBM Format</h4>
<canvas id="qr-ibm-orig-code"></canvas>
<h4 id="qr-ibm-orig-pdf-label" style="display: none;">PDF 417 Format</h4>
<canvas id="qr-ibm-orig-pdf" style="display: none;"></canvas>
<pre id="qr-ibm-orig-result" style="display: none;">
{
"@context": ["https://www.w3.org/2018/credentials/v1"],
"id": "did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac#vc-SOME-LONG-UUID-THAT-LOOKS-LIKE-THIS",
"type": ["VerifiableCredential"],
"issuer": "did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac",
"issuanceDate": "2021-02-28T11:31:10Z",
"expirationDate": "2021-02-28T02:21:39Z",
"credentialSchema": {
"id": "did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac;id=libertyhealthpass;version=0.1",
"type": "JsonSchemaValidator2018"
},
"credentialSubject": {
"display": "#999999E",
"passType": "COVID-19 Vaccination",
"subject": {
"birthDate": "1981-01-01",
"name": {
"family": "Doe",
"given": "Jane"
}
},
"type": "Liberty HealthPass"
},
"proof": {
"created": "2021-02-28T12:12:40Z",
"creator": "did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac#key-1",
"nonce": "SOME-LONG-UUID-THAT-LOOKS-LIKE-THIS",
"signatureValue": "SOME-SUPER-SECURE-BUT-EXTREMELY-LONG-SIGNATURE-THAT-CONTAINS-AT-LEAST-THIS-MANY-CHARACTERS-IN-IT",
"type": "EcdsaSecp256r1Signature2019"
}
}
</pre>
<pre id="qr-ibm-orig-bytes" class="xs-hidden"></pre>
</div>
<div class="quarter xs-hidden">
<label for="verify">Verify a QR Code</small></label><br/>
<textarea id="qr-verify" rows="10" cols="30" placeholder="cred:type:version:signature:pubkey:payload"></textarea>
<br><br>
<button class="qr-btn" onclick="verifyQRCode()">Verify</button>
<br><br>
<pre id="qr-verify-result"></pre>
<pre id="qr-verify-verified"></pre>
</div>
</div>
</div>
<script src="js/qrcode.min.js"></script>
<script src="js/base32.min.js"></script>
<script src="js/libbcmath.js" type="text/javascript"></script>
<script src="js/bcmath.js" type="text/javascript"></script>
<script src="js/pdf417.js" type="text/javascript"></script>
<script src="js/pcf-utils.js"></script>
<script src="js/ui-utils.js"></script>
<script src="js/pcf.sdk.min.js"></script>
<script>
function e(elem) { return document.getElementById(elem); }
function getValueArray(elemArray) {
const fields = elemArray.map(function(elemId) {
return e(elemId).value;
})
return fields;
}
function signAndDisplayQR(elemPref, _type, _version, priKeyPEM, pubKeyId, payloadValueArray) {
PCF.signAndPack(_type, _version, priKeyPEM, pubKeyId, payloadValueArray).then(uri => {
PCFUtils.debugURI(uri).then( debugInfo => {
UIUtils.drawsQR(elemPref, uri, debugInfo);
PCF.unpackAndVerify(uri).then(result => {
UIUtils.drawVerifiedSymbol(elemPref+'-code',result);
});
});
});
}
function parse(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 new Date(y,m-1,d);
}
// Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
function pad(base32Str) {
switch (base32Str.length % 8) {
case 2: return base32Str + "======";
case 4: return base32Str + "====";
case 5: return base32Str + "===";
case 7: return base32Str + "=";
}
return base32Str;
}
function rmPad(base32Str) {
return base32Str.replace(/=/g, '');
}
function didHexToBase32(did) {
[did, schema, one, two] = did.split(":");
oneB32 = rmPad(base32.encode(hexToBytes(one)));
twoB32 = rmPad(base32.encode(hexToBytes(two)));
return [did, schema, oneB32, twoB32].join(":");
}
function didBase32ToHex(did) {
[did, schema, one, two] = did.split(":");
oneHex = bytesToHex(base32.decode.asBytes(pad(one)));
twoHex = bytesToHex(base32.decode.asBytes(pad(two)));
return [did, schema, oneHex, twoHex].join(":");
}
function generateQRCodes() {
gtag('event', 'generateQR');
// Where to Download the public key
const pubKeyLink = e("qr-link").value.trim().replace("http://","");
// PEM code of the private key
const priKeyPEM = e('privkey').value;
const fieldArray = [
"qr-pat-firstname", "qr-pat-lastname", "qr-pat-dob",
"qr-cred-display", "qr-cred-passtype", "qr-head-issuance-date",
"qr-head-expiration-date", "qr-head-issuer", "qr-head-id"
];
signAndDisplayQR("qr-ibm", "liberty", "1", priKeyPEM, pubKeyLink, getValueArray(fieldArray));
let issuerHex = didBase32ToHex(e("qr-head-issuer").value);
let ibmW3C = {
"@context": e("qr-head-context").value,
"id": issuerHex + "#vc-" + e("qr-head-id").value,
"issuer": issuerHex,
"type": [e("qr-head-type").value],
"issuanceDate": parse(e("qr-head-issuance-date").value).toJSON(),
"expirationDate": parse(e("qr-head-expiration-date").value).toJSON(),
"credentialSchema": {
"id": issuerHex + ";" + e("qr-credschema-id").value,
"type": e("qr-credschema-type").value,
},
"credentialSubject": {
"display": e("qr-cred-display").value,
"passType": e("qr-cred-passtype").value,
"subject": {
"birthDate": parse(e("qr-pat-dob").value).toJSON().substr(0,10),
"name": {
"family": e("qr-pat-lastname").value,
"given": e("qr-pat-firstname").value
}
},
"type": e("qr-cred-type").value,
},
"proof": {
"created": parse(e("qr-head-issuance-date").value).toJSON(),
"creator": issuerHex + "#key-1",
"nonce": "SOME-LONG-UUID-THAT-LOOKS-LIKE-THIS",
"signatureValue": "SOME-SUPER-SECURE-BUT-EXTREMELY-LONG-SIGNATURE-THAT-CONTAINS-AT-LEAST-THIS-MANY-CHARACTERS-IN-IT",
"type": "EcdsaSecp256r1Signature2019",
}
};
let ibmURI = JSON.stringify(ibmW3C);
UIUtils.drawsQR("qr-ibm-orig", ibmURI);
// Updates screen elements.
e("qr-ibm-orig-result").innerHTML= ibmURI;
e("qr-ibm-orig-code").style.display='block';
e("qr-ibm-orig-result").style.display='block';
}
function verifyQRCode() {
gtag('event', 'verifyQR');
PCFUtils.debugParseURI(e("qr-verify").value).then(result => {
e("qr-verify-result").innerHTML = result;
});
PCFUtils.debugVerify(e("qr-verify").value).then(debug => {
e('qr-verify-verified').innerHTML = debug;
});
}
</script>
<script>
function loadDemo() {
let issuerHex = "did:hpass:b3a918aa1ec2b0f0d58a6ca3e7c8ebb6a631cbb2408ad5e8ecaca9fdb48ee4bf:7210026a6918e3250c1d1094636c0a89cc06468cf302bce2216e20c755d020ac";
e("qr-head-context").value = "https://www.w3.org/2018/credentials/v1";
e("qr-head-issuance-date").value = "20210228";
e("qr-head-type").value = "VerifiableCredential";
e("qr-head-expiration-date").value = "20210328";
e("qr-head-issuer").value = didHexToBase32(issuerHex);
e("qr-head-id").value = "SOME-LONG-UUID-THAT-LOOKS-LIKE-THIS";
e("qr-pat-firstname").value = "Jane";
e("qr-pat-lastname").value = "Doe";
e("qr-pat-dob").value = "19810101";
e("qr-cred-type").value = "Liberty HealthPass";
e("qr-cred-display").value = "#999999E";
e("qr-cred-passtype").value = "COVID-19 Vaccination";
e("qr-credschema-type").value = "JsonSchemaValidator2018";
e("qr-credschema-id").value = "id=libertyhealthpass;version=0.1";
}
loadDemo();
</script>
<script>
async function preloadKey() {
PCF.resolveKey(e("qr-link").value);
}
window.onload = function() {
preloadKey();
}
</script>
</body>
</html>