1 // Copyright 2018 Google LLC
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_INTERNAL_KEY_TYPE_INFO_STORE_H_
18 #define TINK_INTERNAL_KEY_TYPE_INFO_STORE_H_
19
20 #include <atomic>
21 #include <functional>
22 #include <initializer_list>
23 #include <memory>
24 #include <string>
25 #include <typeindex>
26 #include <utility>
27
28 #include "absl/container/flat_hash_map.h"
29 #include "absl/strings/str_join.h"
30 #include "tink/core/key_manager_impl.h"
31 #include "tink/core/key_type_manager.h"
32 #include "tink/core/private_key_manager_impl.h"
33 #include "tink/core/private_key_type_manager.h"
34 #include "tink/internal/fips_utils.h"
35 #include "tink/key_manager.h"
36
37 namespace crypto {
38 namespace tink {
39 namespace internal {
40
41 // Stores information about key types constructed from their KeyTypeManager or
42 // KeyManager. This is used by the Configuration and Registry classes.
43 //
44 // Once inserted, Info objects must remain valid for the lifetime of the
45 // KeyTypeInfoStore object, and the Info object's pointer stability is required.
46 // Elements in Info, which include the KeyTypeManager or KeyManager, must not
47 // be replaced.
48 //
49 // Example:
50 // KeyTypeInfoStore store;
51 // crypto::tink::util::Status status =
52 // store.AddKeyTypeManager(absl::make_unique<AesGcmKeyManager>(), true);
53 // crypto::tink::util::StatusOr<KeyTypeInfoStore::Info*> info =
54 // store.Get(AesGcmKeyManager().get_key_type());
55 class KeyTypeInfoStore {
56 public:
57 KeyTypeInfoStore() = default;
58
59 // Movable, but not copyable.
60 KeyTypeInfoStore(KeyTypeInfoStore&& other) = default;
61 KeyTypeInfoStore& operator=(KeyTypeInfoStore&& other) = default;
62
63 // Information about a key type constructed from its KeyTypeManager or
64 // KeyManager.
65 class Info {
66 public:
67 // Takes ownership of `manager`.
68 template <typename KeyProto, typename KeyFormatProto,
69 typename... Primitives>
Info(KeyTypeManager<KeyProto,KeyFormatProto,List<Primitives...>> * manager,bool new_key_allowed)70 Info(KeyTypeManager<KeyProto, KeyFormatProto, List<Primitives...>>* manager,
71 bool new_key_allowed)
72 : key_manager_type_index_(std::type_index(typeid(*manager))),
73 public_key_type_manager_type_index_(absl::nullopt),
74 new_key_allowed_(new_key_allowed),
75 key_type_manager_(absl::WrapUnique(manager)),
76 internal_key_factory_(
77 absl::make_unique<internal::KeyFactoryImpl<KeyTypeManager<
78 KeyProto, KeyFormatProto, List<Primitives...>>>>(manager)),
79 key_factory_(internal_key_factory_.get()),
80 key_deriver_(CreateDeriverFunctionFor(manager)) {
81 // TODO(C++17): Replace with a fold expression.
82 (void)std::initializer_list<int>{
83 0, (primitive_to_manager_.emplace(
84 std::type_index(typeid(Primitives)),
85 internal::MakeKeyManager<Primitives>(manager)),
86 0)...};
87 }
88
89 // Takes ownership of `private_manager`, but not of `public_manager`, which
90 // must only be alive for the duration of the constructor.
91 template <typename PrivateKeyProto, typename KeyFormatProto,
92 typename PublicKeyProto, typename PublicPrimitivesList,
93 typename... PrivatePrimitives>
Info(PrivateKeyTypeManager<PrivateKeyProto,KeyFormatProto,PublicKeyProto,List<PrivatePrimitives...>> * private_manager,KeyTypeManager<PublicKeyProto,void,PublicPrimitivesList> * public_manager,bool new_key_allowed)94 Info(PrivateKeyTypeManager<PrivateKeyProto, KeyFormatProto, PublicKeyProto,
95 List<PrivatePrimitives...>>* private_manager,
96 KeyTypeManager<PublicKeyProto, void, PublicPrimitivesList>*
97 public_manager,
98 bool new_key_allowed)
99 : key_manager_type_index_(std::type_index(typeid(*private_manager))),
100 public_key_type_manager_type_index_(
101 std::type_index(typeid(*public_manager))),
102 new_key_allowed_(new_key_allowed),
103 key_type_manager_(absl::WrapUnique(private_manager)),
104 internal_key_factory_(
105 absl::make_unique<internal::PrivateKeyFactoryImpl<
106 PrivateKeyProto, KeyFormatProto, PublicKeyProto,
107 List<PrivatePrimitives...>, PublicPrimitivesList>>(
108 private_manager, public_manager)),
109 key_factory_(internal_key_factory_.get()),
110 key_deriver_(CreateDeriverFunctionFor(private_manager)) {
111 // TODO(C++17): Replace with a fold expression.
112 (void)std::initializer_list<int>{
113 0, (primitive_to_manager_.emplace(
114 std::type_index(typeid(PrivatePrimitives)),
115 internal::MakePrivateKeyManager<PrivatePrimitives>(
116 private_manager, public_manager)),
117 0)...};
118 }
119
120 // Takes ownership of `manager`. KeyManager is the legacy/internal version
121 // of KeyTypeManager.
122 template <typename P>
Info(KeyManager<P> * manager,bool new_key_allowed)123 Info(KeyManager<P>* manager, bool new_key_allowed)
124 : key_manager_type_index_(std::type_index(typeid(*manager))),
125 public_key_type_manager_type_index_(absl::nullopt),
126 new_key_allowed_(new_key_allowed),
127 key_type_manager_(nullptr),
128 internal_key_factory_(nullptr),
129 key_factory_(&manager->get_key_factory()) {
130 primitive_to_manager_.emplace(std::type_index(typeid(P)),
131 absl::WrapUnique(manager));
132 }
133
134 template <typename P>
get_key_manager(absl::string_view requested_type_url)135 crypto::tink::util::StatusOr<const KeyManager<P>*> get_key_manager(
136 absl::string_view requested_type_url) const {
137 auto it = primitive_to_manager_.find(std::type_index(typeid(P)));
138 if (it == primitive_to_manager_.end()) {
139 return crypto::tink::util::Status(
140 absl::StatusCode::kInvalidArgument,
141 absl::StrCat(
142 "Primitive type ", typeid(P).name(),
143 " not among supported primitives ",
144 absl::StrJoin(
145 primitive_to_manager_.begin(), primitive_to_manager_.end(),
146 ", ",
147 [](std::string* out,
148 const std::pair<const std::type_index,
149 std::unique_ptr<KeyManagerBase>>& kv) {
150 absl::StrAppend(out, kv.first.name());
151 }),
152 " for type URL ", requested_type_url));
153 }
154 return static_cast<const KeyManager<P>*>(it->second.get());
155 }
156
key_manager_type_index()157 const std::type_index& key_manager_type_index() const {
158 return key_manager_type_index_;
159 }
160
public_key_type_manager_type_index()161 const absl::optional<std::type_index>& public_key_type_manager_type_index()
162 const {
163 return public_key_type_manager_type_index_;
164 }
165
new_key_allowed()166 bool new_key_allowed() const { return new_key_allowed_.load(); }
167
set_new_key_allowed(bool b)168 void set_new_key_allowed(bool b) { new_key_allowed_.store(b); }
169
key_factory()170 const KeyFactory& key_factory() const { return *key_factory_; }
171
172 const std::function<crypto::tink::util::StatusOr<
173 google::crypto::tink::KeyData>(absl::string_view, InputStream*)>&
key_deriver()174 key_deriver() const {
175 return key_deriver_;
176 }
177
178 private:
179 // Dynamic type_index of the KeyManager or KeyTypeManager for this key type.
180 std::type_index key_manager_type_index_;
181 // Dynamic type_index of the public KeyTypeManager for this key type when
182 // inserted into the registry via RegisterAsymmetricKeyManagers. Otherwise,
183 // nullopt.
184 absl::optional<std::type_index> public_key_type_manager_type_index_;
185 // Whether the key manager allows the creation of new keys.
186 std::atomic<bool> new_key_allowed_;
187
188 // Map from primitive type_index to KeyManager.
189 absl::flat_hash_map<std::type_index, std::unique_ptr<KeyManagerBase>>
190 primitive_to_manager_;
191 // Key type manager. Equals nullptr if Info was constructed from a
192 // KeyManager.
193 const std::shared_ptr<void> key_type_manager_;
194
195 // Key factory. Equals nullptr if Info was constructed from a KeyManager.
196 std::unique_ptr<const KeyFactory> internal_key_factory_;
197 // Unowned version of `internal_key_factory_` if Info was constructed from a
198 // KeyTypeManager. Key factory belonging to the KeyManager if Info was
199 // constructed from a KeyManager.
200 const KeyFactory* key_factory_;
201
202 // Derives a key if Info was constructed from a KeyTypeManager with a
203 // non-void KeyFormat type. Else, this function is empty and casting to a
204 // bool returns false.
205 std::function<crypto::tink::util::StatusOr<google::crypto::tink::KeyData>(
206 absl::string_view, InputStream*)>
207 key_deriver_;
208 };
209
210 // Adds a crypto::tink::KeyTypeManager to KeyTypeInfoStore. `new_key_allowed`
211 // indicates whether `manager` may create new keys.
212 template <class KeyTypeManager>
213 crypto::tink::util::Status AddKeyTypeManager(
214 std::unique_ptr<KeyTypeManager> manager, bool new_key_allowed);
215
216 // Adds a pair of crypto::tink::PrivateKeyTypeManager and
217 // crypto::tink::KeyTypeManager to KeyTypeInfoStore. `new_key_allowed`
218 // indicates whether `private_manager` may create new keys.
219 template <class PrivateKeyTypeManager, class PublicKeyTypeManager>
220 crypto::tink::util::Status AddAsymmetricKeyTypeManagers(
221 std::unique_ptr<PrivateKeyTypeManager> private_manager,
222 std::unique_ptr<PublicKeyTypeManager> public_manager,
223 bool new_key_allowed);
224
225 // Adds a crypto::tink::KeyManager to KeyTypeInfoStore. `new_key_allowed`
226 // indicates whether `manager` may create new keys. KeyManager is the
227 // legacy/internal version of KeyTypeManager.
228 template <class P>
229 crypto::tink::util::Status AddKeyManager(
230 std::unique_ptr<KeyManager<P>> manager, bool new_key_allowed);
231
232 // Gets Info associated with `type_url`, returning either a valid, non-null
233 // Info or an error.
234 crypto::tink::util::StatusOr<Info*> Get(absl::string_view type_url) const;
235
IsEmpty()236 bool IsEmpty() const { return type_url_to_info_.empty(); }
237
238 private:
239 // Whether a key manager with `type_url` and `key_manager_type_index` can be
240 // inserted.
241 crypto::tink::util::Status IsInsertable(
242 absl::string_view type_url, const std::type_index& key_manager_type_index,
243 bool new_key_allowed) const;
244
Add(std::string type_url,std::unique_ptr<Info> info,bool new_key_allowed)245 void Add(std::string type_url, std::unique_ptr<Info> info,
246 bool new_key_allowed) {
247 auto it = type_url_to_info_.find(type_url);
248 if (it != type_url_to_info_.end()) {
249 it->second->set_new_key_allowed(new_key_allowed);
250 } else {
251 type_url_to_info_.insert({type_url, std::move(info)});
252 }
253 }
254
255 // Map from the type_url to Info.
256 // Elements in Info must not be replaced, and pointer stability is required
257 // for `Get()`.
258 absl::flat_hash_map<std::string, std::unique_ptr<Info>> type_url_to_info_;
259 };
260
261 template <class P>
AddKeyManager(std::unique_ptr<KeyManager<P>> manager,bool new_key_allowed)262 crypto::tink::util::Status KeyTypeInfoStore::AddKeyManager(
263 std::unique_ptr<KeyManager<P>> manager, bool new_key_allowed) {
264 std::string type_url = manager->get_key_type();
265 if (!manager->DoesSupport(type_url)) {
266 return ToStatusF(absl::StatusCode::kInvalidArgument,
267 "The manager does not support type '%s'.", type_url);
268 }
269
270 crypto::tink::util::Status status = IsInsertable(
271 type_url, std::type_index(typeid(*manager)), new_key_allowed);
272 if (!status.ok()) {
273 return status;
274 }
275
276 auto info = absl::make_unique<Info>(manager.release(), new_key_allowed);
277 Add(type_url, std::move(info), new_key_allowed);
278 return crypto::tink::util::OkStatus();
279 }
280
281 template <class KeyTypeManager>
AddKeyTypeManager(std::unique_ptr<KeyTypeManager> manager,bool new_key_allowed)282 crypto::tink::util::Status KeyTypeInfoStore::AddKeyTypeManager(
283 std::unique_ptr<KeyTypeManager> manager, bool new_key_allowed) {
284 // Check FIPS status.
285 internal::FipsCompatibility fips_compatible = manager->FipsStatus();
286 auto fips_status = internal::ChecksFipsCompatibility(fips_compatible);
287 if (!fips_status.ok()) {
288 return crypto::tink::util::Status(
289 absl::StatusCode::kInternal,
290 absl::StrCat("Failed registering the key manager for ",
291 typeid(*manager).name(),
292 " as it is not FIPS compatible: ", fips_status.message()));
293 }
294
295 std::string type_url = manager->get_key_type();
296 crypto::tink::util::Status status = IsInsertable(
297 type_url, std::type_index(typeid(*manager)), new_key_allowed);
298 if (!status.ok()) {
299 return status;
300 }
301
302 auto info = absl::make_unique<Info>(manager.release(), new_key_allowed);
303 Add(type_url, std::move(info), new_key_allowed);
304 return crypto::tink::util::OkStatus();
305 }
306
307 template <class PrivateKeyTypeManager, class PublicKeyTypeManager>
AddAsymmetricKeyTypeManagers(std::unique_ptr<PrivateKeyTypeManager> private_manager,std::unique_ptr<PublicKeyTypeManager> public_manager,bool new_key_allowed)308 crypto::tink::util::Status KeyTypeInfoStore::AddAsymmetricKeyTypeManagers(
309 std::unique_ptr<PrivateKeyTypeManager> private_manager,
310 std::unique_ptr<PublicKeyTypeManager> public_manager,
311 bool new_key_allowed) {
312 std::string private_type_url = private_manager->get_key_type();
313 std::string public_type_url = public_manager->get_key_type();
314 if (private_type_url == public_type_url) {
315 return crypto::tink::util::Status(
316 absl::StatusCode::kInvalidArgument,
317 "Passed in key managers must have different get_key_type() results.");
318 }
319
320 // Check FIPS status.
321 auto private_fips_status =
322 internal::ChecksFipsCompatibility(private_manager->FipsStatus());
323 if (!private_fips_status.ok()) {
324 return crypto::tink::util::Status(
325 absl::StatusCode::kInternal,
326 absl::StrCat(
327 "Failed registering the key manager for ",
328 typeid(*private_manager).name(),
329 " as it is not FIPS compatible: ", private_fips_status.message()));
330 }
331 auto public_fips_status =
332 internal::ChecksFipsCompatibility(public_manager->FipsStatus());
333 if (!public_fips_status.ok()) {
334 return crypto::tink::util::Status(
335 absl::StatusCode::kInternal,
336 absl::StrCat(
337 "Failed registering the key manager for ",
338 typeid(*public_manager).name(),
339 " as it is not FIPS compatible: ", public_fips_status.message()));
340 }
341
342 crypto::tink::util::Status private_status =
343 IsInsertable(private_type_url, std::type_index(typeid(*private_manager)),
344 new_key_allowed);
345 if (!private_status.ok()) {
346 return private_status;
347 }
348 crypto::tink::util::Status public_status =
349 IsInsertable(public_type_url, std::type_index(typeid(*public_manager)),
350 new_key_allowed);
351 if (!public_status.ok()) {
352 return public_status;
353 }
354
355 util::StatusOr<KeyTypeInfoStore::Info*> private_found = Get(private_type_url);
356 util::StatusOr<const KeyTypeInfoStore::Info*> public_found =
357 Get(public_type_url);
358
359 // Only one of the private and public key type managers is found.
360 if (private_found.ok() && !public_found.ok()) {
361 return crypto::tink::util::Status(
362 absl::StatusCode::kInvalidArgument,
363 absl::StrCat(
364 "Private key manager corresponding to ",
365 typeid(*private_manager).name(),
366 " was previously registered, but key manager corresponding to ",
367 typeid(*public_manager).name(),
368 " was not, so it's impossible to register them jointly"));
369 }
370 if (!private_found.ok() && public_found.ok()) {
371 return crypto::tink::util::Status(
372 absl::StatusCode::kInvalidArgument,
373 absl::StrCat("Key manager corresponding to ",
374 typeid(*public_manager).name(),
375 " was previously registered, but private key manager "
376 "corresponding to ",
377 typeid(*private_manager).name(),
378 " was not, so it's impossible to register them jointly"));
379 }
380
381 // Both private and public key type managers are found.
382 if (private_found.ok() && public_found.ok()) {
383 if (!(*private_found)->public_key_type_manager_type_index().has_value()) {
384 return crypto::tink::util::Status(
385 absl::StatusCode::kInvalidArgument,
386 absl::StrCat("private key manager corresponding to ",
387 typeid(*private_manager).name(),
388 " is already registered without public key manager, "
389 "cannot be re-registered with public key manager. "));
390 }
391 if ((*private_found)->public_key_type_manager_type_index() !=
392 std::type_index(typeid(*public_manager))) {
393 return crypto::tink::util::Status(
394 absl::StatusCode::kInvalidArgument,
395 absl::StrCat(
396 "private key manager corresponding to ",
397 typeid(*private_manager).name(), " is already registered with ",
398 (*private_found)->public_key_type_manager_type_index()->name(),
399 ", cannot be re-registered with ",
400 typeid(*public_manager).name()));
401 }
402 // Since `private_manager` passed the `IsInsertable` check above, the
403 // `set_new_key_allowed` operation is permissible.
404 (*private_found)->set_new_key_allowed(new_key_allowed);
405 return crypto::tink::util::OkStatus();
406 }
407
408 // Both private and public key type managers were not found.
409 auto private_info = absl::make_unique<Info>(
410 private_manager.release(), public_manager.get(), new_key_allowed);
411 Add(private_type_url, std::move(private_info), new_key_allowed);
412 // TODO(b/265705174): Store public key type managers in an asymmetric pair
413 // with new_key_allowed = false.
414 auto public_info =
415 absl::make_unique<Info>(public_manager.release(), new_key_allowed);
416 Add(public_type_url, std::move(public_info), new_key_allowed);
417
418 return crypto::tink::util::OkStatus();
419 }
420
421 } // namespace internal
422 } // namespace tink
423 } // namespace crypto
424
425 #endif // TINK_INTERNAL_KEY_TYPE_INFO_STORE_H_
426