Bug 1421501 - WebIDL and DOM for PrioEncoder r=edgar,hsivonen
☠☠ backed out by eee49d0f606b ☠ ☠
authorRobert Helmer <rhelmer@mozilla.com>
Wed, 20 Jun 2018 17:21:17 -0700
changeset 433033 cae4910806c7976aed9803f22e730c88f7703390
parent 433032 600a005d36137445d8bb3af5f3ff77bd53a9cb81
child 433034 ce2f6b168f041c540513f4d99542ed3870fb3680
push id68189
push userrhelmer@mozilla.com
push dateThu, 23 Aug 2018 13:56:45 +0000
treeherderautoland@cae4910806c7 [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)