"
* @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(" 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(" Key generated!");
setTimeout(function () {
statustextEl.html("");
}, 5000);
callback(alice);
});
});
}
});
}, 100);
}
function exportPublicKeyToFile() {
getOwnPublicKey(function (pgp_public) {
if (pgp_public == false) {
showAlert("Something went wrong.");
} else {
openSaveFileDialog(function (path) {
writeToFile(path, pgp_public);
showAlert("Public key saved.");
}, "public-key.asc", ".asc");
}
});
}
function exportPublicKeyToRegistry() {
if (!inStorage("signingkey") || getStorage("signingkey") == "undefined") {
showAlert("You must create and back up your private key first.");
return;
}
showOkCancelPrompt("Double-check that your notary profile is complete and that you pressed the Save button after making any changes before continuing.", function (ok) {
if (!ok) {
return;
}
getOwnPublicKey(function (pgp_public) {
if (pgp_public == false) {
showAlert("Something went wrong.");
} else {
submitPublicKeyToRegistry(
pgp_public,
getStorage("notary_name"),
getStorage("notary_email"),
getStorage("notary_location"),
getStorage("notary_expires"),
getStorage("notary_idnumber"),
getStorage("notary_state"),
function (msg, ok) {
if (!ok) {
showAlert("Error: " + msg);
} else {
showAlert(msg);
}
}
);
}
});
});
}
/**
* Get user's own public key, prompting for key password if needed.
* @param {function} callback cb(result): public key string or false on error
* @returns {undefined}
*/
function getOwnPublicKey(callback) {
loadKeyFromLocalStorage(function (message, ok) {
if (ok) {
keymgr.export_pgp_public({}, function (err, pgp_public) {
if (err) {
callback(false);
} else {
callback(pgp_public);
}
});
} else {
showAlert("Error: " + message);
}
});
}
/**
* This should be modified to prompt the user for a backup password,
* but that doesn't work. https://github.com/keybase/kbpgp/issues/211
* @returns {undefined}
*/
function exportPrivateKey() {
loadKeyFromLocalStorage(function (message, ok) {
if (!ok) {
showAlert("Error: " + message);
return;
}
openSaveFileDialog(function (path) {
keymgr.export_pgp_private({}, function (err, pgp_private) {
if (err) {
showAlert("Something went wrong.");
} else {
writeToFile(path, pgp_private);
showAlert("Private key backup saved.");
}
});
}, "private-key.asc", ".asc");
});
}
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);
showPasswordPrompt("Enter password for imported key (password was set when exported):", function (pass) {
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");
}
/**
* Call the native system GPG to "decrypt" a PGP signature. This should work when the hacky "base64 decode and search for strings" method fails.
* @param {String} sigdata
* @param {Function} callback (string|null) message, (string|null) fingerprint, (string|null) signername, (bool) verified, (bool) success
* @returns {undefined}
*/
function readSignatureExternally(sigdata, callback) {
const exec = require('child_process').exec;
const os = require('os');
const process = require('process');
const sigfilepath = getNewTempFilePath() + ".asc";
writeToFile(sigfilepath, sigdata);
var gpgexecutable = "gpg";
switch (os.platform()) {
case "win32":
// Most systems will have it here
gpgexecutable = '"C:\\Program Files (x86)\\gnupg\\bin\\gpg.exe"';
if (!fs.existsSync(gpgexecutable)) {
// Let's hope it's in %PATH%
gpgexecutable = "gpg.exe";
}
break;
case "linux":
break;
default:
break;
}
var command = gpgexecutable + " -vv --decrypt " + sigfilepath;
exec(command, function (error, stdout, stderr) {
console.log(stdout);
var msg = null;
if (stdout.length > 50) {
msg = stdout;
} else {
callback(null, null, null, false, false);
}
var verified = false;
var signername = null;
console.log(stderr);
var keyid = null;
var keyidregex = /(keyid|RSA key) ([A-F0-9]+)/;
if (keyidregex.test(stderr)) {
keyid = stderr.match(keyidregex)[2];
}
var goodsigregex = /Good signature from "([a-zA-Z0-9\s]+) <.+@.+>"/;
if (goodsigregex.test(stderr)) {
// GPG actually has a matching public key, so that's cool
verified = true;
signername = stderr.match(goodsigregex)[1];
}
callback(msg, keyid, signername, verified, true);
});
}
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) {
addPublicKeyToKeyring(keyfile, function (res) {
if (res === true) {
showAlert("Public key file loaded. You can now analyze PDFs signed by the key's owner.");
} else {
showAlert("Error loading public key: " + res);
}
});
};
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");
}
/**
*
* @param {type} keyfile
* @param {type} callback cb(result): result is true if successful, an error string if failed.
* @returns {undefined}
*/
function addPublicKeyToKeyring(keyfile, callback) {
kbpgp.KeyManager.import_from_armored_pgp({
armored: keyfile
}, function (err, pubkeymgr) {
if (!err) {
keyring.add_key_manager(pubkeymgr);
callback(true);
} else {
callback(err);
}
});
}
/**
* Look up a full or partial public key fingerprint with the Netsyms notary registry.
* @param {string} fingerprint
* @param {function} callback cb(result): `result` is an array of notary info and keys (see below), or `false` if there was an error or no results.
* @returns {undefined}
*
* result = [{
* fingerprint,
* name,
* email,
* location,
* commissionexpires,
* idnumber,
* state,
* publickey
* }]
*
* All but fingerprint and publickey could be null.
*
*/
function lookupPublicKey(fingerprint, callback) {
$.ajax({
url: "https://data.netsyms.net/v1/notary/fetchkey/",
dataType: "json",
data: {
fingerprint: fingerprint
},
success: function (resp) {
if (resp.count == 0) {
callback(false);
return;
}
callback(resp.results);
},
error: function () {
callback(false);
}
});
}
/**
* Import multiple public keys and only callback when all are done.
* @param {type} keys see lookupPublicKey()
* @param {function} callback
* @returns {undefined}
*/
function importPublicKeysFromRegistry(keys, callback) {
var i = 0;
var loop = function (keys) {
addPublicKeyToKeyring(keys[i].publickey, function () {
i++;
if (i < keys.length) {
loop(keys);
} else {
callback();
}
});
};
loop(keys);
}
/**
* Upload a public key to the Netsyms notary registry server.
* @param {string} pubkey PGP public key file contents, armored
* @param {string} name Notary name
* @param {string} email Notary email
* @param {string} location Notary location
* @param {string} expires Commission expiration date; server will parse.
* @param {string} idnumber Commission ID number
* @param {string} state Two-char state
* @param {function} callback ((string) message, (bool) okaytrue_errorfalse)
* @returns {undefined}
*/
function submitPublicKeyToRegistry(pubkey, name, email, location, expires, idnumber, state, callback) {
$.ajax({
url: "https://data.netsyms.net/v1/notary/publishkey/",
method: "POST",
dataType: "json",
data: {
publickey: pubkey,
name: name,
email: email,
location: location,
commissionexpires: expires,
idnumber: idnumber,
state: state
},
success: function (resp) {
if (resp.status == "OK") {
callback(resp.msg, true);
} else if (resp.status == "ERROR") {
callback(resp.msg, false);
} else {
callback("The registry server didn't send a valid response.", false);
}
},
error: function () {
callback("There was a problem communicating with the registry server. Try again later.", false);
}
});
}
/**
* Erase the local private key data with lots of prompting and dire warnings.
* @returns {undefined}
*/
function erasePrivateKey() {
showOkCancelPrompt(" DANGER: THIS WILL RESULT IN DATA LOSS -- READ CAREFULLY
\n\
Erasing your private key means you won't be able to notarize or sign electronic documents without generating a new key. \n\
If you have not exported your public key, electronically verifying documents you have signed will be impossible.\n\
If you plan on using your private key in the future, press cancel and back up your private key to a file.\n\
Some states require you use the same key for the entire length of your commission.\n\
IF YOU CONTINUE, YOUR PRIVATE KEY WILL NOT BE RECOVERABLE WITHOUT A BACKUP.
", function (ok) {
if (!ok) {
return;
}
var txt = prompt("To erase your private key, type \"ERASE MY SIGNING KEY\"");
if (txt.toUpperCase() != "ERASE MY SIGNING KEY") {
return;
}
unloadKey();
localStorage.removeItem("signingkey");
alert("Signing key erased.");
});
}
/**
* 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);