Bug 1421501 - WebIDL and DOM for PrioEncoder r=edgar,hsivonen
☠☠ backed out by 6d71f0af2bcc ☠ ☠
authorRobert Helmer <rhelmer@mozilla.com>
Wed, 20 Jun 2018 17:21:17 -0700
changeset 433228 844232d77d0d8696d580663a5c9e3e8398aa93f6
parent 433227 3affc66728b0aea1764ef9f7dc194a579a61a05b
child 433229 492f05d220b1495339691c2bf6c74c106a89b867
push id107006
push usertoros@mozilla.com
push dateFri, 24 Aug 2018 09:52:56 +0000
treeherdermozilla-inbound@190b827aaa2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedgar, hsivonen
bugs1421501
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1421501 - WebIDL and DOM for PrioEncoder r=edgar,hsivonen MozReview-Commit-ID: L8htRm3J1mZ
browser/app/profile/firefox.js
dom/bindings/moz.build
dom/chrome-webidl/PrioEncoder.webidl
dom/chrome-webidl/moz.build
dom/moz.build
dom/prio/PrioEncoder.cpp
dom/prio/PrioEncoder.h
dom/prio/moz.build
mfbt/TextUtils.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1739,8 +1739,13 @@ pref("app.shield.optoutstudies.enabled",
 #endif
 
 // Savant Shield study preferences
 pref("shield.savant.enabled", false);
 pref("shield.savant.loglevel", "warn");
 
 // Multi-lingual preferences
 pref("intl.multilingual.enabled", false);
+
+// Prio preferences
+// Curve25519 public keys for Prio servers
+pref("prio.publicKeyA", "35AC1C7576C7C6EDD7FED6BCFC337B34D48CB4EE45C86BEEFB40BD8875707733");
+pref("prio.publicKeyB", "26E6674E65425B823F1F1D5F96E3BB3EF9E406EC7FBA7DEF8B08A35DD135AF50");
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -90,16 +90,17 @@ LOCAL_INCLUDES += [
     '/layout/generic',
     '/layout/style',
     '/layout/xul/tree',
     '/media/mtransport',
     '/media/webrtc/',
     '/media/webrtc/signaling/src/common/time_profiling',
     '/media/webrtc/signaling/src/peerconnection',
     '/media/webrtc/trunk/',
+    '/third_party/msgpack/include',
 ]
 
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
 DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
 
 UNIFIED_SOURCES += [
     'BindingUtils.cpp',
     'CallbackInterface.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/PrioEncoder.webidl
@@ -0,0 +1,22 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[ChromeOnly, Exposed=(Window,System)]
+namespace PrioEncoder {
+  [Throws, NewObject]
+  Promise<PrioEncodedData> encode(ByteString batchID, PrioParams params);
+};
+
+dictionary PrioParams {
+  required boolean startupCrashDetected;
+  required boolean safeModeUsage;
+  required boolean browserIsUserDefault;
+};
+
+dictionary PrioEncodedData {
+  Uint8Array a;
+  Uint8Array b;
+};
\ No newline at end of file
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -38,16 +38,17 @@ WEBIDL_FILES = [
     'MatchPattern.webidl',
     'MessageManager.webidl',
     'MozDocumentObserver.webidl',
     'MozSharedMap.webidl',
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
     'PrecompiledScript.webidl',
+    'PrioEncoder.webidl',
     'PromiseDebugging.webidl',
     'StructuredCloneHolder.webidl',
     'WebExtensionContentScript.webidl',
     'WebExtensionPolicy.webidl',
     'XULFrameElement.webidl',
     'XULScrollElement.webidl'
 ]
 
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -58,16 +58,17 @@ DIRS += [
     'jsurl',
     'asmjscache',
     'mathml',
     'media',
     'midi',
     'notification',
     'offline',
     'power',
+    'prio',
     'push',
     'quota',
     'security',
     'storage',
     'svg',
     'locales',
     'network',
     'permission',
new file mode 100644
--- /dev/null
+++ b/dom/prio/PrioEncoder.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/Services.h"
+#include "mozilla/TextUtils.h"
+
+#include "PrioEncoder.h"
+
+namespace mozilla {
+namespace dom {
+
+/* static */ StaticRefPtr<PrioEncoder> PrioEncoder::sSingleton;
+
+/* static */ PublicKey PrioEncoder::sPublicKeyA = nullptr;
+/* static */ PublicKey PrioEncoder::sPublicKeyB = nullptr;
+
+PrioEncoder::PrioEncoder() = default;
+PrioEncoder::~PrioEncoder()
+{
+  if (sPublicKeyA) {
+    PublicKey_clear(sPublicKeyA);
+    sPublicKeyA = nullptr;
+  }
+
+  if (sPublicKeyB) {
+    PublicKey_clear(sPublicKeyB);
+    sPublicKeyB = nullptr;
+  }
+
+  Prio_clear();
+}
+
+/* static */
+already_AddRefed<Promise>
+PrioEncoder::Encode(GlobalObject& aGlobal, const nsCString& aBatchID, const PrioParams& aPrioParams, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+
+  if (!global) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  SECStatus prio_rv = SECSuccess;
+
+  if (!sSingleton) {
+    sSingleton = new PrioEncoder();
+
+    ClearOnShutdown(&sSingleton);
+
+    Prio_init();
+
+    nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyA;
+    nsresult rv = Preferences::GetCString("prio.publicKeyA", prioKeyA);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyB;
+    rv = Preferences::GetCString("prio.publicKeyB", prioKeyB);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    // Check that both public keys are of the right length
+    // and contain only hex digits 0-9a-fA-f
+    if (!PrioEncoder::IsValidHexPublicKey(prioKeyA)
+        || !PrioEncoder::IsValidHexPublicKey(prioKeyB))  {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    prio_rv = PublicKey_import_hex(&sPublicKeyA, reinterpret_cast<const unsigned char*>(prioKeyA.BeginReading()), CURVE25519_KEY_LEN_HEX);
+    if (prio_rv != SECSuccess) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    prio_rv = PublicKey_import_hex(&sPublicKeyB, reinterpret_cast<const unsigned char*>(prioKeyB.BeginReading()), CURVE25519_KEY_LEN_HEX);
+    if (prio_rv != SECSuccess) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+  }
+
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+
+  bool dataItems[] = {
+    aPrioParams.mStartupCrashDetected,
+    aPrioParams.mSafeModeUsage,
+    aPrioParams.mBrowserIsUserDefault
+  };
+
+  PrioConfig prioConfig = PrioConfig_new(mozilla::ArrayLength(dataItems), sPublicKeyA, sPublicKeyB, reinterpret_cast<const unsigned char*>(aBatchID.BeginReading()), aBatchID.Length());
+
+  if (!prioConfig) {
+    promise->MaybeReject(NS_ERROR_FAILURE);
+    return promise.forget();
+  }
+
+  auto configGuard = MakeScopeExit([&] {
+    PrioConfig_clear(prioConfig);
+  });
+
+  unsigned char* forServerA = nullptr;
+  unsigned int lenA = 0;
+  unsigned char* forServerB = nullptr;
+  unsigned int lenB = 0;
+
+  prio_rv = PrioClient_encode(prioConfig, dataItems, &forServerA, &lenA, &forServerB, &lenB);
+
+  // Package the data into the dictionary
+  PrioEncodedData data;
+
+  nsTArray<uint8_t> arrayForServerA;
+  nsTArray<uint8_t> arrayForServerB;
+
+  if (!arrayForServerA.AppendElements(reinterpret_cast<uint8_t*>(forServerA), lenA, fallible)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+
+  free(forServerA);
+
+  if (!arrayForServerB.AppendElements(reinterpret_cast<uint8_t*>(forServerB), lenB, fallible)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+
+  free(forServerB);
+
+  JS::Rooted<JS::Value> valueA(aGlobal.Context());
+  if (!ToJSValue(aGlobal.Context(), TypedArrayCreator<Uint8Array>(arrayForServerA), &valueA)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+  data.mA.Construct().Init(&valueA.toObject());
+
+  JS::Rooted<JS::Value> valueB(aGlobal.Context());
+  if (!ToJSValue(aGlobal.Context(), TypedArrayCreator<Uint8Array>(arrayForServerB), &valueB)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+  data.mB.Construct().Init(&valueB.toObject());
+
+  if (prio_rv != SECSuccess) {
+    promise->MaybeReject(NS_ERROR_FAILURE);
+    return promise.forget();
+  }
+
+  promise->MaybeResolve(data);
+
+  return promise.forget();
+}
+
+bool
+PrioEncoder::IsValidHexPublicKey(mozilla::Span<const char> aStr)
+{
+  if (aStr.Length() != CURVE25519_KEY_LEN_HEX) {
+    return false;
+  }
+
+  for (auto c : aStr) {
+    if (!IsAsciiHexDigit(c)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/prio/PrioEncoder.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_PrioEncoder_h
+#define mozilla_dom_PrioEncoder_h
+
+#include "mozilla/dom/PrioEncoderBinding.h"
+
+#include "mprio.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class PrioEncoder
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(PrioEncoder)
+
+  static already_AddRefed<Promise>
+  Encode(GlobalObject& aGlobal, const nsCString& aBatchID, const PrioParams& aPrioParams, ErrorResult& aRv);
+
+private:
+  PrioEncoder();
+  ~PrioEncoder();
+
+  static PublicKey
+  sPublicKeyA;
+
+  static PublicKey
+  sPublicKeyB;
+
+  static StaticRefPtr<PrioEncoder>
+  sSingleton;
+
+  static bool
+  IsValidHexPublicKey(mozilla::Span<const char>);
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_PrioEncoder_h
new file mode 100644
--- /dev/null
+++ b/dom/prio/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "DOM")
+
+LOCAL_INCLUDES += [
+    '/third_party/msgpack/include'
+]
+
+EXPORTS.mozilla.dom += [
+    'PrioEncoder.h',
+]
+
+UNIFIED_SOURCES += [
+    'PrioEncoder.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/mfbt/TextUtils.h
+++ b/mfbt/TextUtils.h
@@ -116,16 +116,32 @@ constexpr bool
 IsAsciiDigit(Char aChar)
 {
   using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
   auto uc = static_cast<UnsignedChar>(aChar);
   return '0' <= uc && uc <= '9';
 }
 
 /**
+ * Returns true iff |aChar| matches [0-9a-fA-F].
+ *
+ * This function is basically isxdigit, but guaranteed to be only for ASCII.
+ */
+template<typename Char>
+constexpr bool
+IsAsciiHexDigit(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+  return ('0' <= uc && uc <= '9') ||
+         ('a' <= uc && uc <= 'f') ||
+         ('A' <= uc && uc <= 'F');
+}
+
+/**
  * Returns true iff |aChar| matches [a-zA-Z0-9].
  *
  * This function is basically what you thought isalnum was, except its behavior
  * doesn't depend on the user's current locale.
  */
 template<typename Char>
 constexpr bool
 IsAsciiAlphanumeric(Char aChar)