From 7253df1632425dc6aade1a4f0c4d0aa6210a9fec Mon Sep 17 00:00:00 2001
From: Daniel Huigens <d.huigens@protonmail.com>
Date: Wed, 31 Oct 2018 20:39:33 +0100
Subject: [PATCH] Don't hash when comparing key fingerprints

---
 src/key.js               | 18 +++++++++++++-----
 src/packet/public_key.js | 10 +++++++++-
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/src/key.js b/src/key.js
index b669d2db..a66d92f6 100644
--- a/src/key.js
+++ b/src/key.js
@@ -39,6 +39,7 @@ import util from './util';
  * @param  {module:packet.List} packetlist The packets that form this key
  * @borrows module:packet.PublicKey#getKeyId as Key#getKeyId
  * @borrows module:packet.PublicKey#getFingerprint as Key#getFingerprint
+ * @borrows module:packet.PublicKey#hasSameFingerprintAs as Key#hasSameFingerprintAs
  * @borrows module:packet.PublicKey#getAlgorithmInfo as Key#getAlgorithmInfo
  * @borrows module:packet.PublicKey#getCreationTime as Key#getCreationTime
  * @borrows module:packet.PublicKey#isDecrypted as Key#isDecrypted
@@ -575,7 +576,7 @@ Key.prototype.update = async function(key) {
   if (await key.verifyPrimaryKey() === enums.keyStatus.invalid) {
     return;
   }
-  if (this.getFingerprint() !== key.getFingerprint()) {
+  if (!this.hasSameFingerprintAs(key)) {
     throw new Error('Key update method: fingerprints of keys not equal');
   }
   if (this.isPublic() && key.isPrivate()) {
@@ -583,7 +584,7 @@ Key.prototype.update = async function(key) {
     const equal = (this.subKeys.length === key.subKeys.length) &&
           (this.subKeys.every(destSubKey => {
             return key.subKeys.some(srcSubKey => {
-              return destSubKey.getFingerprint() === srcSubKey.getFingerprint();
+              return destSubKey.hasSameFingerprintAs(srcSubKey);
             });
           }));
     if (!equal) {
@@ -618,7 +619,7 @@ Key.prototype.update = async function(key) {
   await Promise.all(key.subKeys.map(async srcSubKey => {
     let found = false;
     await Promise.all(this.subKeys.map(async dstSubKey => {
-      if (srcSubKey.getFingerprint() === dstSubKey.getFingerprint()) {
+      if (dstSubKey.hasSameFingerprintAs(srcSubKey)) {
         await dstSubKey.update(srcSubKey, this.keyPacket);
         found = true;
       }
@@ -853,7 +854,7 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
     if (privateKey.isPublic()) {
       throw new Error('Need private key for signing');
     }
-    if (privateKey.getFingerprint() === primaryKey.getFingerprint()) {
+    if (privateKey.hasSameFingerprintAs(primaryKey)) {
       throw new Error('Not implemented for self signing');
     }
     const signingKey = await privateKey.getSigningKey();
@@ -1039,6 +1040,7 @@ User.prototype.update = async function(user, primaryKey) {
  * @classdesc Class that represents a subkey packet and the relevant signatures.
  * @borrows module:packet.PublicSubkey#getKeyId as SubKey#getKeyId
  * @borrows module:packet.PublicSubkey#getFingerprint as SubKey#getFingerprint
+ * @borrows module:packet.PublicSubkey#hasSameFingerprintAs as SubKey#hasSameFingerprintAs
  * @borrows module:packet.PublicSubkey#getAlgorithmInfo as SubKey#getAlgorithmInfo
  * @borrows module:packet.PublicSubkey#getCreationTime as SubKey#getCreationTime
  * @borrows module:packet.PublicSubkey#isDecrypted as SubKey#isDecrypted
@@ -1145,7 +1147,7 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
   if (await subKey.verify(primaryKey) === enums.keyStatus.invalid) {
     return;
   }
-  if (this.getFingerprint() !== subKey.getFingerprint()) {
+  if (!this.hasSameFingerprintAs(subKey)) {
     throw new Error('SubKey update method: fingerprints of subkeys not equal');
   }
   // key packet
@@ -1209,6 +1211,12 @@ SubKey.prototype.revoke = async function(primaryKey, {
     };
 });
 
+Key.prototype.hasSameFingerprintAs =
+SubKey.prototype.hasSameFingerprintAs =
+  function(other) {
+    return this.keyPacket.hasSameFingerprintAs(other.keyPacket || other);
+  };
+
 /**
  * Reads an unarmored OpenPGP key list and returns one or multiple key objects
  * @param {Uint8Array} data to be parsed
diff --git a/src/packet/public_key.js b/src/packet/public_key.js
index 023e26db..ca43ea05 100644
--- a/src/packet/public_key.js
+++ b/src/packet/public_key.js
@@ -229,10 +229,18 @@ PublicKey.prototype.getFingerprintBytes = function () {
  * Calculates the fingerprint of the key
  * @returns {String} A string containing the fingerprint in lowercase hex
  */
-PublicKey.prototype.getFingerprint = function () {
+PublicKey.prototype.getFingerprint = function() {
   return util.Uint8Array_to_hex(this.getFingerprintBytes());
 };
 
+/**
+ * Calculates whether two keys have the same fingerprint without actually calculating the fingerprint
+ * @returns {Boolean} Whether the two keys have the same version and public key data
+ */
+PublicKey.prototype.hasSameFingerprintAs = function(other) {
+  return this.version === other.version && util.equalsUint8Array(this.writePublicKey(), other.writePublicKey());
+};
+
 /**
  * Returns algorithm information
  * @returns {Object} An object of the form {algorithm: String, bits:int, curve:String}