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