summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/dap/DAPTelemetry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/dap/DAPTelemetry.cpp')
-rw-r--r--toolkit/components/telemetry/dap/DAPTelemetry.cpp309
1 files changed, 309 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/dap/DAPTelemetry.cpp b/toolkit/components/telemetry/dap/DAPTelemetry.cpp
new file mode 100644
index 0000000000..c20dc50557
--- /dev/null
+++ b/toolkit/components/telemetry/dap/DAPTelemetry.cpp
@@ -0,0 +1,309 @@
+/* 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 "DAPTelemetryBindings.h"
+
+#include "mozilla/Logging.h"
+#include "nsPromiseFlatString.h"
+
+#include "nss.h"
+#include "nsNSSComponent.h"
+#include "secmodt.h"
+#include "pk11pub.h"
+#include "ScopedNSSTypes.h"
+
+static mozilla::LazyLogModule sLogger("DAPTelemetry");
+#undef LOG
+#define LOG(...) MOZ_LOG(sLogger, mozilla::LogLevel::Debug, (__VA_ARGS__))
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(DAPTelemetry, nsIDAPTelemetry)
+
+// This function was copied from pk11_hpke_unittest.cc
+// And modified to take a Span.
+static std::vector<uint8_t> Pkcs8(Span<const uint8_t> sk,
+ Span<const uint8_t> pk) {
+ // Only X25519 format.
+ std::vector<uint8_t> v(105);
+ v.assign({
+ 0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
+ 0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
+ });
+ v.insert(v.end(), sk.begin(), sk.end());
+ v.insert(v.end(), {
+ 0xa1,
+ 0x23,
+ 0x03,
+ 0x21,
+ 0x00,
+ });
+ v.insert(v.end(), pk.begin(), pk.end());
+ return v;
+}
+
+// This function was copied from cpputil.h
+static unsigned char* toUcharPtr(const uint8_t* v) {
+ return const_cast<unsigned char*>(static_cast<const unsigned char*>(v));
+}
+
+/// If successful this returns a pointer to a HpkeContext which must be
+/// released using dapDestroyHpkeContext or PK11_HPKE_DestroyContext.
+HpkeContext* dapSetupHpkeContextInternal(
+ const uint8_t* aKey, uint32_t aKeyLength, const uint8_t* aInfo,
+ uint32_t aInfoLength, SECKEYPublicKey* aPkE, SECKEYPrivateKey* aSkE,
+ nsTArray<uint8_t>* aOutputEncapsulatedKey) {
+ SECStatus status = PK11_HPKE_ValidateParameters(
+ HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, HpkeAeadAes128Gcm);
+ if (status != SECSuccess) {
+ MOZ_LOG(sLogger, mozilla::LogLevel::Error,
+ ("Invalid HKPE parameters found."));
+ return nullptr;
+ }
+
+ UniqueHpkeContext context(
+ PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
+ HpkeAeadAes128Gcm, nullptr, nullptr));
+
+ SECKEYPublicKey* pkR_raw = nullptr;
+ status = PK11_HPKE_Deserialize(context.get(), aKey, aKeyLength, &pkR_raw);
+ UniqueSECKEYPublicKey pkR(pkR_raw);
+ pkR_raw = nullptr;
+ if (status != SECSuccess) {
+ MOZ_LOG(sLogger, mozilla::LogLevel::Error,
+ ("Failed to deserialize HPKE encryption key."));
+ return nullptr;
+ }
+
+ const SECItem hpkeInfo = {siBuffer, toUcharPtr(aInfo), aInfoLength};
+
+ status = PK11_HPKE_SetupS(context.get(), aPkE, aSkE, pkR.get(), &hpkeInfo);
+ if (status != SECSuccess) {
+ MOZ_LOG(sLogger, mozilla::LogLevel::Error, ("HPKE setup failed."));
+ return nullptr;
+ }
+
+ const SECItem* hpkeEncapKey = PK11_HPKE_GetEncapPubKey(context.get());
+ if (!hpkeEncapKey) {
+ MOZ_LOG(sLogger, mozilla::LogLevel::Error,
+ ("Failed to get HPKE encapsulated public key."));
+ return nullptr;
+ }
+
+ aOutputEncapsulatedKey->AppendElements(hpkeEncapKey->data, hpkeEncapKey->len);
+
+ return context.release();
+}
+
+extern "C" {
+/// If successful this returns a pointer to a PK11Context which must be
+/// released using dapReleaseCmac.
+void* dapStartCmac(uint8_t* aSeed) {
+ MOZ_RELEASE_ASSERT(EnsureNSSInitializedChromeOrContent(),
+ "Could not initialize NSS.");
+
+ UniquePK11SlotInfo slot(PK11_GetBestSlot(CKM_AES_CMAC, nullptr));
+ MOZ_RELEASE_ASSERT(slot, "DAPTelemetry: dapStartCmac(): Failed to get slot.");
+
+ SECItem keyItem = {siBuffer, toUcharPtr(aSeed), 16};
+ UniquePK11SymKey key(PK11_ImportSymKey(slot.get(), CKM_AES_CMAC,
+ PK11_OriginUnwrap, CKA_SIGN, &keyItem,
+ nullptr));
+ MOZ_RELEASE_ASSERT(key,
+ "DAPTelemetry: dapStartCmac(): Failed to import key.");
+
+ UniqueSECItem param(PK11_ParamFromIV(CKM_AES_CMAC, nullptr));
+ MOZ_RELEASE_ASSERT(
+ param, "DAPTelemetry: dapStartCmac(): Failed to create parameters.");
+
+ PK11Context* ctx = PK11_CreateContextBySymKey(CKM_AES_CMAC, CKA_SIGN,
+ key.get(), param.get());
+ MOZ_RELEASE_ASSERT(ctx,
+ "DAPTelemetry: dapStartCmac(): Failed to create context.");
+
+ return ctx;
+}
+
+void dapUpdateCmac(void* aContext, const uint8_t* aData, uint32_t aDataLen) {
+ SECStatus res =
+ PK11_DigestOp(static_cast<PK11Context*>(aContext), aData, aDataLen);
+ MOZ_RELEASE_ASSERT(
+ res == SECSuccess,
+ "DAPTelemetry: dapUpdateCmac(): Mac digest update failed.");
+}
+
+void dapFinalizeCmac(void* aContext, uint8_t* aMac) {
+ unsigned int maclen = 0;
+
+ SECStatus res =
+ PK11_DigestFinal(static_cast<PK11Context*>(aContext), aMac, &maclen, 16);
+ MOZ_RELEASE_ASSERT(
+ res == SECSuccess,
+ "DAPTelemetry: dapFinalizeCmac(): PK11_DigestFinal failed.");
+ MOZ_RELEASE_ASSERT(
+ maclen == 16,
+ "DAPTelemetry: dapFinalizeCmac(): PK11_DigestFinal returned "
+ "too few MAC bytes.");
+}
+
+void dapReleaseCmac(void* aContext) {
+ PK11_DestroyContext(static_cast<PK11Context*>(aContext), true);
+}
+
+/// If successful this returns a pointer to a PK11Context which must be
+/// released using dapReleaseCtrCtx.
+void* dapStartAesCtr(const uint8_t* aKey) {
+ MOZ_RELEASE_ASSERT(EnsureNSSInitializedChromeOrContent(),
+ "Could not initialize NSS.");
+
+ UniquePK11SlotInfo slot(PK11_GetBestSlot(CKM_AES_CTR, nullptr));
+ MOZ_RELEASE_ASSERT(aKey,
+ "DAPTelemetry: dapStartAesCtr(): Failed to get slot.");
+
+ SECItem ctrKeyItem = {siBuffer, toUcharPtr(aKey), 16};
+ UniquePK11SymKey ctrKey(PK11_ImportSymKey(slot.get(), CKM_AES_CTR,
+ PK11_OriginUnwrap, CKA_ENCRYPT,
+ &ctrKeyItem, nullptr));
+ MOZ_RELEASE_ASSERT(ctrKey,
+ "DAPTelemetry: dapStartAesCtr(): Failed to create key.");
+
+ SECItem ctrParam = {siBuffer, nullptr, 0};
+ CK_AES_CTR_PARAMS ctrParamInner;
+ ctrParamInner.ulCounterBits = 128;
+ memset(&ctrParamInner.cb, 0, 16);
+ ctrParam.type = siBuffer;
+ ctrParam.data = reinterpret_cast<unsigned char*>(&ctrParamInner);
+ ctrParam.len = static_cast<unsigned int>(sizeof(ctrParamInner));
+
+ PK11Context* ctrCtx = PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT,
+ ctrKey.get(), &ctrParam);
+ MOZ_RELEASE_ASSERT(
+ ctrCtx, "DAPTelemetry: dapStartAesCtr(): Failed to create context.");
+
+ return ctrCtx;
+}
+
+void dapCtrFillBuffer(void* aContext, uint8_t* aBuffer, int aBufferSize) {
+ int ctlen = 0;
+ memset(aBuffer, 0, aBufferSize);
+ SECStatus res = PK11_CipherOp(static_cast<PK11Context*>(aContext), aBuffer,
+ &ctlen, aBufferSize, aBuffer, aBufferSize);
+ MOZ_RELEASE_ASSERT(res == SECSuccess,
+ "DAPTelemetry: dapCtrFillBuffer(...): Encryption failed.");
+}
+
+void dapReleaseCtrCtx(void* aContext) {
+ PK11_DestroyContext(static_cast<PK11Context*>(aContext), true);
+}
+
+/// Takes additional ephemeral keys to make everything deterministic for test
+/// vectors.
+/// If successful this returns a pointer to a HpkeContext which must be
+/// released using dapDestroyHpkeContext or PK11_HPKE_DestroyContext.
+HpkeContext* dapSetupHpkeContextForTesting(
+ const uint8_t* aKey, uint32_t aKeyLength, const uint8_t* aInfo,
+ uint32_t aInfoLength, const uint8_t* aPkEm, uint32_t aPkEmLength,
+ const uint8_t* aSkEm, uint32_t aSkEmLength,
+ nsTArray<uint8_t>* aOutputEncapsulatedKey) {
+ Span<const uint8_t> sk_e(aSkEm, aSkEm + aSkEmLength);
+ Span<const uint8_t> pk_e(aPkEm, aPkEm + aPkEmLength);
+ std::vector<uint8_t> pkcs8_e = Pkcs8(sk_e, pk_e);
+
+ MOZ_RELEASE_ASSERT(EnsureNSSInitializedChromeOrContent(),
+ "Could not initialize NSS.");
+
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ MOZ_RELEASE_ASSERT(slot, "Failed to get slot.");
+
+ SECItem keys_e = {siBuffer, toUcharPtr(pkcs8_e.data()),
+ static_cast<unsigned int>(pkcs8_e.size())};
+ SECKEYPrivateKey* internal_skE_raw = nullptr;
+ SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot.get(), &keys_e, nullptr, nullptr, false, false, KU_ALL,
+ &internal_skE_raw, nullptr);
+ UniqueSECKEYPrivateKey internal_skE(internal_skE_raw);
+ internal_skE_raw = nullptr;
+ MOZ_RELEASE_ASSERT(rv == SECSuccess, "Failed to import skE/pkE.");
+
+ UniqueSECKEYPublicKey internal_pkE(
+ SECKEY_ConvertToPublicKey(internal_skE.get()));
+
+ UniqueHpkeContext result(dapSetupHpkeContextInternal(
+ aKey, aKeyLength, aInfo, aInfoLength, internal_pkE.get(),
+ internal_skE.get(), aOutputEncapsulatedKey));
+
+ return result.release();
+}
+
+void dapDestroyHpkeContext(HpkeContext* aContext) {
+ PK11_HPKE_DestroyContext(aContext, true);
+}
+
+bool dapHpkeEncrypt(HpkeContext* aContext, const uint8_t* aAad,
+ uint32_t aAadLength, const uint8_t* aPlaintext,
+ uint32_t aPlaintextLength,
+ nsTArray<uint8_t>* aOutputShare) {
+ SECItem aad_si = {siBuffer, toUcharPtr(aAad), aAadLength};
+ SECItem plaintext_si = {siBuffer, toUcharPtr(aPlaintext), aPlaintextLength};
+ SECItem* chCt = nullptr;
+ SECStatus rv = PK11_HPKE_Seal(aContext, &aad_si, &plaintext_si, &chCt);
+ if (rv != SECSuccess) {
+ return false;
+ }
+ UniqueSECItem ct(chCt);
+
+ aOutputShare->AppendElements(ct->data, ct->len);
+ return true;
+}
+
+bool dapHpkeEncryptOneshot(const uint8_t* aKey, uint32_t aKeyLength,
+ const uint8_t* aInfo, uint32_t aInfoLength,
+ const uint8_t* aAad, uint32_t aAadLength,
+ const uint8_t* aPlaintext, uint32_t aPlaintextLength,
+ nsTArray<uint8_t>* aOutputEncapsulatedKey,
+ nsTArray<uint8_t>* aOutputShare) {
+ MOZ_RELEASE_ASSERT(EnsureNSSInitializedChromeOrContent(),
+ "Could not initialize NSS.");
+ UniqueHpkeContext context(
+ dapSetupHpkeContextInternal(aKey, aKeyLength, aInfo, aInfoLength, nullptr,
+ nullptr, aOutputEncapsulatedKey));
+ if (!context) {
+ return false;
+ }
+
+ return dapHpkeEncrypt(context.get(), aAad, aAadLength, aPlaintext,
+ aPlaintextLength, aOutputShare);
+}
+}
+
+NS_IMETHODIMP DAPTelemetry::GetReportU8(
+ const nsTArray<uint8_t>& aLeaderHpkeConfig,
+ const nsTArray<uint8_t>& aHelperHpkeConfig, uint8_t aMeasurement,
+ const nsTArray<uint8_t>& aTaskID, const uint64_t aTimePrecision,
+ nsTArray<uint8_t>& aOutReport) {
+ MOZ_RELEASE_ASSERT(aTaskID.Length() == 32, "TaskID must have 32 bytes.");
+ if (!dapGetReportU8(&aLeaderHpkeConfig, &aHelperHpkeConfig, aMeasurement,
+ &aTaskID, aTimePrecision, &aOutReport)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP DAPTelemetry::GetReportVecU16(
+ const nsTArray<uint8_t>& aLeaderHpkeConfig,
+ const nsTArray<uint8_t>& aHelperHpkeConfig,
+ const nsTArray<uint16_t>& aMeasurement, const nsTArray<uint8_t>& aTaskID,
+ const uint64_t aTimePrecision, nsTArray<uint8_t>& aOutReport) {
+ MOZ_RELEASE_ASSERT(aTaskID.Length() == 32, "TaskID must have 32 bytes.");
+ if (!dapGetReportVecU16(&aLeaderHpkeConfig, &aHelperHpkeConfig, &aMeasurement,
+ &aTaskID, aTimePrecision, &aOutReport)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla