/ *
* 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" ) {
showPasswordPrompt ( "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." , function ( pass ) {
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 {
showPasswordPrompt ( "Enter password to unlock signing key:" , function ( pass ) {
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 ) ;
}
} ) ;
}
/ * *
* 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 ) {
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 ) ;