Bug 1421501 - WebIDL and DOM for PrioEncoder r=edgar,hsivonen

MozReview-Commit-ID: L8htRm3J1mZ

--HG--
extra : rebase_source : ca9ccac75cd575be12d18c71ae60dbffb7c529fa
This commit is contained in:
Robert Helmer 2018-06-20 17:21:17 -07:00
parent d0af2bd6c6
commit b8233d3ae3
9 changed files with 295 additions and 0 deletions

View File

@ -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");

View File

@ -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

View File

@ -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;
};

View File

@ -43,6 +43,7 @@ WEBIDL_FILES = [
'MozStorageStatementParams.webidl',
'MozStorageStatementRow.webidl',
'PrecompiledScript.webidl',
'PrioEncoder.webidl',
'PromiseDebugging.webidl',
'StructuredCloneHolder.webidl',
'WebExtensionContentScript.webidl',

View File

@ -63,6 +63,7 @@ DIRS += [
'notification',
'offline',
'power',
'prio',
'push',
'quota',
'security',

180
dom/prio/PrioEncoder.cpp Normal file
View File

@ -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
dom/prio/PrioEncoder.h Normal file
View File

@ -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
dom/prio/moz.build Normal file
View File

@ -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'

View File

@ -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].
*