diff --git a/src/openpgp.js b/src/openpgp.js index 5f45978d..d834b6ea 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -53,7 +53,7 @@ export function initWorker({ path='openpgp.worker.js', worker } = {}) { if (worker || typeof window !== 'undefined' && window.Worker) { asyncProxy = new AsyncProxy({ path, worker, config }); } else { - throw new Error('Initializing web worker failed!'); + throw new Error('Initializing web worker failed'); } } @@ -109,13 +109,13 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal // js fallback already tried if (config.debug) { console.error(err); } if (!util.getWebCrypto()) { - throw new Error('Error generating keypair using js fallback!'); + throw new Error('Error generating keypair using js fallback'); } // fall back to js keygen in a worker - if (config.debug) { console.log('Error generating keypair using native WebCrypto... falling back back to js!'); } + if (config.debug) { console.log('Error generating keypair using native WebCrypto... falling back back to js'); } return asyncProxy.generateKey(options); - }).catch(onError.bind(null, 'Error generating keypair!')); + }).catch(onError.bind(null, 'Error generating keypair')); } @@ -139,8 +139,7 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal * @static */ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, packets }) { - publicKeys = toArray(publicKeys); - privateKeys = toArray(privateKeys); + checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords); if (asyncProxy) { // use web worker if available return asyncProxy.encrypt({ data, publicKeys, privateKeys, passwords, filename, packets }); @@ -160,7 +159,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, pa return getAsciiArmored(msg); } - }, 'Error encrypting message!'); + }, 'Error encrypting message'); } /** @@ -168,7 +167,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, pa * Either a private key, a session key or a password must be specified. * @param {Message} message the message object with the encrypted data * @param {Key} privateKey (optional) private key with decrypted secret key data or session key - * @param {Key|Array} publickeys (optional) array of publickeys or single key, to verify signatures + * @param {Key|Array} publicKeys (optional) array of public keys or single key, to verify signatures * @param {String} sessionKey (optional) session key as a binary string * @param {String} password (optional) single password to decrypt the message * @param {String} format (optional) return data format either as 'utf8' or 'binary' @@ -176,23 +175,23 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, pa * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] } * @static */ -export function decrypt({ message, privateKey, publickeys, sessionKey, password, format='utf8' }) { - publickeys = toArray(publickeys); +export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8' }) { + checkMessage(message); publicKeys = toArray(publicKeys); if (asyncProxy) { // use web worker if available - return asyncProxy.decrypt({ message, privateKey, publickeys, sessionKey, password, format }); + return asyncProxy.decrypt({ message, privateKey, publicKeys, sessionKey, password, format }); } return execute(() => { message = message.decrypt(privateKey, sessionKey, password); const result = parseMessage(message, format); - if (publickeys && result.data) { // verify only if publickeys are specified - result.signatures = message.verify(publickeys); + if (publicKeys && result.data) { // verify only if publicKeys are specified + result.signatures = message.verify(publicKeys); } return result; - }, 'Error decrypting message!'); + }, 'Error decrypting message'); } @@ -211,10 +210,8 @@ export function decrypt({ message, privateKey, publickeys, sessionKey, password, * @static */ export function sign({ data, privateKeys }) { + checkString(data); privateKeys = toArray(privateKeys); - if (!util.isString(data)) { - throw new Error('Only cleartext data of type String supported!'); - } if (asyncProxy) { // use web worker if available return asyncProxy.sign({ data, privateKeys }); @@ -228,7 +225,7 @@ export function sign({ data, privateKeys }) { data: cleartextMessage.armor() }; - }, 'Error signing cleartext message!'); + }, 'Error signing cleartext message'); } /** @@ -240,10 +237,8 @@ export function sign({ data, privateKeys }) { * @static */ export function verify({ message, publicKeys }) { + checkCleartextMessage(message); publicKeys = toArray(publicKeys); - if (!(message instanceof cleartext.CleartextMessage)) { - throw new Error('Parameter [message] needs to be of type CleartextMessage.'); - } if (asyncProxy) { // use web worker if available return asyncProxy.verify({ message, publicKeys }); @@ -254,7 +249,7 @@ export function verify({ message, publicKeys }) { data: message.getText(), signatures: message.verify(publicKeys) - }), 'Error verifying cleartext signed message!'); + }), 'Error verifying cleartext signed message'); } @@ -283,7 +278,7 @@ export function encryptSessionKey({ sessionKey, algo, publicKeys, passwords }) { data: messageLib.encryptSessionKey(sessionKey, algo, publicKeys, passwords).packets.write() - }), 'Error encrypting session key!'); + }), 'Error encrypting session key'); } /** @@ -303,7 +298,7 @@ export function decryptSessionKey({ message, privateKey, sessionKey, password }) return asyncProxy.decryptSessionKey({ message, privateKey, sessionKey, password }); } - return execute(() => message.decryptSessionKey(privateKey, sessionKey, password), 'Error decrypting session key!'); + return execute(() => message.decryptSessionKey(privateKey, sessionKey, password), 'Error decrypting session key'); } @@ -314,6 +309,30 @@ export function decryptSessionKey({ message, privateKey, sessionKey, password }) ////////////////////////// +/** + * Input validation + */ +function checkString(data, name) { + if (!util.isString(data)) { + throw new Error('Parameter [' + (name || 'data') + '] must be of type String'); + } +} +function checkData(data, name) { + if (!util.isUint8Array(data) && !util.isString(data)) { + throw new Error('Parameter [' + (name || 'data') + '] must be of type String or Uint8Array'); + } +} +function checkMessage(message) { + if (!messageLib.Message.prototype.isPrototypeOf(message)) { + throw new Error('Parameter [message] needs to be of type Message'); + } +} +function checkCleartextMessage(message) { + if (!cleartext.CleartextMessage.prototype.isPrototypeOf(message)) { + throw new Error('Parameter [message] needs to be of type CleartextMessage'); + } +} + /** * Format user ids for internal use. */ @@ -324,7 +343,7 @@ function formatUserIds(options) { options.userIds = toArray(options.userIds); // normalize to array options.userIds = options.userIds.map(id => { if (util.isString(id) && !util.isUserId(id)) { - throw new Error('Invalid user id format!'); + throw new Error('Invalid user id format'); } if (util.isUserId(id)) { return id; // user id is already in correct format... no conversion necessary @@ -333,7 +352,7 @@ function formatUserIds(options) { id.name = id.name || ''; id.email = id.email || ''; if (!util.isString(id.name) || (id.email && !util.isEmailAddress(id.email))) { - throw new Error('Invalid user id format!'); + throw new Error('Invalid user id format'); } return id.name + ' <' + id.email + '>'; }); @@ -364,7 +383,7 @@ function createMessage(data, filename) { } else if (util.isString(data)) { msg = messageLib.fromText(data, filename); } else { - throw new Error('Data must be of type String or Uint8Array!'); + throw new Error('Data must be of type String or Uint8Array'); } return msg; } @@ -411,7 +430,7 @@ function parseMessage(message, format) { filename: message.getFilename() }; } else { - throw new Error('Invalid format!'); + throw new Error('Invalid format'); } } @@ -438,5 +457,5 @@ function onError(message, error) { // log the stack trace if (config.debug) { console.error(error.stack); } // rethrow new high level error for api users - throw new Error(message); + throw new Error(message + ': ' + error.message); } diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 4256c757..eadc56f0 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -145,20 +145,37 @@ AsyncProxy.prototype.generateKey = function(options) { }); }; -AsyncProxy.prototype.encrypt = function({ data, publicKeys, privateKeys, passwords, filename, packets }) { +AsyncProxy.prototype.encrypt = function(options) { return this.execute(() => { - if(publicKeys) { - publicKeys = publicKeys.length ? publicKeys : [publicKeys]; - publicKeys = publicKeys.map(key => key.toPacketlist()); + if(options.publicKeys) { + options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); } - if(privateKeys) { - privateKeys = privateKeys.length ? privateKeys : [privateKeys]; - privateKeys = privateKeys.map(key => key.toPacketlist()); + if(options.privateKeys) { + options.privateKeys = options.privateKeys.map(key => key.toPacketlist()); } - this.worker.postMessage({ - event:'encrypt', - options: { data, publicKeys, privateKeys, passwords, filename, packets } - }); + this.worker.postMessage({ event:'encrypt', options }); + }); +}; + +AsyncProxy.prototype.decrypt = function(options) { + return new Promise((resolve, reject) => { + if(options.publicKeys) { + options.publicKeys = options.publicKeys.map(key => key.toPacketlist()); + } + if(options.privateKey) { + options.privateKey = options.privateKey.toPacketlist(); + } + this.worker.postMessage({ event:'decrypt', options }); + + this.tasks.push({ resolve: data => { + if (data.signatures) { + data.signatures = data.signatures.map(sig => { + sig.keyid = type_keyid.fromClone(sig.keyid); + return sig; + }); + } + resolve(data); + }, reject }); }); }; @@ -172,36 +189,6 @@ AsyncProxy.prototype.encryptSessionKey = function({ sessionKey, algo, keys, pass }); }; -AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text) { - var self = this; - - return self.execute(function() { - if (!publicKeys.length) { - publicKeys = [publicKeys]; - } - publicKeys = publicKeys.map(function(key) { - return key.toPacketlist(); - }); - privateKey = privateKey.toPacketlist(); - self.worker.postMessage({ - event: 'sign-and-encrypt-message', - publicKeys: publicKeys, - privateKey: privateKey, - text: text - }); - }); -}; - -AsyncProxy.prototype.decryptMessage = function({ message, privateKey, format }) { - return this.execute(() => { - if(!(String.prototype.isPrototypeOf(privateKey) || typeof privateKey === 'string' || Uint8Array.prototype.isPrototypeOf(privateKey))) { - privateKey = privateKey.toPacketlist(); - } - - this.worker.postMessage({ event:'decrypt-message', message, privateKey, format }); - }); -}; - AsyncProxy.prototype.decryptSessionKey = function(privateKey, message) { var self = this; @@ -218,36 +205,6 @@ AsyncProxy.prototype.decryptSessionKey = function(privateKey, message) { }); }; -AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys, message) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - privateKey = privateKey.toPacketlist(); - if (!publicKeys.length) { - publicKeys = [publicKeys]; - } - publicKeys = publicKeys.map(function(key) { - return key.toPacketlist(); - }); - self.worker.postMessage({ - event: 'decrypt-and-verify-message', - privateKey: privateKey, - publicKeys: publicKeys, - message: message - }); - - self.tasks.push({ resolve:function(data) { - data.signatures = data.signatures.map(function(sig) { - sig.keyid = type_keyid.fromClone(sig.keyid); - return sig; - }); - resolve(data); - }, reject:reject }); - }); - - return promise; -}; - AsyncProxy.prototype.signClearMessage = function(privateKeys, text) { var self = this; diff --git a/src/worker/worker.js b/src/worker/worker.js index b11a8b1f..87781724 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -52,6 +52,7 @@ self.onmessage = function (event) { var data = null, err = null, msg = event.data, + opt = msg.options || {}, correct = false; switch (msg.event) { @@ -68,14 +69,41 @@ self.onmessage = function (event) { window.openpgp.crypto.random.randomBuffer.set(msg.buf); break; - case 'encrypt-message': - if(msg.keys) { - msg.keys = msg.keys.map(packetlistCloneToKey); + case 'generate-key': + window.openpgp.generateKey(opt).then(data => { + data.key = data.key.toPacketlist(); + response({ event:'method-return', data }); + }).catch(e => { + response({ event:'method-return', err:e.message }); + }); + break; + + case 'encrypt': + if(opt.publicKeys) { + opt.publicKeys = opt.publicKeys.map(packetlistCloneToKey); } - window.openpgp.encryptMessage(msg.keys, msg.data, msg.passwords, msg.params).then(function(data) { - response({event: 'method-return', data: data}); - }).catch(function(e) { - response({event: 'method-return', err: e.message}); + if(opt.privateKeys) { + opt.privateKeys = opt.privateKeys.map(packetlistCloneToKey); + } + window.openpgp.encrypt(opt).then(data => { + response({ event:'method-return', data }); + }).catch(e => { + response({ event:'method-return', err:e.message }); + }); + break; + + case 'decrypt': + if(opt.publicKeys) { + opt.publicKeys = opt.publicKeys.map(packetlistCloneToKey); + } + if(opt.privateKey) { + opt.privateKey = packetlistCloneToKey(opt.privateKey); + } + opt.message = packetlistCloneToMessage(opt.message.packets); + window.openpgp.decrypt(opt).then(data => { + response({ event:'method-return', data }); + }).catch(e => { + response({ event:'method-return', err:e.message }); }); break; @@ -90,31 +118,6 @@ self.onmessage = function (event) { }); break; - case 'sign-and-encrypt-message': - if (!msg.publicKeys.length) { - msg.publicKeys = [msg.publicKeys]; - } - msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); - msg.privateKey = packetlistCloneToKey(msg.privateKey); - window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text).then(function(data) { - response({event: 'method-return', data: data}); - }).catch(function(e) { - response({event: 'method-return', err: e.message}); - }); - break; - - case 'decrypt-message': - if(!(String.prototype.isPrototypeOf(msg.privateKey) || typeof msg.privateKey === 'string' || Uint8Array.prototype.isPrototypeOf(msg.privateKey))) { - msg.privateKey = packetlistCloneToKey(msg.privateKey); - } - msg.message = packetlistCloneToMessage(msg.message.packets); - window.openpgp.decryptMessage(msg.privateKey, msg.message, msg.params).then(function(data) { - response({event: 'method-return', data: data}); - }).catch(function(e) { - response({event: 'method-return', err: e.message}); - }); - break; - case 'decrypt-session-key': if(!(String.prototype.isPrototypeOf(msg.privateKey) || typeof msg.privateKey === 'string')) { msg.privateKey = packetlistCloneToKey(msg.privateKey); @@ -127,20 +130,6 @@ self.onmessage = function (event) { }); break; - case 'decrypt-and-verify-message': - msg.privateKey = packetlistCloneToKey(msg.privateKey); - if (!msg.publicKeys.length) { - msg.publicKeys = [msg.publicKeys]; - } - msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); - msg.message = packetlistCloneToMessage(msg.message.packets); - window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message).then(function(data) { - response({event: 'method-return', data: data}); - }).catch(function(e) { - response({event: 'method-return', err: e.message}); - }); - break; - case 'sign-clear-message': msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey); window.openpgp.signClearMessage(msg.privateKeys, msg.text).then(function(data) { @@ -164,15 +153,6 @@ self.onmessage = function (event) { }); break; - case 'generate-key': - window.openpgp.generateKey(msg.options).then(function(data) { - data.key = data.key.toPacketlist(); - response({event: 'method-return', data: data}); - }).catch(function(e) { - response({event: 'method-return', err: e.message}); - }); - break; - case 'decrypt-key': try { msg.privateKey = packetlistCloneToKey(msg.privateKey); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index dea2092f..b15992cb 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -8,7 +8,7 @@ var sinon = require('sinon'), describe('OpenPGP.js public api tests', function() { - describe('initWorker, getWorker, destroyWorker', function() { + describe('initWorker, getWorker, destroyWorker - unit tests', function() { afterEach(function() { openpgp.destroyWorker(); // cleanup worker in case of failure }); @@ -264,4 +264,220 @@ describe('OpenPGP.js public api tests', function() { }); }); + describe('encrypt, decrypt - integration tests', function() { + var pub_key = + ['-----BEGIN PGP PUBLIC KEY BLOCK-----', + 'Version: GnuPG v2.0.19 (GNU/Linux)', + '', + 'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+', + 'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5', + 'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0', + 'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS', + 'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6', + 'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki', + 'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf', + '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa', + 'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag', + 'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr', + 'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb', + 'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA', + 'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP', + 'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2', + 'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X', + 'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD', + 'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY', + 'hz3tYjKhoFTKEIq3y3Pp', + '=h/aX', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); + + var priv_key = + ['-----BEGIN PGP PRIVATE KEY BLOCK-----', + 'Version: GnuPG v2.0.19 (GNU/Linux)', + '', + 'lQH+BFJhL04BBADclrUEDDsm0PSZbQ6pml9FpzTyXiyCyDN+rMOsy9J300Oc10kt', + '/nyBej9vZSRcaW5VpNNj0iA+c1/w2FPf84zNsTzvDmuMaNHFUzky4/vkYuZra//3', + '+Ri7CF8RawSYQ/4IRbC9zqdBlzniyfQOW7Dp/LYe8eibnDSrmkQem0G0jwARAQAB', + '/gMDAu7L//czBpE40p1ZqO8K3k7UejemjsQqc7kOqnlDYd1Z6/3NEA/UM30Siipr', + 'KjdIFY5+hp0hcs6EiiNq0PDfm/W2j+7HfrZ5kpeQVxDek4irezYZrl7JS2xezaLv', + 'k0Fv/6fxasnFtjOM6Qbstu67s5Gpl9y06ZxbP3VpT62+Xeibn/swWrfiJjuGEEhM', + 'bgnsMpHtzAz/L8y6KSzViG/05hBaqrvk3/GeEA6nE+o0+0a6r0LYLTemmq6FbaA1', + 'PHo+x7k7oFcBFUUeSzgx78GckuPwqr2mNfeF+IuSRnrlpZl3kcbHASPAOfEkyMXS', + 'sWGE7grCAjbyQyM3OEXTSyqnehvGS/1RdB6kDDxGwgE/QFbwNyEh6K4eaaAThW2j', + 'IEEI0WEnRkPi9fXyxhFsCLSI1XhqTaq7iDNqJTxE+AX2b9ZuZXAxI3Tc/7++vEyL', + '3p18N/MB2kt1Wb1azmXWL2EKlT1BZ5yDaJuBQ8BhphM3tCRUZXN0IE1jVGVzdGlu', + 'Z3RvbiA8dGVzdEBleGFtcGxlLmNvbT6IuQQTAQIAIwUCUmEvTgIbLwcLCQgHAwIB', + 'BhUIAgkKCwQWAgMBAh4BAheAAAoJEEpjYTpNbkCUMAwD+gIK08qpEZSVas9qW+Ok', + '32wzNkwxe6PQgZwcyBqMQYZUcKagC8+89pMQQ5sKUGvpIgat42Tf1KLGPcvG4cDA', + 'JZ6w2PYz9YHQqPh9LA+PAnV8m25TcGmKcKgvFUqQ3U53X/Y9sBP8HooRqfwwHcv9', + 'pMgQmojmNbI4VHydRqIBePawnQH+BFJhL04BBADpH8+0EVolpPiOrXTKoBKTiyrB', + 'UyxzodyJ8zmVJ3HMTEU/vidpQwzISwoc/ndDFMXQauq6xqBCD9m2BPQI3UdQzXnb', + 'LsAI52nWCIqOkzM5NAKWoKhyXK9Y4UH4v9LAYQgl/stIISvCgG4mJ8lzzEBWvRdf', + 'Qm2Ghb64/3V5NDdemwARAQAB/gMDAu7L//czBpE40iPcpLzL7GwBbWFhSWgSLy53', + 'Md99Kxw3cApWCok2E8R9/4VS0490xKZIa5y2I/K8thVhqk96Z8Kbt7MRMC1WLHgC', + 'qJvkeQCI6PrFM0PUIPLHAQtDJYKtaLXxYuexcAdKzZj3FHdtLNWCooK6n3vJlL1c', + 'WjZcHJ1PH7USlj1jup4XfxsbziuysRUSyXkjn92GZLm+64vCIiwhqAYoizF2NHHG', + 'hRTN4gQzxrxgkeVchl+ag7DkQUDANIIVI+A63JeLJgWJiH1fbYlwESByHW+zBFNt', + 'qStjfIOhjrfNIc3RvsggbDdWQLcbxmLZj4sB0ydPSgRKoaUdRHJY0S4vp9ouKOtl', + '2au/P1BP3bhD0fDXl91oeheYth+MSmsJFDg/vZJzCJhFaQ9dp+2EnjN5auNCNbaI', + 'beFJRHFf9cha8p3hh+AK54NRCT++B2MXYf+TPwqX88jYMBv8kk8vYUgo8128r1zQ', + 'EzjviQE9BBgBAgAJBQJSYS9OAhsuAKgJEEpjYTpNbkCUnSAEGQECAAYFAlJhL04A', + 'CgkQ4IT3RGwgLJe6ogQA2aaJEIBIXtgrs+8WSJ4k3DN4rRXcXaUZf667pjdD9nF2', + '3BzjFH6Z78JIGaxRHJdM7b05aE8YuzM8f3NIlT0F0OLq/TI2muYU9f/U2DQBuf+w', + 'KTB62+PELVgi9MsXC1Qv/u/o1LZtmmxTFFOD35xKsxZZI2OJj2pQpqObW27M8Nlc', + 'BQQAw2YA3fFc38qPK+PY4rZyTRdbvjyyX+1zeqIo8wn7QCQwXs+OGaH2fGoT35AI', + 'SXuqKcWqoEuO7OBSEFThCXBfUYMC01OrqKEswPm/V3zZkLu01q12UMwZach28QwK', + '/YZly4ioND2tdazj17u2rU2dwtiHPe1iMqGgVMoQirfLc+k=', + '=lw5e', + '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); + + var passphrase = 'hello world'; + var plaintext = 'short message\nnext line\n한국어/조선말'; + var password1 = 'I am a password'; + var password2 = 'I am another password'; + + var privateKey, publicKey; + + before(function() { + openpgp.initWorker({ path:'../dist/openpgp.worker.js' }); + }); + + beforeEach(function() { + publicKey = openpgp.key.readArmored(pub_key); + expect(publicKey.keys).to.have.length(1); + expect(publicKey.err).to.not.exist; + privateKey = openpgp.key.readArmored(priv_key); + expect(privateKey.keys).to.have.length(1); + expect(privateKey.err).to.not.exist; + }); + + after(function() { + openpgp.destroyWorker(); // cleanup worker in case of failure + }); + + it('Decrypting key with wrong passphrase returns false', function () { + expect(privateKey.keys[0].decrypt('wrong passphrase')).to.be.false; + }); + + it('Decrypting key with correct passphrase returns true', function () { + expect(privateKey.keys[0].decrypt(passphrase)).to.be.true; + }); + + function testHelper(encOpt, decOpt, dontUnlock) { + if (!dontUnlock) { + expect(privateKey.keys[0].decrypt(passphrase)).to.be.true; + } + return openpgp.encrypt(encOpt).then(function(encrypted) { + expect(encrypted.data).to.exist; + var msg = openpgp.message.readArmored(encrypted.data); + expect(msg).to.exist; + + decOpt.message = msg; + return openpgp.decrypt(decOpt); + }); + } + + it('Calling decrypt with not decrypted key leads to exception', function (done) { + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + }; + var decOpt = { + privateKey: privateKey.keys[0] + }; + testHelper(encOpt, decOpt, true).catch(function(error) { + expect(error.message).to.match(/not decrypted/); + done(); + }); + }); + + it('should encrypt then decrypt with pgp key pair', function(done) { + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + }; + var decOpt = { + privateKey: privateKey.keys[0] + }; + testHelper(encOpt, decOpt).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures).to.not.exist; + done(); + }); + }); + + it('should encrypt/sign and decrypt/verify with pgp key pair', function(done) { + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + privateKeys: privateKey.keys + }; + var decOpt = { + privateKey: privateKey.keys[0], + publicKeys: publicKey.keys + }; + testHelper(encOpt, decOpt).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures[0].valid).to.be.true; + 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'; + + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + privateKeys: privateKey.keys + }; + var decOpt = { + privateKey: privateKey.keys[0], + publicKeys: openpgp.key.readArmored(wrong_pubkey).keys + }; + testHelper(encOpt, decOpt).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures[0].valid).to.be.null; + done(); + }); + }); + + it('should encrypt and decrypt with one password', function(done) { + var encOpt = { + data: plaintext, + passwords: password1 + }; + var decOpt = { + password: password1 + }; + testHelper(encOpt, decOpt).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); + }); + + it('should encrypt and decrypt with two password2', function(done) { + var encOpt = { + data: plaintext, + passwords: [password1, password2] + }; + var decOpt = { + password: password2 + }; + testHelper(encOpt, decOpt).then(function(decrypted) { + expect(decrypted.data).to.equal(plaintext); + done(); + }); + }); + }); + });