From 57d07091e82d5b74fa1ff4d8a8784bda4a3049fc Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 3 Mar 2017 16:54:08 -0800 Subject: [PATCH 01/11] add support for detached signatures --- src/encoding/armor.js | 20 ++++++++--- src/enums.js | 3 +- src/message.js | 67 +++++++++++++++++++++++++++++++++++-- src/signature.js | 78 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 src/signature.js diff --git a/src/encoding/armor.js b/src/encoding/armor.js index 70af9a78..099fd074 100644 --- a/src/encoding/armor.js +++ b/src/encoding/armor.js @@ -62,10 +62,7 @@ function getType(text) { return enums.armor.multipart_last; } else - // BEGIN PGP SIGNATURE - // Used for detached signatures, OpenPGP/MIME signatures, and - // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE - // for detached signatures. + // BEGIN PGP SIGNED MESSAGE if (/SIGNED MESSAGE/.test(header[1])) { return enums.armor.signed; @@ -86,6 +83,14 @@ function getType(text) { // Used for armoring private keys. if (/PRIVATE KEY BLOCK/.test(header[1])) { return enums.armor.private_key; + + } else + // BEGIN PGP SIGNATURE + // Used for detached signatures, OpenPGP/MIME signatures, and + // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE + // for detached signatures. + if (/SIGNATURE/.test(header[1])) { + return enums.armor.signature; } } @@ -397,6 +402,13 @@ function armor(messagetype, body, partindex, parttotal) { result.push("\r\n=" + getCheckSum(body) + "\r\n"); result.push("-----END PGP PRIVATE KEY BLOCK-----\r\n"); break; + case enums.armor.signature: + result.push("-----BEGIN PGP SIGNATURE-----\r\n"); + result.push(addheader()); + result.push(base64.encode(body)); + result.push("\r\n=" + getCheckSum(body) + "\r\n"); + result.push("-----END PGP SIGNATURE-----\r\n"); + break; } return result.join(''); diff --git a/src/enums.js b/src/enums.js index 837c6649..861f2f01 100644 --- a/src/enums.js +++ b/src/enums.js @@ -297,7 +297,8 @@ export default { signed: 2, message: 3, public_key: 4, - private_key: 5 + private_key: 5, + signature: 6 }, /** Asserts validity and converts from string/integer to integer. */ diff --git a/src/message.js b/src/message.js index 31b09c75..387e0b59 100644 --- a/src/message.js +++ b/src/message.js @@ -32,6 +32,7 @@ import enums from './enums.js'; import armor from './encoding/armor.js'; import config from './config'; import crypto from './crypto'; +import signature from './signature.js'; import * as keyModule from './key.js'; /** @@ -344,19 +345,81 @@ Message.prototype.sign = function(privateKeys) { return new Message(packetlist); }; +/** + * Create a detached signature for the message (the literal data packet of the message) + * @param {Array} privateKey private keys with decrypted secret key data for signing + * @return {module:signature~Signature} new detached signature of message content + */ +Message.prototype.signDetached = function(privateKeys) { + + var packetlist = new packet.List(); + + var literalDataPacket = this.packets.findPacket(enums.packet.literal); + if (!literalDataPacket) { + throw new Error('No literal data packet to sign.'); + } + + var literalFormat = enums.write(enums.literal, literalDataPacket.format); + var signatureType = literalFormat === enums.literal.binary ? + enums.signature.binary : enums.signature.text; + + for (var i = 0; i < privateKeys.length; i++) { + var signingKeyPacket = privateKeys[i].getSigningKeyPacket(); + var signaturePacket = new packet.Signature(); + signaturePacket.signatureType = signatureType; + signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; + if (!signingKeyPacket.isDecrypted) { + throw new Error('Private key is not decrypted.'); + } + signaturePacket.sign(signingKeyPacket, literalDataPacket); + packetlist.push(signaturePacket); + } + + return new signature.Signature(packetlist); +}; + + /** * Verify message signatures * @param {Array} keys array of keys to verify signatures * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ Message.prototype.verify = function(keys) { - var result = []; var msg = this.unwrapCompressed(); var literalDataList = msg.packets.filterByTag(enums.packet.literal); if (literalDataList.length !== 1) { throw new Error('Can only verify message with one literal data packet.'); } var signatureList = msg.packets.filterByTag(enums.packet.signature); + return createVerificationObjects(signatureList, literalDataList, keys); +}; + +/** + * Verify detached message signature + * @param {Array} keys array of keys to verify signatures + * @param {Signature} + * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature + */ +Message.prototype.verifyDetached = function(signature, keys) { + var msg = this.unwrapCompressed(); + var literalDataList = msg.packets.filterByTag(enums.packet.literal); + if (literalDataList.length !== 1) { + throw new Error('Can only verify message with one literal data packet.'); + } + var signatureList = signature.packets; + return createVerificationObjects(signatureList, literalDataList, keys); +}; + +/** + * Create list of objects containing signer's keyid and validity of signature + * @param {Array} signatureList array of signature packets + * @param {Array} literalDataList array of literal data packets + * @param {Array} keys array of keys to verify signatures + * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature + */ +function createVerificationObjects(signatureList, literalDataList, keys) { + var result = []; for (var i = 0; i < signatureList.length; i++) { var keyPacket = null; for (var j = 0; j < keys.length; j++) { @@ -377,7 +440,7 @@ Message.prototype.verify = function(keys) { result.push(verifiedSig); } return result; -}; +} /** * Unwrap compressed message diff --git a/src/signature.js b/src/signature.js new file mode 100644 index 00000000..342d7e68 --- /dev/null +++ b/src/signature.js @@ -0,0 +1,78 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * @requires config + * @requires crypto + * @requires encoding/armor + * @requires enums + * @requires packet + * @module signature + */ + +'use strict'; + +import packet from './packet'; +import enums from './enums.js'; +import armor from './encoding/armor.js'; + +/** + * @class + * @classdesc Class that represents an OpenPGP message. + * Can be an encrypted message, signed message, compressed message or literal message + * @param {module:packet/packetlist} packetlist The packets that form this message + * See {@link http://tools.ietf.org/html/rfc4880#section-11.3} + */ + +export function Signature(packetlist) { + if (!(this instanceof Signature)) { + return new Signature(packetlist); + } + this.packets = packetlist || new packet.List(); +} + + +/** + * Returns ASCII armored text of signature + * @return {String} ASCII armor + */ +Signature.prototype.armor = function() { + return armor.encode(enums.armor.signature, this.packets.write()); +}; + +/** + * reads an OpenPGP armored signature and returns a signature object + * @param {String} armoredText text to be parsed + * @return {module:signature~Signature} new signature object + * @static + */ +export function readArmored(armoredText) { + var input = armor.decode(armoredText).data; + return read(input); +} + +/** + * reads an OpenPGP signature as byte array and returns a signature object + * @param {Uint8Array} input binary signature + * @return {Signature} new signature object + * @static + */ +export function read(input) { + var packetlist = new packet.List(); + packetlist.read(input); + return new Signature(packetlist); +} From 7b58cd9200b7ce3bd922d0779facb33fe2dd078c Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 10:15:01 -0800 Subject: [PATCH 02/11] add tests --- src/message.js | 4 ++-- test/general/signature.js | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/message.js b/src/message.js index 387e0b59..ee5248b2 100644 --- a/src/message.js +++ b/src/message.js @@ -32,7 +32,7 @@ import enums from './enums.js'; import armor from './encoding/armor.js'; import config from './config'; import crypto from './crypto'; -import signature from './signature.js'; +import * as sigModule from './signature.js'; import * as keyModule from './key.js'; /** @@ -376,7 +376,7 @@ Message.prototype.signDetached = function(privateKeys) { packetlist.push(signaturePacket); } - return new signature.Signature(packetlist); + return new sigModule.Signature(packetlist); }; diff --git a/test/general/signature.js b/test/general/signature.js index 15c72784..6f58375a 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -595,7 +595,7 @@ describe("Signature", function() { expect(pubKey.users[0].selfCertifications).to.eql(pubKey2.users[0].selfCertifications); }); - it('Verify a detached signature', function() { + it('Verify a detached signature using readSignedContent', function() { var detachedSig = ['-----BEGIN PGP SIGNATURE-----', 'Version: GnuPG v1.4.13 (Darwin)', 'Comment: GPGTools - https://gpgtools.org', @@ -641,6 +641,30 @@ describe("Signature", function() { expect(result[0].valid).to.be.true; }); + it('Detached signature signing and verification cleartext', function () { + var msg = openpgp.message.fromText('hello'); + var pubKey2 = openpgp.key.readArmored(pub_key_arm2).keys[0]; + var privKey2 = openpgp.key.readArmored(priv_key_arm2).keys[0]; + privKey2.decrypt('hello world'); + + var detachedSig = msg.signDetached([privKey2]); + + var result = msg.verifyDetached(detachedSig, [pubKey2]); + expect(result[0].valid).to.be.true; + }); + + it('Detached signature signing and verification encrypted', function () { + var msg = openpgp.message.fromText('hello'); + var pubKey2 = openpgp.key.readArmored(pub_key_arm2).keys[0]; + var privKey2 = openpgp.key.readArmored(priv_key_arm2).keys[0]; + privKey2.decrypt('hello world'); + msg.encrypt({keys: [pubKey2] }); + + var detachedSig = msg.signDetached([privKey2]); + var result = msg.verifyDetached(detachedSig, [pubKey2]); + expect(result[0].valid).to.be.true; + }); + it('Sign message with key without password', function(done) { var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null}; if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys From 4ed7f258364c9ca8e5286a7fd792a103ddf2b4ce Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 10:46:50 -0800 Subject: [PATCH 03/11] more tests --- test/general/signature.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/general/signature.js b/test/general/signature.js index 6f58375a..0c0d4a95 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -647,10 +647,15 @@ describe("Signature", function() { var privKey2 = openpgp.key.readArmored(priv_key_arm2).keys[0]; privKey2.decrypt('hello world'); - var detachedSig = msg.signDetached([privKey2]); - - var result = msg.verifyDetached(detachedSig, [pubKey2]); - expect(result[0].valid).to.be.true; + var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null}; + if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys + openpgp.generateKey(opt).then(function(gen) { + var generatedKey = gen.key; + var detachedSig = msg.signDetached([generatedKey, privKey2]); + var result = msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]); + expect(result[0].valid).to.be.true; + expect(result[1].valid).to.be.true; + }); }); it('Detached signature signing and verification encrypted', function () { @@ -661,8 +666,15 @@ describe("Signature", function() { msg.encrypt({keys: [pubKey2] }); var detachedSig = msg.signDetached([privKey2]); - var result = msg.verifyDetached(detachedSig, [pubKey2]); - expect(result[0].valid).to.be.true; + + var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null}; + if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys + openpgp.generateKey(opt).then(function(gen) { + var key = gen.key; + var result = msg.verifyDetached(detachedSig, [pubKey2, key.toPublic()]); + expect(result[0].valid).to.be.true; + expect(result[0].valid).to.be.false; + }); }); it('Sign message with key without password', function(done) { From 4fd4c13ddd6d7ff9896a60a76a6678d04d9f2c3f Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 13:20:58 -0800 Subject: [PATCH 04/11] refactor cleartext message to use signature object instead of packetlist --- src/cleartext.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index d84da8d5..712ce83b 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -29,23 +29,26 @@ import config from './config'; import packet from './packet'; import enums from './enums.js'; import armor from './encoding/armor.js'; +import * as sigModule from './signature.js'; /** * @class * @classdesc Class that represents an OpenPGP cleartext signed message. * See {@link http://tools.ietf.org/html/rfc4880#section-7} * @param {String} text The cleartext of the signed message - * @param {module:packet/packetlist} packetlist The packetlist with signature packets or undefined - * if message not yet signed + * @param {module:Signature} signature The detached signature or an empty signature if message not yet signed */ -export function CleartextMessage(text, packetlist) { +export function CleartextMessage(text, signature) { if (!(this instanceof CleartextMessage)) { - return new CleartextMessage(text, packetlist); + return new CleartextMessage(text, signature); } // normalize EOL to canonical form this.text = text.replace(/\r/g, '').replace(/[\t ]+\n/g, "\n").replace(/\n/g,"\r\n"); - this.packets = packetlist || new packet.List(); + if (signature && !(signature instanceof sigModule.Signature)) { + throw new Error('Invalid signature input'); + } + this.signature = signature || new sigModule.Signature([]); } /** @@ -54,7 +57,7 @@ export function CleartextMessage(text, packetlist) { */ CleartextMessage.prototype.getSigningKeyIds = function() { var keyIds = []; - var signatureList = this.packets.filterByTag(enums.packet.signature); + var signatureList = this.signature.packets; signatureList.forEach(function(packet) { keyIds.push(packet.issuerKeyId); }); @@ -84,7 +87,7 @@ CleartextMessage.prototype.sign = function(privateKeys) { signaturePacket.sign(signingKeyPacket, literalDataPacket); packetlist.push(signaturePacket); } - this.packets = packetlist; + this.signature = new sigModule.Signature(packetlist); }; /** @@ -94,7 +97,7 @@ CleartextMessage.prototype.sign = function(privateKeys) { */ CleartextMessage.prototype.verify = function(keys) { var result = []; - var signatureList = this.packets.filterByTag(enums.packet.signature); + var signatureList = this.signature.packets; var literalDataPacket = new packet.Literal(); // we assume that cleartext signature is generated based on UTF8 cleartext literalDataPacket.setText(this.text); @@ -137,7 +140,7 @@ CleartextMessage.prototype.armor = function() { var body = { hash: enums.read(enums.hash, config.prefer_hash_algorithm).toUpperCase(), text: this.text, - data: this.packets.write() + data: this.signature.packets.write() }; return armor.encode(enums.armor.signed, body); }; @@ -157,7 +160,8 @@ export function readArmored(armoredText) { var packetlist = new packet.List(); packetlist.read(input.data); verifyHeaders(input.headers, packetlist); - var newMessage = new CleartextMessage(input.text, packetlist); + var signature = new sigModule.Signature(packetlist); + var newMessage = new CleartextMessage(input.text, signature); return newMessage; } From c3908cbec0300e11ca8958e889edcc0dfb9a318a Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 16:12:55 -0800 Subject: [PATCH 05/11] add detached sign/verify options to cleartext --- src/cleartext.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index 712ce83b..3b705926 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -69,6 +69,15 @@ CleartextMessage.prototype.getSigningKeyIds = function() { * @param {Array} privateKeys private keys with decrypted secret key data for signing */ CleartextMessage.prototype.sign = function(privateKeys) { + this.signature = this.signDetached(privateKeys); +}; + +/** + * Sign the cleartext message + * @param {Array} privateKeys private keys with decrypted secret key data for signing + * @return {module:signature~Signature} new detached signature of message content + */ +CleartextMessage.prototype.signDetached = function(privateKeys) { var packetlist = new packet.List(); var literalDataPacket = new packet.Literal(); literalDataPacket.setText(this.text); @@ -87,7 +96,7 @@ CleartextMessage.prototype.sign = function(privateKeys) { signaturePacket.sign(signingKeyPacket, literalDataPacket); packetlist.push(signaturePacket); } - this.signature = new sigModule.Signature(packetlist); + return new sigModule.Signature(packetlist); }; /** @@ -96,8 +105,17 @@ CleartextMessage.prototype.sign = function(privateKeys) { * @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature */ CleartextMessage.prototype.verify = function(keys) { + return this.verifyDetached(this.signature, keys); +}; + +/** + * Verify signatures of cleartext signed message + * @param {Array} keys array of keys to verify signatures + * @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature + */ +CleartextMessage.prototype.verifyDetached = function(signature, keys) { var result = []; - var signatureList = this.signature.packets; + var signatureList = signature.packets; var literalDataPacket = new packet.Literal(); // we assume that cleartext signature is generated based on UTF8 cleartext literalDataPacket.setText(this.text); From 43355e079fd9dbea4aaac013dda6c7b4beced93c Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 16:36:57 -0800 Subject: [PATCH 06/11] add detached sig option to upper level functions --- src/openpgp.js | 95 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/src/openpgp.js b/src/openpgp.js index b027991e..303ae718 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -178,36 +178,44 @@ export function decryptKey({ privateKey, passphrase }) { * @param {Key|Array} privateKeys (optional) private keys for signing. If omitted message will not be signed * @param {String|Array} passwords (optional) array of passwords or a single password to encrypt the message * @param {String} filename (optional) a filename for the literal data packet - * @param {Boolean} armor (optional) if the return value should be ascii armored or the message object - * @return {Promise} encrypted ASCII armored message, or the full Message object if 'armor' is false + * @param {Boolean} armor (optional) if the return values should be ascii armored or the message/signature objects + * @param {Boolean} detached (optional) if the signature should be detached (if true, signature will be added to returned object) + * @return {Promise} encrypted (and optionally signed message) in the form: + * {data: ASCII armored message if 'armor' is true, + * message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true} * @static */ -export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true }) { +export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true, detached=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, filename, armor }); } - + var result = {}; return Promise.resolve().then(() => { let message = createMessage(data, filename); if (privateKeys) { // sign the message only if private keys are specified - message = message.sign(privateKeys); + if (detached) { + var signature = message.signDetached(privateKeys); + if (armor) { + result.signature = signature.armor(); + } else { + result.signature = signature; + } + } else { + message = message.sign(privateKeys); + } } return message.encrypt(publicKeys, passwords); }).then(message => { - - if(armor) { - return { - data: message.armor() - }; + if (armor) { + result.data = message.armor(); + } else { + result.message = message; } - return { - message: message - }; - + return result; }).catch(onError.bind(null, 'Error encrypting message')); } @@ -220,11 +228,12 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar * @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String } * @param {String} password (optional) single password to decrypt the message * @param {String} format (optional) return data format either as 'utf8' or 'binary' + * @param {Signature} signature (optional) detached signature for verification * @return {Promise} decrypted and verified message in the form: * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] } * @static */ -export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8' }) { +export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8', signature=null }) { checkMessage(message); publicKeys = toArray(publicKeys); if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported @@ -235,7 +244,12 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, const result = parseMessage(message, format); if (publicKeys && result.data) { // verify only if publicKeys are specified - result.signatures = message.verify(publicKeys); + if (signature) { + //detached signature + result.signatures = message.verifyDetached(signature, publicKeys); + } else { + result.signatures = message.verify(publicKeys); + } } return result; @@ -255,10 +269,12 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, * @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 + * @return {Promise} signed cleartext in the form: + * {data: ASCII armored message if 'armor' is true, + * message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true} * @static */ -export function sign({ data, privateKeys, armor=true }) { +export function sign({ data, privateKeys, armor=true, detached=false}) { checkString(data); privateKeys = toArray(privateKeys); @@ -266,19 +282,28 @@ export function sign({ data, privateKeys, armor=true }) { return asyncProxy.delegate('sign', { data, privateKeys, armor }); } + var result = {}; return execute(() => { const cleartextMessage = new cleartext.CleartextMessage(data); - cleartextMessage.sign(privateKeys); - if(armor) { - return { - data: cleartextMessage.armor() - }; + if (detached) { + var signature = cleartextMessage.signDetached(privateKeys); + if (armor) { + result.signature = signature.armor(); + } else { + result.signature = signature; + } + } else { + cleartextMessage.sign(privateKeys); } - return { - message: cleartextMessage - }; + + if (armor) { + result.data = cleartextMessage.armor(); + } else { + result.message = cleartextMessage; + } + return result; }, 'Error signing cleartext message'); } @@ -287,11 +312,12 @@ export function sign({ data, privateKeys, armor=true }) { * Verifies signatures of cleartext signed message * @param {Key|Array} publicKeys array of publicKeys or single key, to verify signatures * @param {CleartextMessage} message cleartext message object with signatures + * @param {Signature} signature (optional) detached signature for verification * @return {Promise} cleartext with status of verified signatures in the form of: * { data:String, signatures: [{ keyid:String, valid:Boolean }] } * @static */ -export function verify({ message, publicKeys }) { +export function verify({ message, publicKeys, signature=null }) { checkCleartextMessage(message); publicKeys = toArray(publicKeys); @@ -299,12 +325,19 @@ export function verify({ message, publicKeys }) { return asyncProxy.delegate('verify', { message, publicKeys }); } - return execute(() => ({ + var result = {}; + return execute(() => { + result.data = message.getText(); - data: message.getText(), - signatures: message.verify(publicKeys) + if (signature) { + //detached signature + result.signatures = message.verifyDetached(signature, publicKeys); + } else { + result.signatures = message.verify(publicKeys); + } + return result; - }), 'Error verifying cleartext signed message'); + }, 'Error verifying cleartext signed message'); } From 7bf697769d04f70087caac5704dc09b04d687402 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 20:02:05 -0800 Subject: [PATCH 07/11] added detached sig tests for high level openpgpjs functions --- test/general/openpgp.js | 110 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 9cde0211..209e9ab1 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -663,6 +663,29 @@ describe('OpenPGP.js public api tests', function() { }); }); + it('should encrypt/sign and decrypt/verify with detached signatures', function(done) { + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + privateKeys: privateKey.keys, + detached: true + }; + var decOpt = { + privateKey: privateKey.keys[0], + publicKeys: publicKey.keys + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = openpgp.message.readArmored(encrypted.data); + decOpt.signature = openpgp.signature.readArmored(encrypted.signature); + return openpgp.decrypt(decOpt); + }).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].getSigningKeyPacket().getKeyId().toHex()); + done(); + }); + }); + it('should fail to verify decrypted data with wrong public pgp key', function(done) { var encOpt = { data: plaintext, @@ -684,6 +707,29 @@ describe('OpenPGP.js public api tests', function() { }); }); + it('should fail to verify decrypted data with wrong public pgp key with detached signatures', function(done) { + var encOpt = { + data: plaintext, + publicKeys: publicKey.keys, + privateKeys: privateKey.keys, + detached: true + }; + var decOpt = { + privateKey: privateKey.keys[0], + publicKeys: openpgp.key.readArmored(wrong_pubkey).keys + }; + openpgp.encrypt(encOpt).then(function(encrypted) { + decOpt.message = openpgp.message.readArmored(encrypted.data); + decOpt.signature = openpgp.signature.readArmored(encrypted.signature); + return openpgp.decrypt(decOpt); + }).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].getSigningKeyPacket().getKeyId().toHex()); + done(); + }); + }); + it('should sign and verify cleartext data', function(done) { var signOpt = { data: plaintext, @@ -704,6 +750,27 @@ describe('OpenPGP.js public api tests', function() { }); }); + it('should sign and verify cleartext data with detached signatures', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys, + detached: true + }; + var verifyOpt = { + publicKeys: publicKey.keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = openpgp.cleartext.readArmored(signed.data); + verifyOpt.signature = openpgp.signature.readArmored(signed.signature); + 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].getSigningKeyPacket().getKeyId().toHex()); + done(); + }); + }); + it('should sign and fail to verify cleartext data with wrong public pgp key', function(done) { var signOpt = { data: plaintext, @@ -723,6 +790,27 @@ describe('OpenPGP.js public api tests', function() { }); }); + it('should sign and fail to verify cleartext data with wrong public pgp key with detached signature', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys, + detached: true + }; + var verifyOpt = { + publicKeys: openpgp.key.readArmored(wrong_pubkey).keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = openpgp.cleartext.readArmored(signed.data); + verifyOpt.signature = openpgp.signature.readArmored(signed.signature); + 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].getSigningKeyPacket().getKeyId().toHex()); + done(); + }); + }); + it('should sign and verify cleartext data and not armor', function(done) { var signOpt = { data: plaintext, @@ -742,6 +830,28 @@ describe('OpenPGP.js public api tests', function() { done(); }); }); + + it('should sign and verify cleartext data and not armor with detached signatures', function(done) { + var signOpt = { + data: plaintext, + privateKeys: privateKey.keys, + detached: true, + armor: false + }; + var verifyOpt = { + publicKeys: publicKey.keys + }; + openpgp.sign(signOpt).then(function(signed) { + verifyOpt.message = signed.message; + verifyOpt.signature = signed.signature; + 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].getSigningKeyPacket().getKeyId().toHex()); + done(); + }); + }); }); describe('ELG / DSA encrypt, decrypt, sign, verify', function() { From 7d02154dc959c246b2b01d975648ee94f19031f5 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 6 Mar 2017 20:04:10 -0800 Subject: [PATCH 08/11] small fixes --- src/cleartext.js | 2 +- src/encoding/armor.js | 1 + src/index.js | 7 +++++++ src/signature.js | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index 3b705926..7c17ddf6 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -48,7 +48,7 @@ export function CleartextMessage(text, signature) { if (signature && !(signature instanceof sigModule.Signature)) { throw new Error('Invalid signature input'); } - this.signature = signature || new sigModule.Signature([]); + this.signature = signature || new sigModule.Signature(new packet.List()); } /** diff --git a/src/encoding/armor.js b/src/encoding/armor.js index 099fd074..68607308 100644 --- a/src/encoding/armor.js +++ b/src/encoding/armor.js @@ -38,6 +38,7 @@ import config from '../config'; * 3 = PGP MESSAGE * 4 = PUBLIC KEY BLOCK * 5 = PRIVATE KEY BLOCK + * 6 = SIGNATURE */ function getType(text) { var reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$\n/m; diff --git a/src/index.js b/src/index.js index 32832af5..7472326b 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,13 @@ export * from './openpgp'; import * as keyMod from './key'; export const key = keyMod; +/** + * @see module:signature + * @name module:openpgp.signature + */ +import * as signatureMod from './signature'; +export const signature = signatureMod; + /** * @see module:message * @name module:openpgp.message diff --git a/src/signature.js b/src/signature.js index 342d7e68..f0963025 100644 --- a/src/signature.js +++ b/src/signature.js @@ -32,7 +32,7 @@ import armor from './encoding/armor.js'; /** * @class - * @classdesc Class that represents an OpenPGP message. + * @classdesc Class that represents an OpenPGP signature. * Can be an encrypted message, signed message, compressed message or literal message * @param {module:packet/packetlist} packetlist The packets that form this message * See {@link http://tools.ietf.org/html/rfc4880#section-11.3} From 465d4643a8ef993fdcabfb61a5fe0e94d3140b6a Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Tue, 7 Mar 2017 16:51:08 -0800 Subject: [PATCH 09/11] add clone to packetlist support for signature objects --- src/packet/clone.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/packet/clone.js b/src/packet/clone.js index 0fefe29f..1451e642 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -26,6 +26,7 @@ import * as key from '../key.js'; import * as message from '../message.js'; import * as cleartext from '../cleartext.js'; +import * as signature from '../signature.js' import Packetlist from './packetlist.js'; import type_keyid from '../type/keyid.js'; @@ -55,6 +56,17 @@ export function clonePackets(options) { if (options.key) { options.key = options.key.toPacketlist(); } + if (options.message) { + //could be either a Message or CleartextMessage object + if (options.message instanceof message.Message) { + options.message = options.message.packets; + } else { + options.message.signature = options.message.signature.packets; + } + } + if (options.signature) { + options.signature = options.signature.packets; + } return options; } @@ -91,7 +103,10 @@ export function parseClonedPackets(options, method) { options.message = packetlistCloneToMessage(options.message); } if (options.signatures) { - options.signatures = options.signatures.map(packetlistCloneToSignature); + options.signatures = options.signatures.map(packetlistCloneToSignatures); + } + if (options.signature) { + options.signature = packetlistCloneToSignature(options.signature); } return options; } @@ -102,16 +117,22 @@ function packetlistCloneToKey(clone) { } function packetlistCloneToMessage(clone) { - const packetlist = Packetlist.fromStructuredClone(clone.packets); + const packetlist = Packetlist.fromStructuredClone(clone); return new message.Message(packetlist); } function packetlistCloneToCleartextMessage(clone) { - var packetlist = Packetlist.fromStructuredClone(clone.packets); - return new cleartext.CleartextMessage(clone.text, packetlist); + var packetlist = Packetlist.fromStructuredClone(clone.signature); + return new cleartext.CleartextMessage(clone.text, new signature.Signature(packetlist)); } -function packetlistCloneToSignature(clone) { +//verification objects +function packetlistCloneToSignatures(clone) { clone.keyid = type_keyid.fromClone(clone.keyid); return clone; } + +function packetlistCloneToSignature(clone) { + var packetlist = Packetlist.fromStructuredClone(clone); + return new signature.Signature(packetlist); +} From da9e3c62648e093d845bf56c1539eb657d7050ea Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Tue, 7 Mar 2017 19:10:33 -0800 Subject: [PATCH 10/11] small fixes --- src/openpgp.js | 8 ++++---- src/packet/clone.js | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/openpgp.js b/src/openpgp.js index 303ae718..95275b9a 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -189,7 +189,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar 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, filename, armor }); + return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor, detached }); } var result = {}; return Promise.resolve().then(() => { @@ -237,7 +237,7 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, checkMessage(message); publicKeys = toArray(publicKeys); if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported - return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format }); + return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format, signature }); } return message.decrypt(privateKey, sessionKey, password).then(message => { @@ -279,7 +279,7 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { privateKeys = toArray(privateKeys); if (asyncProxy) { // use web worker if available - return asyncProxy.delegate('sign', { data, privateKeys, armor }); + return asyncProxy.delegate('sign', { data, privateKeys, armor, detached }); } var result = {}; @@ -322,7 +322,7 @@ export function verify({ message, publicKeys, signature=null }) { publicKeys = toArray(publicKeys); if (asyncProxy) { // use web worker if available - return asyncProxy.delegate('verify', { message, publicKeys }); + return asyncProxy.delegate('verify', { message, publicKeys, signature }); } var result = {}; diff --git a/src/packet/clone.js b/src/packet/clone.js index 1451e642..05855be4 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -60,11 +60,11 @@ export function clonePackets(options) { //could be either a Message or CleartextMessage object if (options.message instanceof message.Message) { options.message = options.message.packets; - } else { + } else if (options.message instanceof cleartext.CleartextMessage) { options.message.signature = options.message.signature.packets; } } - if (options.signature) { + if (options.signature && (options.signature instanceof signature.Signature)) { options.signature = options.signature.packets; } return options; @@ -133,6 +133,10 @@ function packetlistCloneToSignatures(clone) { } function packetlistCloneToSignature(clone) { + if (typeof clone === "string") { + //signature is armored + return clone; + } var packetlist = Packetlist.fromStructuredClone(clone); return new signature.Signature(packetlist); } From 4ec07672a7d818de593b98a48fee5920a32858c4 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 10 Mar 2017 10:38:59 -0800 Subject: [PATCH 11/11] return signature object inside verification object --- src/cleartext.js | 2 ++ src/message.js | 3 +++ src/openpgp.js | 2 +- src/packet/clone.js | 8 ++++++++ src/signature.js | 4 +--- test/general/openpgp.js | 21 ++++++++++++++++++++- test/general/signature.js | 11 +++++++++++ 7 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index 7c17ddf6..21a91a7f 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -136,6 +136,8 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys) { verifiedSig.keyid = signatureList[i].issuerKeyId; verifiedSig.valid = null; } + verifiedSig.signature = new sigModule.Signature([signatureList[i]]); + result.push(verifiedSig); } return result; diff --git a/src/message.js b/src/message.js index ee5248b2..8580fd36 100644 --- a/src/message.js +++ b/src/message.js @@ -431,12 +431,15 @@ function createVerificationObjects(signatureList, literalDataList, keys) { var verifiedSig = {}; if (keyPacket) { + //found a key packet that matches keyId of signature verifiedSig.keyid = signatureList[i].issuerKeyId; verifiedSig.valid = signatureList[i].verify(keyPacket, literalDataList[0]); } else { verifiedSig.keyid = signatureList[i].issuerKeyId; verifiedSig.valid = null; } + verifiedSig.signature = new sigModule.Signature([signatureList[i]]); + result.push(verifiedSig); } return result; diff --git a/src/openpgp.js b/src/openpgp.js index 95275b9a..58656bf8 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -243,7 +243,7 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, return message.decrypt(privateKey, sessionKey, password).then(message => { const result = parseMessage(message, format); - if (publicKeys && result.data) { // verify only if publicKeys are specified + if (result.data) { // verify if (signature) { //detached signature result.signatures = message.verifyDetached(signature, publicKeys); diff --git a/src/packet/clone.js b/src/packet/clone.js index 05855be4..17c89867 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -67,9 +67,16 @@ export function clonePackets(options) { if (options.signature && (options.signature instanceof signature.Signature)) { options.signature = options.signature.packets; } + if (options.signatures) { + options.signatures = options.signatures.map(sig => verificationObjectToClone(sig)); + } return options; } +function verificationObjectToClone(verObject) { + verObject.signature = verObject.signature.packets; + return verObject; +} ////////////////////////////// // // @@ -129,6 +136,7 @@ function packetlistCloneToCleartextMessage(clone) { //verification objects function packetlistCloneToSignatures(clone) { clone.keyid = type_keyid.fromClone(clone.keyid); + clone.signature = new signature.Signature(clone.signature); return clone; } diff --git a/src/signature.js b/src/signature.js index f0963025..69ccf58d 100644 --- a/src/signature.js +++ b/src/signature.js @@ -33,9 +33,7 @@ import armor from './encoding/armor.js'; /** * @class * @classdesc Class that represents an OpenPGP signature. - * Can be an encrypted message, signed message, compressed message or literal message - * @param {module:packet/packetlist} packetlist The packets that form this message - * See {@link http://tools.ietf.org/html/rfc4880#section-11.3} + * @param {module:packet/packetlist} packetlist The signature packets */ export function Signature(packetlist) { diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 209e9ab1..917a0aae 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -637,7 +637,8 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures).to.not.exist; + expect(decrypted.signatures).to.exist; + expect(decrypted.signatures.length).to.equal(0); done(); }); }); @@ -659,6 +660,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -682,6 +684,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -703,6 +706,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -726,6 +730,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -746,6 +751,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -767,6 +773,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -786,6 +793,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -807,6 +815,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -827,6 +836,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -849,6 +859,7 @@ describe('OpenPGP.js public api tests', function() { 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].getSigningKeyPacket().getKeyId().toHex()); + expect(verified.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -872,6 +883,9 @@ describe('OpenPGP.js public api tests', function() { }).then(function(encrypted) { expect(encrypted.data).to.exist; expect(encrypted.data).to.equal(plaintext); + expect(encrypted.signatures[0].valid).to.be.true; + expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); + expect(encrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -938,6 +952,7 @@ describe('OpenPGP.js public api tests', function() { openpgp.decrypt({ privateKey:privKey, message:message }).then(function(decrypted) { expect(decrypted.data).to.equal('hello 3des\n'); + expect(decrypted.signatures.length).to.equal(0); done(); }); }); @@ -957,6 +972,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures.length).to.equal(0); done(); }); }); @@ -974,6 +990,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures.length).to.equal(0); done(); }); }); @@ -992,6 +1009,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); + expect(decrypted.signatures.length).to.equal(0); done(); }); }); @@ -1015,6 +1033,7 @@ describe('OpenPGP.js public api tests', function() { expect(encOpt.data.byteLength).to.equal(0); // transfered buffer should be empty } expect(decrypted.data).to.deep.equal(new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01])); + expect(decrypted.signatures.length).to.equal(0); done(); }); }); diff --git a/test/general/signature.js b/test/general/signature.js index 0c0d4a95..052126ee 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -265,6 +265,7 @@ describe("Signature", function() { openpgp.decrypt({ privateKey: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) { expect(decrypted.data).to.exist; expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -309,6 +310,7 @@ describe("Signature", function() { expect(verified).to.exist; expect(verified).to.have.length(1); expect(verified[0].valid).to.be.true; + expect(verified[0].signature.packets.length).to.equal(1); done(); }); }); @@ -333,6 +335,7 @@ describe("Signature", function() { expect(verified).to.exist; expect(verified).to.have.length(1); expect(verified[0].valid).to.be.true; + expect(verified[0].signature.packets.length).to.equal(1); done(); }); @@ -356,6 +359,7 @@ describe("Signature", function() { expect(verified).to.exist; expect(verified).to.have.length(1); expect(verified[0].valid).to.be.true; + expect(verified[0].signature.packets.length).to.equal(1); done(); }); @@ -390,6 +394,7 @@ describe("Signature", function() { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); }); @@ -426,6 +431,7 @@ describe("Signature", function() { expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].signature.packets.length).to.equal(1); done(); }); @@ -469,6 +475,8 @@ describe("Signature", function() { expect(verifiedSig).to.have.length(2); expect(verifiedSig[0].valid).to.be.true; expect(verifiedSig[1].valid).to.be.true; + expect(verifiedSig[0].signature.packets.length).to.equal(1); + expect(verifiedSig[1].signature.packets.length).to.equal(1); done(); }); @@ -513,6 +521,8 @@ describe("Signature", function() { expect(cleartextSig.signatures).to.have.length(2); expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[1].valid).to.be.true; + expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); + expect(cleartextSig.signatures[1].signature.packets.length).to.equal(1); done(); }); }); @@ -533,6 +543,7 @@ describe("Signature", function() { expect(cleartextSig.data).to.equal(plaintext.replace(/\r/g,'')); expect(cleartextSig.signatures).to.have.length(1); expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); done(); });