Only AEAD-protect when target keys support it
This commit is contained in:
parent
e9a360019c
commit
e24b46192d
26
src/key.js
26
src/key.js
|
@ -1402,14 +1402,15 @@ function getExpirationTime(keyPacket, signature) {
|
|||
/**
|
||||
* Returns the preferred signature hash algorithm of a key
|
||||
* @param {object} key
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @returns {Promise<String>}
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredHashAlgo(key) {
|
||||
export async function getPreferredHashAlgo(key, date) {
|
||||
let hash_algo = config.prefer_hash_algorithm;
|
||||
let pref_algo = hash_algo;
|
||||
if (key instanceof Key) {
|
||||
const primaryUser = await key.getPrimaryUser();
|
||||
const primaryUser = await key.getPrimaryUser(date);
|
||||
if (primaryUser && primaryUser.selfCertification.preferredHashAlgorithms) {
|
||||
[pref_algo] = primaryUser.selfCertification.preferredHashAlgorithms;
|
||||
hash_algo = crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
|
||||
|
@ -1437,13 +1438,14 @@ export async function getPreferredHashAlgo(key) {
|
|||
/**
|
||||
* Returns the preferred symmetric algorithm for a set of keys
|
||||
* @param {Array<module:key.Key>} keys Set of keys
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @returns {Promise<module:enums.symmetric>} Preferred symmetric algorithm
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredSymAlgo(keys) {
|
||||
export async function getPreferredSymAlgo(keys, date) {
|
||||
const prioMap = {};
|
||||
await Promise.all(keys.map(async function(key) {
|
||||
const primaryUser = await key.getPrimaryUser();
|
||||
const primaryUser = await key.getPrimaryUser(date);
|
||||
if (!primaryUser || !primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
||||
return config.encryption_cipher;
|
||||
}
|
||||
|
@ -1471,13 +1473,20 @@ export async function getPreferredSymAlgo(keys) {
|
|||
/**
|
||||
* Returns the preferred aead algorithm for a set of keys
|
||||
* @param {Array<module:key.Key>} keys Set of keys
|
||||
* @returns {Promise<module:enums.aead>} Preferred aead algorithm
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @returns {Promise<module:enums.aead>} Preferred aead algorithm, or null if the public keys do not support aead
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredAeadAlgo(keys) {
|
||||
export async function getPreferredAeadAlgo(keys, date) {
|
||||
let supports_aead = true;
|
||||
const prioMap = {};
|
||||
await Promise.all(keys.map(async function(key) {
|
||||
const primaryUser = await key.getPrimaryUser();
|
||||
const primaryUser = await key.getPrimaryUser(date);
|
||||
if (!primaryUser || !primaryUser.selfCertification.features ||
|
||||
!(primaryUser.selfCertification.features[0] & enums.features.aead)) {
|
||||
supports_aead = false;
|
||||
return;
|
||||
}
|
||||
if (!primaryUser || !primaryUser.selfCertification.preferredAeadAlgorithms) {
|
||||
return config.aead_mode;
|
||||
}
|
||||
|
@ -1487,6 +1496,9 @@ export async function getPreferredAeadAlgo(keys) {
|
|||
entry.count++;
|
||||
});
|
||||
}));
|
||||
if (!supports_aead) {
|
||||
return null;
|
||||
}
|
||||
let prefAlgo = { prio: 0, algo: config.aead_mode };
|
||||
for (const algo in prioMap) {
|
||||
try {
|
||||
|
|
|
@ -93,7 +93,7 @@ Message.prototype.getSigningKeyIds = function() {
|
|||
* Decrypt the message. Either a private key, a session key, or a password must be specified.
|
||||
* @param {Array<Key>} privateKeys (optional) private keys with decrypted secret data
|
||||
* @param {Array<String>} passwords (optional) passwords used to decrypt
|
||||
* @param {Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String }
|
||||
* @param {Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] }
|
||||
* @returns {Promise<Message>} new message with decrypted content
|
||||
* @async
|
||||
*/
|
||||
|
@ -244,7 +244,7 @@ Message.prototype.getText = function() {
|
|||
* Encrypt the message either with public keys, passwords, or both at once.
|
||||
* @param {Array<Key>} keys (optional) public key(s) for message encryption
|
||||
* @param {Array<String>} passwords (optional) password(s) for message encryption
|
||||
* @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String }
|
||||
* @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] }
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
* @param {Date} date (optional) override the creation date of the literal package
|
||||
* @returns {Promise<Message>} new message with encrypted content
|
||||
|
@ -260,11 +260,14 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
throw new Error('Invalid session key for encryption.');
|
||||
}
|
||||
symAlgo = sessionKey.algorithm;
|
||||
aeadAlgo = sessionKey.aeadAlgorithm || config.aead_mode;
|
||||
aeadAlgo = sessionKey.aeadAlgorithm;
|
||||
sessionKey = sessionKey.data;
|
||||
} else if (keys && keys.length) {
|
||||
symAlgo = enums.read(enums.symmetric, await getPreferredSymAlgo(keys));
|
||||
aeadAlgo = enums.read(enums.aead, await getPreferredAeadAlgo(keys));
|
||||
symAlgo = enums.read(enums.symmetric, await getPreferredSymAlgo(keys, date));
|
||||
aeadAlgo = await getPreferredAeadAlgo(keys, date);
|
||||
if (aeadAlgo) {
|
||||
aeadAlgo = enums.read(enums.aead, aeadAlgo);
|
||||
}
|
||||
} else if (passwords && passwords.length) {
|
||||
symAlgo = enums.read(enums.symmetric, config.encryption_cipher);
|
||||
aeadAlgo = enums.read(enums.aead, config.aead_mode);
|
||||
|
@ -278,7 +281,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
|
||||
const msg = await encryptSessionKey(sessionKey, symAlgo, aeadAlgo, keys, passwords, wildcard, date);
|
||||
|
||||
if (config.aead_protect) {
|
||||
if (config.aead_protect && (config.aead_protect_version !== 4 || aeadAlgo)) {
|
||||
symEncryptedPacket = new packet.SymEncryptedAEADProtected();
|
||||
symEncryptedPacket.aeadAlgorithm = aeadAlgo;
|
||||
} else if (config.integrity_protect) {
|
||||
|
@ -423,7 +426,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, date=new
|
|||
}
|
||||
const onePassSig = new packet.OnePassSignature();
|
||||
onePassSig.type = signatureType;
|
||||
onePassSig.hashAlgorithm = await getPreferredHashAlgo(privateKey);
|
||||
onePassSig.hashAlgorithm = await getPreferredHashAlgo(privateKey, date);
|
||||
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
|
||||
if (i === privateKeys.length - 1) {
|
||||
|
@ -507,7 +510,7 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
|
|||
const signaturePacket = new packet.Signature(date);
|
||||
signaturePacket.signatureType = signatureType;
|
||||
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey);
|
||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, date);
|
||||
await signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
||||
return signaturePacket;
|
||||
})).then(signatureList => {
|
||||
|
|
|
@ -1199,6 +1199,7 @@ p92yZgB3r2+f6/GIe2+7
|
|||
it('getPreferredAeadAlgo() - one key - OCB', async function() {
|
||||
const key1 = openpgp.key.readArmored(twoKeys).keys[0];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
primaryUser.selfCertification.preferredAeadAlgorithms = [2,1];
|
||||
const prefAlgo = await openpgp.key.getPreferredAeadAlgo([key1]);
|
||||
expect(prefAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
|
@ -1209,11 +1210,25 @@ p92yZgB3r2+f6/GIe2+7
|
|||
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 primaryUser2 = await key2.getPrimaryUser();
|
||||
primaryUser2.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||
const prefAlgo = await openpgp.key.getPreferredAeadAlgo([key1, key2]);
|
||||
expect(prefAlgo).to.equal(openpgp.config.aead_mode);
|
||||
});
|
||||
|
||||
it('getPreferredAeadAlgo() - two key - one with no support', async function() {
|
||||
const keys = openpgp.key.readArmored(twoKeys).keys;
|
||||
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 openpgp.key.getPreferredAeadAlgo([key1, key2]);
|
||||
expect(prefAlgo).to.be.null;
|
||||
});
|
||||
|
||||
it('Preferences of generated key', function() {
|
||||
const testPref = function(key) {
|
||||
// key flags
|
||||
|
|
|
@ -604,6 +604,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
publicKey = openpgp.key.readArmored(pub_key);
|
||||
expect(publicKey.keys).to.have.length(1);
|
||||
expect(publicKey.err).to.not.exist;
|
||||
publicKeyNoAEAD = openpgp.key.readArmored(pub_key);
|
||||
privateKey = openpgp.key.readArmored(priv_key);
|
||||
expect(privateKey.keys).to.have.length(1);
|
||||
expect(privateKey.err).to.not.exist;
|
||||
|
@ -679,6 +680,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.use_native = false;
|
||||
openpgp.config.aead_protect = true;
|
||||
openpgp.config.aead_protect_version = 4;
|
||||
|
||||
// Monkey-patch AEAD feature flag
|
||||
publicKey.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2000_2008.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2038_2045.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -688,6 +694,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.use_native = true;
|
||||
openpgp.config.aead_protect = true;
|
||||
openpgp.config.aead_protect_version = 4;
|
||||
|
||||
// Monkey-patch AEAD feature flag
|
||||
publicKey.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2000_2008.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2038_2045.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -697,6 +708,11 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.aead_protect = true;
|
||||
openpgp.config.aead_protect_version = 4;
|
||||
openpgp.config.aead_mode = openpgp.enums.aead.ocb;
|
||||
|
||||
// Monkey-patch AEAD feature flag
|
||||
publicKey.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2000_2008.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
publicKey_2038_2045.keys[0].users[0].selfCertifications[0].features = [7];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1020,20 +1036,21 @@ describe('OpenPGP.js public api tests', function() {
|
|||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
expect(encrypted.data).to.match(/^-----BEGIN PGP MESSAGE/);
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedAEADProtected)).to.equal(openpgp.config.aead_protect && openpgp.config.aead_protect_version !== 4);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt using custom session key and decrypt using private key', function () {
|
||||
it('should encrypt using custom session key and decrypt using private key', async function () {
|
||||
const sessionKey = {
|
||||
data: openpgp.crypto.generateSessionKey('aes128'),
|
||||
data: await openpgp.crypto.generateSessionKey('aes128'),
|
||||
algorithm: 'aes128'
|
||||
};
|
||||
const encOpt = {
|
||||
data: plaintext,
|
||||
sessionKeys: sessionKey,
|
||||
sessionKey: sessionKey,
|
||||
publicKeys: publicKey.keys
|
||||
};
|
||||
const decOpt = {
|
||||
|
@ -1042,6 +1059,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
expect(encrypted.data).to.match(/^-----BEGIN PGP MESSAGE/);
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedAEADProtected)).to.equal(openpgp.config.aead_protect && openpgp.config.aead_protect_version !== 4);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
|
@ -1060,6 +1078,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
};
|
||||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedAEADProtected)).to.equal(openpgp.config.aead_protect);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(async function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
|
@ -1070,6 +1089,63 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should encrypt/sign and decrypt/verify (no AEAD support)', function () {
|
||||
const encOpt = {
|
||||
data: plaintext,
|
||||
publicKeys: publicKeyNoAEAD.keys,
|
||||
privateKeys: privateKey.keys
|
||||
};
|
||||
const decOpt = {
|
||||
privateKeys: privateKey.keys[0],
|
||||
publicKeys: publicKeyNoAEAD.keys
|
||||
};
|
||||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedAEADProtected)).to.equal(openpgp.config.aead_protect && openpgp.config.aead_protect_version !== 4);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(async function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
expect(decrypted.signatures[0].valid).to.be.true;
|
||||
const keyPacket = await privateKey.keys[0].getSigningKeyPacket();
|
||||
expect(decrypted.signatures[0].keyid.toHex()).to.equal(keyPacket.getKeyId().toHex());
|
||||
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt/sign and decrypt/verify with generated key', function () {
|
||||
const genOpt = {
|
||||
userIds: [{ name: 'Test User', email: 'text@example.com' }],
|
||||
numBits: 512
|
||||
};
|
||||
if (openpgp.util.getWebCryptoAll()) { genOpt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||
|
||||
return openpgp.generateKey(genOpt).then(function(newKey) {
|
||||
const newPublicKey = openpgp.key.readArmored(newKey.publicKeyArmored);
|
||||
const newPrivateKey = openpgp.key.readArmored(newKey.privateKeyArmored);
|
||||
|
||||
const encOpt = {
|
||||
data: plaintext,
|
||||
publicKeys: newPublicKey.keys,
|
||||
privateKeys: newPrivateKey.keys
|
||||
};
|
||||
const decOpt = {
|
||||
privateKeys: newPrivateKey.keys[0],
|
||||
publicKeys: newPublicKey.keys
|
||||
};
|
||||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedAEADProtected)).to.equal(openpgp.config.aead_protect);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(async function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
expect(decrypted.signatures[0].valid).to.be.true;
|
||||
const keyPacket = await newPrivateKey.keys[0].getSigningKeyPacket();
|
||||
expect(decrypted.signatures[0].keyid.toHex()).to.equal(keyPacket.getKeyId().toHex());
|
||||
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should encrypt/sign and decrypt/verify with null string input', function () {
|
||||
const encOpt = {
|
||||
data: '',
|
||||
|
@ -1719,6 +1795,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
const pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0];
|
||||
const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0];
|
||||
await privKeyDE.decrypt(passphrase);
|
||||
pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag
|
||||
return openpgp.encrypt({
|
||||
publicKeys: pubKeyDE,
|
||||
privateKeys: privKeyDE,
|
||||
|
|
Loading…
Reference in New Issue
Block a user