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 }