Bug 902580 - Use securely random Message-ID to avoid fingerprinting. r=jcranmer
authorArthur Edelstein <arthuredelstein@gmail.com>
Mon, 09 Nov 2015 19:39:40 +0000
changeset 18722 a8573d4c6729
parent 18721 eb9ee96bc5d3
child 18723 982a9b6b77b2
push id11469
push useraleth@instantbird.org
push date2015-12-08 19:08 +0000
treeherdercomm-central@a8573d4c6729 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcranmer
bugs902580
Bug 902580 - Use securely random Message-ID to avoid fingerprinting. r=jcranmer
mailnews/compose/src/nsMsgCompUtils.cpp
--- a/mailnews/compose/src/nsMsgCompUtils.cpp
+++ b/mailnews/compose/src/nsMsgCompUtils.cpp
@@ -28,16 +28,18 @@
 #include "nsComponentManagerUtils.h"
 #include "nsMemory.h"
 #include "nsCRTGlue.h"
 #include <ctype.h>
 #include "mozilla/mailnews/Services.h"
 #include "mozilla/Services.h"
 #include "nsIMIMEInfo.h"
 #include "nsIMsgHeaderParser.h"
+#include "nsIRandomGenerator.h"
+#include "nsID.h"
 
 NS_IMPL_ISUPPORTS(nsMsgCompUtils, nsIMsgCompUtils)
 
 nsMsgCompUtils::nsMsgCompUtils()
 {
 }
 
 nsMsgCompUtils::~nsMsgCompUtils()
@@ -510,28 +512,39 @@ nsresult mime_generate_headers(nsIMsgCom
   return NS_OK;
 }
 
 #undef APPEND_BOOL // X-Mozilla-Draft-Info helper macro
 
 static void
 GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
 {
-  static bool      firstTime = true;
-
+  // Attempt to generate bytes from system entropy-based RNG.
+  nsCOMPtr<nsIRandomGenerator> randomGenerator(do_GetService("@mozilla.org/security/random-generator;1"));
+  MOZ_ASSERT(randomGenerator, "nsIRandomGenerator service not retrievable");
+  uint8_t *tempBuffer;
+  nsresult rv = randomGenerator->GenerateRandomBytes(len, &tempBuffer);
+  if (NS_SUCCEEDED(rv))
+  {
+    memcpy(buf, tempBuffer, len);
+    free(tempBuffer);
+    return;
+  }
+  // nsIRandomGenerator failed -- fall back to low entropy PRNG.
+  static bool firstTime = true;
   if (firstTime)
   {
     // Seed the random-number generator with current time so that
     // the numbers will be different every time we run.
     srand( (unsigned)PR_Now() );
     firstTime = false;
   }
 
   for( int32_t i = 0; i < len; i++ )
-    buf[i] = rand() % 10;
+    buf[i] = rand() % 256;
 }
 
 char
 *mime_make_separator(const char *prefix)
 {
   unsigned char rand_buf[13];
   GenerateGlobalRandomBytes(rand_buf, 12);
 
@@ -894,19 +907,16 @@ static bool isValidHost( const char* hos
       }
 
   return nullptr != host;
 }
 
 char *
 msg_generate_message_id (nsIMsgIdentity *identity)
 {
-  uint32_t now = (uint32_t)(PR_Now() / PR_USEC_PER_SEC);
-
-  uint32_t salt = 0;
   const char *host = 0;
 
   nsCString forcedFQDN;
   nsCString from;
   nsresult rv = NS_OK;
 
   rv = identity->GetCharAttribute("FQDN", forcedFQDN);
 
@@ -925,19 +935,25 @@ msg_generate_message_id (nsIMsgIdentity 
       ++host;
   }
 
   if (!isValidHost(host))
   /* If we couldn't find a valid host name to use, we can't generate a
      valid message ID, so bail, and let NNTP and SMTP generate them. */
     return 0;
 
-  GenerateGlobalRandomBytes((unsigned char *) &salt, sizeof(salt));
-  return PR_smprintf("<%lX.%lX@%s>",
-           (unsigned long) now, (unsigned long) salt, host);
+  // Generate 128-bit UUID for the local part. We use the high-entropy
+  // GenerateGlobalRandomBytes to make tracking more difficult.
+  nsID uuid;
+  GenerateGlobalRandomBytes((unsigned char*) &uuid, sizeof(nsID));
+  char uuidString[NSID_LENGTH];
+  uuid.ToProvidedString(uuidString);
+  // Drop first and last characters (curly braces).
+  uuidString[NSID_LENGTH - 2] = 0;
+  return PR_smprintf("<%s@%s>", uuidString + 1, host);
 }
 
 
 inline static bool is7bitCharset(const nsCString& charset)
 {
   // charset name is canonical (no worry about case-sensitivity)
   return Substring(charset, 0, 8).EqualsLiteral("ISO-2022-");
 }
@@ -1421,20 +1437,26 @@ msg_pick_real_name (nsMsgAttachmentHandl
         nsCOMPtr<nsIMIMEInfo> mimeInfo;
         nsCString mediaType(Substring(nonDataPart, 0, nonDataPart.FindChar(';')));
         mimeService->GetFromTypeAndExtension(mediaType, EmptyCString(), getter_AddRefs(mimeInfo));
         if (!mimeInfo)
           return;
         nsCString filename;
         nsCString extension;
         mimeInfo->GetPrimaryExtension(extension);
-        unsigned char filePrefix[10];
-        GenerateGlobalRandomBytes(filePrefix, 8);
+        unsigned char filePrefixBytes[8];
+        GenerateGlobalRandomBytes(filePrefixBytes, 8);
+        // Create a filename prefix with 16 lowercase letters,
+        // representing 8 bytes.
         for (int32_t i = 0; i < 8; i++)
-          filename.Append(filePrefix[i] + 'a');
+        {
+          // A pair of letters, each any of (a-p).
+          filename.Append((filePrefixBytes[i] & 0xF) + 'a');
+          filename.Append((filePrefixBytes[i] >> 4) + 'a');
+        }
         filename.Append('.');
         filename.Append(extension);
         attachment->m_realName = filename;
       }
     }
     else
     {
       /* Take the part of the file name after the last / or \ */