diff --git a/src/openpgp.js b/src/openpgp.js index 3fa9ef14..c9c3d06d 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -26,8 +26,6 @@ import * as messageLib from './message.js'; import * as cleartext from './cleartext.js'; import * as key from './key.js'; -import armorLib from './encoding/armor.js'; -import enums from './enums.js'; import config from './config/config.js'; import util from './util'; import AsyncProxy from './worker/async_proxy.js'; @@ -154,7 +152,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar if(armor) { return { - data: armorLib.encode(enums.armor.message, message.packets.write()) + data: message.armor() }; } @@ -207,25 +205,33 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, /** * Signs a cleartext message - * @param {String} data cleartext input to be signed - * @param {Key|Array} privateKeys array of keys or single key with decrypted secret key data to sign cleartext - * @return {Promise} ASCII armored message + * @param {String} data cleartext input to be signed + * @param {Key|Array} privateKeys array of keys or single key with decrypted secret key data to sign cleartext + * @param {Boolean} armor (optional) if the return value should be ascii armored or the message object + * @return {Promise} ASCII armored message or the message of type CleartextMessage * @static */ -export function sign({ data, privateKeys }) { +export function sign({ data, privateKeys, armor=true }) { checkString(data); privateKeys = toArray(privateKeys); if (asyncProxy) { // use web worker if available - return asyncProxy.delegate('sign', { data, privateKeys }); + return asyncProxy.delegate('sign', { data, privateKeys, armor }); } return execute(() => { const cleartextMessage = new cleartext.CleartextMessage(data); cleartextMessage.sign(privateKeys); + + if(armor) { + return { + data: cleartextMessage.armor() + }; + } + return { - data: cleartextMessage.armor() + message: cleartextMessage }; }, 'Error signing cleartext message'); diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index f22a6477..0515a0c3 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -30,6 +30,7 @@ import crypto from '../crypto'; import packet from '../packet'; import * as key from '../key.js'; import * as message from '../message.js'; +import * as cleartext from '../cleartext.js'; import type_keyid from '../type/keyid.js'; const INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker @@ -137,7 +138,7 @@ AsyncProxy.prototype.delegate = function(method, options) { this.worker.postMessage({ event:method, options:clonePackets(options) }); // remember to handle parsing cloned packets from worker - this.tasks.push({ resolve: data => resolve(parseClonedPackets(data)), reject }); + this.tasks.push({ resolve: data => resolve(parseClonedPackets(data, method)), reject }); }); }; @@ -154,11 +155,13 @@ function clonePackets(options) { return options; } -function parseClonedPackets(data) { +function parseClonedPackets(data, method) { if (data.key) { data.key = packetlistCloneToKey(data.key); } - if (data.message) { + if (data.message && method === 'sign') { // sign supports only CleartextMessage + data.message = packetlistCloneToCleartextMessage(data.message); + } else if (data.message) { data.message = packetlistCloneToMessage(data.message); } if (data.signatures) { @@ -177,6 +180,11 @@ function packetlistCloneToMessage(clone) { return new message.Message(packetlist); } +function packetlistCloneToCleartextMessage(clone) { + var packetlist = packet.List.fromStructuredClone(clone.packets); + return new cleartext.CleartextMessage(clone.text, packetlist); +} + function packetlistCloneToSignature(clone) { clone.keyid = type_keyid.fromClone(clone.keyid); return clone; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 9f7a9c99..940d2c01 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -264,7 +264,7 @@ describe('OpenPGP.js public api tests', function() { }); }); - describe('encrypt, decrypt - integration tests', function() { + describe('encrypt, decrypt, sign, verify - integration tests', function() { var pub_key = ['-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -385,12 +385,24 @@ describe('OpenPGP.js public api tests', function() { }); }); - describe('with unlocked key', function() { + describe('with pgp key pair', function() { + var wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' + + 'Version: OpenPGP.js v0.9.0\r\n' + + 'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' + + '\r\n' + + 'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' + + 'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' + + 'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' + + 'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' + + 'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + + '=6XMW\r\n' + + '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n'; + beforeEach(function() { expect(privateKey.keys[0].decrypt(passphrase)).to.be.true; }); - it('should encrypt then decrypt with pgp key pair', function(done) { + it('should encrypt then decrypt', function(done) { var encOpt = { data: plaintext, publicKeys: publicKey.keys, @@ -408,7 +420,7 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should encrypt/sign and decrypt/verify with pgp key pair', function(done) { + it('should encrypt/sign and decrypt/verify', function(done) { var encOpt = { data: plaintext, publicKeys: publicKey.keys, @@ -424,23 +436,12 @@ describe('OpenPGP.js public api tests', function() { }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].primaryKey.getKeyId().toHex()); done(); }); }); - it('should fail to verify with wrong public pgp key', function(done) { - var wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' + - 'Version: OpenPGP.js v0.9.0\r\n' + - 'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' + - '\r\n' + - 'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' + - 'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' + - 'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' + - 'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' + - 'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' + - '=6XMW\r\n' + - '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n'; - + it('should fail to verify decrypted data with wrong public pgp key', function(done) { var encOpt = { data: plaintext, publicKeys: publicKey.keys, @@ -456,95 +457,156 @@ describe('OpenPGP.js public api tests', function() { }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures[0].valid).to.be.null; + expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].primaryKey.getKeyId().toHex()); + done(); + }); + }); + + it('should sign and verify cleartext data', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys + }; + var verifyOpt = { + publicKeys: publicKey.keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = openpgp.cleartext.readArmored(signed.data); + return openpgp.verify(verifyOpt); + }).then(function(verified) { + expect(verified.data).to.equal(plaintext); + expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].primaryKey.getKeyId().toHex()); + done(); + }); + }); + + it('should sign and fail to verify cleartext data with wrong public pgp key', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys + }; + var verifyOpt = { + publicKeys: openpgp.key.readArmored(wrong_pubkey).keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = openpgp.cleartext.readArmored(signed.data); + return openpgp.verify(verifyOpt); + }).then(function(verified) { + expect(verified.data).to.equal(plaintext); + expect(verified.signatures[0].valid).to.be.null; + expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].primaryKey.getKeyId().toHex()); + done(); + }); + }); + + it('should sign and verify cleartext data and not armor', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys, + armor: false + }; + var verifyOpt = { + publicKeys: publicKey.keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = signed.message; + return openpgp.verify(verifyOpt); + }).then(function(verified) { + expect(verified.data).to.equal(plaintext); + expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].primaryKey.getKeyId().toHex()); done(); }); }); }); - it('should encrypt and decrypt with one password', function(done) { - var encOpt = { - data: plaintext, - passwords: password1 - }; - var decOpt = { - password: password1 - }; - openpgp.encrypt(encOpt).then(function(encrypted) { - decOpt.message = openpgp.message.readArmored(encrypted.data); - return openpgp.decrypt(decOpt); - }).then(function(decrypted) { - expect(decrypted.data).to.equal(plaintext); - done(); + describe('with symmetric entryption', function() { + it('should encrypt and decrypt with one password', function(done) { + var encOpt = { + data: plaintext, + passwords: password1 + }; + var decOpt = { + password: password1 + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = openpgp.message.readArmored(encrypted.data); + return openpgp.decrypt(decOpt); + }).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); }); - }); - it('should encrypt and decrypt with two passwords', function(done) { - var encOpt = { - data: plaintext, - passwords: [password1, password2] - }; - var decOpt = { - password: password2 - }; - openpgp.encrypt(encOpt).then(function(encrypted) { - decOpt.message = openpgp.message.readArmored(encrypted.data); - return openpgp.decrypt(decOpt); - }).then(function(decrypted) { - expect(decrypted.data).to.equal(plaintext); - done(); + it('should encrypt and decrypt with two passwords', function(done) { + var encOpt = { + data: plaintext, + passwords: [password1, password2] + }; + var decOpt = { + password: password2 + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = openpgp.message.readArmored(encrypted.data); + return openpgp.decrypt(decOpt); + }).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); }); - }); - it('should encrypt and decrypt with password and not ascii armor', function(done) { - var encOpt = { - data: plaintext, - passwords: password1, - armor: false - }; - var decOpt = { - password: password1 - }; - openpgp.encrypt(encOpt).then(function(encrypted) { - decOpt.message = encrypted.message; - return openpgp.decrypt(decOpt); - }).then(function(decrypted) { - expect(decrypted.data).to.equal(plaintext); - done(); + it('should encrypt and decrypt with password and not ascii armor', function(done) { + var encOpt = { + data: plaintext, + passwords: password1, + armor: false + }; + var decOpt = { + password: password1 + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = encrypted.message; + return openpgp.decrypt(decOpt); + }).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); }); - }); - it('should encrypt and decrypt with one session key', function(done) { - var encOpt = { - data: plaintext, - passwords: password1 - }; - var decOpt = { - sessionKey: password1 - }; - openpgp.encrypt(encOpt).then(function(encrypted) { - decOpt.message = openpgp.message.readArmored(encrypted.data); - return openpgp.decrypt(decOpt); - }).then(function(decrypted) { - expect(decrypted.data).to.equal(plaintext); - done(); + it('should encrypt and decrypt with one session key', function(done) { + var encOpt = { + data: plaintext, + passwords: password1 + }; + var decOpt = { + sessionKey: password1 + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = openpgp.message.readArmored(encrypted.data); + return openpgp.decrypt(decOpt); + }).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); }); - }); - it('should encrypt and decrypt with two session keys and not ascii armor', function(done) { - var encOpt = { - data: plaintext, - passwords: [password1, password2], - armor: false - }; - var decOpt = { - sessionKey: password2 - }; - openpgp.encrypt(encOpt).then(function(encrypted) { - decOpt.message = encrypted.message; - return openpgp.decrypt(decOpt); - }).then(function(decrypted) { - expect(decrypted.data).to.equal(plaintext); - done(); + it('should encrypt and decrypt with two session keys and not ascii armor', function(done) { + var encOpt = { + data: plaintext, + passwords: [password1, password2], + armor: false + }; + var decOpt = { + sessionKey: password2 + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = encrypted.message; + return openpgp.decrypt(decOpt); + }).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); }); }); }