1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "device/fido/cbor_extract.h"
6 
7 #include <type_traits>
8 
9 #include "base/callback.h"
10 #include "base/check_op.h"
11 #include "components/cbor/values.h"
12 
13 namespace device {
14 namespace cbor_extract {
15 
16 namespace {
17 
18 using internal::Type;
19 
20 static_assert(sizeof(StepOrByte<void>) == 1,
21               "things should fit into a single byte");
22 
23 const bool kTrue = true;
24 const bool kFalse = false;
25 
CBORTypeToBitfield(const cbor::Value::Type type)26 constexpr uint8_t CBORTypeToBitfield(const cbor::Value::Type type) {
27   const unsigned type_u = static_cast<unsigned>(type);
28   if (type_u >= 8) {
29     __builtin_unreachable();
30   }
31   return 1u << type_u;
32 }
33 
34 // ASSERT_TYPE_IS asserts that the type of |a| is |b|. This is used to ensure
35 // that the documented output types for elements of |Type| are correct.
36 #define ASSERT_TYPE_IS(a, b)                                                \
37   static_assert(                                                            \
38       std::is_same<decltype(&a), decltype(reinterpret_cast<b*>(0))>::value, \
39       "types need updating");
40 
41 class Extractor {
42  public:
Extractor(base::span<const void * > outputs,base::span<const StepOrByte<void>> steps)43   Extractor(base::span<const void*> outputs,
44             base::span<const StepOrByte<void>> steps)
45       : outputs_(outputs), steps_(steps) {}
46 
ValuesFromMap(const cbor::Value::MapValue & map)47   bool ValuesFromMap(const cbor::Value::MapValue& map) {
48     for (;;) {
49       // steps_[] emits a CHECK, and we don't want the code-size hit. Thus
50       // bounds are DCHECKed but then steps_.data() is dereferenced.
51       DCHECK_LT(step_i_, steps_.size());
52       const internal::Step step = steps_.data()[step_i_++].step;
53       const Type value_type = static_cast<Type>(step.value_type);
54       if (value_type == Type::kStop) {
55         return true;
56       }
57 
58       DCHECK_LT(step_i_, steps_.size());
59       const uint8_t key_or_string_indicator = steps_.data()[step_i_++].u8;
60       cbor::Value::MapValue::const_iterator map_it;
61       if (key_or_string_indicator == StepOrByte<void>::STRING_KEY) {
62         DCHECK_LT(step_i_, steps_.size());
63         std::string key(&steps_.data()[step_i_].c);
64         step_i_ += key.size() + 1;
65         map_it = map.find(cbor::Value(std::move(key)));
66       } else {
67         map_it = map.find(cbor::Value(static_cast<int64_t>(
68             static_cast<int8_t>(key_or_string_indicator))));
69       }
70 
71       const void** output = nullptr;
72       if (value_type != Type::kMap) {
73         DCHECK_LT(step.output_index, outputs_.size());
74         output = &outputs_.data()[step.output_index];
75       }
76 
77       if (map_it == map.end()) {
78         if (step.required) {
79           return false;
80         }
81         if (output) {
82           *output = nullptr;
83         }
84         continue;
85       }
86 
87       // kExpectedCBORTypes is an array of bitmaps of acceptable types for each
88       // |Type|.
89       static constexpr uint8_t kExpectedCBORTypes[] = {
90           // kBytestring
91           CBORTypeToBitfield(cbor::Value::Type::BYTE_STRING),
92           // kString
93           CBORTypeToBitfield(cbor::Value::Type::STRING),
94           // kBoolean
95           CBORTypeToBitfield(cbor::Value::Type::SIMPLE_VALUE),
96           // kInt
97           CBORTypeToBitfield(cbor::Value::Type::NEGATIVE) |
98               CBORTypeToBitfield(cbor::Value::Type::UNSIGNED),
99           // kMap
100           CBORTypeToBitfield(cbor::Value::Type::MAP),
101           // kArray
102           CBORTypeToBitfield(cbor::Value::Type::ARRAY),
103           // kValue
104           0xff,
105       };
106 
107       const cbor::Value& value = map_it->second;
108       const unsigned cbor_type_u = static_cast<unsigned>(value.type());
109       const unsigned value_type_u = static_cast<unsigned>(value_type);
110       DCHECK(value_type_u < base::size(kExpectedCBORTypes));
111       if (cbor_type_u >= 8 ||
112           (kExpectedCBORTypes[value_type_u] & (1u << cbor_type_u)) == 0) {
113         return false;
114       }
115 
116       switch (value_type) {
117         case Type::kBytestring:
118           ASSERT_TYPE_IS(value.GetBytestring(), const std::vector<uint8_t>);
119           *output = &value.GetBytestring();
120           break;
121         case Type::kString:
122           ASSERT_TYPE_IS(value.GetString(), const std::string);
123           *output = &value.GetString();
124           break;
125         case Type::kBoolean:
126           switch (value.GetSimpleValue()) {
127             case cbor::Value::SimpleValue::TRUE_VALUE:
128               *output = &kTrue;
129               break;
130             case cbor::Value::SimpleValue::FALSE_VALUE:
131               *output = &kFalse;
132               break;
133             default:
134               return false;
135           }
136           break;
137         case Type::kInt:
138           ASSERT_TYPE_IS(value.GetInteger(), const int64_t);
139           *output = &value.GetInteger();
140           break;
141         case Type::kMap:
142           if (!ValuesFromMap(value.GetMap())) {
143             return false;
144           }
145           break;
146         case Type::kArray:
147           ASSERT_TYPE_IS(value.GetArray(), const std::vector<cbor::Value>);
148           *output = &value.GetArray();
149           break;
150         case Type::kValue:
151           *output = &value;
152           break;
153         case Type::kStop:
154           return false;
155       }
156     }
157 
158     return true;
159   }
160 
161  private:
162   base::span<const void*> outputs_;
163   base::span<const StepOrByte<void>> steps_;
164   size_t step_i_ = 0;
165 };
166 
167 }  // namespace
168 
169 namespace internal {
170 
Extract(base::span<const void * > outputs,base::span<const StepOrByte<void>> steps,const cbor::Value::MapValue & map)171 bool Extract(base::span<const void*> outputs,
172              base::span<const StepOrByte<void>> steps,
173              const cbor::Value::MapValue& map) {
174   DCHECK(steps[steps.size() - 1].step.value_type ==
175          static_cast<uint8_t>(Type::kStop));
176   Extractor extractor(outputs, steps);
177   return extractor.ValuesFromMap(map);
178 }
179 
180 }  // namespace internal
181 
ForEachPublicKeyEntry(const cbor::Value::ArrayValue & array,const cbor::Value & key,base::RepeatingCallback<bool (const cbor::Value &)> callback)182 bool ForEachPublicKeyEntry(
183     const cbor::Value::ArrayValue& array,
184     const cbor::Value& key,
185     base::RepeatingCallback<bool(const cbor::Value&)> callback) {
186   const cbor::Value type_key("type");
187   const std::string public_key("public-key");
188 
189   for (const cbor::Value& value : array) {
190     if (!value.is_map()) {
191       return false;
192     }
193     const cbor::Value::MapValue& map = value.GetMap();
194     const auto type_it = map.find(type_key);
195     if (type_it == map.end() || !type_it->second.is_string()) {
196       return false;
197     }
198     if (type_it->second.GetString() != public_key) {
199       continue;
200     }
201 
202     const auto value_it = map.find(key);
203     if (value_it == map.end() || !callback.Run(value_it->second)) {
204       return false;
205     }
206   }
207 
208   return true;
209 }
210 
211 }  // namespace cbor_extract
212 }  // namespace device
213