diff --git a/dom/prio/PrioEncoder.cpp b/dom/prio/PrioEncoder.cpp index adcc61a8e025..0937477de072 100644 --- a/dom/prio/PrioEncoder.cpp +++ b/dom/prio/PrioEncoder.cpp @@ -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 prioKeyA; - rv = Preferences::GetCString("prio.publicKeyA", prioKeyA); - if (NS_FAILED(rv)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - nsAutoCStringN 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(prioKeyA.BeginReading()), - CURVE25519_KEY_LEN_HEX); - if (prio_rv != SECSuccess) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - prio_rv = PublicKey_import_hex( - &sPublicKeyB, - reinterpret_cast(prioKeyB.BeginReading()), - CURVE25519_KEY_LEN_HEX); - if (prio_rv != SECSuccess) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return; - } - - sSingleton = new PrioEncoder(); - ClearOnShutdown(&sSingleton); - } - - nsTArray dataItems = aPrioParams.mBooleans; - if (dataItems.Length() > gNumBooleans) { - aRv.ThrowRangeError( - 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(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 arrayForServerA; nsTArray arrayForServerB; - if (!arrayForServerA.AppendElements(reinterpret_cast(forServerA), - lenA, fallible)) { + if (!arrayForServerA.AppendElements( + reinterpret_cast(aResult.BeginReading()), + aResult.Length(), fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } - free(forServerA); - - if (!arrayForServerB.AppendElements(reinterpret_cast(forServerB), - lenB, fallible)) { + if (!arrayForServerB.AppendElements( + reinterpret_cast(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 valueA(aGlobal.Context()); if (!ToJSValue(aGlobal.Context(), TypedArrayCreator(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& 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(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(forServerA), lenA); + bResult.Adopt(reinterpret_cast(forServerB), lenB); + + if (prio_rv != SECSuccess) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + bool PrioEncoder::IsValidHexPublicKey(mozilla::Span aStr) { if (aStr.Length() != CURVE25519_KEY_LEN_HEX) { return false; @@ -190,5 +154,58 @@ bool PrioEncoder::IsValidHexPublicKey(mozilla::Span aStr) { return true; } +/* static */ +nsresult PrioEncoder::LazyInitSingleton() { + if (!sSingleton) { + nsresult rv; + + nsAutoCStringN prioKeyA; + rv = Preferences::GetCString("prio.publicKeyA", prioKeyA); + if (NS_FAILED(rv)) { + return rv; + } + + nsAutoCStringN 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(prioKeyA.BeginReading()), + CURVE25519_KEY_LEN_HEX); + if (prio_rv != SECSuccess) { + return NS_ERROR_UNEXPECTED; + } + + prio_rv = PublicKey_import_hex( + &sPublicKeyB, + reinterpret_cast(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 diff --git a/dom/prio/PrioEncoder.h b/dom/prio/PrioEncoder.h index 2195e0d63da4..4bf818c3fd0f 100644 --- a/dom/prio/PrioEncoder.h +++ b/dom/prio/PrioEncoder.h @@ -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& aData, nsCString& aResult, + nsCString& bResult); + + // DOM API static void Encode(GlobalObject& aGlobal, const nsCString& aBatchID, const PrioParams& aPrioParams, RootedDictionary& aData, @@ -40,6 +46,8 @@ class PrioEncoder { static StaticRefPtr sSingleton; static bool IsValidHexPublicKey(mozilla::Span); + + static nsresult LazyInitSingleton(); }; } // namespace dom diff --git a/dom/prio/test/gtest/TestPrioEncoder.cpp b/dom/prio/test/gtest/TestPrioEncoder.cpp index b72979b5fbc4..1e90559eaaff 100644 --- a/dom/prio/test/gtest/TestPrioEncoder.cpp +++ b/dom/prio/test/gtest/TestPrioEncoder.cpp @@ -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()));