From 7365ffc9b750d4b436baf3751ec5f6a950c18cea Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 9 Feb 2018 10:49:15 +0100 Subject: [PATCH] wildcard implementation --- src/message.js | 57 +++++++++++++------ src/openpgp.js | 26 ++++----- .../public_key_encrypted_session_key.js | 1 - src/type/keyid.js | 10 ++++ 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/message.js b/src/message.js index 2036710c..97e1b348 100644 --- a/src/message.js +++ b/src/message.js @@ -35,6 +35,7 @@ import armor from './encoding/armor'; import enums from './enums'; import util from './util'; import packet from './packet'; +import type_keyid from './type/keyid'; import { Signature } from './signature'; import { getPreferredHashAlgo, getPreferredSymAlgo } from './key'; @@ -154,10 +155,14 @@ Message.prototype.decryptSessionKeys = function(privateKeys, passwords) { throw new Error('No symmetrically encrypted session key packet found.'); } await Promise.all(symESKeyPacketlist.map(async function(packet) { - try { - await packet.decrypt(password); - keyPackets.push(packet); - } catch (err) {} + for (var i = 0; i < passwords.length; i++) { + try { + // eslint-disable-next-line no-await-in-loop + await packet.decrypt(passwords[i]); + keyPackets.push(packet); + break; + } catch (err) {} + } })); } else if (privateKeys) { @@ -165,16 +170,30 @@ Message.prototype.decryptSessionKeys = function(privateKeys, passwords) { if (!pkESKeyPacketlist) { throw new Error('No public key encrypted session key packet found.'); } - var privateKeyPacket = privateKey.getKeyPacket(this.getEncryptionKeyIds()); - if (!privateKeyPacket.isDecrypted) { - throw new Error('Private key is not decrypted.'); - } await Promise.all(pkESKeyPacketlist.map(async function(packet) { - if (packet.publicKeyId.equals(privateKeyPacket.getKeyId())) { - try { - await packet.decrypt(privateKeyPacket); - keyPackets.push(packet); - } catch (err) {} + for (var i = 0; i < privateKeys.length; i++){ + var privateKeyPackets; + if (packet.publicKeyId.isWildcard()) { + // wildcard key ID - try all key packets + privateKeyPackets = privateKeys[i].getAllKeyPackets(); + } else { + privateKeyPackets = [privateKeys[i].getKeyPacket([packet.publicKeyId])]; + } + for (var j = 0; j < privateKeyPackets.length; j++) { + var privateKeyPacket = privateKeyPackets[j]; + if (!privateKeyPacket) { + continue; + } + if (!privateKeyPacket.isDecrypted) { + throw new Error('Private key is not decrypted.'); + } + try { + // eslint-disable-next-line no-await-in-loop + await packet.decrypt(privateKeyPacket); + keyPackets.push(packet); + break; + } catch (err) {} + } } })); } else { @@ -242,7 +261,7 @@ Message.prototype.getText = function() { * @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String } * @return {Message} new message with encrypted content */ -Message.prototype.encrypt = function(keys, passwords, sessionKey) { +Message.prototype.encrypt = function(keys, passwords, sessionKey, useWildcard) { let symAlgo, msg, symEncryptedPacket; return Promise.resolve().then(async () => { if (sessionKey) { @@ -263,7 +282,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey) { sessionKey = crypto.generateSessionKey(symAlgo); } - msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords); + msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, useWildcard); if (config.aead_protect) { symEncryptedPacket = new packet.SymEncryptedAEADProtected(); @@ -297,7 +316,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey) { * @param {Array} passwords (optional) for message encryption * @return {Message} new message with encrypted content */ -export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) { +export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, useWildcard) { var results, packetlist = new packet.List(); return Promise.resolve().then(async () => { @@ -309,7 +328,11 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) { throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex()); } var pkESKeyPacket = new packet.PublicKeyEncryptedSessionKey(); - pkESKeyPacket.publicKeyId = encryptionKeyPacket.getKeyId(); + if (!useWildcard) { + pkESKeyPacket.publicKeyId = encryptionKeyPacket.getKeyId(); + } else { + pkESKeyPacket.publicKeyId = type_keyid.wildcard(); + } pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm; pkESKeyPacket.sessionKey = sessionKey; pkESKeyPacket.sessionKeyAlgorithm = symAlgo; diff --git a/src/openpgp.js b/src/openpgp.js index 24399e73..fa36b8ef 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -201,11 +201,11 @@ export function decryptKey({ privateKey, passphrase }) { * message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true} * @static */ -export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, filename, armor=true, detached=false, signature=null, returnSessionKey=false}) { +export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, filename, armor=true, detached=false, signature=null, returnSessionKey=false, useWildcard=false}) { checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords); if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported - return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, sessionKey, filename, armor, detached, signature, returnSessionKey }); + return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, sessionKey, filename, armor, detached, signature, returnSessionKey, useWildcard }); } var result = {}; return Promise.resolve().then(async function() { @@ -226,7 +226,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, message = await message.sign(privateKeys, signature); } } - return message.encrypt(publicKeys, passwords, sessionKey); + return message.encrypt(publicKeys, passwords, sessionKey, useWildcard); }).then(encrypted => { if (armor) { @@ -395,16 +395,16 @@ export function verify({ message, publicKeys, signature=null }) { * @return {Promise} the encrypted session key packets contained in a message object * @static */ -export function encryptSessionKey({ data, algorithm, publicKeys, passwords }) { +export function encryptSessionKey({ data, algorithm, publicKeys, passwords, useWildcard=false }) { checkBinary(data); checkString(algorithm, 'algorithm'); publicKeys = toArray(publicKeys); passwords = toArray(passwords); if (asyncProxy) { // use web worker if available - return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords }); + return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords, useWildcard }); } return Promise.resolve().then(async function() { - return { message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords) }; + return { message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords, useWildcard) }; }).catch(onError.bind(null, 'Error encrypting session key')); } @@ -412,24 +412,24 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords }) { /** * Decrypt a symmetric session key with a private key or password. Either a private key or * a password must be specified. - * @param {Message} message a message object containing the encrypted session key packets - * @param {Key} privateKey (optional) private key with decrypted secret key data - * @param {String} password (optional) a single password to decrypt the session key + * @param {Message} message a message object containing the encrypted session key packets + * @param {Key|Array} passwords (optional) passwords to decrypt the session key * @return {Promise} Array of decrypted session key, algorithm pairs in form: * { data:Uint8Array, algorithm:String } * or 'undefined' if no key packets found * @static */ -export function decryptSessionKeys({ message, privateKey, password }) { - checkMessage(message); +export function decryptSessionKeys({ message, privateKeys, passwords }) { + checkMessage(message); privateKeys = toArray(privateKeys); passwords = toArray(passwords); if (asyncProxy) { // use web worker if available - return asyncProxy.delegate('decryptSessionKeys', { message, privateKey, password }); + return asyncProxy.delegate('decryptSessionKeys', { message, privateKeys, passwords }); } return Promise.resolve().then(async function() { - return message.decryptSessionKeys(privateKey, password); + return message.decryptSessionKeys(privateKeys, passwords); }).catch(onError.bind(null, 'Error decrypting session keys')); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 9d86e2e0..8c59177e 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -71,7 +71,6 @@ export default function PublicKeyEncryptedSessionKey() { * @return {module:packet/public_key_encrypted_session_key} Object representation */ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { - this.version = bytes[0]; this.publicKeyId.read(bytes.subarray(1,bytes.length)); this.publicKeyAlgorithm = enums.read(enums.publicKey, bytes[9]); diff --git a/src/type/keyid.js b/src/type/keyid.js index 5aca938d..119f842c 100644 --- a/src/type/keyid.js +++ b/src/type/keyid.js @@ -61,6 +61,10 @@ Keyid.prototype.isNull = function() { return this.bytes === ''; }; +Keyid.prototype.isWildcard = function() { + return this.toHex() === '0000000000000000'; +}; + Keyid.mapToHex = function (keyId) { return keyId.toHex(); }; @@ -76,3 +80,9 @@ Keyid.fromId = function (hex) { keyid.read(util.str2Uint8Array(util.hex2bin(hex))); return keyid; }; + +Keyid.wildcard = function () { + var keyid = new Keyid(); + keyid.read(util.str2Uint8Array(util.hex2bin('0000000000000000'))); + return keyid; +};