Slightly simplifies key.js; adds key.verifyKeyPackets which should be run before getEncryption/SigningKeyPacket

This commit is contained in:
Mahrud Sayrafi 2018-03-04 07:00:44 -08:00 committed by Sanjana Rajan
parent 354b961b67
commit ec22dabac3
9 changed files with 373 additions and 367 deletions

View File

@ -53,6 +53,8 @@ export default {
* @property {Boolean} password_collision_check
*/
password_collision_check: false,
/** @property {Boolean} revocations_expire If true, expired revocation signatures are ignored */
revocations_expire: false,
/** @property {Boolean} use_native Use native Node.js crypto/zlib and WebCrypto APIs when available */
use_native: true,

View File

@ -234,13 +234,6 @@ const ECDSASignature = nodeCrypto ?
);
}) : undefined;
const ECParameters = nodeCrypto ?
asn1.define('ECParameters', function() {
this.choice({
namedCurve: this.objid()
});
}) : undefined;
const ECPrivateKey = nodeCrypto ?
asn1.define('ECPrivateKey', function() {
this.seq().obj(

View File

@ -45,12 +45,12 @@ export function Key(packetlist) {
}
// same data as in packetlist but in structured form
this.primaryKey = null;
this.revocationSignature = null;
this.directSignatures = null;
this.users = null;
this.subKeys = null;
this.revocationSignatures = [];
this.directSignatures = [];
this.users = [];
this.subKeys = [];
this.packetlist2structure(packetlist);
if (!this.primaryKey || !this.users) {
if (!this.primaryKey || !this.users.length) {
throw new Error('Invalid key: need at least key and user ID packet');
}
}
@ -73,17 +73,11 @@ Key.prototype.packetlist2structure = function(packetlist) {
case enums.packet.userid:
case enums.packet.userAttribute:
user = new User(packetlist[i]);
if (!this.users) {
this.users = [];
}
this.users.push(user);
break;
case enums.packet.publicSubkey:
case enums.packet.secretSubkey:
user = null;
if (!this.subKeys) {
this.subKeys = [];
}
subKey = new SubKey(packetlist[i]);
this.subKeys.push(subKey);
break;
@ -98,34 +92,19 @@ Key.prototype.packetlist2structure = function(packetlist) {
continue;
}
if (packetlist[i].issuerKeyId.equals(primaryKeyId)) {
if (!user.selfCertifications) {
user.selfCertifications = [];
}
user.selfCertifications.push(packetlist[i]);
} else {
if (!user.otherCertifications) {
user.otherCertifications = [];
}
user.otherCertifications.push(packetlist[i]);
}
break;
case enums.signature.cert_revocation:
if (user) {
if (!user.revocationCertifications) {
user.revocationCertifications = [];
}
user.revocationCertifications.push(packetlist[i]);
user.revocationSignatures.push(packetlist[i]);
} else {
if (!this.directSignatures) {
this.directSignatures = [];
}
this.directSignatures.push(packetlist[i]);
}
break;
case enums.signature.key:
if (!this.directSignatures) {
this.directSignatures = [];
}
this.directSignatures.push(packetlist[i]);
break;
case enums.signature.subkey_binding:
@ -136,14 +115,14 @@ Key.prototype.packetlist2structure = function(packetlist) {
subKey.bindingSignatures.push(packetlist[i]);
break;
case enums.signature.key_revocation:
this.revocationSignature = packetlist[i];
this.revocationSignatures.push(packetlist[i]);
break;
case enums.signature.subkey_revocation:
if (!subKey) {
util.print_debug('Dropping subkey revocation signature without preceding subkey packet');
continue;
}
subKey.revocationSignature = packetlist[i];
subKey.revocationSignatures.push(packetlist[i]);
break;
}
break;
@ -158,40 +137,42 @@ Key.prototype.packetlist2structure = function(packetlist) {
Key.prototype.toPacketlist = function() {
const packetlist = new packet.List();
packetlist.push(this.primaryKey);
packetlist.push(this.revocationSignature);
packetlist.concat(this.revocationSignatures);
packetlist.concat(this.directSignatures);
let i;
for (i = 0; i < this.users.length; i++) {
packetlist.concat(this.users[i].toPacketlist());
}
if (this.subKeys) {
for (i = 0; i < this.subKeys.length; i++) {
packetlist.concat(this.subKeys[i].toPacketlist());
}
}
this.users.map(user => packetlist.concat(user.toPacketlist()));
this.subKeys.map(subKey => packetlist.concat(subKey.toPacketlist()));
return packetlist;
};
/**
* Returns all the private and public subkey packets
* @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey)>}
* Returns packetlist containing all public or private subkey packets matching keyId;
* If keyId is not present, returns all subkey packets.
* @param {type/keyid} keyId
* @returns {module:packet/packetlist}
*/
Key.prototype.getSubkeyPackets = function() {
const subKeys = [];
if (this.subKeys) {
for (let i = 0; i < this.subKeys.length; i++) {
subKeys.push(this.subKeys[i].subKey);
Key.prototype.getSubkeyPackets = function(keyId=null) {
const packets = new packet.List();
this.subKeys.forEach(subKey => {
if (!keyId || subKey.subKey.getKeyId().equals(keyId)) {
packets.push(subKey.subKey);
}
}
return subKeys;
});
return packets;
};
/**
* Returns all the private and public key and subkey packets
* @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key)>}
* Returns a packetlist containing all public or private key packets matching keyId.
* If keyId is not present, returns all key packets starting with the primary key.
* @param {type/keyid} keyId
* @returns {module:packet/packetlist}
*/
Key.prototype.getAllKeyPackets = function() {
return [this.primaryKey].concat(this.getSubkeyPackets());
Key.prototype.getKeyPackets = function(keyId=null) {
const packets = new packet.List();
if (!keyId || this.primaryKey.getKeyId().equals(keyId)) {
packets.push(this.primaryKey);
}
packets.concat(this.getSubkeyPackets(keyId));
return packets;
};
/**
@ -199,32 +180,7 @@ Key.prototype.getAllKeyPackets = function() {
* @returns {Array<module:type/keyid>}
*/
Key.prototype.getKeyIds = function() {
const keyIds = [];
const keys = this.getAllKeyPackets();
for (let i = 0; i < keys.length; i++) {
keyIds.push(keys[i].getKeyId());
}
return keyIds;
};
/**
* Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID
* @param {type/keyid} keyId
* @returns {(module:packet/public_subkey|module:packet/public_key|
* module:packet/secret_subkey|module:packet/secret_key|null)}
*/
Key.prototype.getKeyPackets = function(packetKeyId) {
const keys = this.getAllKeyPackets();
if (packetKeyId.isWildcard()) {
return keys;
}
for (let i = 0; i < keys.length; i++) {
const keyId = keys[i].getKeyId();
if (keyId.equals(packetKeyId)) {
return [keys[i]];
}
}
return [];
return this.getKeyPackets().map(keyPacket => keyPacket.getKeyId());
};
/**
@ -297,11 +253,25 @@ Key.prototype.armor = function() {
return armor.encode(type, this.toPacketlist().write());
};
function isValidSigningKeyPacket(keyPacket, signature, date=new Date()) {
const normDate = util.normalizeDate(date);
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_encrypt) &&
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) &&
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) &&
(!signature.keyFlags ||
(signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0) &&
signature.verified && !signature.revoked && !signature.isExpired(normDate) &&
(normDate === null || (keyPacket.created <= normDate && normDate < getExpirationTime(keyPacket, signature, Infinity)));
}
/**
* Returns first key packet or key packet by given keyId that is available for signing or signature verification
* Returns first key packet or key packet by given keyId that is available for signing and verification
*
* NOTE: call verifyKeyPackets before calling this function.
* @param {module:type/keyid} keyId, optional
* @param {Date} date use the given date for verification instead of the current time
* @returns {(module:packet/secret_subkey|module:packet/secret_key|null)} key packet or null if no signing key has been found
* @returns {(module:packet/secret_subkey|
module:packet/secret_key|null)} key packet or null if no signing key has been found
*/
Key.prototype.getSigningKeyPacket = function (keyId=null, date=new Date()) {
const primaryUser = this.getPrimaryUser(date);
@ -309,7 +279,6 @@ Key.prototype.getSigningKeyPacket = function (keyId=null, date=new Date()) {
isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate, date)) {
return this.primaryKey;
}
if (this.subKeys) {
for (let i = 0; i < this.subKeys.length; i++) {
if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) {
for (let j = 0; j < this.subKeys[i].bindingSignatures.length; j++) {
@ -319,7 +288,7 @@ Key.prototype.getSigningKeyPacket = function (keyId=null, date=new Date()) {
}
}
}
}
// TODO throw descriptive error
return null;
};
@ -332,31 +301,24 @@ function isValidEncryptionKeyPacket(keyPacket, signature, date=new Date()) {
(!signature.keyFlags ||
(signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
(signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0) &&
(!signature.isExpired(normDate) &&
(normDate === null || (keyPacket.created <= normDate && normDate < getExpirationTime(keyPacket, signature, Infinity))));
}
function isValidSigningKeyPacket(keyPacket, signature, date=new Date()) {
const normDate = util.normalizeDate(date);
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_encrypt) &&
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) &&
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) &&
(!signature.keyFlags ||
(signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0) &&
(!signature.isExpired(normDate) &&
(normDate === null || (keyPacket.created <= normDate && normDate < getExpirationTime(keyPacket, signature, Infinity))));
signature.verified && !signature.revoked && !signature.isExpired(normDate) &&
(normDate === null || (keyPacket.created <= normDate && normDate < getExpirationTime(keyPacket, signature, Infinity)));
}
/**
* Returns first key packet or key packet by given keyId that is available for encryption or decryption
*
* NOTE: call verifyKeyPackets before calling this function.
* @param {module:type/keyid} keyId, optional
* @param {Date} date optional
* @returns {(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key|null)} key packet or null if no encryption key has been found
* @param {Date} date, optional
* @returns {(module:packet/public_subkey|
* module:packet/secret_subkey|
* module:packet/secret_key|
* module:packet/public_key|null)} key packet or null if no encryption key has been found
*/
Key.prototype.getEncryptionKeyPacket = function(keyId, date=new Date()) {
// V4: by convention subkeys are preferred for encryption service
// V3: keys MUST NOT have subkeys
if (this.subKeys) {
for (let i = 0; i < this.subKeys.length; i++) {
if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) {
for (let j = 0; j < this.subKeys[i].bindingSignatures.length; j++) {
@ -366,88 +328,103 @@ Key.prototype.getEncryptionKeyPacket = function(keyId, date=new Date()) {
}
}
}
}
// if no valid subkey for encryption, evaluate primary key
const primaryUser = this.getPrimaryUser(date);
if (primaryUser && (!keyId || this.primaryKey.getKeyId().equals(keyId)) &&
isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate, date)) {
return this.primaryKey;
}
// TODO throw descriptive error
return null;
};
/**
* Encrypts all secret key and subkey packets
* Encrypts all secret key and subkey packets matching keyId
* @param {module:type/keyid} keyId
* @param {String} passphrase
* @returns {Promise<Boolean>}
* @returns {Promise<Array<module:packet/secret_key|module:packet/secret_subkey>>}
*/
Key.prototype.encrypt = async function(passphrase) {
Key.prototype.encrypt = async function(passphrase, keyId=null) {
if (!this.isPrivate()) {
throw new Error("Nothing to encrypt in a public key");
}
const keys = this.getAllKeyPackets();
await Promise.all(keys.map(async function(packet) {
await packet.encrypt(passphrase);
await packet.clearPrivateParams();
return packet;
return Promise.all(this.getKeyPackets(keyId).map(async function(keyPacket) {
await keyPacket.encrypt(passphrase);
await keyPacket.clearPrivateParams();
return keyPacket;
}));
return true;
};
/**
* Decrypts all secret key and subkey packets
* Decrypts all secret key and subkey packets matching keyId
* @param {String} passphrase
* @returns {Promise<Boolean>} true if all key and subkey packets decrypted successfully
* @param {module:type/keyid} keyId
* @returns {Promise<Boolean>} true if all matching key and subkey packets decrypted successfully
*/
Key.prototype.decrypt = async function(passphrase) {
Key.prototype.decrypt = async function(passphrase, keyId=null) {
if (!this.isPrivate()) {
throw new Error("Nothing to decrypt in a public key");
}
const keys = this.getAllKeyPackets();
await Promise.all(keys.map(packet => packet.decrypt(passphrase)));
return true;
const results = await Promise.all(this.getKeyPackets(keyId).map(async function(keyPacket) {
return keyPacket.decrypt(passphrase);
}));
return results.every(result => result === true);
};
/**
* Decrypts specific key packets by key ID
* @param {Array<module:type/keyid>} keyIds
* @param {String} passphrase
* @returns {Boolean} true if all key packets decrypted successfully
* Checks if a signature on a key is revoked
* @param {module:packet/secret_key|
* @param {module:packet/signature} signature The signature to verify
* @param {module:packet/public_subkey|
* module:packet/secret_subkey|
* module:packet/public_key|
* module:packet/secret_key} key, optional The key to verify the signature
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<Boolean>} True if the certificate is revoked
*/
Key.prototype.decryptKeyPacket = function(keyIds, passphrase) {
if (this.isPrivate()) {
const keys = this.getAllKeyPackets();
for (let i = 0; i < keys.length; i++) {
const keyId = keys[i].getKeyId();
for (let j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) {
const success = keys[i].decrypt(passphrase);
if (!success) {
return false;
Key.prototype.isRevoked = async function(signature, key, date=new Date()) {
return isDataRevoked(
this.primaryKey, { key: this.primaryKey }, this.revocationSignatures, signature, key, date
);
};
/**
* Returns a packetlist containing all verified public or private key packets matching keyId.
* If keyId is not present, returns all verified key packets starting with the primary key.
* Verification is in the context of given date.
* @param {type/keyid} keyId
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<module:packet/packetlist>}
*/
Key.prototype.verifyKeyPackets = async function(keyId=null, date=new Date()) {
const packets = new packet.List();
const { primaryKey } = this;
if (await this.verifyPrimaryKey(date)) {
if (!keyId || primaryKey.getKeyId().equals(keyId)) {
packets.push(primaryKey);
}
}
await Promise.all(this.subKeys.map(async subKey => {
if (!keyId || subKey.subKey.getKeyId().equals(keyId)) {
if (await subKey.verify(primaryKey, date)) {
packets.push(subKey.subKey);
}
}
} else {
throw new Error("Nothing to decrypt in a public key");
}
return true;
}));
return packets;
};
/**
* Verify primary key. Checks for revocation signatures, expiration time
* and valid self signature
* @param {Date} date (optional) use the given date for verification instead of the current time
* @returns {Promise{module:enums.keyStatus}} The status of the primary key
* @returns {Promise<module:enums.keyStatus>} The status of the primary key
*/
Key.prototype.verifyPrimaryKey = async function(date=new Date()) {
// TODO clarify OpenPGP's behavior given an expired revocation signature
// check revocation signature
if (this.revocationSignature && !this.revocationSignature.isExpired() &&
(this.revocationSignature.verified ||
await this.revocationSignature.verify(this.primaryKey, { key: this.primaryKey }))) {
// check for key revocation signatures
if (await this.isRevoked(null, null, date)) {
return enums.keyStatus.revoked;
}
const creationTime = this.primaryKey.created.getTime();
@ -461,7 +438,7 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date()) {
}
// check for at least one self signature. Self signature of user ID not mandatory
// See {@link https://tools.ietf.org/html/rfc4880#section-11.1}
if (!this.users.some(user => user.userId && user.selfCertifications)) {
if (!this.users.some(user => user.userId && user.selfCertifications.length)) {
return enums.keyStatus.no_self_cert;
}
// check for valid self signature
@ -472,7 +449,8 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date()) {
}
// check V4 expiration time
if (date !== null && this.primaryKey.version === 4) {
const expirationTime = primaryUser.selfCertificate.keyNeverExpires === false ? creationTime + primaryUser.selfCertificate.keyExpirationTime*1000 : Infinity;
const expirationTime = primaryUser.selfCertificate.keyNeverExpires === false ?
creationTime + primaryUser.selfCertificate.keyExpirationTime*1000 : Infinity;
if (!(creationTime <= currentTime && currentTime < expirationTime)) {
return enums.keyStatus.expired;
}
@ -516,6 +494,8 @@ function getExpirationTime(keyPacket, selfCertificate, defaultValue=null) {
* - otherwise, returns the user with the latest self signature
*
* NOTE: call verifyPrimaryUser before calling this function.
* This is because getPrimaryUser isn't async, so it cannot validate and instead
* relies on already validated certificates.
* @param {Date} date use the given date for verification instead of the current time
* @returns {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
*/
@ -523,7 +503,7 @@ Key.prototype.getPrimaryUser = function(date=new Date()) {
let primaryUsers = [];
for (let i = 0; i < this.users.length; i++) {
// here we only check the primary user ID, ignoring the primary user attribute
if (!this.users[i].userId || !this.users[i].selfCertifications) {
if (!this.users[i].userId || !this.users[i].selfCertifications.length) {
continue;
}
for (let j = 0; j < this.users[i].selfCertifications.length; j++) {
@ -548,7 +528,7 @@ Key.prototype.getPrimaryUser = function(date=new Date()) {
/**
* Update key with new components from specified key with same key ID:
* users, subkeys, certificates are merged into the destination key,
* duplicates are ignored.
* duplicates and expired signatures are ignored.
* If the specified key is a private key and the destination key is public,
* the destination key is transformed to a private key.
* @param {module:key~Key} key source key to merge
@ -563,8 +543,8 @@ Key.prototype.update = async function(key) {
}
if (this.isPublic() && key.isPrivate()) {
// check for equal subkey packets
const equal = ((this.subKeys && this.subKeys.length) === (key.subKeys && key.subKeys.length)) &&
(!this.subKeys || this.subKeys.every(function(destSubKey) {
const equal = (this.subKeys.length === key.subKeys.length) &&
(this.subKeys.every(function(destSubKey) {
return key.subKeys.some(function(srcSubKey) {
return destSubKey.subKey.getFingerprint() === srcSubKey.subKey.getFingerprint();
});
@ -574,13 +554,10 @@ Key.prototype.update = async function(key) {
}
this.primaryKey = key.primaryKey;
}
// TODO clarify OpenPGP's behavior given an expired revocation signature
// revocation signature
if (!this.revocationSignature && key.revocationSignature && !key.revocationSignature.isExpired() &&
(key.revocationSignature.verified ||
await key.revocationSignature.verify(key.primaryKey, { key: key.primaryKey }))) {
this.revocationSignature = key.revocationSignature;
}
// revocation signatures
await mergeSignatures(key, this, 'revocationSignatures', function(srcRevSig) {
return isDataRevoked(that.primaryKey, that, [srcRevSig], null, key.primaryKey);
});
// direct signatures
await mergeSignatures(key, this, 'directSignatures');
// TODO replace when Promise.some or Promise.any are implemented
@ -600,7 +577,6 @@ Key.prototype.update = async function(key) {
}));
// TODO replace when Promise.some or Promise.any are implemented
// subkeys
if (key.subKeys) {
await Promise.all(key.subKeys.map(async function(srcSubKey) {
let found = false;
await Promise.all(that.subKeys.map(async function(dstSubKey) {
@ -613,7 +589,6 @@ Key.prototype.update = async function(key) {
that.subKeys.push(srcSubKey);
}
}));
}
};
/**
@ -627,7 +602,7 @@ Key.prototype.update = async function(key) {
async function mergeSignatures(source, dest, attr, checkFn) {
source = source[attr];
if (source) {
if (!dest[attr]) {
if (!dest[attr].length) {
dest[attr] = source;
} else {
await Promise.all(source.map(async function(sourceSig) {
@ -683,7 +658,8 @@ Key.prototype.signAllUsers = async function(privateKeys) {
* - if no arguments are given, verifies the self certificates;
* - otherwise, verifies all certificates signed with given keys.
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @returns {Promise<Array<({keyid: module:type/keyid, valid: Boolean})>>} list of signer's keyid and validity of signature
* @returns {Promise<Array<{keyid: module:type/keyid,
* valid: Boolean}>>} List of signer's keyid and validity of signature
*/
Key.prototype.verifyPrimaryUser = async function(keys) {
const { primaryKey } = this;
@ -692,7 +668,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) {
let lastPrimaryUserID = null;
await Promise.all(this.users.map(async function(user) {
// here we verify both the primary user ID or the primary user attribute
if (!(user.userId || user.userAttribute) || !user.selfCertifications) {
if (!(user.userId || user.userAttribute) || !user.selfCertifications.length) {
return;
}
const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey };
@ -733,7 +709,9 @@ Key.prototype.verifyPrimaryUser = async function(keys) {
* - if no arguments are given, verifies the self certificates;
* - otherwise, verifies all certificates signed with given keys.
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @returns {Promise<Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>>} list of userid, signer's keyid and validity of signature
* @returns {Promise<Array<{userid: String,
* keyid: module:type/keyid,
* valid: Boolean}>>} list of userid, signer's keyid and validity of signature
*/
Key.prototype.verifyAllUsers = async function(keys) {
const results = [];
@ -762,9 +740,9 @@ function User(userPacket) {
}
this.userId = userPacket.tag === enums.packet.userid ? userPacket : null;
this.userAttribute = userPacket.tag === enums.packet.userAttribute ? userPacket : null;
this.selfCertifications = null;
this.otherCertifications = null;
this.revocationCertifications = null;
this.selfCertifications = [];
this.otherCertifications = [];
this.revocationSignatures = [];
}
/**
@ -774,41 +752,18 @@ function User(userPacket) {
User.prototype.toPacketlist = function() {
const packetlist = new packet.List();
packetlist.push(this.userId || this.userAttribute);
packetlist.concat(this.revocationCertifications);
packetlist.concat(this.revocationSignatures);
packetlist.concat(this.selfCertifications);
packetlist.concat(this.otherCertifications);
return packetlist;
};
/**
* Checks if a self certificate of the user is revoked
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {module:packet/signature} certificate The certificate to verify
* @param {module:packet/public_subkey|module:packet/public_key|
* module:packet/secret_subkey|module:packet/secret_key} key, optional The key to verify the signature
* @returns {Promise<Boolean>} True if the certificate is revoked
*/
User.prototype.isRevoked = async function(primaryKey, certificate, key) {
certificate.revoked = null;
if (this.revocationCertifications) {
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
// TODO clarify OpenPGP's behavior given an expired revocation signature
const results = await Promise.all(this.revocationCertifications.map(async function(revCert) {
return revCert.issuerKeyId.equals(certificate.issuerKeyId) &&
!revCert.isExpired() &&
(revCert.verified || revCert.verify(key || primaryKey, dataToVerify));
}));
certificate.revoked = results.some(result => result === true);
return certificate.revoked;
}
return false;
};
/**
* Signs user
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} privateKeys decrypted private keys for signing
* @returns {Promise<module:key~Key>} new user with new certificate signatures
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} privateKeys Decrypted private keys for signing
* @returns {Promise<module:key~Key>} New user with new certificate signatures
*/
User.prototype.sign = async function(primaryKey, privateKeys) {
const dataToSign = { userid: this.userId || this.userAttribute, key: primaryKey };
@ -820,7 +775,7 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
if (privateKey.primaryKey.getFingerprint() === primaryKey.getFingerprint()) {
throw new Error('Not implemented for self signing');
}
await privateKey.verifyPrimaryUser();
await privateKey.verifyKeyPackets();
const signingKeyPacket = privateKey.getSigningKeyPacket();
if (!signingKeyPacket) {
throw new Error(`Could not find valid signing key packet in key ${
@ -843,12 +798,34 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
return user;
};
/**
* Checks if a given certificate of the user is revoked
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {module:packet/signature} certificate The certificate to verify
* @param {module:packet/public_subkey|
* module:packet/secret_subkey|
* module:packet/public_key|
* module:packet/secret_key} key, optional The key to verify the signature
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<Boolean>} True if the certificate is revoked
*/
User.prototype.isRevoked = async function(primaryKey, certificate, key, date=new Date()) {
return isDataRevoked(
primaryKey, {
key: primaryKey,
userid: this.userId || this.userAttribute
}, this.revocationSignatures, certificate, key, date
);
};
/**
* Verifies the user certificate
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {module:packet/secret_key|
module:packet/public_key} primaryKey The primary key packet
* @param {module:packet/signature} certificate A certificate of this user
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @param {Date} date use the given date for verification instead of the current time
* @param {Array<module:key~Key>} keys Array of keys to verify certificate signatures
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<module:enums.keyStatus>} status of the certificate
*/
User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, date=new Date()) {
@ -857,7 +834,7 @@ User.prototype.verifyCertificate = async function(primaryKey, certificate, keys,
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
const results = await Promise.all(keys.map(async function(key) {
if (!key.getKeyIds().some(id => id.equals(keyid))) { return; }
await key.verifyPrimaryUser();
await key.verifyKeyPackets();
const keyPacket = key.getSigningKeyPacket(keyid, date);
if (certificate.revoked || await that.isRevoked(primaryKey, certificate, keyPacket)) {
return enums.keyStatus.revoked;
@ -875,13 +852,15 @@ User.prototype.verifyCertificate = async function(primaryKey, certificate, keys,
/**
* Verifies all user certificates
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @returns {Promise<Array<({keyid: module:type/keyid, valid: Boolean})>>} list of signer's keyid and validity of signature
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} keys Array of keys to verify certificate signatures
* @returns {Promise<Array<{keyid: module:type/keyid,
* valid: Boolean}>>} List of signer's keyid and validity of signature
*/
User.prototype.verifyAllCertifications = async function(primaryKey, keys) {
const that = this;
const certifications = this.selfCertifications.concat(this.otherCertifications || []);
const certifications = this.selfCertifications.concat(this.otherCertifications);
return Promise.all(certifications.map(async function(certification) {
const status = await that.verifyCertificate(primaryKey, certification, keys);
return {
@ -894,17 +873,19 @@ User.prototype.verifyAllCertifications = async function(primaryKey, keys) {
/**
* Verify User. Checks for existence of self signatures, revocation signatures
* and validity of self signature
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @returns {Promise<module:enums.keyStatus>} status of user
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @returns {Promise<module:enums.keyStatus>} Status of user
*/
User.prototype.verify = async function(primaryKey) {
if (!this.selfCertifications) {
if (!this.selfCertifications.length) {
return enums.keyStatus.no_self_cert;
}
const that = this;
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
// TODO replace when Promise.some or Promise.any are implemented
const results = [enums.keyStatus.invalid].concat(await Promise.all(this.selfCertifications.map(async function(selfCertification) {
const results = [enums.keyStatus.invalid].concat(
await Promise.all(this.selfCertifications.map(async function(selfCertification) {
if (selfCertification.revoked || await that.isRevoked(primaryKey, selfCertification)) {
return enums.keyStatus.revoked;
}
@ -923,7 +904,7 @@ User.prototype.verify = async function(primaryKey) {
/**
* Update user with new components from specified user
* @param {module:key~User} user source user to merge
* @param {Promise<module:packet/signature>} primaryKey primary key used for validation
* @param {module:packet/signature} primaryKey primary key used for validation
*/
User.prototype.update = async function(user, primaryKey) {
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
@ -934,7 +915,9 @@ User.prototype.update = async function(user, primaryKey) {
// other signatures
await mergeSignatures(user, this, 'otherCertifications');
// revocation signatures
await mergeSignatures(user, this, 'revocationCertifications');
await mergeSignatures(user, this, 'revocationSignatures', function(srcRevSig) {
return isDataRevoked(primaryKey, dataToVerify, [srcRevSig]);
});
};
/**
@ -947,7 +930,7 @@ function SubKey(subKeyPacket) {
}
this.subKey = subKeyPacket;
this.bindingSignatures = [];
this.revocationSignature = null;
this.revocationSignatures = [];
}
/**
@ -957,65 +940,43 @@ function SubKey(subKeyPacket) {
SubKey.prototype.toPacketlist = function() {
const packetlist = new packet.List();
packetlist.push(this.subKey);
packetlist.push(this.revocationSignature);
for (let i = 0; i < this.bindingSignatures.length; i++) {
packetlist.push(this.bindingSignatures[i]);
}
packetlist.concat(this.revocationSignatures);
packetlist.concat(this.bindingSignatures);
return packetlist;
};
/**
* Returns true if the subkey can be used for encryption
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Date} date use the given date for verification instead of the current time
* @returns {Promise<Boolean>}
* Checks if a binding signature of a subkey is revoked
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {module:packet/signature} signature The binding signature to verify
* @param {module:packet/public_subkey|
* module:packet/secret_subkey|
* module:packet/public_key|
* module:packet/secret_key} key, optional The key to verify the signature
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<Boolean>} True if the binding signature is revoked
*/
SubKey.prototype.isValidEncryptionKey = async function(primaryKey, date=new Date()) {
if (await this.verify(primaryKey, date) !== enums.keyStatus.valid) {
return false;
}
for (let i = 0; i < this.bindingSignatures.length; i++) {
if (isValidEncryptionKeyPacket(this.subKey, this.bindingSignatures[i], date)) {
return true;
}
}
return false;
};
/**
* Returns true if the subkey can be used for signing of data
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Date} date use the given date for verification instead of the current time
* @returns {Promise<Boolean>}
*/
SubKey.prototype.isValidSigningKey = async function(primaryKey, date=new Date()) {
if (await this.verify(primaryKey, date) !== enums.keyStatus.valid) {
return false;
}
for (let i = 0; i < this.bindingSignatures.length; i++) {
if (isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i], date)) {
return true;
}
}
return false;
SubKey.prototype.isRevoked = async function(primaryKey, signature, key, date=new Date()) {
return isDataRevoked(
primaryKey, {
key: primaryKey,
bind: this.subKey
}, this.revocationSignatures, signature, key, date
);
};
/**
* Verify subkey. Checks for revocation signatures, expiration time
* and valid binding signature
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Date} date use the given date for verification instead of the current time
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<module:enums.keyStatus>} The status of the subkey
*/
SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
const that = this;
// TODO clarify OpenPGP's behavior given an expired revocation signature
// check subkey revocation signature
if (this.revocationSignature && !this.revocationSignature.isExpired() &&
(this.revocationSignature.verified ||
await this.revocationSignature.verify(primaryKey, { key: primaryKey, bind: this.subKey }))) {
return enums.keyStatus.revoked;
}
const dataToVerify = { key: primaryKey, bind: this.subKey };
const creationTime = this.subKey.created.getTime();
const currentTime = util.normalizeDate(date);
// check V3 expiration time
@ -1027,25 +988,31 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
}
// check subkey binding signatures (at least one valid binding sig needed)
// TODO replace when Promise.some or Promise.any are implemented
const results = [enums.keyStatus.invalid].concat(await Promise.all(this.bindingSignatures.map(async function(bindingSignature) {
const results = [enums.keyStatus.invalid].concat(
await Promise.all(this.bindingSignatures.map(async function(bindingSignature) {
// check binding signature is verified
if (!(bindingSignature.verified || await bindingSignature.verify(primaryKey, dataToVerify))) {
return enums.keyStatus.invalid;
}
// check binding signature is not revoked
if (bindingSignature.revoked || await that.isRevoked(primaryKey, bindingSignature, null, date)) {
return enums.keyStatus.revoked;
}
// check binding signature is not expired
if (bindingSignature.isExpired(date)) {
return enums.keyStatus.expired; // last expired binding signature
}
// check binding signature can verify
if (!(bindingSignature.verified ||
await bindingSignature.verify(primaryKey, { key: primaryKey, bind: that.subKey }))) {
return enums.keyStatus.invalid; // last invalid binding signature
return enums.keyStatus.expired;
}
// check V4 expiration time
if (that.subKey.version === 4 && currentTime !== null) {
const expirationTime = bindingSignature.keyNeverExpires === false ? (creationTime + bindingSignature.keyExpirationTime*1000) : Infinity;
const expirationTime = bindingSignature.keyNeverExpires === false ?
(creationTime + bindingSignature.keyExpirationTime*1000) : Infinity;
if (!(creationTime <= currentTime && currentTime < expirationTime)) {
return enums.keyStatus.expired; // last V4 expired binding signature
}
}
return enums.keyStatus.valid; // found a binding signature that passed all checks
})));
}))
);
return results.some(status => status === enums.keyStatus.valid) ?
enums.keyStatus.valid : results.pop();
};
@ -1071,7 +1038,7 @@ SubKey.prototype.getExpirationTime = function() {
/**
* Update subkey with new components from specified subkey
* @param {module:key~SubKey} subKey source subkey to merge
* @param {Promise<module:packet/signature>} primaryKey primary key used for validation
* @param {module:packet/signature} primaryKey primary key used for validation
*/
SubKey.prototype.update = async function(subKey, primaryKey) {
if (await subKey.verify(primaryKey) === enums.keyStatus.invalid) {
@ -1087,27 +1054,24 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
}
// update missing binding signatures
const that = this;
await Promise.all(subKey.bindingSignatures.map(async function(newBindingSignature) {
if (newBindingSignature.verified ||
await newBindingSignature.verify(primaryKey, { key: primaryKey, bind: that.subKey })) {
const dataToVerify = { key: primaryKey, bind: that.subKey };
await mergeSignatures(subKey, this, 'bindingSignatures', async function(srcBindSig) {
if (!(srcBindSig.verified || await srcBindSig.verify(primaryKey, dataToVerify))) {
return false;
}
for (let i = 0; i < that.bindingSignatures.length; i++) {
if (that.bindingSignatures[i].issuerKeyId.equals(newBindingSignature.issuerKeyId)) {
that.bindingSignatures[i] = newBindingSignature;
return;
if (that.bindingSignatures[i].issuerKeyId.equals(srcBindSig.issuerKeyId)) {
// TODO check which one is more recent
that.bindingSignatures[i] = srcBindSig;
return false;
}
}
that.bindingSignatures.push(newBindingSignature);
}
}));
// TODO clarify OpenPGP's behavior given an expired revocation signature
// revocation signature
if (!this.revocationSignature &&
subKey.revocationSignature &&
!subKey.revocationSignature.isExpired() &&
(subKey.revocationSignature.verified ||
await subKey.revocationSignature.verify(primaryKey, { key: primaryKey, bind: this.subKey }))) {
this.revocationSignature = subKey.revocationSignature;
}
return true;
});
// revocation signatures
await mergeSignatures(subKey, this, 'revocationSignatures', function(srcRevSig) {
return isDataRevoked(primaryKey, dataToVerify, [srcRevSig]);
});
};
/**
@ -1146,7 +1110,8 @@ export function read(data) {
/**
* Reads an OpenPGP armored text and returns one or multiple key objects
* @param {String} armoredText text to be parsed
* @returns {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
* @returns {{keys: Array<module:key~Key>,
err: (Array<Error>|null)}} result object with key and error arrays
* @static
*/
export function readArmored(armoredText) {
@ -1255,16 +1220,13 @@ export function generate(options) {
export async function reformat(options) {
let secretKeyPacket;
let secretSubkeyPacket;
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
// 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');
}
try {
await options.privateKey.decrypt();
}
catch(err) {
if (!options.privateKey.decrypt()) {
throw new Error('Key not decrypted');
}
@ -1295,7 +1257,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
if (options.passphrase) {
await secretKeyPacket.encrypt(options.passphrase);
if (secretSubkeyPacket) {
secretSubkeyPacket.encrypt(options.passphrase);
await secretSubkeyPacket.encrypt(options.passphrase);
}
}
@ -1380,6 +1342,43 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
return new Key(packetlist);
}
/**
* Checks if a given certificate or binding signature is revoked
* @param {module:packet/secret_key|
* module:packet/public_key} primaryKey The primary key packet
* @param {Object} dataToVerify The data to check
* @param {Array<module:packet/signature>} revocations The revocation signatures to check
* @param {module:packet/signature} signature The certificate or signature to check
* @param {module:packet/public_subkey|
* module:packet/secret_subkey|
* module:packet/public_key|
* module:packet/secret_key} key, optional The key packet to check the signature
* @param {Date} date Use the given date instead of the current time
* @returns {Promise<Boolean>} True if the signature revokes the data
*/
async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, key, date=new Date()) {
key = key || primaryKey;
const normDate = util.normalizeDate(date);
const revocationKeyIds = [];
await Promise.all(revocations.map(async function(revocationSignature) {
if (!(config.revocations_expire && revocationSignature.isExpired(normDate)) &&
(revocationSignature.verified || await revocationSignature.verify(key, dataToVerify))) {
// TODO get an identifier of the revoked object instead
revocationKeyIds.push(revocationSignature.issuerKeyId);
return true;
}
return false;
}));
// TODO further verify that this is the signature that should be revoked
// In particular, if signature.issuerKeyId is a wildcard, any revocation signature will revoke it
if (signature) {
signature.revoked = revocationKeyIds.some(keyId => keyId.equals(signature.issuerKeyId)) ? true :
signature.revoked;
return signature.revoked;
}
return revocationKeyIds.length > 0;
}
/**
* Returns the preferred signature hash algorithm of a key
* @param {object} key
@ -1388,7 +1387,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
export function getPreferredHashAlgo(key) {
let hash_algo = config.prefer_hash_algorithm;
let pref_algo = hash_algo;
if (Key.prototype.isPrototypeOf(key)) {
if (key instanceof Key) {
const primaryUser = key.getPrimaryUser();
if (primaryUser && primaryUser.selfCertificate.preferredHashAlgorithms) {
[pref_algo] = primaryUser.selfCertificate.preferredHashAlgorithms;

View File

@ -148,24 +148,23 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
if (!symESKeyPacketlist) {
throw new Error('No symmetrically encrypted session key packet found.');
}
await Promise.all(symESKeyPacketlist.map(async function(packet) {
await Promise.all(symESKeyPacketlist.map(async function(keyPacket) {
await Promise.all(passwords.map(async function(password) {
try {
await packet.decrypt(password);
keyPackets.push(packet);
await keyPacket.decrypt(password);
keyPackets.push(keyPacket);
} catch (err) {}
}));
}));
} else if (privateKeys) {
const pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey);
if (!pkESKeyPacketlist) {
throw new Error('No public key encrypted session key packet found.');
}
await Promise.all(pkESKeyPacketlist.map(async function(packet) {
await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) {
const privateKeyPackets = privateKeys.reduce(function(acc, privateKey) {
return acc.concat(privateKey.getKeyPackets(packet.publicKeyId));
}, []);
return acc.concat(privateKey.getKeyPackets(keyPacket.publicKeyId));
}, new packet.List());
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
if (!privateKeyPacket) {
return;
@ -174,8 +173,8 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
throw new Error('Private key is not decrypted.');
}
try {
await packet.decrypt(privateKeyPacket);
keyPackets.push(packet);
await keyPacket.decrypt(privateKeyPacket);
keyPackets.push(keyPacket);
} catch (err) {}
}));
}));
@ -302,7 +301,7 @@ export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwor
if (publicKeys) {
const results = await Promise.all(publicKeys.map(async function(key) {
await key.verifyPrimaryUser();
await key.verifyKeyPackets(undefined, date);
const encryptionKeyPacket = key.getEncryptionKeyPacket(undefined, date);
if (!encryptionKeyPacket) {
throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex());
@ -318,7 +317,6 @@ export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwor
}));
packetlist.concat(results);
}
if (passwords) {
const testDecrypt = async function(keyPacket, password) {
try {
@ -396,7 +394,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, date=new
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
await privateKey.verifyPrimaryUser();
await privateKey.verifyKeyPackets(undefined, date);
const signingKeyPacket = privateKey.getSigningKeyPacket(undefined, date);
if (!signingKeyPacket) {
throw new Error('Could not find valid key packet for signing in key ' +
@ -475,10 +473,11 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
await privateKey.verifyPrimaryUser();
await privateKey.verifyKeyPackets(undefined, date);
const signingKeyPacket = privateKey.getSigningKeyPacket(undefined, date);
if (!signingKeyPacket) {
throw new Error('Could not find valid key packet for signing in key ' + privateKey.primaryKey.getKeyId().toHex());
throw new Error('Could not find valid key packet for signing in key ' +
privateKey.primaryKey.getKeyId().toHex());
}
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
@ -545,15 +544,14 @@ export async function createVerificationObjects(signatureList, literalDataList,
return Promise.all(signatureList.map(async function(signature) {
let keyPacket = null;
await Promise.all(keys.map(async function(key) {
await key.verifyPrimaryUser();
// Look for the unique key packet that matches issuerKeyId of signature
await key.verifyKeyPackets(signature.issuerKeyId, date);
const result = key.getSigningKeyPacket(signature.issuerKeyId, date);
if (result) {
keyPacket = result;
}
}));
// Look for the unique key packet that matches issuerKeyId of signature
const verifiedSig = {
keyid: signature.issuerKeyId,
valid: keyPacket ? await signature.verify(keyPacket, literalDataList[0]) : null

View File

@ -177,6 +177,19 @@ Packetlist.prototype.some = async function (callback) {
return false;
};
/**
* Executes the callback function once for each element,
* returns true if all callbacks returns a truthy value
*/
Packetlist.prototype.every = function (callback) {
for (let i = 0; i < this.length; i++) {
if (!callback(this[i], i, this)) {
return false;
}
}
return true;
};
/**
* Traverses packet tree and returns first matching packet
* @param {module:enums.packet} type The packet type
@ -240,6 +253,7 @@ Packetlist.prototype.concat = function (packetlist) {
this.push(packetlist[i]);
}
}
return this;
};
/**

View File

@ -86,6 +86,7 @@ export default function Signature(date=new Date()) {
this.embeddedSignature = null;
this.verified = null;
this.revoked = null;
}
/**

View File

@ -53,7 +53,7 @@ Keyid.prototype.toHex = function() {
};
Keyid.prototype.equals = function(keyid) {
return this.bytes === keyid.bytes;
return keyid.isWildcard() || this.bytes === keyid.bytes;
};
Keyid.prototype.isNull = function() {

View File

@ -1278,10 +1278,11 @@ describe('Key', function() {
});
});
it('Find a valid subkey binding signature among many invalid ones', function(done) {
it('Find a valid subkey binding signature among many invalid ones', function() {
const k = openpgp.key.readArmored(valid_binding_sig_among_many_expired_sigs_pub).keys[0];
return k.verifyKeyPackets().then(() => {
expect(k.getEncryptionKeyPacket()).to.not.be.null;
done();
})
});
});

View File

@ -448,8 +448,7 @@ describe("Signature", function() {
const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
const keyids = esMsg.getEncryptionKeyIds();
privKey.decryptKeyPacket(keyids, 'hello world');
esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId));
return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg }).then(function(decrypted) {
expect(decrypted.data).to.exist;
@ -483,8 +482,7 @@ describe("Signature", function() {
const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
const keyids = esMsg.getEncryptionKeyIds();
privKey.decryptKeyPacket(keyids, 'hello world');
esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId));
return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg }).then(function(decrypted) {
expect(decrypted.data).to.exist;