blob: c33c8924963c6da9c0208bc3831f1981ac5b2a32 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browsing_topics/util.h"
#include <algorithm>
#include "base/numerics/byte_conversions.h"
#include "base/rand_util.h"
#include "crypto/hash.h"
#include "crypto/hmac.h"
#include "third_party/blink/public/common/features.h"
namespace browsing_topics {
namespace {
// Note that updating the use case prefixes below will change the pre-existing
// per-user stickiness. Some of the derived data may already have been persisted
// elsewhere. Be sure you are aware of the implications before updating those
// strings. Note also that the version here is just about the hash method, and
// is distinctive from the broader configuration version of the Topics API.
const char kRandomOrTopTopicDecisionPrefix[] =
"TopicsV1_RandomOrTopTopicDecision|";
const char kRandomTopicIndexDecisionPrefix[] =
"TopicsV1_RandomTopicIndexDecision|";
const char kTopTopicIndexDecisionPrefix[] = "TopicsV1_TopTopicIndexDecision|";
const char kEpochIntroductionTimeDecisionPrefix[] =
"TopicsV1_EpochSwitchTimeDecision|";
const char kEpochPhaseOutTimeDecisionPrefix[] =
"TopicsV1_EpochPhaseOutTimeDecision|";
const char kContextDomainStoragePrefix[] = "TopicsV1_ContextDomainStorage|";
const char kMainFrameHostStoragePrefix[] = "TopicsV1_MainFrameHostStorage|";
uint64_t HmacHash(ReadOnlyHmacKey hmac_key,
const std::string& use_case_prefix,
const std::string& data) {
auto hash = crypto::hmac::SignSha256(
hmac_key, base::as_byte_span(use_case_prefix + data));
return base::U64FromNativeEndian(base::span(hash).first<8u>());
}
bool g_hmac_key_overridden = false;
browsing_topics::HmacKey& GetHmacKeyOverrideForTesting() {
static browsing_topics::HmacKey key;
return key;
}
std::string GetEpochId(base::Time epoch_calculation_time) {
int64_t time_microseconds =
epoch_calculation_time.ToDeltaSinceWindowsEpoch().InMicroseconds();
return std::string(reinterpret_cast<const char*>(&time_microseconds),
sizeof(time_microseconds));
}
} // namespace
bool DoesCalculationFailDueToHanging(CalculatorResultStatus status) {
return status == CalculatorResultStatus::kHangingAfterApiUsageRequested ||
status == CalculatorResultStatus::kHangingAfterHistoryRequested ||
status == CalculatorResultStatus::kHangingAfterModelRequested ||
status == CalculatorResultStatus::kHangingAfterAnnotationRequested;
}
HmacKey GenerateRandomHmacKey() {
if (g_hmac_key_overridden)
return GetHmacKeyOverrideForTesting();
HmacKey result = {};
base::RandBytes(result);
return result;
}
uint64_t HashTopDomainForRandomOrTopTopicDecision(
ReadOnlyHmacKey hmac_key,
base::Time epoch_calculation_time,
const std::string& top_domain) {
return HmacHash(hmac_key, kRandomOrTopTopicDecisionPrefix,
GetEpochId(epoch_calculation_time) + top_domain);
}
uint64_t HashTopDomainForRandomTopicIndexDecision(
ReadOnlyHmacKey hmac_key,
base::Time epoch_calculation_time,
const std::string& top_domain) {
return HmacHash(hmac_key, kRandomTopicIndexDecisionPrefix,
GetEpochId(epoch_calculation_time) + top_domain);
}
uint64_t HashTopDomainForTopTopicIndexDecision(
ReadOnlyHmacKey hmac_key,
base::Time epoch_calculation_time,
const std::string& top_domain) {
return HmacHash(hmac_key, kTopTopicIndexDecisionPrefix,
GetEpochId(epoch_calculation_time) + top_domain);
}
uint64_t HashTopDomainForEpochIntroductionTimeDecision(
ReadOnlyHmacKey hmac_key,
base::Time epoch_calculation_time,
const std::string& top_domain) {
return HmacHash(hmac_key, kEpochIntroductionTimeDecisionPrefix,
GetEpochId(epoch_calculation_time) + top_domain);
}
uint64_t HashTopDomainForEpochPhaseOutTimeDecision(
ReadOnlyHmacKey hmac_key,
base::Time epoch_calculation_time,
const std::string& top_domain) {
return HmacHash(hmac_key, kEpochPhaseOutTimeDecisionPrefix,
GetEpochId(epoch_calculation_time) + top_domain);
}
HashedDomain HashContextDomainForStorage(ReadOnlyHmacKey hmac_key,
const std::string& context_domain) {
return HashedDomain(
HmacHash(hmac_key, kContextDomainStoragePrefix, context_domain));
}
HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host) {
auto hash =
crypto::hash::Sha256(kMainFrameHostStoragePrefix + main_frame_host);
int64_t result = base::I64FromNativeEndian(base::span(hash).first<8u>());
return HashedHost(result);
}
void OverrideHmacKeyForTesting(ReadOnlyHmacKey hmac_key) {
g_hmac_key_overridden = true;
std::ranges::copy(hmac_key, GetHmacKeyOverrideForTesting().begin());
}
} // namespace browsing_topics