Fix writing newly generated embedded primary key binding signatures

This commit is contained in:
Daniel Huigens 2019-09-11 17:37:01 +02:00
parent 5d9629d6a3
commit a731a607ce
5 changed files with 29 additions and 17 deletions

View File

@ -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 {Date} date (optional) override the creationtime of the signature
* @param {Object} userId (optional) user ID * @param {Object} userId (optional) user ID
* @param {Object} detached (optional) whether to create a detached signature packet * @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 * @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()) { if (!signingKeyPacket.isDecrypted()) {
throw new Error('Private key is not decrypted.'); throw new Error('Private key is not decrypted.');
} }
@ -933,7 +934,7 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa
Object.assign(signaturePacket, signatureProperties); Object.assign(signaturePacket, signatureProperties);
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userId); signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userId);
await signaturePacket.sign(signingKeyPacket, dataToSign, detached); await signaturePacket.sign(signingKeyPacket, dataToSign, detached, streaming);
return signaturePacket; return signaturePacket;
} }

View File

@ -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 {Signature} signature (optional) any existing detached signature to add to the message
* @param {Date} date (optional) override the creation time of the 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 {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<Message>} new message with signed content * @returns {Promise<Message>} new message with signed content
* @async * @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 packetlist = new packet.List();
const literalDataPacket = this.packets.findPacket(enums.packet.literal); 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.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); return new Message(packetlist);
}; };
@ -505,15 +506,16 @@ Message.prototype.compress = function(compression) {
* @param {Signature} signature (optional) any existing detached signature * @param {Signature} signature (optional) any existing detached signature
* @param {Date} date (optional) override the creation time of the 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 {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<module:signature.Signature>} new detached signature of message content * @returns {Promise<module:signature.Signature>} new detached signature of message content
* @async * @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); const literalDataPacket = this.packets.findPacket(enums.packet.literal);
if (!literalDataPacket) { if (!literalDataPacket) {
throw new Error('No literal data packet to sign.'); 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 {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 {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} detached (optional) whether to create detached signature packets
* @param {Boolean} streaming (optional) whether to process data as a stream
* @returns {Promise<module:packet.List>} list of signature packets * @returns {Promise<module:packet.List>} list of signature packets
* @async * @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(); const packetlist = new packet.List();
// If data packet was created from Uint8Array, use binary, otherwise use text // 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 ${ throw new Error(`Could not find valid signing key packet in key ${
privateKey.getKeyId().toHex()}`); 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 => { })).then(signatureList => {
signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); signatureList.forEach(signaturePacket => packetlist.push(signaturePacket));
}); });

View File

@ -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 (privateKeys.length || signature) { // sign the message only if private keys or signature is specified
if (detached) { 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; result.signature = armor ? detachedSignature.armor() : detachedSignature;
} else { } else {
message = await message.sign(privateKeys, signature, date, fromUserIds); message = await message.sign(privateKeys, signature, date, fromUserIds, message.fromStream);
} }
} }
message = message.compress(compression); message = message.compress(compression);
@ -442,7 +442,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message &
const result = {}; const result = {};
return Promise.resolve().then(async function() { return Promise.resolve().then(async function() {
if (detached) { 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; result.signature = armor ? signature.armor() : signature;
if (message.packets) { if (message.packets) {
result.signature = stream.transformPair(message.packets.write(), async (readable, writable) => { result.signature = stream.transformPair(message.packets.write(), async (readable, writable) => {
@ -453,7 +453,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message &
}); });
} }
} else { } else {
message = await message.sign(privateKeys, undefined, date, fromUserIds); message = await message.sign(privateKeys, undefined, date, fromUserIds, message.fromStream);
if (armor) { if (armor) {
result.data = message.armor(); result.data = message.armor();
} else { } else {

View File

@ -149,10 +149,11 @@ Signature.prototype.write = function () {
* @param {module:packet.SecretKey} key private key used to sign the message. * @param {module:packet.SecretKey} key private key used to sign the message.
* @param {Object} data Contains packets to be signed. * @param {Object} data Contains packets to be signed.
* @param {Boolean} detached (optional) whether to create a detached signature * @param {Boolean} detached (optional) whether to create a detached signature
* @param {Boolean} streaming (optional) whether to process data as a stream
* @returns {Promise<Boolean>} * @returns {Promise<Boolean>}
* @async * @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 signatureType = enums.write(enums.signature, this.signatureType);
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm); const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm); 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); this.signedHashValue = stream.slice(stream.clone(hash), 0, 2);
const params = key.params; 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) 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 // Store the fact that this signature is valid, e.g. for when we call `await
// getLatestValidSignature(this.revocationSignatures, key, data)` later. Note // getLatestValidSignature(this.revocationSignatures, key, data)` later. Note

View File

@ -1853,7 +1853,8 @@ function versionSpecificTests() {
it('Generate key - one signing subkey', function() { it('Generate key - one signing subkey', function() {
const userId = 'test <a@b.com>'; const userId = 'test <a@b.com>';
const opt = {curve: 'curve25519', userIds: [userId], passphrase: '123', subkeys:[{}, {sign: true}]}; 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.length).to.equal(1);
expect(key.users[0].userId.userid).to.equal(userId); expect(key.users[0].userId.userid).to.equal(userId);
expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;
@ -1871,7 +1872,8 @@ function versionSpecificTests() {
return openpgp.generateKey(opt).then(async function({ key }) { return openpgp.generateKey(opt).then(async function({ key }) {
await key.decrypt('123'); await key.decrypt('123');
return openpgp.reformatKey({ privateKey: key, userIds: [userId] }); 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.length).to.equal(1);
expect(key.users[0].userId.userid).to.equal(userId); expect(key.users[0].userId.userid).to.equal(userId);
expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;