fix sporadic two password decryption failure
This commit is contained in:
parent
bdf1ce8d64
commit
210ec26ed3
3
npm-shrinkwrap.json
generated
3
npm-shrinkwrap.json
generated
|
@ -842,7 +842,8 @@
|
|||
"babel-plugin-transform-remove-strict-mode": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-strict-mode/-/babel-plugin-transform-remove-strict-mode-0.0.2.tgz",
|
||||
"integrity": "sha1-kTaFqrlUOfOg7YjliPvV6ZeJBXk="
|
||||
"integrity": "sha1-kTaFqrlUOfOg7YjliPvV6ZeJBXk=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-runtime": {
|
||||
"version": "6.23.0",
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-async-to-generator": "^6.24.1",
|
||||
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||
"babel-plugin-transform-remove-strict-mode": "0.0.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
|
@ -76,7 +77,6 @@
|
|||
"dependencies": {
|
||||
"asmcrypto-lite": "git+https://github.com/openpgpjs/asmcrypto-lite.git",
|
||||
"asn1.js": "^5.0.0",
|
||||
"babel-plugin-transform-remove-strict-mode": "0.0.2",
|
||||
"bn.js": "^4.11.8",
|
||||
"buffer": "^5.0.8",
|
||||
"elliptic": "git+https://github.com/openpgpjs/elliptic.git",
|
||||
|
|
|
@ -37,7 +37,7 @@ export default {
|
|||
prefer_hash_algorithm: enums.hash.sha256,
|
||||
encryption_cipher: enums.symmetric.aes256,
|
||||
compression: enums.compression.zip,
|
||||
aead_protect: false, // use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption
|
||||
aead_protect: false, // use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption (NOT INTEROPERABLE WITH OTHER OPENPGP IMPLEMENTATIONS)
|
||||
integrity_protect: true, // use integrity protection for symmetric encryption
|
||||
ignore_mdc_error: false, // fail on decrypt if message is not integrity protected
|
||||
checksum_required: false, // do not throw error when armor is missing a checksum
|
||||
|
@ -46,7 +46,8 @@ export default {
|
|||
use_native: true, // use native node.js crypto and Web Crypto apis (if available)
|
||||
zero_copy: false, // use transferable objects between the Web Worker and main thread
|
||||
debug: false,
|
||||
tolerant: true, // ignore unsupported/unrecognizable packets instead of throwing an error
|
||||
tolerant: true, // ignore unsupported/unrecognizable packets instead of throwing an error,
|
||||
password_collision_check: false, // work-around for rare GPG decryption bug with encrypting with multiple passwords
|
||||
show_version: true,
|
||||
show_comment: true,
|
||||
versionstring: "OpenPGP.js VERSION",
|
||||
|
|
|
@ -97,9 +97,9 @@ Message.prototype.getSigningKeyIds = function() {
|
|||
* @return {Message} new message with decrypted content
|
||||
*/
|
||||
Message.prototype.decrypt = async function(privateKey, sessionKey, password) {
|
||||
const keyObj = sessionKey || await this.decryptSessionKey(privateKey, password);
|
||||
if (!keyObj || !util.isUint8Array(keyObj.data) || !util.isString(keyObj.algorithm)) {
|
||||
throw new Error('Invalid session key for decryption.');
|
||||
let keyObjs = sessionKey || await this.decryptSessionKey(privateKey, password);
|
||||
if (!util.isArray(keyObjs)) {
|
||||
keyObjs = [keyObjs];
|
||||
}
|
||||
|
||||
const symEncryptedPacketlist = this.packets.filterByTag(
|
||||
|
@ -113,7 +113,26 @@ Message.prototype.decrypt = async function(privateKey, sessionKey, password) {
|
|||
}
|
||||
|
||||
const symEncryptedPacket = symEncryptedPacketlist[0];
|
||||
await symEncryptedPacket.decrypt(keyObj.algorithm, keyObj.data);
|
||||
let exception = null;
|
||||
for (let i = 0; i < keyObjs.length; i++) {
|
||||
if (!keyObjs[i] || !util.isUint8Array(keyObjs[i].data) || !util.isString(keyObjs[i].algorithm)) {
|
||||
throw new Error('Invalid session key for decryption.');
|
||||
}
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await symEncryptedPacket.decrypt(keyObjs[i].algorithm, keyObjs[i].data);
|
||||
break;
|
||||
}
|
||||
catch(e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symEncryptedPacket.packets || !symEncryptedPacket.packets.length) {
|
||||
throw exception ? exception : new Error('Decryption failed.');
|
||||
}
|
||||
|
||||
const resultMsg = new Message(symEncryptedPacket.packets);
|
||||
symEncryptedPacket.packets = new packet.List(); // remove packets after decryption
|
||||
|
||||
|
@ -124,23 +143,21 @@ Message.prototype.decrypt = async function(privateKey, sessionKey, password) {
|
|||
* Decrypt an encrypted session key either with a private key or a password.
|
||||
* @param {Key} privateKey (optional) private key with decrypted secret data
|
||||
* @param {String} password (optional) password used to decrypt
|
||||
* @return {Object} object with sessionKey, algorithm in the form:
|
||||
* @return {Array} array of object with potential sessionKey, algorithm pairs in the form:
|
||||
* { data:Uint8Array, algorithm:String }
|
||||
*/
|
||||
Message.prototype.decryptSessionKey = function(privateKey, password) {
|
||||
var keyPacket;
|
||||
var keyPackets = [];
|
||||
return Promise.resolve().then(async () => {
|
||||
if (password) {
|
||||
var symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
|
||||
if (!symESKeyPacketlist) {
|
||||
throw new Error('No symmetrically encrypted session key packet found.');
|
||||
}
|
||||
// TODO replace when Promise.some or Promise.any are implemented
|
||||
await symESKeyPacketlist.some(async function(packet) {
|
||||
await symESKeyPacketlist.map(async function(packet) {
|
||||
try {
|
||||
await packet.decrypt(password);
|
||||
keyPacket = packet;
|
||||
return true;
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
});
|
||||
|
||||
|
@ -159,8 +176,7 @@ Message.prototype.decryptSessionKey = function(privateKey, password) {
|
|||
if (packet.publicKeyId.equals(privateKeyPacket.getKeyId())) {
|
||||
try {
|
||||
await packet.decrypt(privateKeyPacket);
|
||||
keyPacket = packet;
|
||||
return true;
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
}
|
||||
});
|
||||
|
@ -168,11 +184,8 @@ Message.prototype.decryptSessionKey = function(privateKey, password) {
|
|||
throw new Error('No key or password specified.');
|
||||
}
|
||||
}).then(() => {
|
||||
if (keyPacket) {
|
||||
return {
|
||||
data: keyPacket.sessionKey,
|
||||
algorithm: keyPacket.sessionKeyAlgorithm
|
||||
};
|
||||
if (keyPackets.length) {
|
||||
return keyPackets.map(packet => ({ data: packet.sessionKey, algorithm: packet.sessionKeyAlgorithm }));
|
||||
} else {
|
||||
throw new Error('Session key decryption failed.');
|
||||
}
|
||||
|
@ -296,14 +309,38 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) {
|
|||
}
|
||||
|
||||
if (passwords) {
|
||||
results = await Promise.all(passwords.map(async function(password) {
|
||||
|
||||
const testDecrypt = async function(keyPacket, password) {
|
||||
try {
|
||||
await keyPacket.decrypt(password);
|
||||
return 1;
|
||||
}
|
||||
catch (e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const sum = (accumulator, currentValue) => accumulator + currentValue;
|
||||
|
||||
const encryptPassword = async function(sessionKey, symAlgo, password) {
|
||||
|
||||
var symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey();
|
||||
symEncryptedSessionKeyPacket.sessionKey = sessionKey;
|
||||
symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
await symEncryptedSessionKeyPacket.encrypt(password);
|
||||
|
||||
if (config.password_collision_check) {
|
||||
var results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd)));
|
||||
if (results.reduce(sum) !== 1) {
|
||||
return encryptPassword(sessionKey, symAlgo, password);
|
||||
}
|
||||
}
|
||||
|
||||
delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption
|
||||
return symEncryptedSessionKeyPacket;
|
||||
}));
|
||||
};
|
||||
|
||||
results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, pwd)));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
}).then(() => {
|
||||
|
|
|
@ -164,6 +164,18 @@ var plaintext = 'short message\nnext line\n한국어/조선말';
|
|||
var password1 = 'I am a password';
|
||||
var password2 = 'I am another password';
|
||||
|
||||
var twoPasswordGPGFail = ['-----BEGIN PGP MESSAGE-----',
|
||||
'Version: OpenPGP.js v3.0.0',
|
||||
'Comment: https://openpgpjs.org',
|
||||
'',
|
||||
'wy4ECQMIWjj3WEfWxGpgrfb3vXu0TS9L8UNTBvNZFIjltGjMVkLFD+/afgs5',
|
||||
'aXt0wy4ECQMIrFo3TFN5xqtgtB+AaAjBcWJrA4bvIPBpJ38PbMWeF0JQgrqg',
|
||||
'j3uehxXy0mUB5i7B61g0ho+YplyFGM0s9XayJCnu40tWmr5LqqsRxuwrhJKR',
|
||||
'migslOF/l6Y9F0F9xGIZWGhxp3ugQPjVKjj8fOH7ap14mLm60C8q8AOxiSmL',
|
||||
'ubsd/hL7FPZatUYAAZVA0a6hmQ==',
|
||||
'=cHCV',
|
||||
'-----END PGP MESSAGE-----'].join('\n');
|
||||
|
||||
describe('OpenPGP.js public api tests', function() {
|
||||
|
||||
describe('initWorker, getWorker, destroyWorker - unit tests', function() {
|
||||
|
@ -532,7 +544,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
privateKey: privateKey.keys[0]
|
||||
});
|
||||
}).then(function(decrypted) {
|
||||
expect(decrypted.data).to.deep.equal(sk);
|
||||
expect(decrypted[0].data).to.deep.equal(sk);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -547,7 +559,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
password: password1
|
||||
});
|
||||
}).then(function(decrypted) {
|
||||
expect(decrypted.data).to.deep.equal(sk);
|
||||
expect(decrypted[0].data).to.deep.equal(sk);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -563,9 +575,9 @@ describe('OpenPGP.js public api tests', function() {
|
|||
privateKey: privateKey.keys[0]
|
||||
});
|
||||
|
||||
}).then(function(decryptedSessionKey) {
|
||||
}).then(function(decryptedSessionKeys) {
|
||||
return openpgp.decrypt({
|
||||
sessionKey: decryptedSessionKey,
|
||||
sessionKey: decryptedSessionKeys[0],
|
||||
message: openpgp.message.readArmored(msgAsciiArmored)
|
||||
});
|
||||
|
||||
|
@ -586,9 +598,9 @@ describe('OpenPGP.js public api tests', function() {
|
|||
password: password1
|
||||
});
|
||||
|
||||
}).then(function(decryptedSessionKey) {
|
||||
}).then(function(decryptedSessionKeys) {
|
||||
return openpgp.decrypt({
|
||||
sessionKey: decryptedSessionKey,
|
||||
sessionKey: decryptedSessionKeys[0],
|
||||
message: openpgp.message.readArmored(msgAsciiArmored)
|
||||
});
|
||||
|
||||
|
@ -1224,7 +1236,6 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
// FIXME this test sporadically fails
|
||||
it('should encrypt and decrypt with two passwords', function() {
|
||||
var encOpt = {
|
||||
data: plaintext,
|
||||
|
@ -1242,6 +1253,18 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should decrypt with two passwords message which GPG fails on', function() {
|
||||
|
||||
var decOpt = {
|
||||
message: openpgp.message.readArmored(twoPasswordGPGFail),
|
||||
password: password2
|
||||
};
|
||||
return openpgp.decrypt(decOpt).then(function(decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
expect(decrypted.signatures.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt and decrypt with password and not ascii armor', function() {
|
||||
var encOpt = {
|
||||
data: plaintext,
|
||||
|
|
Loading…
Reference in New Issue
Block a user