Chromium Code Reviews| Index: device/usb/webusb_descriptors.cc |
| diff --git a/device/usb/webusb_descriptors.cc b/device/usb/webusb_descriptors.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..60729fa0cf6f60b7338fce42d962f3e5391ad973 |
| --- /dev/null |
| +++ b/device/usb/webusb_descriptors.cc |
| @@ -0,0 +1,277 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <iterator> |
| + |
| +#include "base/logging.h" |
| +#include "device/usb/webusb_descriptors.h" |
| + |
| +namespace device { |
| + |
| +namespace { |
| + |
| +// These constants are defined by the Universal Serial Device 3.0 Specification |
| +// Revision 1.0. |
| +const uint8_t kBosDescriptorType = 0x0F; |
| +const uint8_t kDeviceCapabilityDescriptorType = 0x10; |
| + |
| +const uint8_t kPlatformDevCapabilityType = 0x05; |
| + |
| +// These constants are defined by the WebUSB specification: |
| +// http://reillyeon.github.io/webusb/ |
| +const uint8_t kWebUsbCapabilityUUID[16] = { |
| + // Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}. |
| + 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, |
| + 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}; |
| + |
| +const uint8_t kDescriptorSetDescriptorType = 0x00; |
| +const uint8_t kConfigurationSubsetDescriptorType = 0x01; |
| +const uint8_t kFunctionSubsetDescriptorType = 0x02; |
| +const uint8_t kUrlDescriptorType = 0x03; |
| + |
| +bool ParseUrl(GURL* url, |
| + std::vector<uint8_t>::const_iterator* it, |
| + std::vector<uint8_t>::const_iterator end) { |
| + uint8_t length = (*it)[0]; |
| + DCHECK_LE(length, std::distance(*it, end)); |
| + DCHECK_EQ((*it)[1], kUrlDescriptorType); |
|
Ken Rockot(use gerrit already)
2015/08/04 00:59:02
If it is passed as 1 short of |end|, can't (*it)[1
Reilly Grant (use Gerrit)
2015/08/04 01:05:20
These properties should be guaranteed by the calle
|
| + |
| + const char* str = reinterpret_cast<const char*>(&(*it)[2]); |
|
Ken Rockot(use gerrit already)
2015/08/04 00:59:02
As can (*it)[2]
|
| + *url = GURL(std::string(str, length - 2)); |
|
Ken Rockot(use gerrit already)
2015/08/04 00:59:02
length - 2 can underflow
|
| + if (!url->is_valid()) { |
| + return false; |
| + } |
| + |
| + std::advance(*it, length); |
| + return true; |
| +} |
| + |
| +bool ParseFunction(WebUsbFunctionSubset* function, |
| + std::vector<uint8_t>::const_iterator* it, |
| + std::vector<uint8_t>::const_iterator end) { |
| + uint8_t length = (*it)[0]; |
| + DCHECK_LE(length, std::distance(*it, end)); |
| + DCHECK_EQ((*it)[1], kFunctionSubsetDescriptorType); |
|
Ken Rockot(use gerrit already)
2015/08/04 00:59:02
same deal as above
|
| + |
| + if (length != 5) { |
| + return false; |
| + } |
| + |
| + function->first_interface = (*it)[2]; |
| + |
| + // Validate the Function Subset header. |
| + uint16_t total_length = (*it)[3] + ((*it)[4] << 8); |
| + if (length > total_length || total_length > std::distance(*it, end)) { |
| + return false; |
| + } |
| + |
| + end = *it + total_length; |
| + std::advance(*it, length); |
| + |
| + while (*it != end) { |
| + uint8_t length = (*it)[0]; |
| + if (length < 2 || std::distance(*it, end) < length) { |
| + return false; |
| + } |
| + |
| + uint8_t type = (*it)[1]; |
| + if (type == kUrlDescriptorType) { |
| + GURL origin; |
| + if (!ParseUrl(&origin, it, end)) { |
| + return false; |
| + } |
| + function->origins.push_back(origin); |
| + } else { |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseConfiguration(WebUsbConfigurationSubset* configuration, |
| + std::vector<uint8_t>::const_iterator* it, |
| + std::vector<uint8_t>::const_iterator end) { |
| + uint8_t length = (*it)[0]; |
| + DCHECK_LE(length, std::distance(*it, end)); |
| + DCHECK_EQ((*it)[1], kConfigurationSubsetDescriptorType); |
| + |
| + if (length != 5) { |
| + return false; |
| + } |
| + |
| + configuration->configuration_value = (*it)[2]; |
| + |
| + // Validate the Configuration Subset header. |
| + uint16_t total_length = (*it)[3] + ((*it)[4] << 8); |
| + if (length > total_length || total_length > std::distance(*it, end)) { |
| + return false; |
| + } |
| + |
| + end = *it + total_length; |
| + std::advance(*it, length); |
| + |
| + while (*it != end) { |
| + uint8_t length = (*it)[0]; |
| + if (length < 2 || std::distance(*it, end) < length) { |
| + return false; |
| + } |
| + |
| + uint8_t type = (*it)[1]; |
| + if (type == kFunctionSubsetDescriptorType) { |
| + WebUsbFunctionSubset function; |
| + if (!ParseFunction(&function, it, end)) { |
| + return false; |
| + } |
| + configuration->functions.push_back(function); |
| + } else if (type == kUrlDescriptorType) { |
| + GURL origin; |
| + if (!ParseUrl(&origin, it, end)) { |
| + return false; |
| + } |
| + configuration->origins.push_back(origin); |
| + } else { |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +WebUsbFunctionSubset::WebUsbFunctionSubset() : first_interface(0) {} |
| + |
| +WebUsbFunctionSubset::~WebUsbFunctionSubset() {} |
| + |
| +WebUsbConfigurationSubset::WebUsbConfigurationSubset() |
| + : configuration_value(0) {} |
| + |
| +WebUsbConfigurationSubset::~WebUsbConfigurationSubset() {} |
| + |
| +WebUsbDescriptorSet::WebUsbDescriptorSet() {} |
| + |
| +WebUsbDescriptorSet::~WebUsbDescriptorSet() {} |
| + |
| +bool WebUsbDescriptorSet::Parse(const std::vector<uint8_t>& bytes) { |
| + if (bytes.size() < 4) { |
| + return false; |
| + } |
| + |
| + // Validate the descriptor set header. |
| + uint16_t total_length = bytes[2] + (bytes[3] << 8); |
| + if (bytes[0] != 4 || // bLength |
| + bytes[1] != kDescriptorSetDescriptorType || // bDescriptorType |
| + 4 > total_length || total_length > bytes.size()) { // wTotalLength |
| + return false; |
|
Ken Rockot(use gerrit already)
2015/08/04 00:59:02
Is it worth vlogging anything for some of these fa
Reilly Grant (use Gerrit)
2015/08/04 01:05:20
I had a bunch of VLOGs when I was developing but I
|
| + } |
| + |
| + std::vector<uint8_t>::const_iterator it = bytes.begin(); |
| + std::vector<uint8_t>::const_iterator end = it + total_length; |
| + std::advance(it, 4); |
| + |
| + while (it != bytes.end()) { |
| + uint8_t length = it[0]; |
| + if (length < 2 || std::distance(it, end) < length) { |
| + return false; |
| + } |
| + |
| + uint8_t type = it[1]; |
| + if (type == kConfigurationSubsetDescriptorType) { |
| + WebUsbConfigurationSubset configuration; |
| + if (!ParseConfiguration(&configuration, &it, end)) { |
| + return false; |
| + } |
| + configurations.push_back(configuration); |
| + } else if (type == kUrlDescriptorType) { |
| + GURL origin; |
| + if (!ParseUrl(&origin, &it, end)) { |
| + return false; |
| + } |
| + origins.push_back(origin); |
| + } else { |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor() |
| + : version(0), vendor_code(0) {} |
| + |
| +WebUsbPlatformCapabilityDescriptor::~WebUsbPlatformCapabilityDescriptor() {} |
| + |
| +bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor( |
| + const std::vector<uint8_t>& bytes) { |
| + if (bytes.size() < 5) { |
| + // Too short for the BOS descriptor header. |
| + return false; |
| + } |
| + |
| + // Validate the BOS descriptor, defined in Table 9-12 of the Universal Serial |
| + // Bus 3.1 Specification, Revision 1.0. |
| + uint16_t total_length = bytes[2] + (bytes[3] << 8); |
| + if (bytes[0] != 5 || // bLength |
| + bytes[1] != kBosDescriptorType || // bDescriptorType |
| + 5 > total_length || total_length > bytes.size()) { // wTotalLength |
| + return false; |
| + } |
| + |
| + uint8_t num_device_caps = bytes[4]; |
| + std::vector<uint8_t>::const_iterator it = bytes.begin(); |
| + std::vector<uint8_t>::const_iterator end = it + total_length; |
| + std::advance(it, 5); |
| + |
| + uint8_t length = 0; |
| + bool found_vendor_code = false; |
| + for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) { |
| + if (it == end) { |
| + return false; |
| + } |
| + |
| + // Validate the Device Capability descriptor, defined in Table 9-13 of the |
| + // Universal Serial Bus 3.1 Specification, Revision 1.0. |
| + length = it[0]; |
| + if (length < 3 || std::distance(it, end) < length || // bLength |
| + it[1] != kDeviceCapabilityDescriptorType) { // bDescriptorType |
| + return false; |
| + } |
| + |
| + if (it[2] != kPlatformDevCapabilityType) { // bDevCapabilityType |
| + continue; |
| + } |
| + |
| + // Validate the Platform Capability Descriptor, defined in Table 9-18 of the |
| + // Universal Serial Bus 3.1 Specification, Revision 1.0. |
| + if (length < 20) { |
| + // Platform capability descriptors must be at least 20 bytes. |
| + return false; |
| + } |
| + |
| + if (memcmp(&it[4], kWebUsbCapabilityUUID, sizeof(kWebUsbCapabilityUUID)) != |
| + 0) { // PlatformCapabilityUUID |
| + continue; |
| + } |
| + |
| + if (length < 23) { |
| + // The WebUSB capability descriptor must be at least 23 bytes (to allow |
| + // for future versions). |
| + return false; |
| + } |
| + |
| + version = it[20] + (it[21] << 8); // bcdVersion |
| + if (version < 0x0100) { |
| + continue; |
| + } |
| + |
| + // Version 1.0 only defines a single field, bVendorCode. |
| + vendor_code = it[22]; |
| + found_vendor_code = true; |
| + } |
| + |
| + return found_vendor_code; |
| +} |
| + |
| +} // namespace device |