Add openpgp.generateSessionKey

This commit is contained in:
Daniel Huigens 2020-02-18 16:56:55 +01:00
parent a9a9a90ae2
commit be7b174df4
3 changed files with 61 additions and 32 deletions

View File

@ -20,7 +20,7 @@ export default openpgp;
export { export {
encrypt, decrypt, sign, verify, encrypt, decrypt, sign, verify,
generateKey, reformatKey, revokeKey, decryptKey, generateKey, reformatKey, revokeKey, decryptKey,
encryptSessionKey, decryptSessionKeys, generateSessionKey, encryptSessionKey, decryptSessionKeys,
initWorker, getWorker, destroyWorker initWorker, getWorker, destroyWorker
} from './openpgp'; } from './openpgp';

View File

@ -276,6 +276,23 @@ Message.prototype.getText = function() {
return null; return null;
}; };
/**
* Generate a new session key object, taking the algorithm preferences of the passed public keys into account, if any.
* @param {Array<Key>} keys (optional) public key(s) to select algorithm preferences for
* @param {Date} date (optional) date to select algorithm preferences at
* @param {Array} userIds (optional) user IDs to select algorithm preferences for
* @returns {Promise<{ data: Uint8Array, algorithm: String }>} object with session key data and algorithm
* @async
*/
export async function generateSessionKey(keys = [], date = new Date(), userIds = []) {
const algorithm = enums.read(enums.symmetric, await getPreferredAlgo('symmetric', keys, date, userIds));
const aeadAlgorithm = config.aead_protect && await isAeadSupported(keys, date, userIds) ?
enums.read(enums.aead, await getPreferredAlgo('aead', keys, date, userIds)) :
undefined;
const sessionKeyData = await crypto.generateSessionKey(algorithm);
return { data: sessionKeyData, algorithm, aeadAlgorithm };
}
/** /**
* Encrypt the message either with public keys, passwords, or both at once. * 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<Key>} keys (optional) public key(s) for message encryption
@ -289,38 +306,26 @@ Message.prototype.getText = function() {
* @async * @async
*/ */
Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard = false, date = new Date(), userIds = [], streaming) { Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard = false, date = new Date(), userIds = [], streaming) {
let symAlgo;
let aeadAlgo;
let symEncryptedPacket;
if (sessionKey) { if (sessionKey) {
if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) { if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) {
throw new Error('Invalid session key for encryption.'); throw new Error('Invalid session key for encryption.');
} }
symAlgo = sessionKey.algorithm;
aeadAlgo = sessionKey.aeadAlgorithm;
sessionKey = sessionKey.data;
} else if (keys && keys.length) { } else if (keys && keys.length) {
symAlgo = enums.read(enums.symmetric, await getPreferredAlgo('symmetric', keys, date, userIds)); sessionKey = await generateSessionKey(keys, date, userIds);
if (config.aead_protect && await isAeadSupported(keys, date, userIds)) {
aeadAlgo = enums.read(enums.aead, await getPreferredAlgo('aead', keys, date, userIds));
}
} else if (passwords && passwords.length) { } else if (passwords && passwords.length) {
symAlgo = enums.read(enums.symmetric, config.encryption_cipher); sessionKey = await generateSessionKey();
aeadAlgo = enums.read(enums.aead, config.aead_mode);
} else { } else {
throw new Error('No keys, passwords, or session key provided.'); throw new Error('No keys, passwords, or session key provided.');
} }
if (!sessionKey) { const { data: sessionKeyData, algorithm, aeadAlgorithm } = sessionKey;
sessionKey = await crypto.generateSessionKey(symAlgo);
}
const msg = await encryptSessionKey(sessionKey, symAlgo, aeadAlgo, keys, passwords, wildcard, date, userIds); const msg = await encryptSessionKey(sessionKeyData, algorithm, aeadAlgorithm, keys, passwords, wildcard, date, userIds);
if (config.aead_protect && aeadAlgo) { let symEncryptedPacket;
if (aeadAlgorithm) {
symEncryptedPacket = new packet.SymEncryptedAEADProtected(); symEncryptedPacket = new packet.SymEncryptedAEADProtected();
symEncryptedPacket.aeadAlgorithm = aeadAlgo; symEncryptedPacket.aeadAlgorithm = aeadAlgorithm;
} else if (config.integrity_protect) { } else if (config.integrity_protect) {
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected(); symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
} else { } else {
@ -328,7 +333,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
} }
symEncryptedPacket.packets = this.packets; symEncryptedPacket.packets = this.packets;
await symEncryptedPacket.encrypt(symAlgo, sessionKey, streaming); await symEncryptedPacket.encrypt(algorithm, sessionKeyData, streaming);
msg.packets.push(symEncryptedPacket); msg.packets.push(symEncryptedPacket);
symEncryptedPacket.packets = new packet.List(); // remove packets after encryption symEncryptedPacket.packets = new packet.List(); // remove packets after encryption
@ -345,8 +350,8 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
/** /**
* Encrypt a session key either with public keys, passwords, or both at once. * Encrypt a session key either with public keys, passwords, or both at once.
* @param {Uint8Array} sessionKey session key for encryption * @param {Uint8Array} sessionKey session key for encryption
* @param {String} symAlgo session key algorithm * @param {String} algorithm session key algorithm
* @param {String} aeadAlgo (optional) aead algorithm, e.g. 'eax' or 'ocb' * @param {String} aeadAlgorithm (optional) aead algorithm, e.g. 'eax' or 'ocb'
* @param {Array<Key>} publicKeys (optional) public key(s) for message encryption * @param {Array<Key>} publicKeys (optional) public key(s) for message encryption
* @param {Array<String>} passwords (optional) 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 * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
@ -355,7 +360,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
* @returns {Promise<Message>} new message with encrypted content * @returns {Promise<Message>} new message with encrypted content
* @async * @async
*/ */
export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKeys, passwords, wildcard = false, date = new Date(), userIds = []) { export async function encryptSessionKey(sessionKey, algorithm, aeadAlgorithm, publicKeys, passwords, wildcard = false, date = new Date(), userIds = []) {
const packetlist = new packet.List(); const packetlist = new packet.List();
if (publicKeys) { if (publicKeys) {
@ -365,7 +370,7 @@ export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKey
pkESKeyPacket.publicKeyId = wildcard ? type_keyid.wildcard() : encryptionKey.getKeyId(); pkESKeyPacket.publicKeyId = wildcard ? type_keyid.wildcard() : encryptionKey.getKeyId();
pkESKeyPacket.publicKeyAlgorithm = encryptionKey.keyPacket.algorithm; pkESKeyPacket.publicKeyAlgorithm = encryptionKey.keyPacket.algorithm;
pkESKeyPacket.sessionKey = sessionKey; pkESKeyPacket.sessionKey = sessionKey;
pkESKeyPacket.sessionKeyAlgorithm = symAlgo; pkESKeyPacket.sessionKeyAlgorithm = algorithm;
await pkESKeyPacket.encrypt(encryptionKey.keyPacket); await pkESKeyPacket.encrypt(encryptionKey.keyPacket);
delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption
return pkESKeyPacket; return pkESKeyPacket;
@ -384,19 +389,19 @@ export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKey
const sum = (accumulator, currentValue) => accumulator + currentValue; const sum = (accumulator, currentValue) => accumulator + currentValue;
const encryptPassword = async function(sessionKey, symAlgo, aeadAlgo, password) { const encryptPassword = async function(sessionKey, algorithm, aeadAlgorithm, password) {
const symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey(); const symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey();
symEncryptedSessionKeyPacket.sessionKey = sessionKey; symEncryptedSessionKeyPacket.sessionKey = sessionKey;
symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo; symEncryptedSessionKeyPacket.sessionKeyAlgorithm = algorithm;
if (aeadAlgo) { if (aeadAlgorithm) {
symEncryptedSessionKeyPacket.aeadAlgorithm = aeadAlgo; symEncryptedSessionKeyPacket.aeadAlgorithm = aeadAlgorithm;
} }
await symEncryptedSessionKeyPacket.encrypt(password); await symEncryptedSessionKeyPacket.encrypt(password);
if (config.password_collision_check) { if (config.password_collision_check) {
const results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd))); const results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd)));
if (results.reduce(sum) !== 1) { if (results.reduce(sum) !== 1) {
return encryptPassword(sessionKey, symAlgo, password); return encryptPassword(sessionKey, algorithm, password);
} }
} }
@ -404,7 +409,7 @@ export async function encryptSessionKey(sessionKey, symAlgo, aeadAlgo, publicKey
return symEncryptedSessionKeyPacket; return symEncryptedSessionKeyPacket;
}; };
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, aeadAlgo, pwd))); const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, algorithm, aeadAlgorithm, pwd)));
packetlist.concat(results); packetlist.concat(results);
} }

View File

@ -131,7 +131,6 @@ export async function destroyWorker() {
* @async * @async
* @static * @static
*/ */
export function generateKey({ userIds = [], passphrase = "", numBits = 2048, rsaBits = numBits, keyExpirationTime = 0, curve = "", date = new Date(), subkeys = [{}] }) { export function generateKey({ userIds = [], passphrase = "", numBits = 2048, rsaBits = numBits, keyExpirationTime = 0, curve = "", date = new Date(), subkeys = [{}] }) {
userIds = toArray(userIds); userIds = toArray(userIds);
const options = { userIds, passphrase, rsaBits, keyExpirationTime, curve, date, subkeys }; const options = { userIds, passphrase, rsaBits, keyExpirationTime, curve, date, subkeys };
@ -516,6 +515,31 @@ export function verify({ message, publicKeys, format = 'utf8', streaming = messa
// // // //
/////////////////////////////////////////////// ///////////////////////////////////////////////
/**
* Decrypt symmetric session keys with a private key or password. Either a private key or
* a password must be specified.
* @param {Message} message a message object containing the encrypted session key packets
* @param {Key|Array<Key>} privateKeys (optional) private keys with decrypted secret key data
* @param {String|Array<String>} passwords (optional) passwords to decrypt the session key
* @returns {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form:
* { data:Uint8Array, algorithm:String }
* or 'undefined' if no key packets found
* @async
* @static
*/
export function generateSessionKey({ publicKeys, date = new Date(), toUserIds = [] }) {
publicKeys = toArray(publicKeys); toUserIds = toArray(toUserIds);
if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('generateSessionKey', { publicKeys, date, toUserIds });
}
return Promise.resolve().then(async function() {
return messageLib.generateSessionKey(publicKeys, date, toUserIds);
}).catch(onError.bind(null, 'Error generating session key'));
}
/** /**
* Encrypt a symmetric session key with public keys, passwords, or both at once. At least either public keys * Encrypt a symmetric session key with public keys, passwords, or both at once. At least either public keys