xref: /aosp_15_r20/external/tink/cc/primitive_set.h (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16 
17 #ifndef TINK_PRIMITIVE_SET_H_
18 #define TINK_PRIMITIVE_SET_H_
19 
20 #include <algorithm>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/base/thread_annotations.h"
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/memory/memory.h"
29 #include "absl/status/status.h"
30 #include "absl/synchronization/mutex.h"
31 #include "tink/crypto_format.h"
32 #include "tink/util/errors.h"
33 #include "tink/util/statusor.h"
34 #include "proto/tink.pb.h"
35 
36 namespace crypto {
37 namespace tink {
38 
39 // A container class for a set of primitives (i.e. implementations of
40 // cryptographic primitives offered by Tink).  It provides also
41 // additional properties for the primitives it holds.  In particular,
42 // one of the primitives in the set can be distinguished as "the
43 // primary" one.
44 //
45 // PrimitiveSet is an auxiliary class used for supporting key rotation:
46 // primitives in a set correspond to keys in a keyset.  Users will
47 // usually work with primitive instances, which essentially wrap
48 // primitive sets.  For example an instance of an Aead-primitive for a
49 // given keyset holds a set of Aead-primitivies corresponding to the
50 // keys in the keyset, and uses the set members to do the actual
51 // crypto operations: to encrypt data the primary Aead-primitive from
52 // the set is used, and upon decryption the ciphertext's prefix
53 // determines the identifier of the primitive from the set.
54 //
55 // PrimitiveSet is a public class to allow its use in implementations
56 // of custom primitives.
57 template <class P>
58 class PrimitiveSet {
59  public:
60   // Entry-objects hold individual instances of primitives in the set.
61   template <class P2>
62   class Entry {
63    public:
New(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)64     static crypto::tink::util::StatusOr<std::unique_ptr<Entry<P>>> New(
65         std::unique_ptr<P> primitive,
66         const google::crypto::tink::KeysetInfo::KeyInfo& key_info) {
67       if (key_info.status() != google::crypto::tink::KeyStatusType::ENABLED) {
68         return util::Status(absl::StatusCode::kInvalidArgument,
69                             "The key must be ENABLED.");
70       }
71       auto identifier_result = CryptoFormat::GetOutputPrefix(key_info);
72       if (!identifier_result.ok()) return identifier_result.status();
73       if (primitive == nullptr) {
74         return util::Status(absl::StatusCode::kInvalidArgument,
75                             "The primitive must be non-null.");
76       }
77       std::string identifier = identifier_result.value();
78       return absl::WrapUnique(new Entry(std::move(primitive), identifier,
79                                         key_info.status(), key_info.key_id(),
80                                         key_info.output_prefix_type(),
81                                         key_info.type_url()));
82     }
83 
get_primitive()84     P2& get_primitive() const { return *primitive_; }
85 
get_identifier()86     const std::string& get_identifier() const { return identifier_; }
87 
get_status()88     google::crypto::tink::KeyStatusType get_status() const { return status_; }
89 
get_key_id()90     uint32_t get_key_id() const { return key_id_; }
91 
get_output_prefix_type()92     google::crypto::tink::OutputPrefixType get_output_prefix_type() const {
93       return output_prefix_type_;
94     }
95 
get_key_type_url()96     absl::string_view get_key_type_url() const { return key_type_url_; }
97 
98    private:
Entry(std::unique_ptr<P2> primitive,const std::string & identifier,google::crypto::tink::KeyStatusType status,uint32_t key_id,google::crypto::tink::OutputPrefixType output_prefix_type,absl::string_view key_type_url)99     Entry(std::unique_ptr<P2> primitive, const std::string& identifier,
100           google::crypto::tink::KeyStatusType status, uint32_t key_id,
101           google::crypto::tink::OutputPrefixType output_prefix_type,
102           absl::string_view key_type_url)
103         : primitive_(std::move(primitive)),
104           identifier_(identifier),
105           status_(status),
106           key_id_(key_id),
107           output_prefix_type_(output_prefix_type),
108           key_type_url_(key_type_url) {}
109 
110     std::unique_ptr<P> primitive_;
111     std::string identifier_;
112     google::crypto::tink::KeyStatusType status_;
113     uint32_t key_id_;
114     google::crypto::tink::OutputPrefixType output_prefix_type_;
115     const std::string key_type_url_;
116   };
117 
118   typedef std::vector<std::unique_ptr<Entry<P>>> Primitives;
119   typedef absl::flat_hash_map<std::string, Primitives>
120       CiphertextPrefixToPrimitivesMap;
121 
122  private:
123   // Helper methods for mutations, used by the Builder and the deprecated
124   // mutation methods on PrimitiveSet.
125 
SetPrimaryImpl(Entry<P> ** output,Entry<P> * primary,const CiphertextPrefixToPrimitivesMap & primitives)126   static crypto::tink::util::Status SetPrimaryImpl(
127       Entry<P>** output, Entry<P>* primary,
128       const CiphertextPrefixToPrimitivesMap& primitives) {
129     if (!primary) {
130       return util::Status(absl::StatusCode::kInvalidArgument,
131                           "The primary primitive must be non-null.");
132     }
133     if (primary->get_status() != google::crypto::tink::KeyStatusType::ENABLED) {
134       return util::Status(absl::StatusCode::kInvalidArgument,
135                           "Primary has to be enabled.");
136     }
137 
138     if (primitives.count(primary->get_identifier()) == 0) {
139       return util::Status(absl::StatusCode::kInvalidArgument,
140                           "Primary cannot be set to an entry which is "
141                           "not held by this primitive set.");
142     }
143 
144     *output = primary;
145     return crypto::tink::util::OkStatus();
146   }
147 
AddPrimitiveImpl(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info,CiphertextPrefixToPrimitivesMap & primitives,std::vector<Entry<P> * > & primitives_in_keyset_order)148   static crypto::tink::util::StatusOr<Entry<P>*> AddPrimitiveImpl(
149       std::unique_ptr<P> primitive,
150       const google::crypto::tink::KeysetInfo::KeyInfo& key_info,
151       CiphertextPrefixToPrimitivesMap& primitives,
152       std::vector<Entry<P>*>& primitives_in_keyset_order) {
153     auto entry_or = Entry<P>::New(std::move(primitive), key_info);
154     if (!entry_or.ok()) return entry_or.status();
155 
156     std::string identifier = entry_or.value()->get_identifier();
157     primitives[identifier].push_back(std::move(entry_or.value()));
158 
159     Entry<P>* stored_entry = primitives[identifier].back().get();
160     primitives_in_keyset_order.push_back(stored_entry);
161     return stored_entry;
162   }
163 
164  public:
165   // Builder is used to construct PrimitiveSet objects. Objects returned by
166   // the builder are immutable. Calling any of the non-const methods on them
167   // will fail.
168   class Builder {
169    public:
170     // Adds 'primitive' to this set for the specified 'key'.
AddPrimitive(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)171     Builder& AddPrimitive(
172         std::unique_ptr<P> primitive,
173         const google::crypto::tink::KeysetInfo::KeyInfo& key_info) & {
174       absl::MutexLock lock(&mutex_);
175       if (!status_.ok()) return *this;
176       status_ = AddPrimitiveImpl(std::move(primitive), key_info, primitives_,
177                                  primitives_in_keyset_order_)
178                     .status();
179       return *this;
180     }
181 
AddPrimitive(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)182     Builder&& AddPrimitive(
183         std::unique_ptr<P> primitive,
184         const google::crypto::tink::KeysetInfo::KeyInfo& key_info) && {
185       return std::move(AddPrimitive(std::move(primitive), key_info));
186     }
187 
188     // Adds 'primitive' to this set for the specified 'key' and marks it
189     // primary.
AddPrimaryPrimitive(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)190     Builder& AddPrimaryPrimitive(
191         std::unique_ptr<P> primitive,
192         const google::crypto::tink::KeysetInfo::KeyInfo& key_info) & {
193       absl::MutexLock lock(&mutex_);
194       if (!status_.ok()) return *this;
195       auto entry_result =
196           AddPrimitiveImpl(std::move(primitive), key_info, primitives_,
197                            primitives_in_keyset_order_);
198       if (!entry_result.ok()) {
199         status_ = entry_result.status();
200         return *this;
201       }
202       status_ = SetPrimaryImpl(&primary_, entry_result.value(), primitives_);
203       return *this;
204     }
205 
AddPrimaryPrimitive(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)206     Builder&& AddPrimaryPrimitive(
207         std::unique_ptr<P> primitive,
208         const google::crypto::tink::KeysetInfo::KeyInfo& key_info) && {
209       return std::move(AddPrimaryPrimitive(std::move(primitive), key_info));
210     }
211 
212     // Add the given annotations. Existing annotations will not be overwritten.
AddAnnotations(absl::flat_hash_map<std::string,std::string> annotations)213     Builder& AddAnnotations(
214         absl::flat_hash_map<std::string, std::string> annotations) & {
215       absl::MutexLock lock(&mutex_);
216       annotations_.merge(std::move(annotations));
217       return *this;
218     }
219 
AddAnnotations(absl::flat_hash_map<std::string,std::string> annotations)220     Builder&& AddAnnotations(
221         absl::flat_hash_map<std::string, std::string> annotations) && {
222       return std::move(AddAnnotations(std::move(annotations)));
223     }
224 
Build()225     crypto::tink::util::StatusOr<PrimitiveSet<P>> Build() && {
226       absl::MutexLock lock(&mutex_);
227       if (!status_.ok()) return status_;
228       return PrimitiveSet<P>(std::move(primitives_), primary_,
229                              std::move(primitives_in_keyset_order_),
230                              std::move(annotations_));
231     }
232 
233    private:
234     // Owned by primitives_.
235     Entry<P>* primary_ ABSL_GUARDED_BY(mutex_) = nullptr;
236     CiphertextPrefixToPrimitivesMap primitives_ ABSL_GUARDED_BY(mutex_);
237     // Entries in the original keyset key order, all owned by primitives_.
238     std::vector<Entry<P>*> primitives_in_keyset_order_ ABSL_GUARDED_BY(mutex_);
239     absl::flat_hash_map<std::string, std::string> annotations_
240         ABSL_GUARDED_BY(mutex_);
241     absl::Mutex mutex_;
242     crypto::tink::util::Status status_ ABSL_GUARDED_BY(mutex_);
243   };
244 
245   // PrimitiveSet is movable, but not copyable
246   PrimitiveSet(PrimitiveSet&&) = default;
247   PrimitiveSet<P>& operator=(PrimitiveSet&&) = default;
248   PrimitiveSet(const PrimitiveSet&) = delete;
249   PrimitiveSet<P>& operator=(const PrimitiveSet&) = delete;
250 
251   // Constructs an empty PrimitiveSet.
252   // Note: This is equivalent to PrimitiveSet<P>(/*annotations=*/{}).
253   ABSL_DEPRECATED(
254       "Constructing PrimitiveSet using constructors is deprecated. Use "
255       "PrimitiveSet<>::Builder instead.")
256   PrimitiveSet<P>() = default;
257   // Constructs an empty PrimitiveSet with `annotations`.
258   ABSL_DEPRECATED(
259       "Constructing PrimitiveSet using constructors is deprecated. Use "
260       "PrimitiveSet<>::Builder instead.")
261   explicit PrimitiveSet<P>(
262       const absl::flat_hash_map<std::string, std::string>& annotations)
annotations_(annotations)263       : annotations_(annotations) {}
264 
265   // Adds 'primitive' to this set for the specified 'key'.
266   ABSL_DEPRECATED(
267       "Mutating PrimitiveSets after construction is deprecated. Use "
268       "PrimitiveSet<>::Builder instead.")
AddPrimitive(std::unique_ptr<P> primitive,const google::crypto::tink::KeysetInfo::KeyInfo & key_info)269   crypto::tink::util::StatusOr<Entry<P>*> AddPrimitive(
270       std::unique_ptr<P> primitive,
271       const google::crypto::tink::KeysetInfo::KeyInfo& key_info) {
272     if (!is_mutable()) {
273       return util::Status(absl::StatusCode::kFailedPrecondition,
274                           "PrimitiveSet is not mutable.");
275     }
276 
277     absl::MutexLock lock(primitives_mutex_.get());
278     return AddPrimitiveImpl(std::move(primitive), key_info, primitives_,
279                             primitives_in_keyset_order_);
280   }
281 
282   // Returns the entries with primitives identified by 'identifier'.
get_primitives(absl::string_view identifier)283   crypto::tink::util::StatusOr<const Primitives*> get_primitives(
284       absl::string_view identifier) const {
285     absl::MutexLockMaybe lock(primitives_mutex_.get());
286     auto found = primitives_.find(std::string(identifier));
287     if (found == primitives_.end()) {
288       return ToStatusF(absl::StatusCode::kNotFound,
289                        "No primitives found for identifier '%s'.", identifier);
290     }
291     return &(found->second);
292   }
293 
294   // Returns all primitives that use RAW prefix.
get_raw_primitives()295   crypto::tink::util::StatusOr<const Primitives*> get_raw_primitives() const {
296     return get_primitives(CryptoFormat::kRawPrefix);
297   }
298 
299   // Sets the given 'primary' as the primary primitive of this set.
300   ABSL_DEPRECATED(
301       "Mutating PrimitiveSets after construction is deprecated. Use "
302       "PrimitiveSet<>::Builder instead.")
set_primary(Entry<P> * primary)303   crypto::tink::util::Status set_primary(Entry<P>* primary) {
304     if (!is_mutable()) {
305       return util::Status(absl::StatusCode::kFailedPrecondition,
306                           "PrimitiveSet is not mutable.");
307     }
308     absl::MutexLock lock(primitives_mutex_.get());
309     return SetPrimaryImpl(&primary_, primary, primitives_);
310   }
311 
312   // Returns the entry with the primary primitive.
get_primary()313   const Entry<P>* get_primary() const {
314     absl::MutexLockMaybe lock(primitives_mutex_.get());
315     return primary_;
316   }
317 
318   // Returns all entries.
get_all()319   std::vector<Entry<P>*> get_all() const {
320     absl::MutexLockMaybe lock(primitives_mutex_.get());
321     std::vector<Entry<P>*> result;
322     for (const auto& prefix_and_vector : primitives_) {
323       for (const auto& primitive : prefix_and_vector.second) {
324         result.push_back(primitive.get());
325       }
326     }
327     return result;
328   }
329 
330   // Returns all entries in the original keyset key order.
get_all_in_keyset_order()331   std::vector<Entry<P>*> get_all_in_keyset_order() const {
332     absl::MutexLockMaybe lock(primitives_mutex_.get());
333     return primitives_in_keyset_order_;
334   }
335 
get_annotations()336   const absl::flat_hash_map<std::string, std::string>& get_annotations() const {
337     return annotations_;
338   }
339 
is_mutable()340   bool is_mutable() const { return primitives_mutex_ != nullptr; }
341 
342  private:
343   // Constructs an empty PrimitiveSet.
344   // Note: This is equivalent to PrimitiveSet<P>(/*annotations=*/{}).
PrimitiveSet(CiphertextPrefixToPrimitivesMap primitives,Entry<P> * primary,std::vector<Entry<P> * > primitives_in_keyset_order,absl::flat_hash_map<std::string,std::string> annotations)345   PrimitiveSet(CiphertextPrefixToPrimitivesMap primitives, Entry<P>* primary,
346                std::vector<Entry<P>*> primitives_in_keyset_order,
347                absl::flat_hash_map<std::string, std::string> annotations)
348       : primary_(primary),
349         primitives_mutex_(nullptr),
350         primitives_(std::move(primitives)),
351         primitives_in_keyset_order_(std::move(primitives_in_keyset_order)),
352         annotations_(std::move(annotations)) {}
353 
354   // Owned by primitives_.
355   Entry<P>* primary_ ABSL_GUARDED_BY(primitives_mutex_) = nullptr;
356   // If primitives_mutex_ is a nullptr, PrimitiveSet is immutable and lock-free.
357   // If not nullptr, primitives_mutex_ guards all read and write access.
358   mutable std::unique_ptr<absl::Mutex> primitives_mutex_ =
359       absl::make_unique<absl::Mutex>();
360   CiphertextPrefixToPrimitivesMap primitives_
361       ABSL_GUARDED_BY(primitives_mutex_);
362   // Entries in the original keyset key order, all owned by primitives_.
363   std::vector<Entry<P>*> primitives_in_keyset_order_
364       ABSL_GUARDED_BY(primitives_mutex_);
365 
366   absl::flat_hash_map<std::string, std::string> annotations_;
367 };
368 
369 }  // namespace tink
370 }  // namespace crypto
371 
372 #endif  // TINK_PRIMITIVE_SET_H_
373