Implement preferred AEAD algorithms
This commit is contained in:
parent
93f75f398f
commit
5f97a8c937
|
@ -51,6 +51,13 @@ export default {
|
|||
* @property {Boolean} aead_protect
|
||||
*/
|
||||
aead_protect: false,
|
||||
/**
|
||||
* Default Authenticated Encryption with Additional Data (AEAD) encryption mode
|
||||
* Only has an effect when aead_protect is set to true.
|
||||
* @memberof module:config
|
||||
* @property {Integer} aead_mode Default AEAD mode {@link module:enums.aead}
|
||||
*/
|
||||
aead_mode: enums.aead.eax,
|
||||
/**
|
||||
* Chunk Size Byte for Authenticated Encryption with Additional Data (AEAD) mode
|
||||
* Only has an effect when aead_protect is set to true.
|
||||
|
|
|
@ -366,7 +366,8 @@ export default {
|
|||
reason_for_revocation: 29,
|
||||
features: 30,
|
||||
signature_target: 31,
|
||||
embedded_signature: 32
|
||||
embedded_signature: 32,
|
||||
preferred_aead_algorithms: 34
|
||||
},
|
||||
|
||||
/** Key flags
|
||||
|
|
37
src/key.js
37
src/key.js
|
@ -1261,6 +1261,11 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
|
|||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
|
||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
|
||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
|
||||
if (config.aead_protect === 'draft04') {
|
||||
signaturePacket.preferredAeadAlgorithms = [];
|
||||
signaturePacket.preferredAeadAlgorithms.push(enums.aead.eax);
|
||||
signaturePacket.preferredAeadAlgorithms.push(enums.aead.ocb);
|
||||
}
|
||||
signaturePacket.preferredHashAlgorithms = [];
|
||||
// prefer fast asm.js implementations (SHA-256). SHA-1 will not be secure much longer...move to bottom of list
|
||||
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256);
|
||||
|
@ -1457,3 +1462,35 @@ export async function getPreferredSymAlgo(keys) {
|
|||
}
|
||||
return prefAlgo.algo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredAeadAlgo(keys) {
|
||||
const prioMap = {};
|
||||
await Promise.all(keys.map(async function(key) {
|
||||
const primaryUser = await key.getPrimaryUser();
|
||||
if (!primaryUser || !primaryUser.selfCertification.preferredAeadAlgorithms) {
|
||||
return config.aead_mode;
|
||||
}
|
||||
primaryUser.selfCertification.preferredAeadAlgorithms.forEach(function(algo, index) {
|
||||
const entry = prioMap[algo] || (prioMap[algo] = { prio: 0, count: 0, algo: algo });
|
||||
entry.prio += 64 >> index;
|
||||
entry.count++;
|
||||
});
|
||||
}));
|
||||
let prefAlgo = { prio: 0, algo: config.aead_mode };
|
||||
for (const algo in prioMap) {
|
||||
try {
|
||||
if (enums.read(enums.aead, algo) && // known algorithm
|
||||
prioMap[algo].count === keys.length && // available for all keys
|
||||
prioMap[algo].prio > prefAlgo.prio) {
|
||||
prefAlgo = prioMap[algo];
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
return prefAlgo.algo;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import enums from './enums';
|
|||
import util from './util';
|
||||
import packet from './packet';
|
||||
import { Signature } from './signature';
|
||||
import { getPreferredHashAlgo, getPreferredSymAlgo } from './key';
|
||||
import { getPreferredHashAlgo, getPreferredSymAlgo, getPreferredAeadAlgo } from './key';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -252,6 +252,7 @@ Message.prototype.getText = function() {
|
|||
*/
|
||||
Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard=false, date=new Date()) {
|
||||
let symAlgo;
|
||||
let aeadAlgo;
|
||||
let symEncryptedPacket;
|
||||
|
||||
if (sessionKey) {
|
||||
|
@ -259,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;
|
||||
sessionKey = sessionKey.data;
|
||||
} else if (keys && keys.length) {
|
||||
symAlgo = enums.read(enums.symmetric, await getPreferredSymAlgo(keys));
|
||||
aeadAlgo = enums.read(enums.aead, await getPreferredAeadAlgo(keys));
|
||||
} else if (passwords && passwords.length) {
|
||||
symAlgo = enums.read(enums.symmetric, config.encryption_cipher);
|
||||
aeadAlgo = enums.read(enums.aead, config.aead_mode);
|
||||
} else {
|
||||
throw new Error('No keys, passwords, or session key provided.');
|
||||
}
|
||||
|
@ -272,10 +276,11 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
sessionKey = await crypto.generateSessionKey(symAlgo);
|
||||
}
|
||||
|
||||
const msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, date);
|
||||
const msg = await encryptSessionKey(sessionKey, symAlgo, aeadAlgo, keys, passwords, wildcard, date);
|
||||
|
||||
if (config.aead_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedAEADProtected();
|
||||
symEncryptedPacket.aeadAlgorithm = aeadAlgo;
|
||||
} else if (config.integrity_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
|
||||
} else {
|
||||
|
@ -291,7 +296,8 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
message: msg,
|
||||
sessionKey: {
|
||||
data: sessionKey,
|
||||
algorithm: symAlgo
|
||||
algorithm: symAlgo,
|
||||
aeadAlgorithm: aeadAlgo
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -300,6 +306,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
* Encrypt a session key either with public keys, passwords, or both at once.
|
||||
* @param {Uint8Array} sessionKey session key for encryption
|
||||
* @param {String} symAlgo session key algorithm
|
||||
* @param {String} aeadAlgo (optional) aead algorithm, e.g. 'eax' or 'ocb'
|
||||
* @param {Array<Key>} publicKeys (optional) public key(s) for message encryption
|
||||
* @param {Array<String>} passwords (optional) for message encryption
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
|
@ -307,7 +314,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
|
|||
* @returns {Promise<Message>} new message with encrypted content
|
||||
* @async
|
||||
*/
|
||||
export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wildcard=false, date=new Date()) {
|
||||
export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKeys, passwords, wildcard=false, date=new Date()) {
|
||||
const packetlist = new packet.List();
|
||||
|
||||
if (publicKeys) {
|
||||
|
@ -340,10 +347,13 @@ export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwor
|
|||
|
||||
const sum = (accumulator, currentValue) => accumulator + currentValue;
|
||||
|
||||
const encryptPassword = async function(sessionKey, symAlgo, password) {
|
||||
const encryptPassword = async function(sessionKey, symAlgo, aeadAlgo, password) {
|
||||
const symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey();
|
||||
symEncryptedSessionKeyPacket.sessionKey = sessionKey;
|
||||
symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
if (aeadAlgo) {
|
||||
symEncryptedSessionKeyPacket.aeadAlgorithm = aeadAlgo;
|
||||
}
|
||||
await symEncryptedSessionKeyPacket.encrypt(password);
|
||||
|
||||
if (config.password_collision_check) {
|
||||
|
@ -357,7 +367,7 @@ export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwor
|
|||
return symEncryptedSessionKeyPacket;
|
||||
};
|
||||
|
||||
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, pwd)));
|
||||
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, aeadAlgo, pwd)));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
|
||||
|
|
|
@ -395,6 +395,7 @@ export function verify({ message, publicKeys, signature=null, date=new Date() })
|
|||
* or passwords must be specified.
|
||||
* @param {Uint8Array} data the session key to be encrypted e.g. 16 random bytes (for aes128)
|
||||
* @param {String} algorithm algorithm of the symmetric session key e.g. 'aes128' or 'aes256'
|
||||
* @param {String} aeadAlgorithm (optional) aead algorithm, e.g. 'eax' or 'ocb'
|
||||
* @param {Key|Array<Key>} publicKeys (optional) array of public keys or single key, used to encrypt the key
|
||||
* @param {String|Array<String>} passwords (optional) passwords for the message
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
|
@ -402,16 +403,16 @@ export function verify({ message, publicKeys, signature=null, date=new Date() })
|
|||
* @async
|
||||
* @static
|
||||
*/
|
||||
export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wildcard=false }) {
|
||||
export function encryptSessionKey({ data, algorithm, aeadAlgorithm, publicKeys, passwords, wildcard=false }) {
|
||||
checkBinary(data); checkString(algorithm, 'algorithm'); publicKeys = toArray(publicKeys); passwords = toArray(passwords);
|
||||
|
||||
if (asyncProxy) { // use web worker if available
|
||||
return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords, wildcard });
|
||||
return asyncProxy.delegate('encryptSessionKey', { data, algorithm, aeadAlgorithm, publicKeys, passwords, wildcard });
|
||||
}
|
||||
|
||||
return Promise.resolve().then(async function() {
|
||||
|
||||
return { message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords, wildcard) };
|
||||
return { message: await messageLib.encryptSessionKey(data, algorithm, aeadAlgorithm, publicKeys, passwords, wildcard) };
|
||||
|
||||
}).catch(onError.bind(null, 'Error encrypting session key'));
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ function Signature(date=new Date()) {
|
|||
this.signatureTargetHashAlgorithm = null;
|
||||
this.signatureTargetHash = null;
|
||||
this.embeddedSignature = null;
|
||||
this.preferredAeadAlgorithms = null;
|
||||
|
||||
this.verified = null;
|
||||
this.revoked = null;
|
||||
|
@ -355,6 +356,10 @@ Signature.prototype.write_all_sub_packets = function () {
|
|||
if (this.embeddedSignature !== null) {
|
||||
arr.push(write_sub_packet(sub.embedded_signature, this.embeddedSignature.write()));
|
||||
}
|
||||
if (this.preferredAeadAlgorithms !== null) {
|
||||
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredAeadAlgorithms));
|
||||
arr.push(write_sub_packet(sub.preferred_aead_algorithms, bytes));
|
||||
}
|
||||
|
||||
const result = util.concatUint8Array(arr);
|
||||
const length = util.writeNumber(result.length, 2);
|
||||
|
@ -531,6 +536,10 @@ Signature.prototype.read_sub_packet = function (bytes) {
|
|||
this.embeddedSignature = new Signature();
|
||||
this.embeddedSignature.read(bytes.subarray(mypos, bytes.length));
|
||||
break;
|
||||
case 34:
|
||||
// Preferred AEAD Algorithms
|
||||
read_array.call(this, 'preferredAeadAlgorithms', bytes.subarray(mypos, bytes.length));
|
||||
break;
|
||||
default:
|
||||
util.print_debug("Unknown signature subpacket type " + type + " @:" + mypos);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ function SymEncryptedAEADProtected() {
|
|||
this.tag = enums.packet.symEncryptedAEADProtected;
|
||||
this.version = VERSION;
|
||||
this.cipherAlgo = null;
|
||||
this.aeadAlgorithm = 'eax';
|
||||
this.aeadAlgo = null;
|
||||
this.chunkSizeByte = null;
|
||||
this.iv = null;
|
||||
|
@ -131,7 +132,7 @@ SymEncryptedAEADProtected.prototype.decrypt = async function (sessionKeyAlgorith
|
|||
* @async
|
||||
*/
|
||||
SymEncryptedAEADProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
|
||||
this.aeadAlgo = config.aead_protect === 'draft04' ? enums.aead.eax : enums.aead.gcm;
|
||||
this.aeadAlgo = config.aead_protect === 'draft04' ? enums.write(enums.aead, this.aeadAlgorithm) : enums.aead.gcm;
|
||||
const mode = crypto[enums.read(enums.aead, this.aeadAlgo)];
|
||||
this.iv = await crypto.random.getRandomBytes(mode.ivLength); // generate new random IV
|
||||
let data = this.packets.write();
|
||||
|
|
|
@ -53,7 +53,7 @@ function SymEncryptedSessionKey() {
|
|||
this.sessionKey = null;
|
||||
this.sessionKeyEncryptionAlgorithm = null;
|
||||
this.sessionKeyAlgorithm = 'aes256';
|
||||
this.aeadAlgorithm = 'eax';
|
||||
this.aeadAlgorithm = enums.read(enums.aead, config.aead_mode);
|
||||
this.encrypted = null;
|
||||
this.s2k = null;
|
||||
this.iv = null;
|
||||
|
|
|
@ -1192,6 +1192,24 @@ p92yZgB3r2+f6/GIe2+7
|
|||
expect(prefAlgo).to.equal(openpgp.config.encryption_cipher);
|
||||
});
|
||||
|
||||
it('getPreferredAeadAlgo() - one key - OCB', async function() {
|
||||
const key1 = openpgp.key.readArmored(twoKeys).keys[0];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredAeadAlgorithms = [2,1];
|
||||
const prefAlgo = await openpgp.key.getPreferredAeadAlgo([key1]);
|
||||
expect(prefAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it('getPreferredAeadAlgo() - two key - one without pref', async function() {
|
||||
const keys = openpgp.key.readArmored(twoKeys).keys;
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredAeadAlgorithms = [2,1];
|
||||
const prefAlgo = await openpgp.key.getPreferredAeadAlgo([key1, key2]);
|
||||
expect(prefAlgo).to.equal(openpgp.config.aead_mode);
|
||||
});
|
||||
|
||||
it('Preferences of generated key', function() {
|
||||
const testPref = function(key) {
|
||||
// key flags
|
||||
|
@ -1202,6 +1220,10 @@ p92yZgB3r2+f6/GIe2+7
|
|||
expect(key.subKeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
|
||||
const sym = openpgp.enums.symmetric;
|
||||
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes128, sym.aes192, sym.cast5, sym.tripledes]);
|
||||
if (openpgp.config.aead_protect === 'draft04') {
|
||||
const aead = openpgp.enums.aead;
|
||||
expect(key.users[0].selfCertifications[0].preferredAeadAlgorithms).to.eql([aead.eax, aead.ocb]);
|
||||
}
|
||||
const hash = openpgp.enums.hash;
|
||||
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha512, hash.sha1]);
|
||||
const compr = openpgp.enums.compression;
|
||||
|
|
Loading…
Reference in New Issue
Block a user