From a731a607cea2517e35d250bf8e6d0b0359df3380 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 11 Sep 2019 17:37:01 +0200 Subject: [PATCH] Fix writing newly generated embedded primary key binding signatures --- src/key.js | 5 +++-- src/message.js | 15 +++++++++------ src/openpgp.js | 8 ++++---- src/packet/signature.js | 12 +++++++++--- test/general/key.js | 6 ++++-- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/key.js b/src/key.js index ffa3e9ab..c5831244 100644 --- a/src/key.js +++ b/src/key.js @@ -923,9 +923,10 @@ User.prototype.isRevoked = async function(primaryKey, certificate, key, date = n * @param {Date} date (optional) override the creationtime of the signature * @param {Object} userId (optional) user ID * @param {Object} detached (optional) whether to create a detached signature packet + * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {module:packet/signature} signature packet */ -export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userId, detached = false) { +export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userId, detached = false, streaming = false) { if (!signingKeyPacket.isDecrypted()) { throw new Error('Private key is not decrypted.'); } @@ -933,7 +934,7 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa Object.assign(signaturePacket, signatureProperties); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userId); - await signaturePacket.sign(signingKeyPacket, dataToSign, detached); + await signaturePacket.sign(signingKeyPacket, dataToSign, detached, streaming); return signaturePacket; } diff --git a/src/message.js b/src/message.js index f57e82f2..826b5df1 100644 --- a/src/message.js +++ b/src/message.js @@ -418,10 +418,11 @@ export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKey * @param {Signature} signature (optional) any existing detached signature to add to the message * @param {Date} date (optional) override the creation time of the signature * @param {Array} userIds (optional) user IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {Promise} new message with signed content * @async */ -Message.prototype.sign = async function(privateKeys = [], signature = null, date = new Date(), userIds = []) { +Message.prototype.sign = async function(privateKeys = [], signature = null, date = new Date(), userIds = [], streaming = false) { const packetlist = new packet.List(); const literalDataPacket = this.packets.findPacket(enums.packet.literal); @@ -474,7 +475,7 @@ Message.prototype.sign = async function(privateKeys = [], signature = null, date }); packetlist.push(literalDataPacket); - packetlist.concat(await createSignaturePackets(literalDataPacket, privateKeys, signature, date, false)); + packetlist.concat(await createSignaturePackets(literalDataPacket, privateKeys, signature, date, userIds, false, streaming)); return new Message(packetlist); }; @@ -505,15 +506,16 @@ Message.prototype.compress = function(compression) { * @param {Signature} signature (optional) any existing detached signature * @param {Date} date (optional) override the creation time of the signature * @param {Array} userIds (optional) user IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {Promise} new detached signature of message content * @async */ -Message.prototype.signDetached = async function(privateKeys = [], signature = null, date = new Date(), userIds = []) { +Message.prototype.signDetached = async function(privateKeys = [], signature = null, date = new Date(), userIds = [], streaming = false) { const literalDataPacket = this.packets.findPacket(enums.packet.literal); if (!literalDataPacket) { throw new Error('No literal data packet to sign.'); } - return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, date, userIds, true)); + return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, date, userIds, true, streaming)); }; /** @@ -524,10 +526,11 @@ Message.prototype.signDetached = async function(privateKeys = [], signature = nu * @param {Date} date (optional) override the creationtime of the signature * @param {Array} userIds (optional) user IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] * @param {Boolean} detached (optional) whether to create detached signature packets + * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {Promise} list of signature packets * @async */ -export async function createSignaturePackets(literalDataPacket, privateKeys, signature = null, date = new Date(), userIds = [], detached = false) { +export async function createSignaturePackets(literalDataPacket, privateKeys, signature = null, date = new Date(), userIds = [], detached = false, streaming = false) { const packetlist = new packet.List(); // If data packet was created from Uint8Array, use binary, otherwise use text @@ -544,7 +547,7 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig throw new Error(`Could not find valid signing key packet in key ${ privateKey.getKeyId().toHex()}`); } - return createSignaturePacket(literalDataPacket, privateKey, signingKey.keyPacket, { signatureType }, date, userId, detached); + return createSignaturePacket(literalDataPacket, privateKey, signingKey.keyPacket, { signatureType }, date, userId, detached, streaming); })).then(signatureList => { signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); }); diff --git a/src/openpgp.js b/src/openpgp.js index 7c5fbc26..9cbbcb00 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -322,10 +322,10 @@ export function encrypt({ message, publicKeys, privateKeys, passwords, sessionKe } if (privateKeys.length || signature) { // sign the message only if private keys or signature is specified if (detached) { - const detachedSignature = await message.signDetached(privateKeys, signature, date, fromUserIds); + const detachedSignature = await message.signDetached(privateKeys, signature, date, fromUserIds, message.fromStream); result.signature = armor ? detachedSignature.armor() : detachedSignature; } else { - message = await message.sign(privateKeys, signature, date, fromUserIds); + message = await message.sign(privateKeys, signature, date, fromUserIds, message.fromStream); } } message = message.compress(compression); @@ -442,7 +442,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message & const result = {}; return Promise.resolve().then(async function() { if (detached) { - const signature = await message.signDetached(privateKeys, undefined, date, fromUserIds); + const signature = await message.signDetached(privateKeys, undefined, date, fromUserIds, message.fromStream); result.signature = armor ? signature.armor() : signature; if (message.packets) { result.signature = stream.transformPair(message.packets.write(), async (readable, writable) => { @@ -453,7 +453,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message & }); } } else { - message = await message.sign(privateKeys, undefined, date, fromUserIds); + message = await message.sign(privateKeys, undefined, date, fromUserIds, message.fromStream); if (armor) { result.data = message.armor(); } else { diff --git a/src/packet/signature.js b/src/packet/signature.js index 84de671c..8165017d 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -149,10 +149,11 @@ Signature.prototype.write = function () { * @param {module:packet.SecretKey} key private key used to sign the message. * @param {Object} data Contains packets to be signed. * @param {Boolean} detached (optional) whether to create a detached signature + * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {Promise} * @async */ -Signature.prototype.sign = async function (key, data, detached = false) { +Signature.prototype.sign = async function (key, data, detached = false, streaming = false) { const signatureType = enums.write(enums.signature, this.signatureType); const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm); const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm); @@ -182,9 +183,14 @@ Signature.prototype.sign = async function (key, data, detached = false) { this.signedHashValue = stream.slice(stream.clone(hash), 0, 2); const params = key.params; - this.signature = stream.fromAsync(async () => crypto.signature.sign( + const signed = async () => crypto.signature.sign( publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash) - )); + ); + if (streaming) { + this.signature = stream.fromAsync(signed); + } else { + this.signature = await signed(); + } // Store the fact that this signature is valid, e.g. for when we call `await // getLatestValidSignature(this.revocationSignatures, key, data)` later. Note diff --git a/test/general/key.js b/test/general/key.js index 9a2efa49..87edd60e 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -1853,7 +1853,8 @@ function versionSpecificTests() { it('Generate key - one signing subkey', function() { const userId = 'test '; const opt = {curve: 'curve25519', userIds: [userId], passphrase: '123', subkeys:[{}, {sign: true}]}; - return openpgp.generateKey(opt).then(async function({ key }) { + return openpgp.generateKey(opt).then(async function({ privateKeyArmored }) { + const { keys: [key] } = await openpgp.key.readArmored(privateKeyArmored); expect(key.users.length).to.equal(1); expect(key.users[0].userId.userid).to.equal(userId); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; @@ -1871,7 +1872,8 @@ function versionSpecificTests() { return openpgp.generateKey(opt).then(async function({ key }) { await key.decrypt('123'); return openpgp.reformatKey({ privateKey: key, userIds: [userId] }); - }).then(async function({ key }) { + }).then(async function({ privateKeyArmored }) { + const { keys: [key] } = await openpgp.key.readArmored(privateKeyArmored); expect(key.users.length).to.equal(1); expect(key.users[0].userId.userid).to.equal(userId); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;