| 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..f64b4db23e5da9b7f808a7f900d835d30471be98
|
| --- /dev/null
|
| +++ b/device/usb/webusb_descriptors.cc
|
| @@ -0,0 +1,290 @@
|
| +// 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) {
|
| + // These conditions must be guaranteed by the caller.
|
| + DCHECK(*it != end);
|
| + uint8_t length = (*it)[0];
|
| + DCHECK_LE(length, std::distance(*it, end));
|
| + DCHECK_GE(length, 2);
|
| + DCHECK_EQ((*it)[1], kUrlDescriptorType);
|
| +
|
| + if (length == 2) {
|
| + return false;
|
| + }
|
| +
|
| + const char* str = reinterpret_cast<const char*>(&(*it)[2]);
|
| + *url = GURL(std::string(str, length - 2));
|
| + 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) {
|
| + // These conditions must be guaranteed by the caller.
|
| + DCHECK(*it != end);
|
| + uint8_t length = (*it)[0];
|
| + DCHECK_LE(length, std::distance(*it, end));
|
| + DCHECK_GE(length, 2);
|
| + DCHECK_EQ((*it)[1], kFunctionSubsetDescriptorType);
|
| +
|
| + 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) {
|
| + // These conditions must be guaranteed by the caller.
|
| + DCHECK(*it != end);
|
| + uint8_t length = (*it)[0];
|
| + DCHECK_LE(length, std::distance(*it, end));
|
| + DCHECK_GE(length, 2);
|
| + 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;
|
| + }
|
| +
|
| + 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
|
|
|