Rename config.ignoreMdcError
, drop config.integrityProtect
and allow V4 keys to be AEAD-encrypted (#1261)
* Rename `config.ignoreMdcError` to `config.allowUnauthenticatedMessages` * Do not support creating sym. enc. messages without integrity protection * Use `config.aeadProtect` to determine SKESK encryption mode
This commit is contained in:
parent
f41412a5a2
commit
6e2a787ff8
4
openpgp.d.ts
vendored
4
openpgp.d.ts
vendored
|
@ -305,10 +305,10 @@ interface Config {
|
||||||
compression: enums.compression;
|
compression: enums.compression;
|
||||||
showVersion: boolean;
|
showVersion: boolean;
|
||||||
showComment: boolean;
|
showComment: boolean;
|
||||||
integrityProtect: boolean;
|
|
||||||
deflateLevel: number;
|
deflateLevel: number;
|
||||||
aeadProtect: boolean;
|
aeadProtect: boolean;
|
||||||
ignoreMdcError: boolean;
|
allowUnauthenticatedMessages: boolean;
|
||||||
|
allowUnauthenticatedStream: boolean;
|
||||||
checksumRequired: boolean;
|
checksumRequired: boolean;
|
||||||
minRsaBits: number;
|
minRsaBits: number;
|
||||||
passwordCollisionCheck: boolean;
|
passwordCollisionCheck: boolean;
|
||||||
|
|
|
@ -82,19 +82,20 @@ export default {
|
||||||
* @property {Integer} s2kIterationCountByte
|
* @property {Integer} s2kIterationCountByte
|
||||||
*/
|
*/
|
||||||
s2kIterationCountByte: 224,
|
s2kIterationCountByte: 224,
|
||||||
/** Use integrity protection for symmetric encryption
|
|
||||||
* @memberof module:config
|
|
||||||
* @property {Boolean} integrityProtect
|
|
||||||
*/
|
|
||||||
integrityProtect: true,
|
|
||||||
/**
|
/**
|
||||||
|
* Allow decryption of messages without integrity protection.
|
||||||
|
* This is an **insecure** setting:
|
||||||
|
* - message modifications cannot be detected, thus processing the decrypted data is potentially unsafe.
|
||||||
|
* - it enables downgrade attacks against integrity-protected messages.
|
||||||
* @memberof module:config
|
* @memberof module:config
|
||||||
* @property {Boolean} ignoreMdcError Fail on decrypt if message is not integrity protected
|
* @property {Boolean} allowUnauthenticatedMessages
|
||||||
*/
|
*/
|
||||||
ignoreMdcError: false,
|
allowUnauthenticatedMessages: false,
|
||||||
/**
|
/**
|
||||||
|
* Allow streaming unauthenticated data before its integrity has been checked.
|
||||||
|
* This setting is **insecure** if the partially decrypted message is processed further or displayed to the user.
|
||||||
* @memberof module:config
|
* @memberof module:config
|
||||||
* @property {Boolean} allowUnauthenticatedStream Stream unauthenticated data before integrity has been checked
|
* @property {Boolean} allowUnauthenticatedStream
|
||||||
*/
|
*/
|
||||||
allowUnauthenticatedStream: false,
|
allowUnauthenticatedStream: false,
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -181,16 +181,13 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
signaturePacket.isPrimaryUserID = true;
|
signaturePacket.isPrimaryUserID = true;
|
||||||
}
|
}
|
||||||
if (config.integrityProtect) {
|
// integrity protection always enabled
|
||||||
signaturePacket.features = [0];
|
signaturePacket.features = [0];
|
||||||
signaturePacket.features[0] |= enums.features.modificationDetection;
|
signaturePacket.features[0] |= enums.features.modificationDetection;
|
||||||
}
|
|
||||||
if (config.aeadProtect) {
|
if (config.aeadProtect) {
|
||||||
signaturePacket.features || (signaturePacket.features = [0]);
|
|
||||||
signaturePacket.features[0] |= enums.features.aead;
|
signaturePacket.features[0] |= enums.features.aead;
|
||||||
}
|
}
|
||||||
if (config.v5Keys) {
|
if (config.v5Keys) {
|
||||||
signaturePacket.features || (signaturePacket.features = [0]);
|
|
||||||
signaturePacket.features[0] |= enums.features.v5Keys;
|
signaturePacket.features[0] |= enums.features.v5Keys;
|
||||||
}
|
}
|
||||||
if (options.keyExpirationTime > 0) {
|
if (options.keyExpirationTime > 0) {
|
||||||
|
|
|
@ -326,10 +326,8 @@ export class Message {
|
||||||
if (aeadAlgorithm) {
|
if (aeadAlgorithm) {
|
||||||
symEncryptedPacket = new AEADEncryptedDataPacket();
|
symEncryptedPacket = new AEADEncryptedDataPacket();
|
||||||
symEncryptedPacket.aeadAlgorithm = aeadAlgorithm;
|
symEncryptedPacket.aeadAlgorithm = aeadAlgorithm;
|
||||||
} else if (config.integrityProtect) {
|
|
||||||
symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket();
|
|
||||||
} else {
|
} else {
|
||||||
symEncryptedPacket = new SymmetricallyEncryptedDataPacket();
|
symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket();
|
||||||
}
|
}
|
||||||
symEncryptedPacket.packets = this.packets;
|
symEncryptedPacket.packets = this.packets;
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,7 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
const blockLen = crypto.cipher[this.symmetric].blockSize;
|
const blockLen = crypto.cipher[this.symmetric].blockSize;
|
||||||
this.iv = await crypto.random.getRandomBytes(blockLen);
|
this.iv = await crypto.random.getRandomBytes(blockLen);
|
||||||
|
|
||||||
if (this.version === 5) {
|
if (config.aeadProtect) {
|
||||||
this.s2k_usage = 253;
|
this.s2k_usage = 253;
|
||||||
this.aead = 'eax';
|
this.aead = 'eax';
|
||||||
const mode = crypto[this.aead];
|
const mode = crypto[this.aead];
|
||||||
|
|
|
@ -75,8 +75,8 @@ class SymmetricallyEncryptedDataPacket {
|
||||||
*/
|
*/
|
||||||
async decrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
|
async decrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
|
||||||
// If MDC errors are not being ignored, all missing MDC packets in symmetrically encrypted data should throw an error
|
// If MDC errors are not being ignored, all missing MDC packets in symmetrically encrypted data should throw an error
|
||||||
if (!config.ignoreMdcError) {
|
if (!config.allowUnauthenticatedMessages) {
|
||||||
throw new Error('Decryption failed due to missing MDC.');
|
throw new Error('Message is not authenticated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.encrypted = await stream.readToEnd(this.encrypted);
|
this.encrypted = await stream.readToEnd(this.encrypted);
|
||||||
|
|
|
@ -67,9 +67,9 @@ module.exports = () => describe("Packet", function() {
|
||||||
|
|
||||||
it('Symmetrically encrypted packet without integrity protection - allow decryption', async function() {
|
it('Symmetrically encrypted packet without integrity protection - allow decryption', async function() {
|
||||||
const aeadProtectVal = openpgp.config.aeadProtect;
|
const aeadProtectVal = openpgp.config.aeadProtect;
|
||||||
const ignoreMdcErrorVal = openpgp.config.ignoreMdcError;
|
const allowUnauthenticatedMessagesVal = openpgp.config.allowUnauthenticatedMessages;
|
||||||
openpgp.config.aeadProtect = false;
|
openpgp.config.aeadProtect = false;
|
||||||
openpgp.config.ignoreMdcError = true;
|
openpgp.config.allowUnauthenticatedMessages = true;
|
||||||
|
|
||||||
const message = new openpgp.PacketList();
|
const message = new openpgp.PacketList();
|
||||||
const testText = input.createSomeMessage();
|
const testText = input.createSomeMessage();
|
||||||
|
@ -94,7 +94,7 @@ module.exports = () => describe("Packet", function() {
|
||||||
expect(await stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
|
expect(await stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.aeadProtect = aeadProtectVal;
|
openpgp.config.aeadProtect = aeadProtectVal;
|
||||||
openpgp.config.ignoreMdcError = ignoreMdcErrorVal;
|
openpgp.config.allowUnauthenticatedMessages = allowUnauthenticatedMessagesVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ module.exports = () => describe("Packet", function() {
|
||||||
|
|
||||||
const msg2 = new openpgp.PacketList();
|
const msg2 = new openpgp.PacketList();
|
||||||
await msg2.read(message.write(), { SymmetricallyEncryptedDataPacket: openpgp.SymmetricallyEncryptedDataPacket });
|
await msg2.read(message.write(), { SymmetricallyEncryptedDataPacket: openpgp.SymmetricallyEncryptedDataPacket });
|
||||||
await expect(msg2[0].decrypt(algo, key, undefined, openpgp.config)).to.eventually.be.rejectedWith('Decryption failed due to missing MDC.');
|
await expect(msg2[0].decrypt(algo, key, undefined, openpgp.config)).to.eventually.be.rejectedWith('Message is not authenticated.');
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.aeadProtect = aeadProtectVal;
|
openpgp.config.aeadProtect = aeadProtectVal;
|
||||||
}
|
}
|
||||||
|
@ -838,12 +838,12 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
|
||||||
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
const { privateParams, publicParams } = await crypto.generateParams(rsa, 1024, 65537);
|
const { privateParams, publicParams } = await crypto.generateParams(rsa, 1024, 65537);
|
||||||
|
|
||||||
const secretKeyPacket = new openpgp.SecretKeyPacket(undefined, { ...openpgp.config, v5Keys: true });
|
const secretKeyPacket = new openpgp.SecretKeyPacket();
|
||||||
secretKeyPacket.privateParams = privateParams;
|
secretKeyPacket.privateParams = privateParams;
|
||||||
secretKeyPacket.publicParams = publicParams;
|
secretKeyPacket.publicParams = publicParams;
|
||||||
secretKeyPacket.algorithm = "rsaSign";
|
secretKeyPacket.algorithm = "rsaSign";
|
||||||
secretKeyPacket.isEncrypted = false;
|
secretKeyPacket.isEncrypted = false;
|
||||||
await secretKeyPacket.encrypt('hello', openpgp.config);
|
await secretKeyPacket.encrypt('hello', { ...openpgp.config, aeadProtect: true });
|
||||||
expect(secretKeyPacket.s2k_usage).to.equal(253);
|
expect(secretKeyPacket.s2k_usage).to.equal(253);
|
||||||
|
|
||||||
const raw = new openpgp.PacketList();
|
const raw = new openpgp.PacketList();
|
||||||
|
@ -860,12 +860,12 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
|
||||||
it('Writing and encryption of a secret key packet (CFB)', async function() {
|
it('Writing and encryption of a secret key packet (CFB)', async function() {
|
||||||
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
const { privateParams, publicParams } = await crypto.generateParams(rsa, 1024, 65537);
|
const { privateParams, publicParams } = await crypto.generateParams(rsa, 1024, 65537);
|
||||||
const secretKeyPacket = new openpgp.SecretKeyPacket(undefined, { ...openpgp.config, v5Keys: false });
|
const secretKeyPacket = new openpgp.SecretKeyPacket();
|
||||||
secretKeyPacket.privateParams = privateParams;
|
secretKeyPacket.privateParams = privateParams;
|
||||||
secretKeyPacket.publicParams = publicParams;
|
secretKeyPacket.publicParams = publicParams;
|
||||||
secretKeyPacket.algorithm = "rsaSign";
|
secretKeyPacket.algorithm = "rsaSign";
|
||||||
secretKeyPacket.isEncrypted = false;
|
secretKeyPacket.isEncrypted = false;
|
||||||
await secretKeyPacket.encrypt('hello', openpgp.config);
|
await secretKeyPacket.encrypt('hello', { ...openpgp.config, aeadProtect: false });
|
||||||
expect(secretKeyPacket.s2k_usage).to.equal(254);
|
expect(secretKeyPacket.s2k_usage).to.equal(254);
|
||||||
|
|
||||||
const raw = new openpgp.PacketList();
|
const raw = new openpgp.PacketList();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user