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

315 lines
10 KiB
JavaScript

/*
* Copyright 2021 Netsyms Technologies.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var keymgr;
var keyring = new kbpgp.keyring.KeyRing();
/**
* Load and unlock the private key in localstorage, prompting user as needed. If there is no key, generates, saves, and loads a new one.
* @param {function} callback Passed two arguments: message for user, and boolean true if OK false if error.
* @returns {undefined}
*/
function loadKeyFromLocalStorage(callback) {
if (typeof keymgr != "undefined") {
callback("Key already loaded.", true);
return;
}
$("#lockstatus").css("display", "none");
if (!inStorage("signingkey") || getStorage("signingkey") == "undefined") {
var pass = prompt("Generating a new signing key (might take a while, be patient). Enter a password to protect it. You'll need to save this password somewhere safe; it cannot be recovered.");
generatePrivateKey(getStorage("notary_name") + " <null@null.com>", pass, function (key) {
if (typeof key == "undefined") {
callback("Could not generate key.", false);
return;
}
keymgr = key;
keyring.add_key_manager(keymgr);
setStorage("signingkey", keymgr.armored_pgp_private);
callback("Signing key generated.", true);
});
} else {
var pass = prompt("Enter password to unlock signing key:");
loadPrivateKey(getStorage("signingkey"), pass, function (key) {
if (typeof key == "undefined") {
callback("Could not unlock key. Password is probably incorrect.", false);
return;
}
keymgr = key;
keyring.add_key_manager(keymgr);
callback("Signing key unlocked.", true);
});
}
}
function unloadKey() {
keymgr = undefined;
$("#lockstatus").css("display", "");
showToast("<i class='fas fa-lock'></i> Signing key locked.");
}
function loadKeyFromLocalStorageWithUserFeedback() {
loadKeyFromLocalStorage(function (msg, ok) {
if (ok) {
showToast("<i class='fas fa-unlock'></i> " + msg);
} else {
showAlert("Error: " + msg);
}
});
}
/**
* Load a private key.
* @param {string} armoredkey PGP private key
* @param {string} pass key password
* @param {function} callback Passed a new keymanager for the key.
* @returns {undefined}
*/
function loadPrivateKey(armoredkey, pass, callback) {
kbpgp.KeyManager.import_from_armored_pgp({
armored: armoredkey
}, function (err, key) {
if (!err) {
if (key.is_pgp_locked()) {
key.unlock_pgp({
passphrase: pass
}, function (err) {
if (!err) {
console.log("Loaded private key with passphrase");
callback(key);
} else {
console.error(err);
callback(undefined);
}
});
} else {
console.log("Loaded private key w/o passphrase");
callback(key);
}
} else {
console.error(err);
callback(undefined);
}
});
}
/**
* Sign a message with a key and return the signed message in the callback.
* @param {type} text
* @param {type} key
* @param {type} callback
* @returns {undefined}
*/
function signMessage(text, key, callback) {
var params = {
msg: text,
sign_with: key
};
kbpgp.box(params, function (err, result_string, result_buffer) {
//console.log(err, result_string, result_buffer);
callback(result_string);
});
}
/**
* Read a signed PGP message and return the contents and signer's fingerprint/key ID.
* @param {string} pgpmsg "-----BEGIN PGP MESSAGE----- ..."
* @param {function} callback function(message, fingerprint) {}
* @param {function} onerror function(errormessage) {}
* @returns {undefined}
*/
function verifyMessage(pgpmsg, callback, onerror) {
kbpgp.unbox({keyfetch: keyring, armored: pgpmsg}, function (err, literals) {
if (err != null) {
onerror(err);
} else {
var message = literals[0].toString();
var fingerprint = null;
var ds = km = null;
ds = literals[0].get_data_signer();
if (ds) {
km = ds.get_key_manager();
}
if (km) {
fingerprint = km.get_pgp_fingerprint().toString('hex');
}
callback(message, fingerprint);
}
});
}
/**
* Generate a new private key.
* @param {string} userid Something like "Test User <test@netsyms.com>"
* @param {string} passphrase protects the key
* @param {function} callback Passed the keymanager for the new key
* @returns {undefined}
*/
function generatePrivateKey(userid, passphrase, callback) {
var statustextEl = $("#statustext");
statustextEl.html("<i class='fas fa-spin fa-spinner'></i> Generating cryptographic key...");
setTimeout(function () {
var F = kbpgp["const"].openpgp;
var opts = {
userid: userid,
primary: {
nbits: 2048,
flags: F.certify_keys | F.sign_data | F.auth | F.encrypt_comm | F.encrypt_storage,
expire_in: 0 // never expire
},
subkeys: []
};
kbpgp.KeyManager.generate(opts, function (err, alice) {
if (!err) {
alice.sign({}, function (err) {
alice.export_pgp_private({
passphrase: passphrase
}, function (err, pgp_private) {
statustextEl.html("<i class='fas fa-check'></i> Key generated!");
setTimeout(function () {
statustextEl.html("");
}, 5000);
callback(alice);
});
});
}
});
}, 100);
}
function exportPublicKey() {
loadKeyFromLocalStorage(function (message, ok) {
if (ok) {
openSaveFileDialog(function (path) {
keymgr.export_pgp_public({}, function (err, pgp_public) {
if (err) {
showAlert("Something went wrong.");
} else {
writeToFile(path, pgp_public);
}
});
}, "public-key.asc", ".asc");
} else {
showAlert("Error: " + message);
}
});
}
function exportPrivateKey() {
var pass = prompt("Enter password for private key:");
const savepriv = function (key) {
var pass2 = prompt("Enter a password to protect the key backup:");
openSaveFileDialog(function (path) {
key.export_pgp_private({
passphrase: pass2
}, function (err, pgp_private) {
if (err) {
showAlert("Something went wrong.");
} else {
writeToFile(path, pgp_private);
}
});
}, "private-key.asc", ".asc");
}
kbpgp.KeyManager.import_from_armored_pgp({
armored: getStorage("signingkey")
}, function (err, key) {
if (!err) {
if (key.is_pgp_locked()) {
key.unlock_pgp({
passphrase: pass
}, function (err) {
if (!err) {
savepriv(key);
} else {
showAlert("Could not unlock key. Password is probably incorrect.");
}
});
} else {
console.log("Loaded private key w/o passphrase");
savepriv(key);
}
} else {
showAlert("Could not unlock key: " + err);
}
});
}
function importPrivateKey() {
if (inStorage("signingkey") && getStorage("signingkey") != "undefined") {
if (!confirm("The restored key will replace the current key, which will be unrecoverable unless you made a backup. Continue?")) {
return;
}
}
keymgr = null;
openFileDialog(function (path) {
var keyfile = getFileAsString(path);
var pass = prompt("Enter password for imported key (password was set when exported):");
loadPrivateKey(keyfile, pass, function (key) {
if (typeof key == "undefined") {
showAlert("Could not import key. Password is probably incorrect.");
return;
}
keymgr = key;
setStorage("signingkey", keymgr.armored_pgp_private);
showAlert("Private key imported.");
});
}, ".asc");
}
function calculateSHA256HashOfBuffer(buffer) {
const hasha = require('hasha');
var hashstr = hasha(Buffer.from(buffer), {algorithm: 'sha256'});
return hashstr;
}
function calculateSHA256HashOfString(str) {
const hasha = require('hasha');
var hashstr = hasha(str, {algorithm: 'sha256'});
return hashstr;
}
function openPublicKeyFile() {
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);
showAlert("Public key file loaded. You can now analyze PDFs signed by the key's owner.");
} else {
showAlert("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");
}
/**
* Show visual indicator when private key is not loaded/unlocked.
* @returns {undefined}
*/
setInterval(function () {
if (typeof keymgr == "undefined") {
$("#lockstatus").css("display", "");
} else {
$("#lockstatus").css("display", "none");
}
}, 1000);