diff --git a/D224587.1728128070.diff b/D224587.1728128070.diff new file mode 100644 index 0000000..439ae13 --- /dev/null +++ b/D224587.1728128070.diff @@ -0,0 +1,50 @@ +diff --git a/dom/media/webrtc/transport/nricectx.cpp b/dom/media/webrtc/transport/nricectx.cpp +--- a/dom/media/webrtc/transport/nricectx.cpp ++++ b/dom/media/webrtc/transport/nricectx.cpp +@@ -124,23 +124,30 @@ + static int nr_crypto_nss_hmac(UCHAR* key, size_t keyl, UCHAR* buf, size_t bufl, + UCHAR* result) { + CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC; + PK11SlotInfo* slot = nullptr; + MOZ_ASSERT(keyl > 0); +- SECItem keyi = {siBuffer, key, static_cast(keyl)}; ++ CK_KEY_DERIVATION_STRING_DATA idkey = {key, keyl}; ++ SECItem keyi = {siBuffer, (unsigned char*)&idkey, sizeof(idkey)}; ++ PK11SymKey* tmpKey = nullptr; + PK11SymKey* skey = nullptr; + PK11Context* hmac_ctx = nullptr; + SECStatus status; + unsigned int hmac_len; + SECItem param = {siBuffer, nullptr, 0}; + int err = R_INTERNAL; + + slot = PK11_GetInternalKeySlot(); + if (!slot) goto abort; + +- skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi, +- nullptr); ++ // HMAC is used for hash calculation only so use derive instead of import ++ // to be FIPS compliant. ++ tmpKey = PK11_KeyGen(slot, mech, NULL, keyl, nullptr); ++ if (!tmpKey) goto abort; ++ ++ skey = PK11_Derive(tmpKey, CKM_CONCATENATE_DATA_AND_BASE, &keyi, mech, ++ CKA_SIGN, keyl); + if (!skey) goto abort; + + hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, ¶m); + if (!hmac_ctx) goto abort; + +@@ -157,10 +164,11 @@ + + err = 0; + + abort: + if (hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE); ++ if (tmpKey) PK11_FreeSymKey(tmpKey); + if (skey) PK11_FreeSymKey(skey); + if (slot) PK11_FreeSlot(slot); + + return err; + } + diff --git a/D224588.1728128098.diff b/D224588.1728128098.diff new file mode 100644 index 0000000..29d717f --- /dev/null +++ b/D224588.1728128098.diff @@ -0,0 +1,224 @@ +diff --git a/third_party/libsrtp/src/crypto/cipher/aes_gcm_nss.c b/third_party/libsrtp/src/crypto/cipher/aes_gcm_nss.c +--- a/third_party/libsrtp/src/crypto/cipher/aes_gcm_nss.c ++++ b/third_party/libsrtp/src/crypto/cipher/aes_gcm_nss.c +@@ -54,10 +54,11 @@ + #include "crypto_types.h" + #include "cipher_types.h" + #include "cipher_test_cases.h" + #include + #include ++#include "nss_fips.h" + + srtp_debug_module_t srtp_mod_aes_gcm = { + 0, /* debugging is off by default */ + "aes gcm nss" /* printable module name */ + }; +@@ -211,12 +212,17 @@ + if (!slot) { + return (srtp_err_status_cipher_fail); + } + + SECItem key_item = { siBuffer, (unsigned char *)key, c->key_size }; +- c->key = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, +- CKA_ENCRYPT, &key_item, NULL); ++ if (PK11_IsFIPS()) { ++ c->key = PK11_ImportSymKey_FIPS(slot, CKM_AES_GCM, PK11_OriginUnwrap, ++ CKA_ENCRYPT, &key_item, NULL); ++ } else { ++ c->key = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, ++ CKA_ENCRYPT, &key_item, NULL); ++ } + PK11_FreeSlot(slot); + + if (!c->key) { + return (srtp_err_status_cipher_fail); + } +diff --git a/third_party/libsrtp/src/crypto/cipher/aes_icm_nss.c b/third_party/libsrtp/src/crypto/cipher/aes_icm_nss.c +--- a/third_party/libsrtp/src/crypto/cipher/aes_icm_nss.c ++++ b/third_party/libsrtp/src/crypto/cipher/aes_icm_nss.c +@@ -51,10 +51,11 @@ + #include "crypto_types.h" + #include "err.h" /* for srtp_debug */ + #include "alloc.h" + #include "cipher_types.h" + #include "cipher_test_cases.h" ++#include "nss_fips.h" + + srtp_debug_module_t srtp_mod_aes_icm = { + 0, /* debugging is off by default */ + "aes icm nss" /* printable module name */ + }; +@@ -252,12 +253,17 @@ + if (!slot) { + return srtp_err_status_bad_param; + } + + SECItem keyItem = { siBuffer, (unsigned char *)key, c->key_size }; +- c->key = PK11_ImportSymKey(slot, CKM_AES_CTR, PK11_OriginUnwrap, +- CKA_ENCRYPT, &keyItem, NULL); ++ if (PK11_IsFIPS()) { ++ c->key = PK11_ImportSymKey_FIPS(slot, CKM_AES_CTR, PK11_OriginUnwrap, ++ CKA_ENCRYPT, &keyItem, NULL); ++ } else { ++ c->key = PK11_ImportSymKey(slot, CKM_AES_CTR, PK11_OriginUnwrap, ++ CKA_ENCRYPT, &keyItem, NULL); ++ } + PK11_FreeSlot(slot); + + if (!c->key) { + return srtp_err_status_cipher_fail; + } +diff --git a/third_party/libsrtp/src/crypto/include/nss_fips.h b/third_party/libsrtp/src/crypto/include/nss_fips.h +new file mode 100644 +--- /dev/null ++++ b/third_party/libsrtp/src/crypto/include/nss_fips.h +@@ -0,0 +1,148 @@ ++/* ++ * Copyright (c) 2024, Red Hat, Inc. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following ++ * disclaimer in the documentation and/or other materials provided ++ * with the distribution. ++ * ++ * Neither the name of the Red Hat, Inc. nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ++ * OF THE POSSIBILITY OF SUCH DAMAGE. ++*/ ++ ++/* ++ Adapted from Red Hat Ceph patch by ++ Radoslaw Zarzynski ++ ++ PK11_ImportSymKey() is a part of NSS API that becomes unavailable ++ in the FIPS mode. Apparently NSS targets stricter restrictions ++ than those coming from Level 1 of FIPS 140-2. In the consequence, ++ loading a symmetric key from plain keyring or key db fails. ++ ++ A raw crypto key is in-memory wrapped with fresh, random wrapping ++ key just before being imported via PK11_UnwrapSymKey(). Of course, ++ this effectively lowers to FIPS level 1. Still, this would be no ++ different from what OpenSSL gives in the matter. ++*/ ++ ++#ifndef NSS_FIPS_H ++#define NSS_FIPS_H ++ ++static PK11SymKey *PK11_ImportSymKey_FIPS( ++ PK11SlotInfo * const slot, ++ const CK_MECHANISM_TYPE type, ++ const PK11Origin origin, ++ const CK_ATTRIBUTE_TYPE operation, ++ SECItem * const raw_key, ++ void * const wincx) ++{ ++ PK11SymKey* wrapping_key = NULL; ++ PK11Context *wrap_key_crypt_context = NULL; ++ SECItem *raw_key_aligned = NULL; ++ CK_MECHANISM_TYPE wrap_mechanism = 0; ++ ++ struct { ++ unsigned char data[256]; ++ int len; ++ } wrapped_key; ++ ++ #define SCOPE_DATA_FREE() \ ++ { \ ++ PK11_FreeSymKey(wrapping_key); \ ++ PK11_DestroyContext(wrap_key_crypt_context, PR_TRUE); \ ++ SECITEM_FreeItem(raw_key_aligned, PR_TRUE); \ ++ } ++ ++ if(raw_key->len > sizeof(wrapped_key.data)) { ++ return NULL; ++ } ++ ++ // getting 306 on my system which is CKM_DES3_ECB. ++ wrap_mechanism = PK11_GetBestWrapMechanism(slot); ++ ++ // Generate a wrapping key. It will be used exactly twice over the scope: ++ // * to encrypt raw_key giving wrapped_key, ++ // * to decrypt wrapped_key in the internals of PK11_UnwrapSymKey(). ++ wrapping_key = PK11_KeyGen(slot, wrap_mechanism, NULL, ++ PK11_GetBestKeyLength(slot, wrap_mechanism), NULL); ++ if (wrapping_key == NULL) { ++ return NULL; ++ } ++ ++ // Prepare a PK11 context for the raw_key -> wrapped_key encryption. ++ SECItem tmp_sec_item; ++ memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); ++ wrap_key_crypt_context = PK11_CreateContextBySymKey( ++ wrap_mechanism, ++ CKA_ENCRYPT, ++ wrapping_key, ++ &tmp_sec_item); ++ if (wrap_key_crypt_context == NULL) { ++ SCOPE_DATA_FREE(); ++ return NULL; ++ } ++ ++ // Finally wrap the key. Important note is that the wrapping mechanism ++ // selection (read: just grabbing a cipher) offers, at least in my NSS ++ // copy, mostly CKM_*_ECB ciphers (with 3DES as the leading one, see ++ // wrapMechanismList[] in pk11mech.c). There is no CKM_*_*_PAD variant ++ // which means that plaintext we are providing to PK11_CipherOp() must ++ // be aligned to cipher's block size. For 3DES it's 64 bits. ++ raw_key_aligned = PK11_BlockData(raw_key, PK11_GetBlockSize(wrap_mechanism, NULL)); ++ if (raw_key_aligned == NULL) { ++ SCOPE_DATA_FREE(); ++ return NULL; ++ } ++ ++ if (PK11_CipherOp(wrap_key_crypt_context, wrapped_key.data, &wrapped_key.len, ++ sizeof(wrapped_key.data), raw_key_aligned->data, ++ raw_key_aligned->len) != SECSuccess) { ++ SCOPE_DATA_FREE(); ++ return NULL; ++ } ++ ++ if (PK11_Finalize(wrap_key_crypt_context) != SECSuccess) { ++ SCOPE_DATA_FREE(); ++ return NULL; ++ } ++ ++ // Key is wrapped now so we can acquire the ultimate PK11SymKey through ++ // unwrapping it. Of course these two opposite operations form NOP with ++ // a side effect: FIPS level 1 compatibility. ++ memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); ++ ++ SECItem wrapped_key_item; ++ memset(&wrapped_key_item, 0, sizeof(wrapped_key_item)); ++ wrapped_key_item.data = wrapped_key.data; ++ wrapped_key_item.len = wrapped_key.len; ++ ++ PK11SymKey *ret = PK11_UnwrapSymKey(wrapping_key, wrap_mechanism, ++ &tmp_sec_item, &wrapped_key_item, type, ++ operation, raw_key->len); ++ SCOPE_DATA_FREE(); ++ return ret; ++ } ++ ++#endif // NSS_FIPS_H + diff --git a/firefox.spec b/firefox.spec index b34e4e6..b0b153b 100644 --- a/firefox.spec +++ b/firefox.spec @@ -236,6 +236,12 @@ Patch154: firefox-nss-addon-hack.patch # ARM run-time patch Patch155: rhbz-1354671.patch +# --- fips webrtc fix +Patch200: webrtc-128.0.patch.patch +Patch201: D224587.1728128070.diff +Patch202: D224588.1728128098.diff + + # ---- Test patches ---- # Generate without context by # GENDIFF_DIFF_ARGS=-U0 gendiff firefox-xxxx .firefox-tests-xpcshell @@ -1170,6 +1176,14 @@ echo "--------------------------------------------" %patch -P155 -p1 -b .rhbz-1354671 %endif +# Fips webrtc patch +%ifnarch ppc64 ppc64le s390x +%patch -P200 -p1 -b .webrtc-128.0 +%patch -P201 -p1 -b .D224587 +%patch -P202 -p1 -b .D224588 +%endif + + # ---- Security patches ---- %{__rm} -f .mozconfig diff --git a/webrtc-128.0.patch.patch b/webrtc-128.0.patch.patch new file mode 100644 index 0000000..6bd7f7c --- /dev/null +++ b/webrtc-128.0.patch.patch @@ -0,0 +1,2369 @@ +diff -up firefox-128.2.0/dom/crypto/WebCryptoTask.cpp.webrtc firefox-128.2.0/dom/crypto/WebCryptoTask.cpp +--- firefox-128.2.0/dom/crypto/WebCryptoTask.cpp.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/crypto/WebCryptoTask.cpp 2024-09-30 21:42:30.750124316 +0200 +@@ -118,60 +118,6 @@ enum TelemetryAlgorithm { + } \ + } + +-class ClearException { +- public: +- explicit ClearException(JSContext* aCx) : mCx(aCx) {} +- +- ~ClearException() { JS_ClearPendingException(mCx); } +- +- private: +- JSContext* mCx; +-}; +- +-template +-static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, +- nsString& aName) { +- ClearException ce(aCx); +- +- if (aAlgorithm.IsString()) { +- // If string, then treat as algorithm name +- aName.Assign(aAlgorithm.GetAsString()); +- } else { +- // Coerce to algorithm and extract name +- JS::Rooted value(aCx, +- JS::ObjectValue(*aAlgorithm.GetAsObject())); +- Algorithm alg; +- +- if (!alg.Init(aCx, value)) { +- return NS_ERROR_DOM_SYNTAX_ERR; +- } +- +- aName = alg.mName; +- } +- +- if (!NormalizeToken(aName, aName)) { +- return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +- } +- +- return NS_OK; +-} +- +-template +-static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) { +- ClearException ce(aCx); +- +- if (!aAlgorithm.IsObject()) { +- return NS_ERROR_DOM_SYNTAX_ERR; +- } +- +- JS::Rooted value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject())); +- if (!aTarget.Init(aCx, value)) { +- return NS_ERROR_DOM_TYPE_MISMATCH_ERR; +- } +- +- return NS_OK; +-} +- + inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) { + if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) || + aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { +@@ -2198,6 +2144,30 @@ class GenerateSymmetricKeyTask : public + virtual void Cleanup() override { mKey = nullptr; } + }; + ++class GenerateAsymmetricKeyTask : public WebCryptoTask { ++ public: ++ GenerateAsymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, ++ const ObjectOrString& aAlgorithm, bool aExtractable, ++ const Sequence& aKeyUsages); ++ ++ protected: ++ UniquePLArenaPool mArena; ++ UniquePtr mKeyPair; ++ nsString mAlgName; ++ CK_MECHANISM_TYPE mMechanism; ++ PK11RSAGenParams mRsaParams; ++ SECKEYDHParams mDhParams; ++ nsString mNamedCurve; ++ ++ virtual nsresult DoCrypto() override; ++ virtual void Resolve() override; ++ virtual void Cleanup() override; ++ ++ private: ++ UniqueSECKEYPublicKey mPublicKey; ++ UniqueSECKEYPrivateKey mPrivateKey; ++}; ++ + GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask( + nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm, + bool aExtractable, const Sequence& aKeyUsages) +diff -up firefox-128.2.0/dom/crypto/WebCryptoTask.h.webrtc firefox-128.2.0/dom/crypto/WebCryptoTask.h +--- firefox-128.2.0/dom/crypto/WebCryptoTask.h.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/crypto/WebCryptoTask.h 2024-09-30 21:41:07.648369048 +0200 +@@ -175,31 +175,60 @@ class WebCryptoTask : public CancelableR + nsresult mRv; + }; + +-// XXX This class is declared here (unlike others) to enable reuse by WebRTC. +-class GenerateAsymmetricKeyTask : public WebCryptoTask { ++class ClearException { + public: +- GenerateAsymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx, +- const ObjectOrString& aAlgorithm, bool aExtractable, +- const Sequence& aKeyUsages); +- +- protected: +- UniquePLArenaPool mArena; +- UniquePtr mKeyPair; +- nsString mAlgName; +- CK_MECHANISM_TYPE mMechanism; +- PK11RSAGenParams mRsaParams; +- SECKEYDHParams mDhParams; +- nsString mNamedCurve; +- +- virtual nsresult DoCrypto() override; +- virtual void Resolve() override; +- virtual void Cleanup() override; ++ explicit ClearException(JSContext* aCx) : mCx(aCx) {} ++ ++ ~ClearException() { JS_ClearPendingException(mCx); } + + private: +- UniqueSECKEYPublicKey mPublicKey; +- UniqueSECKEYPrivateKey mPrivateKey; ++ JSContext* mCx; + }; + ++template ++nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, ++ nsString& aName) { ++ ClearException ce(aCx); ++ ++ if (aAlgorithm.IsString()) { ++ // If string, then treat as algorithm name ++ aName.Assign(aAlgorithm.GetAsString()); ++ } else { ++ // Coerce to algorithm and extract name ++ JS::Rooted value(aCx, ++ JS::ObjectValue(*aAlgorithm.GetAsObject())); ++ Algorithm alg; ++ ++ if (!alg.Init(aCx, value)) { ++ return NS_ERROR_DOM_SYNTAX_ERR; ++ } ++ ++ aName = alg.mName; ++ } ++ ++ if (!NormalizeToken(aName, aName)) { ++ return NS_ERROR_DOM_NOT_SUPPORTED_ERR; ++ } ++ ++ return NS_OK; ++} ++ ++template ++nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) { ++ ClearException ce(aCx); ++ ++ if (!aAlgorithm.IsObject()) { ++ return NS_ERROR_DOM_SYNTAX_ERR; ++ } ++ ++ JS::Rooted value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject())); ++ if (!aTarget.Init(aCx, value)) { ++ return NS_ERROR_DOM_TYPE_MISMATCH_ERR; ++ } ++ ++ return NS_OK; ++} ++ + } // namespace mozilla::dom + + #endif // mozilla_dom_WebCryptoTask_h +diff -up firefox-128.2.0/dom/media/webrtc/components.conf.webrtc firefox-128.2.0/dom/media/webrtc/components.conf +--- firefox-128.2.0/dom/media/webrtc/components.conf.webrtc 2024-09-30 21:41:07.649369081 +0200 ++++ firefox-128.2.0/dom/media/webrtc/components.conf 2024-09-30 21:41:07.649369081 +0200 +@@ -0,0 +1,14 @@ ++# -*- 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/. ++ ++Classes = [ ++ { ++ 'cid': '{e665acb0-5952-11ef-bb8c-18c04d07c34d}', ++ 'contract_ids': ['@mozilla.org/rtccert/service;1'], ++ 'headers': ['/dom/media/webrtc/RTCCertService.h'], ++ 'constructor': 'mozilla::dom::NewRTCCertService', ++ }, ++] +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.cpp.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.cpp +--- firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.cpp.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.cpp 2024-09-30 21:41:07.649369081 +0200 +@@ -104,13 +104,15 @@ class MediaTransportHandlerSTS : public + // via IPC anymore + const nsTArray& aStunAddrs) override; + +- void ActivateTransport( +- const std::string& aTransportId, const std::string& aLocalUfrag, +- const std::string& aLocalPwd, size_t aComponentCount, +- const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, +- bool aPrivacyRequested) override; ++ void ActivateTransport(const std::string& aTransportId, ++ const std::string& aLocalUfrag, ++ const std::string& aLocalPwd, size_t aComponentCount, ++ const std::string& aUfrag, ++ const std::string& aPassword, ++ const nsTArray& aCertFingerprint, ++ SSLKEAType aAuthType, bool aDtlsClient, ++ const DtlsDigestList& aDigests, ++ bool aPrivacyRequested) override; + + void RemoveTransportsExcept( + const std::set& aTransportIds) override; +@@ -799,14 +801,13 @@ void MediaTransportHandlerSTS::ActivateT + const std::string& aTransportId, const std::string& aLocalUfrag, + const std::string& aLocalPwd, size_t aComponentCount, + const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, +- bool aPrivacyRequested) { ++ const nsTArray& aCertFingerprint, SSLKEAType aAuthType, ++ bool aDtlsClient, const DtlsDigestList& aDigests, bool aPrivacyRequested) { + MOZ_RELEASE_ASSERT(mInitPromise); + + mInitPromise->Then( + mStsThread, __func__, +- [=, keyDer = aKeyDer.Clone(), certDer = aCertDer.Clone(), ++ [=, aCertFingerprint = aCertFingerprint.Clone(), + self = RefPtr(this)]() { + if (!mIceCtx) { + return; // Probably due to XPCOM shutdown +@@ -814,7 +815,7 @@ void MediaTransportHandlerSTS::ActivateT + + MOZ_ASSERT(aComponentCount); + RefPtr dtlsIdentity( +- DtlsIdentity::Deserialize(keyDer, certDer, aAuthType)); ++ DtlsIdentity::Deserialize(aCertFingerprint, aAuthType)); + if (!dtlsIdentity) { + MOZ_ASSERT(false); + return; +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.h.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.h +--- firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.h.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandler.h 2024-09-30 21:41:07.650369114 +0200 +@@ -98,8 +98,8 @@ class MediaTransportHandler { + const std::string& aTransportId, const std::string& aLocalUfrag, + const std::string& aLocalPwd, size_t aComponentCount, + const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, ++ const nsTArray& aCertFingerprint, SSLKEAType aAuthType, ++ bool aDtlsClient, const DtlsDigestList& aDigests, + bool aPrivacyRequested) = 0; + + virtual void RemoveTransportsExcept( +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp +--- firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp 2024-09-30 21:41:07.650369114 +0200 +@@ -269,17 +269,16 @@ void MediaTransportHandlerIPC::ActivateT + const std::string& aTransportId, const std::string& aLocalUfrag, + const std::string& aLocalPwd, size_t aComponentCount, + const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, +- bool aPrivacyRequested) { ++ const nsTArray& aCertFingerprint, SSLKEAType aAuthType, ++ bool aDtlsClient, const DtlsDigestList& aDigests, bool aPrivacyRequested) { + mInitPromise->Then( + mCallbackThread, __func__, +- [=, keyDer = aKeyDer.Clone(), certDer = aCertDer.Clone(), ++ [=, certFingerprint = aCertFingerprint.Clone(), + self = RefPtr(this)](bool /*dummy*/) { + if (mChild) { + mChild->SendActivateTransport(aTransportId, aLocalUfrag, aLocalPwd, + aComponentCount, aUfrag, aPassword, +- keyDer, certDer, aAuthType, aDtlsClient, ++ certFingerprint, aAuthType, aDtlsClient, + aDigests, aPrivacyRequested); + } + }, +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.h.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.h +--- firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.h.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.h 2024-09-30 21:41:07.650369114 +0200 +@@ -49,13 +49,15 @@ class MediaTransportHandlerIPC final : p + // this up internally + const nsTArray& aStunAddrs) override; + +- void ActivateTransport( +- const std::string& aTransportId, const std::string& aLocalUfrag, +- const std::string& aLocalPwd, size_t aComponentCount, +- const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, +- bool aPrivacyRequested) override; ++ void ActivateTransport(const std::string& aTransportId, ++ const std::string& aLocalUfrag, ++ const std::string& aLocalPwd, size_t aComponentCount, ++ const std::string& aUfrag, ++ const std::string& aPassword, ++ const nsTArray& aCertFingerprint, ++ SSLKEAType aAuthType, bool aDtlsClient, ++ const DtlsDigestList& aDigests, ++ bool aPrivacyRequested) override; + + void RemoveTransportsExcept( + const std::set& aTransportIds) override; +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportParent.cpp.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportParent.cpp +--- firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportParent.cpp.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/MediaTransportParent.cpp 2024-09-30 21:41:07.650369114 +0200 +@@ -174,12 +174,12 @@ mozilla::ipc::IPCResult MediaTransportPa + mozilla::ipc::IPCResult MediaTransportParent::RecvActivateTransport( + const string& transportId, const string& localUfrag, const string& localPwd, + const int& componentCount, const string& remoteUfrag, +- const string& remotePwd, nsTArray&& keyDer, +- nsTArray&& certDer, const int& authType, const bool& dtlsClient, +- const DtlsDigestList& digests, const bool& privacyRequested) { ++ const string& remotePwd, nsTArray&& certFingerprint, ++ const int& authType, const bool& dtlsClient, const DtlsDigestList& digests, ++ const bool& privacyRequested) { + mImpl->mHandler->ActivateTransport( + transportId, localUfrag, localPwd, componentCount, remoteUfrag, remotePwd, +- keyDer, certDer, static_cast(authType), dtlsClient, digests, ++ certFingerprint, static_cast(authType), dtlsClient, digests, + privacyRequested); + return ipc::IPCResult::Ok(); + } +diff -up firefox-128.2.0/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp.webrtc firefox-128.2.0/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp +--- firefox-128.2.0/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp 2024-09-30 21:41:07.650369114 +0200 +@@ -4353,9 +4353,8 @@ void PeerConnectionImpl::UpdateTransport + candidates.end()); + } + +- nsTArray keyDer; +- nsTArray certDer; +- nsresult rv = Identity()->Serialize(&keyDer, &certDer); ++ nsTArray certFingerprint; ++ nsresult rv = Identity()->Serialize(certFingerprint); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: Failed to serialize DTLS identity: %d", + __FUNCTION__, (int)rv); +@@ -4371,7 +4370,7 @@ void PeerConnectionImpl::UpdateTransport + + mTransportHandler->ActivateTransport( + transport.mTransportId, transport.mLocalUfrag, transport.mLocalPwd, +- components, ufrag, pwd, keyDer, certDer, Identity()->auth_type(), ++ components, ufrag, pwd, certFingerprint, Identity()->auth_type(), + transport.mDtls->GetRole() == JsepDtlsTransport::kJsepDtlsClient, digests, + PrivacyRequested()); + +diff -up firefox-128.2.0/dom/media/webrtc/MediaTransportParent.h.webrtc firefox-128.2.0/dom/media/webrtc/MediaTransportParent.h +--- firefox-128.2.0/dom/media/webrtc/MediaTransportParent.h.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/MediaTransportParent.h 2024-09-30 21:41:07.648369048 +0200 +@@ -40,9 +40,9 @@ class MediaTransportParent : public dom: + const string& transportId, const string& localUfrag, + const string& localPwd, const int& componentCount, + const string& remoteUfrag, const string& remotePwd, +- nsTArray&& keyDer, nsTArray&& certDer, +- const int& authType, const bool& dtlsClient, +- const DtlsDigestList& digests, const bool& privacyRequested); ++ nsTArray&& certFingerprint, const int& authType, ++ const bool& dtlsClient, const DtlsDigestList& digests, ++ const bool& privacyRequested); + mozilla::ipc::IPCResult RecvRemoveTransportsExcept( + const StringVector& transportIds); + mozilla::ipc::IPCResult RecvStartIceChecks(const bool& isControlling, +diff -up firefox-128.2.0/dom/media/webrtc/moz.build.webrtc firefox-128.2.0/dom/media/webrtc/moz.build +--- firefox-128.2.0/dom/media/webrtc/moz.build.webrtc 2024-08-26 16:23:34.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/moz.build 2024-09-30 21:41:07.650369114 +0200 +@@ -41,6 +41,18 @@ SOURCES += [ + "CubebDeviceEnumerator.cpp", + ] + ++XPCOM_MANIFESTS += [ ++ "components.conf", ++] ++ ++IPDL_SOURCES += ["PRTCCertServiceTransaction.ipdl"] ++ ++XPIDL_SOURCES += [ ++ "nsIRTCCertService.idl", ++] ++ ++XPIDL_MODULE = "rtc_certservice" ++ + if CONFIG["MOZ_WEBRTC"]: + EXPORTS += [ + "MediaEngineRemoteVideoSource.h", +@@ -51,7 +63,11 @@ if CONFIG["MOZ_WEBRTC"]: + UNIFIED_SOURCES += [ + "MediaEngineRemoteVideoSource.cpp", + "MediaEngineWebRTCAudio.cpp", ++ "RTCCertCache.cpp", + "RTCCertificate.cpp", ++ "RTCCertService.cpp", ++ "RTCCertServiceData.cpp", ++ "RTCCertServiceParent.cpp", + "RTCIdentityProviderRegistrar.cpp", + ] + # MediaEngineWebRTC.cpp needs to be built separately. +@@ -113,7 +129,11 @@ EXPORTS.mozilla += [ + "PeerIdentity.h", + ] + EXPORTS.mozilla.dom += [ ++ "RTCCertCache.h", + "RTCCertificate.h", ++ "RTCCertService.h", ++ "RTCCertServiceData.h", ++ "RTCCertServiceParent.h", + ] + + include("/ipc/chromium/chromium-config.mozbuild") +diff -up firefox-128.2.0/dom/media/webrtc/nsIRTCCertService.idl.webrtc firefox-128.2.0/dom/media/webrtc/nsIRTCCertService.idl +--- firefox-128.2.0/dom/media/webrtc/nsIRTCCertService.idl.webrtc 2024-09-30 21:41:07.651369147 +0200 ++++ firefox-128.2.0/dom/media/webrtc/nsIRTCCertService.idl 2024-09-30 21:41:07.650369114 +0200 +@@ -0,0 +1,34 @@ ++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ ++/* 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 "nsISupports.idl" ++ ++%{C++ ++#include "mozilla/dom/RTCCertServiceData.h" ++%} ++ ++native CertFingerprint(mozilla::dom::CertFingerprint); ++native RTCCertFingerprintPromise(RefPtr); ++ ++native CertData(mozilla::dom::CertData); ++native RTCCertificatePromise(RefPtr); ++ ++[ptr] native CERTCertificate(CERTCertificate); ++ ++[uuid(e665acb0-5952-11ef-bb8c-18c04d07c34d)] ++interface nsIRTCCertService : nsISupports ++{ ++ // Init the class ++ [notxpcom, nostdcall] void Initialize(); ++ ++ // Generate cert ++ [notxpcom, nostdcall] RTCCertFingerprintPromise GenerateCertificate(in Array aParam, in PRTime aExpires, in unsigned long aMechanism, in uint32_t aSignatureAlg); ++ ++ // Remove cert ++ [notxpcom, nostdcall] void RemoveCertificate([const] in CertFingerprint aCertFingerprint); ++ ++ // Get cert ++ [notxpcom, nostdcall] RTCCertificatePromise getCertificate([const] in CertFingerprint aCertFingerprint); ++}; +diff -up firefox-128.2.0/dom/media/webrtc/PMediaTransport.ipdl.webrtc firefox-128.2.0/dom/media/webrtc/PMediaTransport.ipdl +--- firefox-128.2.0/dom/media/webrtc/PMediaTransport.ipdl.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/PMediaTransport.ipdl 2024-09-30 21:41:07.648369048 +0200 +@@ -62,8 +62,7 @@ parent: + int componentCount, + string remoteUfrag, + string remotePwd, +- uint8_t[] keyDer, +- uint8_t[] certDer, ++ uint8_t[] certFingerprint, + int authType, + bool dtlsClient, + DtlsDigestList digests, +diff -up firefox-128.2.0/dom/media/webrtc/PRTCCertServiceTransaction.ipdl.webrtc firefox-128.2.0/dom/media/webrtc/PRTCCertServiceTransaction.ipdl +--- firefox-128.2.0/dom/media/webrtc/PRTCCertServiceTransaction.ipdl.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/PRTCCertServiceTransaction.ipdl 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,33 @@ ++/* 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/. */ ++ ++/* ++ * IPC Transaction protocol for the Cert Service DOM API. ++ * This IPC protocol allows to generate private / public keys and certificate ++ * in socket process and return public key and certificate back to ++ * content process. ++ */ ++ ++include protocol PBackground; ++ ++using PRTime from "prtime.h"; ++using mozilla::dom::CertFingerprint from "mozilla/dom/RTCCertServiceData.h"; ++using mozilla::dom::CertDataIPC from "mozilla/dom/RTCCertServiceData.h"; ++ ++namespace mozilla { ++namespace dom { ++ ++[ParentProc=Socket, ChildProc=Content, ChildImpl=virtual, ParentImpl=virtual] ++async protocol PRTCCertServiceTransaction { ++ parent: ++ async GenerateCertificate(uint8_t[] aParam, PRTime aExpires, uint32_t aMechanism, uint32_t aSignatureAlg) returns (CertFingerprint fingerprint); ++ async RemoveCertificate(CertFingerprint aCertFingerprint); ++ async GetCertificate(CertFingerprint aCertFingerprint) returns (CertDataIPC certificate); ++ ++ child: ++ async __delete__(); ++}; ++ ++} ++} +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertCache.cpp.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertCache.cpp +--- firefox-128.2.0/dom/media/webrtc/RTCCertCache.cpp.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertCache.cpp 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,48 @@ ++/* -*- 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 "RTCCertCache.h" ++ ++namespace mozilla::dom { ++ ++nsTArray> RTCCertCache::sCertCache; ++mozilla::StaticMutex RTCCertCache::sRTCCertCacheLock MOZ_UNANNOTATED; ++ ++void RTCCertCache::CacheCert(UniquePtr aCert) { ++ StaticMutexAutoLock CacheLock(sRTCCertCacheLock); ++ for (size_t i = 0; i < sCertCache.Length(); i++) { ++ if (!sCertCache[i]) { ++ sCertCache[i] = std::move(aCert); ++ return; ++ } ++ } ++ sCertCache.AppendElement(std::move(aCert)); ++} ++ ++GeneratedCertificate* RTCCertCache::LookupCert( ++ const CertFingerprint aCertFingerprint) { ++ StaticMutexAutoLock CacheLock(sRTCCertCacheLock); ++ for (size_t i = 0; i < sCertCache.Length(); i++) { ++ if (sCertCache[i] && ++ sCertCache[i]->mCertFingerprint.Match(&aCertFingerprint)) { ++ return sCertCache[i].get(); ++ } ++ } ++ return nullptr; ++} ++ ++void RTCCertCache::RemoveCert(const CertFingerprint aCertFingerprint) { ++ StaticMutexAutoLock CacheLock(sRTCCertCacheLock); ++ for (size_t i = 0; i < sCertCache.Length(); i++) { ++ if (sCertCache[i] && ++ sCertCache[i]->mCertFingerprint.Match(&aCertFingerprint)) { ++ sCertCache[i] = nullptr; ++ break; ++ } ++ } ++} ++ ++} // namespace mozilla::dom +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertCache.h.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertCache.h +--- firefox-128.2.0/dom/media/webrtc/RTCCertCache.h.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertCache.h 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,36 @@ ++/* -*- 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_CertCache_h ++#define mozilla_dom_CertCache_h ++ ++#include "mozilla/dom/RTCCertServiceData.h" ++#include "mozilla/StaticMutex.h" ++ ++namespace mozilla::dom { ++ ++struct GeneratedCertificate { ++ UniqueSECKEYPublicKey mPublicKey; ++ UniqueSECKEYPrivateKey mPrivateKey; ++ UniqueCERTCertificate mCertificate; ++ CertFingerprint mCertFingerprint; ++ PRTime mExpires = 0; ++}; ++ ++class RTCCertCache { ++ public: ++ static void CacheCert(UniquePtr aCert); ++ static GeneratedCertificate* LookupCert( ++ const CertFingerprint aCertFingerprint); ++ static void RemoveCert(const CertFingerprint aCertFingerprint); ++ ++ private: ++ static nsTArray> sCertCache; ++ static mozilla::StaticMutex sRTCCertCacheLock MOZ_UNANNOTATED; ++}; ++} // namespace mozilla::dom ++ ++#endif // mozilla_dom_CertCache_h +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertificate.cpp.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertificate.cpp +--- firefox-128.2.0/dom/media/webrtc/RTCCertificate.cpp.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertificate.cpp 2024-09-30 21:41:07.649369081 +0200 +@@ -25,13 +25,12 @@ + #include "mozilla/UniquePtr.h" + #include "mozilla/dom/BindingDeclarations.h" + #include "mozilla/dom/CryptoBuffer.h" +-#include "mozilla/dom/CryptoKey.h" + #include "mozilla/dom/KeyAlgorithmBinding.h" + #include "mozilla/dom/KeyAlgorithmProxy.h" + #include "mozilla/dom/Promise.h" + #include "mozilla/dom/RTCCertificateBinding.h" ++#include "mozilla/dom/RootedDictionary.h" + #include "mozilla/dom/StructuredCloneHolder.h" +-#include "mozilla/dom/SubtleCryptoBinding.h" + #include "mozilla/dom/UnionTypes.h" + #include "mozilla/dom/WebCryptoCommon.h" + #include "mozilla/dom/WebCryptoTask.h" +@@ -42,6 +41,7 @@ + #include "nsStringFlags.h" + #include "nsStringFwd.h" + #include "nsTLiteralString.h" ++#include "nsServiceManagerUtils.h" + #include "pk11pub.h" + #include "plarena.h" + #include "secasn1.h" +@@ -71,271 +71,203 @@ NS_INTERFACE_MAP_END + PRTime(PR_USEC_PER_SEC) * PRTime(60) /*sec*/ \ + * PRTime(60) /*min*/ * PRTime(24) /*hours*/ + #define EXPIRATION_DEFAULT ONE_DAY* PRTime(30) +-#define EXPIRATION_SLACK ONE_DAY + #define EXPIRATION_MAX ONE_DAY* PRTime(365) /*year*/ + +-const size_t RTCCertificateCommonNameLength = 16; + const size_t RTCCertificateMinRsaSize = 1024; + +-class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask { +- public: +- GenerateRTCCertificateTask(nsIGlobalObject* aGlobal, JSContext* aCx, +- const ObjectOrString& aAlgorithm, +- const Sequence& aKeyUsages, +- PRTime aExpires) +- : GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, true, aKeyUsages), +- mExpires(aExpires), +- mAuthType(ssl_kea_null), +- mCertificate(nullptr), +- mSignatureAlg(SEC_OID_UNKNOWN) { +- if (NS_FAILED(mEarlyRv)) { +- // webrtc-pc says to throw NotSupportedError if we have passed "an +- // algorithm that the user agent cannot or will not use to generate a +- // certificate". This catches these cases. +- mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; +- } ++static PRTime ReadExpires(JSContext* aCx, const ObjectOrString& aOptions, ++ ErrorResult& aRv) { ++ // This conversion might fail, but we don't really care; use the default. ++ // If this isn't an object, or it doesn't coerce into the right type, ++ // then we won't get the |expires| value. Either will be caught later. ++ RTCCertificateExpiration expiration; ++ if (!aOptions.IsObject()) { ++ return EXPIRATION_DEFAULT; + } +- +- private: +- PRTime mExpires; +- SSLKEAType mAuthType; +- UniqueCERTCertificate mCertificate; +- SECOidTag mSignatureAlg; +- +- static CERTName* GenerateRandomName(PK11SlotInfo* aSlot) { +- uint8_t randomName[RTCCertificateCommonNameLength]; +- SECStatus rv = +- PK11_GenerateRandomOnSlot(aSlot, randomName, sizeof(randomName)); +- if (rv != SECSuccess) { +- return nullptr; +- } +- +- char buf[sizeof(randomName) * 2 + 4]; +- strncpy(buf, "CN=", 4); +- for (size_t i = 0; i < sizeof(randomName); ++i) { +- snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]); +- } +- buf[sizeof(buf) - 1] = '\0'; +- +- return CERT_AsciiToName(buf); ++ JS::Rooted value(aCx, JS::ObjectValue(*aOptions.GetAsObject())); ++ if (!expiration.Init(aCx, value)) { ++ aRv.NoteJSContextException(aCx); ++ return 0; + } + +- nsresult GenerateCertificate() { +- UniquePK11SlotInfo slot(PK11_GetInternalSlot()); +- MOZ_ASSERT(slot.get()); +- +- UniqueCERTName subjectName(GenerateRandomName(slot.get())); +- if (!subjectName) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++ if (!expiration.mExpires.WasPassed()) { ++ return EXPIRATION_DEFAULT; ++ } ++ static const uint64_t max = ++ static_cast(EXPIRATION_MAX / PR_USEC_PER_MSEC); ++ if (expiration.mExpires.Value() > max) { ++ return EXPIRATION_MAX; ++ } ++ return static_cast(expiration.mExpires.Value() * PR_USEC_PER_MSEC); ++} + +- UniqueSECKEYPublicKey publicKey(mKeyPair->mPublicKey->GetPublicKey()); +- UniqueCERTSubjectPublicKeyInfo spki( +- SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); +- if (!spki) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++RTCCertificateMetadata::RTCCertificateMetadata() ++ : mExpires(0), ++ mSignatureAlg(SEC_OID_UNKNOWN), ++ mMechanism(CKM_INVALID_MECHANISM), ++ mRsaParams() {} ++ ++nsresult RTCCertificateMetadata::Init(JSContext* aCx, ++ const ObjectOrString& aAlgorithm, ++ SSLKEAType* aAuthType, ErrorResult& aRv) { ++ mExpires = ReadExpires(aCx, aAlgorithm, aRv); ++ if (aRv.Failed()) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } + +- UniqueCERTCertificateRequest certreq( +- CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr)); +- if (!certreq) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++ mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); ++ if (!mArena) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } + +- PRTime now = PR_Now(); +- PRTime notBefore = now - EXPIRATION_SLACK; +- mExpires += now; ++ // Extract algorithm name ++ nsresult rv = GetAlgorithmName(aCx, aAlgorithm, mAlgName); ++ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_NOT_SUPPORTED_ERR); + +- UniqueCERTValidity validity(CERT_CreateValidity(notBefore, mExpires)); +- if (!validity) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++ // Construct an appropriate KeyAlorithm ++ if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { ++ RootedDictionary params(aCx); ++ rv = Coerce(aCx, params, aAlgorithm); ++ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); + +- unsigned long serial; +- // Note: This serial in principle could collide, but it's unlikely, and we +- // don't expect anyone to be validating certificates anyway. +- SECStatus rv = PK11_GenerateRandomOnSlot( +- slot.get(), reinterpret_cast(&serial), sizeof(serial)); +- if (rv != SECSuccess) { ++ // Pull relevant info ++ uint32_t modulusLength = params.mModulusLength; ++ CryptoBuffer publicExponent; ++ if (!publicExponent.Assign(params.mPublicExponent)) { + return NS_ERROR_DOM_UNKNOWN_ERR; + } + +- // NB: CERTCertificates created with CERT_CreateCertificate are not safe to +- // use with other NSS functions like CERT_DupCertificate. The strategy +- // here is to create a tbsCertificate ("to-be-signed certificate"), encode +- // it, and sign it, resulting in a signed DER certificate that can be +- // decoded into a CERTCertificate. +- UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate( +- serial, subjectName.get(), validity.get(), certreq.get())); +- if (!tbsCertificate) { +- return NS_ERROR_DOM_UNKNOWN_ERR; ++ nsString hashName; ++ rv = GetAlgorithmName(aCx, params.mHash, hashName); ++ NS_ENSURE_SUCCESS(rv, rv); ++ if (!hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { ++ return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + +- MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN); +- PLArenaPool* arena = tbsCertificate->arena; ++ mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + +- rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature, mSignatureAlg, +- nullptr); +- if (rv != SECSuccess) { +- return NS_ERROR_DOM_UNKNOWN_ERR; ++ // Set up params struct ++ mRsaParams.keySizeInBits = modulusLength; ++ bool converted = publicExponent.GetBigIntValue(mRsaParams.pe); ++ if (!converted) { ++ return NS_ERROR_DOM_INVALID_ACCESS_ERR; + } + +- // Set version to X509v3. +- *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3; +- tbsCertificate->version.len = 1; +- +- SECItem innerDER = {siBuffer, nullptr, 0}; +- if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(), +- SEC_ASN1_GET(CERT_CertificateTemplate))) { +- return NS_ERROR_DOM_UNKNOWN_ERR; ++ auto sz = static_cast(mRsaParams.keySizeInBits); ++ if (sz < RTCCertificateMinRsaSize) { ++ return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + +- SECItem* certDer = PORT_ArenaZNew(arena, SECItem); +- if (!certDer) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++ SerializeRSAParam(&mParam, &mRsaParams); + +- UniqueSECKEYPrivateKey privateKey(mKeyPair->mPrivateKey->GetPrivateKey()); +- rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len, +- privateKey.get(), mSignatureAlg); +- if (rv != SECSuccess) { +- return NS_ERROR_DOM_UNKNOWN_ERR; +- } ++ mSignatureAlg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; ++ *aAuthType = ssl_kea_rsa; ++ } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) { ++ RootedDictionary params(aCx); ++ rv = Coerce(aCx, params, aAlgorithm); ++ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); + +- mCertificate.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDer, +- nullptr, false, true)); +- if (!mCertificate) { +- return NS_ERROR_DOM_UNKNOWN_ERR; ++ if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) { ++ return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } +- return NS_OK; +- } +- +- nsresult BeforeCrypto() override { +- if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { +- // Double check that size is OK. +- auto sz = static_cast(mRsaParams.keySizeInBits); +- if (sz < RTCCertificateMinRsaSize) { +- return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +- } +- +- KeyAlgorithmProxy& alg = mKeyPair->mPublicKey->Algorithm(); +- if (alg.mType != KeyAlgorithmProxy::RSA || +- !alg.mRsa.mHash.mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { +- return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +- } +- +- mSignatureAlg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; +- mAuthType = ssl_kea_rsa; +- +- } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) { +- // We only support good curves in WebCrypto. +- // If that ever changes, check that a good one was chosen. +- +- mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; +- mAuthType = ssl_kea_ecdh; +- } else { ++ mMechanism = CKM_EC_KEY_PAIR_GEN; ++ if (!SerializeECParams(&mParam, ++ CreateECParamsForCurve(mNamedCurve, mArena.get()))) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } +- return NS_OK; +- } + +- nsresult DoCrypto() override { +- nsresult rv = GenerateAsymmetricKeyTask::DoCrypto(); +- NS_ENSURE_SUCCESS(rv, rv); +- +- rv = GenerateCertificate(); +- NS_ENSURE_SUCCESS(rv, rv); +- +- return NS_OK; ++ // We only support good curves in WebCrypto. ++ // If that ever changes, check that a good one was chosen. ++ mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; ++ *aAuthType = ssl_kea_ecdh; ++ } else { ++ return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + +- virtual void Resolve() override { +- // Make copies of the private key and certificate, otherwise, when this +- // object is deleted, the structures they reference will be deleted too. +- UniqueSECKEYPrivateKey key = mKeyPair->mPrivateKey->GetPrivateKey(); +- CERTCertificate* cert = CERT_DupCertificate(mCertificate.get()); +- RefPtr result = +- new RTCCertificate(mResultPromise->GetParentObject(), key.release(), +- cert, mAuthType, mExpires); +- mResultPromise->MaybeResolve(result); +- } +-}; +- +-static PRTime ReadExpires(JSContext* aCx, const ObjectOrString& aOptions, +- ErrorResult& aRv) { +- // This conversion might fail, but we don't really care; use the default. +- // If this isn't an object, or it doesn't coerce into the right type, +- // then we won't get the |expires| value. Either will be caught later. +- RTCCertificateExpiration expiration; +- if (!aOptions.IsObject()) { +- return EXPIRATION_DEFAULT; +- } +- JS::Rooted value(aCx, JS::ObjectValue(*aOptions.GetAsObject())); +- if (!expiration.Init(aCx, value)) { +- aRv.NoteJSContextException(aCx); +- return 0; +- } ++ return NS_OK; ++} + +- if (!expiration.mExpires.WasPassed()) { +- return EXPIRATION_DEFAULT; +- } +- static const uint64_t max = +- static_cast(EXPIRATION_MAX / PR_USEC_PER_MSEC); +- if (expiration.mExpires.Value() > max) { +- return EXPIRATION_MAX; +- } +- return static_cast(expiration.mExpires.Value() * PR_USEC_PER_MSEC); ++RefPtr RTCCertificateMetadata::Generate( ++ nsCOMPtr aCertService) { ++ return aCertService->GenerateCertificate(mParam, mExpires, mMechanism, ++ mSignatureAlg); + } + +-already_AddRefed RTCCertificate::GenerateCertificate( ++already_AddRefed RTCCertificate::Generate( + const GlobalObject& aGlobal, const ObjectOrString& aOptions, +- ErrorResult& aRv, JS::Compartment* aCompartment) { ++ ErrorResult& aRv) { + nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get()); +- RefPtr p = Promise::Create(global, aRv); ++ RefPtr resultPromise = Promise::Create(global, aRv); + if (aRv.Failed()) { + return nullptr; + } +- Sequence usages; +- if (!usages.AppendElement(u"sign"_ns, fallible)) { +- aRv.Throw(NS_ERROR_OUT_OF_MEMORY); ++ ++ nsresult rv = mData.Init(aGlobal.Context(), aOptions, &mAuthType, aRv); ++ if (NS_FAILED(rv)) { ++ // webrtc-pc says to throw NotSupportedError if we have passed "an ++ // algorithm that the user agent cannot or will not use to generate a ++ // certificate". This catches these cases. ++ if (!aRv.Failed()) { ++ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); ++ } + return nullptr; + } + +- PRTime expires = ReadExpires(aGlobal.Context(), aOptions, aRv); +- if (aRv.Failed()) { ++ mCertService = do_GetService("@mozilla.org/rtccert/service;1"); ++ if (!mCertService) { ++ aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; + } +- RefPtr task = new GenerateRTCCertificateTask( +- global, aGlobal.Context(), aOptions, usages, expires); +- task->DispatchWithPromise(p); +- return p.forget(); +-} +- +-RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal) +- : mGlobal(aGlobal), +- mPrivateKey(nullptr), +- mCertificate(nullptr), +- mAuthType(ssl_kea_null), +- mExpires(0) {} +- +-RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal, +- SECKEYPrivateKey* aPrivateKey, +- CERTCertificate* aCertificate, +- SSLKEAType aAuthType, PRTime aExpires) +- : mGlobal(aGlobal), +- mPrivateKey(aPrivateKey), +- mCertificate(aCertificate), +- mAuthType(aAuthType), +- mExpires(aExpires) {} ++ ++ mData.Generate(mCertService) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [s = RefPtr{this}, this, ++ resultPromise](const CertFingerprint& aResult) { ++ mCertFingerprint = aResult; ++ mCertService->GetCertificate(mCertFingerprint) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [self = RefPtr{s}, ++ resultPromise](UniquePtr&& aResult) mutable { ++ self->mCertificate = std::move(aResult->mCertificate); ++ self->mExpires = aResult->mExpires; ++ resultPromise->MaybeResolve(self); ++ }, ++ [self = RefPtr{s}, resultPromise](nsresult aError) { ++ resultPromise->MaybeReject(aError); ++ }); ++ }, ++ [s = RefPtr{this}, resultPromise](nsresult aError) { ++ resultPromise->MaybeReject(aError); ++ }); ++ ++ return resultPromise.forget(); ++} ++ ++already_AddRefed RTCCertificate::GenerateCertificate( ++ const GlobalObject& aGlobal, const ObjectOrString& aOptions, ++ ErrorResult& aRv, JS::Compartment* aCompartment) { ++ RefPtr cert = ++ new RTCCertificate(xpc::NativeGlobal(aGlobal.Get())); ++ return cert->Generate(aGlobal, aOptions, aRv); ++} ++ ++RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {}; ++ ++RTCCertificate::~RTCCertificate() { ++ /* TODO -> how to handle clone? ++ if (mCertService && mCertificate) { ++ mCertService->RemoveCertificate(mCertFingerprint); ++ } ++ */ ++} + + RefPtr RTCCertificate::CreateDtlsIdentity() const { +- if (!mPrivateKey || !mCertificate) { ++ if (!mCertificate) { + return nullptr; + } +- UniqueSECKEYPrivateKey key(SECKEY_CopyPrivateKey(mPrivateKey.get())); +- UniqueCERTCertificate cert(CERT_DupCertificate(mCertificate.get())); +- RefPtr id = +- new DtlsIdentity(std::move(key), std::move(cert), mAuthType); ++ RefPtr id = new DtlsIdentity(mCertFingerprint, mAuthType); + return id; + } + +@@ -344,17 +276,10 @@ JSObject* RTCCertificate::WrapObject(JSC + return RTCCertificate_Binding::Wrap(aCx, this, aGivenProto); + } + +-bool RTCCertificate::WritePrivateKey(JSStructuredCloneWriter* aWriter) const { +- JsonWebKey jwk; +- nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), jwk); +- if (NS_FAILED(rv)) { +- return false; +- } +- nsString json; +- if (!jwk.ToJSON(json)) { +- return false; +- } +- return StructuredCloneHolder::WriteString(aWriter, json); ++bool RTCCertificate::WriteCertificateFingerprint( ++ JSStructuredCloneWriter* aWriter) const { ++ return JS_WriteBytes(aWriter, mCertFingerprint.mHash, ++ CertFingerprint::sHashByteLen); + } + + bool RTCCertificate::WriteCertificate(JSStructuredCloneWriter* aWriter) const { +@@ -370,27 +295,23 @@ bool RTCCertificate::WriteCertificate(JS + + bool RTCCertificate::WriteStructuredClone( + JSContext* aCx, JSStructuredCloneWriter* aWriter) const { +- if (!mPrivateKey || !mCertificate) { ++ if (!mCertificate) { + return false; + } + + return JS_WriteUint32Pair(aWriter, RTCCERTIFICATE_SC_VERSION, mAuthType) && + JS_WriteUint32Pair(aWriter, (mExpires >> 32) & 0xffffffff, + mExpires & 0xffffffff) && +- WritePrivateKey(aWriter) && WriteCertificate(aWriter); ++ WriteCertificateFingerprint(aWriter) && WriteCertificate(aWriter); + } + +-bool RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader) { +- nsString json; +- if (!StructuredCloneHolder::ReadString(aReader, json)) { +- return false; +- } +- JsonWebKey jwk; +- if (!jwk.Init(json)) { ++bool RTCCertificate::ReadCertificateFingerprint( ++ JSStructuredCloneReader* aReader) { ++ if (!JS_ReadBytes(aReader, mCertFingerprint.mHash, ++ CertFingerprint::sHashByteLen)) { + return false; + } +- mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk); +- return !!mPrivateKey; ++ return true; + } + + bool RTCCertificate::ReadCertificate(JSStructuredCloneReader* aReader) { +@@ -428,7 +349,8 @@ already_AddRefed RTCCert + } + cert->mExpires = static_cast(high) << 32 | low; + +- if (!cert->ReadPrivateKey(aReader) || !cert->ReadCertificate(aReader)) { ++ if (!cert->ReadCertificateFingerprint(aReader) || ++ !cert->ReadCertificate(aReader)) { + return nullptr; + } + +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertificate.h.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertificate.h +--- firefox-128.2.0/dom/media/webrtc/RTCCertificate.h.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertificate.h 2024-09-30 21:41:07.649369081 +0200 +@@ -15,7 +15,11 @@ + #include "mozilla/AlreadyAddRefed.h" + #include "mozilla/Assertions.h" + #include "mozilla/RefPtr.h" ++#include "mozilla/dom/SubtleCryptoBinding.h" ++#include "mozilla/MozPromise.h" ++#include "mozilla/dom/RTCCertService.h" + #include "nsCycleCollectionParticipant.h" ++#include "nsICancelableRunnable.h" + #include "nsIGlobalObject.h" + #include "nsISupports.h" + #include "nsWrapperCache.h" +@@ -41,6 +45,26 @@ class GlobalObject; + class ObjectOrString; + class Promise; + ++class RTCCertificateMetadata { ++ public: ++ RTCCertificateMetadata(); ++ ++ nsresult Init(JSContext* aCx, const ObjectOrString& aAlgorithm, ++ SSLKEAType* aAuthType, ErrorResult& aRv); ++ RefPtr Generate( ++ nsCOMPtr aCertService); ++ ++ private: ++ nsTArray mParam; ++ PRTime mExpires; ++ SECOidTag mSignatureAlg; ++ UniquePLArenaPool mArena; ++ CK_MECHANISM_TYPE mMechanism; ++ PK11RSAGenParams mRsaParams; ++ nsString mNamedCurve; ++ nsString mAlgName; ++}; ++ + class RTCCertificate final : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS +@@ -52,9 +76,6 @@ class RTCCertificate final : public nsIS + ErrorResult& aRv, JS::Compartment* aCompartment = nullptr); + + explicit RTCCertificate(nsIGlobalObject* aGlobal); +- RTCCertificate(nsIGlobalObject* aGlobal, SECKEYPrivateKey* aPrivateKey, +- CERTCertificate* aCertificate, SSLKEAType aAuthType, +- PRTime aExpires); + + nsIGlobalObject* GetParentObject() const { return mGlobal; } + virtual JSObject* WrapObject(JSContext* aCx, +@@ -76,20 +97,29 @@ class RTCCertificate final : public nsIS + JSStructuredCloneReader* aReader); + + private: +- ~RTCCertificate() = default; ++ // TODO: cert ref counts? -> clone = remove? ++ ~RTCCertificate(); + void operator=(const RTCCertificate&) = delete; + RTCCertificate(const RTCCertificate&) = delete; + ++ already_AddRefed Generate(const GlobalObject& aGlobal, ++ const ObjectOrString& aOptions, ++ ErrorResult& aRv); ++ + bool ReadCertificate(JSStructuredCloneReader* aReader); +- bool ReadPrivateKey(JSStructuredCloneReader* aReader); ++ bool ReadCertificateFingerprint(JSStructuredCloneReader* aReader); + bool WriteCertificate(JSStructuredCloneWriter* aWriter) const; +- bool WritePrivateKey(JSStructuredCloneWriter* aWriter) const; ++ bool WriteCertificateFingerprint(JSStructuredCloneWriter* aWriter) const; + + RefPtr mGlobal; +- UniqueSECKEYPrivateKey mPrivateKey; ++ CertFingerprint mCertFingerprint; ++ ++ RTCCertificateMetadata mData; ++ ++ nsCOMPtr mCertService; + UniqueCERTCertificate mCertificate; +- SSLKEAType mAuthType; +- PRTime mExpires; ++ SSLKEAType mAuthType = ssl_kea_null; ++ PRTime mExpires = 0; + }; + + } // namespace dom +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertService.cpp.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertService.cpp +--- firefox-128.2.0/dom/media/webrtc/RTCCertService.cpp.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertService.cpp 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,154 @@ ++/* -*- 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 "RTCCertService.h" ++#include "mozilla/net/SocketProcessBridgeChild.h" ++#include "mozilla/ipc/BackgroundChild.h" ++#include "mozilla/ipc/Endpoint.h" ++#include "mozilla/ipc/PBackgroundChild.h" ++ ++namespace mozilla::dom { ++ ++NS_IMPL_ISUPPORTS(RTCCertService, nsIRTCCertService) ++ ++already_AddRefed NewRTCCertService() { ++ nsCOMPtr certService(new RTCCertService()); ++ certService->Initialize(); ++ return certService.forget(); ++} ++ ++void RTCCertService::Initialize() { ++ using EndpointPromise = ++ MozPromise, ++ nsCString, true>; ++ mInitPromise = ++ net::SocketProcessBridgeChild::GetSocketProcessBridge() ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [](const RefPtr& aBridge) { ++ mozilla::ipc::Endpoint ++ parentEndpoint; ++ mozilla::ipc::Endpoint ++ childEndpoint; ++ ++ mozilla::dom::PRTCCertServiceTransaction::CreateEndpoints( ++ &parentEndpoint, &childEndpoint); ++ ++ if (!aBridge || !aBridge->SendInitRTCCertServiceTransaction( ++ std::move(parentEndpoint))) { ++ NS_WARNING( ++ "RTCCertService async init failed! Webrtc " ++ "networking " ++ "will not work!"); ++ return EndpointPromise::CreateAndReject( ++ nsCString("SendInitRTCCertServiceTransaction failed!"), ++ __func__); ++ } ++ return EndpointPromise::CreateAndResolve( ++ std::move(childEndpoint), __func__); ++ }, ++ [](const nsCString& aError) { ++ return EndpointPromise::CreateAndReject(aError, __func__); ++ }) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [this, self = RefPtr(this)]( ++ mozilla::ipc::Endpoint&& ++ aEndpoint) { ++ RefPtr child = ++ new RTCCertServiceTransactionChild(); ++ aEndpoint.Bind(child); ++ mChild = child; ++ ++ return InitPromise::CreateAndResolve(true, __func__); ++ }, ++ [=](const nsCString& aError) { ++ NS_WARNING( ++ "RTCCertService async init failed! Webrtc " ++ "networking " ++ "will not work!"); ++ return InitPromise::CreateAndReject(aError, __func__); ++ }); ++} ++ ++RefPtr RTCCertService::GenerateCertificate( ++ const nsTArray& aParam, PRTime aExpires, uint32_t aMechanism, ++ uint32_t aSignatureAlg) { ++ return mInitPromise->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [self = RefPtr(this), this, param = aParam.Clone(), ++ aExpires, aMechanism, aSignatureAlg](bool /* dummy */) { ++ if (!mChild) { ++ return RTCCertFingerprintPromise::CreateAndReject(NS_ERROR_FAILURE, ++ __func__); ++ } ++ RefPtr promise = ++ mChild ++ ->SendGenerateCertificate(param, aExpires, aMechanism, ++ aSignatureAlg) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [](CertFingerprint&& aCertFingerprint) { ++ return RTCCertFingerprintPromise::CreateAndResolve( ++ std::move(aCertFingerprint), __func__); ++ }, ++ [](mozilla::ipc::ResponseRejectReason aReason) { ++ return RTCCertFingerprintPromise::CreateAndReject( ++ NS_ERROR_FAILURE, __func__); ++ }); ++ return promise; ++ }, ++ [](const nsCString& aError) { ++ return RTCCertFingerprintPromise::CreateAndReject(NS_ERROR_FAILURE, ++ __func__); ++ }); ++} ++ ++void RTCCertService::RemoveCertificate( ++ const mozilla::dom::CertFingerprint aCertFingerprint) { ++ mInitPromise->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [self = RefPtr(this), this, ++ aCertFingerprint](bool /* dummy */) { ++ if (mChild) { ++ mChild->SendRemoveCertificate(aCertFingerprint); ++ } ++ }, ++ [](const nsCString& aError) {}); ++} ++ ++RefPtr RTCCertService::GetCertificate( ++ const CertFingerprint aCertFingerprint) { ++ return mInitPromise->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [self = RefPtr(this), this, ++ aCertFingerprint](bool /* dummy */) { ++ if (!mChild) { ++ return RTCCertificatePromise::CreateAndReject(NS_ERROR_FAILURE, ++ __func__); ++ } ++ RefPtr promise = ++ mChild->SendGetCertificate(aCertFingerprint) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [](const CertDataIPC& aCertDataIPC) { ++ return RTCCertificatePromise::CreateAndResolve( ++ MakeUnique(&aCertDataIPC), __func__); ++ }, ++ ++ [](mozilla::ipc::ResponseRejectReason aReason) { ++ return RTCCertificatePromise::CreateAndReject( ++ NS_ERROR_FAILURE, __func__); ++ }); ++ return promise; ++ }, ++ [](const nsCString& aError) { ++ return RTCCertificatePromise::CreateAndReject(NS_ERROR_FAILURE, ++ __func__); ++ }); ++} ++ ++} // namespace mozilla::dom +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.cpp.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.cpp +--- firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.cpp.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.cpp 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,77 @@ ++/* -*- 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 "RTCCertServiceData.h" ++#include "cert.h" ++#include "mozpkix/nss_scoped_ptrs.h" ++#include "sslerr.h" ++ ++namespace mozilla::dom { ++ ++CertFingerprint::CertFingerprint(const nsTArray& aCertFingerprint) { ++ MOZ_ASSERT(aCertFingerprint.Length() == sHashByteLen); ++ memcpy(mHash, const_cast(aCertFingerprint.Elements()), ++ static_cast(aCertFingerprint.Length())); ++} ++ ++CertFingerprint::operator nsTArray() { ++ nsTArray ret; ++ ret.AppendElements(reinterpret_cast(mHash), sHashByteLen); ++ return ret; ++} ++ ++bool CertFingerprint::Match(const CertFingerprint* aCertFingerprint) { ++ return mHash[0] == aCertFingerprint->mHash[0] && ++ mHash[1] == aCertFingerprint->mHash[1]; ++} ++ ++CertDataIPC::CertDataIPC(const CertData* aCertData) { ++ mExpires = aCertData->mExpires; ++ mCertificate.AppendElements(aCertData->mCertificate->derCert.data, ++ aCertData->mCertificate->derCert.len); ++} ++ ++CertData::CertData(const CertDataIPC* aCertDataIPC) { ++ SECItem certDer = { ++ siBuffer, const_cast(aCertDataIPC->mCertificate.Elements()), ++ static_cast(aCertDataIPC->mCertificate.Length())}; ++ UniqueCERTCertificate cert(CERT_NewTempCertificate( ++ CERT_GetDefaultCertDB(), &certDer, nullptr, true, true)); ++ mCertificate = std::move(cert); ++ mExpires = aCertDataIPC->mExpires; ++} ++ ++void SerializeRSAParam(nsTArray* aParams, ++ PK11RSAGenParams* aRsaParams) { ++ aParams->AppendElements(reinterpret_cast(aRsaParams), ++ sizeof(*aRsaParams)); ++} ++ ++PK11RSAGenParams DeserializeRSAParam(nsTArray* aParams) { ++ MOZ_ASSERT(aParams->Length() <= sizeof(PK11RSAGenParams)); ++ return *(reinterpret_cast(aParams->Elements())); ++} ++ ++bool SerializeECParams(nsTArray* aParams, SECItem* aECParams) { ++ if (!aECParams) { ++ return false; ++ } ++ aParams->AppendElements(reinterpret_cast(aECParams->data), ++ aECParams->len); ++ return true; ++} ++ ++SECItem* DeserializeECParams(nsTArray* aParams) { ++ SECItem* ret = ::SECITEM_AllocItem(nullptr, nullptr, 0); ++ SECItem it = {siBuffer, reinterpret_cast(aParams->Elements()), ++ static_cast(aParams->Length())}; ++ if (::SECITEM_CopyItem(nullptr, ret, &it) != SECSuccess) { ++ return nullptr; ++ } ++ return ret; ++} ++ ++} // namespace mozilla::dom +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.h.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.h +--- firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.h.webrtc 2024-09-30 21:41:07.649369081 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertServiceData.h 2024-09-30 21:41:07.649369081 +0200 +@@ -0,0 +1,105 @@ ++/* 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_RTCCertServiceGlobal_h_ ++#define mozilla_dom_RTCCertServiceGlobal_h_ ++ ++#include "ScopedNSSTypes.h" ++#include "ipc/IPCMessageUtils.h" ++#include "mozilla/ipc/IPDLParamTraits.h" ++#include "mozilla/MozPromise.h" ++ ++namespace mozilla { ++namespace dom { ++ ++struct CertFingerprint { ++ CertFingerprint() = default; ++ explicit CertFingerprint(const nsTArray& aCertFingerprint); ++ operator nsTArray(); ++ ++ bool Match(const struct CertFingerprint* aCertFingerprint); ++ unsigned char* AsChar() { return reinterpret_cast(mHash); } ++ ++ public: ++ const static size_t sHashByteLen = 16; ++ uint64_t mHash[2]; ++}; ++ ++struct CertData; ++struct CertDataIPC { ++ CertDataIPC() = default; ++ explicit CertDataIPC(const CertData* aCertData); ++ ++ public: ++ nsTArray mCertificate; ++ PRTime mExpires; ++}; ++ ++struct CertData { ++ CertData(UniqueCERTCertificate aCertificate, PRTime aExpires) ++ : mCertificate(std::move(aCertificate)), mExpires(aExpires) {} ++ explicit CertData(const CertDataIPC* aCertDataIPC); ++ ++ // Don't copy CertData ++ CertData(const CertData&) = delete; ++ CertData& operator=(const CertData&) = delete; ++ ++ public: ++ UniqueCERTCertificate mCertificate; ++ PRTime mExpires; ++}; ++ ++using RTCCertFingerprintPromise = ++ MozPromise; ++using RTCCertificatePromise = ++ MozPromise, nsresult, /* IsExclusive = */ true>; ++ ++void SerializeRSAParam(nsTArray* aParams, ++ PK11RSAGenParams* aRsaParams); ++PK11RSAGenParams DeserializeRSAParam(nsTArray* aParams); ++ ++bool SerializeECParams(nsTArray* aParams, SECItem* aECParams); ++SECItem* DeserializeECParams(nsTArray* aParams); ++} // namespace dom ++ ++namespace ipc { ++template <> ++struct IPDLParamTraits { ++ typedef dom::CertFingerprint paramType; ++ static void Write(IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, ++ const paramType& aVar) { ++ WriteIPDLParam(aWriter, aActor, aVar.mHash[0]); ++ WriteIPDLParam(aWriter, aActor, aVar.mHash[1]); ++ } ++ static bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, ++ paramType* aVar) { ++ if (!ReadIPDLParam(aReader, aActor, aVar->mHash) || ++ !ReadIPDLParam(aReader, aActor, aVar->mHash + 1)) { ++ return false; ++ } ++ return true; ++ } ++}; ++ ++template <> ++struct IPDLParamTraits { ++ typedef dom::CertDataIPC paramType; ++ static void Write(IPC::MessageWriter* aWriter, ipc::IProtocol* aActor, ++ const paramType& aVar) { ++ WriteIPDLParam(aWriter, aActor, aVar.mCertificate); ++ WriteIPDLParam(aWriter, aActor, aVar.mExpires); ++ } ++ static bool Read(IPC::MessageReader* aReader, mozilla::ipc::IProtocol* aActor, ++ paramType* aVar) { ++ if (!ReadIPDLParam(aReader, aActor, &aVar->mCertificate) || ++ !ReadIPDLParam(aReader, aActor, &aVar->mExpires)) { ++ return false; ++ } ++ return true; ++ } ++}; ++} // namespace ipc ++} // namespace mozilla ++ ++#endif // mozilla_dom_RTCCertServiceGlobal_h_ +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertService.h.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertService.h +--- firefox-128.2.0/dom/media/webrtc/RTCCertService.h.webrtc 2024-09-30 21:41:07.648369048 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertService.h 2024-09-30 21:41:07.648369048 +0200 +@@ -0,0 +1,49 @@ ++/* -*- 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_RTCCertService_h_ ++#define mozilla_dom_RTCCertService_h_ ++ ++#include "mozilla/dom/PRTCCertServiceTransactionChild.h" ++#include "nsIRTCCertService.h" ++#include "mozilla/RefPtr.h" ++ ++namespace mozilla::dom { ++ ++already_AddRefed NewRTCCertService(); ++ ++class RTCCertServiceTransactionChild : public PRTCCertServiceTransactionChild { ++ public: ++ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RTCCertServiceTransactionChild); ++ ++ private: ++ ~RTCCertServiceTransactionChild() = default; ++}; ++ ++class RTCCertService final : public nsIRTCCertService { ++ public: ++ NS_DECL_THREADSAFE_ISUPPORTS ++ NS_DECL_NSIRTCCERTSERVICE ++ ++ RTCCertService() = default; ++ ++ private: ++ ~RTCCertService() = default; ++ ++ RefPtr mChild; ++ ++ // |mChild| can only be initted asynchronously, |mInitPromise| resolves ++ // when that happens. The |Then| calls make it convenient to dispatch API ++ // calls to main, which is a bonus. ++ // Init promise is not exclusive; this lets us call |Then| on it for every ++ // API call we get, instead of creating another promise each time. ++ typedef MozPromise InitPromise; ++ RefPtr mInitPromise; ++}; ++ ++} // namespace mozilla::dom ++ ++#endif // mozilla_dom_CertServiceChild_h +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.cpp.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.cpp +--- firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.cpp.webrtc 2024-09-30 21:41:07.649369081 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.cpp 2024-09-30 21:41:07.649369081 +0200 +@@ -0,0 +1,353 @@ ++/* -*- 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 "RTCCertCache.h" ++#include "RTCCertServiceParent.h" ++#include "mozilla/ipc/PBackgroundParent.h" ++#include "mozilla/ipc/BackgroundParent.h" ++ ++#define ONE_DAY \ ++ PRTime(PR_USEC_PER_SEC) * PRTime(60) /*sec*/ \ ++ * PRTime(60) /*min*/ * PRTime(24) /*hours*/ ++#define EXPIRATION_SLACK ONE_DAY ++ ++namespace mozilla::dom { ++ ++using RTCCertificateGeneratorPromise = ++ MozPromise, nsresult, ++ /* IsExclusive = */ true>; ++ ++class RTCCertificateGenerator final : public CancelableRunnable { ++ public: ++ RTCCertificateGenerator(); ++ RefPtr Generate(nsTArray& aParam, ++ PRTime aExpires, ++ CK_MECHANISM_TYPE aMechanism, ++ SECOidTag aSignatureAlg); ++ ++ private: ++ ~RTCCertificateGenerator(); ++ ++ bool IsOnOriginalThread() { ++ return !mOriginalEventTarget || mOriginalEventTarget->IsOnCurrentThread(); ++ } ++ ++ nsresult GenerateKeys(); ++ nsresult GenerateCertificate(); ++ ++ NS_IMETHOD Run() override; ++ nsresult Cancel() override; ++ void Finish(); ++ ++ UniquePtr mGen; ++ ++ // Source data ++ void* mParam = nullptr; ++ PK11RSAGenParams mRsaParams; ++ SECItem* mCurveParams = nullptr; ++ CK_MECHANISM_TYPE mMechanism = 0; ++ SECOidTag mSignatureAlg = SEC_OID_UNKNOWN; ++ nsresult mCryptoResult = NS_OK; ++ ++ RefPtr mGenPromise; ++ nsCOMPtr mOriginalEventTarget; ++}; ++ ++const size_t RTCCertificateCommonNameLength = 16; ++ ++nsresult RTCCertificateGenerator::GenerateKeys() { ++ UniquePK11SlotInfo slot(PK11_GetInternalSlot()); ++ MOZ_ASSERT(slot.get()); ++ ++ mGen->mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair( ++ slot.get(), mMechanism, mParam, TempPtrToSetter(&mGen->mPublicKey), ++ PR_FALSE, PR_TRUE, nullptr)); ++ ++ if (!mGen->mPrivateKey.get() || !mGen->mPublicKey.get()) { ++ return NS_ERROR_DOM_OPERATION_ERR; ++ } ++ ++ return NS_OK; ++} ++ ++static CERTName* GenerateRandomName(PK11SlotInfo* aSlot) { ++ uint8_t randomName[RTCCertificateCommonNameLength]; ++ SECStatus rv = ++ PK11_GenerateRandomOnSlot(aSlot, randomName, sizeof(randomName)); ++ if (rv != SECSuccess) { ++ return nullptr; ++ } ++ ++ char buf[sizeof(randomName) * 2 + 4]; ++ strncpy(buf, "CN=", 4); ++ for (size_t i = 0; i < sizeof(randomName); ++i) { ++ snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]); ++ } ++ buf[sizeof(buf) - 1] = '\0'; ++ ++ return CERT_AsciiToName(buf); ++} ++ ++nsresult RTCCertificateGenerator::GenerateCertificate() { ++ UniquePK11SlotInfo slot(PK11_GetInternalSlot()); ++ MOZ_ASSERT(slot.get()); ++ ++ UniqueCERTName subjectName(GenerateRandomName(slot.get())); ++ if (!subjectName) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ UniqueCERTSubjectPublicKeyInfo spki( ++ SECKEY_CreateSubjectPublicKeyInfo(mGen->mPublicKey.get())); ++ if (!spki) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ UniqueCERTCertificateRequest certreq( ++ CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr)); ++ if (!certreq) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ PRTime now = PR_Now(); ++ PRTime notBefore = now - EXPIRATION_SLACK; ++ mGen->mExpires += now; ++ ++ UniqueCERTValidity validity(CERT_CreateValidity(notBefore, mGen->mExpires)); ++ if (!validity) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ unsigned long serial; ++ // Note: This serial in principle could collide, but it's unlikely, and we ++ // don't expect anyone to be validating certificates anyway. ++ SECStatus rv = PK11_GenerateRandomOnSlot( ++ slot.get(), reinterpret_cast(&serial), sizeof(serial)); ++ if (rv != SECSuccess) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ // NB: CERTCertificates created with CERT_CreateCertificate are not safe to ++ // use with other NSS functions like CERT_DupCertificate. The strategy ++ // here is to create a tbsCertificate ("to-be-signed certificate"), encode ++ // it, and sign it, resulting in a signed DER certificate that can be ++ // decoded into a CERTCertificate. ++ UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate( ++ serial, subjectName.get(), validity.get(), certreq.get())); ++ if (!tbsCertificate) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN); ++ PLArenaPool* arena = tbsCertificate->arena; ++ ++ rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature, mSignatureAlg, ++ nullptr); ++ if (rv != SECSuccess) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ // Set version to X509v3. ++ *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3; ++ tbsCertificate->version.len = 1; ++ ++ SECItem innerDER = {siBuffer, nullptr, 0}; ++ if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(), ++ SEC_ASN1_GET(CERT_CertificateTemplate))) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ SECItem* certDer = PORT_ArenaZNew(arena, SECItem); ++ if (!certDer) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len, ++ mGen->mPrivateKey.get(), mSignatureAlg); ++ if (rv != SECSuccess) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ mGen->mCertificate.reset(CERT_NewTempCertificate( ++ CERT_GetDefaultCertDB(), certDer, nullptr, false, true)); ++ if (!mGen->mCertificate) { ++ return NS_ERROR_DOM_UNKNOWN_ERR; ++ } ++ ++ if (PK11_HashBuf(SEC_OID_MD5, mGen->mCertFingerprint.AsChar(), certDer->data, ++ AssertedCast(certDer->len)) != SECSuccess) { ++ return NS_ERROR_FAILURE; ++ } ++ ++ return NS_OK; ++} ++ ++RefPtr RTCCertificateGenerator::Generate( ++ nsTArray& aParam, PRTime aExpires, CK_MECHANISM_TYPE aMechanism, ++ SECOidTag aSignatureAlg) { ++ mGenPromise = MakeRefPtr(__func__); ++ ++ mGen = MakeUnique(); ++ mGen->mExpires = aExpires; ++ ++ mMechanism = aMechanism; ++ mSignatureAlg = aSignatureAlg; ++ ++ if (mMechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) { ++ mRsaParams = DeserializeRSAParam(&aParam); ++ mParam = &mRsaParams; ++ } else if (mMechanism == CKM_EC_KEY_PAIR_GEN) { ++ mCurveParams = DeserializeECParams(&aParam); ++ mParam = mCurveParams; ++ } else { ++ mGenPromise->Reject(NS_ERROR_NOT_IMPLEMENTED, __func__); ++ return mGenPromise; ++ } ++ ++ // Store calling thread ++ mOriginalEventTarget = GetCurrentSerialEventTarget(); ++ ++ // dispatch to thread pool ++ if (!EnsureNSSInitializedChromeOrContent()) { ++ mGenPromise->Reject(NS_ERROR_FAILURE, __func__); ++ return mGenPromise; ++ } ++ ++ mCryptoResult = NS_DispatchBackgroundTask(this); ++ if (NS_FAILED(mCryptoResult)) { ++ mGenPromise->Reject(mCryptoResult, __func__); ++ return mGenPromise; ++ } ++ ++ return mGenPromise; ++} ++ ++RTCCertificateGenerator::RTCCertificateGenerator() ++ : CancelableRunnable("RTCCertificateGenerator") {} ++ ++RTCCertificateGenerator::~RTCCertificateGenerator() { ++ if (mCurveParams) { ++ ::SECITEM_FreeItem(mCurveParams, PR_TRUE); ++ mCurveParams = nullptr; ++ } ++} ++ ++void RTCCertificateGenerator::Finish() { ++ MOZ_ASSERT(IsOnOriginalThread()); ++ ++ if (NS_FAILED(mCryptoResult)) { ++ mGenPromise->Reject(mCryptoResult, __func__); ++ } else { ++ mGenPromise->Resolve(std::move(mGen), __func__); ++ } ++ mGenPromise = nullptr; ++} ++ ++NS_IMETHODIMP ++RTCCertificateGenerator::Run() { ++ // Run heavy crypto operations on the thread pool, off the original thread. ++ if (!IsOnOriginalThread()) { ++ mCryptoResult = GenerateKeys(); ++ if (NS_SUCCEEDED(mCryptoResult)) { ++ mCryptoResult = GenerateCertificate(); ++ } ++ ++ // Back to the original thread, i.e. continue below. ++ mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); ++ return NS_OK; ++ } ++ ++ Finish(); ++ return NS_OK; ++} ++ ++nsresult RTCCertificateGenerator::Cancel() { ++ MOZ_ASSERT(IsOnOriginalThread()); ++ mCryptoResult = NS_BINDING_ABORTED; ++ Finish(); ++ return NS_OK; ++} ++ ++RefPtr RTCCertServiceParent::GenerateCertificate( ++ nsTArray& aParam, PRTime aExpires, uint32_t aMechanism, ++ uint32_t aSignatureAlg) { ++ RefPtr resultPromise = ++ MakeRefPtr(__func__); ++ ++ RefPtr gen = new RTCCertificateGenerator(); ++ gen->Generate(aParam, aExpires, aMechanism, ++ static_cast(aSignatureAlg)) ++ ->Then(GetCurrentSerialEventTarget(), __func__, ++ [s = RefPtr{this}, resultPromise]( ++ RTCCertificateGeneratorPromise::ResolveOrRejectValue&& ++ aValue) mutable { ++ if (aValue.IsResolve()) { ++ UniquePtr genCert = ++ std::move(aValue.ResolveValue()); ++ CertFingerprint certFingerprint = genCert->mCertFingerprint; ++ RTCCertCache::CacheCert(std::move(genCert)); ++ resultPromise->Resolve(certFingerprint, __func__); ++ } else if (aValue.IsReject()) { ++ resultPromise->Reject(aValue.RejectValue(), __func__); ++ } ++ }); ++ ++ return resultPromise; ++} ++ ++RefPtr RTCCertServiceParent::GetCertificate( ++ const CertFingerprint aCertFingerprint) { ++ if (GeneratedCertificate* cert = RTCCertCache::LookupCert(aCertFingerprint)) { ++ auto data = MakeUnique( ++ UniqueCERTCertificate(CERT_DupCertificate(cert->mCertificate.get())), ++ cert->mExpires); ++ return RTCCertificatePromise::CreateAndResolve(std::move(data), __func__); ++ } ++ return RTCCertificatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); ++} ++ ++mozilla::ipc::IPCResult RTCCertServiceParent::RecvGenerateCertificate( ++ nsTArray&& aParam, const PRTime& aExpires, ++ const uint32_t& aMechanism, const uint32_t& aSignatureAlg, ++ GenerateCertificateResolver&& aResolve) { ++ GenerateCertificate(aParam, aExpires, aMechanism, aSignatureAlg) ++ ->Then(GetCurrentSerialEventTarget(), __func__, ++ [aResolve = std::move(aResolve)]( ++ const dom::RTCCertFingerprintPromise::ResolveOrRejectValue& ++ aResult) { ++ if (aResult.IsResolve()) { ++ aResolve(aResult.ResolveValue()); ++ } else { ++ aResolve(CertFingerprint()); ++ } ++ }); ++ return IPC_OK(); ++} ++ ++mozilla::ipc::IPCResult RTCCertServiceParent::RecvRemoveCertificate( ++ const CertFingerprint& aCertFingerprint) { ++ RTCCertCache::RemoveCert(aCertFingerprint); ++ return IPC_OK(); ++} ++ ++mozilla::ipc::IPCResult RTCCertServiceParent::RecvGetCertificate( ++ const CertFingerprint& aCertFingerprint, ++ GetCertificateResolver&& aResolve) { ++ GetCertificate(aCertFingerprint) ++ ->Then( ++ GetCurrentSerialEventTarget(), __func__, ++ [aResolve = std::move(aResolve)]( ++ const dom::RTCCertificatePromise::ResolveOrRejectValue& aResult) { ++ if (aResult.IsResolve()) { ++ aResolve(CertDataIPC(aResult.ResolveValue().get())); ++ } else { ++ aResolve(CertDataIPC()); ++ } ++ }); ++ return IPC_OK(); ++} ++ ++} // namespace mozilla::dom +diff -up firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.h.webrtc firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.h +--- firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.h.webrtc 2024-09-30 21:41:07.649369081 +0200 ++++ firefox-128.2.0/dom/media/webrtc/RTCCertServiceParent.h 2024-09-30 21:41:07.649369081 +0200 +@@ -0,0 +1,42 @@ ++/* -*- 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_CertServiceParent_h ++#define mozilla_dom_CertServiceParent_h ++ ++#include "mozilla/dom/PRTCCertServiceTransactionParent.h" ++#include "mozilla/dom/RTCCertServiceData.h" ++ ++namespace mozilla::dom { ++ ++class RTCCertServiceParent final : public PRTCCertServiceTransactionParent { ++ public: ++ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RTCCertServiceParent); ++ RTCCertServiceParent() = default; ++ ++ mozilla::ipc::IPCResult RecvGenerateCertificate( ++ nsTArray&& aParam, const PRTime& aExpires, ++ const uint32_t& aMechanism, const uint32_t& aSignatureAlg, ++ GenerateCertificateResolver&& aResolve); ++ mozilla::ipc::IPCResult RecvRemoveCertificate( ++ const CertFingerprint& aCertFingerprint); ++ mozilla::ipc::IPCResult RecvGetCertificate( ++ const CertFingerprint& aCertFingerprint, ++ GetCertificateResolver&& aResolve); ++ ++ RefPtr GenerateCertificate( ++ nsTArray& aParam, PRTime aExpires, uint32_t aMechanism, ++ uint32_t aSignatureAlg); ++ RefPtr GetCertificate( ++ const CertFingerprint aCertFingerprint); ++ ++ private: ++ ~RTCCertServiceParent() = default; ++}; ++ ++} // namespace mozilla::dom ++ ++#endif // mozilla_dom_CertServiceTransactionParent_h +diff -up firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.cpp.webrtc firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.cpp +--- firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.cpp.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.cpp 2024-09-30 21:41:07.651369147 +0200 +@@ -17,136 +17,19 @@ + #include "sslerr.h" + + #include "mozilla/Sprintf.h" ++#include "mozilla/dom/RTCCertCache.h" + + namespace mozilla { + +-SECItem* WrapPrivateKeyInfoWithEmptyPassword( +- SECKEYPrivateKey* pk) /* encrypt this private key */ +-{ +- if (!pk) { +- PR_SetError(SEC_ERROR_INVALID_ARGS, 0); +- return nullptr; +- } +- +- UniquePK11SlotInfo slot(PK11_GetInternalSlot()); +- if (!slot) { +- return nullptr; +- } +- +- // For private keys, NSS cannot export anything other than RSA, but we need EC +- // also. So, we use the private key encryption function to serialize instead, +- // using a hard-coded dummy password; this is not intended to provide any +- // additional security, it just works around a limitation in NSS. +- SECItem dummyPassword = {siBuffer, nullptr, 0}; +- UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( +- slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr)); +- +- if (!epki) { +- return nullptr; +- } +- +- return SEC_ASN1EncodeItem( +- nullptr, nullptr, epki.get(), +- NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false)); +-} +- +-SECStatus UnwrapPrivateKeyInfoWithEmptyPassword( +- SECItem* derPKI, const UniqueCERTCertificate& aCert, +- SECKEYPrivateKey** privk) { +- if (!derPKI || !aCert || !privk) { +- PR_SetError(SEC_ERROR_INVALID_ARGS, 0); +- return SECFailure; +- } +- +- UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get())); +- // This is a pointer to data inside publicKey +- SECItem* publicValue = nullptr; +- switch (publicKey->keyType) { +- case dsaKey: +- publicValue = &publicKey->u.dsa.publicValue; +- break; +- case dhKey: +- publicValue = &publicKey->u.dh.publicValue; +- break; +- case rsaKey: +- publicValue = &publicKey->u.rsa.modulus; +- break; +- case ecKey: +- publicValue = &publicKey->u.ec.publicValue; +- break; +- default: +- MOZ_ASSERT(false); +- PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); +- return SECFailure; +- } +- +- UniquePK11SlotInfo slot(PK11_GetInternalSlot()); +- if (!slot) { +- return SECFailure; +- } +- +- UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); +- if (!temparena) { +- return SECFailure; +- } +- +- SECKEYEncryptedPrivateKeyInfo* epki = +- PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo); +- if (!epki) { +- return SECFailure; +- } +- +- SECStatus rv = SEC_ASN1DecodeItem( +- temparena.get(), epki, +- NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI); +- if (rv != SECSuccess) { +- // If SEC_ASN1DecodeItem fails, we cannot assume anything about the +- // validity of the data in epki. The best we can do is free the arena +- // and return. +- return rv; +- } +- +- // See comment in WrapPrivateKeyInfoWithEmptyPassword about this +- // dummy password stuff. +- SECItem dummyPassword = {siBuffer, nullptr, 0}; +- return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( +- slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false, +- publicKey->keyType, KU_ALL, privk, nullptr); +-} +- +-nsresult DtlsIdentity::Serialize(nsTArray* aKeyDer, +- nsTArray* aCertDer) { +- ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get())); +- if (!derPki) { +- return NS_ERROR_FAILURE; +- } +- +- aKeyDer->AppendElements(derPki->data, derPki->len); +- aCertDer->AppendElements(cert_->derCert.data, cert_->derCert.len); ++nsresult DtlsIdentity::Serialize(nsTArray& certFingerprint) { ++ certFingerprint = cert_fingerprint_; + return NS_OK; + } + + /* static */ + RefPtr DtlsIdentity::Deserialize( +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType authType) { +- SECItem certDer = {siBuffer, const_cast(aCertDer.Elements()), +- static_cast(aCertDer.Length())}; +- UniqueCERTCertificate cert(CERT_NewTempCertificate( +- CERT_GetDefaultCertDB(), &certDer, nullptr, true, true)); +- +- SECItem derPKI = {siBuffer, const_cast(aKeyDer.Elements()), +- static_cast(aKeyDer.Length())}; +- +- SECKEYPrivateKey* privateKey; +- if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) != +- SECSuccess) { +- MOZ_ASSERT(false); +- return nullptr; +- } +- +- return new DtlsIdentity(UniqueSECKEYPrivateKey(privateKey), std::move(cert), +- authType); ++ const nsTArray& certFingerprint, SSLKEAType authType) { ++ return new DtlsIdentity(dom::CertFingerprint(certFingerprint), authType); + } + + RefPtr DtlsIdentity::Generate() { +@@ -283,7 +166,7 @@ RefPtr DtlsIdentity::Gener + + constexpr nsLiteralCString DtlsIdentity::DEFAULT_HASH_ALGORITHM; + +-nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) const { ++nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) { + const UniqueCERTCertificate& c = cert(); + MOZ_ASSERT(c); + +@@ -328,4 +211,28 @@ nsresult DtlsIdentity::ComputeFingerprin + return NS_OK; + } + ++const UniqueCERTCertificate& DtlsIdentity::cert() { ++ if (!cert_) { ++ dom::GeneratedCertificate* genCert = ++ dom::RTCCertCache::LookupCert(cert_fingerprint_); ++ if (genCert) { ++ cert_ = UniqueCERTCertificate( ++ CERT_DupCertificate(genCert->mCertificate.get())); ++ } ++ } ++ return cert_; ++} ++ ++const UniqueSECKEYPrivateKey& DtlsIdentity::privkey() { ++ if (!private_key_) { ++ dom::GeneratedCertificate* genCert = ++ dom::RTCCertCache::LookupCert(cert_fingerprint_); ++ if (genCert) { ++ private_key_ = UniqueSECKEYPrivateKey( ++ SECKEY_CopyPrivateKey(genCert->mPrivateKey.get())); ++ } ++ } ++ return private_key_; ++} ++ + } // namespace mozilla +diff -up firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.h.webrtc firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.h +--- firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.h.webrtc 2024-08-26 16:23:35.000000000 +0200 ++++ firefox-128.2.0/dom/media/webrtc/transport/dtlsidentity.h 2024-09-30 21:41:07.651369147 +0200 +@@ -10,6 +10,7 @@ + #include + + #include "ScopedNSSTypes.h" ++#include "mozilla/dom/RTCCertCache.h" + #include "m_cpp_utils.h" + #include "mozilla/RefPtr.h" + #include "nsISupportsImpl.h" +@@ -58,14 +59,15 @@ class DtlsIdentity final { + : private_key_(std::move(privkey)), + cert_(std::move(cert)), + auth_type_(authType) {} ++ DtlsIdentity(dom::CertFingerprint certFingerprint, SSLKEAType authType) ++ : cert_fingerprint_(certFingerprint), auth_type_(authType) {} + + // Allows serialization/deserialization; cannot write IPC serialization code + // directly for DtlsIdentity, since IPC-able types need to be constructable + // on the stack. +- nsresult Serialize(nsTArray* aKeyDer, nsTArray* aCertDer); +- static RefPtr Deserialize(const nsTArray& aKeyDer, +- const nsTArray& aCertDer, +- SSLKEAType authType); ++ nsresult Serialize(nsTArray& certFingerprint); ++ static RefPtr Deserialize( ++ const nsTArray& certFingerprint, SSLKEAType authType); + + // This is only for use in tests, or for external linkage. It makes a (bad) + // instance of this class. +@@ -73,15 +75,15 @@ class DtlsIdentity final { + + // These don't create copies or transfer ownership. If you want these to live + // on, make a copy. +- const UniqueCERTCertificate& cert() const { return cert_; } +- const UniqueSECKEYPrivateKey& privkey() const { return private_key_; } ++ const UniqueCERTCertificate& cert(); ++ const UniqueSECKEYPrivateKey& privkey(); + // Note: this uses SSLKEAType because that is what the libssl API requires. + // This is a giant confusing mess, but libssl indexes certificates based on a + // key exchange type, not authentication type (as you might have reasonably + // expected). + SSLKEAType auth_type() const { return auth_type_; } + +- nsresult ComputeFingerprint(DtlsDigest* digest) const; ++ nsresult ComputeFingerprint(DtlsDigest* digest); + static nsresult ComputeFingerprint(const UniqueCERTCertificate& cert, + DtlsDigest* digest); + +@@ -94,6 +96,7 @@ class DtlsIdentity final { + ~DtlsIdentity() = default; + DISALLOW_COPY_ASSIGN(DtlsIdentity); + ++ dom::CertFingerprint cert_fingerprint_; + UniqueSECKEYPrivateKey private_key_; + UniqueCERTCertificate cert_; + SSLKEAType auth_type_; +diff -up firefox-128.2.0/ipc/glue/BackgroundParentImpl.cpp.webrtc firefox-128.2.0/ipc/glue/BackgroundParentImpl.cpp +--- firefox-128.2.0/ipc/glue/BackgroundParentImpl.cpp.webrtc 2024-08-26 16:23:36.000000000 +0200 ++++ firefox-128.2.0/ipc/glue/BackgroundParentImpl.cpp 2024-09-30 21:41:07.651369147 +0200 +@@ -57,6 +57,7 @@ + #include "mozilla/dom/quota/QuotaParent.h" + #include "mozilla/dom/simpledb/ActorsParent.h" + #include "mozilla/dom/VsyncParent.h" ++#include "mozilla/dom/PRTCCertServiceTransactionParent.h" + #include "mozilla/ipc/BackgroundParent.h" + #include "mozilla/ipc/BackgroundUtils.h" + #include "mozilla/ipc/Endpoint.h" +diff -up firefox-128.2.0/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp.webrtc firefox-128.2.0/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp +--- firefox-128.2.0/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp.webrtc 2024-08-26 16:23:42.000000000 +0200 ++++ firefox-128.2.0/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp 2024-09-30 21:41:07.651369147 +0200 +@@ -197,13 +197,15 @@ class LoopbackTransport : public MediaTr + // this up internally + const nsTArray& aStunAddrs) override {} + +- void ActivateTransport( +- const std::string& aTransportId, const std::string& aLocalUfrag, +- const std::string& aLocalPwd, size_t aComponentCount, +- const std::string& aUfrag, const std::string& aPassword, +- const nsTArray& aKeyDer, const nsTArray& aCertDer, +- SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests, +- bool aPrivacyRequested) override {} ++ void ActivateTransport(const std::string& aTransportId, ++ const std::string& aLocalUfrag, ++ const std::string& aLocalPwd, size_t aComponentCount, ++ const std::string& aUfrag, ++ const std::string& aPassword, ++ const nsTArray& aCertFingerprint, ++ SSLKEAType aAuthType, bool aDtlsClient, ++ const DtlsDigestList& aDigests, ++ bool aPrivacyRequested) override {} + + void RemoveTransportsExcept( + const std::set& aTransportIds) override {} +diff -up firefox-128.2.0/netwerk/ipc/PSocketProcessBridge.ipdl.webrtc firefox-128.2.0/netwerk/ipc/PSocketProcessBridge.ipdl +--- firefox-128.2.0/netwerk/ipc/PSocketProcessBridge.ipdl.webrtc 2024-08-26 16:23:42.000000000 +0200 ++++ firefox-128.2.0/netwerk/ipc/PSocketProcessBridge.ipdl 2024-09-30 21:41:07.651369147 +0200 +@@ -10,6 +10,8 @@ include protocol PBackgroundDataBridge; + include protocol PMediaTransport; + #endif // MOZ_WEBRTC + ++include protocol PRTCCertServiceTransaction; ++ + + namespace mozilla { + namespace net { +@@ -42,6 +44,7 @@ parent: + async InitMediaTransport(Endpoint aEndpoint); + #endif // MOZ_WEBRTC + ++ async InitRTCCertServiceTransaction(Endpoint aEndpoint); + }; + + } +diff -up firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.cpp.webrtc firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.cpp +--- firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.cpp.webrtc 2024-08-26 16:23:43.000000000 +0200 ++++ firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.cpp 2024-09-30 21:41:07.651369147 +0200 +@@ -9,6 +9,7 @@ + #ifdef MOZ_WEBRTC + # include "mozilla/dom/MediaTransportParent.h" + #endif ++#include "mozilla/dom/RTCCertServiceParent.h" + #include "mozilla/ipc/BackgroundParent.h" + #include "mozilla/ipc/Endpoint.h" + #include "SocketProcessChild.h" +@@ -83,6 +84,37 @@ mozilla::ipc::IPCResult SocketProcessBri + } + #endif + ++mozilla::ipc::IPCResult ++SocketProcessBridgeParent::RecvInitRTCCertServiceTransaction( ++ mozilla::ipc::Endpoint&& ++ aEndpoint) { ++ LOG(("SocketProcessBridgeParent::RecvInitRTCCertServiceTransaction\n")); ++ ++ if (!aEndpoint.IsValid()) { ++ return IPC_FAIL(this, "Invalid endpoint"); ++ } ++ ++ if (!mMediaTransportTaskQueue) { ++ nsCOMPtr transportQueue; ++ if (NS_FAILED(NS_CreateBackgroundTaskQueue( ++ "MediaTransport", getter_AddRefs(transportQueue)))) { ++ return IPC_FAIL(this, "NS_CreateBackgroundTaskQueue failed"); ++ } ++ ++ mMediaTransportTaskQueue = std::move(transportQueue); ++ } ++ ++ mMediaTransportTaskQueue->Dispatch( ++ NS_NewRunnableFunction("BackgroundDataBridgeParent::Bind", ++ [endpoint = std::move(aEndpoint)]() mutable { ++ RefPtr actor = ++ new dom::RTCCertServiceParent(); ++ endpoint.Bind(actor); ++ })); ++ ++ return IPC_OK(); ++} ++ + void SocketProcessBridgeParent::ActorDestroy(ActorDestroyReason aReason) { + // See bug 1846478. We might be able to remove this dispatch. + GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction( +diff -up firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.h.webrtc firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.h +--- firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.h.webrtc 2024-08-26 16:23:43.000000000 +0200 ++++ firefox-128.2.0/netwerk/ipc/SocketProcessBridgeParent.h 2024-09-30 21:41:07.651369147 +0200 +@@ -29,6 +29,9 @@ class SocketProcessBridgeParent final : + Endpoint&& aEndpoint); + #endif + ++ mozilla::ipc::IPCResult RecvInitRTCCertServiceTransaction( ++ Endpoint&& aEndpoint); ++ + void ActorDestroy(ActorDestroyReason aReason) override; + + private: +diff -up firefox-128.2.0/third_party/libwebrtc/examples/androidtests/third_party/README.webrtc firefox-128.2.0/third_party/libwebrtc/examples/androidtests/third_party/README