1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <keymaster/authorization_set.h>
18
19 #include <assert.h>
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <new>
25
26 #include <keymaster/android_keymaster_utils.h>
27 #include <keymaster/logger.h>
28
29 namespace keymaster {
30
is_blob_tag(keymaster_tag_t tag)31 static inline bool is_blob_tag(keymaster_tag_t tag) {
32 return (keymaster_tag_get_type(tag) == KM_BYTES || keymaster_tag_get_type(tag) == KM_BIGNUM);
33 }
34
35 const size_t STARTING_ELEMS_CAPACITY = 8;
36
AuthorizationSet(AuthorizationSetBuilder & builder)37 AuthorizationSet::AuthorizationSet(AuthorizationSetBuilder& builder) {
38 elems_ = builder.set.elems_;
39 builder.set.elems_ = NULL;
40
41 elems_size_ = builder.set.elems_size_;
42 builder.set.elems_size_ = 0;
43
44 elems_capacity_ = builder.set.elems_capacity_;
45 builder.set.elems_capacity_ = 0;
46
47 indirect_data_ = builder.set.indirect_data_;
48 builder.set.indirect_data_ = NULL;
49
50 indirect_data_capacity_ = builder.set.indirect_data_capacity_;
51 builder.set.indirect_data_capacity_ = 0;
52
53 indirect_data_size_ = builder.set.indirect_data_size_;
54 builder.set.indirect_data_size_ = 0;
55
56 error_ = builder.set.error_;
57 builder.set.error_ = OK;
58 }
59
~AuthorizationSet()60 AuthorizationSet::~AuthorizationSet() {
61 FreeData();
62 }
63
reserve_elems(size_t count)64 bool AuthorizationSet::reserve_elems(size_t count) {
65 if (is_valid() != OK)
66 return false;
67
68 if (count >= elems_capacity_) {
69 keymaster_key_param_t* new_elems = new (std::nothrow) keymaster_key_param_t[count];
70 if (new_elems == NULL) {
71 set_invalid(ALLOCATION_FAILURE);
72 return false;
73 }
74 memcpy(new_elems, elems_, sizeof(*elems_) * elems_size_);
75 delete[] elems_;
76 elems_ = new_elems;
77 elems_capacity_ = count;
78 }
79 return true;
80 }
81
reserve_indirect(size_t length)82 bool AuthorizationSet::reserve_indirect(size_t length) {
83 if (is_valid() != OK)
84 return false;
85
86 if (length > indirect_data_capacity_) {
87 uint8_t* new_data = new (std::nothrow) uint8_t[length];
88 if (new_data == NULL) {
89 set_invalid(ALLOCATION_FAILURE);
90 return false;
91 }
92 memcpy(new_data, indirect_data_, indirect_data_size_);
93
94 // Fix up the data pointers to point into the new region.
95 for (size_t i = 0; i < elems_size_; ++i) {
96 if (is_blob_tag(elems_[i].tag))
97 elems_[i].blob.data = new_data + (elems_[i].blob.data - indirect_data_);
98 }
99 delete[] indirect_data_;
100 indirect_data_ = new_data;
101 indirect_data_capacity_ = length;
102 }
103 return true;
104 }
105
MoveFrom(AuthorizationSet & set)106 void AuthorizationSet::MoveFrom(AuthorizationSet& set) {
107 elems_ = set.elems_;
108 elems_size_ = set.elems_size_;
109 elems_capacity_ = set.elems_capacity_;
110 indirect_data_ = set.indirect_data_;
111 indirect_data_size_ = set.indirect_data_size_;
112 indirect_data_capacity_ = set.indirect_data_capacity_;
113 error_ = set.error_;
114 set.elems_ = nullptr;
115 set.elems_size_ = 0;
116 set.elems_capacity_ = 0;
117 set.indirect_data_ = nullptr;
118 set.indirect_data_size_ = 0;
119 set.indirect_data_capacity_ = 0;
120 set.error_ = OK;
121 }
122
Reinitialize(const keymaster_key_param_t * elems,const size_t count)123 bool AuthorizationSet::Reinitialize(const keymaster_key_param_t* elems, const size_t count) {
124 FreeData();
125
126 if (elems == NULL || count == 0) {
127 error_ = OK;
128 return true;
129 }
130
131 if (!reserve_elems(count))
132 return false;
133
134 if (!reserve_indirect(ComputeIndirectDataSize(elems, count)))
135 return false;
136
137 memcpy(elems_, elems, sizeof(keymaster_key_param_t) * count);
138 elems_size_ = count;
139 CopyIndirectData();
140 error_ = OK;
141 return true;
142 }
143
set_invalid(Error error)144 void AuthorizationSet::set_invalid(Error error) {
145 FreeData();
146 error_ = error;
147 }
148
Sort()149 void AuthorizationSet::Sort() {
150 qsort(elems_, elems_size_, sizeof(*elems_),
151 reinterpret_cast<int (*)(const void*, const void*)>(keymaster_param_compare));
152 }
153
Deduplicate()154 void AuthorizationSet::Deduplicate() {
155 Sort();
156
157 size_t invalid_count = 0;
158 for (size_t i = 1; i < size(); ++i) {
159 if (elems_[i - 1].tag == KM_TAG_INVALID)
160 ++invalid_count;
161 else if (keymaster_param_compare(elems_ + i - 1, elems_ + i) == 0) {
162 // Mark dups as invalid. Note that this "leaks" the data referenced by KM_BYTES and
163 // KM_BIGNUM entries, but those are just pointers into indirect_data_, so it will all
164 // get cleaned up.
165 elems_[i - 1].tag = KM_TAG_INVALID;
166 ++invalid_count;
167 }
168 }
169 if (size() > 0 && elems_[size() - 1].tag == KM_TAG_INVALID)
170 ++invalid_count;
171
172 if (invalid_count == 0)
173 return;
174
175 Sort();
176
177 // Since KM_TAG_INVALID == 0, all of the invalid entries are first.
178 elems_size_ -= invalid_count;
179 memmove(elems_, elems_ + invalid_count, size() * sizeof(*elems_));
180 }
181
CopyToParamSet(keymaster_key_param_set_t * set) const182 void AuthorizationSet::CopyToParamSet(keymaster_key_param_set_t* set) const {
183 assert(set);
184
185 set->length = size();
186 set->params =
187 reinterpret_cast<keymaster_key_param_t*>(malloc(sizeof(keymaster_key_param_t) * size()));
188
189 for (size_t i = 0; i < size(); ++i) {
190 const keymaster_key_param_t src = (*this)[i];
191 keymaster_key_param_t& dst(set->params[i]);
192
193 dst = src;
194 keymaster_tag_type_t type = keymaster_tag_get_type(src.tag);
195 if (type == KM_BIGNUM || type == KM_BYTES) {
196 void* tmp = malloc(src.blob.data_length);
197 memcpy(tmp, src.blob.data, src.blob.data_length);
198 dst.blob.data = reinterpret_cast<uint8_t*>(tmp);
199 }
200 }
201 }
202
find(keymaster_tag_t tag,int begin) const203 int AuthorizationSet::find(keymaster_tag_t tag, int begin) const {
204 if (is_valid() != OK)
205 return -1;
206
207 int i = ++begin;
208 while (i < (int)elems_size_ && elems_[i].tag != tag)
209 ++i;
210 if (i == (int)elems_size_)
211 return -1;
212 else
213 return i;
214 }
215
erase(int index)216 bool AuthorizationSet::erase(int index) {
217 if (index < 0 || index >= static_cast<int>(size()))
218 return false;
219
220 --elems_size_;
221 for (size_t i = index; i < elems_size_; ++i)
222 elems_[i] = elems_[i + 1];
223 return true;
224 }
225
226 keymaster_key_param_t empty_param = {KM_TAG_INVALID, {}};
operator [](int at)227 keymaster_key_param_t& AuthorizationSet::operator[](int at) {
228 if (is_valid() == OK && at < (int)elems_size_) {
229 return elems_[at];
230 }
231 empty_param = {KM_TAG_INVALID, {}};
232 return empty_param;
233 }
234
operator [](int at) const235 keymaster_key_param_t AuthorizationSet::operator[](int at) const {
236 if (is_valid() == OK && at < (int)elems_size_) {
237 return elems_[at];
238 }
239 empty_param = {KM_TAG_INVALID, {}};
240 return empty_param;
241 }
242
push_back(const keymaster_key_param_set_t & set)243 bool AuthorizationSet::push_back(const keymaster_key_param_set_t& set) {
244 if (is_valid() != OK)
245 return false;
246
247 if (!reserve_elems(elems_size_ + set.length))
248 return false;
249
250 if (!reserve_indirect(indirect_data_size_ + ComputeIndirectDataSize(set.params, set.length)))
251 return false;
252
253 for (size_t i = 0; i < set.length; ++i)
254 if (!push_back(set.params[i]))
255 return false;
256
257 return true;
258 }
259
push_back(keymaster_key_param_t elem)260 bool AuthorizationSet::push_back(keymaster_key_param_t elem) {
261 if (is_valid() != OK)
262 return false;
263
264 if (elems_size_ >= elems_capacity_)
265 if (!reserve_elems(elems_capacity_ ? elems_capacity_ * 2 : STARTING_ELEMS_CAPACITY))
266 return false;
267
268 if (is_blob_tag(elem.tag)) {
269 if (indirect_data_capacity_ - indirect_data_size_ < elem.blob.data_length)
270 if (!reserve_indirect(2 * (indirect_data_capacity_ + elem.blob.data_length)))
271 return false;
272
273 memcpy(indirect_data_ + indirect_data_size_, elem.blob.data, elem.blob.data_length);
274 elem.blob.data = indirect_data_ + indirect_data_size_;
275 indirect_data_size_ += elem.blob.data_length;
276 }
277
278 elems_[elems_size_++] = elem;
279 return true;
280 }
281
serialized_size(const keymaster_key_param_t & param)282 static size_t serialized_size(const keymaster_key_param_t& param) {
283 switch (keymaster_tag_get_type(param.tag)) {
284 case KM_INVALID:
285 return sizeof(uint32_t);
286 case KM_ENUM:
287 case KM_ENUM_REP:
288 case KM_UINT:
289 case KM_UINT_REP:
290 return sizeof(uint32_t) * 2;
291 case KM_ULONG:
292 case KM_ULONG_REP:
293 case KM_DATE:
294 return sizeof(uint32_t) + sizeof(uint64_t);
295 case KM_BOOL:
296 return sizeof(uint32_t) + 1;
297 case KM_BIGNUM:
298 case KM_BYTES:
299 return sizeof(uint32_t) * 3;
300 }
301
302 return sizeof(uint32_t);
303 }
304
serialize(const keymaster_key_param_t & param,uint8_t * buf,const uint8_t * end,const uint8_t * indirect_base)305 static uint8_t* serialize(const keymaster_key_param_t& param, uint8_t* buf, const uint8_t* end,
306 const uint8_t* indirect_base) {
307 buf = append_uint32_to_buf(buf, end, param.tag);
308 switch (keymaster_tag_get_type(param.tag)) {
309 case KM_INVALID:
310 break;
311 case KM_ENUM:
312 case KM_ENUM_REP:
313 buf = append_uint32_to_buf(buf, end, param.enumerated);
314 break;
315 case KM_UINT:
316 case KM_UINT_REP:
317 buf = append_uint32_to_buf(buf, end, param.integer);
318 break;
319 case KM_ULONG:
320 case KM_ULONG_REP:
321 buf = append_uint64_to_buf(buf, end, param.long_integer);
322 break;
323 case KM_DATE:
324 buf = append_uint64_to_buf(buf, end, param.date_time);
325 break;
326 case KM_BOOL:
327 if (buf < end)
328 *buf = static_cast<uint8_t>(param.boolean);
329 buf++;
330 break;
331 case KM_BIGNUM:
332 case KM_BYTES:
333 buf = append_uint32_to_buf(buf, end, param.blob.data_length);
334 buf = append_uint32_to_buf(buf, end, param.blob.data - indirect_base);
335 break;
336 }
337 return buf;
338 }
339
deserialize(keymaster_key_param_t * param,const uint8_t ** buf_ptr,const uint8_t * end,const uint8_t * indirect_base,const uint8_t * indirect_end)340 static bool deserialize(keymaster_key_param_t* param, const uint8_t** buf_ptr, const uint8_t* end,
341 const uint8_t* indirect_base, const uint8_t* indirect_end) {
342 if (!copy_uint32_from_buf(buf_ptr, end, ¶m->tag))
343 return false;
344
345 switch (keymaster_tag_get_type(param->tag)) {
346 case KM_INVALID:
347 return false;
348 case KM_ENUM:
349 case KM_ENUM_REP:
350 return copy_uint32_from_buf(buf_ptr, end, ¶m->enumerated);
351 case KM_UINT:
352 case KM_UINT_REP:
353 return copy_uint32_from_buf(buf_ptr, end, ¶m->integer);
354 case KM_ULONG:
355 case KM_ULONG_REP:
356 return copy_uint64_from_buf(buf_ptr, end, ¶m->long_integer);
357 case KM_DATE:
358 return copy_uint64_from_buf(buf_ptr, end, ¶m->date_time);
359 break;
360 case KM_BOOL:
361 if (*buf_ptr < end) {
362 param->boolean = static_cast<bool>(**buf_ptr);
363 (*buf_ptr)++;
364 return true;
365 }
366 return false;
367
368 case KM_BIGNUM:
369 case KM_BYTES: {
370 uint32_t offset;
371 if (!copy_uint32_from_buf(buf_ptr, end, ¶m->blob.data_length) ||
372 !copy_uint32_from_buf(buf_ptr, end, &offset))
373 return false;
374 if (param->blob.data_length + offset < param->blob.data_length || // Overflow check
375 static_cast<ptrdiff_t>(offset) > indirect_end - indirect_base ||
376 static_cast<ptrdiff_t>(offset + param->blob.data_length) > indirect_end - indirect_base)
377 return false;
378 param->blob.data = indirect_base + offset;
379 return true;
380 }
381 }
382
383 return false;
384 }
385
SerializedSizeOfElements() const386 size_t AuthorizationSet::SerializedSizeOfElements() const {
387 size_t size = 0;
388 for (size_t i = 0; i < elems_size_; ++i) {
389 size += serialized_size(elems_[i]);
390 }
391 return size;
392 }
393
SerializedSize() const394 size_t AuthorizationSet::SerializedSize() const {
395 return sizeof(uint32_t) + // Size of indirect_data_
396 indirect_data_size_ + // indirect_data_
397 sizeof(uint32_t) + // Number of elems_
398 sizeof(uint32_t) + // Size of elems_
399 SerializedSizeOfElements(); // elems_
400 }
401
Serialize(uint8_t * buf,const uint8_t * end) const402 uint8_t* AuthorizationSet::Serialize(uint8_t* buf, const uint8_t* end) const {
403 buf = append_size_and_data_to_buf(buf, end, indirect_data_, indirect_data_size_);
404 buf = append_uint32_to_buf(buf, end, elems_size_);
405 buf = append_uint32_to_buf(buf, end, SerializedSizeOfElements());
406 for (size_t i = 0; i < elems_size_; ++i) {
407 buf = serialize(elems_[i], buf, end, indirect_data_);
408 }
409 return buf;
410 }
411
DeserializeIndirectData(const uint8_t ** buf_ptr,const uint8_t * end)412 bool AuthorizationSet::DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end) {
413 UniquePtr<uint8_t[]> indirect_buf;
414 if (!copy_size_and_data_from_buf(buf_ptr, end, &indirect_data_size_, &indirect_buf)) {
415 LOG_E("Malformed data found in AuthorizationSet deserialization", 0);
416 set_invalid(MALFORMED_DATA);
417 return false;
418 }
419 indirect_data_ = indirect_buf.release();
420 return true;
421 }
422
DeserializeElementsData(const uint8_t ** buf_ptr,const uint8_t * end)423 bool AuthorizationSet::DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end) {
424 uint32_t elements_count;
425 uint32_t elements_size;
426 if (!copy_uint32_from_buf(buf_ptr, end, &elements_count) ||
427 !copy_uint32_from_buf(buf_ptr, end, &elements_size)) {
428 LOG_E("Malformed data found in AuthorizationSet deserialization", 0);
429 set_invalid(MALFORMED_DATA);
430 return false;
431 }
432
433 // Note that the following validation of elements_count is weak, but it prevents allocation of
434 // elems_ arrays which are clearly too large to be reasonable.
435 if (static_cast<ptrdiff_t>(elements_size) > end - *buf_ptr ||
436 elements_count * sizeof(uint32_t) > elements_size ||
437 *buf_ptr + (elements_count * sizeof(*elems_)) < *buf_ptr) {
438 LOG_E("Malformed data found in AuthorizationSet deserialization", 0);
439 set_invalid(MALFORMED_DATA);
440 return false;
441 }
442
443 if (!reserve_elems(elements_count))
444 return false;
445
446 uint8_t* indirect_end = indirect_data_ + indirect_data_size_;
447 const uint8_t* elements_end = *buf_ptr + elements_size;
448 for (size_t i = 0; i < elements_count; ++i) {
449 if (!deserialize(elems_ + i, buf_ptr, elements_end, indirect_data_, indirect_end)) {
450 LOG_E("Malformed data found in AuthorizationSet deserialization", 0);
451 set_invalid(MALFORMED_DATA);
452 return false;
453 }
454 }
455 elems_size_ = elements_count;
456 return true;
457 }
458
Deserialize(const uint8_t ** buf_ptr,const uint8_t * end)459 bool AuthorizationSet::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
460 FreeData();
461
462 if (!DeserializeIndirectData(buf_ptr, end) || !DeserializeElementsData(buf_ptr, end))
463 return false;
464
465 if (indirect_data_size_ != ComputeIndirectDataSize(elems_, elems_size_)) {
466 LOG_E("Malformed data found in AuthorizationSet deserialization", 0);
467 set_invalid(MALFORMED_DATA);
468 return false;
469 }
470 return true;
471 }
472
Clear()473 void AuthorizationSet::Clear() {
474 memset_s(elems_, 0, elems_size_ * sizeof(keymaster_key_param_t));
475 memset_s(indirect_data_, 0, indirect_data_size_);
476 elems_size_ = 0;
477 indirect_data_size_ = 0;
478 }
479
FreeData()480 void AuthorizationSet::FreeData() {
481 Clear();
482
483 delete[] elems_;
484 delete[] indirect_data_;
485
486 elems_ = NULL;
487 indirect_data_ = NULL;
488 elems_capacity_ = 0;
489 indirect_data_capacity_ = 0;
490 error_ = OK;
491 }
492
493 /* static */
ComputeIndirectDataSize(const keymaster_key_param_t * elems,size_t count)494 size_t AuthorizationSet::ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count) {
495 size_t size = 0;
496 for (size_t i = 0; i < count; ++i) {
497 if (is_blob_tag(elems[i].tag)) {
498 size += elems[i].blob.data_length;
499 }
500 }
501 return size;
502 }
503
CopyIndirectData()504 void AuthorizationSet::CopyIndirectData() {
505 memset_s(indirect_data_, 0, indirect_data_capacity_);
506
507 uint8_t* indirect_data_pos = indirect_data_;
508 for (size_t i = 0; i < elems_size_; ++i) {
509 assert(indirect_data_pos <= indirect_data_ + indirect_data_capacity_);
510 if (is_blob_tag(elems_[i].tag)) {
511 memcpy(indirect_data_pos, elems_[i].blob.data, elems_[i].blob.data_length);
512 elems_[i].blob.data = indirect_data_pos;
513 indirect_data_pos += elems_[i].blob.data_length;
514 }
515 }
516 assert(indirect_data_pos == indirect_data_ + indirect_data_capacity_);
517 indirect_data_size_ = indirect_data_pos - indirect_data_;
518 }
519
GetTagCount(keymaster_tag_t tag) const520 size_t AuthorizationSet::GetTagCount(keymaster_tag_t tag) const {
521 size_t count = 0;
522 for (int pos = -1; (pos = find(tag, pos)) != -1;)
523 ++count;
524 return count;
525 }
526
GetTagValueEnum(keymaster_tag_t tag,uint32_t * val) const527 bool AuthorizationSet::GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const {
528 int pos = find(tag);
529 if (pos == -1) {
530 return false;
531 }
532 *val = elems_[pos].enumerated;
533 return true;
534 }
535
GetTagValueEnumRep(keymaster_tag_t tag,size_t instance,uint32_t * val) const536 bool AuthorizationSet::GetTagValueEnumRep(keymaster_tag_t tag, size_t instance,
537 uint32_t* val) const {
538 size_t count = 0;
539 int pos = -1;
540 while (count <= instance) {
541 pos = find(tag, pos);
542 if (pos == -1) {
543 return false;
544 }
545 ++count;
546 }
547 *val = elems_[pos].enumerated;
548 return true;
549 }
550
GetTagValueInt(keymaster_tag_t tag,uint32_t * val) const551 bool AuthorizationSet::GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const {
552 int pos = find(tag);
553 if (pos == -1) {
554 return false;
555 }
556 *val = elems_[pos].integer;
557 return true;
558 }
559
GetTagValueIntRep(keymaster_tag_t tag,size_t instance,uint32_t * val) const560 bool AuthorizationSet::GetTagValueIntRep(keymaster_tag_t tag, size_t instance,
561 uint32_t* val) const {
562 size_t count = 0;
563 int pos = -1;
564 while (count <= instance) {
565 pos = find(tag, pos);
566 if (pos == -1) {
567 return false;
568 }
569 ++count;
570 }
571 *val = elems_[pos].integer;
572 return true;
573 }
574
GetTagValueLong(keymaster_tag_t tag,uint64_t * val) const575 bool AuthorizationSet::GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const {
576 int pos = find(tag);
577 if (pos == -1) {
578 return false;
579 }
580 *val = elems_[pos].long_integer;
581 return true;
582 }
583
GetTagValueLongRep(keymaster_tag_t tag,size_t instance,uint64_t * val) const584 bool AuthorizationSet::GetTagValueLongRep(keymaster_tag_t tag, size_t instance,
585 uint64_t* val) const {
586 size_t count = 0;
587 int pos = -1;
588 while (count <= instance) {
589 pos = find(tag, pos);
590 if (pos == -1) {
591 return false;
592 }
593 ++count;
594 }
595 *val = elems_[pos].long_integer;
596 return true;
597 }
598
GetTagValueDate(keymaster_tag_t tag,uint64_t * val) const599 bool AuthorizationSet::GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const {
600 int pos = find(tag);
601 if (pos == -1) {
602 return false;
603 }
604 *val = elems_[pos].date_time;
605 return true;
606 }
607
GetTagValueBlob(keymaster_tag_t tag,keymaster_blob_t * val) const608 bool AuthorizationSet::GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const {
609 int pos = find(tag);
610 if (pos == -1) {
611 return false;
612 }
613 *val = elems_[pos].blob;
614 return true;
615 }
616
GetTagValueBool(keymaster_tag_t tag) const617 bool AuthorizationSet::GetTagValueBool(keymaster_tag_t tag) const {
618 int pos = find(tag);
619 if (pos == -1) {
620 return false;
621 }
622 assert(elems_[pos].boolean);
623 return elems_[pos].boolean;
624 }
625
ContainsEnumValue(keymaster_tag_t tag,uint32_t value) const626 bool AuthorizationSet::ContainsEnumValue(keymaster_tag_t tag, uint32_t value) const {
627 for (auto& entry : *this)
628 if (entry.tag == tag && entry.enumerated == value)
629 return true;
630 return false;
631 }
632
ContainsIntValue(keymaster_tag_t tag,uint32_t value) const633 bool AuthorizationSet::ContainsIntValue(keymaster_tag_t tag, uint32_t value) const {
634 for (auto& entry : *this)
635 if (entry.tag == tag && entry.integer == value)
636 return true;
637 return false;
638 }
639
640 } // namespace keymaster
641