Fix detached signing of messages created from streams (#887)
This commit is contained in:
parent
038d8466fe
commit
7fb2901ede
|
@ -437,6 +437,14 @@ export function sign({ message, privateKeys, armor=true, streaming=message&&mess
|
||||||
if (detached) {
|
if (detached) {
|
||||||
const signature = await message.signDetached(privateKeys, undefined, date, fromUserIds);
|
const signature = await message.signDetached(privateKeys, undefined, date, fromUserIds);
|
||||||
result.signature = armor ? signature.armor() : signature;
|
result.signature = armor ? signature.armor() : signature;
|
||||||
|
if (message.packets) {
|
||||||
|
result.signature = stream.transformPair(message.packets.write(), async (readable, writable) => {
|
||||||
|
await Promise.all([
|
||||||
|
stream.pipe(result.signature, writable),
|
||||||
|
stream.readToEnd(readable).catch(() => {})
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
message = await message.sign(privateKeys, undefined, date, fromUserIds);
|
message = await message.sign(privateKeys, undefined, date, fromUserIds);
|
||||||
if (armor) {
|
if (armor) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ const priv_key =
|
||||||
|
|
||||||
const passphrase = 'hello world';
|
const passphrase = 'hello world';
|
||||||
|
|
||||||
let plaintext, data, i, canceled, expectedType, dataArrived;
|
let privKey, pubKey, plaintext, data, i, canceled, expectedType, dataArrived;
|
||||||
|
|
||||||
function tests() {
|
function tests() {
|
||||||
it('Encrypt small message', async function() {
|
it('Encrypt small message', async function() {
|
||||||
|
@ -134,9 +134,6 @@ function tests() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sign: Input stream should be canceled when canceling encrypted stream', async function() {
|
it('Sign: Input stream should be canceled when canceling encrypted stream', async function() {
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
const signed = await openpgp.sign({
|
||||||
message: openpgp.message.fromBinary(data),
|
message: openpgp.message.fromBinary(data),
|
||||||
privateKeys: privKey
|
privateKeys: privKey
|
||||||
|
@ -202,10 +199,6 @@ function tests() {
|
||||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||||
openpgp.config.allow_unauthenticated_stream = true;
|
openpgp.config.allow_unauthenticated_stream = true;
|
||||||
try {
|
try {
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const encrypted = await openpgp.encrypt({
|
const encrypted = await openpgp.encrypt({
|
||||||
message: openpgp.message.fromBinary(data),
|
message: openpgp.message.fromBinary(data),
|
||||||
publicKeys: pubKey,
|
publicKeys: pubKey,
|
||||||
|
@ -268,10 +261,6 @@ function tests() {
|
||||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||||
openpgp.config.allow_unauthenticated_stream = true;
|
openpgp.config.allow_unauthenticated_stream = true;
|
||||||
try {
|
try {
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const encrypted = await openpgp.encrypt({
|
const encrypted = await openpgp.encrypt({
|
||||||
message: openpgp.message.fromBinary(data),
|
message: openpgp.message.fromBinary(data),
|
||||||
publicKeys: pubKey,
|
publicKeys: pubKey,
|
||||||
|
@ -307,10 +296,6 @@ function tests() {
|
||||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
||||||
openpgp.config.allow_unauthenticated_stream = true;
|
openpgp.config.allow_unauthenticated_stream = true;
|
||||||
try {
|
try {
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const encrypted = await openpgp.encrypt({
|
const encrypted = await openpgp.encrypt({
|
||||||
message: openpgp.message.fromBinary(data),
|
message: openpgp.message.fromBinary(data),
|
||||||
publicKeys: pubKey,
|
publicKeys: pubKey,
|
||||||
|
@ -342,40 +327,30 @@ function tests() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sign/verify: Detect armor checksum error (allow_unauthenticated_stream=true)', async function() {
|
it('Sign/verify: Detect armor checksum error', async function() {
|
||||||
let allow_unauthenticated_streamValue = openpgp.config.allow_unauthenticated_stream;
|
const signed = await openpgp.sign({
|
||||||
openpgp.config.allow_unauthenticated_stream = true;
|
message: openpgp.message.fromBinary(data),
|
||||||
try {
|
privateKeys: privKey
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
});
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
const msgAsciiArmored = signed.data;
|
||||||
message: openpgp.message.fromBinary(data),
|
const message = await openpgp.message.readArmored(openpgp.stream.transform(msgAsciiArmored, value => {
|
||||||
privateKeys: privKey
|
value += '';
|
||||||
});
|
const newlineIndex = value.indexOf('\r\n', 500);
|
||||||
|
if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex);
|
||||||
const msgAsciiArmored = signed.data;
|
return value;
|
||||||
const message = await openpgp.message.readArmored(openpgp.stream.transform(msgAsciiArmored, value => {
|
}));
|
||||||
value += '';
|
const verified = await openpgp.verify({
|
||||||
const newlineIndex = value.indexOf('\r\n', 500);
|
publicKeys: pubKey,
|
||||||
if (value.length > 1000) return value.slice(0, newlineIndex - 1) + (value[newlineIndex - 1] === 'a' ? 'b' : 'a') + value.slice(newlineIndex);
|
message,
|
||||||
return value;
|
streaming: expectedType
|
||||||
}));
|
});
|
||||||
const verified = await openpgp.verify({
|
expect(util.isStream(verified.data)).to.equal(expectedType);
|
||||||
publicKeys: pubKey,
|
const reader = openpgp.stream.getReader(verified.data);
|
||||||
message,
|
expect(await reader.peekBytes(1024)).not.to.deep.equal(plaintext[0]);
|
||||||
streaming: expectedType
|
dataArrived();
|
||||||
});
|
await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check on message failed');
|
||||||
expect(util.isStream(verified.data)).to.equal(expectedType);
|
expect(verified.signatures).to.exist.and.have.length(1);
|
||||||
const reader = openpgp.stream.getReader(verified.data);
|
|
||||||
expect(await reader.peekBytes(1024)).not.to.deep.equal(plaintext[0]);
|
|
||||||
dataArrived();
|
|
||||||
await expect(reader.readToEnd()).to.be.rejectedWith('Ascii armor integrity check on message failed');
|
|
||||||
expect(verified.signatures).to.exist.and.have.length(1);
|
|
||||||
} finally {
|
|
||||||
openpgp.config.allow_unauthenticated_stream = allow_unauthenticated_streamValue;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Encrypt and decrypt larger message roundtrip (draft04)', async function() {
|
it('Encrypt and decrypt larger message roundtrip (draft04)', async function() {
|
||||||
|
@ -505,40 +480,27 @@ function tests() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sign/verify: Input stream should be canceled when canceling decrypted stream (draft04)', async function() {
|
it('Sign/verify: Input stream should be canceled when canceling verified stream', async function() {
|
||||||
let aead_protectValue = openpgp.config.aead_protect;
|
const signed = await openpgp.sign({
|
||||||
let aead_chunk_size_byteValue = openpgp.config.aead_chunk_size_byte;
|
message: openpgp.message.fromBinary(data),
|
||||||
openpgp.config.aead_protect = true;
|
privateKeys: privKey
|
||||||
openpgp.config.aead_chunk_size_byte = 4;
|
});
|
||||||
try {
|
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
const msgAsciiArmored = signed.data;
|
||||||
message: openpgp.message.fromBinary(data),
|
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
||||||
privateKeys: privKey
|
const verified = await openpgp.verify({
|
||||||
});
|
publicKeys: pubKey,
|
||||||
|
message
|
||||||
const msgAsciiArmored = signed.data;
|
});
|
||||||
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
expect(util.isStream(verified.data)).to.equal(expectedType);
|
||||||
const verified = await openpgp.verify({
|
const reader = openpgp.stream.getReader(verified.data);
|
||||||
publicKeys: pubKey,
|
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
||||||
message
|
dataArrived();
|
||||||
});
|
reader.releaseLock();
|
||||||
expect(util.isStream(verified.data)).to.equal(expectedType);
|
await openpgp.stream.cancel(verified.data, new Error('canceled by test'));
|
||||||
const reader = openpgp.stream.getReader(verified.data);
|
expect(canceled).to.be.true;
|
||||||
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
expect(verified.signatures).to.exist.and.have.length(1);
|
||||||
dataArrived();
|
await expect(verified.signatures[0].verified).to.be.rejectedWith('canceled');
|
||||||
reader.releaseLock();
|
|
||||||
await openpgp.stream.cancel(verified.data, new Error('canceled by test'));
|
|
||||||
expect(canceled).to.be.true;
|
|
||||||
expect(verified.signatures).to.exist.and.have.length(1);
|
|
||||||
await expect(verified.signatures[0].verified).to.be.rejectedWith('canceled');
|
|
||||||
} finally {
|
|
||||||
openpgp.config.aead_protect = aead_protectValue;
|
|
||||||
openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteValue;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Don't pull entire input stream when we're not pulling encrypted stream", async function() {
|
it("Don't pull entire input stream when we're not pulling encrypted stream", async function() {
|
||||||
|
@ -550,14 +512,10 @@ function tests() {
|
||||||
expect(await reader.readBytes(1024)).to.match(/^-----BEGIN PGP MESSAGE-----\r\n/);
|
expect(await reader.readBytes(1024)).to.match(/^-----BEGIN PGP MESSAGE-----\r\n/);
|
||||||
dataArrived();
|
dataArrived();
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
expect(i).to.be.lessThan(50);
|
expect(i).to.be.lessThan(expectedType === 'web' ? 50 : 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Sign: Don't pull entire input stream when we're not pulling signed stream", async function() {
|
it("Sign: Don't pull entire input stream when we're not pulling signed stream", async function() {
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
|
||||||
await privKey.decrypt(passphrase);
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
const signed = await openpgp.sign({
|
||||||
message: openpgp.message.fromBinary(data),
|
message: openpgp.message.fromBinary(data),
|
||||||
privateKeys: privKey
|
privateKeys: privKey
|
||||||
|
@ -566,7 +524,7 @@ function tests() {
|
||||||
expect(await reader.readBytes(1024)).to.match(/^-----BEGIN PGP MESSAGE-----\r\n/);
|
expect(await reader.readBytes(1024)).to.match(/^-----BEGIN PGP MESSAGE-----\r\n/);
|
||||||
dataArrived();
|
dataArrived();
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
expect(i).to.be.lessThan(50);
|
expect(i).to.be.lessThan(expectedType === 'web' ? 50 : 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Don't pull entire input stream when we're not pulling decrypted stream (draft04)", async function() {
|
it("Don't pull entire input stream when we're not pulling decrypted stream (draft04)", async function() {
|
||||||
|
@ -593,7 +551,7 @@ function tests() {
|
||||||
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
||||||
dataArrived();
|
dataArrived();
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
expect(i).to.be.lessThan(50);
|
expect(i).to.be.lessThan(expectedType === 'web' ? 50 : 100);
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.aead_protect = aead_protectValue;
|
openpgp.config.aead_protect = aead_protectValue;
|
||||||
openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteValue;
|
openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteValue;
|
||||||
|
@ -601,36 +559,103 @@ function tests() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Sign/verify: Don't pull entire input stream when we're not pulling verified stream (draft04)", async function() {
|
it("Sign/verify: Don't pull entire input stream when we're not pulling verified stream", async function() {
|
||||||
let aead_protectValue = openpgp.config.aead_protect;
|
const signed = await openpgp.sign({
|
||||||
let aead_chunk_size_byteValue = openpgp.config.aead_chunk_size_byte;
|
message: openpgp.message.fromBinary(data),
|
||||||
openpgp.config.aead_protect = true;
|
privateKeys: privKey
|
||||||
openpgp.config.aead_chunk_size_byte = 4;
|
});
|
||||||
try {
|
const msgAsciiArmored = signed.data;
|
||||||
const pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
||||||
const privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
const verified = await openpgp.verify({
|
||||||
await privKey.decrypt(passphrase);
|
publicKeys: pubKey,
|
||||||
|
message
|
||||||
|
});
|
||||||
|
expect(util.isStream(verified.data)).to.equal(expectedType);
|
||||||
|
const reader = openpgp.stream.getReader(verified.data);
|
||||||
|
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
||||||
|
dataArrived();
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
expect(i).to.be.lessThan(expectedType === 'web' ? 50 : 100);
|
||||||
|
});
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
it('Detached sign small message', async function() {
|
||||||
message: openpgp.message.fromBinary(data),
|
dataArrived(); // Do not wait until data arrived.
|
||||||
privateKeys: privKey
|
const data = new ReadableStream({
|
||||||
});
|
async start(controller) {
|
||||||
const msgAsciiArmored = signed.data;
|
controller.enqueue(util.str_to_Uint8Array('hello '));
|
||||||
const message = await openpgp.message.readArmored(msgAsciiArmored);
|
controller.enqueue(util.str_to_Uint8Array('world'));
|
||||||
const verified = await openpgp.verify({
|
controller.close();
|
||||||
publicKeys: pubKey,
|
}
|
||||||
message
|
});
|
||||||
});
|
const signed = await openpgp.sign({
|
||||||
expect(util.isStream(verified.data)).to.equal(expectedType);
|
message: openpgp.message.fromBinary(data),
|
||||||
const reader = openpgp.stream.getReader(verified.data);
|
privateKeys: privKey,
|
||||||
expect(await reader.readBytes(1024)).to.deep.equal(plaintext[0]);
|
detached: true,
|
||||||
dataArrived();
|
});
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
const sigArmored = await openpgp.stream.readToEnd(signed.signature);
|
||||||
expect(i).to.be.lessThan(50);
|
const signature = await openpgp.message.readArmored(sigArmored);
|
||||||
} finally {
|
const verified = await openpgp.verify({
|
||||||
openpgp.config.aead_protect = aead_protectValue;
|
signature,
|
||||||
openpgp.config.aead_chunk_size_byte = aead_chunk_size_byteValue;
|
publicKeys: pubKey,
|
||||||
}
|
message: openpgp.message.fromText('hello world')
|
||||||
|
});
|
||||||
|
expect(openpgp.util.decode_utf8(verified.data)).to.equal('hello world');
|
||||||
|
expect(verified.signatures).to.exist.and.have.length(1);
|
||||||
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Detached sign small message (not streaming)', async function() {
|
||||||
|
dataArrived(); // Do not wait until data arrived.
|
||||||
|
const data = new ReadableStream({
|
||||||
|
async start(controller) {
|
||||||
|
controller.enqueue(util.str_to_Uint8Array('hello '));
|
||||||
|
controller.enqueue(util.str_to_Uint8Array('world'));
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const signed = await openpgp.sign({
|
||||||
|
message: openpgp.message.fromBinary(data),
|
||||||
|
privateKeys: privKey,
|
||||||
|
detached: true,
|
||||||
|
streaming: false,
|
||||||
|
});
|
||||||
|
const sigArmored = await openpgp.stream.readToEnd(signed.signature);
|
||||||
|
const signature = await openpgp.message.readArmored(sigArmored);
|
||||||
|
const verified = await openpgp.verify({
|
||||||
|
signature,
|
||||||
|
publicKeys: pubKey,
|
||||||
|
message: openpgp.message.fromText('hello world')
|
||||||
|
});
|
||||||
|
expect(openpgp.util.decode_utf8(verified.data)).to.equal('hello world');
|
||||||
|
expect(verified.signatures).to.exist.and.have.length(1);
|
||||||
|
expect(verified.signatures[0].valid).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Detached sign is expected to pull entire input stream when we're not pulling signed stream", async function() {
|
||||||
|
const signed = await openpgp.sign({
|
||||||
|
message: openpgp.message.fromBinary(data),
|
||||||
|
privateKeys: privKey,
|
||||||
|
detached: true
|
||||||
|
});
|
||||||
|
const reader = openpgp.stream.getReader(signed.signature);
|
||||||
|
expect((await reader.readBytes(31)).toString('utf8')).to.equal('-----BEGIN PGP SIGNATURE-----\r\n');
|
||||||
|
dataArrived();
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
expect(i).to.be.greaterThan(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Detached sign: Input stream should be canceled when canceling signed stream', async function() {
|
||||||
|
const signed = await openpgp.sign({
|
||||||
|
message: openpgp.message.fromBinary(data),
|
||||||
|
privateKeys: privKey,
|
||||||
|
detached: true
|
||||||
|
});
|
||||||
|
const reader = openpgp.stream.getReader(signed.signature);
|
||||||
|
expect((await reader.readBytes(31)).toString('utf8')).to.equal('-----BEGIN PGP SIGNATURE-----\r\n');
|
||||||
|
dataArrived();
|
||||||
|
reader.releaseLock();
|
||||||
|
await openpgp.stream.cancel(signed.signature, new Error('canceled by test'));
|
||||||
|
expect(canceled).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (openpgp.util.detectNode()) {
|
if (openpgp.util.detectNode()) {
|
||||||
|
@ -662,6 +687,12 @@ function tests() {
|
||||||
describe('Streaming', function() {
|
describe('Streaming', function() {
|
||||||
let currentTest = 0;
|
let currentTest = 0;
|
||||||
|
|
||||||
|
before(async function() {
|
||||||
|
pubKey = (await openpgp.key.readArmored(pub_key)).keys[0];
|
||||||
|
privKey = (await openpgp.key.readArmored(priv_key)).keys[0];
|
||||||
|
await privKey.decrypt(passphrase);
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
let test = ++currentTest;
|
let test = ++currentTest;
|
||||||
|
|
||||||
|
@ -674,7 +705,7 @@ describe('Streaming', function() {
|
||||||
data = new ReadableStream({
|
data = new ReadableStream({
|
||||||
async pull(controller) {
|
async pull(controller) {
|
||||||
await new Promise(setTimeout);
|
await new Promise(setTimeout);
|
||||||
if (test === currentTest && i++ < 10) {
|
if (test === currentTest && i++ < 100) {
|
||||||
if (i === 4) await dataArrivedPromise;
|
if (i === 4) await dataArrivedPromise;
|
||||||
let randomBytes = await openpgp.crypto.random.getRandomBytes(1024);
|
let randomBytes = await openpgp.crypto.random.getRandomBytes(1024);
|
||||||
controller.enqueue(randomBytes);
|
controller.enqueue(randomBytes);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user