Bug 1530481 - Refactor PrioEncoder to be usable from C++. r=rhelmer

For some reason Telemetry's gtests doesn't like one or more of the dependencies
of mprio.h, so I had to move it into PrioEncoder's cpp and forward declare the
PublicKey typedefs.

It isn't the cleanest, but I'm surprised C++ let me off with just that level
of nonsensery.

Differential Revision: https://phabricator.services.mozilla.com/D22605

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Chris H-C 2019-03-20 20:51:40 +00:00
Родитель a100616355
Коммит 799c96cf89
3 изменённых файлов: 122 добавлений и 95 удалений

Просмотреть файл

@ -11,6 +11,8 @@
#include "mozilla/dom/ToJSValue.h"
#include "mprio.h"
#include "PrioEncoder.h"
namespace mozilla {
@ -50,113 +52,32 @@ void PrioEncoder::Encode(GlobalObject& aGlobal, const nsCString& aBatchID,
return;
}
SECStatus prio_rv = SECSuccess;
if (!sSingleton) {
nsresult rv;
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyA;
rv = Preferences::GetCString("prio.publicKeyA", prioKeyA);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyB;
rv = Preferences::GetCString("prio.publicKeyB", prioKeyB);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
// 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;
}
prio_rv = Prio_init();
if (prio_rv != SECSuccess) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
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;
}
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;
}
sSingleton = new PrioEncoder();
ClearOnShutdown(&sSingleton);
}
nsTArray<bool> dataItems = aPrioParams.mBooleans;
if (dataItems.Length() > gNumBooleans) {
aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>(
NS_LITERAL_STRING("Maximum boolean value exceeded"));
nsCString aResult;
nsCString bResult;
nsresult rv = PrioEncoder::EncodeNative(aBatchID, aPrioParams.mBooleans,
aResult, bResult);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
PrioConfig prioConfig = PrioConfig_new(
dataItems.Length(), sPublicKeyA, sPublicKeyB,
reinterpret_cast<const unsigned char*>(aBatchID.BeginReading()),
aBatchID.Length());
if (!prioConfig) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
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.Elements(), &forServerA,
&lenA, &forServerB, &lenB);
nsTArray<uint8_t> arrayForServerA;
nsTArray<uint8_t> arrayForServerB;
if (!arrayForServerA.AppendElements(reinterpret_cast<uint8_t*>(forServerA),
lenA, fallible)) {
if (!arrayForServerA.AppendElements(
reinterpret_cast<const uint8_t*>(aResult.BeginReading()),
aResult.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
free(forServerA);
if (!arrayForServerB.AppendElements(reinterpret_cast<uint8_t*>(forServerB),
lenB, fallible)) {
if (!arrayForServerB.AppendElements(
reinterpret_cast<const uint8_t*>(bResult.BeginReading()),
bResult.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
free(forServerB);
if (prio_rv != SECSuccess) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
JS::Rooted<JS::Value> valueA(aGlobal.Context());
if (!ToJSValue(aGlobal.Context(),
TypedArrayCreator<Uint8Array>(arrayForServerA), &valueA)) {
@ -176,6 +97,49 @@ void PrioEncoder::Encode(GlobalObject& aGlobal, const nsCString& aBatchID,
aData.mB.Construct().Init(&valueB.toObject());
}
/* static */
nsresult PrioEncoder::EncodeNative(const nsCString& aBatchID,
const nsTArray<bool>& aData,
nsCString& aResult, nsCString& bResult) {
SECStatus prio_rv = SECSuccess;
nsresult rv = PrioEncoder::LazyInitSingleton();
if (NS_FAILED(rv)) {
return rv;
}
if (aData.Length() > gNumBooleans) {
return NS_ERROR_RANGE_ERR;
}
PrioConfig prioConfig = PrioConfig_new(
aData.Length(), sPublicKeyA, sPublicKeyB,
reinterpret_cast<const unsigned char*>(aBatchID.BeginReading()),
aBatchID.Length());
if (!prioConfig) {
return NS_ERROR_FAILURE;
}
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, aData.Elements(), &forServerA, &lenA,
&forServerB, &lenB);
aResult.Adopt(reinterpret_cast<char*>(forServerA), lenA);
bResult.Adopt(reinterpret_cast<char*>(forServerB), lenB);
if (prio_rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool PrioEncoder::IsValidHexPublicKey(mozilla::Span<const char> aStr) {
if (aStr.Length() != CURVE25519_KEY_LEN_HEX) {
return false;
@ -190,5 +154,58 @@ bool PrioEncoder::IsValidHexPublicKey(mozilla::Span<const char> aStr) {
return true;
}
/* static */
nsresult PrioEncoder::LazyInitSingleton() {
if (!sSingleton) {
nsresult rv;
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyA;
rv = Preferences::GetCString("prio.publicKeyA", prioKeyA);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyB;
rv = Preferences::GetCString("prio.publicKeyB", prioKeyB);
if (NS_FAILED(rv)) {
return rv;
}
// 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)) {
return NS_ERROR_UNEXPECTED;
}
SECStatus prio_rv = SECSuccess;
prio_rv = Prio_init();
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
prio_rv = PublicKey_import_hex(
&sPublicKeyA,
reinterpret_cast<const unsigned char*>(prioKeyA.BeginReading()),
CURVE25519_KEY_LEN_HEX);
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
prio_rv = PublicKey_import_hex(
&sPublicKeyB,
reinterpret_cast<const unsigned char*>(prioKeyB.BeginReading()),
CURVE25519_KEY_LEN_HEX);
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
sSingleton = new PrioEncoder();
ClearOnShutdown(&sSingleton);
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -10,9 +10,9 @@
#include "mozilla/dom/PrioEncoderBinding.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mprio.h"
class nsIGlobalObject;
typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
typedef SECKEYPublicKey* PublicKey;
namespace mozilla {
namespace dom {
@ -21,6 +21,12 @@ class PrioEncoder {
public:
NS_INLINE_DECL_REFCOUNTING(PrioEncoder)
// C++ API
static nsresult EncodeNative(const nsCString& aBatchID,
const nsTArray<bool>& aData, nsCString& aResult,
nsCString& bResult);
// DOM API
static void Encode(GlobalObject& aGlobal, const nsCString& aBatchID,
const PrioParams& aPrioParams,
RootedDictionary<PrioEncodedData>& aData,
@ -40,6 +46,8 @@ class PrioEncoder {
static StaticRefPtr<PrioEncoder> sSingleton;
static bool IsValidHexPublicKey(mozilla::Span<const char>);
static nsresult LazyInitSingleton();
};
} // namespace dom

Просмотреть файл

@ -12,6 +12,8 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mprio.h"
TEST(PrioEncoder, BadPublicKeys) {
mozilla::dom::AutoJSAPI jsAPI;
ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope()));