• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_
6 #define COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_
7 
8 #include <memory>
9 #include <optional>
10 #include <string>
11 #include <string_view>
12 
13 #include "base/memory/scoped_refptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/sequence_checker.h"
16 #include "base/task/sequenced_task_runner.h"
17 #include "base/time/time.h"
18 #include "components/metrics/structured/lib/persistent_proto.h"
19 #include "components/metrics/structured/lib/proto/key.pb.h"
20 
21 namespace metrics::structured {
22 
23 // KeyData is the central class for managing keys and generating hashes for
24 // structured metrics.
25 //
26 // The class maintains one key and its rotation data for every project defined
27 // in /tools/metrics/structured/sync/structured.xml. This can be used to
28 // generate:
29 //  - an ID for the project with KeyData::Id.
30 //  - a hash of a given value for an event with KeyData::HmacMetric.
31 //
32 // Every project has a uint64_t project_name_hash that is generated by taking
33 // the first 8 bytes of MD5 hash of the project name. Keys for the project are
34 // retrieved using this project_name_hash. For more details, refer to
35 // //tools/metrics/structured/ccodegen.py.
36 //
37 // KeyData performs key rotation. Every project is associated with a rotation
38 // period, which is 90 days unless specified in structured.xml. Keys are rotated
39 // with a resolution of one day. They are guaranteed not to be used for
40 // HmacMetric or UserProjectId for longer than their rotation period, except in
41 // cases of local clock changes.
42 //
43 // When first created, every project's key rotation date is selected uniformly
44 // so that there is an even distribution of rotations across users. This means
45 // that, for most users, the first rotation period will be shorter than the
46 // standard full rotation period for that project.
47 class KeyData {
48  public:
49   // Delegate to read and upsert keys.
50   class StorageDelegate {
51    public:
52     virtual ~StorageDelegate() = default;
53 
54     // Returns if the delegate is ready to read or upsert keys.
55     virtual bool IsReady() const = 0;
56 
57     // Returns the key associated with |project_name_hash|.
58     //
59     // If the key does not exist yet, then returns nullptr. Note that this will
60     // return the expired key if it needs to be rotated.
61     virtual const KeyProto* GetKey(uint64_t project_name_hash) const = 0;
62 
63     // Upserts the key for |project_name_hash| with duration
64     // |key_rotation_period| and last updated time |last_key_rotation|.
65     //
66     // |last_key_rotation| is the TimeDelta from base::Time::UnixEpoch in
67     // which the key was last rotated.
68     virtual void UpsertKey(uint64_t project_name_hash,
69                            base::TimeDelta last_key_rotation,
70                            base::TimeDelta key_rotation_period) = 0;
71 
72     // Clears all key data.
73     virtual void Purge() = 0;
74   };
75 
76   // Key data will use |storage_delegate| to read and upsert keys.
77   explicit KeyData(std::unique_ptr<StorageDelegate> storage_delegate);
78 
79   KeyData(const KeyData&) = delete;
80   KeyData& operator=(const KeyData&) = delete;
81 
82   ~KeyData();
83 
84   // Returns a digest of |value| for |metric| in the context of
85   // |project_name_hash|. Terminology: a metric is a (name, value) pair, and an
86   // event is a bundle of metrics. Each event is associated with a project.
87   //
88   //  - |project_name_hash| is the uint64 name hash of a project.
89   //  - |metric_name_hash| is the uint64 name hash of a metric.
90   //  - |value| is the string value to hash.
91   //  - |key_rotation_period| is the frequency in which the key is rotated. It
92   //    is used to retrieve the correct key.
93   //
94   // The result is the HMAC digest of the |value| salted with |metric|, using
95   // the key for |project_name_hash|. That is:
96   //
97   //   HMAC_SHA256(key(project_name_hash), concat(value, hex(event),
98   //   hex(metric)))
99   //
100   // Returns 0u in case of an error.
101   uint64_t HmacMetric(uint64_t project_name_hash,
102                       uint64_t metric_name_hash,
103                       const std::string& value,
104                       base::TimeDelta key_rotation_period);
105 
106   // Returns an ID for this (user, |project_name_hash|) pair.
107   // |project_name_hash| is the name of a project, represented by the first 8
108   // bytes of the MD5 hash of its name defined in structured.xml.
109   //
110   // The derived ID is the first 8 bytes of SHA256(key(project_name_hash)).
111   // Returns 0u in case of an error.
112   //
113   // This ID is intended as the only ID for the events of a particular
114   // structured metrics project. See the class comment of
115   // StructuredMetricsProvider for more details.
116   //
117   // Default |key_rotation_period| is 90 days.
118   uint64_t Id(uint64_t project_name_hash, base::TimeDelta key_rotation_period);
119 
120   // Returns when the key for |project_name_hash| was last rotated. Returns
121   // nullopt if the key doesn't exist.
122   std::optional<base::TimeDelta> LastKeyRotation(
123       uint64_t project_name_hash) const;
124 
125   // Return the age of the key for |project_name_hash| since the last rotation,
126   // in weeks.
127   std::optional<int> GetKeyAgeInWeeks(uint64_t project_name_hash) const;
128 
129   // Clears all key data.
130   void Purge();
131 
132  private:
133   // Ensure that a valid key exists for |project|. If a key doesn't exist OR if
134   // the key needs to be rotated, then a new key with |key_rotation_period| will
135   // be created.
136   //
137   // This function assumes that |storage_delegate_->IsReady()| is true.
138   void EnsureKeyUpdated(uint64_t project_name_hash,
139                         base::TimeDelta key_rotation_period);
140 
141   // Retrieves the bytes of the key associated with |project_name_hash|.
142   // If the key does not exist OR if the key is not of size |kKeySize|, returns
143   // std::nullopt .
144   const std::optional<std::string_view> GetKeyBytes(
145       uint64_t project_name_hash) const;
146 
147   // Delegate that handles reading and upserting keys.
148   std::unique_ptr<KeyData::StorageDelegate> storage_delegate_;
149 };
150 
151 }  // namespace metrics::structured
152 
153 #endif  // COMPONENTS_METRICS_STRUCTURED_LIB_KEY_DATA_H_
154