#include "services/device/usb/webusb_descriptors.h"
#include <stdint.h>
#include <algorithm>
#include <array>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/auto_spanification_helper.h"
#include "base/functional/bind.h"
#include "services/device/usb/mock_usb_device_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
namespace device {
using mojom::UsbControlTransferRecipient;
using mojom::UsbControlTransferType;
using mojom::UsbTransferDirection;
using mojom::UsbTransferStatus;
namespace {
const uint8_t kExampleBosDescriptor[] = {
0x05, 0x0F, 0x4C, 0x00, 0x03,
0x14, 0x10, 0x04, 0x00, 0x2A, 0xF9, 0xF6, 0xC2, 0x98, 0x10, 0x2B, 0x49,
0x8E, 0x64, 0xFF, 0x01, 0x0C, 0x7F, 0x94, 0xE1,
0x18, 0x10, 0x05, 0x00, 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65, 0x00, 0x01, 0x42, 0x01,
0x1C, 0x10, 0x05, 0x00, 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C,
0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, 0x00, 0x00, 0x03, 0x06,
0x00, 0x00, 0x01, 0x00};
const uint8_t kExampleUrlDescriptor1[] = {
0x19, 0x03, 0x01, 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o',
'm', '/', 'i', 'n', 'd', 'e', 'x', '.', 'h', 't', 'm', 'l'};
const auto kExampleUrlDescriptor255 = std::to_array<uint8_t>({
0x2E, 0x03, 0xFF, 'c', 'h', 'r', 'o', 'm', 'e', '-', 'e', 'x',
't', 'e', 'n', 's', 'i', 'o', 'n', ':', '/', '/', 'e', 'x',
't', 'e', 'n', 's', 'i', 'o', 'n', 'i', 'd', '/', 'e', 'x',
'a', 'm', 'p', 'l', 'e', '.', 'h', 't', 'm', 'l',
});
ACTION_P2(InvokeCallback, data, length) {
size_t transferred_length = std::min(length, arg6->size());
base::span(arg6->as_vector())
.copy_prefix_from(
UNSAFE_TODO(base::span(data, length)).first(transferred_length));
std::move(arg8).Run(UsbTransferStatus::COMPLETED, arg6, transferred_length);
}
void ExpectLandingPage(const GURL& landing_page) {
EXPECT_EQ(GURL("https://example.com/index.html"), landing_page);
}
class WebUsbDescriptorsTest : public ::testing::Test {};
TEST_F(WebUsbDescriptorsTest, PlatformCapabilityDescriptor) {
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_TRUE(descriptor.ParseFromBosDescriptor(kExampleBosDescriptor));
EXPECT_EQ(0x0100, descriptor.version);
EXPECT_EQ(0x42, descriptor.vendor_code);
}
TEST_F(WebUsbDescriptorsTest, ShortBosDescriptorHeader) {
static const uint8_t kBuffer[] = {0x03, 0x0F, 0x03};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, LongBosDescriptorHeader) {
static const uint8_t kBuffer[] = {0x06, 0x0F, 0x05, 0x00, 0x01};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, InvalidBosDescriptor) {
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kExampleUrlDescriptor1));
}
TEST_F(WebUsbDescriptorsTest, ShortBosDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x04, 0x00, 0x01};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, LongBosDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x06, 0x00, 0x01};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, UnexpectedlyEmptyBosDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x05, 0x00, 0x01};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, ShortCapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x06, 0x00, 0x01, 0x02, 0x10};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, LongCapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x08, 0x00,
0x01, 0x04, 0x10, 0x05};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, NotACapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x08, 0x00,
0x01, 0x03, 0x0F, 0x05};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, NoPlatformCapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x19, 0x00, 0x01, 0x14, 0x10,
0x04, 0x00, 0x2A, 0xF9, 0xF6, 0xC2, 0x98,
0x10, 0x2B, 0x49, 0x8E, 0x64, 0xFF, 0x01,
0x0C, 0x7F, 0x94, 0xE1};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, ShortPlatformCapabilityDescriptor) {
static const uint8_t kBuffer[] = {
0x05, 0x0F, 0x18, 0x00, 0x01, 0x13, 0x10, 0x05, 0x00, 0x2A, 0xF9, 0xF6,
0xC2, 0x98, 0x10, 0x2B, 0x49, 0x8E, 0x64, 0xFF, 0x01, 0x0C, 0x7F, 0x94};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, NoWebUsbCapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x19, 0x00, 0x01, 0x14, 0x10,
0x05, 0x00, 0x2A, 0xF9, 0xF6, 0xC2, 0x98,
0x10, 0x2B, 0x49, 0x8E, 0x64, 0xFF, 0x01,
0x0C, 0x7F, 0x94, 0xE1};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, ShortWebUsbPlatformCapabilityDescriptor) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x19, 0x00, 0x01, 0x14, 0x10,
0x05, 0x00, 0x38, 0xB6, 0x08, 0x34, 0xA9,
0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76,
0x88, 0x15, 0xB6, 0x65};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, WebUsbPlatformCapabilityDescriptorOutOfDate) {
static const uint8_t kBuffer[] = {0x05, 0x0F, 0x1C, 0x00, 0x01, 0x17, 0x10,
0x05, 0x00, 0x38, 0xB6, 0x08, 0x34, 0xA9,
0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76,
0x88, 0x15, 0xB6, 0x65, 0x90, 0x00, 0x01};
WebUsbPlatformCapabilityDescriptor descriptor;
ASSERT_FALSE(descriptor.ParseFromBosDescriptor(kBuffer));
}
TEST_F(WebUsbDescriptorsTest, UrlDescriptor) {
GURL url;
ASSERT_TRUE(ParseWebUsbUrlDescriptor(kExampleUrlDescriptor1, &url));
EXPECT_EQ(GURL("https://example.com/index.html"), url);
}
TEST_F(WebUsbDescriptorsTest, EntireUrlDescriptor) {
GURL url;
ASSERT_TRUE(ParseWebUsbUrlDescriptor(
std::vector<uint8_t>(kExampleUrlDescriptor255.data(),
base::span(kExampleUrlDescriptor255)
.subspan(base::SpanificationSizeofForStdArray(
kExampleUrlDescriptor255))
.data()),
&url));
EXPECT_EQ(GURL("chrome-extension://extensionid/example.html"), url);
}
TEST_F(WebUsbDescriptorsTest, ShortUrlDescriptorHeader) {
static const uint8_t kBuffer[] = {0x01};
GURL url;
ASSERT_FALSE(ParseWebUsbUrlDescriptor(kBuffer, &url));
}
TEST_F(WebUsbDescriptorsTest, ShortUrlDescriptor) {
static const uint8_t kBuffer[] = {0x01, 0x03};
GURL url;
ASSERT_FALSE(ParseWebUsbUrlDescriptor(kBuffer, &url));
}
TEST_F(WebUsbDescriptorsTest, LongUrlDescriptor) {
static const uint8_t kBuffer[] = {0x03, 0x03};
GURL url;
ASSERT_FALSE(ParseWebUsbUrlDescriptor(kBuffer, &url));
}
TEST_F(WebUsbDescriptorsTest, EmptyUrl) {
static const uint8_t kBuffer[] = {0x03, 0x03, 0x00};
GURL url;
ASSERT_FALSE(ParseWebUsbUrlDescriptor(kBuffer, &url));
}
TEST_F(WebUsbDescriptorsTest, InvalidUrl) {
static const uint8_t kBuffer[] = {0x06, 0x03, 0x00, '?', '?', '?'};
GURL url;
ASSERT_FALSE(ParseWebUsbUrlDescriptor(kBuffer, &url));
}
TEST_F(WebUsbDescriptorsTest, ReadDescriptors) {
scoped_refptr<MockUsbDeviceHandle> device_handle(
new MockUsbDeviceHandle(nullptr));
EXPECT_CALL(*device_handle,
ControlTransferInternal(UsbTransferDirection::INBOUND,
UsbControlTransferType::STANDARD,
UsbControlTransferRecipient::DEVICE, 0x06,
0x0F00, 0x0000, _, _, _))
.Times(2)
.WillRepeatedly(
InvokeCallback(kExampleBosDescriptor, sizeof(kExampleBosDescriptor)));
EXPECT_CALL(*device_handle,
ControlTransferInternal(UsbTransferDirection::INBOUND,
UsbControlTransferType::VENDOR,
UsbControlTransferRecipient::DEVICE, 0x42,
0x0001, 0x0002, _, _, _))
.WillOnce(InvokeCallback(kExampleUrlDescriptor1,
sizeof(kExampleUrlDescriptor1)));
ReadWebUsbDescriptors(device_handle, base::BindOnce(&ExpectLandingPage));
}
}
}