[
  {
    "path": ".gitignore",
    "content": "build/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.13...3.27)\n\n# initialize the SDK based on PICO_SDK_PATH\n# note: this must happen before project()\ninclude(pico_sdk_import.cmake)\n\nproject(my_project)\n\n# initialize the Raspberry Pi Pico SDK\npico_sdk_init()\n\nadd_executable(u2f-hax\n    main.c\n)\n\n# Add pico_stdlib library which aggregates commonly used features\ntarget_link_libraries(u2f-hax pico_stdlib hardware_resets hardware_irq)\n\n# create map/bin/hex/uf2 file in addition to ELF.\npico_add_extra_outputs(u2f-hax)\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2025 by R <rqou@berkeley.edu>\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Update: Hi HN et al.!\n\nOther than personal \"because I can\" reasons, my goal with this proof-of-concept was to point at the utter state of computing platforms.\n\nAs a maker of widgets, I would like end-users to be able to do things with their brand-new widgets as painlessly as possible. This is a very relatable \"user story\" that has driven a lot of innovation ever since the dawn of personal computing.\n\nHowever, the entire ecosystem of computers and widgets has developed into a state where there are obvious mismatches between what users intuitively expect to be possible and what is \"actually\" possible. The fact that this hack has been trending is just one data point in support of this -- a \"security key\" is \"supposed to\" look like a reasonably-nicely-packaged end product (i.e. not a random RPi Pico) that just does one thing and one thing only, and it's not supposed to intentionally break the rules in a way that doesn't implement any security. The fact that security keys *also* run arbitrary code, can look like anything, and can thus do arbitrary things might be quite obvious to an *extremely* technical audience who pauses to consider the implications, but it was clearly not the intention of even many of the designers involved in creating the infrastructure this hack leverages.\n\nOther ideas which also poke at this problem include the [USB Rubber Ducky](https://shop.hak5.org/products/usb-rubber-ducky) and the [O.MG Cable](https://shop.hak5.org/products/omg-cable). The \"Universal\" part of USB has both advantages and disadvantages! This is something which I alluded to at the end of this README -- there is currently no good way for *either* humans or computers to easily and reliably tell whether a USB device is working against the user's interests (in simplified terms, \"evil\"), in support of them (\"good\"), or simply as a consequence of larger forces and emergent behaviors (a huge category which overlaps most of the former two).\n\nThis brings us to WebUSB and the growth and dominance of the Web as a platform. Without fully litigating whether \"cross-platform\" applications are a good idea or not, the Web is *by far* the easiest way for somebody to deliver software to run on somebody else's computer. I and other developers no longer need to learn the intricacies of every single target platform. As a consequence, I and other developers no longer need to learn the intricacies, the *conventions*, and the *expectations* of every single target platform.\n\nIf the popularity of retrocomputing that I am seeing in my little corner of the zeitgeist is any indication, we've lost something important along the way.\n\nIn conclusion, I want to see discussions being had which move beyond the basics of \"Why won't Firefox implement WebUSB? Is it going to lose out even further to Chrome?\" and more towards discussions about intentionally curating healthy platforms and ecosystems. This extends beyond the Web and includes computing in all forms (whether on desktops and laptops, tablets and phones, or much-less-visible automation such as \"IoT\" and \"smart home\" devices). Platforms need to be both healthy for developers, so that they enjoy building software for them, and healthy for users, so that they understand what is happening and can engage with computing in a way which empowers them.\n\nI am sure that there are people who have more experience and plenty more to say on these topics than I do, and I look forward to hearing these discussions!\n\n# We don't need no stinkin' WebUSB!\n\nIt turns out that there is a way for a web page to access USB devices *without* requiring WebUSB and its associated political disagreements! Not only that, a device can intentionally design itself to bypass all of the user consent requirements.\n\n![demo video](demo.gif)\n\n## Quick demo\n\nLoad [u2f-hax.uf2](u2f-hax.uf2) onto a Raspberry Pi Pico (RP2040 version), and then load [index.html](index.html) from either localhost or another secure context.\n\nThe \"On!\" and \"Off!\" buttons will toggle the LED, and the state of pin `GP22` will be regularly updated on the page (you can conveniently short it to the adjacent GND pad with a piece of wire or metal).\n\n## How?!\n\nThe Pico is programmed to emulate a [U2F](https://en.wikipedia.org/wiki/Universal_2nd_Factor) dongle (i.e. a physical two-factor security key). However, instead of performing any security functions, arbitrary data is smuggled in the \"key handle\" and signature of `U2F_AUTHENTICATE` messages. As long as the key handle starts with 0xfeedface, the Pico instantly \"confirms\" user presence and returns data.\n\n## Why is this possible?\n\nBy design, the U2F key handle is an opaque blob of data which is conceptually \"owned by\" the security dongle. It is supposed to be returned by the dongle as a result of a registration, stored as-is by the relying party, and then given as-is back to the security dongle when authenticating.\n\nOne reason this key handle functionality exists is to enable an unlimited number of websites to be associated with a particular  low-cost dongle with very limited memory. This hypothetical dongle stores a unique \"master\" encryption key internally. When a new registration is created, it creates a new public/private key pair, returns the public key, encrypts the private key with the \"master\" key, and *returns the encrypted private key as the key handle*. No matter how many registrations are created, the dongle does not have to be responsible for storing the keys associated with them. When the key handle is passed back to the dongle during an authentication, the dongle just unwraps the private key using its master key.\n\nIn order to _allow for_ all of these low-cost designs without _mandating_ any particular internal algorithms, the key handle is treated as opaque, and so we can abuse it to smuggle arbitrary data.\n\nIn order to _return_ data, we need to somehow smuggle it as an ECDSA signature. An ECDSA signature is a tuple of two numbers $(r, s)$, where each of the numbers is calculated $\\mod n$, where $n$ is the _order_ of the elliptic curve base point. This basically means any value from 0 up to 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 (the order of the secp256r1 base point). These numbers are then packed into some ASN.1.\n\nAlthough it is _sometimes_ possible to tell whether an ECDSA signature was actually calculated \"properly\" rather than being some numbers we just made up (see Issue #1), there isn't a good reason for anybody other than the relying party to perform anything beyond basic validity checks. Chrome appears to check whether the numbers in the signature are actually in the range from 0 to $n$, but Firefox doesn't check even that.\n\nAs a result, we can just generate some dummy ASN.1 and then put the data we actually want to send inside of it. In order to reliably get around Chrome's basic validity checks, we just waste the first byte of each number with the value 0x7f. This will result in numbers which are always positive and less than $n$. The entire software stack up to browser JavaScript will pass these \"valid-enough\" numbers straight through.\n\nFinally, because \"access to USB devices\" is politically contentious but \"make users more secure\" has very broad political support across the entire browser industry, this capability is widely supported without requiring extraneous setup, configuration, nor prompting.\n\n## Is this a security vulnerability?\n\nNo.\n\nThis _cannot_ be used to access arbitrary USB devices. It only works with devices which are _intentionally_ breaking the rules. In essence, this is an intentionally vulnerable device.\n\n_However_, it is known that the security model around USB devices is generally... questionable on most platforms. Plugging in a malicious USB device allows it to do anything that you yourself can do with devices such as a keyboard or a mouse.\n\nDo not plug arbitrary unknown devices into your computer (or your phone, etc.).\n"
  },
  {
    "path": "dev_lowlevel.h",
    "content": "/**\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef DEV_LOWLEVEL_H_\n#define DEV_LOWLEVEL_H_\n\n#include \"usb_common.h\"\n\ntypedef void (*usb_ep_handler)(uint8_t *buf, uint16_t len);\n\n// Struct in which we keep the endpoint configuration\nstruct usb_endpoint_configuration {\n    const struct usb_endpoint_descriptor *descriptor;\n    usb_ep_handler handler;\n\n    // Pointers to endpoint + buffer control registers\n    // in the USB controller DPSRAM\n    volatile uint32_t *endpoint_control;\n    volatile uint32_t *buffer_control;\n    volatile uint8_t *data_buffer;\n\n    // Toggle after each packet (unless replying to a SETUP)\n    uint8_t next_pid;\n};\n\n// Struct in which we keep the device configuration\nstruct usb_device_configuration {\n    const struct usb_device_descriptor *device_descriptor;\n    const struct usb_interface_descriptor *interface_descriptor;\n    const struct usb_hid_descriptor *hid_descriptor;\n    const struct usb_configuration_descriptor *config_descriptor;\n    const unsigned char *lang_descriptor;\n    const unsigned char **descriptor_strings;\n    // USB num endpoints is 16\n    struct usb_endpoint_configuration endpoints[USB_NUM_ENDPOINTS];\n};\n\n#define EP0_IN_ADDR  (USB_DIR_IN  | 0)\n#define EP0_OUT_ADDR (USB_DIR_OUT | 0)\n#define EP1_OUT_ADDR (USB_DIR_OUT | 1)\n#define EP2_IN_ADDR  (USB_DIR_IN  | 2)\n\n// EP0 IN and OUT\nstatic const struct usb_endpoint_descriptor ep0_out = {\n        .bLength          = sizeof(struct usb_endpoint_descriptor),\n        .bDescriptorType  = USB_DT_ENDPOINT,\n        .bEndpointAddress = EP0_OUT_ADDR, // EP number 0, OUT from host (rx to device)\n        .bmAttributes     = USB_TRANSFER_TYPE_CONTROL,\n        .wMaxPacketSize   = 64,\n        .bInterval        = 0\n};\n\nstatic const struct usb_endpoint_descriptor ep0_in = {\n        .bLength          = sizeof(struct usb_endpoint_descriptor),\n        .bDescriptorType  = USB_DT_ENDPOINT,\n        .bEndpointAddress = EP0_IN_ADDR, // EP number 0, OUT from host (rx to device)\n        .bmAttributes     = USB_TRANSFER_TYPE_CONTROL,\n        .wMaxPacketSize   = 64,\n        .bInterval        = 0\n};\n\n// Descriptors\nstatic const struct usb_device_descriptor device_descriptor = {\n        .bLength         = sizeof(struct usb_device_descriptor),\n        .bDescriptorType = USB_DT_DEVICE,\n        .bcdUSB          = 0x0110, // USB 1.1 device\n        .bDeviceClass    = 0,      // Specified in interface descriptor\n        .bDeviceSubClass = 0,      // No subclass\n        .bDeviceProtocol = 0,      // No protocol\n        .bMaxPacketSize0 = 64,     // Max packet size for ep0\n        .idVendor        = 0xf055, // Your vendor id\n        .idProduct       = 0x0000, // Your product ID\n        .bcdDevice       = 0,      // No device revision number\n        .iManufacturer   = 1,      // Manufacturer string index\n        .iProduct        = 2,      // Product string index\n        .iSerialNumber = 0,        // No serial number\n        .bNumConfigurations = 1    // One configuration\n};\n\nstatic const struct usb_interface_descriptor interface_descriptor = {\n        .bLength            = sizeof(struct usb_interface_descriptor),\n        .bDescriptorType    = USB_DT_INTERFACE,\n        .bInterfaceNumber   = 0,\n        .bAlternateSetting  = 0,\n        .bNumEndpoints      = 2,    // Interface has 2 endpoints\n        .bInterfaceClass    = 0x03, // HID\n        .bInterfaceSubClass = 0,\n        .bInterfaceProtocol = 0,\n        .iInterface         = 0\n};\n\nstatic const unsigned char report_descriptor[] = {\n        0x06, 0xd0, 0xf1, /* USAGE_PAGE (FIDO Alliance) */\n        0x09, 0x01,       /* USAGE (U2F Authenticator Device) */\n\n        0xa1, 0x01,       /* COLLECTION (Application) */\n\n        0x09, 0x20,       /*   USAGE (Input report data) */\n        0x15, 0x00,       /*   LOGICAL_MINIMUM (0) */\n        0x26, 0xff, 0x00, /*   LOGICAL_MAXIMUM (255) */\n        0x75, 0x08,       /*   REPORT_SIZE (8) */\n        0x95, 0x40,       /*   REPORT_COUNT (64) */\n        0x81, 0x02,       /*   INPUT (Data,Var,Abs); Modifier byte */\n\n        0x09, 0x21,       /*   USAGE (Output report data) */\n        0x15, 0x00,       /*   LOGICAL_MINIMUM (0) */\n        0x26, 0xff, 0x00, /*   LOGICAL_MAXIMUM (255) */\n        0x75, 0x08,       /*   REPORT_SIZE (8) */\n        0x95, 0x40,       /*   REPORT_COUNT (64) */\n        0x91, 0x02,       /*   OUTPUT (Data,Var,Abs); Modifier byte */\n\n        0xc0              /* END_COLLECTION */\n};\n\nstatic const struct usb_hid_descriptor hid_descriptor = {\n        .bLength                = sizeof(struct usb_hid_descriptor),\n        .bDescriptorType        = USB_DT_HID,\n        .bcdHID                 = 0x111,\n        .bCountryCode           = 0,\n        .bNumDescriptors        = 1,\n        .bDescriptorType0       = USB_DT_HID_REPORT,\n        .wDescriptorLength0     = sizeof(report_descriptor),\n};\n\nstatic const struct usb_endpoint_descriptor ep1_out = {\n        .bLength          = sizeof(struct usb_endpoint_descriptor),\n        .bDescriptorType  = USB_DT_ENDPOINT,\n        .bEndpointAddress = EP1_OUT_ADDR, // EP number 1, OUT from host (rx to device)\n        .bmAttributes     = USB_TRANSFER_TYPE_INTERRUPT,\n        .wMaxPacketSize   = 64,\n        .bInterval        = 0\n};\n\nstatic const struct usb_endpoint_descriptor ep2_in = {\n        .bLength          = sizeof(struct usb_endpoint_descriptor),\n        .bDescriptorType  = USB_DT_ENDPOINT,\n        .bEndpointAddress = EP2_IN_ADDR, // EP number 2, IN from host (tx from device)\n        .bmAttributes     = USB_TRANSFER_TYPE_INTERRUPT,\n        .wMaxPacketSize   = 64,\n        .bInterval        = 0\n};\n\nstatic const struct usb_configuration_descriptor config_descriptor = {\n        .bLength         = sizeof(struct usb_configuration_descriptor),\n        .bDescriptorType = USB_DT_CONFIG,\n        .wTotalLength    = (sizeof(config_descriptor) +\n                            sizeof(interface_descriptor) +\n                            sizeof(hid_descriptor) +\n                            sizeof(ep1_out) +\n                            sizeof(ep2_in)),\n        .bNumInterfaces  = 1,\n        .bConfigurationValue = 1, // Configuration 1\n        .iConfiguration = 0,      // No string\n        .bmAttributes = 0xc0,     // attributes: self powered, no remote wakeup\n        .bMaxPower = 0x32         // 100ma\n};\n\nstatic const unsigned char lang_descriptor[] = {\n        4,         // bLength\n        0x03,      // bDescriptorType == String Descriptor\n        0x09, 0x04 // language id = us english\n};\n\nstatic const unsigned char *descriptor_strings[] = {\n        (unsigned char *) \"ArcaneNibble\",       // Vendor\n        (unsigned char *) \"U2F Hax\"             // Product\n};\n\n#endif\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <script>\n            let led_state = 0;\n\n            async function poll_pin() {\n                try {\n                    let credential = await navigator.credentials.get({\n                        publicKey: {\n                            challenge: new Uint8Array([]),\n                            allowCredentials: [{\n                                type: \"public-key\",\n                                transports: [\"usb\"],\n                                id: new Uint8Array([0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, led_state])\n                            }],\n                            userVerification: \"discouraged\",\n                        }\n                    });\n                    let sig = new Uint8Array(credential.response.signature);\n                    let pin = sig[9];\n                    let poll_result = document.getElementById(\"poll_result\");\n                    poll_result.innerText = \"Pin is: \" + pin;\n                    poll_result.style.backgroundColor = pin ? \"green\" : \"red\";\n                } catch (e) {}\n                window.requestAnimationFrame(poll_pin);\n            }\n\n            async function set_up() {\n                document.getElementById(\"turn_on\").onclick = () => {led_state = 1};\n                document.getElementById(\"turn_off\").onclick = () => {led_state = 0};\n                window.requestAnimationFrame(poll_pin);\n            }\n            window.onload = set_up;\n        </script>\n    </head>\n    <body>\n        <div>\n            <button id=\"turn_on\">On!</button>\n            <button id=\"turn_off\">Off!</button>\n        </div>\n        <div id=\"poll_result\"></div>\n    </body>\n</html>\n"
  },
  {
    "path": "main.c",
    "content": "/**\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include <stdio.h>\n\n// Pico\n#include \"pico/stdlib.h\"\n\n// For memcpy\n#include <string.h>\n\n// Include descriptor struct definitions\n#include \"usb_common.h\"\n// USB register definitions from pico-sdk\n#include \"hardware/regs/usb.h\"\n// USB hardware struct definitions from pico-sdk\n#include \"hardware/structs/usb.h\"\n// For interrupt enable and numbers\n#include \"hardware/irq.h\"\n// For resetting the USB controller\n#include \"hardware/resets.h\"\n\n// Device descriptors\n#include \"dev_lowlevel.h\"\n\n#define usb_hw_set ((usb_hw_t *)hw_set_alias_untyped(usb_hw))\n#define usb_hw_clear ((usb_hw_t *)hw_clear_alias_untyped(usb_hw))\n\n// Function prototypes for our device specific endpoint handlers defined\n// later on\nvoid ep0_in_handler(uint8_t *buf, uint16_t len);\nvoid ep0_out_handler(uint8_t *buf, uint16_t len);\nvoid ep1_out_handler(uint8_t *buf, uint16_t len);\nvoid ep2_in_handler(uint8_t *buf, uint16_t len);\n\n// Global device address\nstatic bool should_set_address = false;\nstatic uint8_t dev_addr = 0;\nstatic volatile bool configured = false;\n\n// Global data buffer for EP0\nstatic uint8_t ep0_buf[64];\n\n// Struct defining the device configuration\nstatic struct usb_device_configuration dev_config = {\n        .device_descriptor = &device_descriptor,\n        .interface_descriptor = &interface_descriptor,\n        .hid_descriptor = &hid_descriptor,\n        .config_descriptor = &config_descriptor,\n        .lang_descriptor = lang_descriptor,\n        .descriptor_strings = descriptor_strings,\n        .endpoints = {\n                {\n                        .descriptor = &ep0_out,\n                        .handler = &ep0_out_handler,\n                        .endpoint_control = NULL, // NA for EP0\n                        .buffer_control = &usb_dpram->ep_buf_ctrl[0].out,\n                        // EP0 in and out share a data buffer\n                        .data_buffer = &usb_dpram->ep0_buf_a[0],\n                },\n                {\n                        .descriptor = &ep0_in,\n                        .handler = &ep0_in_handler,\n                        .endpoint_control = NULL, // NA for EP0,\n                        .buffer_control = &usb_dpram->ep_buf_ctrl[0].in,\n                        // EP0 in and out share a data buffer\n                        .data_buffer = &usb_dpram->ep0_buf_a[0],\n                },\n                {\n                        .descriptor = &ep1_out,\n                        .handler = &ep1_out_handler,\n                        // EP1 starts at offset 0 for endpoint control\n                        .endpoint_control = &usb_dpram->ep_ctrl[0].out,\n                        .buffer_control = &usb_dpram->ep_buf_ctrl[1].out,\n                        // First free EPX buffer\n                        .data_buffer = &usb_dpram->epx_data[0 * 64],\n                },\n                {\n                        .descriptor = &ep2_in,\n                        .handler = &ep2_in_handler,\n                        .endpoint_control = &usb_dpram->ep_ctrl[1].in,\n                        .buffer_control = &usb_dpram->ep_buf_ctrl[2].in,\n                        // Second free EPX buffer\n                        .data_buffer = &usb_dpram->epx_data[1 * 64],\n                }\n        }\n};\n\n/**\n * @brief Given an endpoint address, return the usb_endpoint_configuration of that endpoint. Returns NULL\n * if an endpoint of that address is not found.\n *\n * @param addr\n * @return struct usb_endpoint_configuration*\n */\nstruct usb_endpoint_configuration *usb_get_endpoint_configuration(uint8_t addr) {\n    struct usb_endpoint_configuration *endpoints = dev_config.endpoints;\n    for (int i = 0; i < USB_NUM_ENDPOINTS; i++) {\n        if (endpoints[i].descriptor && (endpoints[i].descriptor->bEndpointAddress == addr)) {\n            return &endpoints[i];\n        }\n    }\n    return NULL;\n}\n\n/**\n * @brief Given a C string, fill the EP0 data buf with a USB string descriptor for that string.\n *\n * @param C string you would like to send to the USB host\n * @return the length of the string descriptor in EP0 buf\n */\nuint8_t usb_prepare_string_descriptor(const unsigned char *str) {\n    // 2 for bLength + bDescriptorType + strlen * 2 because string is unicode. i.e. other byte will be 0\n    uint8_t bLength = 2 + (strlen((const char *)str) * 2);\n    static const uint8_t bDescriptorType = 0x03;\n\n    volatile uint8_t *buf = &ep0_buf[0];\n    *buf++ = bLength;\n    *buf++ = bDescriptorType;\n\n    uint8_t c;\n\n    do {\n        c = *str++;\n        *buf++ = c;\n        *buf++ = 0;\n    } while (c != '\\0');\n\n    return bLength;\n}\n\n/**\n * @brief Take a buffer pointer located in the USB RAM and return as an offset of the RAM.\n *\n * @param buf\n * @return uint32_t\n */\nstatic inline uint32_t usb_buffer_offset(volatile uint8_t *buf) {\n    return (uint32_t) buf ^ (uint32_t) usb_dpram;\n}\n\n/**\n * @brief Set up the endpoint control register for an endpoint (if applicable. Not valid for EP0).\n *\n * @param ep\n */\nvoid usb_setup_endpoint(const struct usb_endpoint_configuration *ep) {\n    printf(\"Set up endpoint 0x%x with buffer address 0x%p\\n\", ep->descriptor->bEndpointAddress, ep->data_buffer);\n\n    // EP0 doesn't have one so return if that is the case\n    if (!ep->endpoint_control) {\n        return;\n    }\n\n    // Get the data buffer as an offset of the USB controller's DPRAM\n    uint32_t dpram_offset = usb_buffer_offset(ep->data_buffer);\n    uint32_t reg = EP_CTRL_ENABLE_BITS\n                   | EP_CTRL_INTERRUPT_PER_BUFFER\n                   | (ep->descriptor->bmAttributes << EP_CTRL_BUFFER_TYPE_LSB)\n                   | dpram_offset;\n    *ep->endpoint_control = reg;\n}\n\n/**\n * @brief Set up the endpoint control register for each endpoint.\n *\n */\nvoid usb_setup_endpoints() {\n    const struct usb_endpoint_configuration *endpoints = dev_config.endpoints;\n    for (int i = 0; i < USB_NUM_ENDPOINTS; i++) {\n        if (endpoints[i].descriptor && endpoints[i].handler) {\n            usb_setup_endpoint(&endpoints[i]);\n        }\n    }\n}\n\n/**\n * @brief Set up the USB controller in device mode, clearing any previous state.\n *\n */\nvoid usb_device_init() {\n    // Reset usb controller\n    reset_unreset_block_num_wait_blocking(RESET_USBCTRL);\n\n    // Clear any previous state in dpram just in case\n    memset(usb_dpram, 0, sizeof(*usb_dpram)); // <1>\n\n    // Enable USB interrupt at processor\n    irq_set_enabled(USBCTRL_IRQ, true);\n\n    // Mux the controller to the onboard usb phy\n    usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;\n\n    // Force VBUS detect so the device thinks it is plugged into a host\n    usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;\n\n    // Enable the USB controller in device mode.\n    usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;\n\n    // Enable an interrupt per EP0 transaction\n    usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; // <2>\n\n    // Enable interrupts for when a buffer is done, when the bus is reset,\n    // and when a setup packet is received\n    usb_hw->inte = USB_INTS_BUFF_STATUS_BITS |\n                   USB_INTS_BUS_RESET_BITS |\n                   USB_INTS_SETUP_REQ_BITS;\n\n    // Set up endpoints (endpoint control registers)\n    // described by device configuration\n    usb_setup_endpoints();\n\n    // Present full speed device by enabling pull up on DP\n    usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;\n}\n\n/**\n * @brief Given an endpoint configuration, returns true if the endpoint\n * is transmitting data to the host (i.e. is an IN endpoint)\n *\n * @param ep, the endpoint configuration\n * @return true\n * @return false\n */\nstatic inline bool ep_is_tx(struct usb_endpoint_configuration *ep) {\n    return ep->descriptor->bEndpointAddress & USB_DIR_IN;\n}\n\n/**\n * @brief Starts a transfer on a given endpoint.\n *\n * @param ep, the endpoint configuration.\n * @param buf, the data buffer to send. Only applicable if the endpoint is TX\n * @param len, the length of the data in buf (this example limits max len to one packet - 64 bytes)\n */\nvoid usb_start_transfer(struct usb_endpoint_configuration *ep, uint8_t *buf, uint16_t len) {\n    // We are asserting that the length is <= 64 bytes for simplicity of the example.\n    // For multi packet transfers see the tinyusb port.\n    assert(len <= 64);\n\n    printf(\"Start transfer of len %d on ep addr 0x%x\\n\", len, ep->descriptor->bEndpointAddress);\n\n    // Prepare buffer control register value\n    uint32_t val = len | USB_BUF_CTRL_AVAIL;\n\n    if (ep_is_tx(ep)) {\n        // Need to copy the data from the user buffer to the usb memory\n        memcpy((void *) ep->data_buffer, (void *) buf, len);\n        // Mark as full\n        val |= USB_BUF_CTRL_FULL;\n    }\n\n    // Set pid and flip for next transfer\n    val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;\n    ep->next_pid ^= 1u;\n\n    *ep->buffer_control = val;\n}\n\n/**\n * @brief Send device descriptor to host\n *\n */\nvoid usb_handle_device_descriptor(volatile struct usb_setup_packet *pkt) {\n    const struct usb_device_descriptor *d = dev_config.device_descriptor;\n    // EP0 in\n    struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP0_IN_ADDR);\n    // Always respond with pid 1\n    ep->next_pid = 1;\n    usb_start_transfer(ep, (uint8_t *) d, MIN(sizeof(struct usb_device_descriptor), pkt->wLength));\n}\n\n/**\n * @brief Send the configuration descriptor (and potentially the configuration and endpoint descriptors) to the host.\n *\n * @param pkt, the setup packet received from the host.\n */\nvoid usb_handle_config_descriptor(volatile struct usb_setup_packet *pkt) {\n    uint8_t *buf = &ep0_buf[0];\n\n    // First request will want just the config descriptor\n    const struct usb_configuration_descriptor *d = dev_config.config_descriptor;\n    memcpy((void *) buf, d, sizeof(struct usb_configuration_descriptor));\n    buf += sizeof(struct usb_configuration_descriptor);\n\n    // If we more than just the config descriptor copy it all\n    if (pkt->wLength >= d->wTotalLength) {\n        memcpy((void *) buf, dev_config.interface_descriptor, sizeof(struct usb_interface_descriptor));\n        buf += sizeof(struct usb_interface_descriptor);\n        memcpy((void *) buf, dev_config.hid_descriptor, sizeof(struct usb_hid_descriptor));\n        buf += sizeof(struct usb_hid_descriptor);\n        const struct usb_endpoint_configuration *ep = dev_config.endpoints;\n\n        // Copy all the endpoint descriptors starting from EP1\n        for (uint i = 2; i < USB_NUM_ENDPOINTS; i++) {\n            if (ep[i].descriptor) {\n                memcpy((void *) buf, ep[i].descriptor, sizeof(struct usb_endpoint_descriptor));\n                buf += sizeof(struct usb_endpoint_descriptor);\n            }\n        }\n\n    }\n\n    // Send data\n    // Get len by working out end of buffer subtract start of buffer\n    uint32_t len = (uint32_t) buf - (uint32_t) &ep0_buf[0];\n    usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len, pkt->wLength));\n}\n\nvoid usb_handle_hid_descriptor(volatile struct usb_setup_packet *pkt) {\n    uint8_t *buf = &ep0_buf[0];\n\n    const struct usb_hid_descriptor *d = dev_config.hid_descriptor;\n    memcpy((void *) buf, d, sizeof(struct usb_hid_descriptor));\n    buf += sizeof(struct usb_hid_descriptor);\n    uint32_t len = (uint32_t) buf - (uint32_t) &ep0_buf[0];\n    usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len, pkt->wLength));\n}\n\nvoid usb_handle_report_descriptor(volatile struct usb_setup_packet *pkt) {\n    uint8_t *buf = &ep0_buf[0];\n    memcpy((void *) buf, report_descriptor, sizeof(report_descriptor));\n    uint32_t len = sizeof(report_descriptor);\n    usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len, pkt->wLength));\n}\n\n/**\n * @brief Handle a BUS RESET from the host by setting the device address back to 0.\n *\n */\nvoid usb_bus_reset(void) {\n    // Set address back to 0\n    dev_addr = 0;\n    should_set_address = false;\n    usb_hw->dev_addr_ctrl = 0;\n    configured = false;\n}\n\n/**\n * @brief Send the requested string descriptor to the host.\n *\n * @param pkt, the setup packet from the host.\n */\nvoid usb_handle_string_descriptor(volatile struct usb_setup_packet *pkt) {\n    uint8_t i = pkt->wValue & 0xff;\n    uint8_t len = 0;\n\n    if (i == 0) {\n        len = 4;\n        memcpy(&ep0_buf[0], dev_config.lang_descriptor, len);\n    } else {\n        // Prepare fills in ep0_buf\n        len = usb_prepare_string_descriptor(dev_config.descriptor_strings[i - 1]);\n    }\n\n    usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), &ep0_buf[0], MIN(len, pkt->wLength));\n}\n\n/**\n * @brief Sends a zero length status packet back to the host.\n */\nvoid usb_acknowledge_out_request(void) {\n    usb_start_transfer(usb_get_endpoint_configuration(EP0_IN_ADDR), NULL, 0);\n}\n\n/**\n * @brief Handles a SET_ADDR request from the host. The actual setting of the device address in\n * hardware is done in ep0_in_handler. This is because we have to acknowledge the request first\n * as a device with address zero.\n *\n * @param pkt, the setup packet from the host.\n */\nvoid usb_set_device_address(volatile struct usb_setup_packet *pkt) {\n    // Set address is a bit of a strange case because we have to send a 0 length status packet first with\n    // address 0\n    dev_addr = (pkt->wValue & 0xff);\n    printf(\"Set address %d\\r\\n\", dev_addr);\n    // Will set address in the callback phase\n    should_set_address = true;\n    usb_acknowledge_out_request();\n}\n\n/**\n * @brief Handles a SET_CONFIGRUATION request from the host. Assumes one configuration so simply\n * sends a zero length status packet back to the host.\n *\n * @param pkt, the setup packet from the host.\n */\nvoid usb_set_device_configuration(__unused volatile struct usb_setup_packet *pkt) {\n    // Only one configuration so just acknowledge the request\n    printf(\"Device Enumerated\\r\\n\");\n    usb_acknowledge_out_request();\n    configured = true;\n}\n\n/**\n * @brief Respond to a setup packet from the host.\n *\n */\nvoid usb_handle_setup_packet(void) {\n    volatile struct usb_setup_packet *pkt = (volatile struct usb_setup_packet *) &usb_dpram->setup_packet;\n    uint8_t req_direction = pkt->bmRequestType;\n    uint8_t req = pkt->bRequest;\n\n    // Reset PID to 1 for EP0 IN\n    usb_get_endpoint_configuration(EP0_IN_ADDR)->next_pid = 1u;\n\n    if (req_direction == USB_DIR_OUT) {\n        if (req == USB_REQUEST_SET_ADDRESS) {\n            usb_set_device_address(pkt);\n        } else if (req == USB_REQUEST_SET_CONFIGURATION) {\n            usb_set_device_configuration(pkt);\n        } else {\n            usb_acknowledge_out_request();\n            printf(\"Other OUT request (0x%x)\\r\\n\", pkt->bRequest);\n        }\n    } else if (req_direction == USB_DIR_IN) {\n        if (req == USB_REQUEST_GET_DESCRIPTOR) {\n            uint16_t descriptor_type = pkt->wValue >> 8;\n\n            switch (descriptor_type) {\n                case USB_DT_DEVICE:\n                    usb_handle_device_descriptor(pkt);\n                    printf(\"GET DEVICE DESCRIPTOR\\r\\n\");\n                    break;\n\n                case USB_DT_CONFIG:\n                    usb_handle_config_descriptor(pkt);\n                    printf(\"GET CONFIG DESCRIPTOR\\r\\n\");\n                    break;\n\n                case USB_DT_STRING:\n                    usb_handle_string_descriptor(pkt);\n                    printf(\"GET STRING DESCRIPTOR\\r\\n\");\n                    break;\n\n                default:\n                    printf(\"Unhandled GET_DESCRIPTOR type 0x%x\\r\\n\", descriptor_type);\n            }\n        } else {\n            printf(\"Other IN request (0x%x)\\r\\n\", pkt->bRequest);\n        }\n    } else if (req_direction == 0x81) {\n        if (req == USB_REQUEST_GET_DESCRIPTOR) {\n            uint16_t descriptor_type = pkt->wValue >> 8;\n\n            switch (descriptor_type) {\n                case USB_DT_HID:\n                    usb_handle_hid_descriptor(pkt);\n                    printf(\"GET HID DESCRIPTOR\\r\\n\");\n                    break;\n\n                case USB_DT_HID_REPORT:\n                    usb_handle_report_descriptor(pkt);\n                    printf(\"GET HID REPORT DESCRIPTOR\\r\\n\");\n                    break;\n\n                default:\n                    printf(\"Unhandled GET_DESCRIPTOR type 0x%x\\r\\n\", descriptor_type);\n            }\n        } else {\n            printf(\"Other IN request (0x%x)\\r\\n\", pkt->bRequest);\n        }\n    }\n}\n\n/**\n * @brief Notify an endpoint that a transfer has completed.\n *\n * @param ep, the endpoint to notify.\n */\nstatic void usb_handle_ep_buff_done(struct usb_endpoint_configuration *ep) {\n    uint32_t buffer_control = *ep->buffer_control;\n    // Get the transfer length for this endpoint\n    uint16_t len = buffer_control & USB_BUF_CTRL_LEN_MASK;\n\n    // Call that endpoints buffer done handler\n    ep->handler((uint8_t *) ep->data_buffer, len);\n}\n\n/**\n * @brief Find the endpoint configuration for a specified endpoint number and\n * direction and notify it that a transfer has completed.\n *\n * @param ep_num\n * @param in\n */\nstatic void usb_handle_buff_done(uint ep_num, bool in) {\n    uint8_t ep_addr = ep_num | (in ? USB_DIR_IN : 0);\n    printf(\"EP %d (in = %d) done\\n\", ep_num, in);\n    for (uint i = 0; i < USB_NUM_ENDPOINTS; i++) {\n        struct usb_endpoint_configuration *ep = &dev_config.endpoints[i];\n        if (ep->descriptor && ep->handler) {\n            if (ep->descriptor->bEndpointAddress == ep_addr) {\n                usb_handle_ep_buff_done(ep);\n                return;\n            }\n        }\n    }\n}\n\n/**\n * @brief Handle a \"buffer status\" irq. This means that one or more\n * buffers have been sent / received. Notify each endpoint where this\n * is the case.\n */\nstatic void usb_handle_buff_status() {\n    uint32_t buffers = usb_hw->buf_status;\n    uint32_t remaining_buffers = buffers;\n\n    uint bit = 1u;\n    for (uint i = 0; remaining_buffers && i < USB_NUM_ENDPOINTS * 2; i++) {\n        if (remaining_buffers & bit) {\n            // clear this in advance\n            usb_hw_clear->buf_status = bit;\n            // IN transfer for even i, OUT transfer for odd i\n            usb_handle_buff_done(i >> 1u, !(i & 1u));\n            remaining_buffers &= ~bit;\n        }\n        bit <<= 1u;\n    }\n}\n\n/**\n * @brief USB interrupt handler\n *\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/// \\tag::isr_setup_packet[]\nvoid isr_usbctrl(void) {\n    // USB interrupt handler\n    uint32_t status = usb_hw->ints;\n    uint32_t handled = 0;\n\n    // Setup packet received\n    if (status & USB_INTS_SETUP_REQ_BITS) {\n        handled |= USB_INTS_SETUP_REQ_BITS;\n        usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;\n        usb_handle_setup_packet();\n    }\n/// \\end::isr_setup_packet[]\n\n    // Buffer status, one or more buffers have completed\n    if (status & USB_INTS_BUFF_STATUS_BITS) {\n        handled |= USB_INTS_BUFF_STATUS_BITS;\n        usb_handle_buff_status();\n    }\n\n    // Bus is reset\n    if (status & USB_INTS_BUS_RESET_BITS) {\n        printf(\"BUS RESET\\n\");\n        handled |= USB_INTS_BUS_RESET_BITS;\n        usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;\n        usb_bus_reset();\n    }\n\n    if (status ^ handled) {\n        panic(\"Unhandled IRQ 0x%x\\n\", (uint) (status ^ handled));\n    }\n}\n#ifdef __cplusplus\n}\n#endif\n\n/**\n * @brief EP0 in transfer complete. Either finish the SET_ADDRESS process, or receive a zero\n * length status packet from the host.\n *\n * @param buf the data that was sent\n * @param len the length that was sent\n */\nvoid ep0_in_handler(__unused uint8_t *buf, __unused uint16_t len) {\n    if (should_set_address) {\n        // Set actual device address in hardware\n        usb_hw->dev_addr_ctrl = dev_addr;\n        should_set_address = false;\n    } else {\n        // Receive a zero length status packet from the host on EP0 OUT\n        struct usb_endpoint_configuration *ep = usb_get_endpoint_configuration(EP0_OUT_ADDR);\n        usb_start_transfer(ep, NULL, 0);\n    }\n}\n\nvoid ep0_out_handler(__unused uint8_t *buf, __unused uint16_t len) {\n}\n\n\n//////// U2F HAX STUFF\n\nuint8_t cmd_buf[7609];\nsize_t cmd_pos;\nsize_t cmd_sz;\nint cmd_seq;\nint cmd_in_progress;\n\nuint8_t res_buf[7609];\nsize_t res_pos;\nsize_t res_sz;\nint res_seq;\n\nuint8_t usb_buf[64];\n\nuint32_t cid_in_progress;\nuint32_t cid_next;\n\nvoid do_u2f_cmd() {\n    struct usb_endpoint_configuration *ep2 = usb_get_endpoint_configuration(EP2_IN_ADDR);\n\n    if (cmd_in_progress == 0x81) {\n        // CTAPHID_PING\n        memcpy(res_buf, cmd_buf, cmd_sz);\n\n        res_sz = cmd_sz;\n        res_seq = 0;\n        res_pos = MIN(64 - 7, res_sz);\n    }\n\n    if (cmd_in_progress == 0x83) {\n        // CTAPHID_MSG\n        uint16_t cla_ins = (cmd_buf[0] << 8) | cmd_buf[1];\n\n        if (cla_ins == 0x0003) {\n            // U2F_VERSION\n            res_buf[0] = 'U';\n            res_buf[1] = '2';\n            res_buf[2] = 'F';\n            res_buf[3] = '_';\n            res_buf[4] = 'V';\n            res_buf[5] = '2';\n            res_buf[6] = 0x90;\n            res_buf[7] = 0x00;\n            res_sz = res_pos = 8;\n        } else if (cla_ins == 0x0002) {\n            // U2F_AUTHENTICATE\n\n            if (cmd_buf[2] == 0x07) {\n                // check-only\n                if (cmd_buf[7 + 65] == 0xfe && cmd_buf[7 + 66] == 0xed && cmd_buf[7 + 67] == 0xfa && cmd_buf[7 + 68] == 0xce) {\n                    // ok\n                    res_buf[0] = 0x69;\n                    res_buf[1] = 0x85;\n                    res_sz = res_pos = 2;\n                } else {\n                    // not ok\n                    res_buf[0] = 0x69;\n                    res_buf[1] = 0x84;\n                    res_sz = res_pos = 2;\n                }\n            } else {\n                if (!(cmd_buf[7 + 65] == 0xfe && cmd_buf[7 + 66] == 0xed && cmd_buf[7 + 67] == 0xfa && cmd_buf[7 + 68] == 0xce)) {\n                    // not ok\n                    res_buf[0] = 0x69;\n                    res_buf[1] = 0x84;\n                    res_sz = res_pos = 2;\n                } else {\n                    // control LED with extra byte\n                    if (cmd_buf[7 + 73] == 0)\n                        gpio_put(PICO_DEFAULT_LED_PIN, 0);\n                    if (cmd_buf[7 + 73] == 1)\n                        gpio_put(PICO_DEFAULT_LED_PIN, 1);\n\n                    memset(res_buf, 0, sizeof(res_buf));\n                    res_buf[0] = 0x01;  // user presence\n                    res_buf[1] = 0xde;  // counter\n                    res_buf[2] = 0xad;\n                    res_buf[3] = 0xbe;\n                    res_buf[4] = 0xef;\n\n                    res_buf[5] = 0x30;      // ASN.1 sequence\n                    res_buf[6] = 0x44;\n\n                    res_buf[7] = 0x02;      // ASN.1 integer\n                    res_buf[8] = 0x20;\n\n                    res_buf[9] = 0x7f;     // make it not all zero\n                    // copy bytes [13-16] to signature, except inverted\n                    res_buf[10] = cmd_buf[7 + 69] ^ 0xff;\n                    res_buf[11] = cmd_buf[7 + 70] ^ 0xff;\n                    res_buf[12] = cmd_buf[7 + 71] ^ 0xff;\n                    res_buf[13] = cmd_buf[7 + 72] ^ 0xff;\n                    // read gpio\n                    res_buf[14] = gpio_get(22);\n\n                    res_buf[41] = 0x02;     // ASN.1 integer\n                    res_buf[42] = 0x20;\n\n                    res_buf[43] = 0x7f;     // make it not all zero\n\n                    res_buf[75] = 0x90;\n                    res_buf[76] = 0x00;\n                    res_sz = 77;\n                    res_seq = 0;\n                    res_pos = MIN(64 - 7, res_sz);\n                }\n            }\n        } else {\n            // unsupported\n            res_buf[0] = 0x6d;\n            res_buf[1] = 0x00;\n            res_sz = res_pos = 2;\n        }\n    }\n\n    // common setup for response\n    memset(usb_buf, 0, sizeof(usb_buf));\n    memcpy(usb_buf, &cid_in_progress, 4);\n    usb_buf[4] = cmd_in_progress;\n    usb_buf[5] = res_sz >> 8;\n    usb_buf[6] = res_sz;\n    memcpy(&usb_buf[7], res_buf, res_pos);\n    usb_start_transfer(ep2, usb_buf, 64);\n\n    if (res_pos == res_sz) {\n        cmd_in_progress = 0;\n        cid_in_progress = 0;\n    }\n}\n\nvoid ep1_out_handler(uint8_t *buf, uint16_t len) {\n    printf(\"RX %d bytes from host\\n\", len);\n\n    struct usb_endpoint_configuration *ep2 = usb_get_endpoint_configuration(EP2_IN_ADDR);\n\n    uint32_t this_cid;\n    memcpy(&this_cid, buf, 4);\n\n    if (buf[4] & 0x80) {\n        // init packet\n        uint16_t bcnt = (buf[5] << 8) | buf[6];\n\n        if (this_cid == 0) goto out;\n\n        switch (buf[4]) {\n            case 0x86:\n                // CTAPHID_INIT\n                if (bcnt != 8) {\n                    // invalid len\n                    memset(usb_buf, 0, sizeof(usb_buf));\n                    memcpy(usb_buf, buf, 4);\n                    usb_buf[4] = 0xbf;\n                    usb_buf[5] = 0;\n                    usb_buf[6] = 1;\n                    usb_buf[7] = 3;\n                    usb_start_transfer(ep2, usb_buf, 64);\n                    break;\n                }\n\n                // allocate a new channel id\n                uint32_t new_cid = this_cid;\n                if (new_cid = 0xffffffff) {\n                    ++cid_next;\n                    if (!cid_next) ++cid_next;\n                    new_cid = cid_next;\n                }\n\n                memset(usb_buf, 0, sizeof(usb_buf));\n                memcpy(usb_buf, buf, 4);\n                usb_buf[4] = 0x86;\n                usb_buf[5] = 0;\n                usb_buf[6] = 17;\n                memcpy(&usb_buf[7], &buf[7], 8);\n                memcpy(&usb_buf[15], &new_cid, 4);\n                usb_buf[19] = 2;\n                usb_buf[20] = 0;\n                usb_buf[21] = 0;\n                usb_buf[22] = 0;\n                usb_buf[23] = 0;\n                usb_start_transfer(ep2, usb_buf, 64);\n                break;\n\n            case 0x81:\n            case 0x83:\n                // CTAPHID_MSG or CTAPHID_PING\n                if (cid_in_progress) {\n                    // busy\n                    memset(usb_buf, 0, sizeof(usb_buf));\n                    memcpy(usb_buf, buf, 4);\n                    usb_buf[4] = 0xbf;\n                    usb_buf[5] = 0;\n                    usb_buf[6] = 1;\n                    usb_buf[7] = 6;\n                    usb_start_transfer(ep2, usb_buf, 64);\n                    break;\n                }\n\n                cid_in_progress = this_cid;\n                cmd_in_progress = buf[4];\n                cmd_sz = bcnt;\n                cmd_pos = MIN(64 - 7, bcnt);\n                cmd_seq = 0;\n                memcpy(cmd_buf, &buf[7], cmd_pos);\n\n                if (cmd_pos == cmd_sz)\n                    do_u2f_cmd();\n                break;\n\n            default:\n                // invalid command\n                memset(usb_buf, 0, sizeof(usb_buf));\n                memcpy(usb_buf, buf, 4);\n                usb_buf[4] = 0xbf;\n                usb_buf[5] = 0;\n                usb_buf[6] = 1;\n                usb_buf[7] = 1;\n                usb_start_transfer(ep2, usb_buf, 64);\n                break;\n        }\n    } else {\n        // continuation\n        if (cmd_in_progress && cid_in_progress == this_cid) {\n            if (buf[4] == cmd_seq) {\n                // valid seq\n                uint32_t chunk_sz = MIN(64 - 5, cmd_sz - cmd_pos);\n                memcpy(&cmd_buf[cmd_pos], &buf[5], chunk_sz);\n                cmd_pos += chunk_sz;\n                cmd_seq++;\n\n                if (cmd_pos == cmd_sz)\n                    do_u2f_cmd();\n            } else {\n                // invalid seq\n                memset(usb_buf, 0, sizeof(usb_buf));\n                memcpy(usb_buf, buf, 4);\n                usb_buf[4] = 0xbf;\n                usb_buf[5] = 0;\n                usb_buf[6] = 1;\n                usb_buf[7] = 4;\n                usb_start_transfer(ep2, usb_buf, 64);\n            }\n        }\n    }\n\nout:\n    // Get ready to rx again from host\n    usb_start_transfer(usb_get_endpoint_configuration(EP1_OUT_ADDR), NULL, 64);\n}\n\nvoid ep2_in_handler(__unused uint8_t *buf, uint16_t len) {\n    printf(\"Sent %d bytes to host\\n\", len);\n\n    struct usb_endpoint_configuration *ep2 = usb_get_endpoint_configuration(EP2_IN_ADDR);\n\n    if (res_pos != res_sz) {\n        uint32_t chunk_sz = MIN(64 - 5, res_sz - res_pos);\n        memset(usb_buf, 0, sizeof(usb_buf));\n        memcpy(usb_buf, &cid_in_progress, 4);\n        usb_buf[4] = res_seq++;\n        memcpy(&usb_buf[5], &res_buf[res_pos], chunk_sz);\n        usb_start_transfer(ep2, usb_buf, 64);\n\n        res_pos += chunk_sz;\n        if (res_pos == res_sz) {\n            cmd_in_progress = 0;\n            cid_in_progress = 0;\n        }\n    }\n}\n\nint main(void) {\n    stdio_init_all();\n    printf(\"USB Device Low-Level hardware example\\n\");\n    usb_device_init();\n\n    gpio_init(PICO_DEFAULT_LED_PIN);\n    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);\n\n    gpio_init(22);\n    gpio_set_dir(22, GPIO_IN);\n    gpio_pull_up(22);\n\n    // Wait until configured\n    while (!configured) {\n        tight_loop_contents();\n    }\n\n    // Get ready to rx from host\n    usb_start_transfer(usb_get_endpoint_configuration(EP1_OUT_ADDR), NULL, 64);\n\n    // Everything is interrupt driven so just loop here\n    while (1) {\n        tight_loop_contents();\n    }\n}\n"
  },
  {
    "path": "pico_sdk_import.cmake",
    "content": "# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake\n\n# This can be dropped into an external project to help locate this SDK\n# It should be include()ed prior to project()\n\n# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.\n#\n# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the\n# following conditions are met:\n#\n# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following\n# disclaimer.\n#\n# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following\n# disclaimer in the documentation and/or other materials provided with the distribution.\n#\n# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products\n# derived from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nif (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))\n    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})\n    message(\"Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))\n    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))\n    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))\n    set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')\")\nendif ()\n\nif (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)\n  set(PICO_SDK_FETCH_FROM_GIT_TAG \"master\")\n  message(\"Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG\")\nendif()\n\nset(PICO_SDK_PATH \"${PICO_SDK_PATH}\" CACHE PATH \"Path to the Raspberry Pi Pico SDK\")\nset(PICO_SDK_FETCH_FROM_GIT \"${PICO_SDK_FETCH_FROM_GIT}\" CACHE BOOL \"Set to ON to fetch copy of SDK from git if not otherwise locatable\")\nset(PICO_SDK_FETCH_FROM_GIT_PATH \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" CACHE FILEPATH \"location to download SDK\")\nset(PICO_SDK_FETCH_FROM_GIT_TAG \"${PICO_SDK_FETCH_FROM_GIT_TAG}\" CACHE FILEPATH \"release tag for SDK\")\n\nif (NOT PICO_SDK_PATH)\n    if (PICO_SDK_FETCH_FROM_GIT)\n        include(FetchContent)\n        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})\n        if (PICO_SDK_FETCH_FROM_GIT_PATH)\n            get_filename_component(FETCHCONTENT_BASE_DIR \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" REALPATH BASE_DIR \"${CMAKE_SOURCE_DIR}\")\n        endif ()\n        FetchContent_Declare(\n                pico_sdk\n                GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}\n        )\n\n        if (NOT pico_sdk)\n            message(\"Downloading Raspberry Pi Pico SDK\")\n            # GIT_SUBMODULES_RECURSE was added in 3.17\n            if (${CMAKE_VERSION} VERSION_GREATER_EQUAL \"3.17.0\")\n                FetchContent_Populate(\n                        pico_sdk\n                        QUIET\n                        GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                        GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}\n                        GIT_SUBMODULES_RECURSE FALSE\n\n                        SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src\n                        BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build\n                        SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild\n                )\n            else ()\n                FetchContent_Populate(\n                        pico_sdk\n                        QUIET\n                        GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                        GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}\n\n                        SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src\n                        BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build\n                        SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild\n                )\n            endif ()\n\n            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})\n        endif ()\n        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})\n    else ()\n        message(FATAL_ERROR\n                \"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git.\"\n                )\n    endif ()\nendif ()\n\nget_filename_component(PICO_SDK_PATH \"${PICO_SDK_PATH}\" REALPATH BASE_DIR \"${CMAKE_BINARY_DIR}\")\nif (NOT EXISTS ${PICO_SDK_PATH})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' not found\")\nendif ()\n\nset(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)\nif (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK\")\nendif ()\n\nset(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH \"Path to the Raspberry Pi Pico SDK\" FORCE)\n\ninclude(${PICO_SDK_INIT_CMAKE_FILE})\n"
  },
  {
    "path": "usb_common.h",
    "content": "/*\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef _USB_COMMON_H\n#define _USB_COMMON_H\n\n#include \"pico/types.h\"\n#include \"hardware/structs/usb.h\"\n\n// bmRequestType bit definitions\n#define USB_REQ_TYPE_STANDARD 0x00u\n#define USB_REQ_TYPE_TYPE_MASK 0x60u\n#define USB_REQ_TYPE_TYPE_CLASS 0x20u\n#define USB_REQ_TYPE_TYPE_VENDOR 0x40u\n\n#define USB_REQ_TYPE_RECIPIENT_MASK 0x1fu\n#define USB_REQ_TYPE_RECIPIENT_DEVICE 0x00u\n#define USB_REQ_TYPE_RECIPIENT_INTERFACE 0x01u\n#define USB_REQ_TYPE_RECIPIENT_ENDPOINT 0x02u\n\n#define USB_DIR_OUT 0x00u\n#define USB_DIR_IN 0x80u\n\n#define USB_TRANSFER_TYPE_CONTROL 0x0\n#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x1\n#define USB_TRANSFER_TYPE_BULK 0x2\n#define USB_TRANSFER_TYPE_INTERRUPT 0x3\n#define USB_TRANSFER_TYPE_BITS 0x3\n\n// Descriptor types\n#define USB_DT_DEVICE 0x01\n#define USB_DT_CONFIG 0x02\n#define USB_DT_STRING 0x03\n#define USB_DT_INTERFACE 0x04\n#define USB_DT_ENDPOINT 0x05\n#define USB_DT_HID 0x21\n#define USB_DT_HID_REPORT 0x22\n\n#define USB_REQUEST_GET_STATUS 0x0\n#define USB_REQUEST_CLEAR_FEATURE 0x01\n#define USB_REQUEST_SET_FEATURE 0x03\n#define USB_REQUEST_SET_ADDRESS 0x05\n#define USB_REQUEST_GET_DESCRIPTOR 0x06\n#define USB_REQUEST_SET_DESCRIPTOR 0x07\n#define USB_REQUEST_GET_CONFIGURATION 0x08\n#define USB_REQUEST_SET_CONFIGURATION 0x09\n#define USB_REQUEST_GET_INTERFACE 0x0a\n#define USB_REQUEST_SET_INTERFACE 0x0b\n#define USB_REQUEST_SYNC_FRAME 0x0c\n\n#define USB_REQUEST_MSC_GET_MAX_LUN 0xfe\n#define USB_REQUEST_MSC_RESET 0xff\n\n#define USB_FEAT_ENDPOINT_HALT            0x00\n#define USB_FEAT_DEVICE_REMOTE_WAKEUP   0x01\n#define USB_FEAT_TEST_MODE                0x02\n\n#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05\n\nstruct usb_setup_packet {\n    uint8_t bmRequestType;\n    uint8_t bRequest;\n    uint16_t wValue;\n    uint16_t wIndex;\n    uint16_t wLength;\n} __packed;\n\nstruct usb_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n};\n\nstruct usb_device_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint16_t bcdUSB;\n    uint8_t bDeviceClass;\n    uint8_t bDeviceSubClass;\n    uint8_t bDeviceProtocol;\n    uint8_t bMaxPacketSize0;\n    uint16_t idVendor;\n    uint16_t idProduct;\n    uint16_t bcdDevice;\n    uint8_t iManufacturer;\n    uint8_t iProduct;\n    uint8_t iSerialNumber;\n    uint8_t bNumConfigurations;\n} __packed;\n\nstruct usb_configuration_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint16_t wTotalLength;\n    uint8_t bNumInterfaces;\n    uint8_t bConfigurationValue;\n    uint8_t iConfiguration;\n    uint8_t bmAttributes;\n    uint8_t bMaxPower;\n} __packed;\n\nstruct usb_interface_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint8_t bInterfaceNumber;\n    uint8_t bAlternateSetting;\n    uint8_t bNumEndpoints;\n    uint8_t bInterfaceClass;\n    uint8_t bInterfaceSubClass;\n    uint8_t bInterfaceProtocol;\n    uint8_t iInterface;\n} __packed;\n\nstruct usb_endpoint_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint8_t bEndpointAddress;\n    uint8_t bmAttributes;\n    uint16_t wMaxPacketSize;\n    uint8_t bInterval;\n} __packed;\n\nstruct usb_hid_descriptor {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint16_t bcdHID;\n    uint8_t bCountryCode;\n    uint8_t bNumDescriptors;\n    uint8_t bDescriptorType0;\n    uint16_t wDescriptorLength0;\n} __packed;\n\nstruct usb_endpoint_descriptor_long {\n    uint8_t bLength;\n    uint8_t bDescriptorType;\n    uint8_t bEndpointAddress;\n    uint8_t bmAttributes;\n    uint16_t wMaxPacketSize;\n    uint8_t bInterval;\n    uint8_t bRefresh;\n    uint8_t bSyncAddr;\n} __attribute__((packed));\n\n#endif"
  }
]