xref: /aosp_15_r20/external/tink/cc/core/keyset_handle_builder.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2022 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 #include "tink/keyset_handle_builder.h"
18 
19 #include <iostream>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/log/check.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/string_view.h"
29 #include "absl/types/optional.h"
30 #include "tink/key_status.h"
31 #include "tink/keyset_handle.h"
32 #include "tink/subtle/random.h"
33 #include "proto/tink.pb.h"
34 
35 namespace crypto {
36 namespace tink {
37 namespace {
38 
39 using ::google::crypto::tink::Keyset;
40 
SetBuilderEntryAttributes(KeyStatus status,bool is_primary,absl::optional<int> id,KeysetHandleBuilder::Entry * entry)41 void SetBuilderEntryAttributes(KeyStatus status, bool is_primary,
42                                absl::optional<int> id,
43                                KeysetHandleBuilder::Entry* entry) {
44   entry->SetStatus(status);
45   if (is_primary) {
46     entry->SetPrimary();
47   } else {
48     entry->UnsetPrimary();
49   }
50   if (id.has_value()) {
51     entry->SetFixedId(*id);
52   } else {
53     entry->SetRandomId();
54   }
55 }
56 
57 }  // namespace
58 
KeysetHandleBuilder(const KeysetHandle & handle)59 KeysetHandleBuilder::KeysetHandleBuilder(const KeysetHandle& handle) {
60   for (int i = 0; i < handle.size(); ++i) {
61     KeysetHandle::Entry entry = handle[i];
62     KeysetHandleBuilder::Entry builder_entry =
63         KeysetHandleBuilder::Entry::CreateFromKey(
64             std::move(entry.key_), entry.GetStatus(), entry.IsPrimary());
65     AddEntry(std::move(builder_entry));
66   }
67 }
68 
CreateFromKey(std::shared_ptr<const Key> key,KeyStatus status,bool is_primary)69 KeysetHandleBuilder::Entry KeysetHandleBuilder::Entry::CreateFromKey(
70     std::shared_ptr<const Key> key, KeyStatus status, bool is_primary) {
71   absl::optional<int> id_requirement = key->GetIdRequirement();
72   auto imported_entry = absl::make_unique<internal::KeyEntry>(std::move(key));
73   KeysetHandleBuilder::Entry entry(std::move(imported_entry));
74   SetBuilderEntryAttributes(status, is_primary, id_requirement, &entry);
75   return entry;
76 }
77 
CreateFromParams(std::shared_ptr<const Parameters> parameters,KeyStatus status,bool is_primary,absl::optional<int> id)78 KeysetHandleBuilder::Entry KeysetHandleBuilder::Entry::CreateFromParams(
79     std::shared_ptr<const Parameters> parameters, KeyStatus status,
80     bool is_primary, absl::optional<int> id) {
81   auto generated_entry =
82       absl::make_unique<internal::ParametersEntry>(std::move(parameters));
83   KeysetHandleBuilder::Entry entry(std::move(generated_entry));
84   SetBuilderEntryAttributes(status, is_primary, id, &entry);
85   return entry;
86 }
87 
NextIdFromKeyIdStrategy(internal::KeyIdStrategy strategy,const std::set<int> & ids_so_far)88 util::StatusOr<int> KeysetHandleBuilder::NextIdFromKeyIdStrategy(
89     internal::KeyIdStrategy strategy, const std::set<int>& ids_so_far) {
90   if (strategy.strategy == internal::KeyIdStrategyEnum::kFixedId) {
91     if (!strategy.id_requirement.has_value()) {
92       return util::Status(absl::StatusCode::kInvalidArgument,
93                           "Missing fixed id with fixed id strategy.");
94     }
95     return *strategy.id_requirement;
96   }
97   if (strategy.strategy == internal::KeyIdStrategyEnum::kRandomId) {
98     int id = 0;
99     while (id == 0 || ids_so_far.find(id) != ids_so_far.end()) {
100       id = subtle::Random::GetRandomUInt32();
101     }
102     return id;
103   }
104   return util::Status(absl::StatusCode::kInvalidArgument,
105                       "Invalid key id strategy.");
106 }
107 
ClearPrimary()108 void KeysetHandleBuilder::ClearPrimary() {
109   for (KeysetHandleBuilder::Entry& entry : entries_) {
110     entry.UnsetPrimary();
111   }
112 }
113 
AddEntry(KeysetHandleBuilder::Entry entry)114 KeysetHandleBuilder& KeysetHandleBuilder::AddEntry(
115     KeysetHandleBuilder::Entry entry) {
116   CHECK(!entry.added_to_builder_)
117       << "Keyset handle builder entry already added to a builder.";
118   entry.added_to_builder_ = true;
119   if (entry.IsPrimary()) {
120     ClearPrimary();
121   }
122   entries_.push_back(std::move(entry));
123   return *this;
124 }
125 
RemoveEntry(int index)126 KeysetHandleBuilder& KeysetHandleBuilder::RemoveEntry(int index) {
127   CHECK(index >= 0 && index < entries_.size())
128       << "Keyset handle builder entry removal index out of range.";
129   entries_.erase(entries_.begin() + index);
130   return *this;
131 }
132 
CheckIdAssignments()133 util::Status KeysetHandleBuilder::CheckIdAssignments() {
134   // We only want random id entries after fixed id entries. Otherwise, we might
135   // randomly pick an id that is later specified as a fixed id.
136   for (int i = 0; i < entries_.size() - 1; ++i) {
137     if (entries_[i].HasRandomId() && !entries_[i + 1].HasRandomId()) {
138       return util::Status(absl::StatusCode::kFailedPrecondition,
139                           "Entries with random ids may only be followed "
140                           "by other entries with random ids.");
141     }
142   }
143   return util::OkStatus();
144 }
145 
Build()146 util::StatusOr<KeysetHandle> KeysetHandleBuilder::Build() {
147   if (build_called_) {
148       return util::Status(
149           absl::StatusCode::kFailedPrecondition,
150           "KeysetHandleBuilder::Build may only be called once");
151   }
152   build_called_ = true;
153   Keyset keyset;
154   absl::optional<int> primary_id = absl::nullopt;
155 
156   util::Status assigned_ids_status = CheckIdAssignments();
157   if (!assigned_ids_status.ok()) return assigned_ids_status;
158 
159   std::set<int> ids_so_far;
160   for (KeysetHandleBuilder::Entry& entry : entries_) {
161     util::StatusOr<int> id =
162         NextIdFromKeyIdStrategy(entry.GetKeyIdStrategy(), ids_so_far);
163     if (!id.ok()) return id.status();
164 
165     if (ids_so_far.find(*id) != ids_so_far.end()) {
166       return util::Status(
167           absl::StatusCode::kAlreadyExists,
168           absl::StrFormat("Next id %d is already used in the keyset.", *id));
169     }
170     ids_so_far.insert(*id);
171 
172     util::StatusOr<Keyset::Key> key = entry.CreateKeysetKey(*id);
173     if (!key.ok()) return key.status();
174 
175     *keyset.add_key() = *key;
176     if (entry.IsPrimary()) {
177       if (primary_id.has_value()) {
178         return util::Status(
179             absl::StatusCode::kInternal,
180             "Primary is already set in this keyset (should never happen since "
181             "primary is cleared when a new primary is added).");
182       }
183       primary_id = *id;
184     }
185   }
186 
187   if (!primary_id.has_value()) {
188     return util::Status(absl::StatusCode::kFailedPrecondition,
189                         "No primary set in this keyset.");
190   }
191   keyset.set_primary_key_id(*primary_id);
192   util::StatusOr<std::vector<std::shared_ptr<const KeysetHandle::Entry>>>
193       entries = KeysetHandle::GetEntriesFromKeyset(keyset);
194   return KeysetHandle(keyset, *std::move(entries));
195 }
196 
197 }  // namespace tink
198 }  // namespace crypto
199