123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- // 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 "services/device/usb/webusb_descriptors.h"
- #include <limits>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/containers/span.h"
- #include "base/logging.h"
- #include "base/memory/ref_counted_memory.h"
- #include "components/device_event_log/device_event_log.h"
- #include "services/device/usb/usb_device_handle.h"
- #include "url/gurl.h"
- namespace device {
- using mojom::UsbControlTransferRecipient;
- using mojom::UsbControlTransferType;
- using mojom::UsbTransferDirection;
- using mojom::UsbTransferStatus;
- namespace {
- // These constants are defined by the Universal Serial Device 3.0 Specification
- // Revision 1.0.
- const uint8_t kGetDescriptorRequest = 0x06;
- const uint8_t kBosDescriptorType = 0x0F;
- const uint8_t kDeviceCapabilityDescriptorType = 0x10;
- const uint8_t kPlatformDevCapabilityType = 0x05;
- // These constants are defined by the WebUSB specification:
- // http://wicg.github.io/webusb/
- const uint8_t kGetUrlRequest = 0x02;
- 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 size_t kMaxControlTransferLength = std::numeric_limits<uint8_t>::max();
- const int kControlTransferTimeoutMs = 2000; // 2 seconds
- using ReadCompatabilityDescriptorCallback = base::OnceCallback<void(
- const absl::optional<WebUsbPlatformCapabilityDescriptor>& descriptor)>;
- using ReadLandingPageCallback =
- base::OnceCallback<void(const GURL& landing_page)>;
- void OnReadLandingPage(uint8_t landing_page_id,
- ReadLandingPageCallback callback,
- UsbTransferStatus status,
- scoped_refptr<base::RefCountedBytes> buffer,
- size_t length) {
- if (status != UsbTransferStatus::COMPLETED) {
- USB_LOG(EVENT) << "Failed to read WebUSB URL descriptor: "
- << static_cast<int>(landing_page_id);
- std::move(callback).Run(GURL());
- return;
- }
- GURL url;
- ParseWebUsbUrlDescriptor(base::make_span(buffer->front(), length), &url);
- std::move(callback).Run(url);
- }
- void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
- ReadCompatabilityDescriptorCallback callback,
- UsbTransferStatus status,
- scoped_refptr<base::RefCountedBytes> buffer,
- size_t length) {
- if (status != UsbTransferStatus::COMPLETED) {
- USB_LOG(EVENT) << "Failed to read BOS descriptor.";
- std::move(callback).Run(absl::nullopt);
- return;
- }
- WebUsbPlatformCapabilityDescriptor descriptor;
- if (!descriptor.ParseFromBosDescriptor(
- base::make_span(buffer->front(), length))) {
- std::move(callback).Run(absl::nullopt);
- return;
- }
- std::move(callback).Run(descriptor);
- }
- void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle,
- ReadCompatabilityDescriptorCallback callback,
- UsbTransferStatus status,
- scoped_refptr<base::RefCountedBytes> buffer,
- size_t length) {
- if (status != UsbTransferStatus::COMPLETED || length != 5) {
- USB_LOG(EVENT) << "Failed to read BOS descriptor header.";
- std::move(callback).Run(absl::nullopt);
- return;
- }
- const uint8_t* data = buffer->front();
- uint16_t new_length = data[2] | (data[3] << 8);
- auto new_buffer = base::MakeRefCounted<base::RefCountedBytes>(new_length);
- device_handle->ControlTransfer(
- UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
- UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
- kBosDescriptorType << 8, 0, new_buffer, kControlTransferTimeoutMs,
- base::BindOnce(&OnReadBosDescriptor, device_handle, std::move(callback)));
- }
- void OnReadWebUsbCapabilityDescriptor(
- scoped_refptr<UsbDeviceHandle> device_handle,
- ReadLandingPageCallback callback,
- const absl::optional<WebUsbPlatformCapabilityDescriptor>& descriptor) {
- if (!descriptor || !descriptor->landing_page_id) {
- std::move(callback).Run(GURL());
- return;
- }
- ReadWebUsbLandingPage(descriptor->vendor_code, descriptor->landing_page_id,
- device_handle, std::move(callback));
- }
- } // namespace
- WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor()
- : version(0), vendor_code(0) {}
- WebUsbPlatformCapabilityDescriptor::~WebUsbPlatformCapabilityDescriptor() =
- default;
- bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor(
- base::span<const 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];
- auto it = bytes.begin();
- auto end = it + total_length;
- std::advance(it, 5);
- uint8_t length = 0;
- 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 < 22) {
- // The WebUSB capability descriptor must be at least 22 bytes (to allow
- // for future versions).
- return false;
- }
- version = it[20] + (it[21] << 8); // bcdVersion
- if (version < 0x0100) {
- continue;
- }
- // Version 1.0 defines two fields for a total length of 24 bytes.
- if (length != 24) {
- return false;
- }
- vendor_code = it[22];
- landing_page_id = it[23];
- return true;
- }
- return false;
- }
- // Parses a WebUSB URL Descriptor:
- // https://wicg.github.io/webusb/#url-descriptor
- //
- // 0 1 2 3
- // 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
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | length | type | prefix | data[0] |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | data[1] | ...
- // +-+-+-+-+-+-+-+-+-+-+-+------
- bool ParseWebUsbUrlDescriptor(base::span<const uint8_t> bytes, GURL* output) {
- const uint8_t kDescriptorType = 0x03;
- const uint8_t kDescriptorMinLength = 3;
- if (bytes.size() < kDescriptorMinLength) {
- return false;
- }
- // Validate that the length is consistent and fits within the buffer.
- uint8_t length = bytes[0];
- if (length < kDescriptorMinLength || length > bytes.size() ||
- bytes[1] != kDescriptorType) {
- return false;
- }
- // Look up the URL prefix and append the rest of the data in the descriptor.
- std::string url;
- switch (bytes[2]) {
- case 0:
- url.append("http://");
- break;
- case 1:
- url.append("https://");
- break;
- case 255: // 255 indicates that the entire URL is encoded in the URL field.
- break;
- default:
- return false;
- }
- url.append(reinterpret_cast<const char*>(bytes.data() + 3), length - 3);
- *output = GURL(url);
- if (!output->is_valid()) {
- return false;
- }
- return true;
- }
- void ReadWebUsbLandingPage(uint8_t vendor_code,
- uint8_t landing_page_id,
- scoped_refptr<UsbDeviceHandle> device_handle,
- ReadLandingPageCallback callback) {
- auto buffer =
- base::MakeRefCounted<base::RefCountedBytes>(kMaxControlTransferLength);
- device_handle->ControlTransfer(
- UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
- UsbControlTransferRecipient::DEVICE, vendor_code, landing_page_id,
- kGetUrlRequest, buffer, kControlTransferTimeoutMs,
- base::BindOnce(&OnReadLandingPage, landing_page_id, std::move(callback)));
- }
- void ReadWebUsbCapabilityDescriptor(
- scoped_refptr<UsbDeviceHandle> device_handle,
- ReadCompatabilityDescriptorCallback callback) {
- auto buffer = base::MakeRefCounted<base::RefCountedBytes>(5);
- device_handle->ControlTransfer(
- UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
- UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
- kBosDescriptorType << 8, 0, buffer, kControlTransferTimeoutMs,
- base::BindOnce(&OnReadBosDescriptorHeader, device_handle,
- std::move(callback)));
- }
- void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
- ReadLandingPageCallback callback) {
- ReadWebUsbCapabilityDescriptor(
- device_handle, base::BindOnce(&OnReadWebUsbCapabilityDescriptor,
- device_handle, std::move(callback)));
- }
- } // namespace device
|