From 5cf61daa19dbe9df7a6bd5cc7280f28346b8f134 Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Tue, 18 Sep 2018 21:59:23 +0200
Subject: [PATCH 1/5] Check validity of signatures before using them

---
 src/key.js              | 115 ++++++++++++++++++++++------------------
 src/openpgp.js          |   6 +--
 src/packet/signature.js |   6 +++
 test/general/key.js     |  30 +++++++++--
 4 files changed, 98 insertions(+), 59 deletions(-)

diff --git a/src/key.js b/src/key.js
index 7e98f33b..ac6d2f4f 100644
--- a/src/key.js
+++ b/src/key.js
@@ -261,30 +261,37 @@ Key.prototype.armor = function() {
 };
 
 /**
- * Returns the signature that has the latest creation date, while ignoring signatures created in the future.
+ * Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future.
  * @param  {Array<module:packet.Signature>} signatures  List of signatures
  * @param  {Date}                           date        Use the given date instead of the current time
- * @returns {module:packet.Signature} The latest signature
+ * @returns {Promise<module:packet.Signature>} The latest valid signature
+ * @async
  */
-function getLatestSignature(signatures, date=new Date()) {
-  let signature = signatures[0];
-  for (let i = 1; i < signatures.length; i++) {
-    if (signatures[i].created >= signature.created &&
-       (signatures[i].created <= date || date === null)) {
+async function getLatestValidSignature(signatures, primaryKey, dataToVerify, date=new Date()) {
+  let signature;
+  for (let i = signatures.length - 1; i >= 0; i--) {
+    if (
+      (!signature || signatures[i].created >= signature.created) &&
+      // check binding signature is not expired (ie, check for V4 expiration time)
+      !signatures[i].isExpired(date) &&
+      // check binding signature is verified
+      (signatures[i].verified || await signatures[i].verify(primaryKey, dataToVerify))
+    ) {
       signature = signatures[i];
     }
   }
   return signature;
 }
 
-function isValidSigningKeyPacket(keyPacket, signature, date=new Date()) {
+function isValidSigningKeyPacket(keyPacket, signature) {
+  if (!signature.verified || signature.revoked !== false) { // Sanity check
+    throw new Error('Signature not verified');
+  }
   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(date) &&
-         !isDataExpired(keyPacket, signature, date);
+          (signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0);
 }
 
 /**
@@ -302,8 +309,9 @@ Key.prototype.getSigningKey = async function (keyId=null, date=new Date(), userI
     for (let i = 0; i < subKeys.length; i++) {
       if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
         if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
-          const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
-          if (isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
+          const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
+          const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
+          if (bindingSignature && isValidSigningKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
             return subKeys[i];
           }
         }
@@ -311,23 +319,24 @@ Key.prototype.getSigningKey = async function (keyId=null, date=new Date(), userI
     }
     const primaryUser = await this.getPrimaryUser(date, userId);
     if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
-        isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
+        isValidSigningKeyPacket(primaryKey, primaryUser.selfCertification)) {
       return this;
     }
   }
   return null;
 };
 
-function isValidEncryptionKeyPacket(keyPacket, signature, date=new Date()) {
+function isValidEncryptionKeyPacket(keyPacket, signature) {
+  if (!signature.verified || signature.revoked !== false) { // Sanity check
+    throw new Error('Signature not verified');
+  }
   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) &&
          keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) &&
          (!signature.keyFlags ||
           (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
-          (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0) &&
-         signature.verified && !signature.revoked && !signature.isExpired(date) &&
-         !isDataExpired(keyPacket, signature, date);
+          (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0);
 }
 
 /**
@@ -346,8 +355,9 @@ Key.prototype.getEncryptionKey = async function(keyId, date=new Date(), userId={
     for (let i = 0; i < subKeys.length; i++) {
       if (!keyId || subKeys[i].getKeyId().equals(keyId)) {
         if (await subKeys[i].verify(primaryKey, date) === enums.keyStatus.valid) {
-          const bindingSignature = getLatestSignature(subKeys[i].bindingSignatures, date);
-          if (isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature, date)) {
+          const dataToVerify = { key: primaryKey, bind: subKeys[i].keyPacket };
+          const bindingSignature = await getLatestValidSignature(subKeys[i].bindingSignatures, primaryKey, dataToVerify, date);
+          if (bindingSignature && isValidEncryptionKeyPacket(subKeys[i].keyPacket, bindingSignature)) {
             return subKeys[i];
           }
         }
@@ -356,7 +366,7 @@ Key.prototype.getEncryptionKey = async function(keyId, date=new Date(), userId={
     // if no valid subkey for encryption, evaluate primary key
     const primaryUser = await this.getPrimaryUser(date, userId);
     if (primaryUser && (!keyId || primaryKey.getKeyId().equals(keyId)) &&
-        isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification, date)) {
+        isValidEncryptionKeyPacket(primaryKey, primaryUser.selfCertification)) {
       return this;
     }
   }
@@ -492,13 +502,13 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
   if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
     const encryptKey = await this.getEncryptionKey(keyId, null, userId);
     if (!encryptKey) return null;
-    const encryptExpiry = encryptKey.getExpirationTime();
+    const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket);
     if (encryptExpiry < expiry) expiry = encryptExpiry;
   }
   if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
     const signKey = await this.getSigningKey(keyId, null, userId);
     if (!signKey) return null;
-    const signExpiry = signKey.getExpirationTime();
+    const signExpiry = await signKey.getExpirationTime(this.keyPacket);
     if (signExpiry < expiry) expiry = signExpiry;
   }
   return expiry;
@@ -515,16 +525,20 @@ Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
  * @async
  */
 Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
-  const users = this.users.map(function(user, index) {
-    const selfCertification = getLatestSignature(user.selfCertifications, date);
-    return { index, user, selfCertification };
-  }).filter(({ user, selfCertification }) => {
-    return user.userId && selfCertification && (
+  const primaryKey = this.keyPacket;
+  const users = [];
+  for (let i = 0; i < this.users.length; i++) {
+    const user = this.users[i];
+    if (!user.userId || !(
       (userId.name === undefined || user.userId.name === userId.name) &&
       (userId.email === undefined || user.userId.email === userId.email) &&
       (userId.comment === undefined || user.userId.comment === userId.comment)
-    );
-  });
+    )) continue;
+    const dataToVerify = { userId: user.userId, key: primaryKey };
+    const selfCertification = await getLatestValidSignature(user.selfCertifications, primaryKey, dataToVerify, date);
+    if (!selfCertification) continue;
+    users.push({ index: i, user, selfCertification });
+  }
   if (!users.length) {
     if (userId.name !== undefined || userId.email !== undefined ||
         userId.comment !== undefined) {
@@ -539,18 +553,9 @@ Key.prototype.getPrimaryUser = async function(date=new Date(), userId={}) {
     return A.isPrimaryUserID - B.isPrimaryUserID || A.created - B.created;
   }).pop();
   const { user, selfCertification: cert } = primaryUser;
-  const primaryKey = this.keyPacket;
-  const dataToVerify = { userId: user.userId, key: primaryKey };
-  // skip if certificates is invalid, revoked, or expired
-  if (!(cert.verified || await cert.verify(primaryKey, dataToVerify))) {
-    return null;
-  }
   if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) {
     return null;
   }
-  if (cert.isExpired(date)) {
-    return null;
-  }
   return primaryUser;
 };
 
@@ -678,12 +683,15 @@ Key.prototype.revoke = async function({
 /**
  * Get revocation certificate from a revoked key.
  *   (To get a revocation certificate for an unrevoked key, call revoke() first.)
- * @returns {String} armored revocation certificate
+ * @returns {Promise<String>} armored revocation certificate
+ * @async
  */
-Key.prototype.getRevocationCertificate = function() {
-  if (this.revocationSignatures.length) {
+Key.prototype.getRevocationCertificate = async function() {
+  const dataToVerify = { key: this.keyPacket };
+  const revocationSignature = await getLatestValidSignature(this.revocationSignatures, this.keyPacket, dataToVerify);
+  if (revocationSignature) {
     const packetlist = new packet.List();
-    packetlist.push(getLatestSignature(this.revocationSignatures));
+    packetlist.push(revocationSignature);
     return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
   }
 };
@@ -1090,17 +1098,17 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
   const that = this;
   const dataToVerify = { key: primaryKey, bind: this.keyPacket };
   // check subkey binding signatures
-  const bindingSignature = getLatestSignature(this.bindingSignatures, date);
+  const bindingSignature = await getLatestValidSignature(this.bindingSignatures, primaryKey, dataToVerify, date);
   // check binding signature is verified
-  if (!(bindingSignature.verified || await bindingSignature.verify(primaryKey, dataToVerify))) {
+  if (!bindingSignature) {
     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 (ie, check for V4 expiration time)
-  if (bindingSignature.isExpired(date)) {
+  // check for expiration time
+  if (isDataExpired(this.keyPacket, bindingSignature, date)) {
     return enums.keyStatus.expired;
   }
   return enums.keyStatus.valid; // binding signature passed all checks
@@ -1108,11 +1116,16 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
 
 /**
  * Returns the expiration time of the subkey or Infinity if key does not expire
+ * @param  {module:packet.SecretKey|
+ *          module:packet.PublicKey} primaryKey  The primary key packet
  * @param  {Date}                     date       Use the given date instead of the current time
- * @returns {Date}
+ * @returns {Promise<Date>}
+ * @async
  */
-SubKey.prototype.getExpirationTime = function(date=new Date()) {
-  const bindingSignature = getLatestSignature(this.bindingSignatures, date);
+SubKey.prototype.getExpirationTime = async function(primaryKey, date=new Date()) {
+  const dataToVerify = { key: primaryKey, bind: this.keyPacket };
+  const bindingSignature = await getLatestValidSignature(this.bindingSignatures, primaryKey, dataToVerify, date);
+  if (!bindingSignature) return null;
   const keyExpiry = getExpirationTime(this.keyPacket, bindingSignature);
   const sigExpiry = bindingSignature.getExpirationTime();
   return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
@@ -1556,7 +1569,7 @@ async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, k
   // TODO further verify that this is the signature that should be revoked
   if (signature) {
     signature.revoked = revocationKeyIds.some(keyId => keyId.equals(signature.issuerKeyId)) ? true :
-      signature.revoked;
+      signature.revoked || false;
     return signature.revoked;
   }
   return revocationKeyIds.length > 0;
diff --git a/src/openpgp.js b/src/openpgp.js
index e9540a55..389416b3 100644
--- a/src/openpgp.js
+++ b/src/openpgp.js
@@ -124,7 +124,7 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
   }
 
   return generate(options).then(async key => {
-    const revocationCertificate = key.getRevocationCertificate();
+    const revocationCertificate = await key.getRevocationCertificate();
     key.revocationSignatures = [];
 
     return convertStreams({
@@ -159,8 +159,8 @@ export function reformatKey({privateKey, userIds=[], passphrase="", keyExpiratio
 
   options.revoked = options.revocationCertificate;
 
-  return reformat(options).then(key => {
-    const revocationCertificate = key.getRevocationCertificate();
+  return reformat(options).then(async key => {
+    const revocationCertificate = await key.getRevocationCertificate();
     key.revocationSignatures = [];
 
     return {
diff --git a/src/packet/signature.js b/src/packet/signature.js
index 0121cfd0..25464690 100644
--- a/src/packet/signature.js
+++ b/src/packet/signature.js
@@ -201,6 +201,12 @@ Signature.prototype.sign = async function (key, data) {
   this.signature = stream.fromAsync(async () => crypto.signature.sign(
     publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
   ));
+
+  // Store the fact that this signature is valid, e.g. for when we call `await
+  // getLatestValidSignature(this.revocationSignatures, key, data)` later. Note
+  // that this only holds up if the key and data passed to verify are the same
+  // as the ones passed to sign.
+  this.verified = true;
   return true;
 };
 
diff --git a/test/general/key.js b/test/general/key.js
index 1275f2f3..9de32570 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -1389,7 +1389,7 @@ function versionSpecificTests() {
       const actual_delta = (new Date(expiration) - new Date()) / 1000;
       expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
 
-      const subKeyExpiration = await key.subKeys[0].getExpirationTime();
+      const subKeyExpiration = await key.subKeys[0].getExpirationTime(key.primaryKey);
       expect(subKeyExpiration).to.exist;
 
       const actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
@@ -1799,7 +1799,7 @@ describe('Key', function() {
     const pubKey = (await openpgp.key.readArmored(twoKeys)).keys[1];
     expect(pubKey).to.exist;
     expect(pubKey).to.be.an.instanceof(openpgp.key.Key);
-    const expirationTime = await pubKey.subKeys[0].getExpirationTime();
+    const expirationTime = await pubKey.subKeys[0].getExpirationTime(pubKey.primaryKey);
     expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
   });
 
@@ -1990,7 +1990,7 @@ describe('Key', function() {
 
   it('getRevocationCertificate() should produce the same revocation certificate as GnuPG', async function() {
     const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
-    const revocationCertificate = revKey.getRevocationCertificate();
+    const revocationCertificate = await revKey.getRevocationCertificate();
 
     const input = await openpgp.armor.decode(revocation_certificate_arm4);
     const packetlist = new openpgp.packet.List();
@@ -2002,7 +2002,7 @@ describe('Key', function() {
 
   it('getRevocationCertificate() should have an appropriate comment', async function() {
     const revKey = (await openpgp.key.readArmored(revoked_key_arm4)).keys[0];
-    const revocationCertificate = revKey.getRevocationCertificate();
+    const revocationCertificate = await revKey.getRevocationCertificate();
 
     expect(revocationCertificate).to.match(/Comment: This is a revocation certificate/);
     expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
@@ -2170,7 +2170,27 @@ VYGdb3eNlV8CfoEC
 
   it('Selects the most recent subkey binding signature', async function() {
     const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
-    expect(key.subKeys[0].getExpirationTime().toISOString()).to.equal('2015-10-18T07:41:30.000Z');
+    expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
+  });
+
+  it('Selects the most recent non-expired subkey binding signature', async function() {
+    const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
+    key.subKeys[0].bindingSignatures[1].signatureNeverExpires = false;
+    key.subKeys[0].bindingSignatures[1].signatureExpirationTime = 0;
+    expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
+  });
+
+  it('Selects the most recent valid subkey binding signature', async function() {
+    const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
+    key.subKeys[0].bindingSignatures[1].signatureData[0]++;
+    expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
+  });
+
+  it('Handles a key with no valid subkey binding signatures gracefully', async function() {
+    const key = (await openpgp.key.readArmored(multipleBindingSignatures)).keys[0];
+    key.subKeys[0].bindingSignatures[0].signatureData[0]++;
+    key.subKeys[0].bindingSignatures[1].signatureData[0]++;
+    expect(await key.subKeys[0].getExpirationTime(key.primaryKey)).to.be.null;
   });
 
   it('Reject encryption with revoked subkey', async function() {

From a1c47ecdea790e1a8c3aca8e3868c5aa00811d05 Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Thu, 20 Sep 2018 15:28:03 +0200
Subject: [PATCH 2/5] Indicate an error when parsing a key with an authorized
 revocation key

Since we will ignore revocation signatures from authorized revocation keys,
it is dangerous to use these keys.
---
 src/key.js               |  16 +++++--
 src/packet/packetlist.js | 100 ++-------------------------------------
 test/general/key.js      |  44 +++++++++++++++++
 3 files changed, 59 insertions(+), 101 deletions(-)

diff --git a/src/key.js b/src/key.js
index ac6d2f4f..c96138db 100644
--- a/src/key.js
+++ b/src/key.js
@@ -1220,9 +1220,16 @@ SubKey.prototype.revoke = async function(primaryKey, {
 export async function read(data) {
   const result = {};
   result.keys = [];
+  const err = [];
   try {
     const packetlist = new packet.List();
     await packetlist.read(data);
+    if (packetlist.filterByTag(enums.packet.signature).some(
+      signature => signature.revocationKeyClass !== null
+    )) {
+      // Indicate an error, but still parse the key.
+      err.push(new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.'));
+    }
     const keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
     if (keyIndex.length === 0) {
       throw new Error('No key packet found');
@@ -1233,13 +1240,14 @@ export async function read(data) {
         const newKey = new Key(oneKeyList);
         result.keys.push(newKey);
       } catch (e) {
-        result.err = result.err || [];
-        result.err.push(e);
+        err.push(e);
       }
     }
   } catch (e) {
-    result.err = result.err || [];
-    result.err.push(e);
+    err.push(e);
+  }
+  if (err.length) {
+    result.err = err;
   }
   return result;
 }
diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js
index 12b09754..f1394017 100644
--- a/src/packet/packetlist.js
+++ b/src/packet/packetlist.js
@@ -21,6 +21,7 @@ import util from '../util';
  * are stored as numerical indices.
  * @memberof module:packet
  * @constructor
+ * @extends Array
  */
 function List() {
   /**
@@ -31,6 +32,8 @@ function List() {
   this.length = 0;
 }
 
+List.prototype = [];
+
 /**
  * Reads a stream of binary data and interprents it as a list of packets.
  * @param {Uint8Array | ReadableStream<Uint8Array>} A Uint8Array of bytes.
@@ -145,37 +148,6 @@ List.prototype.push = function (packet) {
   this.length++;
 };
 
-/**
- * Remove a packet from the list and return it.
- * @returns {Object}   The packet that was removed
- */
-List.prototype.pop = function() {
-  if (this.length === 0) {
-    return;
-  }
-
-  const packet = this[this.length - 1];
-  delete this[this.length - 1];
-  this.length--;
-
-  return packet;
-};
-
-/**
- * Creates a new PacketList with all packets that pass the test implemented by the provided function.
- */
-List.prototype.filter = function (callback) {
-  const filtered = new List();
-
-  for (let i = 0; i < this.length; i++) {
-    if (callback(this[i], i, this)) {
-      filtered.push(this[i]);
-    }
-  }
-
-  return filtered;
-};
-
 /**
  * Creates a new PacketList with all packets from the given types
  */
@@ -193,58 +165,6 @@ List.prototype.filterByTag = function (...args) {
   return filtered;
 };
 
-/**
- * Executes the provided callback once for each element
- */
-List.prototype.forEach = function (callback) {
-  for (let i = 0; i < this.length; i++) {
-    callback(this[i], i, this);
-  }
-};
-
-/**
- * Returns an array containing return values of callback
- * on each element
- */
-List.prototype.map = function (callback) {
-  const packetArray = [];
-
-  for (let i = 0; i < this.length; i++) {
-    packetArray.push(callback(this[i], i, this));
-  }
-
-  return packetArray;
-};
-
-/**
- * Executes the callback function once for each element
- * until it finds one where callback returns a truthy value
- * @param  {Function} callback
- * @returns {Promise<Boolean>}
- * @async
- */
-List.prototype.some = async function (callback) {
-  for (let i = 0; i < this.length; i++) {
-    if (await callback(this[i], i, this)) {
-      return true;
-    }
-  }
-  return false;
-};
-
-/**
- * Executes the callback function once for each element,
- * returns true if all callbacks returns a truthy value
- */
-List.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
@@ -285,20 +205,6 @@ List.prototype.indexOfTag = function (...args) {
   return tagIndex;
 };
 
-/**
- * Returns slice of packetlist
- */
-List.prototype.slice = function (begin, end) {
-  if (!end) {
-    end = this.length;
-  }
-  const part = new List();
-  for (let i = begin; i < end; i++) {
-    part.push(this[i]);
-  }
-  return part;
-};
-
 /**
  * Concatenates packetlist or array of packets
  */
diff --git a/test/general/key.js b/test/general/key.js
index 9de32570..cd37ed34 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -1223,6 +1223,41 @@ t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU=
 =C0fJ
 -----END PGP PRIVATE KEY BLOCK-----`;
 
+const key_with_authorized_revocation_key = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: OpenPGP.js VERSION
+Comment: https://openpgpjs.org
+
+xsBNBFujnwwBCADK1xX03tSCmktPDS9Ncij3O5wG+F5/5Zm7QJDc39Wt1t/K
+szCSobWtm/UObQVZjsTGwg0ZUPgepgWGGDBL0dlc1NObwUiOhGYnJnd4V25P
+iU5Mg3+DhRq+LzNK+oGlpVPDpwQ48S8HOphbswKUpuaDcEQ2f+NKIc0eXe5m
+ut5x9uoVj8jneUNsHYq6FIlxh4knzpJWFj5+LNi7plMCwKip6srVNf8He/q0
+0xA/4vjSIOfGIE7TCBH33CbHEr98p81Cf4g0E+kswEz5iWE2SDCyYgQkMrkz
+H9mtVqk3nFT8NR0USxKqH9bGhaTx1AWq/HDgsphayPEWQ0usjQDrbQUnABEB
+AAHNDnRlc3QgPGFAYi5jb20+wsCNBBABCABBBQJbo58MBgsJBwgDAhcMgAH0
+cOUNyxrV8eZOCGRKY2E6TW5AlAkQWICi/ReDcrkEFQgKAgMWAgECGQECGwMC
+HgEAADgHB/0WIHh6maX2LZ0u5ujk1tZxWMrCycccopdQNKN0RGD98X4fyY6J
+wfmKb107gcidJBFct0sVWFW8GU42w9pVMU5qWD6kyFkgcmov319UL+7aZ19b
+HOWVKUTb6rFG8/qAbq3BF7YB/cZIBWMFKAS3BRJ4Kz23GheAB2A9oVLmuq5o
+gW5c2R1YC0T0XyXEFiw9uZ+AS6kEZymFPRQfPUIbJs1ct/lAN+mC9Qp0Y6CL
+60Hd6jlKUb6TgljaQ6CtLfT9v72GeKznapKr9IEtsgYv69j0c/MRM2nmu50c
+g+fICiiHrTbNS6jkUz0pZLe7hdhWHeEiqcA9+GC1DxOQCRCS/YNfzsBNBFuj
+nwwBCADK1xX03tSCmktPDS9Ncij3O5wG+F5/5Zm7QJDc39Wt1t/KszCSobWt
+m/UObQVZjsTGwg0ZUPgepgWGGDBL0dlc1NObwUiOhGYnJnd4V25PiU5Mg3+D
+hRq+LzNK+oGlpVPDpwQ48S8HOphbswKUpuaDcEQ2f+NKIc0eXe5mut5x9uoV
+j8jneUNsHYq6FIlxh4knzpJWFj5+LNi7plMCwKip6srVNf8He/q00xA/4vjS
+IOfGIE7TCBH33CbHEr98p81Cf4g0E+kswEz5iWE2SDCyYgQkMrkzH9mtVqk3
+nFT8NR0USxKqH9bGhaTx1AWq/HDgsphayPEWQ0usjQDrbQUnABEBAAHCwF8E
+GAEIABMFAlujnwwJEFiAov0Xg3K5AhsMAACI/QgArvTcutod+7n1D8wCwM50
+jo3x4KPuQw+NwbOnMbFwv0R8i8NqtSFf2bYkkZ7RLViTmphvSon4h2WgfczL
+SBulZ1QZF7zCKXmXDg8/HZgRUflC1XMixpB8Hqouin5AVgMbsbHg30V2uPco
+V3DeFQ8HWxQC9symaMW/20MkqNXgCjM0us7kVwTEJQqZ6KYrFVjKyprSQRyP
+rfckEBuZnj91OS+kAHlZ+ScZIuV4QVF0e2U6oEuB+qFXppR030PJoO6WDOzm
+67hkzfrc3VpKw/I+vVJnZb4GOhNTSpa+p8i4gTyWmrOZ0G85QoldHWlWaRBg
+lbjwPj3QUTbLFvHisYzXEQ==
+=aT8U
+-----END PGP PUBLIC KEY BLOCK-----
+`;
+
 function versionSpecificTests() {
   it('Preferences of generated key', function() {
     const testPref = function(key) {
@@ -1688,6 +1723,15 @@ describe('Key', function() {
     expect(pubKeys.keys[1].getKeyId().toHex()).to.equal('dbf223e870534df4');
   });
 
+  it('Parsing armored key with an authorized revocation key', async function() {
+    const pubKeys = await openpgp.key.readArmored(key_with_authorized_revocation_key);
+    expect(pubKeys).to.exist;
+    expect(pubKeys.err).to.exist.and.have.length(1);
+    expect(pubKeys.err[0].message).to.equal('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
+    expect(pubKeys.keys).to.have.length(1);
+    expect(pubKeys.keys[0].getKeyId().toHex()).to.equal('5880a2fd178372b9');
+  });
+
   it('Parsing V5 public key packet', async function() {
     // Manually modified from https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b2092/back.mkd#sample-eddsa-key
     let packetBytes = openpgp.util.hex_to_Uint8Array(`

From b3af56b8a389da750f0af5cf299cfead5968443f Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Thu, 20 Sep 2018 16:09:22 +0200
Subject: [PATCH 3/5] Ignore third-party revocation signatures

This check was removed in ec22dab.
---
 src/key.js          | 15 +++++++-
 test/general/key.js | 90 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/src/key.js b/src/key.js
index c96138db..982a5e28 100644
--- a/src/key.js
+++ b/src/key.js
@@ -1566,8 +1566,19 @@ async function isDataRevoked(primaryKey, dataToVerify, revocations, signature, k
   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))) {
+    if (
+      // Note: a third-party revocation signature could legitimately revoke a
+      // self-signature if the signature has an authorized revocation key.
+      // However, we don't support passing authorized revocation keys, nor
+      // verifying such revocation signatures. Instead, we indicate an error
+      // when parsing a key with an authorized revocation key, and ignore
+      // third-party revocation signatures here. (It could also be revoking a
+      // third-party key certification, which should only affect
+      // `verifyAllCertifications`.)
+      (!signature || revocationSignature.issuerKeyId.equals(signature.issuerKeyId)) &&
+      !(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;
diff --git a/test/general/key.js b/test/general/key.js
index cd37ed34..eb21125f 100644
--- a/test/general/key.js
+++ b/test/general/key.js
@@ -1258,6 +1258,76 @@ lbjwPj3QUTbLFvHisYzXEQ==
 -----END PGP PUBLIC KEY BLOCK-----
 `;
 
+const key_with_revoked_third_party_cert = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Comment: GPGTools - https://gpgtools.org
+
+mQENBFS2KSEBCACzz8KtwE5ualmgF+rKo8aPQ9inTQWCNzCuTs3HaSe0D5heGoSh
+mJWl9B5zvXN78L3yzmtWQV92CXOkCRWezIY8y+aN+aJZ6PzPE5Yy74404v3yG9ZK
+jGlAWC7Wgkx+YR2vbzj7hDqi5e6TpDGsFkH3OsI3nY7FIvXWbz9Ih4/s/nBPuF0v
+sBZ0n97ItszhnrXvvrF1fQvEviB0+xF5DfUURWP45EA+NWnBl7HFzY4FeN5ImYZK
+Nt6A88i9SIB3MiwRSUy1UwJjL2L8l+rLbr20JbnIUuJN3h/dY10igxyOh5gsXtr1
+fabsm6s2AacrCjQqLkXSnB8Ucu+Enz5R1s0dABEBAAG0KVBhc3N3b3J0ICDDpMOE
+Pz9eXjEyIDIgwrUgIDxwd2RAdGVzdC5jb20+iFoEMBEKABoFAlYvilYTHSBJY2gg
+d2VpcyBlcyBuaWNodAAKCRDheQpvjBNXTQ5jAKC0AMb5Ivoy0DKNI8Hjus72ob3u
+TACg32AGuCernx1Wt7/5oi4KdjxjjxeJATIEEAEIACYFAlS2KSIGCwkIBwMCCRBj
+ZJ9T2gveXAQVCAIKAxYCAQIbAwIeAQAA/c4H/i/NgI36q/2lwcRkt5rsVBUlx+Ho
++iKIEh1+XKfDq4A8DTjeYCCOg/k3hDm2LpmmclwRc2X9CMwraSoFTEN6Em78Kd5a
+DFaNPbWGP0RCW5zqPGAoZSvOlZYsLMaswFMBD93wf3XwHK8HxTJhTmQC1kGSplO1
+GMWkTh6B3tqiy/Jk7Hp5mPASQBid+E9rjr8CgOPF26hjTw+UUBs2ZWO9U9PyhBYH
+xLIwtUDoZvqhTdXD0aV7vhRQw6p3UEzxa8t/1iGogHe2CfcMgq5jYmHLTZN3VGE3
+djwLQIikRRig7dTBD9BgeG6a+22XRbvpOsHBzsAH4UC97nS+wzkgkyvqfOKIRgQQ
+EQoABgUCVi+JYAAKCRDheQpvjBNXTTNIAJwJacb4mKUPrRnnNcGXC6hjizuJOACg
+zVFVnWA/A+GrHBogUD780vcJwMG5AQ0EVLYpIQEIANNCJ5sUKv6YDWRToF/tG6ik
+LjTAcNelr5LCXLT3Y7CAmk7y88vzCaTLZZUWgwyK8lYGZ3x2icoc4fJeo5BhHNJz
+TSL239cTsAugNoVMJFG2xm1TEzBsBCNPOOVpS5cArt6mmhxozwkafawtgA+5z0zB
+vQm0AHPudSAJp3Gx69meRzAJgdFVgljZVyCUmQizJqJ1dQPPgarpJKJy3f0+g0ec
+yx+gTA4nj+zfqjrXM4O1Ok/Di+8mneA3bhadiYU1VjkqY+1UqkQOU0UFdDlBppRj
+xr6h00xECoayyPXr/U+gFSgZHO1mKk3meCyNVKLGAajQxWVWfBwoPixfqOXlBh0A
+EQEAAYkBHwQYAQgAEwUCVLYpIwkQY2SfU9oL3lwCGwwAAMkDB/9QeLRyEOX2LWdZ
+UkxSldMklAvMBjqY27cC1Qn8wiWzHNJKVEnQ9MiKn0mVRohkRKgsiWfSugDVyVov
+eTM7PSjDlAYALCSxSYStykordUSf/5WYb9Mmea4J/WXBQCvwJKFU47ZDl2Tg+HdS
+SVznLTt/ASxd2Nap2mUveC4ivGdSo1KOq3+t95xGC7dh4U3JwPanBZ6cfBJbnSEs
+QWLUAPnlfn37Ff14haRETX3ND82bkXKEGEk6O5HJ0PWuUtl+TFIkYUGh4zFOZZWq
+VHwaffAHDrmTZt/pOXg9l/VFnzfxrId33Tog3Zvejm2+8d2LhSCtfdrdJ/Dx2CZM
+Yzxp9Mp9
+=uVX6
+-----END PGP PUBLIC KEY BLOCK-----
+`;
+
+const certifying_key = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Comment: GPGTools - https://gpgtools.org
+
+mQGiBEOd+/gRBACfqCfgQCmUzOr7iA1CerGVmFm8HcN+NVGSpkwF6pmPJh1XVGEA
+Nz9Aok6Vx4MQ+QCKo9dTXMZWDE4W/vzaKaEmsirsxGgn7JhK0t/9VeXXieWiJirA
+5iTQMsRjfnS6MLLUr56E7HmDZftiOcpJu81S943r+oeINhq37SlJM7Q47wCg8miR
+egss26IZfW3RvBuNW1KEDh0D/195DH6sl+/qmgUAj3M7ai1iKOqkILdNuIkYRc18
+bsBYIAOjY81guhlEabYEqv8FUKPh2A7dAe/4og89HrUsVxOKJ9EGyrcqyJj676gK
+BL383t1dQvyJyWfV5z4umUMaF/xE46go3Trdyu86aJDe57B74RYbWL2aaGLtkPJ2
+rHOnBACG5PmLM6EXWJQSfbRqJHlRysc3MOAXMnxu8DVz+Tm0nPcArUG+Mf3GmxT+
+537nHqF2xEgVvwfkvGB51ideRRQMhHUzy583zkuPYV1cJ6ykfeA6EHzVbT4vRzO8
+AW9ELBKTK9F4N4gGTOdAATcaMC0gwzCz+fofJEJqC/9CS2pYvrQlVGhvbWFzIE9i
+ZXJuZMO2cmZlciA8dG9iZXJuZG9AZ214LmRlPohdBBMRAgAdBQJDnfv4BgsJCAcD
+AgQVAggDBBYCAwECHgECF4AACgkQ4XkKb4wTV02nkACfWvWnRawPI9AmgXpg6jg1
+/22exKkAoMJ+yFhjtuGobOrIAPcEYrwlTQXBiGsEEBECACsFAksJKNQFgwHihQAe
+Gmh0dHA6Ly93d3cuY2FjZXJ0Lm9yZy9jcHMucGhwAAoJENK7DQFl0P1Y4VoAmgMc
+2qWuBtOBb6Uj6DskTtXORlPgAKCB3Hqp8dJ3dbQh5m0Ebf8F3P71WrkCDQRDnfw1
+EAgAkp+0jMpfKKF6Gxy63fP3tCFzn9vq3GBtbNnvp8b0dx6nf+ZxELt/q9K4Yz9g
++sXq0RFQGV+YwS2BGoogzRcT4PHmUBcEAbjZIs9lZdZDEF0/68d+32mHSkLZJxGI
+ezXJK3+MpGPnCMbQ63UYpcY1BvL7Vbj6P4X75dJJReGIHQMBA0FEYB5AVm6HrWU5
+eDvOZ2w8QAAUluFnD9/xNRqBpcwm5uoox7zq60W5coK6p6WX8t5+WMMrRKF2A1Ru
+aTxYQKo3f8XQA4e6tEcdGFlk1K9W8Ov1xVRQa6EqQYZFesbuoo8HHuSNsJ7PQrP+
+vyYcafohlO/q4QtJXoUimsrEywADBQf7BQWrEx9YlNNsUD2yuov8pYCymxfUVTzK
+huxGHmNj1htXfWWScA2uqD97HOdFu5nvoL2tdaO1RQR/OXKRBcUg6FhOQqqxQSxi
+Vcsoy3aofGi3CWVXgn7KlSopkhlb4ELjzt5H+BMneXdgowO4MimXAfivI7OZl2fN
+ut7emyN9qaeY/e25UKmCYhmhE5hM2+lV8wEmmu/qTCPiZ2u0zH/PE9AAwRz/6X+p
+gsW0WIQpI6iQSSq4KyJxebtJFmCSTFawuXB6rCGovDXo/BkRsDEj1rpZnkwKJPa0
+dEhKK4EzNrUzpWHeE3gKPjFXVmcjIPWVAC3BJoJRHOHg8wqLKcX5MYhGBBgRAgAG
+BQJDnfw1AAoJEOF5Cm+ME1dNChoAoMKa/qx/RKlu3iQPtN6p4NlhRA9IAJ94F/7l
+cKFQz1DDfFCfVpSIJRGozQ==
+=EYzO
+-----END PGP PUBLIC KEY BLOCK-----
+`;
+
 function versionSpecificTests() {
   it('Preferences of generated key', function() {
     const testPref = function(key) {
@@ -1809,6 +1879,26 @@ describe('Key', function() {
     )).to.eventually.equal(openpgp.enums.keyStatus.revoked);
   });
 
+  it('Verify status of key with non-self revocation signature', async function() {
+    const { keys: [pubKey] } = await openpgp.key.readArmored(key_with_revoked_third_party_cert);
+    const [selfCertification] = await pubKey.verifyPrimaryUser();
+    const publicSigningKey = await pubKey.getSigningKey();
+    expect(selfCertification.keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex());
+    expect(selfCertification.valid).to.be.true;
+
+    const { keys: [certifyingKey] } = await openpgp.key.readArmored(certifying_key);
+    const certifyingSigningKey = await certifyingKey.getSigningKey();
+    const signatures = await pubKey.verifyPrimaryUser([certifyingKey]);
+    expect(signatures.length).to.equal(2);
+    expect(signatures[0].keyid.toHex()).to.equal(publicSigningKey.getKeyId().toHex());
+    expect(signatures[0].valid).to.be.null;
+    expect(signatures[1].keyid.toHex()).to.equal(certifyingSigningKey.getKeyId().toHex());
+    expect(signatures[1].valid).to.be.false;
+
+    const { user } = await pubKey.getPrimaryUser();
+    expect(await user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey])).to.equal(openpgp.enums.keyStatus.revoked);
+  });
+
   it('Evaluate key flags to find valid encryption key packet', async function() {
     const pubKeys = await openpgp.key.readArmored(pub_sig_test);
     expect(pubKeys).to.exist;

From bbcdacef8d90e867e0e35c1ca1703b3278477133 Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Thu, 20 Sep 2018 17:02:50 +0200
Subject: [PATCH 4/5] Small documentation fixes

---
 src/key.js            |  8 ++++----
 src/openpgp.js        |  2 +-
 test/serviceworker.js | 47 -------------------------------------------
 3 files changed, 5 insertions(+), 52 deletions(-)
 delete mode 100644 test/serviceworker.js

diff --git a/src/key.js b/src/key.js
index 982a5e28..1d30b871 100644
--- a/src/key.js
+++ b/src/key.js
@@ -483,11 +483,12 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date(), userId={}) {
 /**
  * Returns the latest date when the key can be used for encrypting, signing, or both, depending on the `capabilities` paramater.
  * When `capabilities` is null, defaults to returning the expiry date of the primary key.
+ * Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
  * Returns Infinity if the key doesn't expire.
  * @param  {encrypt|sign|encrypt_sign} capabilities, optional
  * @param  {module:type/keyid} keyId, optional
  * @param  {Object} userId, optional user ID
- * @returns {Promise<Date>}
+ * @returns {Promise<Date | Infinity | null>}
  * @async
  */
 Key.prototype.getExpirationTime = async function(capabilities, keyId, userId) {
@@ -1116,10 +1117,11 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
 
 /**
  * Returns the expiration time of the subkey or Infinity if key does not expire
+ * Returns null if the subkey is invalid.
  * @param  {module:packet.SecretKey|
  *          module:packet.PublicKey} primaryKey  The primary key packet
  * @param  {Date}                     date       Use the given date instead of the current time
- * @returns {Promise<Date>}
+ * @returns {Promise<Date | Infinity | null>}
  * @async
  */
 SubKey.prototype.getExpirationTime = async function(primaryKey, date=new Date()) {
@@ -1199,8 +1201,6 @@ SubKey.prototype.revoke = async function(primaryKey, {
   return subKey;
 };
 
-/**
- */
 ['getKeyId', 'getFingerprint', 'getAlgorithmInfo', 'getCreationTime', 'isDecrypted'].forEach(name => {
   Key.prototype[name] =
   SubKey.prototype[name] =
diff --git a/src/openpgp.js b/src/openpgp.js
index 389416b3..8fb07ef8 100644
--- a/src/openpgp.js
+++ b/src/openpgp.js
@@ -344,7 +344,7 @@ export function encrypt({ message, publicKeys, privateKeys, passwords, sessionKe
  * @param  {String|Array<String>} passwords   (optional) passwords to decrypt the message
  * @param  {Object|Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String }
  * @param  {Key|Array<Key>} publicKeys        (optional) array of public keys or single key, to verify signatures
- * @param  {String} format                    (optional) return data format either as 'utf8' or 'binary'
+ * @param  {'utf8'|'binary'} format           (optional) whether to return data as a string(Stream) or Uint8Array(Stream). If 'utf8' (the default), also normalize newlines.
  * @param  {'web'|'node'|false} streaming     (optional) whether to return data as a stream. Defaults to the type of stream `message` was created from, if any.
  * @param  {Signature} signature              (optional) detached signature for verification
  * @param  {Date} date                        (optional) use the given date for verification instead of the current time
diff --git a/test/serviceworker.js b/test/serviceworker.js
deleted file mode 100644
index 157fd0f5..00000000
--- a/test/serviceworker.js
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-// addEventListener('fetch', event => {
-//   console.log(event);
-//   const url = new URL(event.request.url);
-//   console.log(url);
-//   if (url.pathname === '/test/somedata') {
-//     let plaintext = [];
-//     let i = 0;    
-//     let canceled = false;
-//     const data = new ReadableStream({
-//       /*start(_controller) {
-//         controller = _controller;
-//       },*/
-//       async pull(controller) {
-//         await new Promise(resolve => setTimeout(resolve, 1000));
-//         console.log(i);
-//         if (i++ < 10) {
-//           let randomBytes = new Uint8Array(1000);
-//           randomBytes.fill(i);
-//           controller.enqueue(randomBytes);
-//           plaintext.push(randomBytes);
-//         } else {
-//           controller.close();
-//         }
-//       },
-//       cancel() {
-//         console.log('canceled!');
-//       }
-//     });
-
-    
-//     const response = new Response(data, {
-//       headers: {
-//         'Content-Type': 'application/octet-stream; charset=utf-8',
-//         'Content-Disposition': 'Content-Disposition: attachment; filename=data.bin;'
-//       }
-//     });
-
-//     event.respondWith(response);
-//   }
-
-// });
-
-    
\ No newline at end of file

From ac6b57781b2905997afa75516aa0a20e4cf05196 Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Fri, 21 Sep 2018 21:23:42 +0200
Subject: [PATCH 5/5] Make isValid*KeyPacket inner functions

---
 src/key.js | 46 +++++++++++++++++++++++-----------------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/src/key.js b/src/key.js
index 1d30b871..b669d2db 100644
--- a/src/key.js
+++ b/src/key.js
@@ -283,17 +283,6 @@ async function getLatestValidSignature(signatures, primaryKey, dataToVerify, dat
   return signature;
 }
 
-function isValidSigningKeyPacket(keyPacket, signature) {
-  if (!signature.verified || signature.revoked !== false) { // Sanity check
-    throw new Error('Signature not verified');
-  }
-  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);
-}
-
 /**
  * Returns last created key or key by given keyId that is available for signing and verification
  * @param  {module:type/keyid} keyId, optional
@@ -324,20 +313,18 @@ Key.prototype.getSigningKey = async function (keyId=null, date=new Date(), userI
     }
   }
   return null;
-};
 
-function isValidEncryptionKeyPacket(keyPacket, signature) {
-  if (!signature.verified || signature.revoked !== false) { // Sanity check
-    throw new Error('Signature not verified');
+  function isValidSigningKeyPacket(keyPacket, signature) {
+    if (!signature.verified || signature.revoked !== false) { // Sanity check
+      throw new Error('Signature not verified');
+    }
+    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);
   }
-  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) &&
-         keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) &&
-         (!signature.keyFlags ||
-          (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
-          (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0);
-}
+};
 
 /**
  * Returns last created key or key by given keyId that is available for encryption or decryption
@@ -371,6 +358,19 @@ Key.prototype.getEncryptionKey = async function(keyId, date=new Date(), userId={
     }
   }
   return null;
+
+  function isValidEncryptionKeyPacket(keyPacket, signature) {
+    if (!signature.verified || signature.revoked !== false) { // Sanity check
+      throw new Error('Signature not verified');
+    }
+    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) &&
+      keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) &&
+      (!signature.keyFlags ||
+        (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 ||
+        (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0);
+  }
 };
 
 /**