• 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_KEY_DATA_H_
6 #define COMPONENTS_METRICS_STRUCTURED_KEY_DATA_H_
7 
8 #include <string>
9 
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/sequence_checker.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "components/metrics/structured/persistent_proto.h"
16 #include "components/metrics/structured/storage.pb.h"
17 #include "third_party/abseil-cpp/absl/types/optional.h"
18 
19 namespace metrics::structured {
20 
21 class KeyDataTest;
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 // KeyData performs key rotation. Every project is associated with a rotation
33 // period, which is 90 days unless specified in structured.xml. Keys are rotated
34 // with a resolution of one day. They are guaranteed not to be used for
35 // HmacMetric or UserProjectId for longer than their rotation period, except in
36 // cases of local clock changes.
37 //
38 // When first created, every project's key rotation date is selected uniformly
39 // so that there is an even distribution of rotations across users. This means
40 // that, for most users, the first rotation period will be shorter than the
41 // standard full rotation period for that project.
42 //
43 // Key storage is backed by a PersistentProto, stored at the path given to the
44 // constructor.
45 class KeyData {
46  public:
47   KeyData(const base::FilePath& path,
48           const base::TimeDelta& save_delay,
49           base::OnceCallback<void()> on_initialized);
50   ~KeyData();
51 
52   KeyData(const KeyData&) = delete;
53   KeyData& operator=(const KeyData&) = delete;
54 
55   // Returns a digest of |value| for |metric| in the context of
56   // |project_name_hash|. Terminology: a metric is a (name, value) pair, and an
57   // event is a bundle of metrics. Each event is associated with a project.
58   //
59   //  - |project_name_hash| is the uint64 name hash of a project.
60   //  - |metric_name_hash| is the uint64 name hash of a metric.
61   //  - |value| is the string value to hash.
62   //
63   // The result is the HMAC digest of the |value| salted with |metric|, using
64   // the key for |project_name_hash|. That is:
65   //
66   //   HMAC_SHA256(key(project_name_hash), concat(value, hex(event),
67   //   hex(metric)))
68   //
69   // Returns 0u in case of an error.
70   uint64_t HmacMetric(uint64_t project_name_hash,
71                       uint64_t metric_name_hash,
72                       const std::string& value,
73                       int key_rotation_period);
74 
75   // Returns an ID for this (user, |project_name_hash|) pair.
76   // |project_name_hash| is the name of a project, represented by the first 8
77   // bytes of the MD5 hash of its name defined in structured.xml.
78   //
79   // The derived ID is the first 8 bytes of SHA256(key(project_name_hash)).
80   // Returns 0u in case of an error.
81   //
82   // This ID is intended as the only ID for the events of a particular
83   // structured metrics project. However, events are uploaded from the device
84   // alongside the UMA client ID, which is only removed after the event reaches
85   // the server. This means events are associated with the client ID when
86   // uploaded from the device. See the class comment of
87   // StructuredMetricsProvider for more details.
88   //
89   // Default |key_rotation_period| is 90 days.
90   uint64_t Id(uint64_t project_name_hash, int key_rotation_period);
91 
92   // Returns when the key for |project_name_hash| was last rotated, in days
93   // since epoch. Returns nullopt if the key doesn't exist.
94   absl::optional<int> LastKeyRotation(uint64_t project_name_hash) const;
95 
96   // Return the age of the key for |project_name_hash| since the last rotation,
97   // in weeks.
98   absl::optional<int> GetKeyAgeInWeeks(uint64_t project_name_hash) const;
99 
100   // Clears all key data from memory and from disk. If this is called before the
101   // underlying proto has been read from disk, the purge will be performed once
102   // the read is complete.
103   void Purge();
104 
105   // Returns whether this KeyData instance has finished reading from disk and is
106   // ready to be used. If false, both Id and HmacMetric will return 0u.
is_initialized()107   bool is_initialized() { return is_initialized_; }
108 
109  private:
110   friend class KeyDataTest;
111 
112   void WriteNowForTest();
113 
114   void OnRead(ReadStatus status);
115 
116   void OnWrite(WriteStatus status);
117 
118   // Ensure that a valid key exists for |project|, and return it. Either returns
119   // a string of size |kKeySize| or absl::nullopt, which indicates an error. If
120   // a key doesn't exist OR if the key needs to be rotated, then a new key with
121   // |key_rotation_period| will be created.
122   absl::optional<std::string> ValidateAndGetKey(uint64_t project_name_hash,
123                                                 int key_rotation_period);
124 
125   // Regenerate |key|, also updating the |last_key_rotation| and
126   // |key_rotation_period|. This triggers a save.
127   void UpdateKey(KeyProto* key, int last_key_rotation, int key_rotation_period);
128 
129   // Storage for keys.
130   std::unique_ptr<PersistentProto<KeyDataProto>> proto_;
131 
132   // Whether this instance has finished reading from disk.
133   bool is_initialized_ = false;
134 
135   base::OnceCallback<void()> on_initialized_;
136 
137   SEQUENCE_CHECKER(sequence_checker_);
138 
139   scoped_refptr<base::SequencedTaskRunner> task_runner_;
140   base::WeakPtrFactory<KeyData> weak_factory_{this};
141 };
142 
143 }  // namespace metrics::structured
144 
145 #endif  // COMPONENTS_METRICS_STRUCTURED_KEY_DATA_H_
146