| 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 |