<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bruno Dias</title>
    <description>The latest articles on DEV Community by Bruno Dias (@brvn01).</description>
    <link>https://dev.to/brvn01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4204882%2F455eda9a-83a3-43bf-a05c-6b7c663fb8e5.jpg</url>
      <title>DEV Community: Bruno Dias</title>
      <link>https://dev.to/brvn01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brvn01"/>
    <language>en</language>
    <item>
      <title>Build Your Own FIDO2 Security Key and HSM with PicoKeys</title>
      <dc:creator>Bruno Dias</dc:creator>
      <pubDate>Sun, 25 Oct 2026 00:15:00 +0000</pubDate>
      <link>https://dev.to/brvn01/picokeys-on-esp32-s3-461b</link>
      <guid>https://dev.to/brvn01/picokeys-on-esp32-s3-461b</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Part 1 — Introduction to PicoKeys

&lt;ul&gt;
&lt;li&gt;My Recommendation&lt;/li&gt;
&lt;li&gt;Introduction to PicoKeys&lt;/li&gt;
&lt;li&gt;Community vs Enterprise&lt;/li&gt;
&lt;li&gt;PicoKey App&lt;/li&gt;
&lt;li&gt;
Flashing the Firmware

&lt;ul&gt;
&lt;li&gt;Flashing Through the Browser — ESP32-S2/S3&lt;/li&gt;
&lt;li&gt;VID/PID&lt;/li&gt;
&lt;li&gt;PicoKey App&lt;/li&gt;
&lt;li&gt;Pico Patcher&lt;/li&gt;
&lt;li&gt;Dynamic Changes Through Pico HSM&lt;/li&gt;
&lt;li&gt;Manual Compilation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using an ESP32-S3&lt;/li&gt;
&lt;li&gt;
A Microcontroller Is Not a Secure Element

&lt;ul&gt;
&lt;li&gt;Flashing FIDO2 Through the Browser&lt;/li&gt;
&lt;li&gt;Configuring a PIN&lt;/li&gt;
&lt;li&gt;Listing and Deleting Configured Passkeys&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Part 2 — Compiling PicoKeys Firmware

&lt;ul&gt;
&lt;li&gt;Compiling the Firmware Without Secure Boot&lt;/li&gt;
&lt;li&gt;Installing Dependencies and ESP-IDF&lt;/li&gt;
&lt;li&gt;
Downloading and Compiling Pico FIDO

&lt;ul&gt;
&lt;li&gt;Failure to Enforce the alwaysUv Policy&lt;/li&gt;
&lt;li&gt;Memory Validation During U2F Processing&lt;/li&gt;
&lt;li&gt;Compiling FIDO&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Downloading and Compiling Pico HSM&lt;/li&gt;
&lt;li&gt;Flashing the Firmware Without Secure Boot&lt;/li&gt;
&lt;li&gt;Compiling the Firmware with Secure Boot&lt;/li&gt;
&lt;li&gt;Do I Need to Enable Flash Encryption?&lt;/li&gt;
&lt;li&gt;Generating the Signing Keys&lt;/li&gt;
&lt;li&gt;
Associating the Key Digest with PicoKeys Firmware

&lt;ul&gt;
&lt;li&gt;Generating the Public Key&lt;/li&gt;
&lt;li&gt;Converting the Digest to the Format Used by PicoKeys&lt;/li&gt;
&lt;li&gt;Adding the Digest to PicoKeys FIDO and HSM Source Code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configuring and Signing Secure Boot for FIDO and HSM&lt;/li&gt;
&lt;li&gt;Generating Signed FIDO and HSM Firmware&lt;/li&gt;
&lt;li&gt;Flashing the Secure Boot Image for FIDO and HSM&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Part 3 — Secure Boot and Flash Encryption

&lt;ul&gt;
&lt;li&gt;Compiling the Firmware with Secure Boot and Flash Encryption&lt;/li&gt;
&lt;li&gt;Cloning the Projects and Installing ESP-IDF 5.5.1&lt;/li&gt;
&lt;li&gt;Required Adjustments for HSM and FIDO&lt;/li&gt;
&lt;li&gt;Generating the Keys&lt;/li&gt;
&lt;li&gt;Associating the Key Digest with PicoKeys Firmware&lt;/li&gt;
&lt;li&gt;Configuring Secure Boot and Flash Encryption&lt;/li&gt;
&lt;li&gt;Generating and Validating the Signed FIDO and HSM Firmware&lt;/li&gt;
&lt;li&gt;Preparing the Board and FE Key — First Firmware Only&lt;/li&gt;
&lt;li&gt;Flashing the First Firmware onto the Board&lt;/li&gt;
&lt;li&gt;Flashing New Encrypted Firmware — Second or Later Flash&lt;/li&gt;
&lt;li&gt;Sources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1 — Introduction to PicoKeys &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ Always use the YubiKey VID/PID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the VID/PID set to the YubiKey profile, PicoKeys (FIDO version only) can be recognized by &lt;code&gt;ykman&lt;/code&gt; (YubiKey Manager). You can view stored FIDO2 credentials and delete individual credentials by their ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ykman fido credentials list
ykman fido credentials delete &amp;lt;start-of-ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With &lt;code&gt;ykman&lt;/code&gt;, we can list and delete FIDO2 credentials, configure YubiKey OTP directly on PicoKeys FIDO, manage the PIN, and more. In my tests, listing and deleting FIDO2 credentials worked without any issues. I was also able to view and change other settings.&lt;/p&gt;

&lt;p&gt;I was also able to configure a &lt;strong&gt;Yubico OTP&lt;/strong&gt; credential using the YubiKey slot feature. These slots can store authentication configurations such as Yubico OTP, HOTP, static passwords, and Challenge-Response. Pressing the button once activates slot 1; pressing it twice activates slot 2.&lt;/p&gt;


&lt;/div&gt;



&lt;h3&gt;
  
  
  My Recommendation &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ My recommendation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before getting started, I want to share my recommendation on whether it's worth buying hardware to run PicoKeys firmware.&lt;/p&gt;

&lt;p&gt;The answer is: &lt;strong&gt;it depends a lot on YOUR goal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'd mainly recommend PicoKeys if you really need a physical security key supporting &lt;em&gt;FIDO2&lt;/em&gt; or a simple &lt;em&gt;HSM&lt;/em&gt; for personal use, but don't want to pay the price of a &lt;strong&gt;YubiKey 5&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, it's important to consider the total cost of this project. To use PicoKeys properly, we need to apply the recommended security settings. For that, you'll need to purchase a license for the official app, which currently costs around &lt;em&gt;30 euros&lt;/em&gt; (approximately R$ 167.32 at the exchange rate used when I wrote this).&lt;/p&gt;

&lt;p&gt;On top of that, you still need to buy a compatible device (such as an &lt;em&gt;ESP32-S3&lt;/em&gt;), which will usually cost between R$ 80 and R$ 100, although the price may vary considerably depending on the board, seller, and location.&lt;/p&gt;

&lt;p&gt;If you don't want to pay for the app, you can still do everything manually, but then you need to put in the effort to configure the board and keep it up to date.&lt;/p&gt;

&lt;p&gt;If your goal is &lt;em&gt;to study and better understand how technologies such as FIDO/FIDO2, HSM, and Smart Cards work&lt;/em&gt;, I consider PicoKeys a very good option, especially because the firmware is open source. It lets you experiment with FIDO, HSM, and Smart Cards at a relatively low cost compared with a YubiKey.&lt;/p&gt;

&lt;p&gt;However, if you intend to use the key in a real-world environment to protect important accounts, credentials, or keys, my recommendation changes: you need a &lt;em&gt;YubiKey&lt;/em&gt; or another more serious security device.&lt;/p&gt;

&lt;p&gt;If you still want to use PicoKeys for personal use, you'll need to purchase the official app (or apply all the protections manually) to configure the security features available in the project.&lt;/p&gt;


&lt;/div&gt;


&lt;h3&gt;
  
  
  Introduction to PicoKeys &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;PicoKey&lt;/em&gt; is an open-source project that turns microcontrollers (including some &lt;em&gt;RP2040&lt;/em&gt;- and &lt;em&gt;ESP32-S3&lt;/em&gt;-based boards) into security devices similar to physical authentication keys, such as the YubiKey.&lt;/p&gt;

&lt;p&gt;Depending on the firmware used, the device can provide features such as &lt;em&gt;FIDO2&lt;/em&gt;, &lt;em&gt;HSM&lt;/em&gt;, and &lt;em&gt;OpenPGP &amp;amp; PIV&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The idea is to provide a key similar to a YubiKey: instead of storing certain credentials or private keys directly on the computer, they can be protected inside the physical device. The main difference is that &lt;em&gt;PicoKey&lt;/em&gt; is an open-source project.&lt;/p&gt;

&lt;p&gt;Because PicoKey uses generic hardware and open-source firmware, it offers plenty of flexibility, but it also requires care. Unlike a YubiKey, whose hardware, firmware, and manufacturing chain are controlled by the manufacturer, PicoKey's security also depends in part on the microcontroller used, the firmware configuration, and the protections enabled on the chip itself.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ Firefox compatibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During my tests, Pico FIDO worked correctly in Google Chrome, including requesting the PIN, but it did not work in Firefox.&lt;/p&gt;

&lt;p&gt;I found earlier reports in the Pico FIDO project describing specific incompatibilities with Firefox on Linux. According to those reports, Firefox uses the &lt;code&gt;authenticator-rs&lt;/code&gt; library for CTAP/FIDO communication, and some responses generated by Pico FIDO had already caused parsing issues in that implementation.&lt;/p&gt;

&lt;p&gt;However, after manually compiling the firmware and changing the VID/PID to YubiKey, it started working in Firefox without issues.&lt;/p&gt;

&lt;p&gt;It's worth making clear that the YubiKey doesn't have this problem.&lt;/p&gt;


&lt;/div&gt;


&lt;h4&gt;
  
  
  Community vs Enterprise &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;PicoKey has two main licensing options: &lt;em&gt;Community&lt;/em&gt; and &lt;em&gt;Enterprise&lt;/em&gt;. The &lt;em&gt;Community&lt;/em&gt; version is free and distributed under the &lt;em&gt;AGPLv3&lt;/em&gt; license. It is mainly intended for personal use, studies, labs, testing, and prototype development.&lt;/p&gt;

&lt;p&gt;We can download the firmware, study the source code, modify it, and use it normally. However, there's a catch: because of the AGPLv3 license, certain obligations apply if the firmware is modified and redistributed or used as part of certain network services.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Enterprise&lt;/em&gt; version uses a proprietary commercial license and is aimed at production environments, businesses, manufacturers, and deployments involving multiple users or devices. Apart from the licensing difference, Enterprise customers may receive &lt;a href="https://www.picokeys.com/pico-hsm/" rel="noopener noreferrer"&gt;additional features&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For personal use of PicoKey as &lt;em&gt;FIDO2, HSM, or OpenPGP&lt;/em&gt;, the Community version is normally sufficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  PicoKey App &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;em&gt;PicoKey App&lt;/em&gt; is the official application for configuring, managing, and updating devices in the PicoKeys ecosystem, including &lt;em&gt;Pico FIDO&lt;/em&gt;, &lt;em&gt;Pico HSM&lt;/em&gt;, and &lt;em&gt;Pico OpenPGP&lt;/em&gt;. It lets us view device information, update firmware, configure board-specific settings, manage credentials, and enable security features such as &lt;em&gt;Secure Boot&lt;/em&gt; and &lt;em&gt;Secure Lock&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The app is also responsible for a process called &lt;em&gt;commissioning&lt;/em&gt;, which essentially adapts a generic firmware image to the specific hardware on which it is running. This is necessary because the same firmware can run on several different boards, but some characteristics depend on the hardware used, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPIO used by the button;&lt;/li&gt;
&lt;li&gt;LED and indicator behavior;&lt;/li&gt;
&lt;li&gt;board identification;&lt;/li&gt;
&lt;li&gt;USB VID/PID;&lt;/li&gt;
&lt;li&gt;device-specific settings;&lt;/li&gt;
&lt;li&gt;certain security settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The documentation explains that this information cannot be set generically during firmware compilation and must be configured later on the device.&lt;/p&gt;

&lt;p&gt;Among the PicoKey App features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update firmware;&lt;/li&gt;
&lt;li&gt;configure VID/PID;&lt;/li&gt;
&lt;li&gt;configure LED and button;&lt;/li&gt;
&lt;li&gt;manage passkeys;&lt;/li&gt;
&lt;li&gt;manage OATH and OTP slots;&lt;/li&gt;
&lt;li&gt;manage Pico HSM keys;&lt;/li&gt;
&lt;li&gt;configure PIN and SO-PIN;&lt;/li&gt;
&lt;li&gt;configure Secure Boot;&lt;/li&gt;
&lt;li&gt;configure Secure Lock;&lt;/li&gt;
&lt;li&gt;perform a factory reset;&lt;/li&gt;
&lt;li&gt;manage some OpenPGP and PIV features.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ The PicoKey App is not open source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although the PicoKeys firmware projects remain open source, the &lt;em&gt;PicoKey App is proprietary&lt;/em&gt; and its source code is not publicly available.&lt;/p&gt;

&lt;p&gt;In addition, &lt;strong&gt;&lt;em&gt;the app requires a paid license per device&lt;/em&gt;&lt;/strong&gt;. There are currently two licenses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1 device:&lt;/strong&gt; €29.49;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 devices:&lt;/strong&gt; €49.49.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The good news is that the licenses are lifetime licenses.&lt;/p&gt;


&lt;/div&gt;


&lt;p&gt;The firmware itself works independently of the app, since its cryptographic features are provided by the firmware rather than the PicoKey App. Even without the app, we'll be able to use FIDO2, WebAuthn, PIC, OpenPGP, HSM, SSH, and so on.&lt;/p&gt;

&lt;p&gt;Unfortunately, the problem arises during initial setup and when managing certain hardware-specific features. In newer versions, the app has become the tool for performing &lt;em&gt;commissioning&lt;/em&gt; (as mentioned above) and configuring board-specific parameters.&lt;/p&gt;

&lt;p&gt;Therefore, for normal PicoKey use in a lab or for simpler personal projects, you won't need the app as much. There is also an alternative for more "advanced" users: you can compile the firmware manually, including the parameters specific to the board you're using.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/polhenarejos/pico-fido/discussions/237" rel="noopener noreferrer"&gt;developer himself&lt;/a&gt; confirmed that PicoKey can be configured without the app. This means the PicoKey App is not technically required for PicoKey to work, but we lose several of its useful features, such as firmware updates and enabling &lt;em&gt;Secure Boot&lt;/em&gt; and/or &lt;em&gt;Secure Lock&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flashing the Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;em&gt;PicoKeys&lt;/em&gt; firmware can be installed using different methods, depending on the controller. The easiest way is to use a ready-made image provided by the project and flash it directly through the browser, or compile the firmware manually.&lt;/p&gt;

&lt;p&gt;Earlier, when talking about the app, I mentioned &lt;em&gt;commissioning&lt;/em&gt;, but it's worth emphasizing that &lt;em&gt;installing the firmware and performing commissioning are different things&lt;/em&gt;. Installing the firmware means flashing Pico FIDO, Pico HSM, or Pico OpenPGP onto the controller.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Commissioning&lt;/em&gt;, on the other hand, configures and customizes device characteristics such as VID/PID, USB identification, LED/button, some security options, and other settings specific to the controller being used.&lt;/p&gt;

&lt;h5&gt;
  
  
  Flashing Through the Browser — ESP32-S2/S3 &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Boards based on the &lt;em&gt;ESP32-S2 or ESP32-S3&lt;/em&gt; can be flashed directly through a browser. With this method, we don't need to install &lt;code&gt;esptool&lt;/code&gt; or ESP-IDF, or download the image manually. The browser communicates directly with the ESP32 bootloader and flashes the selected firmware.&lt;/p&gt;

&lt;p&gt;To put an ESP32-S3 into flashing mode, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hold the &lt;em&gt;BOOT&lt;/em&gt; button;&lt;/li&gt;
&lt;li&gt;Press the &lt;em&gt;RESET&lt;/em&gt; button;&lt;/li&gt;
&lt;li&gt;Release the &lt;em&gt;RESET&lt;/em&gt; button;&lt;/li&gt;
&lt;li&gt;Release the &lt;em&gt;BOOT&lt;/em&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then open ESP32 Flasher, select the firmware you want, and click &lt;em&gt;Connect&lt;/em&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;💡 DFU or ROM Download Mode?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The PicoKeys documentation calls this procedure &lt;strong&gt;DFU Mode&lt;/strong&gt;, but the term used by the ESP32 manufacturer (Espressif) is &lt;strong&gt;ROM Download Mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This works because the ESP32 has a bootloader programmed into ROM by the manufacturer. When you use the &lt;code&gt;BOOT + RESET&lt;/code&gt; combination, the microcontroller starts that bootloader and waits for new firmware to be flashed. When you flash the firmware, it is written to flash memory, and the ROM starts it.&lt;/p&gt;


&lt;/div&gt;


&lt;p&gt;After flashing, the ESP32 reboots and runs the PicoKeys firmware.&lt;/p&gt;

&lt;h5&gt;
  
  
  VID/PID &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Installing the firmware as explained above uses the parameters provided by the image distributed by the project. However, some third-party tools may not recognize these identifiers. The &lt;a href="https://www.picokeys.com/esp32-flasher/" rel="noopener noreferrer"&gt;ESP32 Flasher&lt;/a&gt; itself recommends configuring VID/PID to improve compatibility with tools such as &lt;code&gt;OpenSC&lt;/code&gt;, &lt;code&gt;PKCS#11&lt;/code&gt;, &lt;code&gt;ykman&lt;/code&gt;, &lt;code&gt;gpg&lt;/code&gt;, and others.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;VID (Vendor ID)&lt;/strong&gt; identifies the USB device manufacturer, while the &lt;strong&gt;PID (Product ID)&lt;/strong&gt; identifies a product associated with that manufacturer. Some applications use these identifiers to find compatible devices, while others can identify them directly from the protocols they implement.&lt;/p&gt;

&lt;p&gt;This means that &lt;strong&gt;changing the VID/PID is not required to use every firmware feature&lt;/strong&gt;, but it may be necessary for certain tools to recognize the device. For example, &lt;em&gt;Pico FIDO uses HID&lt;/em&gt;, a USB interface commonly supported by operating systems and browsers. This allows the device to work as a FIDO2/WebAuthn authenticator without relying on the PicoKey App or on another manufacturer's specific VID/PID.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pico HSM uses CCID&lt;/em&gt;, an interface for communicating with &lt;em&gt;smart card&lt;/em&gt; devices. In this case, recognition may depend on the identifiers supported by the driver or application being used.&lt;/p&gt;

&lt;p&gt;The official documentation describes several ways to change USB identifiers without relying on the PicoKey App, which is the easiest method for changing them, although I've never used it myself.&lt;/p&gt;

&lt;h5&gt;
  
  
  PicoKey App &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;The &lt;a href="https://www.picokeys.com/picokeyapp/" rel="noopener noreferrer"&gt;PicoKey App&lt;/a&gt; lets you select predefined identifiers or enter a VID/PID manually. The settings are applied during &lt;em&gt;commissioning&lt;/em&gt;, without recompiling the firmware. However, as we've already seen, this requires paying for the app.&lt;/p&gt;

&lt;h5&gt;
  
  
  Pico Patcher &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;They also provide &lt;a href="https://www.picokeys.com/pico-patcher/" rel="noopener noreferrer"&gt;Pico Patcher&lt;/a&gt;, a free tool that lets you change the identifiers directly in your browser, but it only works with Raspberry Pi boards. The procedure is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visit the &lt;a href="https://www.picokeys.com/pico-patcher/" rel="noopener noreferrer"&gt;Pico Patcher page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select a firmware file in &lt;code&gt;.uf2&lt;/code&gt; format.&lt;/li&gt;
&lt;li&gt;Choose one of the available manufacturers or enter a custom VID/PID.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Patch&lt;/code&gt; to generate a new firmware image.&lt;/li&gt;
&lt;li&gt;Flash the modified image onto the device.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Available &lt;em&gt;presets&lt;/em&gt; include YubiKey, Nitrokey, GnuPG, and FSIJ. All processing takes place locally in the browser, so the modified firmware is not uploaded to the project's servers.&lt;/p&gt;

&lt;h5&gt;
  
  
  Dynamic Changes Through Pico HSM &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Pico HSM also offers a command-line alternative through the &lt;a href="https://github.com/polhenarejos/pico-hsm/blob/master/tools/pico-hsm-tool.py" rel="noopener noreferrer"&gt;pico-hsm-tool.py&lt;/a&gt; tool. According to the official documentation, you can change the VID/PID dynamically using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./pico-hsm-tool.py phy vidpid 1234:5678
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The values &lt;code&gt;1234:5678&lt;/code&gt; are examples and should be replaced with the desired identifiers. After successfully running the command, you need to restart the device for the new identifiers to take effect. This method lets you change VID/PID without creating a new firmware image.&lt;/p&gt;
&lt;h5&gt;
  
  
  Manual Compilation &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Another method is to configure the USB identifiers directly in the source code or firmware build parameters. The Pico HSM and Pico FIDO repositories document options such as &lt;code&gt;USB_VID&lt;/code&gt;, &lt;code&gt;USB_PID&lt;/code&gt;, and &lt;code&gt;VIDPID&lt;/code&gt; for builds targeting Raspberry Pi Pico boards.&lt;/p&gt;

&lt;p&gt;On ESP32-S3, you need to use &lt;code&gt;-DVIDPID=XXX&lt;/code&gt; to specify the profile you want to apply. The table below lists the available profile names.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;VID:PID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HSM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NitroHSM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20A0:4230&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HSM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YubiHSM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1050:0030&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FIDO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NitroFIDO2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20A0:42B1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FIDO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Nitro3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20A0:42B2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FIDO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Yubikey5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1050:0407&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FIDO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YubikeyNeo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1050:0116&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PGP/OpenPGP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NitroStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20A0:4211&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PGP/OpenPGP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NitroPro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20A0:4108&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PGP/OpenPGP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Gnuk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;234B:0000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PGP/OpenPGP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GnuPG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1209:2440&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Using an ESP32-S3 &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I bought a &lt;em&gt;XIAO ESP32-S3&lt;/em&gt; because the PicoKeys project itself recommends it, especially for security reasons. The main advantage of this board is that it has security features implemented directly in hardware, improving protection for both the firmware and the keys stored on it.&lt;/p&gt;

&lt;p&gt;One of its main features is &lt;em&gt;Secure Boot v2&lt;/em&gt;. It cryptographically verifies the bootloader and firmware before executing them. Once configured, an attacker cannot simply flash modified firmware onto the device to try to extract its keys. Only firmware signed with an authorized key will be executed.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Be careful with Secure Boot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Be very careful when using &lt;em&gt;Secure Boot v2&lt;/em&gt;. Once PicoKeys enables it, the board will only run PicoKeys firmware with a valid signature recognized by the device.&lt;/p&gt;

&lt;p&gt;This means the board will only work with firmware signed by PicoKeys and will not run other firmware. Even resetting the board won't let you install other firmware.&lt;/p&gt;

&lt;p&gt;This happens because &lt;em&gt;Secure Boot&lt;/em&gt; programs a reference to the accepted public key into an &lt;em&gt;eFuse&lt;/em&gt;. As a result, any new bootloader or firmware must be signed with a key corresponding to that reference. If you flash firmware that isn't signed with the private key corresponding to the public key in the &lt;em&gt;eFuse&lt;/em&gt;, the ESP32-S3 will refuse to run it.&lt;/p&gt;

&lt;p&gt;So only enable Secure Boot when you're sure you won't use the board for any other purpose.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;Another important feature of this board is &lt;em&gt;Flash Encryption&lt;/em&gt;. It has two operating modes. Under the default configuration, the ESP32-S3 generates an XTS-AES key during its first boot and stores it in the eFuses (this mode is known as &lt;em&gt;Development&lt;/em&gt;), protected against reading. The problem is that, in Release mode, a different method is required to flash new firmware: it must be signed and then encrypted with the board's key before flashing.&lt;/p&gt;

&lt;p&gt;You can also generate the &lt;em&gt;Flash Encryption&lt;/em&gt; key on your computer, write it to the eFuses before enabling encryption, and keep it on the computer to prepare firmware before flashing (this mode is known as &lt;em&gt;Release&lt;/em&gt;). The advantage is that we can keep flashing firmware using ROM Download Mode, but we must send an already encrypted image. To do that, we need to use &lt;code&gt;--no-stub&lt;/code&gt; in Secure Download Mode.&lt;/p&gt;

&lt;p&gt;Because Secure Boot also uses eFuses, it's worth clarifying that it uses a different area from &lt;em&gt;Flash Encryption&lt;/em&gt;. With &lt;em&gt;Secure Boot&lt;/em&gt;, the &lt;em&gt;eFuses&lt;/em&gt; store a reference to the key used to verify firmware signatures. With &lt;em&gt;Flash Encryption&lt;/em&gt;, they store the key used to encrypt flash memory data.&lt;/p&gt;

&lt;p&gt;When PicoKey uses an eFuse area to store cryptographic data, that area is write-protected to prevent changes. Also, eFuses are permanent: once programmed, their contents cannot simply be deleted or restored as they can in flash memory.&lt;/p&gt;

&lt;p&gt;Another useful feature is &lt;em&gt;Secure Lock&lt;/em&gt;. It programs permanent locks into eFuses to prevent changes to certain security settings. In theory, that is what Secure Lock does, but in my tests, several protections were applied simply by enabling &lt;em&gt;Secure Boot&lt;/em&gt;, including many associated with &lt;em&gt;Secure Lock&lt;/em&gt;. JTAG, for example, was permanently disabled, and ROM Download entered Secure Download Mode, which restricts available commands. Therefore, even without applying &lt;em&gt;Secure Lock&lt;/em&gt;, these protections remain active after enabling Secure Boot. Secure Lock does a few other things, but I see those additional protections as desirable rather than essential; not having them doesn't invalidate using the device.&lt;/p&gt;

&lt;p&gt;The ESP32-S3 also has hardware cryptographic accelerators for AES and SHA, along with HMAC, RSA Digital Signature (RSA_DS), and RNG peripherals. The HMAC peripheral can use a key stored in eFuses without exposing it to software. RSA_DS can also use HMAC to protect RSA private key parameters and perform signatures without exposing the key to firmware.&lt;/p&gt;

&lt;p&gt;In Pico HSM, the SDK also enables the internal SAR ADC entropy source and uses the hardware RNG to obtain random data. Physical noise from the chip is mixed into the RNG, helping generate keys and other random values. This source is internal to the ESP32-S3, so there's no need to connect an antenna or any other component.&lt;/p&gt;

&lt;p&gt;With this entropy source enabled, the ESP32-S3 hardware RNG uses physical noise from the SAR ADC to generate truly random numbers instead of relying only on a pseudorandom sequence. The &lt;em&gt;SAR ADC&lt;/em&gt; stands for &lt;em&gt;Successive Approximation Register Analog-to-Digital Converter&lt;/em&gt;. It is a converter that measures an analog voltage and turns it into a digital value. On the ESP32-S3, the chip's natural electrical noise slightly changes these measurements. With the entropy source enabled, the hardware RNG uses and mixes these variations to produce random numbers. The ADC does not generate the numbers on its own; it provides a physical entropy source for the RNG.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That's what I understood about the SAR ADC, but I should make it clear that this is outside my area of study, so I'm sharing my understanding 😅.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Be careful with Secure Boot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've already warned about the care needed when enabling &lt;em&gt;Secure Boot&lt;/em&gt;, but it's worth repeating: once enabled, &lt;strong&gt;Secure Boot&lt;/strong&gt; cannot be disabled.&lt;/p&gt;

&lt;p&gt;Once this protection is enabled by PicoKeys firmware, the board will only accept PicoKeys firmware or firmware you sign yourself (if you compiled it manually). If you're using the app, make sure you don't plan to reuse this board for other projects or firmware later, because that won't be possible.&lt;/p&gt;


&lt;/div&gt;


&lt;h4&gt;
  
  
  A Microcontroller Is Not a Secure Element &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ A microcontroller is not a Secure Element&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I want to make it very clear that, as interesting and functional as this project is, even with an &lt;em&gt;Enterprise&lt;/em&gt; version and the many security features of the ESP32-S3, this &lt;strong&gt;does not make it equivalent to a YubiKey or a certified enterprise HSM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The ESP32-S3 is a &lt;em&gt;microcontroller&lt;/em&gt; designed for a wide range of projects, from very simple ones to more advanced applications. However, that doesn't make the ESP32-S3 a &lt;em&gt;Secure Element&lt;/em&gt;. It wasn't specifically designed to withstand advanced physical attacks like a YubiKey.&lt;/p&gt;

&lt;p&gt;This is also explained in the &lt;a href="https://docs.picokeys.com/security/threat-model/" rel="noopener noreferrer"&gt;PicoKeys project documentation&lt;/a&gt;. Attacks such as &lt;em&gt;decapping, microprobing, advanced fault injection&lt;/em&gt;, and other forms of laboratory physical analysis are outside the project's threat model.&lt;/p&gt;


&lt;/div&gt;


&lt;h5&gt;
  
  
  Flashing FIDO2 Through the Browser &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;I'll do everything online using the browser-based tools provided by the project. The official project recommends using &lt;em&gt;ESP32 Flasher online&lt;/em&gt;. The official installer is available at &lt;a href="https://www.picokeys.com/esp32-flasher/" rel="noopener noreferrer"&gt;https://www.picokeys.com/esp32-flasher/&lt;/a&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Perform the entire process using Google Chrome or, if you want to use Firefox, make sure it isn't the Snap version.&lt;/p&gt;


&lt;/div&gt;


&lt;p&gt;Before flashing the firmware, we need to put the &lt;em&gt;XIAO ESP32-S3&lt;/em&gt; into the boot mode used for programming. PicoKeys calls this &lt;em&gt;DFU Mode&lt;/em&gt;. With the board connected to the computer, do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Press and hold the &lt;strong&gt;BOOT (B)&lt;/strong&gt; button;&lt;/li&gt;
&lt;li&gt;Press and release the &lt;strong&gt;RESET (R)&lt;/strong&gt; button;&lt;/li&gt;
&lt;li&gt;Release the &lt;strong&gt;BOOT (B)&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The board will enter a mode where it waits to be flashed. With the ESP32-S3 in DFU Mode, open the official installer at &lt;a href="https://www.picokeys.com/esp32-flasher/" rel="noopener noreferrer"&gt;https://www.picokeys.com/esp32-flasher/&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Pico Fido&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Select the device corresponding to the ESP32-S3;&lt;/li&gt;
&lt;li&gt;Confirm the connection;&lt;/li&gt;
&lt;li&gt;Start flashing;&lt;/li&gt;
&lt;li&gt;Wait until the process is completely finished before disconnecting the board.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The website itself downloads and flashes the firmware required by the ESP32-S3. After flashing, disconnect and reconnect the board. It will boot with &lt;em&gt;Pico FIDO&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now let's check whether Linux specifically recognizes the FIDO2 interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the tools:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; fido2-tools

&lt;span class="c"&gt;# List the devices:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;fido2-token &lt;span class="nt"&gt;-L&lt;/span&gt;
/dev/hidraw17: &lt;span class="nv"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x2e8a, &lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x10fe &lt;span class="o"&gt;(&lt;/span&gt;Pol Henarejos Pico Key&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# List our FIDO device's capabilities:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;fido2-token &lt;span class="nt"&gt;-I&lt;/span&gt; /dev/hidraw17
proto: 0x02
major: 0x08
minor: 0x00
build: 0x00
caps: 0x05 &lt;span class="o"&gt;(&lt;/span&gt;wink, cbor, msg&lt;span class="o"&gt;)&lt;/span&gt;
version strings: U2F_V2, FIDO_2_0, FIDO_2_1, FIDO_2_2, FIDO_2_3
extension strings: uvm, credBlob, credProtect, hmac-secret, largeBlobKey, minPinLength, hmac-secret-mc, thirdPartyPayment
algorithms: es256 &lt;span class="o"&gt;(&lt;/span&gt;public-key&lt;span class="o"&gt;)&lt;/span&gt;, es384 &lt;span class="o"&gt;(&lt;/span&gt;public-key&lt;span class="o"&gt;)&lt;/span&gt;, unknown &lt;span class="o"&gt;(&lt;/span&gt;public-key&lt;span class="o"&gt;)&lt;/span&gt;
aaguid: 89fb94b706c936739b7e30526d968145
options: rk, noalwaysUv, credMgmt, authnrCfg, noclientPin, largeBlobs, perCredMgmtRO, pinUvAuthToken, setMinPINLength, nomakeCredUvNotRqd
fwversion: 0x800
maxmsgsiz: 1024
maxcredcntlst: 16
maxcredlen: 1024
maxlargeblob: 2048
maxrpids &lt;span class="k"&gt;in &lt;/span&gt;minpinlen: 120
minpinlen: 4
pin protocols: 1, 2
pin retries: 8
pin change required: &lt;span class="nb"&gt;false
&lt;/span&gt;uv retries: undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h6&gt;
  
  
  Configuring a PIN &lt;a&gt;&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;We already discussed PINs when I explained the YubiKey, specifically FIDO authentication. It can require only the physical presence of the key and a touch, or the key plus additional user verification such as a PIN or biometrics.&lt;/p&gt;

&lt;p&gt;Basically, the PIN confirms that the person using the physical key is authorized to access certain functions, such as passkeys stored on the device and management operations.&lt;/p&gt;

&lt;p&gt;A PIN is optional for some operations, so you can use Pico FIDO without one. However, some services and features may require user verification, making a PIN necessary.&lt;/p&gt;

&lt;p&gt;To configure a PIN, we can use &lt;code&gt;fido2-token&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fido2-token &lt;span class="nt"&gt;-S&lt;/span&gt; /dev/hidraw17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Another way to configure the PIN is through the &lt;em&gt;PicoKey App&lt;/em&gt;. The app lets you set the PIN and adjust related policies, such as minimum length and complexity.&lt;/p&gt;

&lt;p&gt;To change the PIN:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fido2-token &lt;span class="nt"&gt;-C&lt;/span&gt; /dev/hidraw17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To remove the PIN, you need to reset the authenticator. Be careful, though: a reset does not delete just the PIN. It also deletes credentials/passkeys stored on the device and restores the FIDO state.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fido2-token &lt;span class="nt"&gt;-R&lt;/span&gt; /dev/hidraw17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h6&gt;
  
  
  Listing and Deleting Configured Passkeys &lt;a&gt;&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;We can list resident passkeys stored on the key, but only those that are &lt;em&gt;discoverable&lt;/em&gt;. We can also delete them individually using &lt;code&gt;fido2-token&lt;/code&gt;. A &lt;em&gt;discoverable credential&lt;/em&gt; is a FIDO2 credential stored on the physical key that the key can find without the website first specifying which credential to use. This enables passkey/passwordless scenarios, in which you can sign in without first entering a username and password.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;non-discoverable credential&lt;/em&gt; works differently. The private key is still protected inside the physical device, but the device doesn't keep a searchable entry for that login. In this case, the server must send a &lt;em&gt;Credential ID&lt;/em&gt; to specify which credential to use.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;discoverable passkey&lt;/em&gt; needs to keep a persistent record inside the authenticator with enough data to find that account later. The problem is that this consumes the key's internal storage, so it's always a good idea to delete credentials you no longer use.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;non-discoverable credential&lt;/em&gt; usually takes up much less, or almost no, space on the key. This is because the server stores the &lt;em&gt;Credential ID&lt;/em&gt; and sends it back during login. The key derives or retrieves the necessary material from that identifier without having to store one entry per website.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Bug when deleting passkeys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a bug when deleting resident passkeys from the key, and it was still present as of October 6, 2026. Apparently, it's a bug related to &lt;code&gt;fido2-token/libfido2&lt;/code&gt;, but we can delete them without problems using Python's &lt;code&gt;fido2&lt;/code&gt; library. I'll include the code I used at the end of this section.&lt;/p&gt;


&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check which /dev/hidrawX belongs to the key:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;fido2-token &lt;span class="nt"&gt;-L&lt;/span&gt;
/dev/hidraw3: &lt;span class="nv"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x2e8a, &lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x10fe &lt;span class="o"&gt;(&lt;/span&gt;Pol Henarejos Pico Key&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# List the registered discoverable passkeys:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fido2-token &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /dev/hidraw3
Enter PIN &lt;span class="k"&gt;for&lt;/span&gt; /dev/hidraw3: 
00: &lt;span class="nv"&gt;asdasdasdasdafU3lMtcdsffdsdHLsdfsffsOOgd459&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; teste.com

&lt;span class="c"&gt;# To delete one, the command should be:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fido2-token &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'asdasdasdasdafU3lMtcdsffdsdHLsdfsffsOOgd459='&lt;/span&gt; /dev/hidraw3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;💡 Python script for fido2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fido2.hid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CtapHidDevice&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fido2.ctap2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Ctap2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ClientPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CredentialManagement&lt;/span&gt;


&lt;span class="n"&gt;devices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CtapHidDevice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_devices&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No FIDO devices found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Devices found:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Enter the device ID: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ctap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ctap2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#print("\nSupported CTAP versions:", ctap.info.versions)
#print("Options:", ctap.info.options)
&lt;/span&gt;
&lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getpass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Enter the FIDO PIN: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;client_pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ClientPin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_pin_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ClientPin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PERMISSION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREDENTIAL_MGMT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;credman&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CredentialManagement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ctap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Discoverable credentials:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;all_credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;credman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerate_rps&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;rp_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CredentialManagement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESULT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;rp_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CredentialManagement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESULT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RP_ID_HASH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rp_info&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerate_creds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rp_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CredentialManagement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESULT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREDENTIAL_ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CredentialManagement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESULT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  [&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      User: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Credential ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;all_credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rp_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;all_credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No discoverable credentials found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter the number of the credential to delete or press ENTER to exit: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid index.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_credentials&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Index out of range.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;all_credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;You selected:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RP:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Credential ID:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;confirm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Type APAGAR to confirm: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;confirm&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APAGAR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cancelled.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;credman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_cred&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error deleting credential:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Credential deleted successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;p&gt;The table below summarizes what we can do with the &lt;code&gt;fido2-token&lt;/code&gt; command:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App Feature&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Detect Pico FIDO&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -L&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View capabilities/AAGUID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -I &amp;lt;device&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set PIN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -S &amp;lt;device&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change PIN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -C &amp;lt;device&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reset FIDO&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -R &amp;lt;device&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List passkeys&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fido2-token -L -r &amp;lt;device&amp;gt;&lt;/code&gt; + &lt;code&gt;-L -k&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View passkey metadata&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -I -c &amp;lt;device&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete passkeys&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -D -i ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Change credential name/display name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -S -c ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read/write/delete &lt;code&gt;largeBlob&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -G/-S/-D -b ...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable &lt;code&gt;Always UV&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -S -u&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable &lt;code&gt;Always UV&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -D -u&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set minimum PIN length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-token -S -l N&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create credentials manually&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-cred&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test authentication/assertion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fido2-assert&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;↑ Back to the Table of Contents&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2 — Compiling PicoKeys Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compiling the Firmware Without Secure Boot &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First, I'll show how to compile &lt;em&gt;without enabling Secure Boot&lt;/em&gt;, since this feature is irreversible. This way, you can test and use the board, and reflash it more easily if needed. Use a board as a lab device: test and learn before taking irreversible steps. Once everything is working and you feel comfortable and confident, we'll enable the security features.&lt;/p&gt;

&lt;p&gt;I'll continue using the &lt;em&gt;XIAO ESP32-S3&lt;/em&gt; for the procedures described here. I want to clarify that I won't specify a tag when cloning the repositories, since the installation procedure has changed and new improvements have been added to each project's main branch. Below are the branches I used for each project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### FIDO
Hash: ade770281dd4fd8c627add34ed078f56ec40a07b
tag: nightly-main and nightly-development

### HSM
Hash: 5d6a32c81b2367fb83e5320400f61217d2bd0d2f 
tag: nightly-master and nightly-development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I submitted two fixes for security issues in the FIDO project, so they may soon be merged into the main project. In that case, you will still be able to use the hashes above, but you'll need to apply the fixes manually until they are incorporated.&lt;/p&gt;

&lt;p&gt;I just want you to understand that using the main branch changes the way the firmware is compiled, since new features have been added and the build process is even easier. That's why I didn't specify a tag when cloning the repositories.&lt;/p&gt;
&lt;h4&gt;
  
  
  Installing Dependencies and ESP-IDF &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;I'll install the compilation tools used by ESP-IDF on a virtual machine running Ubuntu 26.04.1:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git wget flex bison gperf python3 python3-pip &lt;span class="se"&gt;\&lt;/span&gt;
  python3-venv cmake ninja-build ccache libffi-dev libssl-dev &lt;span class="se"&gt;\&lt;/span&gt;
  dfu-util libusb-1.0-0 openssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let's create a directory to store and install the ESP-IDF source code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/src/picokeys
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because ESP-IDF uses Git submodules for its components and dependencies, we need to clone with &lt;code&gt;--recursive&lt;/code&gt; to download them. Otherwise, we'd have the main repository but not the complete source code required to compile the firmware. I'll also download tag v5.5.1 specifically, corresponding to version 5.5.1 of the code. Even though this is the latest available version as I write this tutorial, I want to make it very clear which version I'm using so it doesn't get confused with whatever version you might be running.&lt;/p&gt;

&lt;p&gt;Another reason for using v5.5.1 specifically is that the Pico FIDO project's build script uses that exact version. It runs &lt;code&gt;git checkout tags/v5.5.1&lt;/code&gt; before installing the tools and compiling.&lt;/p&gt;

&lt;p&gt;To find out which version to use, check these files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For HSM: &lt;a href="https://github.com/polhenarejos/pico-hsm/blob/master/workflows/autobuild.sh" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-hsm/blob/master/workflows/autobuild.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For FIDO: &lt;a href="https://github.com/polhenarejos/pico-fido/blob/main/workflows/autobuild.sh" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-fido/blob/main/workflows/autobuild.sh&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Search for &lt;code&gt;tags/v&lt;/code&gt;, and you'll see the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"esp32"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/espressif/esp-idf.git
&lt;span class="nb"&gt;cd &lt;/span&gt;esp-idf
git checkout tags/v5.5.1
./install.sh esp32s3
&lt;span class="nb"&gt;.&lt;/span&gt; ./export.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The key line for identifying the version is &lt;code&gt;git checkout tags/v5.5.1&lt;/code&gt;. So let's clone ESP-IDF at exactly version 5.5.1:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v5.5.1 &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/espressif/esp-idf.git esp-idf &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;esp-idf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's install the tools needed to develop and compile firmware for the ESP32-S3:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;IDF_PYTHON_ENV_PATH &lt;span class="p"&gt;;&lt;/span&gt; ./install.sh esp32s3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;IDF_PYTHON_ENV_PATH&lt;/code&gt; variable specifies where the &lt;code&gt;esp-idf&lt;/code&gt; environment is installed. I'm including it in case you have another version already loaded in your environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whenever you open a new terminal, you need to reload the ESP-IDF environment:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/esp-idf

&lt;span class="c"&gt;# Load the ESP-IDF environment:&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; ./export.sh

&lt;span class="c"&gt;# Check the version:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py &lt;span class="nt"&gt;--version&lt;/span&gt;
ESP-IDF v5.5.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Downloading and Compiling Pico FIDO &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;With the ESP-IDF environment loaded, let's clone and compile Pico FIDO:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys 
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/polhenarejos/pico-fido.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;pico-fido
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Removing the automatic notification when connecting the board&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time I connect the board to my computer, Chrome shows a pop-up saying that a Pico Key was detected. Since I don't like this behavior, I'll disable it.&lt;/p&gt;

&lt;p&gt;In Pico FIDO v8.0, the address is stored in the SDK's USB descriptor:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-fido

vim pico-keys-sdk/src/usb/usb_descriptors.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Find &lt;code&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/code&gt;, then change &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;, as shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="n"&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VENDOR_REQUEST_WEBUSB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="n"&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VENDOR_REQUEST_WEBUSB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This parameter is the landing page index. With &lt;code&gt;0&lt;/code&gt;, the firmware no longer advertises a page for Chrome to open, so the automatic notification should disappear.&lt;/p&gt;


&lt;/div&gt;



&lt;h5&gt;
  
  
  Failure to Enforce the alwaysUv Policy &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;The &lt;code&gt;alwaysUv&lt;/code&gt; setting specifies that the authenticator (physical security key) must always require user verification (via PIN or biometrics) before performing authentication operations. However, I found a possible inconsistency in &lt;code&gt;src/fido/cbor_get_assertion.c&lt;/code&gt;: the firmware checks authorization when it is provided, but doesn't explicitly require that authorization on every execution path when &lt;code&gt;alwaysUv&lt;/code&gt; is enabled.&lt;/p&gt;

&lt;p&gt;This could allow certain authentication operations to be performed without user verification, bypassing the configured security policy. To fix this, edit the following file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim src/fido/cbor_get_assertion.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Replace this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;next &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pinUvAuthParam.present &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pinUvAuthParam.len &lt;span class="o"&gt;==&lt;/span&gt; 0 &lt;span class="o"&gt;||&lt;/span&gt; pinUvAuthParam.data &lt;span class="o"&gt;==&lt;/span&gt; NULL&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;check_user_presence&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    CBOR_ERROR&lt;span class="o"&gt;(&lt;/span&gt;CTAP2_ERR_OPERATION_DENIED&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;next &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        bool alwaysUv &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;get_opts&lt;span class="o"&gt;()&lt;/span&gt; &amp;amp; FIDO2_OPT_AUV&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
                        &lt;span class="o"&gt;(&lt;/span&gt;file_has_data&lt;span class="o"&gt;(&lt;/span&gt;ef_pin&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;keydev_unlocked&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;alwaysUv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pinUvAuthParam.present &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            CBOR_ERROR&lt;span class="o"&gt;(&lt;/span&gt;CTAP2_ERR_PUAT_REQUIRED&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pinUvAuthParam.present &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pinUvAuthParam.len &lt;span class="o"&gt;==&lt;/span&gt; 0 &lt;span class="o"&gt;||&lt;/span&gt; pinUvAuthParam.data &lt;span class="o"&gt;==&lt;/span&gt; NULL&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;check_user_presence&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    CBOR_ERROR&lt;span class="o"&gt;(&lt;/span&gt;CTAP2_ERR_OPERATION_DENIED&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ Tests to validate the alwaysUv issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tested the behavior using the original firmware and was able to confirm the issue:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3 fido_alwaysUv.py
Factory reset deletes the PIN, credentials, and FIDO settings.
Keep only the device being tested connected.

A Pico with a YubiKey VID/PID may look identical to a real YubiKey&lt;span class="p"&gt;;&lt;/span&gt; the program cannot physically tell them apart.
With the disposable Pico connected, press Enter. Then disconnect it when the program tells you to:
Waiting &lt;span class="k"&gt;for &lt;/span&gt;disconnection. Disconnect the Pico now and keep it unplugged from USB...

Disconnection detected. Reconnect the Pico now...

Sending factory reset immediately after reconnection...
Factory reset completed.
Creating a &lt;span class="nb"&gt;test &lt;/span&gt;FIDO credential...
Setting a &lt;span class="nb"&gt;test &lt;/span&gt;PIN and enabling alwaysUv...
alwaysUv is enabled.
Testing getAssertion without PIN/UV...

FAILURE: BUG PRESENT — getAssertion succeeded without PIN/UV &lt;span class="k"&gt;while &lt;/span&gt;alwaysUv was enabled.
Running a positive control with PIN/UV...
OK: the authenticated positive control worked and &lt;span class="nb"&gt;set &lt;/span&gt;the UV flag.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After applying the fix above, I ran another test that showed the fix was effective.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3 fido_alwaysUv.py
Factory reset deletes the PIN, credentials, and FIDO settings.
Keep only the device being tested connected.

A Pico with a YubiKey VID/PID may look identical to a real YubiKey&lt;span class="p"&gt;;&lt;/span&gt; the program cannot physically tell them apart.
With the disposable Pico connected, press Enter. Then disconnect it when the program tells you to:
Waiting &lt;span class="k"&gt;for &lt;/span&gt;disconnection. Disconnect the Pico now and keep it unplugged from USB...

Disconnection detected. Reconnect the Pico now...

Sending factory reset immediately after reconnection...
Factory reset completed.
Creating a &lt;span class="nb"&gt;test &lt;/span&gt;FIDO credential...
Setting a &lt;span class="nb"&gt;test &lt;/span&gt;PIN and enabling alwaysUv...
alwaysUv is enabled.
Testing getAssertion without PIN/UV...

OK: getAssertion without a UV token was rejected with PIN_REQUIRED.
Running a positive control with PIN/UV...
OK: the authenticated positive control worked and &lt;span class="nb"&gt;set &lt;/span&gt;the UV flag.
RESULT: PASSED — getAssertion required PIN/UV and the authenticated control worked.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;h5&gt;
  
  
  Memory Validation During U2F Processing &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;I found a possible problem with how Pico FIDO validates the length of data received during U2F operations. Although this is an older protocol, many systems may still use U2F, so it's worth fixing. The firmware uses a length supplied by the client (such as a browser or any other system requesting the operation) without fully checking whether it matches the actual amount of data received.&lt;/p&gt;

&lt;p&gt;This could cause out-of-bounds memory reads, crashes, or unexpected behavior when the device receives malformed messages. Someone with access to the computer connected to Pico FIDO could send crafted USB commands to try to exploit this flaw. To address the problem, edit the following file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim src/fido/cmd_authenticate.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Replace these lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;apdu.nc &amp;lt; CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_WRONG_DATA&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;req-&amp;gt;keyHandleLen &amp;lt; KEY_HANDLE_LEN&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_INCORRECT_PARAMS&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With these:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    const size_t key_handle_offset &lt;span class="o"&gt;=&lt;/span&gt; offsetof&lt;span class="o"&gt;(&lt;/span&gt;CTAP_AUTHENTICATE_REQ, keyHandle&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;apdu.nc &amp;lt; key_handle_offset&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_WRONG_DATA&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;req-&amp;gt;keyHandleLen &amp;lt; KEY_HANDLE_LEN &lt;span class="o"&gt;||&lt;/span&gt;
        req-&amp;gt;keyHandleLen &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; CTAP_MAX_KH_SIZE&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_INCORRECT_PARAMS&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;size_t&lt;span class="o"&gt;)&lt;/span&gt;apdu.nc &amp;lt; key_handle_offset + req-&amp;gt;keyHandleLen&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_WRONG_DATA&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the same file, replace the two lines below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    uint8_t &lt;span class="k"&gt;*&lt;/span&gt;tmp_kh &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;uint8_t &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; calloc&lt;span class="o"&gt;(&lt;/span&gt;1, req-&amp;gt;keyHandleLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    memcpy&lt;span class="o"&gt;(&lt;/span&gt;tmp_kh, req-&amp;gt;keyHandle, req-&amp;gt;keyHandleLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With these:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    uint8_t &lt;span class="k"&gt;*&lt;/span&gt;tmp_kh &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;uint8_t &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; calloc&lt;span class="o"&gt;(&lt;/span&gt;1, req-&amp;gt;keyHandleLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;tmp_kh &lt;span class="o"&gt;==&lt;/span&gt; NULL&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        mbedtls_ecp_keypair_free&lt;span class="o"&gt;(&lt;/span&gt;&amp;amp;key&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;SW_MEMORY_FAILURE&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    memcpy&lt;span class="o"&gt;(&lt;/span&gt;tmp_kh, req-&amp;gt;keyHandle, req-&amp;gt;keyHandleLen&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ Tests to validate the U2F processing issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tested the behavior using the original firmware and confirmed the issue:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3 fido_u2f_memory_test.py
Device detected: Pol Henarejos Pico Key, &lt;span class="nv"&gt;VID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1050, &lt;span class="nv"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0407

Physically confirm that this is the disposable Pico FIDO.
Type TESTAR to &lt;span class="k"&gt;continue&lt;/span&gt;: TESTAR

Truncated key handle &lt;span class="o"&gt;(&lt;/span&gt;expected SW_WRONG_DATA 6700&lt;span class="o"&gt;)&lt;/span&gt;: received 69 85
FAILED: expected 67 00
Oversized key handle &lt;span class="o"&gt;(&lt;/span&gt;expected SW_INCORRECT_PARAMS 6A80&lt;span class="o"&gt;)&lt;/span&gt;: received 69 85
FAILED: expected 6A 80
RESULT: FAILED — check the received status codes and installed firmware.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After applying the fix above, I ran another test that showed the fix was effective.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3 fido_u2f_memory_test.py
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 fido_u2f_memory_test.py
Device detected: Pol Henarejos Pico Key, &lt;span class="nv"&gt;VID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1050, &lt;span class="nv"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0407
Physically confirm that this is the disposable Pico FIDO.
Type TESTAR to &lt;span class="k"&gt;continue&lt;/span&gt;: TESTAR

Truncated key handle &lt;span class="o"&gt;(&lt;/span&gt;expected SW_WRONG_DATA 6700&lt;span class="o"&gt;)&lt;/span&gt;: received 67 00
OK
Oversized key handle &lt;span class="o"&gt;(&lt;/span&gt;expected SW_INCORRECT_PARAMS 6A80&lt;span class="o"&gt;)&lt;/span&gt;: received 6A 80
OK
RESULT: PASSED — both invalid APDUs were rejected.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;h5&gt;
  
  
  Compiling FIDO &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Before compiling, I'd like to mention two security issues I found in the FIDO firmware. Although I've already implemented the fixes and validated the results in my own tests, I also submitted a &lt;a href="https://github.com/polhenarejos/pico-fido/issues/299" rel="noopener noreferrer"&gt;suggestion to the official project&lt;/a&gt; so the developers can review both the fixes and their impact, and potentially include these improvements in the official code in the future.&lt;/p&gt;

&lt;p&gt;The test scripts were generated using AI and reviewed by me. You can &lt;a href="https://gitlab.com/sysnetbr/codes/-/tree/main/picokeys?ref_type=heads" rel="noopener noreferrer"&gt;access the scripts on my GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let's compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If you compiled HSM before FIDO, run:&lt;/span&gt;
idf.py fullclean

&lt;span class="c"&gt;# Set ESP32-S3 as the target microcontroller for firmware compilation:&lt;/span&gt;
idf.py set-target esp32s3

&lt;span class="c"&gt;# Compile the source code using the YubiKey5 VID/PID:&lt;/span&gt;
idf.py &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yubikey5 build

&lt;span class="c"&gt;# To compile using the PicoKeys VID/PID, run the command below:&lt;/span&gt;
idf.py build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Compiling Pico FIDO generates several &lt;code&gt;.bin&lt;/code&gt; files, including the bootloader, partition table, and main firmware. The &lt;code&gt;build/flash_args&lt;/code&gt; file tells us which files to flash and at which flash addresses. The command below combines these files into a single image, which I'm calling &lt;code&gt;pico_fido_esp32-s3.bin&lt;/code&gt;. This step is optional if you plan to flash the device using &lt;code&gt;idf.py flash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, this command is necessary if you want to create a single &lt;code&gt;.bin&lt;/code&gt; file to distribute or flash using another method, such as &lt;code&gt;esptool.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

&lt;span class="c"&gt;# Merge the '.bin' files generated during compilation:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_fido_esp32-s3.bin @flash_args &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Downloading and Compiling Pico HSM &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Use only the v6.6 tag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran some partial tests with Pico HSM and had a lot of trouble getting it recognized at first. From what I've seen, it still isn't fully mature. To get the device recognized by &lt;code&gt;pkcs11-tool&lt;/code&gt; and &lt;code&gt;opensc-tool&lt;/code&gt;, I had to use exactly version 6.6, with commit &lt;code&gt;251b35dd9c4fd929923fc3b192f6793826efeaef&lt;/code&gt;, tagged &lt;code&gt;v6.6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my tests, firmware compiled from the main branch wasn't recognized by these tools. I also had to use &lt;code&gt;libccid&lt;/code&gt; version &lt;strong&gt;1.8.2 or newer&lt;/strong&gt;. The earlier versions I tested didn't recognize the HSM.&lt;/p&gt;

&lt;p&gt;Another important detail: don't use the YubiHSM VID/PID. Compile without specifying a particular profile. The &lt;code&gt;yubihsm-shell&lt;/code&gt; tool doesn't recognize Pico HSM when it is compiled with &lt;code&gt;YubiHSM&lt;/code&gt; because that option only changes the USB VID/PID. It doesn't make the firmware implement the YubiHSM 2 protocol, which &lt;code&gt;yubihsm-shell&lt;/code&gt; uses. Pico HSM communicates over CCID and should be accessed using compatible tools such as &lt;code&gt;opensc-tool&lt;/code&gt; and &lt;code&gt;pkcs11-tool&lt;/code&gt;.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The tutorial described here only works with the Pico HSM main branch&lt;/strong&gt;. I'll keep these build instructions here because a lot has changed since version 6.6, including the way the firmware is compiled, so I believe this pattern will continue with minor adjustments. &lt;strong&gt;&lt;em&gt;But keep in mind that using the main branch will cause the HSM not to be recognized&lt;/em&gt;&lt;/strong&gt; (at least at the time I wrote this tutorial).&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;💡 How to compile version 6.6?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To learn &lt;a href="https://gitlab.com/sysnetbr/codes/-/blob/main/picokeys/en-PicoHSM-v6.6_compile.md?ref_type=heads" rel="noopener noreferrer"&gt;how to compile exactly version 6.6, read this guide&lt;/a&gt;. This covers the basic process. Other customizations, such as Secure Boot and Flash Encryption, can still be applied; you just need to fix the issues so the firmware compiles according to the linked guide.&lt;/p&gt;


&lt;/div&gt;


&lt;p&gt;Pico HSM also uses ESP-IDF targeting ESP32-S3, and we'll use version 5.5.1 here as well, for the same reasons as with FIDO.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/polhenarejos/pico-hsm.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;pico-hsm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Removing the automatic notification when connecting the board&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time I connect the board to my computer, Chrome shows a pop-up saying that a Pico Key was detected. Since I don't like this behavior, I'll disable it.&lt;/p&gt;

&lt;p&gt;In Pico HSM, the address is in the SDK's USB descriptor:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-hsm

vim pico-keys-sdk/src/usb/usb_descriptors.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Find &lt;code&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/code&gt;, then change &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;, as shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="n"&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VENDOR_REQUEST_WEBUSB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="n"&gt;TUD_BOS_WEBUSB_DESCRIPTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VENDOR_REQUEST_WEBUSB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This parameter is the landing page index. With &lt;code&gt;0&lt;/code&gt;, the firmware no longer advertises a page for Chrome to open, so the automatic notification should disappear.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;In the code I tested, two adjustments were required before compilation. These changes make ESP-IDF register the HSM component and find the &lt;code&gt;libcvc&lt;/code&gt; dependency. Edit the &lt;code&gt;CMakeLists.txt&lt;/code&gt; file in the Pico HSM root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim CMakeLists.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside &lt;code&gt;if(ESP_PLATFORM)&lt;/code&gt; (the first block, usually around line 23), define &lt;code&gt;PICOKEYS_COMPONENTS&lt;/code&gt; as &lt;code&gt;hsm&lt;/code&gt; before the first ESP-IDF &lt;code&gt;include(...)&lt;/code&gt;. The original section looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ESP_PLATFORM&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/hsm&lt;span class="o"&gt;)&lt;/span&gt;
    include&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;IDF_PATH&lt;span class="o"&gt;}&lt;/span&gt;/tools/cmake/project.cmake&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It should look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;ESP_PLATFORM&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;PICOKEYS_COMPONENTS hsm&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/hsm&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$ENV{IDF_PATH}/tools/cmake/project.cmake&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;else&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's make ESP-IDF find the &lt;code&gt;libcvc&lt;/code&gt; dependency. Start by editing the Pico Keys SDK importer:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim pico-keys-sdk/picokeys_sdk_import.cmake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside &lt;code&gt;if(ESP_PLATFORM)&lt;/code&gt;, there is a &lt;code&gt;set(EXTRA_COMPONENT_DIRS ...)&lt;/code&gt; definition. Find it (usually around line 73 in the file on the main branch). The full original section looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ESP_PLATFORM&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DEFINED CONFIG_TINYUSB_DESC_CUSTOM_VID&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;USB_VID CONFIG_TINYUSB_DESC_CUSTOM_VID&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DEFINED CONFIG_TINYUSB_DESC_CUSTOM_PID&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;USB_PID CONFIG_TINYUSB_DESC_CUSTOM_PID&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED ENABLE_POWER_ON_RESET&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_POWER_ON_RESET 0&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED ENABLE_PQC&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_PQC 0&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED SDKCONFIG_DEFAULTS AND NOT DEFINED ENV&lt;span class="o"&gt;{&lt;/span&gt;SDKCONFIG_DEFAULTS&lt;span class="o"&gt;})&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;SDKCONFIG_DEFAULTS &lt;span class="s2"&gt;"pico-keys-sdk/config/esp32/sdkconfig.defaults"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;EXTRA_COMPONENT_DIRS
        src/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PICOKEYS_COMPONENTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
        pico-keys-sdk/config/esp32/components/mbedtls
        pico-keys-sdk/config/esp32/components/pico-keys-sdk
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;COMPONENTS &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PICOKEYS_COMPONENTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; pico-keys-sdk&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;USB_ITF_HID&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/tinycbor
        &lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND COMPONENTS tinycbor&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_PQC&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/mlkem512
            pico-keys-sdk/config/esp32/components/mlkem768
            pico-keys-sdk/config/esp32/components/mlkem1024
        &lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
endif&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After &lt;code&gt;set(EXTRA_COMPONENT_DIRS ...)&lt;/code&gt;, add:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_LIBCVC&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/libcvc
        &lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The new full section looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ESP_PLATFORM&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DEFINED CONFIG_TINYUSB_DESC_CUSTOM_VID&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;USB_VID CONFIG_TINYUSB_DESC_CUSTOM_VID&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DEFINED CONFIG_TINYUSB_DESC_CUSTOM_PID&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;USB_PID CONFIG_TINYUSB_DESC_CUSTOM_PID&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED ENABLE_POWER_ON_RESET&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_POWER_ON_RESET 0&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED ENABLE_PQC&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_PQC 0&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NOT DEFINED SDKCONFIG_DEFAULTS AND NOT DEFINED ENV&lt;span class="o"&gt;{&lt;/span&gt;SDKCONFIG_DEFAULTS&lt;span class="o"&gt;})&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;SDKCONFIG_DEFAULTS &lt;span class="s2"&gt;"pico-keys-sdk/config/esp32/sdkconfig.defaults"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;EXTRA_COMPONENT_DIRS
        src/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PICOKEYS_COMPONENTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
        pico-keys-sdk/config/esp32/components/mbedtls
        pico-keys-sdk/config/esp32/components/pico-keys-sdk
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_LIBCVC&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/libcvc
        &lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;COMPONENTS &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PICOKEYS_COMPONENTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; pico-keys-sdk&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;USB_ITF_HID&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/tinycbor
        &lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND COMPONENTS tinycbor&lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ENABLE_PQC&lt;span class="o"&gt;)&lt;/span&gt;
        list&lt;span class="o"&gt;(&lt;/span&gt;APPEND EXTRA_COMPONENT_DIRS
            pico-keys-sdk/config/esp32/components/mlkem512
            pico-keys-sdk/config/esp32/components/mlkem768
            pico-keys-sdk/config/esp32/components/mlkem1024
        &lt;span class="o"&gt;)&lt;/span&gt;
    endif&lt;span class="o"&gt;()&lt;/span&gt;
endif&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This adjustment is necessary because the HSM component declares &lt;code&gt;libcvc&lt;/code&gt; in &lt;code&gt;REQUIRES&lt;/code&gt;, but the SDK importer does not include (at the time of writing) that component's directory in the list searched by ESP-IDF.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;💡 An issue was opened for the problem above&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I opened an &lt;a href="https://github.com/polhenarejos/pico-fido/issues/302" rel="noopener noreferrer"&gt;issue&lt;/a&gt; asking whether the change above could be incorporated into the main project. If accepted, this manual step may soon become unnecessary.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;Now let's disable &lt;code&gt;CONFIG_ESP_COREDUMP_ENABLE_TO_UART&lt;/code&gt;. With this option enabled, someone could trigger a failure on the ESP32-S3, causing a &lt;em&gt;memory dump&lt;/em&gt; to be sent over UART, a serial communication interface used to send and receive data. The problem is that this dump could contain sensitive information such as cryptographic keys, PINs, and other data stored in RAM.&lt;/p&gt;

&lt;p&gt;This feature also applies to FIDO, but it isn't present in the main branch, which is why I didn't document how to disable it there.&lt;/p&gt;

&lt;p&gt;This feature could be exploited in an &lt;em&gt;Information Disclosure&lt;/em&gt; attack, allowing someone with UART access to obtain sensitive data from the device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim sdkconfig.defaults
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Change the line by commenting it out. This is how it looks &lt;strong&gt;before disabling it&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before disabling:&lt;/span&gt;
&lt;span class="nv"&gt;CONFIG_ESP_COREDUMP_ENABLE_TO_UART&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is how it should look after disabling it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's compile Pico HSM:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/esp-idf

&lt;span class="c"&gt;# Load the ESP-IDF environment:&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; ./export.sh

&lt;span class="c"&gt;# Enter the Pico HSM project directory:&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-hsm

&lt;span class="c"&gt;# If you compiled FIDO before HSM, run:&lt;/span&gt;
idf.py fullclean

&lt;span class="c"&gt;# Set ESP32-S3 as the target microcontroller for firmware compilation:&lt;/span&gt;
idf.py set-target esp32s3

&lt;span class="c"&gt;# Compile the source code using the YubiHSM VID/PID:&lt;/span&gt;
idf.py &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YubiHSM build

&lt;span class="c"&gt;# To compile using the PicoKeys VID/PID, run the command below:&lt;/span&gt;
idf.py build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As with FIDO, compiling HSM generates several &lt;code&gt;.bin&lt;/code&gt; files. &lt;code&gt;flash_args&lt;/code&gt; specifies which files to flash and at which addresses. &lt;code&gt;merge_bin&lt;/code&gt; combines these files into one, but this is optional; we can flash the board with &lt;code&gt;idf.py flash&lt;/code&gt;. However, this command is necessary if you want a single &lt;code&gt;.bin&lt;/code&gt; file for distribution or for flashing through another method, such as &lt;code&gt;esptool.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

&lt;span class="c"&gt;# Merge the '.bin' files generated during compilation:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_hsm_esp32-s3.bin @flash_args &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Flashing the Firmware Without Secure Boot &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Let's flash the firmware without Secure Boot. First, put the ESP32-S3 into &lt;em&gt;Download Mode&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hold &lt;strong&gt;BOOT&lt;/strong&gt;, press and release &lt;strong&gt;RESET&lt;/strong&gt;, then release &lt;strong&gt;BOOT&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, run this command to see which port the board appeared on:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /dev/ttyACM&lt;span class="k"&gt;*&lt;/span&gt; /dev/ttyUSB&lt;span class="k"&gt;*&lt;/span&gt;
/dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In my case it was &lt;code&gt;/dev/ttyACM0&lt;/code&gt;. I'll use this name in the command below to flash the firmware. If yours is different, use the port shown on your system, but make sure it belongs to the correct ESP32-S3 board you want to flash.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For FIDO:&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-fido &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
    write_flash 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
    release/pico_fido_esp32-s3.bin

&lt;span class="c"&gt;# For HSM:&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-hsm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
    write_flash 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
    release/pico_hsm_esp32-s3.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Compiling the Firmware with Secure Boot &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The ESP32-S3 includes security features that help protect the device against unauthorized firmware changes. &lt;strong&gt;Secure Boot&lt;/strong&gt; verifies the firmware's digital signature before executing it, allowing only images signed with an authorized key to boot. This prevents someone from replacing the firmware stored in flash memory, since the microcontroller will refuse to execute a new image if its signature is invalid.&lt;/p&gt;

&lt;p&gt;Simply enabling &lt;strong&gt;Secure Boot v2&lt;/strong&gt; on the ESP32-S3 automatically applies some security protections, even without using &lt;em&gt;Secure Lock&lt;/em&gt;. In my case, the two additional Secure Boot digest slots were revoked, the JTAG interface was permanently disabled, and the device switched to Secure Download Mode.&lt;/p&gt;

&lt;p&gt;Revoking the additional digest slots prevents someone from registering new signing keys and then executing firmware that the board should not originally accept. This closes off one possible way to bypass Secure Boot protection.&lt;/p&gt;

&lt;p&gt;With the &lt;em&gt;JTAG interface permanently disabled&lt;/em&gt;, someone can no longer use that interface to debug the processor, inspect memory, or interfere with firmware execution. This makes attacks requiring physical access more difficult.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Secure Download Mode&lt;/em&gt; restricts the commands available in download mode, preventing operations such as directly reading eFuses and reducing opportunities to manipulate the device through that interface. This makes it harder to use download mode to circumvent configured protections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All these protections were enabled automatically when I enabled Secure Boot&lt;/strong&gt;. However, this behavior may change in the future, which is why I included the commits used at the beginning of this page.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Attention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enabling Secure Boot involves irreversible changes to the microcontroller. An incorrect configuration can permanently prevent the board from booting.&lt;/p&gt;

&lt;p&gt;That's why we'll prepare the keys, configure firmware signing, and verify that everything works before dealing with eFuse programming.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Do I Need to Enable Flash Encryption? &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;In the code I analyzed, both Pico FIDO and Pico HSM use containers with authenticated encryption to protect sensitive objects such as private keys and credentials. This protects the data at the firmware level, but it does not mean the entire flash memory is encrypted.&lt;/p&gt;

&lt;p&gt;The ESP32-S3's own &lt;em&gt;Flash Encryption&lt;/em&gt; feature adds another layer: it encrypts the partitions configured for it, making it harder to read the firmware and other data by directly accessing the flash. This protection complements the container encryption, but does not prevent data access while firmware is running on the device.&lt;/p&gt;

&lt;p&gt;In theory, the sensitive data stored by Pico FIDO and Pico HSM in these containers is encrypted before being written to flash. Therefore, directly reading the flash should not reveal that data without the key. Since the data is encrypted and the keys used to decrypt it are stored in a protected form in flash, a flash dump alone should not reveal the keys in plaintext.&lt;/p&gt;

&lt;p&gt;Flash Encryption provides additional protection because it also encrypts the configured partitions. This makes analyzing flash contents more difficult for someone with direct access to the memory.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Read before continuing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Flash Encryption&lt;/em&gt; process must take place before enabling &lt;em&gt;Secure Boot&lt;/em&gt;, because the key used to encrypt the flash must be programmed into the eFuses and made unreadable. When Secure Boot setup is completed, ESP-IDF protects the control that would otherwise allow this read protection to be applied later. Because eFuses are permanent, I need to program and protect the Flash Encryption key first and only then enable Secure Boot.&lt;/p&gt;

&lt;p&gt;If you already have a key with Secure Boot configured, you cannot enable Flash Encryption on that board; you'll need to do it on a different board.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;if you want this additional protection&lt;/strong&gt;, don't follow the procedure described here. Follow Part 3 of this article instead, which explains how to enable &lt;em&gt;Flash Encryption&lt;/em&gt; and then activate &lt;em&gt;Secure Boot&lt;/em&gt;.&lt;/p&gt;


&lt;/div&gt;


&lt;h4&gt;
  
  
  Generating the Signing Keys &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;To use Secure Boot, we need to generate a cryptographic key pair:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private key:&lt;/strong&gt; used to sign the firmware during compilation or afterward. It must be stored somewhere secure, outside the ESP32-S3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public key:&lt;/strong&gt; used to verify that the firmware signature is legitimate. The ESP32-S3 stores a cryptographic summary (&lt;em&gt;digest&lt;/em&gt;) of this key in its eFuses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process is relatively simple: when the ESP32-S3 powers on, Secure Boot checks whether the firmware has a valid signature produced by a private key corresponding to one of the public keys authorized on the device. If verification fails, the firmware won't run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secure Boot V2&lt;/strong&gt;, used by the ESP32-S3, works with RSA-3072 signatures. First, let's create a directory to store the keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/picokeys/signing

&lt;span class="c"&gt;# Allow access only to the owner&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.config/picokeys/signing

&lt;span class="c"&gt;# Restrict permissions on new files&lt;/span&gt;
&lt;span class="nb"&gt;umask &lt;/span&gt;077
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's use &lt;code&gt;espsecure.py&lt;/code&gt;, a tool provided by ESP-IDF, to generate an RSA-3072 private key:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py generate_signing_key &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="nt"&gt;--scheme&lt;/span&gt; rsa3072 ~/.config/picokeys/signing/secure-boot.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The command above creates the &lt;code&gt;secure-boot.pem&lt;/code&gt; file containing the private key used to sign the firmware. The parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;generate_signing_key&lt;/code&gt;: generates a new cryptographic key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--version 2&lt;/code&gt;: specifies Secure Boot V2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--scheme rsa3072&lt;/code&gt;: selects the RSA algorithm with a 3072-bit key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you plan to use Pico HSM on another board, we can generate a second key:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py generate_signing_key &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="nt"&gt;--scheme&lt;/span&gt; rsa3072 ~/.config/picokeys/signing/hsm-secure-boot.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, let's make sure only the owner can read or modify the files:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.config/picokeys/signing/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;-secure-boot&lt;/span&gt;.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;ℹ️ While studying this project, I wondered: "Can I use the same key for Pico FIDO and Pico HSM?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simple answer is &lt;strong&gt;yes&lt;/strong&gt;. There is no need to generate a separate key for each firmware, since the key is tied to the microcontroller (ESP32-S3), not to the installed firmware.&lt;/p&gt;

&lt;p&gt;This is because the public key digest is programmed into the ESP32-S3 eFuses. Therefore, as long as the firmware is signed with the corresponding private key, Secure Boot can execute it. This makes it possible, for example, to switch between Pico FIDO and Pico HSM using the same signing key.&lt;/p&gt;

&lt;p&gt;However, there is an important detail: &lt;strong&gt;programming eFuses is permanent&lt;/strong&gt;. Although the ESP32-S3 supports up to three signing keys, you cannot simply replace a key whose digest has already been programmed and protected. For that reason, it's important to decide which keys to use before enabling these protections.&lt;/p&gt;

&lt;p&gt;I also recommend configuring at least two keys in the eFuses: one for each board and a backup key in case you lose the first one. However, this must be done before enabling Secure Boot. When Secure Boot V2 is activated during the first boot, any unconfigured signature slots are revoked. This behavior can be changed, but I don't recommend doing so.&lt;/p&gt;

&lt;p&gt;If you plan to use Secure Lock, you should know that it changes the behavior described above. Even if you provision three keys in the eFuses beforehand, &lt;em&gt;Secure Lock&lt;/em&gt; will use only one and revoke the other two.&lt;/p&gt;

&lt;p&gt;If you lose the private key and have no other authorized key, you won't be able to sign new firmware versions. The device will continue working with the firmware already installed, but it may no longer be possible to update it. Depending on the configuration, this could make the board unusable for new projects.&lt;/p&gt;

&lt;p&gt;If the private key is compromised, someone else could sign firmware that Secure Boot would consider legitimate. If you have no other authorized key to replace it, the recommendation is to use the board only for personal, local testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The recommendation is to use a different key for each board&lt;/strong&gt;, avoiding reuse of the same private key across several devices. That way, losing or compromising one key affects only the corresponding board. It's also important to keep secure backups of your private keys, preferably offline. After all, losing a key can mean permanently losing the ability to update the device.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Associating the Key Digest with PicoKeys Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Now that we have our private key, we need to make a small change to the PicoKeys source code to use our own signing key. This is necessary because the PicoKeys SDK has a hard-coded public key digest, meaning a digest defined directly in the source code. This simplifies enabling Secure Boot and Secure Lock through the PicoKey App, since the firmware has a known digest for provisioning and users don't have to generate and configure their own keys.&lt;/p&gt;

&lt;p&gt;However, because we're compiling the firmware with our own private key, we need to replace that digest with the one corresponding to our public key. That way, we ensure Secure Boot uses the same signing key configured during firmware compilation.&lt;/p&gt;

&lt;p&gt;The digest is a 32-byte cryptographic summary calculated from the public key. Think of it as a fingerprint of the public key: it lets Secure Boot identify which keys are authorized to sign firmware running on the microcontroller. The ESP32-S3 stores this digest in the eFuses.&lt;/p&gt;

&lt;h5&gt;
  
  
  Generating the Public Key &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;Let's use &lt;code&gt;espsecure.py&lt;/code&gt; again, but this time to calculate the public key digest corresponding to the private key we generated earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py digest_sbv2_public_key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; public-digest.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;digest_sbv2_public_key&lt;/code&gt;: calculates the public key digest for Secure Boot V2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--keyfile&lt;/code&gt;: specifies the file containing our private key. The tool extracts the public key information to calculate the digest.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--output&lt;/code&gt;: specifies the name of the file where the digest will be stored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The command generates &lt;code&gt;public-digest.bin&lt;/code&gt;, containing the 32 bytes we need to add to the PicoKeys source code.&lt;/p&gt;
&lt;h5&gt;
  
  
  Converting the Digest to the Format Used by PicoKeys &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;The generated file is binary, but the PicoKeys SDK stores the digest as a byte array in C source code. To make things easier, we can use a small Python script to convert those bytes into hexadecimal format:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - public-digest.bin &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
from pathlib import Path
import sys

digest = Path(sys.argv[1]).read_bytes()

if len(digest) != 32:
    raise SystemExit(f"Esperados 32 bytes; recebidos {len(digest)}")

for start in range(0, 32, 8):
    print("    " + ", ".join(f"0x{b:02x}" for b in digest[start:start+8]) + ",")
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The script reads the file, checks that it contains exactly 32 bytes, and prints the hexadecimal output in the format needed for the source code. The output looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xf3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x4a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0xbe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x3b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x2d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x62&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  Adding the Digest to PicoKeys FIDO and HSM Source Code &lt;a&gt;&lt;/a&gt;
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;For both FIDO and HSM&lt;/em&gt;&lt;/strong&gt;, edit the file below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim pico-keys-sdk/src/otp/otp_esp32.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the correct file, find the following variable:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;esp_secure_boot_digest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It contains the 32 bytes of the digest originally used by PicoKeys. Now replace the existing values with the bytes we just generated, keeping the array structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;esp_secure_boot_digest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mh"&gt;0x0c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xf3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x4a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0x45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0xbe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x3b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x2d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xb9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x62&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6a&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you have separate Pico FIDO and Pico HSM directories, you'll need to repeat this procedure in each one. Use the same digest if it's the same board, or a different one if you're using another board.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Attention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This change only tells the PicoKeys SDK which digest to use during Secure Boot provisioning. It does not sign the firmware or automatically program the key into the eFuses.&lt;/p&gt;

&lt;p&gt;We still need to configure ESP-IDF to sign the bootloader and application using the corresponding private key. &lt;strong&gt;Do not program the eFuses before confirming that all these components use the same signing key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the eFuses are programmed and protected, you cannot simply replace the digest with another one. An incorrect configuration could permanently prevent new firmware from booting.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Configuring and Signing Secure Boot for FIDO and HSM &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;I recommend performing this procedure in a separate directory to avoid confusing firmware configured with your signing key with another version that doesn't have that configuration. Since we're dealing with Secure Boot and ESP32-S3 eFuses, a mistake could result in permanently programming an incorrect digest, preventing new firmware from booting.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Attention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to reapply any changes that aren't persistent, such as disabling &lt;code&gt;CONFIG_ESP_COREDUMP_ENABLE_TO_UART&lt;/code&gt;, configuring PicoKeys not to show a pop-up when plugged in, and so on.&lt;/p&gt;


&lt;/div&gt;


&lt;p&gt;In the &lt;strong&gt;&lt;em&gt;root directory of each project&lt;/em&gt;&lt;/strong&gt;, run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clean the environment and start again:&lt;/span&gt;
idf.py fullclean
idf.py set-target esp32s3

&lt;span class="c"&gt;# Now edit the important settings:&lt;/span&gt;
idf.py menuconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Go to &lt;code&gt;Security features&lt;/code&gt; and check &lt;code&gt;Enable hardware Secure Boot in bootloader&lt;/code&gt;. A new menu will appear. There, select &lt;code&gt;Select secure boot version (Enable Secure Boot version 2)&lt;/code&gt;, and make sure you see &lt;code&gt;(X) Enable Secure Boot version 2&lt;/code&gt; (in my case everything was already correct, but I wanted to make this clear).&lt;/p&gt;

&lt;p&gt;Still under &lt;code&gt;Security features&lt;/code&gt;, select &lt;code&gt;Secure boot private signing key (NEW)&lt;/code&gt;. In my case, it was set to &lt;code&gt;secure_boot_signing_key.pem&lt;/code&gt;. Replace that value with the path and filename of the private key we generated earlier. In my case, I configured it exactly as follows: &lt;code&gt;/home/bruno/.config/picokeys/signing/secure-boot.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go back to the main screen by pressing &lt;code&gt;backspace&lt;/code&gt;, select &lt;code&gt;Partition Table&lt;/code&gt;, then &lt;code&gt;Offset of partition table&lt;/code&gt;. Change the value from &lt;code&gt;0x8000&lt;/code&gt; to &lt;code&gt;0x10000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;s&lt;/code&gt; to save and &lt;code&gt;q&lt;/code&gt; to quit. To make sure everything is correct, we can check:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="na"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;-iE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;'&lt;/span&gt;&lt;span class="py"&gt;CONFIG_SECURE_BOOT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y|CONFIG_PARTITION_TABLE_OFFSET|CONFIG_SECURE_BOOT_V2_ENABLED=y|CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y|CONFIG_SECURE_BOOT_SIGNING_KEY=' /home/bruno/src/picokeys/pico-fido/sdkconfig&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_SECURE_BOOT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_SECURE_BOOT_V2_ENABLED&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_SECURE_BOOT_SIGNING_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/home/bruno/.config/picokeys/signing/secure-boot.pem"&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_PARTITION_TABLE_OFFSET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0x10000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Secure Boot activation point&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enabling Secure Boot in the configuration and flashing/booting this image on the ESP32-S3 may cause the bootloader to program eFuses on the first boot. This programming is permanent. &lt;strong&gt;If you don't want to activate Secure Boot now, do not use or flash an image built with &lt;code&gt;CONFIG_SECURE_BOOT=y&lt;/code&gt; onto your board.&lt;/strong&gt; Only compile and test the lab configuration without Secure Boot.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Generating Signed FIDO and HSM Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Now that we've laid the groundwork, let's build new firmware, but this time &lt;strong&gt;signed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;For FIDO:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-fido

&lt;span class="c"&gt;# Compile the source code using the YubiKey5 VID/PID:&lt;/span&gt;
idf.py &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yubikey5 build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;For HSM:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys/pico-hsm

&lt;span class="c"&gt;# Compile the source code using the YubiKey5 VID/PID:&lt;/span&gt;
idf.py &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YubiHSM build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, in the directory where we cloned the firmware (either FIDO or HSM), let's validate the bootloader and firmware:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="c"&gt;#### FIDO&lt;/span&gt;

&lt;span class="c"&gt;# Validate the bootloader signature:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="nt"&gt;--keyfile&lt;/span&gt; /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys/pico-fido/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.14_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.

&lt;span class="c"&gt;# Validate the firmware signature:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="nt"&gt;--keyfile&lt;/span&gt; /home/bruno/.config/picokeys/signing/secure-boot.pem pico_fido.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys/pico-fido/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.14_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_fido.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.


&lt;span class="c"&gt;#### HSM&lt;/span&gt;

&lt;span class="c"&gt;# Validate the bootloader signature:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="nt"&gt;--keyfile&lt;/span&gt; /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys/pico-hsm/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.

&lt;span class="c"&gt;# Validate the firmware signature:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="nt"&gt;--keyfile&lt;/span&gt; /home/bruno/.config/picokeys/signing/secure-boot.pem pico_hsm.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys/pico-hsm/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_hsm.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Flashing the Secure Boot Image for FIDO and HSM &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ This process is irreversible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The process of flashing signed firmware is irreversible.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;Let's generate the merged file to flash afterward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

&lt;span class="c"&gt;# Merge the '.bin' files generated during FIDO compilation:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_fido_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_fido.bin


&lt;span class="c"&gt;# Merge the '.bin' files generated during HSM compilation:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_hsm_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_hsm.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Compilation generates separate files for the bootloader, partition table, and firmware. The &lt;code&gt;merge_bin&lt;/code&gt; command above combines them into a single image. However, we need to specify the flash address where each file will be written, since &lt;code&gt;.bin&lt;/code&gt; files don't contain that position themselves. The partition table offset was increased from &lt;code&gt;0x8000&lt;/code&gt; to &lt;code&gt;0x10000&lt;/code&gt; because the build showed that the bootloader was &lt;code&gt;0x9060&lt;/code&gt; bytes, larger than the original &lt;code&gt;0x8000&lt;/code&gt; space reserved for it.&lt;/p&gt;

&lt;p&gt;Once the image is ready, we can flash it onto the board:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For FIDO:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
    write_flash 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
    release/pico_fido_esp32-s3.bin

&lt;span class="c"&gt;# For HSM:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/ttyACM0 &lt;span class="se"&gt;\&lt;/span&gt;
    write_flash 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
    release/pico_hsm_esp32-s3.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Disconnect and reconnect the board, enter Download Mode, then run the command below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0 &lt;span class="nt"&gt;--no-stub&lt;/span&gt; get_security_info
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 &lt;span class="k"&gt;in &lt;/span&gt;Secure Download Mode
Enabling default SPI flash mode...

Security Information:
&lt;span class="o"&gt;=====================&lt;/span&gt;
Flags: 0x000000f5 &lt;span class="o"&gt;(&lt;/span&gt;0b11110101&lt;span class="o"&gt;)&lt;/span&gt;
Key Purposes: &lt;span class="o"&gt;(&lt;/span&gt;9, 0, 0, 0, 0, 0, 12&lt;span class="o"&gt;)&lt;/span&gt;
  BLOCK_KEY0 - SECURE_BOOT_DIGEST0
  BLOCK_KEY1 - USER/EMPTY
  BLOCK_KEY2 - USER/EMPTY
  BLOCK_KEY3 - USER/EMPTY
  BLOCK_KEY4 - USER/EMPTY
  BLOCK_KEY5 - USER/EMPTY
Chip ID: 9
API Version: 0
Secure Boot: Enabled
Secure Boot Key Revocation Status:

    Secure Boot Key1 is Revoked

    Secure Boot Key2 is Revoked

Flash Encryption: Disabled
SPI Boot Crypt Count &lt;span class="o"&gt;(&lt;/span&gt;SPI_BOOT_CRYPT_CNT&lt;span class="o"&gt;)&lt;/span&gt;: 0x0
JTAG: Permanently Disabled
Hard resetting via RTS pin...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;BLOCK_KEY0 - SECURE_BOOT_DIGEST0&lt;/code&gt; line shows that the digest of the public key used by Secure Boot was programmed into eFuse block &lt;code&gt;BLOCK_KEY0&lt;/code&gt;. We can also see &lt;code&gt;Secure Boot Key1 is Revoked&lt;/code&gt; and &lt;code&gt;Secure Boot Key2 is Revoked&lt;/code&gt;, indicating that slots 1 and 2 have been revoked and can no longer be used to validate signatures. Therefore, only the key associated with &lt;code&gt;SECURE_BOOT_DIGEST0&lt;/code&gt; remains authorized.&lt;/p&gt;

&lt;p&gt;↑ Back to the Table of Contents&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 3 — Secure Boot and Flash Encryption &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Compiling the Firmware with Secure Boot and Flash Encryption &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I'm not going to spend time explaining Flash Encryption again because I've already covered it. If you want to understand it, read the earlier section "Do I Need to Enable Flash Encryption?".&lt;/p&gt;

&lt;p&gt;In this procedure, the idea is to generate the Flash Encryption key on the computer, keep a secure backup of it, and encrypt the binaries before flashing them using ROM Download Mode. For this to work, the same key must also be stored and protected in an ESP32-S3 eFuse block. The hardware uses this key to decrypt the contents during execution.&lt;/p&gt;

&lt;p&gt;As in Part 2, I'll use ESP-IDF &lt;strong&gt;5.5.1&lt;/strong&gt;, which is used by the PicoKeys project. The projects were compiled using the following commits:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### FIDO
Hash: ade770281dd4fd8c627add34ed078f56ec40a07b
tag: nightly-main and nightly-development

### HSM
Hash: 5d6a32c81b2367fb83e5320400f61217d2bd0d2f 
tag: nightly-master and nightly-development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Attention&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Programming eFuses is &lt;strong&gt;permanent and irreversible&lt;/strong&gt;. Incorrect configuration may prevent the board from booting, make future updates impossible, or render it unusable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I am not responsible for any damage, failures, or losses resulting from following the procedures described in this guide&lt;/strong&gt;, including problems caused by compiling, configuring, or flashing the firmware.&lt;/p&gt;

&lt;p&gt;Before performing any procedure, I recommend reading the &lt;strong&gt;official documentation from the manufacturers and projects involved&lt;/strong&gt;. Each user is responsible for understanding the risks and checking that these instructions are compatible with their device.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Cloning the Projects and Installing ESP-IDF 5.5.1 &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;On Ubuntu, install the dependencies specified by ESP-IDF. Then clone the repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new directory for this project:&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/src/picokeys-fe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe

&lt;span class="c"&gt;# Clone ESP-IDF version 5.5.1:&lt;/span&gt;
git clone &lt;span class="nt"&gt;--branch&lt;/span&gt; v5.5.1 &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/espressif/esp-idf.git esp-idf

&lt;span class="c"&gt;# Clone Pico FIDO:&lt;/span&gt;
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/polhenarejos/pico-fido.git

&lt;span class="c"&gt;# Clone Pico HSM:&lt;/span&gt;
git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/polhenarejos/pico-hsm.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now install ESP-IDF and load the environment:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enter the ESP-IDF project directory:&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;esp-idf

&lt;span class="c"&gt;# Install ESP-IDF for ESP32-S3:&lt;/span&gt;
&lt;span class="nb"&gt;unset &lt;/span&gt;IDF_PYTHON_ENV_PATH &lt;span class="p"&gt;;&lt;/span&gt; ./install.sh esp32s3

&lt;span class="c"&gt;# Load the ESP-IDF environment:&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; ./export.sh

&lt;span class="c"&gt;# Check the version:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py &lt;span class="nt"&gt;--version&lt;/span&gt;
ESP-IDF v5.5.1

&lt;span class="c"&gt;# Return to the base directory:&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Required Adjustments for HSM and FIDO &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;As described in the other procedures, we need to make the necessary adjustments to both FIDO and HSM to fix the issues related to key security, along with any other required changes.&lt;/p&gt;

&lt;p&gt;I won't show the steps again here to avoid repeating the procedure already covered in Part 2.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FIDO procedure&lt;/li&gt;
&lt;li&gt;HSM procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Generating the Keys &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;To use Secure Boot V2, we need an RSA-3072 private key. It will be used to sign the bootloader and application. The ESP32-S3 stores the digest of the corresponding public key in its eFuses.&lt;/p&gt;

&lt;p&gt;The Flash Encryption key is a separate key. It uses AES-XTS and will be used to encrypt the binaries on the computer. This key must also be provisioned on the ESP32-S3 in an eFuse block with the purpose &lt;code&gt;XTS_AES_128_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's start by creating the directories and restricting permissions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/picokeys/signing
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/picokeys/flash-encryption
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.config/picokeys/signing ~/.config/picokeys/flash-encryption
&lt;span class="nb"&gt;umask &lt;/span&gt;077
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now generate the signing key (Secure Boot):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py generate_signing_key &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="nt"&gt;--scheme&lt;/span&gt; rsa3072 &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.config/picokeys/signing/secure-boot.pem

&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.config/picokeys/signing/secure-boot.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let's get the board's MAC address. Here, the MAC will only serve as a name—an identifier that could be anything. I'll use it to make it easier to identify which board the key belongs to. Enable Download Mode (hold BOOT, press and release RESET, then release BOOT).&lt;/p&gt;

&lt;p&gt;Next, run the command below to see which port the board appeared on:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /dev/ttyACM&lt;span class="k"&gt;*&lt;/span&gt; /dev/ttyUSB&lt;span class="k"&gt;*&lt;/span&gt;
/dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In my case it was &lt;code&gt;/dev/ttyACM0&lt;/code&gt;. I'll use this name in the command below to flash the firmware. If yours is different, use the port shown on your system, but make sure it belongs to the correct ESP32-S3 board you want to flash. Now let's check the board's MAC address:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0 read_mac
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 &lt;span class="o"&gt;(&lt;/span&gt;QFN56&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;revision v0.2&lt;span class="o"&gt;)&lt;/span&gt;
Features: WiFi, BLE, Embedded PSRAM 8MB &lt;span class="o"&gt;(&lt;/span&gt;AP_3v3&lt;span class="o"&gt;)&lt;/span&gt;
Crystal is 40MHz
USB mode: USB-Serial/JTAG
MAC: 68:ee:8f:51:e0:38
Uploading stub...
Running stub...
Stub running...
MAC: 68:ee:8f:51:e0:38
Hard resetting via RTS pin...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that we have the MAC, we'll use it as the ID (&lt;code&gt;68ee8f51e038&lt;/code&gt;). The command below generates a 256-bit key (used for Flash Encryption):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py generate_flash_encryption_key &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin

&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;68ee8f51e038&lt;/code&gt; with your own identifier. Use a different Flash Encryption key for each device and keep a protected backup. If you lose this key, you will no longer be able to use this board.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To store this file somewhere secure, such as a password manager, we can do the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 256 ~/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You will see a 256-bit hash:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;11c860d531eec46bb9b29319a798da103e7c121c7dcbe9aa2b251a5a855daae6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Save this value somewhere secure. To restore it to a binary file, do the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; chave.hex pico-68ee8f51e038-restaurado.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;chave.hex&lt;/code&gt; file must contain the 256-bit hash.&lt;/p&gt;
&lt;h4&gt;
  
  
  Associating the Key Digest with PicoKeys Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Now let's calculate the public key digest to validate the firmware signature:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py digest_sbv2_public_key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ~/.config/picokeys/signing/public-digest.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The command above generates a 32-byte binary file. Next, we need to convert its contents to hexadecimal format:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - ~/.config/picokeys/signing/public-digest.bin &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
from pathlib import Path
import sys

digest = Path(sys.argv[1]).read_bytes()
if len(digest) != 32:
    raise SystemExit(f"Esperados 32 bytes; recebidos {len(digest)}")

for start in range(0, 32, 8):
    print("    " + ", ".join(f"0x{b:02x}" for b in digest[start:start+8]) + ",")
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For both FIDO and HSM, edit the file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim pico-keys-sdk/src/otp/otp_esp32.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Find &lt;code&gt;esp_secure_boot_digest[32]&lt;/code&gt; and replace the 32 bytes with the output of the previous command, as demonstrated in the earlier tutorial. This tells the SDK which digest to use when validating the firmware signature. It does not sign the firmware or write anything to the eFuses.&lt;/p&gt;
&lt;h4&gt;
  
  
  Configuring Secure Boot and Flash Encryption &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;In the root directory of each project&lt;/em&gt;, clean the build and configure the ESP32-S3 target:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;idf.py fullclean
idf.py set-target esp32s3
idf.py menuconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Under &lt;code&gt;Security features&lt;/code&gt;, configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Enable hardware Secure Boot in bootloader&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Select secure boot version (Enable Secure Boot version 2)&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Sign binaries during build&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Secure boot private signing key&lt;/code&gt;: path to the RSA private key corresponding to the digest accepted by the board;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Enable flash encryption on boot&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Enable usage mode&lt;/code&gt;: &lt;code&gt;Release&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UART ROM download mode&lt;/code&gt;: &lt;code&gt;(Permanently switch to Secure mode (recommended))&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under &lt;code&gt;Partition Table&lt;/code&gt;, change the partition table offset from &lt;code&gt;0x8000&lt;/code&gt; to &lt;code&gt;0x10000&lt;/code&gt;. Save with &lt;code&gt;s&lt;/code&gt; and quit with &lt;code&gt;q&lt;/code&gt;. Then verify that the options are correct:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_BOOT='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_BOOT_V2_ENABLED='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_BOOT_SIGNING_KEY='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_FLASH_ENC_ENABLED='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_FLASH_ENCRYPTION_AES128='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'^CONFIG_PARTITION_TABLE_OFFSET='&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  sdkconfig
&lt;span class="nv"&gt;CONFIG_SECURE_BOOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_BOOT_V2_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_BOOT_SIGNING_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/bruno/.config/picokeys/signing/secure-boot.pem"&lt;/span&gt;
&lt;span class="nv"&gt;CONFIG_SECURE_FLASH_ENC_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_FLASH_ENCRYPTION_AES128&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="nv"&gt;CONFIG_PARTITION_TABLE_OFFSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Edit the following file, whether you're using FIDO or HSM:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim pico-keys-sdk/config/esp32/partitions.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The default table already uses &lt;code&gt;factory&lt;/code&gt; at &lt;code&gt;0x20000&lt;/code&gt; and &lt;code&gt;part0&lt;/code&gt; at &lt;code&gt;0x200000&lt;/code&gt;, with a size of &lt;code&gt;1M&lt;/code&gt;. We don't need to change those values. The important part is to add the &lt;code&gt;encrypted&lt;/code&gt; flag at the end of the &lt;code&gt;part0&lt;/code&gt; line, so it looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;part0,    0x40,  0x1,     0x200000, 1M, encrypted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ESP-IDF encrypts firmware partitions. The &lt;code&gt;part0&lt;/code&gt; partition, however, is a data partition, so we need to add the &lt;code&gt;encrypted&lt;/code&gt; flag to have it encrypted as well.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;⚠️ Check the keys before flashing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This procedure assumes a fresh ESP32-S3 board with Secure Boot and Flash Encryption disabled. Before flashing the firmware, confirm that it was signed using the private key you generated and that the Flash Encryption key used to encrypt the files is the same one that will be provisioned on the board.&lt;/p&gt;

&lt;p&gt;On the first boot, the firmware may program and protect eFuses to enable these features. This change is permanent. Keep a protected backup of the keys and validate the build and images on the computer before booting the board with this firmware.&lt;/p&gt;


&lt;/div&gt;



&lt;h4&gt;
  
  
  Generating and Validating the Signed FIDO and HSM Firmware &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Let's load the ESP-IDF 5.5.1 environment again (just to be safe) and compile &lt;strong&gt;Pico FIDO&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt; ~/src/picokeys-fe/esp-idf/export.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;IDF_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esp32s3

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-fido

idf.py &lt;span class="nt"&gt;-DIDF_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esp32s3 &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yubikey5 build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For &lt;strong&gt;Pico HSM&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt; ~/src/picokeys-fe/esp-idf/export.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;IDF_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esp32s3

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-hsm

idf.py &lt;span class="nt"&gt;-DIDF_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esp32s3 &lt;span class="nt"&gt;-DVIDPID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YubiHSM build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After building, we need to validate the bootloader and application signatures, as in the previous procedure. &lt;strong&gt;For FIDO&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-fido

&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys-fe/pico-fido/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.


&lt;span class="nv"&gt;$ &lt;/span&gt;idf.py secure-verify-signature &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  pico_fido.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys-fe/pico-fido/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_fido.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;For HSM&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-hsm

idf.py secure-verify-signature &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  bootloader/bootloader.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys-fe/pico-hsm/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem bootloader/bootloader.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.


idf.py secure-verify-signature &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; ~/.config/picokeys/signing/secure-boot.pem &lt;span class="se"&gt;\&lt;/span&gt;
  pico_hsm.bin
Executing action: secure-verify-signature
Running espsecure &lt;span class="k"&gt;in &lt;/span&gt;directory /home/bruno/src/picokeys-fe/pico-hsm/build
Executing &lt;span class="s2"&gt;"/home/bruno/.espressif/python_env/idf5.5_py3.12_env/bin/python -m espsecure verify_signature --version 2 --keyfile /home/bruno/.config/picokeys/signing/secure-boot.pem pico_hsm.bin"&lt;/span&gt;...
espsecure.py v4.12.0
Signature block 0 is valid &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 0 verification successful using the supplied key &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Signature block 1 invalid. Skipping.
Signature block 2 invalid. Skipping.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For each command, signature block 0 must be valid. Blocks 1 and 2 may show as &lt;em&gt;invalid&lt;/em&gt; if they were not used. Do not flash firmware whose signature has not been validated.&lt;/p&gt;
&lt;h4&gt;
  
  
  Preparing the Board and FE Key — First Firmware Only &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Connect only the board you intend to use (&lt;strong&gt;this board must not already have Secure Boot enabled&lt;/strong&gt;). Then put the board into Download Mode (hold BOOT, press and release RESET, then release BOOT).&lt;/p&gt;

&lt;p&gt;Next, run the command below to see which port it appeared on:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /dev/ttyACM&lt;span class="k"&gt;*&lt;/span&gt; /dev/ttyUSB&lt;span class="k"&gt;*&lt;/span&gt;
/dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In my case it was &lt;code&gt;/dev/ttyACM0&lt;/code&gt;. I'll use this name in the command below to flash the firmware. If yours is different, use the port shown on your system, but make sure it belongs to the correct ESP32-S3 board you want to flash.&lt;/p&gt;

&lt;p&gt;Now we need to confirm a few things. Start by running the commands below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;espefuse.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; summary | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'ENABLE_SECURITY_DOWNLOAD|DIS_DOWNLOAD_MODE'&lt;/span&gt;
DIS_DOWNLOAD_MODE &lt;span class="o"&gt;(&lt;/span&gt;BLOCK0&lt;span class="o"&gt;)&lt;/span&gt;                         Set this bit to disable download mode &lt;span class="o"&gt;(&lt;/span&gt;boot_mode[3 &lt;span class="o"&gt;=&lt;/span&gt; False R/W &lt;span class="o"&gt;(&lt;/span&gt;0b0&lt;span class="o"&gt;)&lt;/span&gt;
ENABLE_SECURITY_DOWNLOAD &lt;span class="o"&gt;(&lt;/span&gt;BLOCK0&lt;span class="o"&gt;)&lt;/span&gt;                  Set this bit to &lt;span class="nb"&gt;enable &lt;/span&gt;secure UART download mode   &lt;span class="o"&gt;=&lt;/span&gt; False R/W &lt;span class="o"&gt;(&lt;/span&gt;0b0&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; get_security_info | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'Secure Boot|Flash Encryption|SPI_BOOT_CRYPT_CNT|BLOCK_KEY1'&lt;/span&gt;
  BLOCK_KEY1 - USER/EMPTY
Secure Boot: Disabled
Flash Encryption: Disabled
SPI Boot Crypt Count &lt;span class="o"&gt;(&lt;/span&gt;SPI_BOOT_CRYPT_CNT&lt;span class="o"&gt;)&lt;/span&gt;: 0x0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These commands confirm several details, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure Boot is disabled.&lt;/li&gt;
&lt;li&gt;Flash Encryption is disabled and &lt;code&gt;SPI_BOOT_CRYPT_CNT&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Secure Download Mode is disabled.&lt;/li&gt;
&lt;li&gt;The block intended for Flash Encryption is empty and available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the procedure below, &lt;code&gt;BLOCK_KEY1&lt;/code&gt; is reserved for &lt;em&gt;Flash Encryption&lt;/em&gt; and &lt;code&gt;BLOCK_KEY0&lt;/code&gt; for the &lt;em&gt;Secure Boot&lt;/em&gt; digest. &lt;strong&gt;If the commands above do not confirm that &lt;code&gt;BLOCK_KEY1&lt;/code&gt; is free, stop. Do not substitute another block without reviewing the key allocation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As required by the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/release-v5.5/esp32s3/security/security-features-enablement-workflows.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, we now need to erase the flash (I don't see why this would be necessary, but let's follow the instructions exactly) and confirm that the AES-XTS key is 32 bytes long:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; erase_flash
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 &lt;span class="o"&gt;(&lt;/span&gt;QFN56&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;revision v0.2&lt;span class="o"&gt;)&lt;/span&gt;
Features: WiFi, BLE, Embedded PSRAM 8MB &lt;span class="o"&gt;(&lt;/span&gt;AP_3v3&lt;span class="o"&gt;)&lt;/span&gt;
Crystal is 40MHz
USB mode: USB-Serial/JTAG
MAC: 68:ee:8f:51:e0:38
Uploading stub...
Running stub...
Stub running...
Erasing flash &lt;span class="o"&gt;(&lt;/span&gt;this may take a &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;...
Chip erase completed successfully &lt;span class="k"&gt;in &lt;/span&gt;4.7 seconds.
Hard resetting via RTS pin...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we'll write the Flash Encryption key into eFuse block &lt;code&gt;BLOCK_KEY1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;espefuse.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  burn_key BLOCK_KEY1 &lt;span class="s2"&gt;"/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin"&lt;/span&gt; XTS_AES_128_KEY
espefuse.py v4.12.0
Connecting...

&lt;span class="o"&gt;===&lt;/span&gt; Run &lt;span class="s2"&gt;"burn_key"&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt;
Sensitive data will be hidden &lt;span class="o"&gt;(&lt;/span&gt;see &lt;span class="nt"&gt;--show-sensitive-info&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Burn keys to blocks:
 - BLOCK_KEY1 -&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??]
    Reversing byte order &lt;span class="k"&gt;for &lt;/span&gt;AES-XTS hardware peripheral
    &lt;span class="s1"&gt;'KEY_PURPOSE_1'&lt;/span&gt;: &lt;span class="s1"&gt;'USER'&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;'XTS_AES_128_KEY'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
    Disabling write to &lt;span class="s1"&gt;'KEY_PURPOSE_1'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
    Disabling &lt;span class="nb"&gt;read &lt;/span&gt;to key block
    Disabling write to key block


Check all blocks &lt;span class="k"&gt;for &lt;/span&gt;burn...
idx, BLOCK_NAME,          Conclusion
&lt;span class="o"&gt;[&lt;/span&gt;00] BLOCK0               is not empty
    &lt;span class="o"&gt;(&lt;/span&gt;written &lt;span class="o"&gt;)&lt;/span&gt;: 0x0000000000000100000000000000d1f50000000000000000
    &lt;span class="o"&gt;(&lt;/span&gt;to write&lt;span class="o"&gt;)&lt;/span&gt;: 0x000000000000000000000000400000000000000201000200
    &lt;span class="o"&gt;(&lt;/span&gt;coding scheme &lt;span class="o"&gt;=&lt;/span&gt; NONE&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;05] BLOCK_KEY1           is empty, will burn the new value
&lt;span class="nb"&gt;.&lt;/span&gt; 
This is an irreversible operation!
Type &lt;span class="s1"&gt;'BURN'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;all capitals&lt;span class="o"&gt;)&lt;/span&gt; to &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
BURN
BURN BLOCK5  - OK &lt;span class="o"&gt;(&lt;/span&gt;write block &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;read &lt;/span&gt;block&lt;span class="o"&gt;)&lt;/span&gt;
BURN BLOCK0  - OK &lt;span class="o"&gt;(&lt;/span&gt;all write block bits are &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Reading updated efuses...
Successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;By default, &lt;code&gt;burn_key&lt;/code&gt; protects the key against both reading and writing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The procedure will ask you to type &lt;code&gt;BURN&lt;/code&gt; to continue. Type it and press &lt;em&gt;Enter&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now let's check &lt;code&gt;get_security_info&lt;/code&gt; again. &lt;code&gt;BLOCK_KEY1&lt;/code&gt; should have the &lt;code&gt;XTS_AES_128_KEY&lt;/code&gt; purpose and be protected against reading and writing.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; esp32s3 &lt;span class="nt"&gt;--port&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; get_security_info | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'Secure Boot|Flash Encryption|SPI_BOOT_CRYPT_CNT|BLOCK_KEY1'&lt;/span&gt;
  BLOCK_KEY1 - XTS_AES_128_KEY
Secure Boot: Disabled
Flash Encryption: Disabled
SPI Boot Crypt Count &lt;span class="o"&gt;(&lt;/span&gt;SPI_BOOT_CRYPT_CNT&lt;span class="o"&gt;)&lt;/span&gt;: 0x0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Flashing the First Firmware onto the Board &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Let's merge the files into a single binary:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;### FIDO:&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-fido &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_fido_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_fido.bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..

&lt;span class="c"&gt;### HSM:&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-hsm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_hsm_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_hsm.bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the layout above, the addresses used are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bootloader&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x10000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application/Firmware (&lt;code&gt;factory&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x20000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data (&lt;code&gt;part0&lt;/code&gt;), if it contains anything&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x200000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now let's flash the board:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# FIDO:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; write_flash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 release/pico_fido_esp32-s3.bin


&lt;span class="c"&gt;# HSM:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; write_flash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 release/pico_hsm_esp32-s3.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  

&lt;p&gt;&lt;strong&gt;💡 write_flash vs encrypt_flash_data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;write_flash&lt;/code&gt; is used with the signed binaries when Flash Encryption is not yet active on the board. Since we've configured &lt;em&gt;Flash Encryption on boot&lt;/em&gt; and already provisioned the key in the eFuse, the bootloader will encrypt the firmware during its first boot on the board.&lt;/p&gt;

&lt;p&gt;For subsequent flashes, after &lt;em&gt;Flash Encryption&lt;/em&gt; is active, we'll use &lt;code&gt;encrypt_flash_data&lt;/code&gt; to encrypt the new firmware on the computer using the board's &lt;em&gt;Flash Encryption&lt;/em&gt; key. Then we'll flash the encrypted file using &lt;code&gt;write_flash&lt;/code&gt;, at the same address used for encryption.&lt;/p&gt;


&lt;/div&gt;



&lt;p&gt;Disconnect and reconnect the board, enter Download Mode, then run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;--port&lt;/span&gt; /dev/ttyACM0 &lt;span class="nt"&gt;--no-stub&lt;/span&gt; get_security_info
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 &lt;span class="k"&gt;in &lt;/span&gt;Secure Download Mode
Enabling default SPI flash mode...

Security Information:
&lt;span class="o"&gt;=====================&lt;/span&gt;
Flags: 0x000006f5 &lt;span class="o"&gt;(&lt;/span&gt;0b11011110101&lt;span class="o"&gt;)&lt;/span&gt;
Key Purposes: &lt;span class="o"&gt;(&lt;/span&gt;9, 4, 0, 0, 0, 0, 12&lt;span class="o"&gt;)&lt;/span&gt;
  BLOCK_KEY0 - SECURE_BOOT_DIGEST0
  BLOCK_KEY1 - XTS_AES_128_KEY
  BLOCK_KEY2 - USER/EMPTY
  BLOCK_KEY3 - USER/EMPTY
  BLOCK_KEY4 - USER/EMPTY
  BLOCK_KEY5 - USER/EMPTY
Chip ID: 9
API Version: 0
Secure Boot: Enabled
Secure Boot Key Revocation Status:

    Secure Boot Key1 is Revoked

    Secure Boot Key2 is Revoked

Flash Encryption: Enabled
SPI Boot Crypt Count &lt;span class="o"&gt;(&lt;/span&gt;SPI_BOOT_CRYPT_CNT&lt;span class="o"&gt;)&lt;/span&gt;: 0x7
Dcache &lt;span class="k"&gt;in &lt;/span&gt;UART download mode: Disabled
Icache &lt;span class="k"&gt;in &lt;/span&gt;UART download mode: Disabled
JTAG: Permanently Disabled
Hard resetting via RTS pin...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;BLOCK_KEY0 - SECURE_BOOT_DIGEST0&lt;/code&gt; line shows that the Secure Boot public key digest was programmed into &lt;code&gt;BLOCK_KEY0&lt;/code&gt;. The messages &lt;code&gt;Secure Boot Key1 is Revoked&lt;/code&gt; and &lt;code&gt;Secure Boot Key2 is Revoked&lt;/code&gt; indicate that slots 1 and 2 have been revoked. Therefore, slot 0 remains authorized to validate signatures.&lt;/p&gt;

&lt;p&gt;We can also see that Flash Encryption is active. The &lt;code&gt;SPI_BOOT_CRYPT_CNT: 0x7&lt;/code&gt; value confirms that Flash Encryption was enabled, and &lt;code&gt;BLOCK_KEY1 - XTS_AES_128_KEY&lt;/code&gt; shows that the FE key is stored in the expected block. The board is also in Secure Download Mode. In this mode, downloading is limited to basic flashing commands. Data and instruction caches are disabled during download, and JTAG has been permanently disabled.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flashing New Encrypted Firmware — Second or Later Flash &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This procedure is for the &lt;strong&gt;second or subsequent flash&lt;/strong&gt;, after the first firmware has already enabled Flash Encryption. After compiling the firmware, we'll merge the binaries into a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;### FIDO:&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-fido &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_fido_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_fido.bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..


&lt;span class="c"&gt;### HSM:&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/picokeys-fe/pico-hsm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build

esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 merge_bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; ../release/pico_hsm_esp32-s3.bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 bootloader/bootloader.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x10000 partition_table/partition-table.bin &lt;span class="se"&gt;\&lt;/span&gt;
  0x20000 pico_hsm.bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's encrypt the firmware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# FIDO:&lt;/span&gt;
espsecure.py encrypt_flash_data &lt;span class="nt"&gt;--aes_xts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; &lt;span class="s2"&gt;"/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address&lt;/span&gt; 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; release/pico_fido_esp32-s3.enc.bin &lt;span class="se"&gt;\&lt;/span&gt;
  release/pico_fido_esp32-s3.bin

&lt;span class="c"&gt;# HSM:&lt;/span&gt;
espsecure.py encrypt_flash_data &lt;span class="nt"&gt;--aes_xts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; &lt;span class="s2"&gt;"/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address&lt;/span&gt; 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; release/pico_hsm_esp32-s3.enc.bin &lt;span class="se"&gt;\&lt;/span&gt;
  release/pico_hsm_esp32-s3.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before flashing, let's run a test to validate that encryption worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;espsecure.py decrypt_flash_data &lt;span class="nt"&gt;--aes_xts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--keyfile&lt;/span&gt; &lt;span class="s2"&gt;"/home/bruno/.config/picokeys/flash-encryption/pico-68ee8f51e038.bin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address&lt;/span&gt; 0x0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/pico_hsm.dec.bin &lt;span class="se"&gt;\&lt;/span&gt;
  release/pico_hsm_esp32-s3.enc.bin

&lt;span class="c"&gt;# Compare the original firmware (encrypted) with the newly decrypted firmware (if there is no output, everything matches):&lt;/span&gt;
cmp release/pico_hsm_esp32-s3.bin /tmp/pico_hsm.dec.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To flash, we need to use the &lt;code&gt;--force&lt;/code&gt; option. Without it, the following happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; &lt;span class="nt"&gt;--no-stub&lt;/span&gt; write_flash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 release/pico_hsm_esp32-s3.enc.bin
esptool.py v4.12.0
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 &lt;span class="k"&gt;in &lt;/span&gt;Secure Download Mode
Enabling default SPI flash mode...
Configuring flash size...
WARNING: In &lt;span class="k"&gt;case&lt;/span&gt; of failure, please &lt;span class="nb"&gt;set &lt;/span&gt;a specific &lt;span class="nt"&gt;--flash_size&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

A fatal error occurred: WARNING: Detected flash encryption and secure download mode enabled.
Flashing plaintext binary may brick your device! Use &lt;span class="nt"&gt;--force&lt;/span&gt; to override the warning.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This warning is expected from esptool because it detected a board with &lt;em&gt;Flash Encryption&lt;/em&gt; and &lt;em&gt;Secure Download Mode&lt;/em&gt; enabled. When these features are active, it cannot verify whether the file has been encrypted correctly, which is why we need &lt;code&gt;--force&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we've validated the encryption, we can proceed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# FIDO:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; &lt;span class="nt"&gt;--no-stub&lt;/span&gt; write_flash &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 release/pico_fido_esp32-s3.enc.bin


&lt;span class="c"&gt;# HSM:&lt;/span&gt;
esptool.py &lt;span class="nt"&gt;--chip&lt;/span&gt; ESP32-S3 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/dev/ttyACM0"&lt;/span&gt; &lt;span class="nt"&gt;--no-stub&lt;/span&gt; write_flash &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--flash_mode&lt;/span&gt; dio &lt;span class="nt"&gt;--flash_freq&lt;/span&gt; 80m &lt;span class="nt"&gt;--flash_size&lt;/span&gt; keep &lt;span class="se"&gt;\&lt;/span&gt;
  0x0 release/pico_hsm_esp32-s3.enc.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sources &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/hmac.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/hmac.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/security/secure-boot-v2.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/security/secure-boot-v2.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32/api-reference/kconfig.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32/api-reference/kconfig.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32s3/security/flash-encryption.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/v5.2/esp32s3/security/flash-encryption.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/flash-encryption.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/flash-encryption.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/secure-boot-v2.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/secure-boot-v2.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/security-features-enablement-workflows.html" rel="noopener noreferrer"&gt;https://docs.espressif.com/projects/esp-idf/en/v5.5/esp32s3/security/security-features-enablement-workflows.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/picohsm/first-steps/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/picohsm/first-steps/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/picohsm/key-generation/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/picohsm/key-generation/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/picohsm/sign-verify/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/picohsm/sign-verify/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/picokeyapp/security/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/picokeyapp/security/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/production/otp-fuses" rel="noopener noreferrer"&gt;https://docs.picokeys.com/production/otp-fuses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/production/otp-fuses/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/production/otp-fuses/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.picokeys.com/security/threat-model/" rel="noopener noreferrer"&gt;https://docs.picokeys.com/security/threat-model/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/polhenarejos/pico-fido" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-fido&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/polhenarejos/pico-hsm" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-hsm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/polhenarejos/pico-hsm/pull/143" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-hsm/pull/143&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/polhenarejos/pico-vault-enroller" rel="noopener noreferrer"&gt;https://github.com/polhenarejos/pico-vault-enroller&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/" rel="noopener noreferrer"&gt;https://www.picokeys.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/getting-started/" rel="noopener noreferrer"&gt;https://www.picokeys.com/getting-started/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/pico-fido/" rel="noopener noreferrer"&gt;https://www.picokeys.com/pico-fido/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/pico-hsm/" rel="noopener noreferrer"&gt;https://www.picokeys.com/pico-hsm/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/picokeyapp/" rel="noopener noreferrer"&gt;https://www.picokeys.com/picokeyapp/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.picokeys.com/pico-openpgp/" rel="noopener noreferrer"&gt;https://www.picokeys.com/pico-openpgp/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;↑ Back to the Table of Contents&lt;/p&gt;

</description>
      <category>picokeys</category>
      <category>esp32s3</category>
      <category>fido</category>
      <category>hsm</category>
    </item>
  </channel>
</rss>
