Bug 1634496 - Show additional details about signature and recipient keys of an OpenPGP message. r=PatrickBrunschwig a=wsmwk
authorKai Engert <kaie@kuix.de>
Mon, 20 Jul 2020 11:53:57 +0200
changeset 38962 07fd8331e66639ab2ce0090c0230da3eaf9bc0ad
parent 38961 67ac6cde43534e7d399fb9782249a20d3ae14822
child 38963 d65a21259dcf3c774aa4cc005d77cb340a4ec60c
push id2666
push userkaie@kuix.de
push dateFri, 24 Jul 2020 13:43:38 +0000
treeherdercomm-beta@d8d407bdc124 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPatrickBrunschwig, wsmwk
bugs1634496
Bug 1634496 - Show additional details about signature and recipient keys of an OpenPGP message. r=PatrickBrunschwig a=wsmwk Differential Revision: https://phabricator.services.mozilla.com/D84349
mail/extensions/openpgp/content/modules/RNP.jsm
mail/extensions/openpgp/content/modules/RNPLib.jsm
mail/extensions/openpgp/content/modules/mimeVerify.jsm
mail/extensions/openpgp/content/strings/msgReadStatus.ftl
mail/extensions/openpgp/content/ui/enigmailMessengerOverlay.js
mail/extensions/openpgp/content/ui/enigmailMsgHdrViewOverlay.js
mail/extensions/openpgp/content/ui/msgReadStatus.js
mail/extensions/openpgp/content/ui/msgReadStatus.xhtml
--- a/mail/extensions/openpgp/content/modules/RNP.jsm
+++ b/mail/extensions/openpgp/content/modules/RNP.jsm
@@ -658,16 +658,47 @@ var RNP = {
   },
 
   policyForbidsAlg(alg) {
     // TODO: implement policy
     // Currently, all algorithms are allowed
     return false;
   },
 
+  getKeyIdsFromRecipHandle(recip_handle, resultRecipAndPrimary) {
+    resultRecipAndPrimary.keyId = "";
+    resultRecipAndPrimary.primaryKeyId = "";
+
+    let c_key_id = new ctypes.char.ptr();
+    if (RNPLib.rnp_recipient_get_keyid(recip_handle, c_key_id.address())) {
+      throw new Error("rnp_recipient_get_keyid failed");
+    }
+    let recip_key_id = c_key_id.readString();
+    resultRecipAndPrimary.keyId = recip_key_id;
+    RNPLib.rnp_buffer_destroy(c_key_id);
+
+    let recip_key_handle = this.getKeyHandleByKeyIdOrFingerprint(
+      RNPLib.ffi,
+      "0x" + recip_key_id
+    );
+    if (!recip_key_handle.isNull()) {
+      let primary_signer_handle = this.getPrimaryKeyHandleIfSub(
+        RNPLib.ffi,
+        recip_key_handle
+      );
+      if (!primary_signer_handle.isNull()) {
+        resultRecipAndPrimary.primaryKeyId = this.getKeyIDFromHandle(
+          primary_signer_handle
+        );
+        RNPLib.rnp_key_handle_destroy(primary_signer_handle);
+      }
+      RNPLib.rnp_key_handle_destroy(recip_key_handle);
+    }
+  },
+
   async decrypt(encrypted, options, alreadyDecrypted = false) {
     let input_from_memory = new RNPLib.rnp_input_t();
 
     var tmp_array = ctypes.char.array()(encrypted);
     var encrypted_array = ctypes.cast(
       tmp_array,
       ctypes.uint8_t.array(encrypted.length)
     );
@@ -686,29 +717,36 @@ var RNP = {
 
     let result = {};
     result.decryptedData = "";
     result.statusFlags = 0;
     result.extStatusFlags = 0;
 
     result.userId = "";
     result.keyId = "";
-    result.encToDetails = "";
+    result.encToDetails = {};
+    result.encToDetails.myRecipKey = {};
+    result.encToDetails.allRecipKeys = [];
+
+    if (alreadyDecrypted) {
+      result.encToDetails = options.encToDetails;
+    }
 
     let verify_op = new RNPLib.rnp_op_verify_t();
     result.exitCode = RNPLib.rnp_op_verify_create(
       verify_op.address(),
       RNPLib.ffi,
       input_from_memory,
       output_to_memory
     );
 
     result.exitCode = RNPLib.rnp_op_verify_execute(verify_op);
 
     let rnpCannotDecrypt = false;
+    let queryAllEncryptionRecipients = false;
 
     let useDecodedData;
     let processSignature;
     switch (result.exitCode) {
       case RNPLib.RNP_SUCCESS:
         useDecodedData = true;
         processSignature = true;
         break;
@@ -721,22 +759,24 @@ var RNP = {
         useDecodedData = true;
         processSignature = false;
         result.statusFlags |= EnigmailConstants.EXPIRED_SIGNATURE;
         break;
       case RNPLib.RNP_ERROR_DECRYPT_FAILED:
         rnpCannotDecrypt = true;
         useDecodedData = false;
         processSignature = false;
+        queryAllEncryptionRecipients = true;
         result.statusFlags |= EnigmailConstants.DECRYPTION_FAILED;
         break;
       case RNPLib.RNP_ERROR_NO_SUITABLE_KEY:
         rnpCannotDecrypt = true;
         useDecodedData = false;
         processSignature = false;
+        queryAllEncryptionRecipients = true;
         result.statusFlags |=
           EnigmailConstants.DECRYPTION_FAILED | EnigmailConstants.NO_SECKEY;
         break;
       default:
         useDecodedData = false;
         processSignature = false;
         console.debug(
           "rnp_op_verify_execute returned unexpected: " + result.exitCode
@@ -769,16 +809,18 @@ var RNP = {
         if (!validIntegrityProtection) {
           useDecodedData = false;
           result.statusFlags |=
             EnigmailConstants.MISSING_MDC | EnigmailConstants.DECRYPTION_FAILED;
         } else if (mode == "null" || this.policyForbidsAlg(cipher)) {
           // don't indicate decryption, because a non-protecting or insecure cipher was used
           result.statusFlags |= EnigmailConstants.UNKNOWN_ALGO;
         } else {
+          queryAllEncryptionRecipients = true;
+
           let recip_handle = new RNPLib.rnp_recipient_handle_t();
           let rv = RNPLib.rnp_op_verify_get_used_recipient(
             verify_op,
             recip_handle.address()
           );
           if (rv) {
             throw new Error("rnp_op_verify_get_used_recipient failed");
           }
@@ -787,47 +829,55 @@ var RNP = {
           rv = RNPLib.rnp_recipient_get_alg(recip_handle, c_alg.address());
           if (rv) {
             throw new Error("rnp_recipient_get_alg failed");
           }
 
           if (this.policyForbidsAlg(c_alg.readString())) {
             result.statusFlags |= EnigmailConstants.UNKNOWN_ALGO;
           } else {
-            let c_key_id = new ctypes.char.ptr();
-            rv = RNPLib.rnp_recipient_get_keyid(
+            this.getKeyIdsFromRecipHandle(
               recip_handle,
-              c_key_id.address()
+              result.encToDetails.myRecipKey
             );
-            if (rv) {
-              throw new Error("rnp_recipient_get_keyid failed");
-            }
-            let recip_key_id = c_key_id.readString();
-
-            let recip_key_handle = this.getKeyHandleByKeyIdOrFingerprint(
-              RNPLib.ffi,
-              "0x" + recip_key_id
-            );
-            let primary_signer_handle = this.getPrimaryKeyHandleIfSub(
-              RNPLib.ffi,
-              recip_key_handle
-            );
-            if (!primary_signer_handle.isNull()) {
-              recip_key_id = this.getKeyIDFromHandle(primary_signer_handle);
-              RNPLib.rnp_key_handle_destroy(primary_signer_handle);
-            }
-            RNPLib.rnp_key_handle_destroy(recip_key_handle);
-
-            result.encToDetails = recip_key_id;
             result.statusFlags |= EnigmailConstants.DECRYPTION_OKAY;
           }
         }
       }
     }
 
+    if (queryAllEncryptionRecipients) {
+      let all_recip_count = new ctypes.size_t();
+      if (
+        RNPLib.rnp_op_verify_get_recipient_count(
+          verify_op,
+          all_recip_count.address()
+        )
+      ) {
+        throw new Error("rnp_op_verify_get_recipient_count failed");
+      }
+      if (all_recip_count.value > 1) {
+        for (let recip_i = 0; recip_i < all_recip_count.value; recip_i++) {
+          let other_recip_handle = new RNPLib.rnp_recipient_handle_t();
+          if (
+            RNPLib.rnp_op_verify_get_recipient_at(
+              verify_op,
+              recip_i,
+              other_recip_handle.address()
+            )
+          ) {
+            throw new Error("rnp_op_verify_get_recipient_at failed");
+          }
+          let encTo = {};
+          this.getKeyIdsFromRecipHandle(other_recip_handle, encTo);
+          result.encToDetails.allRecipKeys.push(encTo);
+        }
+      }
+    }
+
     if (useDecodedData) {
       let result_buf = new ctypes.uint8_t.ptr();
       let result_len = new ctypes.size_t();
       let rv = RNPLib.rnp_output_memory_get_buf(
         output_to_memory,
         result_buf.address(),
         result_len.address(),
         false
@@ -881,16 +931,17 @@ var RNP = {
         //       and set result.decryptKey*
         //       It isn't obvious how to do that with GPGME, because
         //       gpgme_op_decrypt_result provides the list of all the
         //       encryption keys, only.
 
         // The result may still contain wrapping like compression,
         // and optional signature data. Recursively call ourselves
         // to perform the remaining processing.
+        options.encToDetails = result.encToDetails;
         return RNP.decrypt(r2.decryptedData, options, true);
       }
     }
 
     return result;
   },
 
   async getVerifyDetails(ffi, fromAddr, verify_op, result) {
--- a/mail/extensions/openpgp/content/modules/RNPLib.jsm
+++ b/mail/extensions/openpgp/content/modules/RNPLib.jsm
@@ -1118,16 +1118,25 @@ function enableRNPLibJS() {
     rnp_op_verify_get_used_recipient: librnp.declare(
       "rnp_op_verify_get_used_recipient",
       abi,
       rnp_result_t,
       rnp_op_verify_t,
       rnp_recipient_handle_t.ptr
     ),
 
+    rnp_op_verify_get_recipient_at: librnp.declare(
+      "rnp_op_verify_get_recipient_at",
+      abi,
+      rnp_result_t,
+      rnp_op_verify_t,
+      ctypes.size_t,
+      rnp_recipient_handle_t.ptr
+    ),
+
     rnp_recipient_get_keyid: librnp.declare(
       "rnp_recipient_get_keyid",
       abi,
       rnp_result_t,
       rnp_recipient_handle_t,
       ctypes.char.ptr.ptr
     ),
 
--- a/mail/extensions/openpgp/content/modules/mimeVerify.jsm
+++ b/mail/extensions/openpgp/content/modules/mimeVerify.jsm
@@ -600,35 +600,17 @@ MimeVerify.prototype = {
           "\n" +
           data +
           "\n--" +
           bound +
           "--\n";
       }
     }
 
-    if ("outputDecryptedData" in this.mimeSvc) {
-      // TB >= 57
-      this.mimeSvc.outputDecryptedData(data, data.length);
-    } else {
-      let gConv = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
-        Ci.nsIStringInputStream
-      );
-      gConv.setData(data, data.length);
-      try {
-        this.mimeSvc.onStartRequest(null, null);
-        this.mimeSvc.onDataAvailable(null, null, gConv, 0, data.length);
-        this.mimeSvc.onStopRequest(null, null, 0);
-      } catch (ex) {
-        EnigmailLog.ERROR(
-          "mimeVerify.jsm: returnData(): mimeSvc.onDataAvailable failed:\n" +
-            ex.toString()
-        );
-      }
-    }
+    this.mimeSvc.outputDecryptedData(data, data.length);
   },
 
   setMsgWindow(msgWindow, msgUriSpec) {
     EnigmailLog.DEBUG("mimeVerify.jsm: setMsgWindow: " + msgUriSpec + "\n");
 
     if (!this.msgWindow) {
       this.msgWindow = msgWindow;
       this.msgUriSpec = msgUriSpec;
--- a/mail/extensions/openpgp/content/strings/msgReadStatus.ftl
+++ b/mail/extensions/openpgp/content/strings/msgReadStatus.ftl
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 openpgp-view-signer-key =
     .label = View signer key
 openpgp-view-your-encryption-key =
-    .label = View your recipient key
+    .label = View your decryption key
 openpgp-openpgp = OpenPGP
 
 openpgp-no-sig = No Digital Signature
 openpgp-uncertain-sig = Uncertain Digital Signature
 openpgp-invalid-sig = Invalid Digital Signature
 openpgp-good-sig = Good Digital Signature
 
 openpgp-sig-uncertain-no-key = This message contains a digital signature, but it is uncertain if it is correct. To verify the signature, you need to obtain a copy of the sender's public key.
@@ -18,9 +18,17 @@ openpgp-sig-uncertain-uid-mismatch = Thi
 openpgp-sig-uncertain-not-accepted = This message contains a digital signature, but you haven't yet decided if the signer's key is acceptable to you.
 openpgp-sig-invalid-rejected = This message contains a digital signature, but you have previously decided to reject the signer key.
 openpgp-sig-invalid-technical-problem = This message contains a digital signature, but a technical error was detected. Either the message has been corrupted, or the message has been modified by someone else.
 openpgp-sig-valid-unverified = This message includes a valid digital signature from a key that you have already accepted. However, you have not yet verified that the key is really owned by the sender.
 openpgp-sig-valid-verified = This message includes a valid digital signature from a verified key.
 openpgp-sig-valid-own-key = This message includes a valid digital signature from your personal key.
 
 openpgp-sig-key-id = Signer key ID: { $key }
-openpgp-enc-key-id = Recipient key ID: { $key }
+openpgp-sig-key-id-with-subkey-id = Signer key ID: { $key } (Sub key ID: { $subkey })
+
+openpgp-enc-key-id = Your decryption key ID: { $key }
+openpgp-enc-key-with-subkey-id = Your decryption key ID: { $key } (Sub key ID: { $subkey })
+
+openpgp-unknown-key-id = Unknown key
+
+openpgp-other-enc-additional-key-ids = In addition, the message was encrypted to the owners of the following keys:
+openpgp-other-enc-all-key-ids = The message was encrypted to the owners of the following keys:
--- a/mail/extensions/openpgp/content/ui/enigmailMessengerOverlay.js
+++ b/mail/extensions/openpgp/content/ui/enigmailMessengerOverlay.js
@@ -1001,17 +1001,17 @@ Enigmail.msg = {
               EnigmailConstants.POSSIBLE_PGPMIME,
               0, // exitCode, statusFlags
               0,
               "",
               "", // keyId, userId
               "", // sigDetails
               await l10n.formatValue("possibly-pgp-mime"), // infoMsg
               null, // blockSeparation
-              "", // encToDetails
+              null, // encToDetails
               null
             ); // xtraStatus
           }
         } else if (!isAuto) {
           Enigmail.msg.messageReload(false);
         }
         return;
       }
--- a/mail/extensions/openpgp/content/ui/enigmailMsgHdrViewOverlay.js
+++ b/mail/extensions/openpgp/content/ui/enigmailMsgHdrViewOverlay.js
@@ -95,23 +95,25 @@ if (!Enigmail) {
 Enigmail.hdrView = {
   lastEncryptedMsgKey: null,
   lastEncryptedUri: null,
   flexbuttonAction: null,
 
   msgSignatureState: EnigmailConstants.MSG_SIG_NONE,
   msgEncryptionState: EnigmailConstants.MSG_ENC_NONE,
   msgSignatureKeyId: "",
-  msgEncryptionKeyId: "",
+  msgEncryptionKeyId: null,
+  msgEncryptionAllKeyIds: null,
 
   reset() {
     this.msgSignatureState = EnigmailConstants.MSG_SIG_NONE;
     this.msgEncryptionState = EnigmailConstants.MSG_ENC_NONE;
     this.msgSignatureKeyId = "";
-    this.msgEncryptionKeyId = "";
+    this.msgEncryptionKeyId = null;
+    this.msgEncryptionAllKeyIds = null;
   },
 
   hdrViewLoad() {
     EnigmailLog.DEBUG("enigmailMsgHdrViewOverlay.js: this.hdrViewLoad\n");
 
     // THE FOLLOWING OVERRIDES CODE IN msgHdrViewOverlay.js
     // which wouldn't work otherwise
 
@@ -185,16 +187,17 @@ Enigmail.hdrView = {
   },
 
   viewOpenpgpInfo() {
     let params = {};
     params.msgSignatureState = this.msgSignatureState;
     params.msgEncryptionState = this.msgEncryptionState;
     params.msgSignatureKeyId = this.msgSignatureKeyId;
     params.msgEncryptionKeyId = this.msgEncryptionKeyId;
+    params.msgEncryptionAllKeyIds = this.msgEncryptionAllKeyIds;
     params.from = "";
 
     if ("from" in currentHeaderData) {
       try {
         params.from = EnigmailFuncs.stripEmail(
           currentHeaderData.from.headerValue
         ).toLowerCase();
       } catch (ex) {
@@ -352,17 +355,21 @@ Enigmail.hdrView = {
     // TODO: visualize the following signature attributes,
     // cross-check with corresponding email attributes
     // - date
     // - signer uid
     // - signer key
     // - signing and hash alg
 
     this.msgSignatureKeyId = keyId;
-    this.msgEncryptionKeyId = encToDetails;
+
+    if (encToDetails) {
+      this.msgEncryptionKeyId = encToDetails.myRecipKey;
+      this.msgEncryptionAllKeyIds = encToDetails.allRecipKeys;
+    }
 
     let tmp = {
       statusFlags,
       extStatusFlags,
       keyId,
       userId,
       msgSigned,
       blockSeparation,
@@ -1143,17 +1150,18 @@ Enigmail.hdrView = {
         }
         if (this.hasUnauthenticatedParts(mimePartNumber)) {
           EnigmailLog.DEBUG(
             "enigmailMsgHdrViewOverlay.js: updateSecurityStatus: found unauthenticated part\n"
           );
           statusFlags |= EnigmailConstants.PARTIALLY_PGP;
         }
 
-        let encToDetails = "";
+        let encToDetails = null;
+
         if (extraDetails && extraDetails.length > 0) {
           try {
             let o = JSON.parse(extraDetails);
             if ("encryptedTo" in o) {
               encToDetails = o.encryptedTo;
             }
           } catch (x) {
             console.debug(x);
--- a/mail/extensions/openpgp/content/ui/msgReadStatus.js
+++ b/mail/extensions/openpgp/content/ui/msgReadStatus.js
@@ -12,16 +12,18 @@ var { EnigmailWindows } = ChromeUtils.im
 );
 var { EnigmailKeyRing } = ChromeUtils.import(
   "chrome://openpgp/content/modules/keyRing.jsm"
 );
 
 let gSigKeyId;
 let gEncKeyId;
 
+let myl10n = new Localization(["messenger/openpgp/msgReadStatus.ftl"], true);
+
 function setText(id, value) {
   var element = document.getElementById(id);
   if (!element) {
     return;
   }
   if (element.hasChildNodes()) {
     element.firstElementChild.remove();
   }
@@ -147,40 +149,116 @@ function onLoad() {
   if (hasAnyEnc || hasAnySig) {
     document.getElementById("techLabel").collapsed = false;
   }
 
   document.getElementById("encryptionLabel").value = encInfoLabel;
   setText("encryptionExplanation", encInfo);
 
   if (params.msgSignatureKeyId) {
+    let sigKeyInfo = EnigmailKeyRing.getKeyById(params.msgSignatureKeyId);
+    let signedBySubkey =
+      sigKeyInfo && sigKeyInfo.keyId != params.msgSignatureKeyId;
+
     let idElement = document.getElementById("signatureKeyId");
     idElement.collapsed = false;
-    document.l10n.setAttributes(idElement, "openpgp-sig-key-id", {
-      key: "0x" + params.msgSignatureKeyId,
-    });
 
-    if (EnigmailKeyRing.getKeyById(params.msgSignatureKeyId)) {
+    if (signedBySubkey) {
+      document.l10n.setAttributes(
+        idElement,
+        "openpgp-sig-key-id-with-subkey-id",
+        {
+          key: "0x" + sigKeyInfo.keyId,
+          subkey: "0x" + params.msgSignatureKeyId,
+        }
+      );
+    } else {
+      document.l10n.setAttributes(idElement, "openpgp-sig-key-id", {
+        key: "0x" + params.msgSignatureKeyId,
+      });
+    }
+
+    if (sigKeyInfo) {
       document.getElementById("viewSignatureKey").collapsed = false;
       gSigKeyId = params.msgSignatureKeyId;
     }
   }
 
-  if (params.msgEncryptionKeyId) {
+  let myIdToSkipInList = "";
+  if (params.msgEncryptionKeyId && params.msgEncryptionKeyId.keyId) {
+    myIdToSkipInList = params.msgEncryptionKeyId.keyId;
+
+    // If we were given a separate primaryKeyId, it means that
+    // keyId is a subkey.
+    let havePrimaryId = !!params.msgEncryptionKeyId.primaryKeyId;
     let idElement = document.getElementById("encryptionKeyId");
     idElement.collapsed = false;
-    document.l10n.setAttributes(idElement, "openpgp-enc-key-id", {
-      key: "0x" + params.msgEncryptionKeyId,
-    });
 
-    if (EnigmailKeyRing.getKeyById(params.msgEncryptionKeyId)) {
+    if (havePrimaryId) {
+      document.l10n.setAttributes(idElement, "openpgp-enc-key-with-subkey-id", {
+        key: "0x" + params.msgEncryptionKeyId.primaryKeyId,
+        subkey: "0x" + params.msgEncryptionKeyId.keyId,
+      });
+    } else {
+      document.l10n.setAttributes(idElement, "openpgp-enc-key-id", {
+        key: "0x" + params.msgEncryptionKeyId.keyId,
+      });
+    }
+
+    if (EnigmailKeyRing.getKeyById(params.msgEncryptionKeyId.keyId)) {
       document.getElementById("viewEncryptionKey").collapsed = false;
-      gEncKeyId = params.msgEncryptionKeyId;
+      gEncKeyId = params.msgEncryptionKeyId.keyId;
     }
   }
+
+  let otherKeysLabel = "openpgp-other-enc-all-key-ids";
+
+  if (params.msgEncryptionAllKeyIds) {
+    let list = "";
+    for (let key of params.msgEncryptionAllKeyIds) {
+      if (key.keyId == myIdToSkipInList) {
+        continue;
+      }
+
+      let idStr = "";
+
+      let havePrimaryId2 = !!key.primaryKeyId;
+      let idForSearching = havePrimaryId2 ? key.primaryKeyId : key.keyId;
+
+      let keyInfo = EnigmailKeyRing.getKeyById(idForSearching);
+      if (keyInfo) {
+        idStr += keyInfo.userId;
+      } else {
+        idStr += myl10n.formatValueSync("openpgp-unknown-key-id");
+      }
+
+      if (havePrimaryId2) {
+        idStr += " 0x" + key.primaryKeyId + " (0x" + key.keyId + ")";
+      } else {
+        idStr += " 0x" + key.keyId;
+      }
+
+      if (list) {
+        list += ", ";
+      }
+      list += idStr;
+      list += "\n";
+    }
+
+    if (list) {
+      document.getElementById("otherEncryptionKeys").collapsed = false;
+      setText("otherEncryptionKeysList", list);
+    }
+
+    if (myIdToSkipInList) {
+      otherKeysLabel = "openpgp-other-enc-additional-key-ids";
+    }
+  }
+
+  setText("otherLabel", myl10n.formatValueSync(otherKeysLabel));
 }
 /* eslint-enable complexity */
 
 function viewKeyHelper(keyId) {
   EnigmailWindows.openKeyDetails(window, keyId, false);
 }
 
 function viewSignatureKey() {
--- a/mail/extensions/openpgp/content/ui/msgReadStatus.xhtml
+++ b/mail/extensions/openpgp/content/ui/msgReadStatus.xhtml
@@ -7,17 +7,17 @@
 <?xml-stylesheet href="chrome://messenger/skin/openpgp/msgReadStatus.css" type="text/css"?>
 
 <!DOCTYPE window [
 <!ENTITY % smimeDTD SYSTEM "chrome://messenger-smime/locale/msgReadSecurityInfo.dtd"> %smimeDTD;
 ]>
 
 <window title="&status.label;"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
-        style="width: 40em; height: 22em;"
+        style="width: 50em; height: 30em;"
         onload="onLoad();">
 <dialog id="msgReadStatus"
         buttons="accept">
   <script src="chrome://openpgp/content/ui/msgReadStatus.js"/>
 
   <linkset>
     <html:link rel="localization" href="messenger/openpgp/msgReadStatus.ftl"/>
   </linkset>
@@ -43,11 +43,15 @@
     <description id="encryptionExplanation"/>
     <vbox id="encryptionKey">
       <label id="encryptionKeyId" collapsed="true"/>
       <hbox>
         <button id="viewEncryptionKey" data-l10n-id="openpgp-view-your-encryption-key"
                 oncommand="viewEncryptionKey()" collapsed="true"/>
       </hbox>
     </vbox>
+    <vbox id="otherEncryptionKeys" flex="1" collapsed="true">
+      <description id="otherLabel"/>
+      <description id="otherEncryptionKeysList"/>
+    </vbox>
   </vbox>
 </dialog>
 </window>