Merge pull request #683 from openpgpjs/generate_key_refactor
More generateKey options
This commit is contained in:
commit
2de426e0bc
225
src/key.js
225
src/key.js
|
@ -291,7 +291,6 @@ Key.prototype.getSigningKeyPacket = async function (keyId=null, date=new Date())
|
|||
};
|
||||
|
||||
function isValidEncryptionKeyPacket(keyPacket, signature, date=new Date()) {
|
||||
const normDate = util.normalizeDate(date);
|
||||
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) &&
|
||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) &&
|
||||
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) &&
|
||||
|
@ -342,18 +341,24 @@ Key.prototype.getEncryptionKeyPacket = async function(keyId, date=new Date()) {
|
|||
|
||||
/**
|
||||
* Encrypts all secret key and subkey packets matching keyId
|
||||
* @param {String|Array<String>} passphrases - if multiple passphrases, then should be in same order as packets each should encrypt
|
||||
* @param {module:type/keyid} keyId
|
||||
* @param {String} passphrase
|
||||
* @returns {Promise<Array<module:packet.SecretKey|module:packet.SecretSubkey>>}
|
||||
* @async
|
||||
*/
|
||||
Key.prototype.encrypt = async function(passphrase, keyId=null) {
|
||||
Key.prototype.encrypt = async function(passphrases, keyId=null) {
|
||||
if (!this.isPrivate()) {
|
||||
throw new Error("Nothing to encrypt in a public key");
|
||||
}
|
||||
|
||||
return Promise.all(this.getKeyPackets(keyId).map(async function(keyPacket) {
|
||||
await keyPacket.encrypt(passphrase);
|
||||
const keyPackets = this.getKeyPackets(keyId);
|
||||
passphrases = util.isArray(passphrases) ? passphrases : new Array(keyPackets.length).fill(passphrases);
|
||||
if (passphrases.length !== keyPackets.length) {
|
||||
throw new Error("Invalid number of passphrases for key");
|
||||
}
|
||||
|
||||
return Promise.all(keyPackets.map(async function(keyPacket, i) {
|
||||
await keyPacket.encrypt(passphrases[i]);
|
||||
await keyPacket.clearPrivateParams();
|
||||
return keyPacket;
|
||||
}));
|
||||
|
@ -361,18 +366,32 @@ Key.prototype.encrypt = async function(passphrase, keyId=null) {
|
|||
|
||||
/**
|
||||
* Decrypts all secret key and subkey packets matching keyId
|
||||
* @param {String} passphrase
|
||||
* @param {String|Array<String>} passphrases
|
||||
* @param {module:type/keyid} keyId
|
||||
* @returns {Promise<Boolean>} true if all matching key and subkey packets decrypted successfully
|
||||
* @async
|
||||
*/
|
||||
Key.prototype.decrypt = async function(passphrase, keyId=null) {
|
||||
Key.prototype.decrypt = async function(passphrases, keyId=null) {
|
||||
if (!this.isPrivate()) {
|
||||
throw new Error("Nothing to decrypt in a public key");
|
||||
}
|
||||
passphrases = util.isArray(passphrases) ? passphrases : [passphrases];
|
||||
|
||||
const results = await Promise.all(this.getKeyPackets(keyId).map(async function(keyPacket) {
|
||||
return keyPacket.decrypt(passphrase);
|
||||
let decrypted = false;
|
||||
let error = null;
|
||||
await Promise.all(passphrases.map(async function(passphrase) {
|
||||
try {
|
||||
await keyPacket.decrypt(passphrase);
|
||||
decrypted = true;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}));
|
||||
if (!decrypted) {
|
||||
throw error;
|
||||
}
|
||||
return decrypted;
|
||||
}));
|
||||
return results.every(result => result === true);
|
||||
};
|
||||
|
@ -1100,18 +1119,34 @@ export function readArmored(armoredText) {
|
|||
* Assumes already in form of "User Name <username@email.com>"
|
||||
* If array is used, the first userId is set as primary user Id
|
||||
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
|
||||
* @param {Number} [options.keyExpirationTime=0]
|
||||
* The number of seconds after the key creation time that the key expires
|
||||
* @param {String} curve (optional) elliptic curve for ECC keys
|
||||
* @param {Date} date Override the creation date of the key and the key signatures
|
||||
* @param {Array<Object>} subkeys (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
|
||||
* sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
|
||||
* @returns {Promise<module:key.Key>}
|
||||
* @async
|
||||
* @static
|
||||
*/
|
||||
export function generate(options) {
|
||||
let secretKeyPacket;
|
||||
let secretSubkeyPacket;
|
||||
return Promise.resolve().then(() => {
|
||||
export async function generate(options) {
|
||||
options.sign = true; // primary key is always a signing key
|
||||
options = sanitizeKeyOptions(options);
|
||||
options.subkeys = options.subkeys.map(function(subkey, index) { return sanitizeKeyOptions(options.subkeys[index], options); });
|
||||
|
||||
let promises = [generateSecretKey(options)];
|
||||
promises = promises.concat(options.subkeys.map(generateSecretSubkey));
|
||||
return Promise.all(promises).then(packets => wrapKeyObject(packets[0], packets.slice(1), options));
|
||||
|
||||
function sanitizeKeyOptions(options, subkeyDefaults={}) {
|
||||
options.curve = options.curve || subkeyDefaults.curve;
|
||||
options.numBits = options.numBits || subkeyDefaults.numBits;
|
||||
options.keyExpirationTime = options.keyExpirationTime !== undefined ? options.keyExpirationTime : subkeyDefaults.keyExpirationTime;
|
||||
options.passphrase = util.isString(options.passphrase) ? options.passphrase : subkeyDefaults.passphrase;
|
||||
options.date = options.date || subkeyDefaults.date;
|
||||
|
||||
options.sign = options.sign || false;
|
||||
|
||||
if (options.curve) {
|
||||
try {
|
||||
options.curve = enums.write(enums.curve, options.curve);
|
||||
|
@ -1119,81 +1154,64 @@ export function generate(options) {
|
|||
throw new Error('Not valid curve.');
|
||||
}
|
||||
if (options.curve === enums.curve.ed25519 || options.curve === enums.curve.curve25519) {
|
||||
options.keyType = options.keyType || enums.publicKey.eddsa;
|
||||
if (options.sign) {
|
||||
options.algorithm = enums.publicKey.eddsa;
|
||||
options.curve = enums.curve.ed25519;
|
||||
} else {
|
||||
options.algorithm = enums.publicKey.ecdh;
|
||||
options.curve = enums.curve.curve25519;
|
||||
}
|
||||
} else {
|
||||
options.keyType = options.keyType || enums.publicKey.ecdsa;
|
||||
if (options.sign) {
|
||||
options.algorithm = enums.publicKey.ecdsa;
|
||||
} else {
|
||||
options.algorithm = enums.publicKey.ecdh;
|
||||
}
|
||||
}
|
||||
options.subkeyType = options.subkeyType || enums.publicKey.ecdh;
|
||||
} else if (options.numBits) {
|
||||
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
||||
options.subkeyType = options.subkeyType || enums.publicKey.rsa_encrypt_sign;
|
||||
options.algorithm = enums.publicKey.rsa_encrypt_sign;
|
||||
} else {
|
||||
throw new Error('Key type not specified.');
|
||||
throw new Error('Unrecognized key type');
|
||||
}
|
||||
|
||||
if (options.keyType !== enums.publicKey.rsa_encrypt_sign &&
|
||||
options.keyType !== enums.publicKey.ecdsa &&
|
||||
options.keyType !== enums.publicKey.eddsa) {
|
||||
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||
throw new Error('Unsupported key type');
|
||||
}
|
||||
|
||||
if (options.subkeyType !== enums.publicKey.rsa_encrypt_sign &&
|
||||
options.subkeyType !== enums.publicKey.ecdh) {
|
||||
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||
throw new Error('Unsupported subkey type');
|
||||
}
|
||||
|
||||
if (!options.passphrase) { // Key without passphrase is unlocked by definition
|
||||
options.unlocked = true;
|
||||
}
|
||||
if (util.isString(options.userIds)) {
|
||||
options.userIds = [options.userIds];
|
||||
}
|
||||
|
||||
return Promise.all([generateSecretKey(), generateSecretSubkey()]).then(() => wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options));
|
||||
});
|
||||
|
||||
function generateSecretKey() {
|
||||
secretKeyPacket = new packet.SecretKey(options.date);
|
||||
secretKeyPacket.packets = null;
|
||||
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
|
||||
options.curve = options.curve === enums.curve.curve25519 ? enums.curve.ed25519 : options.curve;
|
||||
return secretKeyPacket.generate(options.numBits, options.curve);
|
||||
return options;
|
||||
}
|
||||
|
||||
function generateSecretSubkey() {
|
||||
secretSubkeyPacket = new packet.SecretSubkey(options.date);
|
||||
async function generateSecretKey(options) {
|
||||
const secretKeyPacket = new packet.SecretKey(options.date);
|
||||
secretKeyPacket.packets = null;
|
||||
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.subkeyType);
|
||||
options.curve = options.curve === enums.curve.ed25519 ? enums.curve.curve25519 : options.curve;
|
||||
return secretSubkeyPacket.generate(options.numBits, options.curve);
|
||||
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.algorithm);
|
||||
await secretKeyPacket.generate(options.numBits, options.curve);
|
||||
return secretKeyPacket;
|
||||
}
|
||||
|
||||
async function generateSecretSubkey(options) {
|
||||
const secretSubkeyPacket = new packet.SecretSubkey(options.date);
|
||||
secretSubkeyPacket.packets = null;
|
||||
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.algorithm);
|
||||
await secretSubkeyPacket.generate(options.numBits, options.curve);
|
||||
return secretSubkeyPacket;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformats and signs an OpenPGP with a given User ID. Currently only supports RSA keys.
|
||||
* Reformats and signs an OpenPGP key with a given User ID. Currently only supports RSA keys.
|
||||
* @param {module:key.Key} options.privateKey The private key to reformat
|
||||
* @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign]
|
||||
* @param {String|Array<String>} options.userIds
|
||||
* Assumes already in form of "User Name <username@email.com>"
|
||||
* If array is used, the first userId is set as primary user Id
|
||||
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
|
||||
* @param {Number} [options.keyExpirationTime=0]
|
||||
* The number of seconds after the key creation time that the key expires
|
||||
* @param {Date} date Override the creation date of the key and the key signatures
|
||||
* @param {Array<Object>} subkeys (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
|
||||
*
|
||||
* @returns {Promise<module:key.Key>}
|
||||
* @async
|
||||
* @static
|
||||
*/
|
||||
export async function reformat(options) {
|
||||
let secretKeyPacket;
|
||||
let secretSubkeyPacket;
|
||||
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
||||
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) {
|
||||
throw new Error('Only RSA Encrypt or Sign supported');
|
||||
}
|
||||
options = sanitizeKeyOptions(options);
|
||||
|
||||
try {
|
||||
const isDecrypted = options.privateKey.getKeyPackets().every(keyPacket => keyPacket.isDecrypted);
|
||||
|
@ -1204,37 +1222,54 @@ export async function reformat(options) {
|
|||
throw new Error('Key not decrypted');
|
||||
}
|
||||
|
||||
if (!options.passphrase) { // Key without passphrase is unlocked by definition
|
||||
options.unlocked = true;
|
||||
}
|
||||
if (util.isString(options.userIds)) {
|
||||
options.userIds = [options.userIds];
|
||||
}
|
||||
const packetlist = options.privateKey.toPacketlist();
|
||||
let secretKeyPacket;
|
||||
const secretSubkeyPackets = [];
|
||||
for (let i = 0; i < packetlist.length; i++) {
|
||||
if (packetlist[i].tag === enums.packet.secretKey) {
|
||||
secretKeyPacket = packetlist[i];
|
||||
options.keyType = secretKeyPacket.algorithm;
|
||||
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
|
||||
secretSubkeyPacket = packetlist[i];
|
||||
options.subkeyType = secretSubkeyPacket.algorithm;
|
||||
secretSubkeyPackets.push(packetlist[i]);
|
||||
}
|
||||
}
|
||||
if (!secretKeyPacket) {
|
||||
throw new Error('Key does not contain a secret key packet');
|
||||
}
|
||||
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
|
||||
|
||||
if (!options.subkeys) {
|
||||
options.subkeys = secretSubkeyPackets.map(() => ({}));
|
||||
}
|
||||
|
||||
if (options.subkeys.length !== secretSubkeyPackets.length) {
|
||||
throw new Error('Number of subkey options does not match number of subkeys');
|
||||
}
|
||||
|
||||
options.subkeys = options.subkeys.map(function(subkey, index) { return sanitizeKeyOptions(options.subkeys[index], options); });
|
||||
|
||||
return wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options);
|
||||
|
||||
function sanitizeKeyOptions(options, subkeyDefaults={}) {
|
||||
options.keyExpirationTime = options.keyExpirationTime || subkeyDefaults.keyExpirationTime;
|
||||
options.passphrase = util.isString(options.passphrase) ? options.passphrase : subkeyDefaults.passphrase;
|
||||
options.date = options.date || subkeyDefaults.date;
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
|
||||
async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
|
||||
// set passphrase protection
|
||||
if (options.passphrase) {
|
||||
await secretKeyPacket.encrypt(options.passphrase);
|
||||
if (secretSubkeyPacket) {
|
||||
await secretSubkeyPacket.encrypt(options.passphrase);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(secretSubkeyPackets.map(async function(secretSubkeyPacket, index) {
|
||||
const subkeyPassphrase = options.subkeys[index].passphrase;
|
||||
if (subkeyPassphrase) {
|
||||
await secretSubkeyPacket.encrypt(subkeyPassphrase);
|
||||
}
|
||||
}));
|
||||
|
||||
const packetlist = new packet.List();
|
||||
|
||||
packetlist.push(secretKeyPacket);
|
||||
|
@ -1248,7 +1283,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
|
|||
dataToSign.key = secretKeyPacket;
|
||||
const signaturePacket = new packet.Signature(options.date);
|
||||
signaturePacket.signatureType = enums.signature.cert_generic;
|
||||
signaturePacket.publicKeyAlgorithm = options.keyType;
|
||||
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(secretKeyPacket);
|
||||
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
||||
signaturePacket.preferredSymmetricAlgorithms = [];
|
||||
|
@ -1287,31 +1322,41 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
|
|||
});
|
||||
});
|
||||
|
||||
if (secretSubkeyPacket) {
|
||||
await Promise.all(secretSubkeyPackets.map(async function(secretSubkeyPacket, index) {
|
||||
const subkeyOptions = options.subkeys[index];
|
||||
const dataToSign = {};
|
||||
dataToSign.key = secretKeyPacket;
|
||||
dataToSign.bind = secretSubkeyPacket;
|
||||
const subkeySignaturePacket = new packet.Signature(options.date);
|
||||
const subkeySignaturePacket = new packet.Signature(subkeyOptions.date);
|
||||
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
|
||||
subkeySignaturePacket.publicKeyAlgorithm = options.keyType;
|
||||
subkeySignaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
||||
subkeySignaturePacket.hashAlgorithm = await getPreferredHashAlgo(secretSubkeyPacket);
|
||||
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
|
||||
if (options.keyExpirationTime > 0) {
|
||||
subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime;
|
||||
subkeySignaturePacket.keyFlags = subkeyOptions.sign ? enums.keyFlags.sign_data : [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
|
||||
if (subkeyOptions.keyExpirationTime > 0) {
|
||||
subkeySignaturePacket.keyExpirationTime = subkeyOptions.keyExpirationTime;
|
||||
subkeySignaturePacket.keyNeverExpires = false;
|
||||
}
|
||||
await subkeySignaturePacket.sign(secretKeyPacket, dataToSign);
|
||||
|
||||
packetlist.push(secretSubkeyPacket);
|
||||
packetlist.push(subkeySignaturePacket);
|
||||
return { secretSubkeyPacket, subkeySignaturePacket};
|
||||
})).then(packets => {
|
||||
packets.forEach(({ secretSubkeyPacket, subkeySignaturePacket }) => {
|
||||
packetlist.push(secretSubkeyPacket);
|
||||
packetlist.push(subkeySignaturePacket);
|
||||
});
|
||||
});
|
||||
|
||||
// set passphrase protection
|
||||
if (options.passphrase) {
|
||||
secretKeyPacket.clearPrivateParams();
|
||||
}
|
||||
|
||||
if (!options.unlocked) {
|
||||
secretKeyPacket.clearPrivateParams();
|
||||
if (secretSubkeyPacket) {
|
||||
await Promise.all(secretSubkeyPackets.map(async function(secretSubkeyPacket, index) {
|
||||
const subkeyPassphrase = options.subkeys[index].passphrase;
|
||||
if (subkeyPassphrase) {
|
||||
secretSubkeyPacket.clearPrivateParams();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return new Key(packetlist);
|
||||
}
|
||||
|
|
|
@ -99,26 +99,22 @@ export function destroyWorker() {
|
|||
* @param {Array<Object>} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }]
|
||||
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
|
||||
* @param {Number} numBits (optional) number of bits for RSA keys: 2048 or 4096.
|
||||
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
|
||||
* @param {String} curve (optional) elliptic curve for ECC keys:
|
||||
* curve25519, p256, p384, p521, secp256k1,
|
||||
* brainpoolP256r1, brainpoolP384r1, or brainpoolP512r1.
|
||||
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
|
||||
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
|
||||
* @param {Date} date (optional) override the creation date of the key and the key signatures
|
||||
* @param {Array<Object>} subkeys (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
|
||||
* sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
|
||||
* @returns {Promise<Object>} The generated key object in the form:
|
||||
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
|
||||
* @async
|
||||
* @static
|
||||
*/
|
||||
|
||||
export function generateKey({
|
||||
userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve="", date=new Date()
|
||||
} = {}) {
|
||||
export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpirationTime=0, curve="", date=new Date(), subkeys=[{}] }) {
|
||||
userIds = formatUserIds(userIds);
|
||||
const options = {
|
||||
userIds, passphrase, numBits, unlocked, keyExpirationTime, curve, date
|
||||
};
|
||||
|
||||
const options = { userIds, passphrase, numBits, keyExpirationTime, curve, date, subkeys };
|
||||
if (util.getWebCryptoAll() && numBits < 2048) {
|
||||
throw new Error('numBits should be 2048 or 4096, found: ' + numBits);
|
||||
}
|
||||
|
@ -141,22 +137,15 @@ export function generateKey({
|
|||
* @param {Key} privateKey private key to reformat
|
||||
* @param {Array<Object>} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }]
|
||||
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
|
||||
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
|
||||
* @returns {Promise<Object>} The generated key object in the form:
|
||||
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
|
||||
* @async
|
||||
* @static
|
||||
*/
|
||||
export function reformatKey({
|
||||
privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0
|
||||
} = {}) {
|
||||
export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date}) {
|
||||
userIds = formatUserIds(userIds);
|
||||
|
||||
const options = {
|
||||
privateKey, userIds, passphrase, unlocked, keyExpirationTime
|
||||
};
|
||||
|
||||
const options = { privateKey, userIds, passphrase, keyExpirationTime, date};
|
||||
if (asyncProxy) {
|
||||
return asyncProxy.delegate('reformatKey', options);
|
||||
}
|
||||
|
@ -172,9 +161,9 @@ export function reformatKey({
|
|||
|
||||
/**
|
||||
* Unlock a private key with your passphrase.
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String} passphrase the user's passphrase chosen during key generation
|
||||
* @returns {Promise<Object>} the unlocked key object in the form: { key:Key }
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String|Array<String>} passphrase the user's passphrase(s) chosen during key generation
|
||||
* @returns {Promise<Object>} the unlocked key object in the form: { key:Key }
|
||||
* @async
|
||||
*/
|
||||
export function decryptKey({ privateKey, passphrase }) {
|
||||
|
@ -193,9 +182,9 @@ export function decryptKey({ privateKey, passphrase }) {
|
|||
|
||||
/**
|
||||
* Lock a private key with your passphrase.
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String} passphrase the user's passphrase chosen during key generation
|
||||
* @returns {Promise<Object>} the locked key object in the form: { key:Key }
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String|Array<String>} passphrase the user's passphrase(s) chosen during key generation
|
||||
* @returns {Promise<Object>} the locked key object in the form: { key:Key }
|
||||
* @async
|
||||
*/
|
||||
export function encryptKey({ privateKey, passphrase }) {
|
||||
|
|
|
@ -1081,7 +1081,6 @@ describe('Key', function() {
|
|||
const opt = {
|
||||
userIds: { name: 'Test User', email: 'text@example.com' },
|
||||
passphrase: 'secret',
|
||||
unlocked: true,
|
||||
date: past
|
||||
};
|
||||
|
||||
|
@ -1108,6 +1107,47 @@ describe('Key', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Generate key - two subkeys with default values', function() {
|
||||
const userId = 'test <a@b.com>';
|
||||
const opt = {curve: 'curve25519', userIds: [userId], passphrase: '123', subkeys:[{},{}]};
|
||||
return openpgp.generateKey(opt).then(function(key) {
|
||||
key = key.key;
|
||||
expect(key.users.length).to.equal(1);
|
||||
expect(key.users[0].userId.userid).to.equal(userId);
|
||||
expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;
|
||||
expect(key.subKeys).to.have.lengthOf(2);
|
||||
expect(key.subKeys[0].subKey.algorithm).to.equal('ecdh');
|
||||
expect(key.subKeys[1].subKey.algorithm).to.equal('ecdh');
|
||||
});
|
||||
});
|
||||
|
||||
it('Generate key - one signing subkey', function() {
|
||||
const userId = 'test <a@b.com>';
|
||||
const opt = {curve: 'curve25519', userIds: [userId], passphrase: '123', subkeys:[{}, {sign: true}]};
|
||||
return openpgp.generateKey(opt).then(function(key) {
|
||||
key = key.key;
|
||||
expect(key.users.length).to.equal(1);
|
||||
expect(key.users[0].userId.userid).to.equal(userId);
|
||||
expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;
|
||||
expect(key.subKeys).to.have.lengthOf(2);
|
||||
expect(key.subKeys[0].subKey.algorithm).to.equal('ecdh');
|
||||
expect(key.subKeys[1].subKey.algorithm).to.equal('eddsa');
|
||||
});
|
||||
});
|
||||
|
||||
it('Generate key - override main key options for subkey', function() {
|
||||
const userId = 'test <a@b.com>';
|
||||
const opt = {numBits: 2048, userIds: [userId], passphrase: '123', subkeys:[{curve: 'curve25519'}]};
|
||||
return openpgp.generateKey(opt).then(function(key) {
|
||||
key = key.key;
|
||||
expect(key.users.length).to.equal(1);
|
||||
expect(key.users[0].userId.userid).to.equal(userId);
|
||||
expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true;
|
||||
expect(key.primaryKey.algorithm).to.equal('rsa_encrypt_sign');
|
||||
expect(key.subKeys[0].subKey.algorithm).to.equal('ecdh');
|
||||
});
|
||||
});
|
||||
|
||||
it('Encrypt key with new passphrase', async function() {
|
||||
const userId = 'test <a@b.com>';
|
||||
const opt = {numBits: 512, userIds: userId, passphrase: 'passphrase'};
|
||||
|
@ -1261,6 +1301,28 @@ describe('Key', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Reformat key with two subkeys with passphrase', function() {
|
||||
const userId1 = 'test <a@b.com>';
|
||||
const userId2 = 'test <b@c.com>';
|
||||
const now = openpgp.util.normalizeDate(new Date());
|
||||
const before = openpgp.util.normalizeDate(new Date(0));
|
||||
const opt1 = {curve: 'curve25519', userIds: [userId1], date: now};
|
||||
return openpgp.generateKey(opt1).then(function(newKey) {
|
||||
newKey = newKey.key;
|
||||
expect(newKey.users[0].userId.userid).to.equal(userId1);
|
||||
expect(+newKey.primaryKey.created).to.equal(+now);
|
||||
expect(+newKey.subKeys[0].subKey.created).to.equal(+now);
|
||||
expect(+newKey.subKeys[0].bindingSignatures[0].created).to.equal(+now);
|
||||
const opt2 = {privateKey: newKey, userIds: [userId2], date: before};
|
||||
return openpgp.reformatKey(opt2).then(function(refKey) {
|
||||
refKey = refKey.key;
|
||||
expect(refKey.users.length).to.equal(1);
|
||||
expect(refKey.users[0].userId.userid).to.equal(userId2);
|
||||
expect(+refKey.subKeys[0].bindingSignatures[0].created).to.equal(+before);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Reformat key with no subkey without passphrase', function() {
|
||||
const userId = 'test1 <a@b.com>';
|
||||
const keys = openpgp.key.readArmored(key_without_subkey).keys;
|
||||
|
|
|
@ -483,22 +483,22 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
|
||||
it('should have default params set', function() {
|
||||
const now = new Date();
|
||||
const now = openpgp.util.normalizeDate(new Date());
|
||||
const opt = {
|
||||
userIds: { name: 'Test User', email: 'text@example.com' },
|
||||
passphrase: 'secret',
|
||||
unlocked: true,
|
||||
date: now
|
||||
date: now,
|
||||
subkeys: []
|
||||
};
|
||||
return openpgp.generateKey(opt).then(function(newKey) {
|
||||
expect(keyGenStub.withArgs({
|
||||
userIds: ['Test User <text@example.com>'],
|
||||
passphrase: 'secret',
|
||||
numBits: 2048,
|
||||
unlocked: true,
|
||||
keyExpirationTime: 0,
|
||||
curve: "",
|
||||
date: now
|
||||
date: now,
|
||||
subkeys: [],
|
||||
}).calledOnce).to.be.true;
|
||||
expect(newKey.key).to.exist;
|
||||
expect(newKey.privateKeyArmored).to.exist;
|
||||
|
@ -506,23 +506,6 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should work for no params', function() {
|
||||
const now = new Date();
|
||||
|
||||
return openpgp.generateKey({date: now}).then(function(newKey) {
|
||||
expect(keyGenStub.withArgs({
|
||||
userIds: [],
|
||||
passphrase: undefined,
|
||||
numBits: 2048,
|
||||
unlocked: false,
|
||||
keyExpirationTime: 0,
|
||||
curve: "",
|
||||
date: now
|
||||
}).calledOnce).to.be.true;
|
||||
expect(newKey.key).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to async proxy', function() {
|
||||
const workerStub = {
|
||||
postMessage: function() {}
|
||||
|
@ -533,7 +516,12 @@ describe('OpenPGP.js public api tests', function() {
|
|||
const proxyGenStub = stub(openpgp.getWorker(), 'delegate');
|
||||
getWebCryptoAllStub.returns();
|
||||
|
||||
openpgp.generateKey();
|
||||
const opt = {
|
||||
userIds: { name: 'Test User', email: 'text@example.com' },
|
||||
passphrase: 'secret',
|
||||
subkeys: []
|
||||
};
|
||||
openpgp.generateKey(opt);
|
||||
expect(proxyGenStub.calledOnce).to.be.true;
|
||||
expect(keyGenStub.calledOnce).to.be.false;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user