Skip to content

Commit

Permalink
Bug 1421501 - WebIDL and DOM for PrioEncoder r=edgar,hsivonen
Browse files Browse the repository at this point in the history
MozReview-Commit-ID: L8htRm3J1mZ

--HG--
extra : rebase_source : bba7f747583412677db1811400b86f1a335ef688
  • Loading branch information
rhelmer committed Jun 21, 2018
1 parent 5e9fa7f commit 3091853
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 0 deletions.
5 changes: 5 additions & 0 deletions browser/app/profile/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -1744,3 +1744,8 @@ 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");
1 change: 1 addition & 0 deletions dom/bindings/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ LOCAL_INCLUDES += [
'/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
Expand Down
22 changes: 22 additions & 0 deletions dom/chrome-webidl/PrioEncoder.webidl
Original file line number Diff line number Diff line change
@@ -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;
};
1 change: 1 addition & 0 deletions dom/chrome-webidl/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ WEBIDL_FILES = [
'MozStorageStatementParams.webidl',
'MozStorageStatementRow.webidl',
'PrecompiledScript.webidl',
'PrioEncoder.webidl',
'PromiseDebugging.webidl',
'StructuredCloneHolder.webidl',
'WebExtensionContentScript.webidl',
Expand Down
1 change: 1 addition & 0 deletions dom/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ DIRS += [
'notification',
'offline',
'power',
'prio',
'push',
'quota',
'security',
Expand Down
180 changes: 180 additions & 0 deletions dom/prio/PrioEncoder.cpp
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions dom/prio/PrioEncoder.h
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions dom/prio/moz.build
Original file line number Diff line number Diff line change
@@ -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'
16 changes: 16 additions & 0 deletions mfbt/TextUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ IsAsciiDigit(Char 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].
*
Expand Down

0 comments on commit 3091853

Please sign in to comment.