webusb_descriptors.cc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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
  3. // found in the LICENSE file.
  4. #include "services/device/usb/webusb_descriptors.h"
  5. #include <limits>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/containers/span.h"
  9. #include "base/logging.h"
  10. #include "base/memory/ref_counted_memory.h"
  11. #include "components/device_event_log/device_event_log.h"
  12. #include "services/device/usb/usb_device_handle.h"
  13. #include "url/gurl.h"
  14. namespace device {
  15. using mojom::UsbControlTransferRecipient;
  16. using mojom::UsbControlTransferType;
  17. using mojom::UsbTransferDirection;
  18. using mojom::UsbTransferStatus;
  19. namespace {
  20. // These constants are defined by the Universal Serial Device 3.0 Specification
  21. // Revision 1.0.
  22. const uint8_t kGetDescriptorRequest = 0x06;
  23. const uint8_t kBosDescriptorType = 0x0F;
  24. const uint8_t kDeviceCapabilityDescriptorType = 0x10;
  25. const uint8_t kPlatformDevCapabilityType = 0x05;
  26. // These constants are defined by the WebUSB specification:
  27. // http://wicg.github.io/webusb/
  28. const uint8_t kGetUrlRequest = 0x02;
  29. const uint8_t kWebUsbCapabilityUUID[16] = {
  30. // Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}.
  31. 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
  32. 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
  33. const size_t kMaxControlTransferLength = std::numeric_limits<uint8_t>::max();
  34. const int kControlTransferTimeoutMs = 2000; // 2 seconds
  35. using ReadCompatabilityDescriptorCallback = base::OnceCallback<void(
  36. const absl::optional<WebUsbPlatformCapabilityDescriptor>& descriptor)>;
  37. using ReadLandingPageCallback =
  38. base::OnceCallback<void(const GURL& landing_page)>;
  39. void OnReadLandingPage(uint8_t landing_page_id,
  40. ReadLandingPageCallback callback,
  41. UsbTransferStatus status,
  42. scoped_refptr<base::RefCountedBytes> buffer,
  43. size_t length) {
  44. if (status != UsbTransferStatus::COMPLETED) {
  45. USB_LOG(EVENT) << "Failed to read WebUSB URL descriptor: "
  46. << static_cast<int>(landing_page_id);
  47. std::move(callback).Run(GURL());
  48. return;
  49. }
  50. GURL url;
  51. ParseWebUsbUrlDescriptor(base::make_span(buffer->front(), length), &url);
  52. std::move(callback).Run(url);
  53. }
  54. void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
  55. ReadCompatabilityDescriptorCallback callback,
  56. UsbTransferStatus status,
  57. scoped_refptr<base::RefCountedBytes> buffer,
  58. size_t length) {
  59. if (status != UsbTransferStatus::COMPLETED) {
  60. USB_LOG(EVENT) << "Failed to read BOS descriptor.";
  61. std::move(callback).Run(absl::nullopt);
  62. return;
  63. }
  64. WebUsbPlatformCapabilityDescriptor descriptor;
  65. if (!descriptor.ParseFromBosDescriptor(
  66. base::make_span(buffer->front(), length))) {
  67. std::move(callback).Run(absl::nullopt);
  68. return;
  69. }
  70. std::move(callback).Run(descriptor);
  71. }
  72. void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle,
  73. ReadCompatabilityDescriptorCallback callback,
  74. UsbTransferStatus status,
  75. scoped_refptr<base::RefCountedBytes> buffer,
  76. size_t length) {
  77. if (status != UsbTransferStatus::COMPLETED || length != 5) {
  78. USB_LOG(EVENT) << "Failed to read BOS descriptor header.";
  79. std::move(callback).Run(absl::nullopt);
  80. return;
  81. }
  82. const uint8_t* data = buffer->front();
  83. uint16_t new_length = data[2] | (data[3] << 8);
  84. auto new_buffer = base::MakeRefCounted<base::RefCountedBytes>(new_length);
  85. device_handle->ControlTransfer(
  86. UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
  87. UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
  88. kBosDescriptorType << 8, 0, new_buffer, kControlTransferTimeoutMs,
  89. base::BindOnce(&OnReadBosDescriptor, device_handle, std::move(callback)));
  90. }
  91. void OnReadWebUsbCapabilityDescriptor(
  92. scoped_refptr<UsbDeviceHandle> device_handle,
  93. ReadLandingPageCallback callback,
  94. const absl::optional<WebUsbPlatformCapabilityDescriptor>& descriptor) {
  95. if (!descriptor || !descriptor->landing_page_id) {
  96. std::move(callback).Run(GURL());
  97. return;
  98. }
  99. ReadWebUsbLandingPage(descriptor->vendor_code, descriptor->landing_page_id,
  100. device_handle, std::move(callback));
  101. }
  102. } // namespace
  103. WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor()
  104. : version(0), vendor_code(0) {}
  105. WebUsbPlatformCapabilityDescriptor::~WebUsbPlatformCapabilityDescriptor() =
  106. default;
  107. bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor(
  108. base::span<const uint8_t> bytes) {
  109. if (bytes.size() < 5) {
  110. // Too short for the BOS descriptor header.
  111. return false;
  112. }
  113. // Validate the BOS descriptor, defined in Table 9-12 of the Universal Serial
  114. // Bus 3.1 Specification, Revision 1.0.
  115. uint16_t total_length = bytes[2] + (bytes[3] << 8);
  116. if (bytes[0] != 5 || // bLength
  117. bytes[1] != kBosDescriptorType || // bDescriptorType
  118. 5 > total_length || total_length > bytes.size()) { // wTotalLength
  119. return false;
  120. }
  121. uint8_t num_device_caps = bytes[4];
  122. auto it = bytes.begin();
  123. auto end = it + total_length;
  124. std::advance(it, 5);
  125. uint8_t length = 0;
  126. for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) {
  127. if (it == end) {
  128. return false;
  129. }
  130. // Validate the Device Capability descriptor, defined in Table 9-13 of the
  131. // Universal Serial Bus 3.1 Specification, Revision 1.0.
  132. length = it[0];
  133. if (length < 3 || std::distance(it, end) < length || // bLength
  134. it[1] != kDeviceCapabilityDescriptorType) { // bDescriptorType
  135. return false;
  136. }
  137. if (it[2] != kPlatformDevCapabilityType) { // bDevCapabilityType
  138. continue;
  139. }
  140. // Validate the Platform Capability Descriptor, defined in Table 9-18 of the
  141. // Universal Serial Bus 3.1 Specification, Revision 1.0.
  142. if (length < 20) {
  143. // Platform capability descriptors must be at least 20 bytes.
  144. return false;
  145. }
  146. if (memcmp(&it[4], kWebUsbCapabilityUUID, sizeof(kWebUsbCapabilityUUID)) !=
  147. 0) { // PlatformCapabilityUUID
  148. continue;
  149. }
  150. if (length < 22) {
  151. // The WebUSB capability descriptor must be at least 22 bytes (to allow
  152. // for future versions).
  153. return false;
  154. }
  155. version = it[20] + (it[21] << 8); // bcdVersion
  156. if (version < 0x0100) {
  157. continue;
  158. }
  159. // Version 1.0 defines two fields for a total length of 24 bytes.
  160. if (length != 24) {
  161. return false;
  162. }
  163. vendor_code = it[22];
  164. landing_page_id = it[23];
  165. return true;
  166. }
  167. return false;
  168. }
  169. // Parses a WebUSB URL Descriptor:
  170. // https://wicg.github.io/webusb/#url-descriptor
  171. //
  172. // 0 1 2 3
  173. // 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
  174. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  175. // | length | type | prefix | data[0] |
  176. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  177. // | data[1] | ...
  178. // +-+-+-+-+-+-+-+-+-+-+-+------
  179. bool ParseWebUsbUrlDescriptor(base::span<const uint8_t> bytes, GURL* output) {
  180. const uint8_t kDescriptorType = 0x03;
  181. const uint8_t kDescriptorMinLength = 3;
  182. if (bytes.size() < kDescriptorMinLength) {
  183. return false;
  184. }
  185. // Validate that the length is consistent and fits within the buffer.
  186. uint8_t length = bytes[0];
  187. if (length < kDescriptorMinLength || length > bytes.size() ||
  188. bytes[1] != kDescriptorType) {
  189. return false;
  190. }
  191. // Look up the URL prefix and append the rest of the data in the descriptor.
  192. std::string url;
  193. switch (bytes[2]) {
  194. case 0:
  195. url.append("http://");
  196. break;
  197. case 1:
  198. url.append("https://");
  199. break;
  200. case 255: // 255 indicates that the entire URL is encoded in the URL field.
  201. break;
  202. default:
  203. return false;
  204. }
  205. url.append(reinterpret_cast<const char*>(bytes.data() + 3), length - 3);
  206. *output = GURL(url);
  207. if (!output->is_valid()) {
  208. return false;
  209. }
  210. return true;
  211. }
  212. void ReadWebUsbLandingPage(uint8_t vendor_code,
  213. uint8_t landing_page_id,
  214. scoped_refptr<UsbDeviceHandle> device_handle,
  215. ReadLandingPageCallback callback) {
  216. auto buffer =
  217. base::MakeRefCounted<base::RefCountedBytes>(kMaxControlTransferLength);
  218. device_handle->ControlTransfer(
  219. UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
  220. UsbControlTransferRecipient::DEVICE, vendor_code, landing_page_id,
  221. kGetUrlRequest, buffer, kControlTransferTimeoutMs,
  222. base::BindOnce(&OnReadLandingPage, landing_page_id, std::move(callback)));
  223. }
  224. void ReadWebUsbCapabilityDescriptor(
  225. scoped_refptr<UsbDeviceHandle> device_handle,
  226. ReadCompatabilityDescriptorCallback callback) {
  227. auto buffer = base::MakeRefCounted<base::RefCountedBytes>(5);
  228. device_handle->ControlTransfer(
  229. UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
  230. UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
  231. kBosDescriptorType << 8, 0, buffer, kControlTransferTimeoutMs,
  232. base::BindOnce(&OnReadBosDescriptorHeader, device_handle,
  233. std::move(callback)));
  234. }
  235. void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
  236. ReadLandingPageCallback callback) {
  237. ReadWebUsbCapabilityDescriptor(
  238. device_handle, base::BindOnce(&OnReadWebUsbCapabilityDescriptor,
  239. device_handle, std::move(callback)));
  240. }
  241. } // namespace device