1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
// 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_
|