Skip to content

Commit

Permalink
Merge pull request #967 from twiss/keygen-fixes
Browse files Browse the repository at this point in the history
Fix generating signing subkeys
  • Loading branch information
twiss committed Sep 11, 2019
2 parents 5d9629d + 18474bd commit 67e98e8
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 17 deletions.
5 changes: 3 additions & 2 deletions src/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -923,17 +923,18 @@ 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.');
}
const signaturePacket = new packet.Signature(date);
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;
}

Expand Down
15 changes: 9 additions & 6 deletions src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<Message>} 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);
Expand Down Expand Up @@ -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);
};
Expand Down Expand Up @@ -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<module:signature.Signature>} 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));
};

/**
Expand All @@ -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<module:packet.List>} 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
Expand All @@ -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));
});
Expand Down
8 changes: 4 additions & 4 deletions src/openpgp.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) => {
Expand All @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions src/packet/secret_key.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ SecretKey.prototype.postCloneTypeFix = function() {
if (this.keyid) {
this.keyid = type_keyid.fromClone(this.keyid);
}
if (this.s2k) {
this.s2k = type_s2k.fromClone(this.s2k);
}
};

export default SecretKey;
12 changes: 9 additions & 3 deletions src/packet/signature.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boolean>}
* @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);
Expand Down Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions test/general/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -1853,7 +1853,8 @@ function versionSpecificTests() {
it('Generate key - one signing subkey', function() {
const userId = 'test <a@b.com>';
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;
Expand All @@ -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;
Expand Down Expand Up @@ -2208,6 +2210,16 @@ describe('Key', function() {

describe('V4', versionSpecificTests);

tryTests('V4 - With Worker', versionSpecificTests, {
if: typeof window !== 'undefined' && window.Worker,
before: async function() {
await openpgp.initWorker({ path: '../dist/openpgp.worker.js' });
},
after: function() {
openpgp.destroyWorker();
}
});

let v5_keysVal;
let aead_protectVal;
tryTests('V5', versionSpecificTests, {
Expand Down

0 comments on commit 67e98e8

Please sign in to comment.