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, &param->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, &param->enumerated);
351     case KM_UINT:
352     case KM_UINT_REP:
353         return copy_uint32_from_buf(buf_ptr, end, &param->integer);
354     case KM_ULONG:
355     case KM_ULONG_REP:
356         return copy_uint64_from_buf(buf_ptr, end, &param->long_integer);
357     case KM_DATE:
358         return copy_uint64_from_buf(buf_ptr, end, &param->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, &param->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