forro

End-to-end encrypted contract form based on PGP.
git clone git://git.defalsify.org/forro.git
Info | Log | Files | Refs | README | LICENSE

key.js (5168B)


      1 /**
      2  * Create a new PGP key from parameters
      3  *
      4  * On successful creation, the private key will be stored to local storage.
      5  * 
      6  * The private key stored in local storage will be always encrypted with the
      7  * literal passphrase. The passphrase may be an empty string.
      8  *
      9  * @param {string} pwd Passphrase
     10  * @param {Object} uid User id  object {name, email}
     11  * @returns {Promise<Object>} Returns a private key object on 
     12  * @todo enable some level of pwd integrity check
     13  * @todo handle failure
     14  */
     15 async function generatePGPKey(pwd, uid) {
     16 	if (uid === undefined) {
     17 		uid = {
     18 			name: "Ola Nordmann",
     19 			email: "ola@nordmann.no",
     20 		};
     21 	}
     22 	uid.comment = 'Generated by forro/' + g_version + ', openpgpjs/5.5.0';
     23 	return new Promise(async (whohoo, doh) => {
     24 		let v = await openpgp.generateKey({
     25 			//type: 'ecc',
     26 			//curve: 'secp256k1',
     27 			type: 'rsa',
     28 			userIDs: [uid],
     29 			passphrase: pwd,
     30 			format: 'armored',
     31 			//config: { rejectCurves: new Set() },
     32 		});
     33 		console.info('our public key', v.publicKey );
     34 		let pk = await openpgp.readKey({
     35 			armoredKey: v.privateKey,
     36 		});
     37 		localStorage.setItem('pgp-key', pk.armor());
     38 
     39 		whohoo(pk);
     40 	});
     41 }
     42 
     43 /**
     44  * Retrieve the private key of the locally stored private key.
     45  *
     46  * @param {string} pwd Passphrase to decrypt the private key with.
     47  * @param {boolean} encrypted If false, return the verbatim key data stored in
     48  * local storage
     49  * @returns {any} openpgpjs private key object if encrypted. Private key literal data if not.
     50  * @todo Make return type same type
     51  * @todo handle failure
     52  */
     53 async function getKey(pwd, encrypted) {
     54 	return new Promise(async (whohoo, doh) => {
     55 		let pk_armor = localStorage.getItem('pgp-key');
     56 		if (pk_armor === null) {
     57 			doh('no key');
     58 			return;
     59 		}
     60 		if (encrypted) {
     61 			return whohoo(pk_armor);
     62 		}
     63 		let pk = await openpgp.readKey({
     64 			armoredKey: pk_armor,
     65 		});
     66 		console.debug('our public key', pk.toPublic().armor());
     67 
     68 		if (pwd !== undefined) {
     69 			openpgp.decryptKey({
     70 				privateKey: pk,
     71 				passphrase: pwd,
     72 			}).then((pk) => {
     73 				whohoo(pk);
     74 			}).catch((e) => {
     75 				doh(e);
     76 			});
     77 		} else {
     78 			whohoo(pk);
     79 		}
     80 	});
     81 }
     82 
     83 /**
     84  * Retrieve the current ciphertext value of the private key in local store.
     85  * @returns {string} Private key ASCII-armored PGP data
     86  */
     87 function getEncryptedKey() {
     88 	return localStorage.getItem('pgp-key');
     89 }
     90 
     91 /**
     92  * Create a HTTP Authorization PUBSIG string from the private key and provided message
     93  * to use for remote submission.
     94  *
     95  * @param {Object} pk Private key
     96  * @param {Object} msg Authenticated message object, containing msg, rcpt auth
     97  * @returns {string} PUBSIG string
     98  * @see encryptMessage
     99  * @see encryptPublicKey
    100  * @see encryptCounter
    101  * @todo define an interface for the message object
    102  *
    103  */
    104 async function generateAuth(pk, msg) {
    105 	let sig = await openpgp.sign({
    106 		signingKeys: g_local_key,
    107 		message: msg,
    108 		format: 'binary',
    109 		detached: true,
    110 	});
    111 	let pubkey = pk.toPublic().write();
    112 	let pubkey_str = String.fromCharCode.apply(null, pubkey);
    113 	let sig_str = String.fromCharCode.apply(null, sig);
    114 
    115 	sig_b = btoa(sig_str);
    116 	pub_b = btoa(pubkey_str);
    117 
    118 	return "pgp:" + pub_b + ":" + sig_b;
    119 }
    120 
    121 /**
    122  * Create a mutable pointer for remote storage
    123  *
    124  * @param {Object} openpgpjs private key object
    125  * @param {string} remote mutable storage prefix
    126  * @returns {string} Mutable pointer in hex
    127  **/
    128 async function generatePointer(pk, pfx) {
    129 	let sha = new jsSHA("SHA-256", "TEXT");
    130 	sha.update(pfx);
    131 	let prefix_digest = sha.getHash("HEX");
    132 
    133 	let identity_id = pk.getFingerprint();
    134 	sha = new jsSHA("SHA-256", "HEX");
    135 	sha.update(prefix_digest);
    136 	sha.update(identity_id);
    137 	return sha.getHash("HEX");
    138 }
    139 
    140 /**
    141  * Validate email value
    142  *
    143  * robbed from https://www.w3resource.com/javascript/form/email-validation.php
    144  *
    145  * @param {string} mail Email to validate
    146  * @returns {boolean} true if valid email string
    147  *
    148  **/
    149 function validateEmail(mail) {
    150 	if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(mail)) {
    151 		return true;
    152 	}
    153 	return false;
    154 }
    155 
    156 /**
    157  * Add local store association between local private key and 
    158  * identifiable information.
    159  *
    160  * (This is a bit of a hack, as openpgpjs (or pgp in general) does not seem
    161  * designed for changing userid after the fact)
    162  *
    163  * @param {Object} pk openpgpjs Private key object
    164  * @param {string} name Name of key owner (claimed)
    165  * @param {string} email Email of key owner (claimed)
    166  * @param {string} pwd Private key passphrase
    167  * @returns {Promise<Object>} Updated private key object
    168  * @todo handle failure
    169  */
    170 async function identify(pk, name, email, pwd) {
    171 	return new Promise(async (whohoo, doh) => {
    172 		const u = openpgp.UserIDPacket.fromObject({
    173 			name: name,
    174 			email: email,
    175 			comment: 'manual entry on forro/' + g_version + ', openpgp/5.5.0',
    176 		});
    177 		let l = pk.toPacketList();
    178 		l.push(u);
    179 
    180 		let pk_new = new openpgp.PrivateKey(l);
    181 		if (pwd !== undefined) {
    182 			pk_new = await openpgp.encryptKey({
    183 				privateKey: pk_new,
    184 				passphrase: pwd,
    185 
    186 			});
    187 		}
    188 
    189 		localStorage.setItem('pgp-key', pk_new.armor());
    190 		
    191 		if (pwd !== undefined) {
    192 			pk_new = await openpgp.decryptKey({
    193 				privateKey: pk_new,
    194 				passphrase: pwd,
    195 			});
    196 		}
    197 
    198 		whohoo(pk_new);
    199 	});
    200 }