mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +00:00
Look up preferred ciphersuite in one go
Instead of calling getPreferredAlgo('symmetric') and getPreferredAlgo('aead'), we define and call getPreferredCipherSuite() to determine the preferred symmetric and AEAD algorithm. Additionally, we remove isAEADSupported(), instead we return aeadAlgorithm: undefined from getPreferredCipherSuite() if AEAD is not supported (CFB is used instead). And finally, we define getPreferredCompressionAlgo() to replace getPreferredAlgo('compression').
This commit is contained in:
parent
e5fe84dc2e
commit
f77ed0c0ed
@ -134,43 +134,59 @@ export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), us
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred symmetric/aead/compression algorithm for a set of keys
|
||||
* @param {'symmetric'|'aead'|'compression'} type - Type of preference to return
|
||||
* Returns the preferred compression algorithm for a set of keys
|
||||
* @param {Array<Key>} [keys] - Set of keys
|
||||
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||
* @param {Array} [userIDs] - User IDs
|
||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<module:enums.symmetric|aead|compression>} Preferred algorithm
|
||||
* @returns {Promise<module:enums.compression>} Preferred compression algorithm
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredAlgo(type, keys = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
||||
const defaultAlgo = { // these are all must-implement in the crypto refresh
|
||||
'symmetric': enums.symmetric.aes128,
|
||||
'aead': enums.aead.ocb,
|
||||
'compression': enums.compression.uncompressed
|
||||
}[type];
|
||||
const preferredSenderAlgo = {
|
||||
'symmetric': config.preferredSymmetricAlgorithm,
|
||||
'aead': config.preferredAEADAlgorithm,
|
||||
'compression': config.preferredCompressionAlgorithm
|
||||
}[type];
|
||||
const prefPropertyName = {
|
||||
'symmetric': 'preferredSymmetricAlgorithms',
|
||||
'aead': 'preferredAEADAlgorithms',
|
||||
'compression': 'preferredCompressionAlgorithms'
|
||||
}[type];
|
||||
export async function getPreferredCompressionAlgo(keys = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
||||
const defaultAlgo = enums.compression.uncompressed;
|
||||
const preferredSenderAlgo = config.preferredCompressionAlgorithm;
|
||||
|
||||
// if preferredSenderAlgo appears in the prefs of all recipients, we pick it
|
||||
// otherwise we use the default algo
|
||||
// if no keys are available, preferredSenderAlgo is returned
|
||||
const senderAlgoSupport = await Promise.all(keys.map(async function(key, i) {
|
||||
const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config);
|
||||
const recipientPrefs = selfCertification[prefPropertyName];
|
||||
const recipientPrefs = selfCertification.preferredCompressionAlgorithms;
|
||||
return !!recipientPrefs && recipientPrefs.indexOf(preferredSenderAlgo) >= 0;
|
||||
}));
|
||||
return senderAlgoSupport.every(Boolean) ? preferredSenderAlgo : defaultAlgo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred symmetric and AEAD algorithm (if any) for a set of keys
|
||||
* @param {Array<Key>} [keys] - Set of keys
|
||||
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||
* @param {Array} [userIDs] - User IDs
|
||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<{ symmetricAlgo: module:enums.symmetric, aeadAlgo: module:enums.aead | undefined }>} Object containing the preferred symmetric algorithm, and the preferred AEAD algorithm, or undefined if CFB is preferred
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredCipherSuite(keys = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
||||
const selfSigs = await Promise.all(keys.map((key, i) => key.getPrimarySelfSignature(date, userIDs[i], config)));
|
||||
if (config.aeadProtect && selfSigs.every(selfSig => selfSig.features[0] & enums.features.seipdv2)) {
|
||||
const defaultCipherSuite = { symmetricAlgo: enums.symmetric.aes128, aeadAlgo: enums.aead.ocb };
|
||||
const desiredCipherSuite = { symmetricAlgo: config.preferredSymmetricAlgorithm, aeadAlgo: config.preferredAEADAlgorithm };
|
||||
return selfSigs.every(selfSig => selfSig.preferredCipherSuites && selfSig.preferredCipherSuites.some(
|
||||
cipherSuite => cipherSuite[0] === desiredCipherSuite.symmetricAlgo && cipherSuite[1] === desiredCipherSuite.aeadAlgo
|
||||
)) ?
|
||||
desiredCipherSuite :
|
||||
defaultCipherSuite;
|
||||
}
|
||||
const defaultSymAlgo = enums.symmetric.aes128;
|
||||
const desiredSymAlgo = config.preferredSymmetricAlgorithm;
|
||||
return {
|
||||
symmetricAlgo: selfSigs.every(selfSig => selfSig.preferredSymmetricAlgorithms && selfSig.preferredSymmetricAlgorithms.includes(desiredSymAlgo)) ?
|
||||
desiredSymAlgo :
|
||||
defaultSymAlgo,
|
||||
aeadAlgo: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create signature packet
|
||||
* @param {Object} dataToSign - Contains packets to be signed
|
||||
@ -293,28 +309,6 @@ export function getKeyExpirationTime(keyPacket, signature) {
|
||||
return expirationTime ? new Date(expirationTime) : Infinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether aead is supported by all keys in the set
|
||||
* @param {Array<Key>} keys - Set of keys
|
||||
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||
* @param {Array} [userIDs] - User IDs
|
||||
* @param {Object} config - full configuration
|
||||
* @returns {Promise<Boolean>}
|
||||
* @async
|
||||
*/
|
||||
export async function isAEADSupported(keys, date = new Date(), userIDs = [], config = defaultConfig) {
|
||||
let supported = true;
|
||||
// TODO replace when Promise.some or Promise.any are implemented
|
||||
await Promise.all(keys.map(async function(key, i) {
|
||||
const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config);
|
||||
if (!selfCertification.features ||
|
||||
!(selfCertification.features[0] & enums.features.seipdv2)) {
|
||||
supported = false;
|
||||
}
|
||||
}));
|
||||
return supported;
|
||||
}
|
||||
|
||||
export function sanitizeKeyOptions(options, subkeyDefaults = {}) {
|
||||
options.type = options.type || subkeyDefaults.type;
|
||||
options.curve = options.curve || subkeyDefaults.curve;
|
||||
|
@ -8,9 +8,9 @@ import {
|
||||
} from './factory';
|
||||
|
||||
import {
|
||||
getPreferredAlgo,
|
||||
isAEADSupported,
|
||||
getPreferredHashAlgo,
|
||||
getPreferredCompressionAlgo,
|
||||
getPreferredCipherSuite,
|
||||
createSignaturePacket
|
||||
} from './helper';
|
||||
|
||||
@ -25,9 +25,9 @@ export {
|
||||
readPrivateKeys,
|
||||
generate,
|
||||
reformat,
|
||||
getPreferredAlgo,
|
||||
isAEADSupported,
|
||||
getPreferredHashAlgo,
|
||||
getPreferredCompressionAlgo,
|
||||
getPreferredCipherSuite,
|
||||
createSignaturePacket,
|
||||
PrivateKey,
|
||||
PublicKey,
|
||||
|
@ -24,7 +24,7 @@ import crypto from './crypto';
|
||||
import enums from './enums';
|
||||
import util from './util';
|
||||
import { Signature } from './signature';
|
||||
import { getPreferredHashAlgo, getPreferredAlgo, isAEADSupported, createSignaturePacket } from './key';
|
||||
import { getPreferredHashAlgo, getPreferredCipherSuite, createSignaturePacket } from './key';
|
||||
import {
|
||||
PacketList,
|
||||
LiteralDataPacket,
|
||||
@ -343,23 +343,21 @@ export class Message {
|
||||
* @async
|
||||
*/
|
||||
static async generateSessionKey(encryptionKeys = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
||||
const algo = await getPreferredAlgo('symmetric', encryptionKeys, date, userIDs, config);
|
||||
const algorithmName = enums.read(enums.symmetric, algo);
|
||||
const aeadAlgorithmName = config.aeadProtect && await isAEADSupported(encryptionKeys, date, userIDs, config) ?
|
||||
enums.read(enums.aead, await getPreferredAlgo('aead', encryptionKeys, date, userIDs, config)) :
|
||||
undefined;
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite(encryptionKeys, date, userIDs, config);
|
||||
const symmetricAlgoName = enums.read(enums.symmetric, symmetricAlgo);
|
||||
const aeadAlgoName = aeadAlgo ? enums.read(enums.aead, aeadAlgo) : undefined;
|
||||
|
||||
await Promise.all(encryptionKeys.map(key => key.getEncryptionKey()
|
||||
.catch(() => null) // ignore key strength requirements
|
||||
.then(maybeKey => {
|
||||
if (maybeKey && (maybeKey.keyPacket.algorithm === enums.publicKey.x25519) && !util.isAES(algo)) {
|
||||
if (maybeKey && (maybeKey.keyPacket.algorithm === enums.publicKey.x25519) && !util.isAES(symmetricAlgo)) {
|
||||
throw new Error('Could not generate a session key compatible with the given `encryptionKeys`: X22519 keys can only be used to encrypt AES session keys; change `config.preferredSymmetricAlgorithm` accordingly.');
|
||||
}
|
||||
})
|
||||
));
|
||||
|
||||
const sessionKeyData = crypto.generateSessionKey(algo);
|
||||
return { data: sessionKeyData, algorithm: algorithmName, aeadAlgorithm: aeadAlgorithmName };
|
||||
const sessionKeyData = crypto.generateSessionKey(symmetricAlgo);
|
||||
return { data: sessionKeyData, algorithm: symmetricAlgoName, aeadAlgorithm: aeadAlgoName };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@
|
||||
import * as stream from '@openpgp/web-stream-tools';
|
||||
import { Message } from './message';
|
||||
import { CleartextMessage } from './cleartext';
|
||||
import { generate, reformat, getPreferredAlgo } from './key';
|
||||
import { generate, reformat, getPreferredCompressionAlgo } from './key';
|
||||
import defaultConfig from './config';
|
||||
import util from './util';
|
||||
import { checkKeyRequirements } from './key/helper';
|
||||
@ -284,7 +284,7 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords,
|
||||
message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||
}
|
||||
message = message.compress(
|
||||
await getPreferredAlgo('compression', encryptionKeys, date, encryptionUserIDs, config),
|
||||
await getPreferredCompressionAlgo(encryptionKeys, date, encryptionUserIDs, config),
|
||||
config
|
||||
);
|
||||
message = await message.encrypt(encryptionKeys, passwords, sessionKey, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config);
|
||||
|
@ -6,7 +6,7 @@ chaiUse(chaiAsPromised);
|
||||
|
||||
import openpgp from '../initOpenpgp.js';
|
||||
import util from '../../src/util.js';
|
||||
import { isAEADSupported, getPreferredAlgo } from '../../src/key';
|
||||
import { getPreferredCipherSuite } from '../../src/key';
|
||||
import KeyID from '../../src/type/keyid.js';
|
||||
|
||||
|
||||
@ -3643,76 +3643,85 @@ aU71tdtNBQ==
|
||||
expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('symmetric') - one key", async function() {
|
||||
it('getPreferredCipherSuite - one key', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const prefAlgo = await getPreferredAlgo('symmetric', [key1], undefined, undefined, {
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
});
|
||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('symmetric') - two key", async function() {
|
||||
it('getPreferredCipherSuite - two keys', async function() {
|
||||
const { aes128, aes192, cast5 } = openpgp.enums.symmetric;
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = [6, aes192, cast5];
|
||||
const prefAlgo = await getPreferredAlgo('symmetric', [key1, key2], undefined, undefined, {
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes192
|
||||
});
|
||||
expect(prefAlgo).to.equal(aes192);
|
||||
const prefAlgo2 = await getPreferredAlgo('symmetric', [key1, key2], undefined, undefined, {
|
||||
expect(symmetricAlgo).to.equal(aes192);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
const { symmetricAlgo: symmetricAlgo2, aeadAlgo: aeadAlgo2 } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
});
|
||||
expect(prefAlgo2).to.equal(aes128);
|
||||
expect(symmetricAlgo2).to.equal(aes128);
|
||||
expect(aeadAlgo2).to.equal(undefined);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('symmetric') - two key - one without pref", async function() {
|
||||
it('getPreferredCipherSuite - two keys - one without pref', async function() {
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = null;
|
||||
const prefAlgo = await getPreferredAlgo('symmetric', [key1, key2]);
|
||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2]);
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('aead') - one key - OCB", async function() {
|
||||
it('getPreferredCipherSuite with AEAD - one key - GCM', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
primaryUser.selfCertification.preferredAEADAlgorithms = [2,1];
|
||||
const prefAlgo = await getPreferredAlgo('aead', [key1], undefined, undefined, {
|
||||
...openpgp.config, preferredAEADAlgorithm: openpgp.enums.aead.ocb
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(prefAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
const supported = await isAEADSupported([key1]);
|
||||
expect(supported).to.be.true;
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('aead') - two key - one without pref", async function() {
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one without pref', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
primaryUser.selfCertification.preferredAEADAlgorithms = [2,1];
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const primaryUser2 = await key2.getPrimaryUser();
|
||||
primaryUser2.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
const prefAlgo = await getPreferredAlgo('aead', [key1, key2]);
|
||||
expect(prefAlgo).to.equal(openpgp.enums.aead.eax);
|
||||
const supported = await isAEADSupported([key1, key2]);
|
||||
expect(supported).to.be.true;
|
||||
primaryUser2.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it("getPreferredAlgo('aead') - two key - one with no support", async function() {
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one with no support', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
primaryUser.selfCertification.preferredAEADAlgorithms = [2,1];
|
||||
const prefAlgo = await getPreferredAlgo('aead', [key1, key2]);
|
||||
expect(prefAlgo).to.equal(openpgp.enums.aead.eax);
|
||||
const supported = await isAEADSupported([key1, key2]);
|
||||
expect(supported).to.be.false;
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('User attribute packet read & write', async function() {
|
||||
|
@ -11,7 +11,7 @@ import crypto from '../../src/crypto';
|
||||
import * as random from '../../src/crypto/random.js';
|
||||
import util from '../../src/util.js';
|
||||
import keyIDType from '../../src/type/keyid.js';
|
||||
import { isAEADSupported } from '../../src/key';
|
||||
import { getPreferredCipherSuite } from '../../src/key';
|
||||
|
||||
import * as input from './testInputs.js';
|
||||
|
||||
@ -3054,7 +3054,8 @@ XfA3pqV4mTzF
|
||||
it('should fail to decrypt modified message', async function() {
|
||||
const allowUnauthenticatedStream = openpgp.config.allowUnauthenticatedStream;
|
||||
const { privateKey: key } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], format: 'object' });
|
||||
expect(await isAEADSupported([key])).to.equal(openpgp.config.aeadProtect);
|
||||
const { aeadAlgo } = await getPreferredCipherSuite([key], undefined, undefined, openpgp.config);
|
||||
expect(!!aeadAlgo).to.equal(openpgp.config.aeadProtect);
|
||||
|
||||
const data = await openpgp.encrypt({ message: await openpgp.createMessage({ binary: new Uint8Array(500) }), encryptionKeys: [key.toPublic()] });
|
||||
const encrypted = data.substr(0, 500) + (data[500] === 'a' ? 'b' : 'a') + data.substr(501);
|
||||
|
Loading…
x
Reference in New Issue
Block a user