Drop support for verification of detached cleartext signatures (#1265)

(Also, use turnstyle to avoid CI browserstack tasks running in parallel.)
This commit is contained in:
larabr 2021-03-18 17:17:39 +01:00 committed by GitHub
parent eba791e11e
commit 3e808c1578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 132 deletions

View File

@ -14,6 +14,12 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 30
abort-after-seconds: 900
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
- run: npm ci - run: npm ci
- run: npm run build-test - run: npm run build-test
@ -28,6 +34,12 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 30
abort-after-seconds: 900
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
- run: npm ci - run: npm ci
- run: npm run build-test --lightweight - run: npm run build-test --lightweight

View File

@ -66,25 +66,10 @@ export class CleartextMessage {
* @async * @async
*/ */
async sign(privateKeys, signature = null, signingKeyIds = [], date = new Date(), userIds = [], config = defaultConfig) { async sign(privateKeys, signature = null, signingKeyIds = [], date = new Date(), userIds = [], config = defaultConfig) {
return new CleartextMessage(this.text, await this.signDetached(privateKeys, signature, signingKeyIds, date, userIds, config));
}
/**
* Sign the cleartext message
* @param {Array<Key>} privateKeys - private keys with decrypted secret key data for signing
* @param {Signature} [signature] - Any existing detached signature
* @param {Array<module:type/keyid~Keyid>} [signingKeyIds] - Array of key IDs to use for signing. Each signingKeyIds[i] corresponds to privateKeys[i]
* @param {Date} [date] - The creation time of the signature that should be created
* @param {Array} [userIds] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
* @param {Object} [config] - Full configuration, defaults to openpgp.config
* @returns {Signature} New detached signature of message content.
* @async
*/
async signDetached(privateKeys, signature = null, signingKeyIds = [], date = new Date(), userIds = [], config = defaultConfig) {
const literalDataPacket = new LiteralDataPacket(); const literalDataPacket = new LiteralDataPacket();
literalDataPacket.setText(this.text); literalDataPacket.setText(this.text);
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIds, date, userIds, true, undefined, config));
return new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIds, date, userIds, true, undefined, config)); return new CleartextMessage(this.text, newSignature);
} }
/** /**
@ -96,19 +81,7 @@ export class CleartextMessage {
* @async * @async
*/ */
verify(keys, date = new Date(), config = defaultConfig) { verify(keys, date = new Date(), config = defaultConfig) {
return this.verifyDetached(this.signature, keys, date, config); const signatureList = this.signature.packets;
}
/**
* Verify signatures of cleartext signed message
* @param {Array<Key>} keys - Array of keys to verify signatures
* @param {Date} [date] - Verify the signature against the given date, i.e. check signature creation time < date < expiration time
* @param {Object} [config] - Full configuration, defaults to openpgp.config
* @returns {Array<{keyid: module:type/keyid~Keyid, valid: Boolean}>} List of signer's keyid and validity of signature.
* @async
*/
verifyDetached(signature, keys, date = new Date(), config = defaultConfig) {
const signatureList = signature.packets;
const literalDataPacket = new LiteralDataPacket(); const literalDataPacket = new LiteralDataPacket();
// we assume that cleartext signature is generated based on UTF8 cleartext // we assume that cleartext signature is generated based on UTF8 cleartext
literalDataPacket.setText(this.text); literalDataPacket.setText(this.text);

View File

@ -353,7 +353,7 @@ export function sign({ message, privateKeys, armor = true, streaming = message &
config = { ...defaultConfig, ...config }; config = { ...defaultConfig, ...config };
checkCleartextOrMessage(message); checkCleartextOrMessage(message);
if (message instanceof CleartextMessage && !armor) throw new Error("Can't sign non-armored cleartext message"); if (message instanceof CleartextMessage && !armor) throw new Error("Can't sign non-armored cleartext message");
if (message instanceof CleartextMessage && detached) throw new Error("Can't sign detached cleartext message"); if (message instanceof CleartextMessage && detached) throw new Error("Can't detach-sign a cleartext message");
privateKeys = toArray(privateKeys); fromUserIds = toArray(fromUserIds); privateKeys = toArray(privateKeys); fromUserIds = toArray(fromUserIds);
return Promise.resolve().then(async function() { return Promise.resolve().then(async function() {
@ -408,12 +408,13 @@ export function verify({ message, publicKeys, format = 'utf8', streaming = messa
config = { ...defaultConfig, ...config }; config = { ...defaultConfig, ...config };
checkCleartextOrMessage(message); checkCleartextOrMessage(message);
if (message instanceof CleartextMessage && format === 'binary') throw new Error("Can't return cleartext message data as binary"); if (message instanceof CleartextMessage && format === 'binary') throw new Error("Can't return cleartext message data as binary");
if (message instanceof CleartextMessage && signature) throw new Error("Can't verify detached cleartext signature");
publicKeys = toArray(publicKeys); publicKeys = toArray(publicKeys);
return Promise.resolve().then(async function() { return Promise.resolve().then(async function() {
const result = {}; const result = {};
if (message instanceof CleartextMessage) { if (message instanceof CleartextMessage) {
result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date, config) : await message.verify(publicKeys, date, config); result.signatures = await message.verify(publicKeys, date, config);
} else { } else {
result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date, streaming, config) : await message.verify(publicKeys, date, streaming, config); result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date, streaming, config) : await message.verify(publicKeys, date, streaming, config);
} }

View File

@ -279,61 +279,43 @@ EJ4QcD/oQ6x1M/8X/iKQCtxZP8RnlrbH7ExkNON5s5g=
}); });
function omnibus() { function omnibus() {
it('Omnibus BrainpoolP256r1 Test', function() { it('Omnibus BrainpoolP256r1 Test', async function() {
const options = { userIds: { name: "Hi", email: "hi@hel.lo" }, curve: "brainpoolP256r1" }; const testData = input.createSomeMessage();
return openpgp.generateKey(options).then(function(firstKey) { const testData2 = input.createSomeMessage();
const hi = firstKey.key;
const pubHi = hi.toPublic();
const options = { userIds: { name: "Bye", email: "bye@good.bye" }, curve: "brainpoolP256r1" }; const firstKey = await openpgp.generateKey({ userIds: { name: "Hi", email: "hi@hel.lo" }, curve: "brainpoolP256r1" });
return openpgp.generateKey(options).then(function(secondKey) { const hi = firstKey.key;
const bye = secondKey.key; const pubHi = hi.toPublic();
const pubBye = bye.toPublic(); const secondKey = await openpgp.generateKey({ userIds: { name: "Bye", email: "bye@good.bye" }, curve: "brainpoolP256r1" });
const bye = secondKey.key;
const pubBye = bye.toPublic();
const testData = input.createSomeMessage(); const cleartextMessage = await openpgp.sign({ message: openpgp.CleartextMessage.fromText(testData), privateKeys: hi });
const testData2 = input.createSomeMessage(); await openpgp.verify({
return Promise.all([ message: await openpgp.readCleartextMessage({ cleartextMessage }),
// Signing message publicKeys: pubHi
openpgp.sign( }).then(output => expect(output.signatures[0].valid).to.be.true);
{ message: openpgp.CleartextMessage.fromText(testData), privateKeys: hi } // Verifying detached signature
).then(async signed => { await openpgp.verify({
const msg = await openpgp.readCleartextMessage({ cleartextMessage: signed }); message: openpgp.Message.fromText(util.removeTrailingSpaces(testData)),
// Verifying signed message publicKeys: pubHi,
return Promise.all([ signature: (await openpgp.readCleartextMessage({ cleartextMessage })).signature
openpgp.verify( }).then(output => expect(output.signatures[0].valid).to.be.true);
{ message: msg, publicKeys: pubHi }
).then(output => expect(output.signatures[0].valid).to.be.true), // Encrypting and signing
// Verifying detached signature const encrypted = await openpgp.encrypt({
openpgp.verify({ message: openpgp.Message.fromText(testData2),
message: openpgp.CleartextMessage.fromText(testData), publicKeys: [pubBye],
publicKeys: pubHi, privateKeys: [hi]
signature: msg.signature });
}).then(output => expect(output.signatures[0].valid).to.be.true) // Decrypting and verifying
]); return openpgp.decrypt({
}), message: await openpgp.readMessage({ armoredMessage: encrypted }),
// Encrypting and signing privateKeys: bye,
openpgp.encrypt( publicKeys: [pubHi]
{ }).then(output => {
message: openpgp.Message.fromText(testData2), expect(output.data).to.equal(testData2);
publicKeys: [pubBye], expect(output.signatures[0].valid).to.be.true;
privateKeys: [hi]
}
).then(async encrypted => {
const msg = await openpgp.readMessage({ armoredMessage: encrypted });
// Decrypting and verifying
return openpgp.decrypt(
{
message: msg,
privateKeys: bye,
publicKeys: [pubHi]
}
).then(output => {
expect(output.data).to.equal(testData2);
expect(output.signatures[0].valid).to.be.true;
});
})
]);
});
}); });
}); });
} }

View File

@ -3,63 +3,49 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp
const chai = require('chai'); const chai = require('chai');
chai.use(require('chai-as-promised')); chai.use(require('chai-as-promised'));
const input = require('./testInputs.js'); const input = require('./testInputs.js');
const util = require('../../src/util');
const expect = chai.expect; const expect = chai.expect;
module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-384,P-521 curves @lightweight', function () { module.exports = () => describe('Elliptic Curve Cryptography for NIST P-256,P-384,P-521 curves @lightweight', function () {
function omnibus() { function omnibus() {
it('Omnibus NIST P-256 Test', function () { it('Omnibus NIST P-256 Test', async function () {
const options = { userIds: { name: "Hi", email: "hi@hel.lo" }, curve: "p256" };
const testData = input.createSomeMessage(); const testData = input.createSomeMessage();
const testData2 = input.createSomeMessage(); const testData2 = input.createSomeMessage();
return openpgp.generateKey(options).then(function (firstKey) {
const hi = firstKey.key;
const pubHi = hi.toPublic();
const options = { userIds: { name: "Bye", email: "bye@good.bye" }, curve: "p256" }; const firstKey = await openpgp.generateKey({ userIds: { name: "Hi", email: "hi@hel.lo" }, curve: "p256" });
return openpgp.generateKey(options).then(function (secondKey) { const hi = firstKey.key;
const bye = secondKey.key; const pubHi = hi.toPublic();
const pubBye = bye.toPublic(); const secondKey = await openpgp.generateKey({ userIds: { name: "Bye", email: "bye@good.bye" }, curve: "p256" });
const bye = secondKey.key;
const pubBye = bye.toPublic();
return Promise.all([ const cleartextMessage = await openpgp.sign({ message: openpgp.CleartextMessage.fromText(testData), privateKeys: hi });
// Signing message await openpgp.verify({
message: await openpgp.readCleartextMessage({ cleartextMessage }),
publicKeys: pubHi
}).then(output => expect(output.signatures[0].valid).to.be.true);
// Verifying detached signature
await openpgp.verify({
message: openpgp.Message.fromText(util.removeTrailingSpaces(testData)),
publicKeys: pubHi,
signature: (await openpgp.readCleartextMessage({ cleartextMessage })).signature
}).then(output => expect(output.signatures[0].valid).to.be.true);
openpgp.sign( // Encrypting and signing
{ message: openpgp.CleartextMessage.fromText(testData), privateKeys: hi } const encrypted = await openpgp.encrypt({
).then(async signed => { message: openpgp.Message.fromText(testData2),
const msg = await openpgp.readCleartextMessage({ cleartextMessage: signed }); publicKeys: [pubBye],
// Verifying signed message privateKeys: [hi]
return Promise.all([ });
openpgp.verify( // Decrypting and verifying
{ message: msg, publicKeys: pubHi } return openpgp.decrypt({
).then(output => expect(output.signatures[0].valid).to.be.true), message: await openpgp.readMessage({ armoredMessage: encrypted }),
// Verifying detached signature privateKeys: bye,
openpgp.verify({ publicKeys: [pubHi]
message: openpgp.CleartextMessage.fromText(testData), }).then(output => {
publicKeys: pubHi, expect(output.data).to.equal(testData2);
signature: msg.signature expect(output.signatures[0].valid).to.be.true;
}).then(output => expect(output.signatures[0].valid).to.be.true)
]);
}),
// Encrypting and signing
openpgp.encrypt(
{ message: openpgp.Message.fromText(testData2),
publicKeys: [pubBye],
privateKeys: [hi] }
).then(async encrypted => {
const msg = await openpgp.readMessage({ armoredMessage: encrypted });
// Decrypting and verifying
return openpgp.decrypt(
{ message: msg,
privateKeys: bye,
publicKeys: [pubHi] }
).then(output => {
expect(output.data).to.equal(testData2);
expect(output.signatures[0].valid).to.be.true;
});
})
]);
});
}); });
}); });
} }