Bug 1816064 - Part 1: Implement the session key for generating the random noise key for fingerprinting randomization. r=tjr

This patch implements the session key that will be used to generate the
random noise for fingerprinting randomization. The session keys will
live in the nsRFPService in the parent process only. nsRFPServices holds
two session keys for normal and private windows respectively. The
lifetime of normal sessino key is tie to the Firefox; it resets with
Firefox restarts. For private session key, it resets when the private
session ends. i.e. all private windows are closed.

Differential Revision: https://phabricator.services.mozilla.com/D170603
This commit is contained in:
Tim Huang 2023-03-02 21:47:06 +00:00
Родитель 4d752ebbe4
Коммит 3a6e536d7c
3 изменённых файлов: 202 добавлений и 0 удалений

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

@ -12921,6 +12921,25 @@
value: 0
mirror: always
# Enable the fingerprinting randomization.
- name: privacy.resistFingerprinting.randomization.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Enable resetting the fingerprinting randomization key daily for normal windwos.
- name: privacy.resistFingerprinting.randomization.daily_reset.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Enable resetting the fingerprinting randomization key daily for private windwos.
- name: privacy.resistFingerprinting.randomization.daily_reset.private.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Anti-tracking permission expiration.
- name: privacy.restrict3rdpartystorage.expiration
type: uint32_t

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

@ -17,6 +17,7 @@
#include <utility>
#include "MainThreadUtils.h"
#include "ScopedNSSTypes.h"
#include "mozilla/ArrayIterator.h"
#include "mozilla/Assertions.h"
@ -28,6 +29,7 @@
#include "mozilla/Likely.h"
#include "mozilla/Logging.h"
#include "mozilla/MacroForEach.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/Preferences.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
@ -66,6 +68,7 @@
#include "nsIGlobalObject.h"
#include "nsIObserverService.h"
#include "nsIRandomGenerator.h"
#include "nsIUserIdleService.h"
#include "nsIXULAppInfo.h"
#include "nscore.h"
@ -90,6 +93,8 @@ static mozilla::LazyLogModule gResistFingerprintingLog(
#define RFP_JITTER_VALUE_PREF \
"privacy.resistFingerprinting.reduceTimerPrecision.jitter"
#define PROFILE_INITIALIZED_TOPIC "profile-initial-state"
#define LAST_PB_SESSION_EXITED_TOPIC "last-pb-context-exited"
#define STARTUP_COMPLETE_TOPIC "browser-delayed-startup-finished"
static constexpr uint32_t kVideoFramesPerSec = 30;
static constexpr uint32_t kVideoDroppedRatio = 5;
@ -97,6 +102,10 @@ static constexpr uint32_t kVideoDroppedRatio = 5;
#define RFP_DEFAULT_SPOOFING_KEYBOARD_LANG KeyboardLang::EN
#define RFP_DEFAULT_SPOOFING_KEYBOARD_REGION KeyboardRegion::US
// The number of seconds we will wait after receiving the idle-daily
// notification before clearing the session keys.
const uint32_t kIdleObserverTimeSec = 1;
NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
static StaticRefPtr<nsRFPService> sRFPService;
@ -644,6 +653,14 @@ nsresult nsRFPService::Init() {
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
NS_ENSURE_SUCCESS(rv, rv);
if (XRE_IsParentProcess()) {
rv = obs->AddObserver(this, LAST_PB_SESSION_EXITED_TOPIC, false);
NS_ENSURE_SUCCESS(rv, rv);
rv = obs->AddObserver(this, STARTUP_COMPLETE_TOPIC, false);
NS_ENSURE_SUCCESS(rv, rv);
}
#if defined(XP_WIN)
rv = obs->AddObserver(this, PROFILE_INITIALIZED_TOPIC, false);
NS_ENSURE_SUCCESS(rv, rv);
@ -734,7 +751,18 @@ void nsRFPService::StartShutdown() {
if (obs) {
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
if (XRE_IsParentProcess()) {
obs->RemoveObserver(this, LAST_PB_SESSION_EXITED_TOPIC);
}
}
nsCOMPtr<nsIUserIdleService> idleService =
do_GetService("@mozilla.org/widget/useridleservice;1");
if (idleService && XRE_IsParentProcess()) {
idleService->RemoveIdleObserver(this, kIdleObserverTimeSec);
}
Preferences::UnregisterCallbacks(nsRFPService::PrefChanged, gCallbackPrefs,
this);
}
@ -1061,5 +1089,137 @@ nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
}
#endif
if (strcmp(LAST_PB_SESSION_EXITED_TOPIC, aTopic) == 0) {
// Clear the private session key when the private session ends so that we
// can generate a new key for the new private session.
ClearSessionKey(true);
}
if (strcmp(STARTUP_COMPLETE_TOPIC, aTopic) == 0) {
// Register the idle observer after the startup is finished.
nsCOMPtr<nsIUserIdleService> idleService =
do_GetService("@mozilla.org/widget/useridleservice;1");
NS_ENSURE_TRUE(idleService, NS_ERROR_NOT_AVAILABLE);
nsresult rv = idleService->AddIdleObserver(this, kIdleObserverTimeSec);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
rv = obs->RemoveObserver(this, STARTUP_COMPLETE_TOPIC);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!strcmp(OBSERVER_TOPIC_IDLE_DAILY, aTopic)) {
if (StaticPrefs::
privacy_resistFingerprinting_randomization_daily_reset_enabled()) {
ClearSessionKey(false);
}
if (StaticPrefs::
privacy_resistFingerprinting_randomization_daily_reset_private_enabled()) {
ClearSessionKey(true);
}
}
return NS_OK;
}
nsresult nsRFPService::EnsureSessionKey(bool aIsPrivate) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_LOG(gResistFingerprintingLog, LogLevel::Info,
("Ensure the session key for %s browsing session\n",
aIsPrivate ? "private" : "normal"));
if (!StaticPrefs::privacy_resistFingerprinting_randomization_enabled()) {
return NS_ERROR_NOT_AVAILABLE;
}
Maybe<nsID>& sessionKey =
aIsPrivate ? mPrivateBrowsingSessionKey : mBrowsingSessionKey;
// The key has been generated, bail out earlier.
if (sessionKey) {
MOZ_LOG(
gResistFingerprintingLog, LogLevel::Info,
("The %s session key exists: %s\n", aIsPrivate ? "private" : "normal",
sessionKey.ref().ToString().get()));
return NS_OK;
}
sessionKey.emplace(nsID::GenerateUUID());
MOZ_LOG(gResistFingerprintingLog, LogLevel::Debug,
("Generated %s session key: %s\n", aIsPrivate ? "private" : "normal",
sessionKey.ref().ToString().get()));
return NS_OK;
}
void nsRFPService::ClearSessionKey(bool aIsPrivate) {
MOZ_ASSERT(XRE_IsParentProcess());
Maybe<nsID>& sessionKey =
aIsPrivate ? mPrivateBrowsingSessionKey : mBrowsingSessionKey;
sessionKey.reset();
}
// static
Maybe<nsTArray<uint8_t>> nsRFPService::GenerateKey(nsIURI* aTopLevelURI,
bool aIsPrivate) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(aTopLevelURI);
MOZ_LOG(gResistFingerprintingLog, LogLevel::Debug,
("Generating %s randomization key for top-level URI: %s\n",
aIsPrivate ? "private" : "normal",
aTopLevelURI->GetSpecOrDefault().get()));
RefPtr<nsRFPService> service = GetOrCreate();
if (NS_FAILED(service->EnsureSessionKey(aIsPrivate))) {
return Nothing();
}
const nsID& sessionKey = aIsPrivate
? service->mPrivateBrowsingSessionKey.ref()
: service->mBrowsingSessionKey.ref();
auto sessionKeyStr = sessionKey.ToString();
// Using the OriginAttributes to get the site from the top-level URI. The site
// is composed of scheme, host, and port.
OriginAttributes attrs;
attrs.SetPartitionKey(aTopLevelURI);
// Generate the key by using the hMAC. The key is based on the session key and
// the partitionKey, i.e. top-level site.
HMAC hmac;
nsresult rv = hmac.Begin(
SEC_OID_SHA256,
Span(reinterpret_cast<const uint8_t*>(sessionKeyStr.get()), NSID_LENGTH));
if (NS_WARN_IF(NS_FAILED(rv))) {
return Nothing();
}
NS_ConvertUTF16toUTF8 topLevelSite(attrs.mPartitionKey);
rv = hmac.Update(reinterpret_cast<const uint8_t*>(topLevelSite.get()),
topLevelSite.Length());
if (NS_WARN_IF(NS_FAILED(rv))) {
return Nothing();
}
Maybe<nsTArray<uint8_t>> key;
key.emplace();
rv = hmac.End(key.ref());
if (NS_WARN_IF(NS_FAILED(rv))) {
return Nothing();
}
return key;
}

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

@ -12,6 +12,7 @@
#include "mozilla/BasicEvents.h"
#include "nsContentUtils.h"
#include "nsHashtablesFwd.h"
#include "nsID.h"
#include "nsIGlobalObject.h"
#include "nsIObserver.h"
#include "nsISupports.h"
@ -227,6 +228,11 @@ class nsRFPService final : public nsIObserver {
const WidgetKeyboardEvent* aKeyboardEvent,
uint32_t& aOut);
// The method to generate the key for randomization. It can return nothing if
// the session key is not available due to the randomization is disabled.
static Maybe<nsTArray<uint8_t>> GenerateKey(nsIURI* aTopLevelURI,
bool aIsPrivate);
private:
nsresult Init();
@ -261,7 +267,24 @@ class nsRFPService final : public nsIObserver {
static void TypeToText(TimerPrecisionType aType, nsACString& aText);
// Generate the session key if it hasn't been generated.
nsresult EnsureSessionKey(bool aIsPrivate);
void ClearSessionKey(bool aIsPrivate);
nsCString mInitialTZValue;
// The keys that represent the browsing session. The lifetime of the key ties
// to the browsing session. For normal windows, the key is generated when
// loading the first http channel after the browser startup and persists until
// the browser shuts down. For private windows, the key is generated when
// opening a http channel on a private window and reset after all private
// windows close, i.e. private browsing session ends.
//
// The key will be used to generate the randomization noise used to fiddle the
// browser fingerprints. Note that this key lives and can only be accessed in
// the parent process.
Maybe<nsID> mBrowsingSessionKey;
Maybe<nsID> mPrivateBrowsingSessionKey;
};
} // namespace mozilla