blob: 958dbc6f4ffaa68a5351a3aed0a7a5c58ed925fa [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_
#define COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "components/metrics/structured/lib/persistent_proto.h"
#include "components/metrics/structured/lib/proto/key.pb.h"
namespace metrics::structured {
// KeyData is the central class for managing keys and generating hashes for
// structured metrics.
//
// The class maintains one key and its rotation data for every project defined
// in /tools/metrics/structured/sync/structured.xml. This can be used to
// generate:
// - an ID for the project with KeyData::Id.
// - a hash of a given value for an event with KeyData::HmacMetric.
//
// Every project has a uint64_t project_name_hash that is generated by taking
// the first 8 bytes of MD5 hash of the project name. Keys for the project are
// retrieved using this project_name_hash. For more details, refer to
// //tools/metrics/structured/ccodegen.py.
//
// KeyData performs key rotation. Every project is associated with a rotation
// period, which is 90 days unless specified in structured.xml. Keys are rotated
// with a resolution of one day. They are guaranteed not to be used for
// HmacMetric or UserProjectId for longer than their rotation period, except in
// cases of local clock changes.
//
// When first created, every project's key rotation date is selected uniformly
// so that there is an even distribution of rotations across users. This means
// that, for most users, the first rotation period will be shorter than the
// standard full rotation period for that project.
class KeyData {
public:
// Delegate to read and upsert keys.
class StorageDelegate {
public:
virtual ~StorageDelegate() = default;
// Returns if the delegate is ready to read or upsert keys.
virtual bool IsReady() const = 0;
// Returns the key associated with |project_name_hash|.
//
// If the key does not exist yet, then returns nullptr. Note that this will
// return the expired key if it needs to be rotated.
virtual const KeyProto* GetKey(uint64_t project_name_hash) const = 0;
// Upserts the key for |project_name_hash| with duration
// |key_rotation_period| and last updated time |last_key_rotation|.
//
// |last_key_rotation| is the TimeDelta from base::Time::UnixEpoch in
// which the key was last rotated.
virtual void UpsertKey(uint64_t project_name_hash,
base::TimeDelta last_key_rotation,
base::TimeDelta key_rotation_period) = 0;
// Clears all key data.
virtual void Purge() = 0;
};
// Key data will use |storage_delegate| to read and upsert keys.
explicit KeyData(std::unique_ptr<StorageDelegate> storage_delegate);
KeyData(const KeyData&) = delete;
KeyData& operator=(const KeyData&) = delete;
~KeyData();
// Returns a digest of |value| for |metric| in the context of
// |project_name_hash|. Terminology: a metric is a (name, value) pair, and an
// event is a bundle of metrics. Each event is associated with a project.
//
// - |project_name_hash| is the uint64 name hash of a project.
// - |metric_name_hash| is the uint64 name hash of a metric.
// - |value| is the string value to hash.
// - |key_rotation_period| is the frequency in which the key is rotated. It
// is used to retrieve the correct key.
//
// The result is the HMAC digest of the |value| salted with |metric|, using
// the key for |project_name_hash|. That is:
//
// HMAC_SHA256(key(project_name_hash), concat(value, hex(event),
// hex(metric)))
//
// Returns 0u in case of an error.
uint64_t HmacMetric(uint64_t project_name_hash,
uint64_t metric_name_hash,
const std::string& value,
base::TimeDelta key_rotation_period);
// Returns an ID for this (user, |project_name_hash|) pair.
// |project_name_hash| is the name of a project, represented by the first 8
// bytes of the MD5 hash of its name defined in structured.xml.
//
// The derived ID is the first 8 bytes of SHA256(key(project_name_hash)).
// Returns 0u in case of an error.
//
// This ID is intended as the only ID for the events of a particular
// structured metrics project. See the class comment of
// StructuredMetricsProvider for more details.
//
// Default |key_rotation_period| is 90 days.
uint64_t Id(uint64_t project_name_hash, base::TimeDelta key_rotation_period);
// Returns when the key for |project_name_hash| was last rotated. Returns
// nullopt if the key doesn't exist.
std::optional<base::TimeDelta> LastKeyRotation(
uint64_t project_name_hash) const;
// Return the age of the key for |project_name_hash| since the last rotation,
// in weeks.
std::optional<int> GetKeyAgeInWeeks(uint64_t project_name_hash) const;
// Clears all key data.
void Purge();
private:
// Ensure that a valid key exists for |project|. If a key doesn't exist OR if
// the key needs to be rotated, then a new key with |key_rotation_period| will
// be created.
//
// This function assumes that |storage_delegate_->IsReady()| is true.
void EnsureKeyUpdated(uint64_t project_name_hash,
base::TimeDelta key_rotation_period);
// Retrieves the bytes of the key associated with |project_name_hash|.
// If the key does not exist OR if the key is not of size |kKeySize|, returns
// std::nullopt .
const std::optional<std::string_view> GetKeyBytes(
uint64_t project_name_hash) const;
// Delegate that handles reading and upserting keys.
std::unique_ptr<KeyData::StorageDelegate> storage_delegate_;
};
} // namespace metrics::structured
#endif // COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_