1 // Copyright 2018 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_INTERNAL_KEYSET_WRAPPER_STORE_H_
18 #define TINK_INTERNAL_KEYSET_WRAPPER_STORE_H_
19
20 #include <memory>
21 #include <typeindex>
22
23 #include "absl/functional/any_invocable.h"
24 #include "tink/internal/keyset_wrapper.h"
25 #include "tink/internal/keyset_wrapper_impl.h"
26 #include "tink/primitive_wrapper.h"
27 #include "tink/util/status.h"
28 #include "tink/util/statusor.h"
29
30 namespace crypto {
31 namespace tink {
32 namespace internal {
33
34 // Stores KeysetWrappers constructed from their PrimitiveWrapper. This is used
35 // by the Configuration and Registry classes.
36 //
37 // Once inserted, elements in Info, which include the PrimitiveWrapper, must not
38 // be replaced.
39 //
40 // Example:
41 // KeysetWrapperStore store;
42 // crypto::tink::util::Status status = store.Add<Aead, Aead>(
43 // absl::make_unique<AeadWrapper>(), std::move(primitive_getter));
44 // crypto::tink::util::StatusOr<const KeysetWrapper<Aead>*> wrapper =
45 // store.Get<Aead>();
46 class KeysetWrapperStore {
47 public:
48 KeysetWrapperStore() = default;
49
50 // Movable, but not copyable.
51 KeysetWrapperStore(KeysetWrapperStore&& other) = default;
52 KeysetWrapperStore& operator=(KeysetWrapperStore&& other) = default;
53
54 // Adds a crypto::tink::PrimitiveWrapper and `primitive_getter` function to
55 // KeysetWrapperStore.
56 template <class P, class Q>
57 crypto::tink::util::Status Add(
58 std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper,
59 absl::AnyInvocable<crypto::tink::util::StatusOr<std::unique_ptr<P>>(
60 const google::crypto::tink::KeyData& key_data) const>
61 primitive_getter);
62
63 // Gets the PrimitiveWrapper that produces primitive P. This is a legacy
64 // function.
65 template <class P>
66 crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*>
67 GetPrimitiveWrapper() const;
68
69 // Gets the KeysetWrapper that produces primitive Q.
70 template <class Q>
71 crypto::tink::util::StatusOr<const KeysetWrapper<Q>*> Get() const;
72
IsEmpty()73 bool IsEmpty() const { return primitive_to_info_.empty(); }
74
75 private:
76 class Info {
77 public:
78 template <typename P, typename Q>
Info(std::unique_ptr<PrimitiveWrapper<P,Q>> wrapper,absl::AnyInvocable<crypto::tink::util::StatusOr<std::unique_ptr<P>> (const google::crypto::tink::KeyData & key_data)const> primitive_getter)79 explicit Info(
80 std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper,
81 absl::AnyInvocable<crypto::tink::util::StatusOr<std::unique_ptr<P>>(
82 const google::crypto::tink::KeyData& key_data) const>
83 primitive_getter)
84 : is_same_primitive_wrapping_(std::is_same<P, Q>::value),
85 wrapper_type_index_(std::type_index(typeid(*wrapper))),
86 q_type_index_(std::type_index(typeid(Q))) {
87 keyset_wrapper_ = absl::make_unique<KeysetWrapperImpl<P, Q>>(
88 wrapper.get(), std::move(primitive_getter));
89 original_wrapper_ = std::move(wrapper);
90 }
91
92 template <typename Q>
Get()93 crypto::tink::util::StatusOr<const KeysetWrapper<Q>*> Get() const {
94 if (q_type_index_ != std::type_index(typeid(Q))) {
95 return crypto::tink::util::Status(
96 absl::StatusCode::kInternal,
97 "RegistryImpl::KeysetWrapper() called with wrong type");
98 }
99 return static_cast<KeysetWrapper<Q>*>(keyset_wrapper_.get());
100 }
101
102 // TODO(b/171021679): Deprecate this and upstream functions.
103 template <typename P>
104 crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*>
GetPrimitiveWrapper()105 GetPrimitiveWrapper() const {
106 if (!is_same_primitive_wrapping_) {
107 // This happens if a user uses a legacy method (like Registry::Wrap)
108 // directly or has a custom key manager for a primitive which has a
109 // PrimitiveWrapper<P,Q> with P != Q.
110 return crypto::tink::util::Status(
111 absl::StatusCode::kFailedPrecondition,
112 absl::StrCat("Cannot use primitive type ", typeid(P).name(),
113 " with a custom key manager."));
114 }
115 if (q_type_index_ != std::type_index(typeid(P))) {
116 return crypto::tink::util::Status(
117 absl::StatusCode::kInternal,
118 "RegistryImpl::LegacyWrapper() called with wrong type");
119 }
120 return static_cast<const PrimitiveWrapper<P, P>*>(
121 original_wrapper_.get());
122 }
123
124 // Returns true if the PrimitiveWrapper is the same class as the one used
125 // to construct this Info.
126 template <typename P, typename Q>
HasSameType(const PrimitiveWrapper<P,Q> & wrapper)127 bool HasSameType(const PrimitiveWrapper<P, Q>& wrapper) {
128 return wrapper_type_index_ == std::type_index(typeid(wrapper));
129 }
130
131 private:
132 bool is_same_primitive_wrapping_;
133 // dynamic std::type_index of the actual PrimitiveWrapper<P,Q> class for
134 // which this key was inserted.
135 std::type_index wrapper_type_index_;
136 // dynamic std::type_index of Q, when PrimitiveWrapper<P,Q> was inserted.
137 std::type_index q_type_index_;
138 // The primitive_wrapper passed in. We use a shared_ptr because
139 // unique_ptr<void> is invalid.
140 std::shared_ptr<void> original_wrapper_;
141 // The keyset_wrapper_. We use a shared_ptr because unique_ptr<void> is
142 // invalid.
143 std::shared_ptr<void> keyset_wrapper_;
144 };
145
146 // Map from primitive type_index to Info.
147 absl::flat_hash_map<std::type_index, Info> primitive_to_info_;
148 };
149
150 template <class P, class Q>
Add(std::unique_ptr<PrimitiveWrapper<P,Q>> wrapper,absl::AnyInvocable<crypto::tink::util::StatusOr<std::unique_ptr<P>> (const google::crypto::tink::KeyData & key_data)const> primitive_getter)151 crypto::tink::util::Status KeysetWrapperStore::Add(
152 std::unique_ptr<PrimitiveWrapper<P, Q>> wrapper,
153 absl::AnyInvocable<crypto::tink::util::StatusOr<std::unique_ptr<P>>(
154 const google::crypto::tink::KeyData& key_data) const>
155 primitive_getter) {
156 if (wrapper == nullptr) {
157 return crypto::tink::util::Status(absl::StatusCode::kInvalidArgument,
158 "Parameter 'wrapper' must be non-null.");
159 }
160 auto it = primitive_to_info_.find(std::type_index(typeid(Q)));
161 if (it != primitive_to_info_.end()) {
162 if (!it->second.HasSameType(*wrapper)) {
163 return util::Status(absl::StatusCode::kAlreadyExists,
164 "A wrapper named for this primitive already exists.");
165 }
166 return crypto::tink::util::OkStatus();
167 }
168
169 primitive_to_info_.insert(
170 {std::type_index(typeid(Q)),
171 Info(std::move(wrapper), std::move(primitive_getter))});
172
173 return crypto::tink::util::OkStatus();
174 }
175
176 template <class P>
177 crypto::tink::util::StatusOr<const PrimitiveWrapper<P, P>*>
GetPrimitiveWrapper()178 KeysetWrapperStore::GetPrimitiveWrapper() const {
179 auto it = primitive_to_info_.find(std::type_index(typeid(P)));
180 if (it == primitive_to_info_.end()) {
181 return util::Status(
182 absl::StatusCode::kNotFound,
183 absl::StrCat("No wrapper registered for type ", typeid(P).name()));
184 }
185 return it->second.GetPrimitiveWrapper<P>();
186 }
187
188 template <class P>
Get()189 crypto::tink::util::StatusOr<const KeysetWrapper<P>*> KeysetWrapperStore::Get()
190 const {
191 auto it = primitive_to_info_.find(std::type_index(typeid(P)));
192 if (it == primitive_to_info_.end()) {
193 return util::Status(
194 absl::StatusCode::kNotFound,
195 absl::StrCat("No wrapper registered for type ", typeid(P).name()));
196 }
197 return it->second.Get<P>();
198 }
199
200 } // namespace internal
201 } // namespace tink
202 } // namespace crypto
203
204 #endif // TINK_INTERNAL_KEYSET_WRAPPER_STORE_H_
205