diff --git a/src/js/crypto.js b/src/js/crypto.js index db11a8c..b0c485b 100644 --- a/src/js/crypto.js +++ b/src/js/crypto.js @@ -274,19 +274,30 @@ function calculateSHA256HashOfString(str) { } function openPublicKeyFile() { - openFileDialog(function (path) { - var keyfile = getFileAsString(path); + openFileDialog(function (path, html5file) { + var importpk = function (keyfile) { + kbpgp.KeyManager.import_from_armored_pgp({ + armored: keyfile + }, function (err, pubkeymgr) { + if (!err) { + keyring.add_key_manager(pubkeymgr); + alert("Public key file loaded. You can now analyze PDFs signed by the key's owner."); + } else { + alert("Error loading public key: " + err); + } + }); + }; - kbpgp.KeyManager.import_from_armored_pgp({ - armored: keyfile - }, function (err, pubkeymgr) { - if (!err) { - keyring.add_key_manager(pubkeymgr); - alert("Public key file loaded. You can now analyze PDFs signed by the key's owner."); - } else { - alert("Error loading public key: " + err); + if (typeof nw != 'undefined') { + var keyfile = getFileAsString(path); + importpk(keyfile); + } else { + var fileReader = new FileReader(); + fileReader.onload = function (e) { + importpk(e.target.result); } - }); + fileReader.readAsText(html5file); + } }, ".asc"); } diff --git a/src/js/filesystem.js b/src/js/filesystem.js index 4db9347..2785305 100644 --- a/src/js/filesystem.js +++ b/src/js/filesystem.js @@ -27,7 +27,7 @@ function openFileDialog(callback, accept) { dialog.setAttribute("accept", accept); } dialog.onchange = function () { - callback(dialog.value); + callback(dialog.value, this.files[0]); } dialog.dispatchEvent(new MouseEvent("click", { "view": window, diff --git a/src/js/pdf.js b/src/js/pdf.js index 911a94b..c55eca3 100644 --- a/src/js/pdf.js +++ b/src/js/pdf.js @@ -56,42 +56,84 @@ function analyzeSignedPDF() { return; } closePDF(false); - openFileDialog(function (path) { - var pdf = Buffer.from(getFileAsUint8Array(path).buffer); + openFileDialog(function (path, html5file) { + var analyze = function (pdf) { + var splitindex = pdf.indexOf("-----BEGIN PGP MESSAGE-----"); + if (splitindex == -1) { + alert("Selected file does not contain any recognized signature data."); + return; + } + var pdfdata = pdf.slice(0, splitindex); + var sigdata = pdf.slice(splitindex).toString(); - var splitindex = pdf.indexOf("-----BEGIN PGP MESSAGE-----"); - if (splitindex == -1) { - alert("Selected file does not contain any recognized signature data."); - return; - } - var pdfdata = pdf.slice(0, splitindex); - var sigdata = pdf.slice(splitindex).toString(); + var verify = function (pdfhash) { + loadKeyFromLocalStorage(function () { + verifyMessage(sigdata, function (msg, fprint) { + parseAndDisplaySignature(msg, pdfhash, true, fprint); + }, function (err) { + console.error(err); + console.log(sigdata); + var base64 = sigdata.split("\n\n", 2)[1].split("\n-----END PGP MESSAGE-----")[0]; + base64 = base64.substring(0, base64.lastIndexOf("\n")).replaceAll("\n", ""); + try { + var msg = window.atob(base64).split("START", 2)[1].split("END", 2)[0]; + parseAndDisplaySignature(msg, pdfhash, false, null); + } catch (ex) { + console.error(ex); + alert("Error: could not parse signature data."); + } + }); + }); - var pdfhash = calculateSHA256HashOfString(pdfdata); + if (typeof nw != 'undefined') { + pdfjsLib.getDocument(pdf).promise.then(function (pdfDoc_) { + pdfDoc = pdfDoc_; - loadKeyFromLocalStorage(function () { - verifyMessage(sigdata, function (msg, fprint) { - parseAndDisplaySignature(msg, pdfhash, true, fprint); - }, function (err) { - console.error(err); - var base64 = sigdata.split("\n\n", 2)[1].split("\n-----END PGP MESSAGE-----")[0]; - base64 = base64.substring(0, base64.lastIndexOf("\n")).replaceAll("\n", ""); - try { - var msg = window.atob(base64).split("START", 2)[1].split("END", 2)[0]; - parseAndDisplaySignature(msg, pdfhash, false, null); - } catch (ex) { - console.error(ex); - alert("Error: could not parse signature data."); + renderAllPages(pdfDoc); + pdfZoom("fitheight"); + }); + } else { + var fileReader = new FileReader(); + fileReader.onload = function () { + pdfjsLib.getDocument(new Uint8Array(this.result)).promise.then(function (pdfDoc_) { + pdfDoc = pdfDoc_; + + renderAllPages(pdfDoc); + pdfZoom("fitheight"); + }); + }; + fileReader.readAsArrayBuffer(html5file); } - }); - }); + }; - pdfjsLib.getDocument(pdf).promise.then(function (pdfDoc_) { - pdfDoc = pdfDoc_; - - renderAllPages(pdfDoc); - pdfZoom("fitheight"); - }); + if (typeof nw != 'undefined') { + verify(calculateSHA256HashOfString(pdfdata)); + } else { + window.crypto.subtle.digest("SHA-256", (new TextEncoder()).encode(pdfdata)) + .then(hash => { + window.hash = hash; + // here hash is an arrayBuffer, + // so we'll connvert it to its hex version + let result = ''; + const view = new DataView(hash); + for (let i = 0; i < hash.byteLength; i += 4) { + result += ('00000000' + view.getUint32(i).toString(16)).slice(-8); + } + verify(result); + }); + } + }; + if (typeof nw != 'undefined') { + // running in NW.js so we have Node + analyze(Buffer.from(getFileAsUint8Array(path).buffer)); + } else { + // no Node :( + var fileReader = new FileReader(); + fileReader.onload = function (e) { + analyze(e.target.result); + } + fileReader.readAsBinaryString(html5file); + } }, ".pdf"); } @@ -143,25 +185,33 @@ then run the analyze tool again to prove if it was changed since notarization.") $("#verifyModalDetailedInfoList").append('
  • State: ' + sanitizeHTMLString(msgparts["STATE"]).toUpperCase() + '
  • '); } if (typeof msgparts["OTS"] == "string") { - var bytearray = []; - var bytestrarray = msgparts["OTS"].match(/.{1,3}/g); - for (var i = 0; i < bytestrarray.length; i++) { - bytearray.push(bytestrarray[i] * 1); - } - const detached = OpenTimestamps.DetachedTimestampFile.fromHash(new OpenTimestamps.Ops.OpSHA256(), Uint8Array.from(Buffer.from(pdfhash, 'hex'))); - const detachedOts = OpenTimestamps.DetachedTimestampFile.deserialize(bytearray); - let options = {}; - OpenTimestamps.verify(detachedOts, detached, options).then(verifyResult => { - console.log(verifyResult); - if (typeof verifyResult != "undefined") { - if (typeof verifyResult.bitcoin != undefined) { - $("#verifyModalDetailedInfoList").append('
  • Signing time independently verified on the Bitcoin blockchain. Signed at ' + formatTimestamp("F j, Y g:i a", verifyResult.bitcoin.timestamp) + '
  • '); - } - if (typeof verifyResult.litecoin != undefined) { - $("#verifyModalDetailedInfoList").append('
  • Signing time independently verified on the Litecoin blockchain. Signed at ' + formatTimestamp("F j, Y g:i a", verifyResult.bitcoin.timestamp) + '
  • '); - } + try { + var bytearray = []; + var bytestrarray = msgparts["OTS"].match(/.{1,3}/g); + for (var i = 0; i < bytestrarray.length; i++) { + bytearray.push(bytestrarray[i] * 1); } - }); + const detached = OpenTimestamps.DetachedTimestampFile.fromHash(new OpenTimestamps.Ops.OpSHA256(), Uint8Array.from(Buffer.from(pdfhash, 'hex'))); + const detachedOts = OpenTimestamps.DetachedTimestampFile.deserialize(bytearray); + console.log(OpenTimestamps.info(detachedOts)); + let options = { + ignoreBitcoinNode: true, + timeout: 5000 + }; + OpenTimestamps.verify(detachedOts, detached, options).then(verifyResult => { + console.log(verifyResult); + if (typeof verifyResult != "undefined") { + if (typeof verifyResult.bitcoin != undefined) { + $("#verifyModalDetailedInfoList").append('
  • Signing time independently verified on the Bitcoin blockchain. Signed at ' + formatTimestamp("F j, Y g:i a", verifyResult.bitcoin.timestamp) + '
  • '); + } + if (typeof verifyResult.litecoin != undefined) { + $("#verifyModalDetailedInfoList").append('
  • Signing time independently verified on the Litecoin blockchain. Signed at ' + formatTimestamp("F j, Y g:i a", verifyResult.bitcoin.timestamp) + '
  • '); + } + } + }); + } catch (ex) { + console.error(ex); + } } $("#verifyModalDetailedInfoList").append('
  • Actual file hash: ' + pdfhash + '
  • '); @@ -287,6 +337,12 @@ function savePDF() { function pdfZoom(str) { disableGuideBox(); + if ($("#page-canvas-container .page-canvas").length == 0) { + setTimeout(function () { + pdfZoom(str); + }, 100); + return; + } var widthpx = $("#page-canvas-container .page-canvas").css("width").replace("px", "") * 1; var zoomstep = 100; console.log(widthpx);