| // 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_ |