OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stddef.h> | 5 #include <stddef.h> |
6 | 6 |
7 #include <iterator> | 7 #include <iterator> |
| 8 #include <map> |
| 9 #include <set> |
8 | 10 |
| 11 #include "base/barrier_closure.h" |
| 12 #include "base/bind.h" |
| 13 #include "base/callback.h" |
9 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "components/device_event_log/device_event_log.h" |
| 16 #include "device/usb/usb_device_handle.h" |
10 #include "device/usb/webusb_descriptors.h" | 17 #include "device/usb/webusb_descriptors.h" |
| 18 #include "net/base/io_buffer.h" |
| 19 |
| 20 using net::IOBufferWithSize; |
11 | 21 |
12 namespace device { | 22 namespace device { |
13 | 23 |
14 namespace { | 24 namespace { |
15 | 25 |
16 // These constants are defined by the Universal Serial Device 3.0 Specification | 26 // These constants are defined by the Universal Serial Device 3.0 Specification |
17 // Revision 1.0. | 27 // Revision 1.0. |
| 28 const uint8_t kGetDescriptorRequest = 0x06; |
| 29 |
18 const uint8_t kBosDescriptorType = 0x0F; | 30 const uint8_t kBosDescriptorType = 0x0F; |
19 const uint8_t kDeviceCapabilityDescriptorType = 0x10; | 31 const uint8_t kDeviceCapabilityDescriptorType = 0x10; |
20 | 32 |
21 const uint8_t kPlatformDevCapabilityType = 0x05; | 33 const uint8_t kPlatformDevCapabilityType = 0x05; |
22 | 34 |
23 // These constants are defined by the WebUSB specification: | 35 // These constants are defined by the WebUSB specification: |
24 // http://wicg.github.io/webusb/ | 36 // http://wicg.github.io/webusb/ |
| 37 const uint8_t kGetAllowedOriginsRequest = 0x01; |
| 38 const uint8_t kGetUrlRequest = 0x02; |
| 39 |
25 const uint8_t kWebUsbCapabilityUUID[16] = { | 40 const uint8_t kWebUsbCapabilityUUID[16] = { |
26 // Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}. | 41 // Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}. |
27 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, | 42 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, |
28 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}; | 43 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}; |
29 | 44 |
30 const uint8_t kDescriptorSetDescriptorType = 0x00; | 45 const int kControlTransferTimeout = 60000; // 1 minute |
31 const uint8_t kConfigurationSubsetDescriptorType = 0x01; | 46 |
32 const uint8_t kFunctionSubsetDescriptorType = 0x02; | 47 using ReadWebUsbDescriptorsCallback = |
33 const uint8_t kUrlDescriptorType = 0x03; | 48 base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins, |
34 | 49 const GURL& landing_page)>; |
35 bool ParseUrl(GURL* url, | 50 |
36 std::vector<uint8_t>::const_iterator* it, | 51 using ReadWebUsbAllowedOriginsCallback = |
37 std::vector<uint8_t>::const_iterator end) { | 52 base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins)>; |
38 // These conditions must be guaranteed by the caller. | 53 |
39 DCHECK(*it != end); | 54 // Parses a WebUSB Function Subset Header: |
40 uint8_t length = (*it)[0]; | 55 // http://wicg.github.io/webusb/#dfn-function-subset-header |
41 DCHECK_LE(length, std::distance(*it, end)); | 56 // |
42 DCHECK_GE(length, 2); | 57 // 0 1 2 3 |
43 DCHECK_EQ((*it)[1], kUrlDescriptorType); | 58 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
44 | 59 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
45 if (length == 2) { | 60 // | length | type | 1st interface | origin[0] | |
46 return false; | 61 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
47 } | 62 // | origin[1] | ... |
48 | 63 // +-+-+-+-+-+-+-+-+-+-+-+------ |
49 const char* str = reinterpret_cast<const char*>(&(*it)[2]); | |
50 *url = GURL(std::string(str, length - 2)); | |
51 if (!url->is_valid()) { | |
52 return false; | |
53 } | |
54 | |
55 std::advance(*it, length); | |
56 return true; | |
57 } | |
58 | |
59 bool ParseFunction(WebUsbFunctionSubset* function, | 64 bool ParseFunction(WebUsbFunctionSubset* function, |
60 std::vector<uint8_t>::const_iterator* it, | 65 std::vector<uint8_t>::const_iterator* it, |
61 std::vector<uint8_t>::const_iterator end) { | 66 std::vector<uint8_t>::const_iterator end) { |
62 // These conditions must be guaranteed by the caller. | 67 const uint8_t kDescriptorType = 0x02; |
63 DCHECK(*it != end); | 68 const uint8_t kDescriptorMinLength = 3; |
| 69 |
| 70 // If this isn't the end of the buffer then there must be at least one byte |
| 71 // available, the length of the descriptor. |
| 72 if (*it == end) |
| 73 return false; |
64 uint8_t length = (*it)[0]; | 74 uint8_t length = (*it)[0]; |
65 DCHECK_LE(length, std::distance(*it, end)); | 75 |
66 DCHECK_GE(length, 2); | 76 // Is this a valid Function Subset Header? It must be long enough, fit within |
67 DCHECK_EQ((*it)[1], kFunctionSubsetDescriptorType); | 77 // the buffer and have the right descriptor type. |
68 | 78 if (length < kDescriptorMinLength || std::distance(*it, end) < length || |
69 if (length != 5) { | 79 (*it)[1] != kDescriptorType) { |
70 return false; | 80 return false; |
71 } | 81 } |
72 | 82 |
73 function->first_interface = (*it)[2]; | 83 function->first_interface = (*it)[2]; |
74 | 84 |
75 // Validate the Function Subset header. | 85 // Everything after the mandatory fields are origin indicies. |
76 uint16_t total_length = (*it)[3] + ((*it)[4] << 8); | 86 std::advance(*it, kDescriptorMinLength); |
77 if (length > total_length || total_length > std::distance(*it, end)) { | 87 uint8_t num_origins = length - kDescriptorMinLength; |
78 return false; | 88 function->origin_ids.reserve(num_origins); |
79 } | 89 for (size_t i = 0; i < num_origins; ++i) { |
80 | 90 uint8_t index = *(*it)++; |
81 end = *it + total_length; | 91 if (index == 0) |
82 std::advance(*it, length); | |
83 | |
84 while (*it != end) { | |
85 uint8_t length = (*it)[0]; | |
86 if (length < 2 || std::distance(*it, end) < length) { | |
87 return false; | 92 return false; |
88 } | 93 function->origin_ids.push_back(index); |
89 | |
90 uint8_t type = (*it)[1]; | |
91 if (type == kUrlDescriptorType) { | |
92 GURL url; | |
93 if (!ParseUrl(&url, it, end)) { | |
94 return false; | |
95 } | |
96 function->origins.push_back(url.GetOrigin()); | |
97 } else { | |
98 return false; | |
99 } | |
100 } | 94 } |
101 | 95 |
102 return true; | 96 return true; |
103 } | 97 } |
104 | 98 |
| 99 // Parses a WebUSB Configuration Subset Header: |
| 100 // http://wicg.github.io/webusb/#dfn-configuration-subset-header |
| 101 // |
| 102 // 0 1 2 3 |
| 103 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| 104 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 105 // | length | type | config value | num functions | |
| 106 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 107 // | origin[0] | origin[1] | ... |
| 108 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------ |
105 bool ParseConfiguration(WebUsbConfigurationSubset* configuration, | 109 bool ParseConfiguration(WebUsbConfigurationSubset* configuration, |
106 std::vector<uint8_t>::const_iterator* it, | 110 std::vector<uint8_t>::const_iterator* it, |
107 std::vector<uint8_t>::const_iterator end) { | 111 std::vector<uint8_t>::const_iterator end) { |
108 // These conditions must be guaranteed by the caller. | 112 const uint8_t kDescriptorType = 0x01; |
109 DCHECK(*it != end); | 113 const uint8_t kDescriptorMinLength = 4; |
| 114 |
| 115 // If this isn't the end of the buffer then there must be at least one byte |
| 116 // available, the length of the descriptor. |
| 117 if (*it == end) |
| 118 return false; |
110 uint8_t length = (*it)[0]; | 119 uint8_t length = (*it)[0]; |
111 DCHECK_LE(length, std::distance(*it, end)); | 120 |
112 DCHECK_GE(length, 2); | 121 // Is this a valid Configuration Subset Header? It must be long enough, fit |
113 DCHECK_EQ((*it)[1], kConfigurationSubsetDescriptorType); | 122 // within the buffer and have the right descriptor type. |
114 | 123 if (length < kDescriptorMinLength || std::distance(*it, end) < length || |
115 if (length != 5) { | 124 (*it)[1] != kDescriptorType) { |
116 return false; | 125 return false; |
117 } | 126 } |
118 | 127 |
119 configuration->configuration_value = (*it)[2]; | 128 configuration->configuration_value = (*it)[2]; |
120 | 129 uint8_t num_functions = (*it)[3]; |
121 // Validate the Configuration Subset header. | 130 |
122 uint16_t total_length = (*it)[3] + ((*it)[4] << 8); | 131 // The next |length - 4| bytes after the mandatory fields are origin indicies. |
123 if (length > total_length || total_length > std::distance(*it, end)) { | 132 std::advance(*it, kDescriptorMinLength); |
124 return false; | 133 uint8_t num_origins = length - kDescriptorMinLength; |
125 } | 134 configuration->origin_ids.reserve(num_origins); |
126 | 135 for (size_t i = 0; i < num_origins; ++i) { |
127 end = *it + total_length; | 136 uint8_t index = *(*it)++; |
128 std::advance(*it, length); | 137 if (index == 0) |
129 | |
130 while (*it != end) { | |
131 uint8_t length = (*it)[0]; | |
132 if (length < 2 || std::distance(*it, end) < length) { | |
133 return false; | 138 return false; |
| 139 configuration->origin_ids.push_back(index); |
| 140 } |
| 141 |
| 142 // |num_functions| function descriptors then follow the descriptor. |
| 143 for (size_t i = 0; i < num_functions; ++i) { |
| 144 WebUsbFunctionSubset function; |
| 145 if (!ParseFunction(&function, it, end)) |
| 146 return false; |
| 147 configuration->functions.push_back(function); |
| 148 } |
| 149 |
| 150 return true; |
| 151 } |
| 152 |
| 153 void OnDoneReadingUrls(scoped_ptr<WebUsbAllowedOrigins> allowed_origins, |
| 154 uint8_t landing_page_id, |
| 155 scoped_ptr<std::map<uint8_t, GURL>> url_map, |
| 156 const ReadWebUsbDescriptorsCallback& callback) { |
| 157 for (uint8_t origin_id : allowed_origins->origin_ids) { |
| 158 const auto& it = url_map->find(origin_id); |
| 159 if (it != url_map->end()) |
| 160 allowed_origins->origins.push_back(it->second.GetOrigin()); |
| 161 } |
| 162 |
| 163 for (auto& configuration : allowed_origins->configurations) { |
| 164 for (uint8_t origin_id : configuration.origin_ids) { |
| 165 const auto& it = url_map->find(origin_id); |
| 166 if (it != url_map->end()) |
| 167 configuration.origins.push_back(it->second.GetOrigin()); |
134 } | 168 } |
135 | 169 |
136 uint8_t type = (*it)[1]; | 170 for (auto& function : configuration.functions) { |
137 if (type == kFunctionSubsetDescriptorType) { | 171 for (uint8_t origin_id : function.origin_ids) { |
138 WebUsbFunctionSubset function; | 172 const auto& it = url_map->find(origin_id); |
139 if (!ParseFunction(&function, it, end)) { | 173 if (it != url_map->end()) |
140 return false; | 174 function.origins.push_back(it->second.GetOrigin()); |
141 } | 175 } |
142 configuration->functions.push_back(function); | |
143 } else if (type == kUrlDescriptorType) { | |
144 GURL url; | |
145 if (!ParseUrl(&url, it, end)) { | |
146 return false; | |
147 } | |
148 configuration->origins.push_back(url.GetOrigin()); | |
149 } else { | |
150 return false; | |
151 } | 176 } |
152 } | 177 } |
153 | 178 |
154 return true; | 179 GURL landing_page; |
| 180 if (landing_page_id != 0) |
| 181 landing_page = (*url_map)[landing_page_id]; |
| 182 |
| 183 callback.Run(std::move(allowed_origins), landing_page); |
| 184 } |
| 185 |
| 186 void OnReadUrlDescriptor(std::map<uint8_t, GURL>* url_map, |
| 187 uint8_t index, |
| 188 const base::Closure& callback, |
| 189 UsbTransferStatus status, |
| 190 scoped_refptr<net::IOBuffer> buffer, |
| 191 size_t length) { |
| 192 if (status != USB_TRANSFER_COMPLETED) { |
| 193 USB_LOG(EVENT) << "Failed to read WebUSB URL descriptor: " << index; |
| 194 callback.Run(); |
| 195 return; |
| 196 } |
| 197 |
| 198 GURL url; |
| 199 if (ParseWebUsbUrlDescriptor( |
| 200 std::vector<uint8_t>(buffer->data(), buffer->data() + length), |
| 201 &url)) { |
| 202 (*url_map)[index] = url; |
| 203 } |
| 204 callback.Run(); |
| 205 } |
| 206 |
| 207 // Reads the descriptor with |index| from the device, adds the value to |
| 208 // |url_map| and then runs |callback|. |
| 209 void ReadUrlDescriptor(scoped_refptr<UsbDeviceHandle> device_handle, |
| 210 uint8_t vendor_code, |
| 211 std::map<uint8_t, GURL>* url_map, |
| 212 uint8_t index, |
| 213 const base::Closure& callback) { |
| 214 scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(255); |
| 215 device_handle->ControlTransfer( |
| 216 USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
| 217 vendor_code, index, kGetUrlRequest, buffer, buffer->size(), |
| 218 kControlTransferTimeout, |
| 219 base::Bind(&OnReadUrlDescriptor, url_map, index, callback)); |
| 220 } |
| 221 |
| 222 // Reads URL descriptors from the device so that it can fill |allowed_origins| |
| 223 // with the GURLs matching the indicies already collected. |
| 224 void ReadUrlDescriptors(scoped_refptr<UsbDeviceHandle> device_handle, |
| 225 uint8_t vendor_code, |
| 226 uint8_t landing_page_id, |
| 227 const ReadWebUsbDescriptorsCallback& callback, |
| 228 scoped_ptr<WebUsbAllowedOrigins> allowed_origins) { |
| 229 if (!allowed_origins) { |
| 230 callback.Run(nullptr, GURL()); |
| 231 return; |
| 232 } |
| 233 |
| 234 std::set<uint8_t> to_request; |
| 235 if (landing_page_id != 0) |
| 236 to_request.insert(landing_page_id); |
| 237 |
| 238 to_request.insert(allowed_origins->origin_ids.begin(), |
| 239 allowed_origins->origin_ids.end()); |
| 240 for (auto& config : allowed_origins->configurations) { |
| 241 to_request.insert(config.origin_ids.begin(), config.origin_ids.end()); |
| 242 for (auto& function : config.functions) { |
| 243 to_request.insert(function.origin_ids.begin(), function.origin_ids.end()); |
| 244 } |
| 245 } |
| 246 |
| 247 scoped_ptr<std::map<uint8_t, GURL>> url_map(new std::map<uint8_t, GURL>()); |
| 248 std::map<uint8_t, GURL>* url_map_ptr = url_map.get(); |
| 249 base::Closure barrier = base::BarrierClosure( |
| 250 static_cast<int>(to_request.size()), |
| 251 base::Bind(&OnDoneReadingUrls, base::Passed(&allowed_origins), |
| 252 landing_page_id, base::Passed(&url_map), callback)); |
| 253 |
| 254 for (uint8_t index : to_request) { |
| 255 ReadUrlDescriptor(device_handle, vendor_code, url_map_ptr, index, barrier); |
| 256 } |
| 257 } |
| 258 |
| 259 void OnReadWebUsbAllowedOrigins( |
| 260 const ReadWebUsbAllowedOriginsCallback& callback, |
| 261 UsbTransferStatus status, |
| 262 scoped_refptr<net::IOBuffer> buffer, |
| 263 size_t length) { |
| 264 if (status != USB_TRANSFER_COMPLETED) { |
| 265 USB_LOG(EVENT) << "Failed to read WebUSB allowed origins."; |
| 266 callback.Run(nullptr); |
| 267 return; |
| 268 } |
| 269 |
| 270 scoped_ptr<WebUsbAllowedOrigins> allowed_origins(new WebUsbAllowedOrigins()); |
| 271 if (allowed_origins->Parse( |
| 272 std::vector<uint8_t>(buffer->data(), buffer->data() + length))) { |
| 273 callback.Run(std::move(allowed_origins)); |
| 274 } else { |
| 275 callback.Run(nullptr); |
| 276 } |
| 277 } |
| 278 |
| 279 void OnReadWebUsbAllowedOriginsHeader( |
| 280 scoped_refptr<UsbDeviceHandle> device_handle, |
| 281 const ReadWebUsbAllowedOriginsCallback& callback, |
| 282 uint8_t vendor_code, |
| 283 UsbTransferStatus status, |
| 284 scoped_refptr<net::IOBuffer> buffer, |
| 285 size_t length) { |
| 286 if (status != USB_TRANSFER_COMPLETED || length != 4) { |
| 287 USB_LOG(EVENT) << "Failed to read WebUSB allowed origins header."; |
| 288 callback.Run(nullptr); |
| 289 return; |
| 290 } |
| 291 |
| 292 uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8); |
| 293 scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length); |
| 294 device_handle->ControlTransfer( |
| 295 USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
| 296 vendor_code, 0, kGetAllowedOriginsRequest, new_buffer, new_buffer->size(), |
| 297 kControlTransferTimeout, |
| 298 base::Bind(&OnReadWebUsbAllowedOrigins, callback)); |
| 299 } |
| 300 |
| 301 void ReadWebUsbAllowedOrigins( |
| 302 scoped_refptr<UsbDeviceHandle> device_handle, |
| 303 uint8_t vendor_code, |
| 304 const ReadWebUsbAllowedOriginsCallback& callback) { |
| 305 scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(4); |
| 306 device_handle->ControlTransfer( |
| 307 USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
| 308 vendor_code, 0, kGetAllowedOriginsRequest, buffer, buffer->size(), |
| 309 kControlTransferTimeout, |
| 310 base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback, |
| 311 vendor_code)); |
| 312 } |
| 313 |
| 314 void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle, |
| 315 const ReadWebUsbDescriptorsCallback& callback, |
| 316 UsbTransferStatus status, |
| 317 scoped_refptr<net::IOBuffer> buffer, |
| 318 size_t length) { |
| 319 if (status != USB_TRANSFER_COMPLETED) { |
| 320 USB_LOG(EVENT) << "Failed to read BOS descriptor."; |
| 321 callback.Run(nullptr, GURL()); |
| 322 return; |
| 323 } |
| 324 |
| 325 WebUsbPlatformCapabilityDescriptor descriptor; |
| 326 if (!descriptor.ParseFromBosDescriptor( |
| 327 std::vector<uint8_t>(buffer->data(), buffer->data() + length))) { |
| 328 callback.Run(nullptr, GURL()); |
| 329 return; |
| 330 } |
| 331 |
| 332 ReadWebUsbAllowedOrigins( |
| 333 device_handle, descriptor.vendor_code, |
| 334 base::Bind(&ReadUrlDescriptors, device_handle, descriptor.vendor_code, |
| 335 descriptor.landing_page_id, callback)); |
| 336 } |
| 337 |
| 338 void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle, |
| 339 const ReadWebUsbDescriptorsCallback& callback, |
| 340 UsbTransferStatus status, |
| 341 scoped_refptr<net::IOBuffer> buffer, |
| 342 size_t length) { |
| 343 if (status != USB_TRANSFER_COMPLETED || length != 5) { |
| 344 USB_LOG(EVENT) << "Failed to read BOS descriptor header."; |
| 345 callback.Run(nullptr, GURL()); |
| 346 return; |
| 347 } |
| 348 |
| 349 uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8); |
| 350 scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length); |
| 351 device_handle->ControlTransfer( |
| 352 USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE, |
| 353 kGetDescriptorRequest, kBosDescriptorType << 8, 0, new_buffer, |
| 354 new_buffer->size(), kControlTransferTimeout, |
| 355 base::Bind(&OnReadBosDescriptor, device_handle, callback)); |
155 } | 356 } |
156 | 357 |
157 } // namespace | 358 } // namespace |
158 | 359 |
159 WebUsbFunctionSubset::WebUsbFunctionSubset() : first_interface(0) {} | 360 WebUsbFunctionSubset::WebUsbFunctionSubset() : first_interface(0) {} |
160 | 361 |
161 WebUsbFunctionSubset::~WebUsbFunctionSubset() {} | 362 WebUsbFunctionSubset::~WebUsbFunctionSubset() {} |
162 | 363 |
163 WebUsbConfigurationSubset::WebUsbConfigurationSubset() | 364 WebUsbConfigurationSubset::WebUsbConfigurationSubset() |
164 : configuration_value(0) {} | 365 : configuration_value(0) {} |
165 | 366 |
166 WebUsbConfigurationSubset::~WebUsbConfigurationSubset() {} | 367 WebUsbConfigurationSubset::~WebUsbConfigurationSubset() {} |
167 | 368 |
168 WebUsbDescriptorSet::WebUsbDescriptorSet() {} | 369 WebUsbAllowedOrigins::WebUsbAllowedOrigins() {} |
169 | 370 |
170 WebUsbDescriptorSet::~WebUsbDescriptorSet() {} | 371 WebUsbAllowedOrigins::~WebUsbAllowedOrigins() {} |
171 | 372 |
172 bool WebUsbDescriptorSet::Parse(const std::vector<uint8_t>& bytes) { | 373 // Parses a WebUSB Allowed Origins Header: |
173 if (bytes.size() < 4) { | 374 // http://wicg.github.io/webusb/#dfn-allowed-origins-header |
| 375 // |
| 376 // 0 1 2 3 |
| 377 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| 378 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 379 // | length | type | total length | |
| 380 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 381 // | num configs | origin[0] | origin[1] | ... |
| 382 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------ |
| 383 bool WebUsbAllowedOrigins::Parse(const std::vector<uint8_t>& bytes) { |
| 384 const uint8_t kDescriptorType = 0x00; |
| 385 const uint8_t kDescriptorMinLength = 5; |
| 386 |
| 387 // The buffer must be at least the length of this descriptor's mandatory |
| 388 // fields. |
| 389 if (bytes.size() < kDescriptorMinLength) |
174 return false; | 390 return false; |
175 } | |
176 | 391 |
177 // Validate the descriptor set header. | 392 // Validate that the length of this descriptor and the total length of the |
| 393 // entire block of descriptors is consistent with the length of the buffer. |
| 394 uint8_t length = bytes[0]; |
178 uint16_t total_length = bytes[2] + (bytes[3] << 8); | 395 uint16_t total_length = bytes[2] + (bytes[3] << 8); |
179 if (bytes[0] != 4 || // bLength | 396 if (length < 5 || length > bytes.size() || // bLength |
180 bytes[1] != kDescriptorSetDescriptorType || // bDescriptorType | 397 bytes[1] != kDescriptorType || // bDescriptorType |
181 4 > total_length || total_length > bytes.size()) { // wTotalLength | 398 total_length < length || total_length > bytes.size()) { // wTotalLength |
182 return false; | 399 return false; |
183 } | 400 } |
184 | 401 |
185 std::vector<uint8_t>::const_iterator it = bytes.begin(); | 402 std::vector<uint8_t>::const_iterator it = bytes.begin(); |
186 std::vector<uint8_t>::const_iterator end = it + total_length; | 403 uint8_t num_configurations = bytes[4]; |
187 std::advance(it, 4); | |
188 | 404 |
189 while (it != bytes.end()) { | 405 // The next |length - 5| bytes after the mandatory fields are origin indicies. |
190 uint8_t length = it[0]; | 406 std::advance(it, kDescriptorMinLength); |
191 if (length < 2 || std::distance(it, end) < length) { | 407 uint8_t num_origins = length - kDescriptorMinLength; |
| 408 origin_ids.reserve(num_origins); |
| 409 for (size_t i = 0; i < num_origins; ++i) { |
| 410 uint8_t index = *it++; |
| 411 if (index == 0) |
192 return false; | 412 return false; |
193 } | 413 origin_ids.push_back(index); |
| 414 } |
194 | 415 |
195 uint8_t type = it[1]; | 416 // |num_configurations| configuration descriptors then follow the descriptor. |
196 if (type == kConfigurationSubsetDescriptorType) { | 417 for (size_t i = 0; i < num_configurations; ++i) { |
197 WebUsbConfigurationSubset configuration; | 418 WebUsbConfigurationSubset configuration; |
198 if (!ParseConfiguration(&configuration, &it, end)) { | 419 if (!ParseConfiguration(&configuration, &it, bytes.end())) |
199 return false; | |
200 } | |
201 configurations.push_back(configuration); | |
202 } else if (type == kUrlDescriptorType) { | |
203 GURL url; | |
204 if (!ParseUrl(&url, &it, end)) { | |
205 return false; | |
206 } | |
207 origins.push_back(url.GetOrigin()); | |
208 } else { | |
209 return false; | 420 return false; |
210 } | 421 configurations.push_back(configuration); |
211 } | 422 } |
212 | 423 |
213 return true; | 424 return true; |
214 } | 425 } |
215 | 426 |
216 WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor() | 427 WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor() |
217 : version(0), vendor_code(0) {} | 428 : version(0), vendor_code(0) {} |
218 | 429 |
219 WebUsbPlatformCapabilityDescriptor::~WebUsbPlatformCapabilityDescriptor() {} | 430 WebUsbPlatformCapabilityDescriptor::~WebUsbPlatformCapabilityDescriptor() {} |
220 | 431 |
(...skipping 12 matching lines...) Expand all Loading... |
233 5 > total_length || total_length > bytes.size()) { // wTotalLength | 444 5 > total_length || total_length > bytes.size()) { // wTotalLength |
234 return false; | 445 return false; |
235 } | 446 } |
236 | 447 |
237 uint8_t num_device_caps = bytes[4]; | 448 uint8_t num_device_caps = bytes[4]; |
238 std::vector<uint8_t>::const_iterator it = bytes.begin(); | 449 std::vector<uint8_t>::const_iterator it = bytes.begin(); |
239 std::vector<uint8_t>::const_iterator end = it + total_length; | 450 std::vector<uint8_t>::const_iterator end = it + total_length; |
240 std::advance(it, 5); | 451 std::advance(it, 5); |
241 | 452 |
242 uint8_t length = 0; | 453 uint8_t length = 0; |
243 bool found_vendor_code = false; | |
244 for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) { | 454 for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) { |
245 if (it == end) { | 455 if (it == end) { |
246 return false; | 456 return false; |
247 } | 457 } |
248 | 458 |
249 // Validate the Device Capability descriptor, defined in Table 9-13 of the | 459 // Validate the Device Capability descriptor, defined in Table 9-13 of the |
250 // Universal Serial Bus 3.1 Specification, Revision 1.0. | 460 // Universal Serial Bus 3.1 Specification, Revision 1.0. |
251 length = it[0]; | 461 length = it[0]; |
252 if (length < 3 || std::distance(it, end) < length || // bLength | 462 if (length < 3 || std::distance(it, end) < length || // bLength |
253 it[1] != kDeviceCapabilityDescriptorType) { // bDescriptorType | 463 it[1] != kDeviceCapabilityDescriptorType) { // bDescriptorType |
254 return false; | 464 return false; |
255 } | 465 } |
256 | 466 |
257 if (it[2] != kPlatformDevCapabilityType) { // bDevCapabilityType | 467 if (it[2] != kPlatformDevCapabilityType) { // bDevCapabilityType |
258 continue; | 468 continue; |
259 } | 469 } |
260 | 470 |
261 // Validate the Platform Capability Descriptor, defined in Table 9-18 of the | 471 // Validate the Platform Capability Descriptor, defined in Table 9-18 of the |
262 // Universal Serial Bus 3.1 Specification, Revision 1.0. | 472 // Universal Serial Bus 3.1 Specification, Revision 1.0. |
263 if (length < 20) { | 473 if (length < 20) { |
264 // Platform capability descriptors must be at least 20 bytes. | 474 // Platform capability descriptors must be at least 20 bytes. |
265 return false; | 475 return false; |
266 } | 476 } |
267 | 477 |
268 if (memcmp(&it[4], kWebUsbCapabilityUUID, sizeof(kWebUsbCapabilityUUID)) != | 478 if (memcmp(&it[4], kWebUsbCapabilityUUID, sizeof(kWebUsbCapabilityUUID)) != |
269 0) { // PlatformCapabilityUUID | 479 0) { // PlatformCapabilityUUID |
270 continue; | 480 continue; |
271 } | 481 } |
272 | 482 |
273 if (length < 23) { | 483 if (length < 22) { |
274 // The WebUSB capability descriptor must be at least 23 bytes (to allow | 484 // The WebUSB capability descriptor must be at least 22 bytes (to allow |
275 // for future versions). | 485 // for future versions). |
276 return false; | 486 return false; |
277 } | 487 } |
278 | 488 |
279 version = it[20] + (it[21] << 8); // bcdVersion | 489 version = it[20] + (it[21] << 8); // bcdVersion |
280 if (version < 0x0100) { | 490 if (version < 0x0100) { |
281 continue; | 491 continue; |
282 } | 492 } |
283 | 493 |
284 // Version 1.0 only defines a single field, bVendorCode. | 494 // Version 1.0 defines two fields for a total length of 24 bytes. |
| 495 if (length != 24) { |
| 496 return false; |
| 497 } |
| 498 |
285 vendor_code = it[22]; | 499 vendor_code = it[22]; |
286 found_vendor_code = true; | 500 landing_page_id = it[23]; |
| 501 return true; |
287 } | 502 } |
288 | 503 |
289 return found_vendor_code; | 504 return false; |
290 } | 505 } |
291 | 506 |
| 507 // Parses a WebUSB URL Descriptor: |
| 508 // http://wicg.github.io/webusb/#dfn-url-descriptor |
| 509 // |
| 510 // 0 1 2 3 |
| 511 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| 512 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 513 // | length | type | prefix | data[0] | |
| 514 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 515 // | data[1] | ... |
| 516 // +-+-+-+-+-+-+-+-+-+-+-+------ |
292 bool ParseWebUsbUrlDescriptor(const std::vector<uint8_t>& bytes, GURL* output) { | 517 bool ParseWebUsbUrlDescriptor(const std::vector<uint8_t>& bytes, GURL* output) { |
293 if (bytes.size() < 2) { | 518 const uint8_t kDescriptorType = 0x03; |
| 519 const uint8_t kDescriptorMinLength = 3; |
| 520 |
| 521 if (bytes.size() < kDescriptorMinLength) { |
294 return false; | 522 return false; |
295 } | 523 } |
| 524 |
| 525 // Validate that the length is consistent and fits within the buffer. |
296 uint8_t length = bytes[0]; | 526 uint8_t length = bytes[0]; |
297 if (length != bytes.size() || bytes[1] != kUrlDescriptorType) { | 527 if (length < kDescriptorMinLength || length < bytes.size() || |
| 528 bytes[1] != kDescriptorType) { |
298 return false; | 529 return false; |
299 } | 530 } |
300 std::vector<uint8_t>::const_iterator it = bytes.begin(); | 531 |
301 return ParseUrl(output, &it, bytes.end()); | 532 // Look up the URL prefix and append the rest of the data in the descriptor. |
| 533 std::string url; |
| 534 switch (bytes[2]) { |
| 535 case 0: |
| 536 url.append("http://"); |
| 537 break; |
| 538 case 1: |
| 539 url.append("https://"); |
| 540 break; |
| 541 default: |
| 542 return false; |
| 543 } |
| 544 url.append(reinterpret_cast<const char*>(bytes.data() + 3), length - 3); |
| 545 |
| 546 *output = GURL(url); |
| 547 if (!output->is_valid()) { |
| 548 return false; |
| 549 } |
| 550 |
| 551 return true; |
| 552 } |
| 553 |
| 554 void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle, |
| 555 const ReadWebUsbDescriptorsCallback& callback) { |
| 556 scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(5); |
| 557 device_handle->ControlTransfer( |
| 558 USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE, |
| 559 kGetDescriptorRequest, kBosDescriptorType << 8, 0, buffer, buffer->size(), |
| 560 kControlTransferTimeout, |
| 561 base::Bind(&OnReadBosDescriptorHeader, device_handle, callback)); |
302 } | 562 } |
303 | 563 |
304 } // namespace device | 564 } // namespace device |
OLD | NEW |