build: "fakeprod" prod-like development mode
What changed, and why it matters
This commit adds an official developer-only 'fakeprod' build mode for Blockstream Jade hardware wallets. It deliberately weakens several security settings (single signature, committed private keys, readable flash-encryption key, enabled JTAG and ROM download mode) so developers can easily flash and debug a production-like firmware. The commit and its documentation explicitly warn that this mode is NOT secure and must never be used on a real wallet. It is a development/testing feature, not a vulnerability in production firmware.
No action required for production security; this is an intentional, documented development mode. Ensure build/release pipelines never ship fakeprod firmware to end users, and verify that production builds do not accidentally enable CONFIG_JADE_FAKEPROD. Consider adding CI checks that reject artifacts built with the fakeprod keys or config.
Security signals we found
In-tree private keys committed to repository (tools/fakeprod_v2.pem, tools/fakeprod_attest.pem)
Secure boot minimum signatures reduced from 2 to 1
Flash encryption set to DEVELOPMENT mode with key readable
JTAG and ROM download mode intentionally left enabled
Attestation re-initialization allowed in fakeprod mode
Documentation explicitly labels mode as NOT secure and development-only
Evidence from the diff
The change introduces a new Kconfig option CONFIG_JADE_FAKEPROD and a –fakeprod switch in tools/switch_to.sh. When enabled, the build signs firmware with an in-tree non-secret RSA key (tools/fakeprod_v2.pem), accepts only one secure-boot signature, uses development-mode flash encryption, leaves JTAG and ROM download mode enabled, keeps the flash-encryption key readable, and skips the eFuse write that disables ROM download mode. Attestation uses a separate in-tree authority key (tools/fakeprod_attest.pem) and allows re-initialization. Code changes in attestation.c, gui.c, main.c and dashboard.c gate fakeprod-specific behavior on CONFIG_JADE_FAKEPROD. The documentation repeatedly states this mode is insecure and for development only.
Changed components
tools/switch_to.sh build configuration scriptmain/Kconfig.projbuildmain/attestation/attestation.cmain/gui.cmain/main.cmain/process/dashboard.cdocs/fakeprod.rsttools/fakeprod_v2.pemtools/fakeprod_attest.pemInspect captured patch +350 / −5
### docs/fakeprod.rst
@@ -0,0 +1,185 @@
+================
+Fakeprod firmware
+================
+
+``--fakeprod`` is a development-only build mode that reproduces a production
+Jade v2 firmware as closely as possible (secure boot + flash encryption)
+while remaining easily flashable for devs.
+
+.. warning::
+
+ Fakeprod builds are **NOT secure** and must never be used on a real wallet.
+ The signing key is committed to the repository, the bootloader accepts a
+ single signature, ROM download mode and JTAG are left enabled, and the
+ flash-encryption key stays readable.
+
+Usage
+=====
+
+.. code-block:: bash
+
+ # Configure the build. Add --jtag to also get a USB-JTAG serial console.
+ ./tools/switch_to.sh jade_v2 --fakeprod --jtag
+
+ # Build (auto-signed with the committed key).
+ idf.py all
+
+ # First-time provisioning:
+ idf.py flash # app + partition table + otadata, plaintext
+ idf.py bootloader-flash # the signed bootloader (not flashed automatically)
+
+ # Power-cycle the unit and wait a bit (on first boot the
+ # bootloader enables secure boot, generates the flash-encryption key,
+ # encrypts the flash in-place, and resets)
+
+ # Subsequent updates (pre-encrypted on the host; see "Re-flashing" below).
+ idf.py encrypted-flash
+
+Differences from production
+===========================
+
+.. list-table::
+ :header-rows: 1
+
+ * - Setting
+ - Production
+ - Fakeprod
+ * - ``CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES``
+ - ``n`` (signed offline by release keys)
+ - ``y``
+ * - ``CONFIG_SECURE_BOOT_SIGNING_KEY``
+ - unset
+ - ``tools/fakeprod_v2.pem``
+ * - ``CONFIG_SECURE_BOOT_V2_MIN_SIGNATURES``
+ - ``2``
+ - ``1``
+ * - ``CONFIG_SECURE_FLASH_ENCRYPTION_MODE_*``
+ - ``RELEASE``
+ - ``DEVELOPMENT``
+ * - ``CONFIG_SECURE_DISABLE_ROM_DL_MODE``
+ - ``y``
+ - removed (ROM download mode stays enabled)
+ * - ``CONFIG_SECURE_BOOT_ALLOW_JTAG``
+ - unset
+ - ``y``
+ * - ``CONFIG_JADE_FAKEPROD``
+ - unset
+ - ``y``
+ * - ``CONFIG_DEBUG_MODE``
+ - unset
+ - ``y``
+
+Everything else (secure boot v2, anti-rollback, flash encryption enabled,
+PSRAM settings, and so on) is inherited unchanged from the production config,
+so the app's profile matches production as closely as possible.
+
+Signing key
+===========
+
+``tools/fakeprod_v2.pem`` an in-tree, non-secret key
+
+Because ``CONFIG_SECURE_BOOT_V2_MIN_SIGNATURES`` is set to ``1`` you can
+reflash using ESP-IDF tools directly
+
+First-boot eFuse programming
+============================
+
+Because secure boot is enabled, the build does **not** flash the bootloader
+automatically (``idf.py flash`` and ``idf.py encrypted-flash`` skip it, to
+avoid accidentally bricking a secure-boot device). Flash it manually once:
+
+.. code-block:: bash
+
+ idf.py bootloader-flash
+
+On the next boot the bootloader performs one-time provisioning:
+
+1. enable secure boot v2 (burn the key digest and ``SECURE_BOOT_EN``);
+2. generate and burn the flash-encryption key;
+3. encrypt the flash in-place (this can take up to a minute);
+4. reset.
+
+If you flash the app but not the bootloader, the app aborts shortly after
+``spi_flash`` init because ``CONFIG_SECURE_FLASH_CHECK_ENC_EN_IN_APP`` detects
+that flash encryption is not enabled, and the unit reboot-loops silently.
+
+The following eFuses are burned on first boot (irreversible):
+
+* ``SECURE_BOOT_EN`` - secure boot is enabled.
+* ``DIS_DIRECT_BOOT``.
+* Flash-encryption key and ``SPI_BOOT_CRYPT_CNT`` (development mode: the
+ counter is neither maxed nor write-protected).
+* ``DIS_DOWNLOAD_MANUAL_ENCRYPT``, ``DIS_DOWNLOAD_DCACHE`` and
+ ``DIS_DOWNLOAD_ICACHE``.
+
+These are the security features that are **not** disabled:
+
+* ROM download mode is kept enabled so ``esptool.py`` can still connect.
+* JTAG: ``HARD_DIS_JTAG`` / ``DIS_USB_JTAG`` are not burned.
+* Flash-encryption key remains readable (``RD_DIS_BLK1`` is not burned).
+* eFuses remain readable (``CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS=y``).
+
+Re-flashing
+===========
+
+There are two ways to update a fakeprod unit
+
+Encrypted (unlimited)
+---------------------
+
+In development mode the flash-encryption key stays readable, so:
+
+.. code-block:: bash
+
+ idf.py encrypted-flash
+
+reads the key from the device, pre-encrypts the image on the host and writes
+encrypted data. This never touches ``SPI_BOOT_CRYPT_CNT``, consumes no eFuse
+bits and can be repeated indefinitely.
+
+This only works **after** first-boot provisioning has programmed the key. On a
+fresh unit it fails with ``Flash encryption key is not programmed``.
+
+Plaintext (limited)
+-------------------
+
+Plaintext can only be written while encryption is off. Toggling encryption
+uses the 3-bit ``SPI_BOOT_CRYPT_CNT`` eFuse which will be maxed out after
+only a few toggles (shoud probably only use for reviving a bricked unit)
+
+Attestation
+===========
+
+Fakeprod exercises the production attestation flow (``register_attestation`` /
+``sign_attestation``) against a dedicated in-tree, non-secret authority key,
+``tools/fakeprod_attest.pem`` (production validates against the real Jade
+master attestation public key instead).
+
+Because fakeprod always builds with ``CONFIG_DEBUG_MODE``, it uses a fixed
+attestation HMAC key and does not burn ``WR_DIS_RD_DIS``, so attestation can
+be **re-provisioned** as many times as needed on the same unit.
+
+Initialise attestation (generates a fresh per-device RSA-4096 key, signs it
+with the fakeprod authority key and sends it to the unit):
+
+.. code-block:: bash
+
+ python jade_attest.py --init-new tools/fakeprod_attest.pem
+
+Verify attestation (asks the unit to sign a random challenge and checks the
+result against the fakeprod authority public key):
+
+.. code-block:: bash
+
+ python jade_attest.py --verify tools/fakeprod_attest.pem
+
+Notes
+=====
+
+* Label a dev unit once you have flashed it with fakeprod firmware, so you
+ dont get confused when you try to reflash with incorrect firmware
+* Anti-rollback stays enabled with ``CONFIG_BOOTLOADER_APP_SECURE_VERSION=2``,
+ which is burned on first boot. Re-flashed images must therefore keep a
+ secure version of at least 2.
+* ``--fakeprod`` alone keeps ``CONFIG_ESP_CONSOLE_NONE=y`` (as in production).
+ Add ``--jtag`` to enable the USB-JTAG serial console for debugging.
### main/Kconfig.projbuild
@@ -323,6 +323,12 @@ menu "Blockstream Jade"
Enables USB JTAG serial rather than CDC tinyusb, implies no tinyusb/host storage/etc
default n
+ config JADE_FAKEPROD
+ bool "Fakeprod, prod-like build with secureboot but dev flashable"
+ default n
+ help
+ Prod-like build with secureboot but dev flashable.
+
config HAS_BATTERY
bool "Enable Battery Functions"
help
### main/attestation/attestation.c
@@ -36,7 +36,7 @@
#define JADE_ATTEST_EFUSE EFUSE_BLK_KEY5
#define JADE_ATTEST_HMAC_EFUSE_ID (JADE_ATTEST_EFUSE - EFUSE_BLK_KEY0)
-#if defined(CONFIG_DEBUG_MODE) && !defined(CONFIG_SECURE_BOOT)
+#if defined(CONFIG_DEBUG_MODE) && (!defined(CONFIG_SECURE_BOOT) || defined(CONFIG_JADE_FAKEPROD))
#define ALLOW_REINITIALISE 1
#endif
@@ -48,6 +48,11 @@ static const char JADE_ATTEST_PARTITION_NAME[] = "attest";
static const uint8_t ATTEST_PUBKEY_HASH[SHA256_LEN]
= { 0x75, 0xd6, 0x18, 0x75, 0xde, 0x1a, 0x11, 0xa6, 0xab, 0x7c, 0xd0, 0xf9, 0xb8, 0x5c, 0x48, 0x2a, 0x35, 0x46,
0xf4, 0xe0, 0xb5, 0xe6, 0x81, 0x62, 0x2a, 0x0c, 0xff, 0x7b, 0x1d, 0xca, 0xe4, 0x0f };
+#elif defined(CONFIG_JADE_FAKEPROD)
+// SHA256 of the fakeprod attestation authority pubkey pem (tools/fakeprod_attest.pem)
+static const uint8_t ATTEST_PUBKEY_HASH[SHA256_LEN]
+ = { 0xe1, 0x6a, 0x5a, 0xb6, 0xd9, 0x44, 0x4f, 0xa5, 0x20, 0x9f, 0x41, 0xf4, 0xaf, 0x77, 0xc6, 0x36, 0xfe, 0x36,
+ 0xed, 0x95, 0x56, 0x4c, 0x72, 0xa2, 0x9b, 0xf4, 0xf7, 0xe5, 0x16, 0x37, 0x95, 0xbe };
#endif
// Data saved to (logically write-once) partition
@@ -150,7 +155,15 @@ static bool load_attestation_data(attestation_data_t* attestation_data)
// Version - atm should always be 1
uint8_t version = 0;
READ_FIELD(&version, sizeof(version));
+
+#ifdef CONFIG_JADE_FAKEPROD
+ if (version != JADE_ATTEST_CURRENT_VERSION) {
+ JADE_LOGE("Attestation data unexpected version %u", version);
+ return false;
+ }
+#else
JADE_ASSERT(version == JADE_ATTEST_CURRENT_VERSION);
+#endif
// Read the encrypted attestation data struct
READ_FIELD(attestation_data, sizeof(attestation_data_t));
### main/gui.c
@@ -28,11 +28,11 @@
// A genuine production v2 Jade may be awaiting mandatory attestation data
#if defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) && defined(CONFIG_SECURE_BOOT) \
- && defined(CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS)
+ && defined(CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS) && !defined(CONFIG_JADE_FAKEPROD)
#include "attestation/attestation.h"
static inline bool gui_awaiting_attestation_data(void) { return !attestation_initialised(); }
#else
-// Jade v1.x and diy devices are never awaiting mandatory attestation data
+// Jade v1.x, diy, and fakeprod devices are never awaiting mandatory attestation data
static inline bool gui_awaiting_attestation_data(void) { return false; }
#endif
### main/main.c
@@ -108,8 +108,10 @@ static void ensure_boot_flags(void)
#ifndef CONFIG_IDF_TARGET_ESP32S3
esp_efuse_disable_basic_rom_console();
#endif
+#ifndef CONFIG_JADE_FAKEPROD
esp_efuse_disable_rom_download_mode();
#endif
+#endif
}
static void validate_running_image(void)
### main/process/dashboard.c
@@ -33,11 +33,11 @@
// A genuine production v2 Jade may be awaiting mandatory attestation data
#if defined(CONFIG_BOARD_TYPE_JADE_V2_ANY) && defined(CONFIG_SECURE_BOOT) \
- && defined(CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS)
+ && defined(CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS) && !defined(CONFIG_JADE_FAKEPROD)
#include "attestation/attestation.h"
static inline bool awaiting_attestation_data(void) { return !attestation_initialised(); }
#else
-// Jade v1.x and diy devices are never awaiting mandatory attestation data
+// Jade v1.x, diy, and fakeprod devices are never awaiting mandatory attestation data
static inline bool awaiting_attestation_data(void) { return false; }
#endif
### tools/fakeprod_attest.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEAujzIf4me7ujQT6JswMRON171E8C6XPoF/SeeCp9p9Odc/dyJ
+U50mD2oqWdwpoZ1uHW6VVUwXnd7Tkm066l/fJoZc8iCA9BUnWDb88b9WNXU21ss0
+QFv5yivGPAV5WHKf78VjWKOFkaSKP+eVPmAIyC232xFNBudy83odHlBMfPrdyIEp
+lgQhKwzh6VtigLUFZrAFp4+OaXSNivDXlght5uDesX//kRSFNEOIZ+KUfHi6Q5gK
+e4FldhDBeJrQ/SflRoIiLiyr4CdoSAHhYf7sY5xoDKGXCQXP5hP0HD9jyihtxTpz
+482lH7vwMNeF7C2A/0vPYdIC8RkcZSsM7fy1oO5vbMHej8sSvYugWvt2Mv/5SVQ+
+7EyVoP3jtjL8dTTBmTklFPj4izufMD+A5+R5flBh2FF+D7UAvOBsWmqE6ZmNqiiS
+QGXROmWRCeTMqtwLUVjKHUjsryvpkMNKg6vtBQNRx5OYhwPCoqZRciWCO6oDfWSW
++Zk/OLliLjwf0QeD87JrQGDw4U0dGCRxEJDSZpVj01RhtCsdrqkE+2XCAZ7d7KIq
+bw9c1as9zkzWuo8Ek9mZ0uGsPxa7BR5GnhdRvHtAypbzQ0REvHEIdXiDwt/EPEWE
++sLlKxkNP5pxW/BnDxM0iVNlyCkCARL9STRNEUe4Uu/pjunx0u6uWApbUDECAwEA
+AQKCAgAGyQmmCenSOwXB7QFI2a4e2tUTielQU2St90hNC0dVPqu27RbMxtQ46g2O
+dhTHOGDnbhy/PZU7sX9kXZnwqZsMAIe446HHItEq59/539aAfpuHomIuORul98fS
+boGgDRcSKY8nztdiOcw2rVWU7vNK9c11zBDSIFMiF1Ahx1uuDwbPFBOjjVV3B9Jg
+Se3SEiZW+jFOjE6sPWydxA4av4Zfi9BZR7hUPRroT1CUNl9iFJT2uv6gxtNfRJeD
+8SINoys5kSq1I3gEo/Lq8rSXOS48J9nroeSKld1f/NifQdj9J9FuBuXrhhXOzO5g
+nS2aMAM76fNtlTT5u0g7UGD5d8RXP2MWS2CqosrcxJ0oIQw8tHH8BNnOkwvzYHtX
+6UMxD3Sk2ig0jecHqgmL1d40RyjJPMxaBfQp4SkGxBe7HbvWVJYqYeNC60CzF+M0
+Hju16HcY56dO+jSmHu31guB15ChwhpDXzZkxwXMAYlVw0Ddnclko1C0P+KQZ8ewx
+A8T/CzRAQmTJHtnkG/teusAfMw7wSKA9JyxFYvdt3k0Iw+iBpoUe0nknnnw4DGbZ
+kyAz27HeZ4xfQ8wXq6v8MPya5hoUsSgOTI2phxbvFzC5bBzJTv9Mlwcyudgiw2hc
+Qq8k3MTaXcM1CZIqb/Ve2+fVuCHE2cSToqLtMdCd8JWJtNzxJQKCAQEA+ZDqGGxy
+pH5IxUtV869FHJYfPujcKxKN5b2JPkZpW1O01Sk2CufKnF0Xc5B+kmxDONa1jo5W
+Vd3BFqWKvv5Cmaq8X27Y/5L62cRFqgbl817HQXz4PshyDElhDxVu7ECHSI4ZdD7k
+o0NX2ABWhtnyt8yvMA5ExmiyH2ictKFps/zPaP0LPN8zyVeeeDdY72snoHZe6E5l
+GhtZ+J6zS1LVBuJc+sMckOTPhy5qy5APYAooRp2xKbtfowwicu44umqCmOp7H+MU
+wYuGIVdGodYmOpKAzW6uFY2ySfcFUGQBezt1KLNJbnd2xDZBdofgLBD/7SFilImp
+Ryr25bykjwvhdQKCAQEAvwnpnippwmpeQyn5WVPclJd2OQ6c6yAEYu8bHsgTxHJx
+SnzTlJz7J1f7beGnYpv/SMx5mUpkLqjEgbPP3IcZ4y4rZrNVHi7Ftwdil16/jl9b
+/SwAZdKdLcT3kXkbOPNrlUDriRcR8cV0bC5ojkHTF0x4c6QcSj1ci1NXDFt/q03y
+cZPI1xaWSixFUAmVaPC85jyhDzWjQescIsfJHVOkBvJibSmA5xLInStLK4dfviNW
+pkfRZatm1a/PuOcjAIpmpbU57iDfHlk8hDFzy2zWG7wHZSIJ/Erxclw/RRqP/lqu
+MaIore9qfsn/v8PMDDrIeKTjF9maWIZKGGKlhxaATQKCAQAErR51zrJnOqaKa9r0
+cfhNjNKRDzPw6qwV/fMwY4ld2LeuttL4aVd2jpf95ls4yyaHTh6gyTH88ebIUdnD
+smf1ESnub9lJEKv6oJ7djLuKT+BT7c+I/aCFcmqLKc+mRZ9DUMZRU0xdcsmWlosI
+g9T0dLVjpSDMxvFvfTpSeCPO55HLWdJ4FYaW4zlo70CPW+amLxGgMLfcQV7yNacO
+ZkLvmhY77KQAYwkwc0I1BkIDfq06hYHoUkZ8e6G5nZUaiRxhWSNteYfOCJs+zK/a
+IHUe69fWAZjpdno8wJAs7XLJqFeNp65cj4h+LLYVTn541Oi2vMVoGxw85SUzMNG0
+jHhxAoIBAF6KmEvG75ngLBAPCLBVWTdMEZzBB5Uwvhr7+67rEyVIAZr8VXUZjw10
+dCB36O9bBiZP0umysAuLR9xsUTTwwzXzllT38OrD+OLQS9FfeWsK9CpBiVzasyCA
+dohOp9MyXA58F1Q7z4yXzDrxaJrmJoJEPOP11m5DGbZ1owGhkngDVjUxDtaq/ett
+J0wpwgAyCznhzU8Km2W25bCssHHL/8PUVjNxs7InUJK/Zy7+iro9INefeRhYj8a2
+gBUghejxlUTyf93B9Mk/HjzJgGWu0fLdxccd5lUt/Xk7/6/CKnqVA9naR0l0z345
+l1CepC91M9rItv4JmWGv66FPyF0nHI0CggEACouK0QLI4GPx0dn4Y4UgRMex9VOJ
+No16e+lEU17nm3MLKQShLwqc0NfvHBAXTwuK+sT+nEP7CA7Gu/aIevgQlS+tqmil
+r3Hpfk+ZDCunxWVJFSpQ5dvGU3lRdFlK2x+xPUgehlAXeKnQaGzeQ0B0lqE023JT
+tBRmZWZynIPnp7hP7D3uSEC13xRjxR9JeUaH3m84GVyHHJYL8AQEgeN5c/v/V/AK
+pqO5OTcScbs3UDozhvnx5eABHK0fZKPlSBBWzLkBLIEZWj/q6JAOsbmG8/PVaLpD
+J+GwylR5xwPRWwXAGpQDqSl18PUWpapuWDKhbq5Zhm5FFCSYVnPWIiyKqw==
+-----END RSA PRIVATE KEY-----
### tools/fakeprod_v2.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
### tools/switch_to.sh
@@ -6,6 +6,7 @@ CONFIG=""
ARCH=""
DEVELOPMENT=""
NORADIO=""
+FAKEPROD=""
CI=""
LOG=""
DEBUG=""
@@ -24,6 +25,7 @@ function usage {
echo "WARNING: THIS SCRIPT IS FOR JADE DEVELOPMENT ONLY";
echo "JADE OPTIONS:"
echo " --noradio Disable Bluetooth support"
+ echo " --fakeprod Prod-like build with secureboot (implies --debug)"
echo " --log Enable logging"
echo " --log-cbor Enable CBOR logging messages over the serial API"
echo " --log-wifi Enable text logging over WiFi"
@@ -69,6 +71,15 @@ function remove_config()
sed -i "/^$REGEX/d" sdkconfig.defaults
}
+function remove_config_if_present()
+{
+ # Remove config value if present: ignore if missing
+ REGEX="$1="
+ if grep -q "$REGEX" "sdkconfig.defaults"; then
+ sed -i "/^$REGEX/d" sdkconfig.defaults
+ fi
+}
+
while true; do
case "$1" in
jade) CONFIG=$1; ARCH="esp32"; shift ;;
@@ -78,6 +89,7 @@ while true; do
qemu) CONFIG=$1; ARCH="esp32"; shift ;;
--dev) DEVELOPMENT="y"; shift ;;
--noradio) NORADIO=1; shift ;;
+ --fakeprod) FAKEPROD=1; shift ;;
--ci) CI="1"; shift ;;
--log) LOG=uart; shift ;;
--log-cbor) LOG=cbor; shift ;;
@@ -113,6 +125,10 @@ else
fi
fi
+if [ -n "$FAKEPROD" ] && [ -n "$DEVELOPMENT" ]; then
+ usage "--fakeprod must not be given with --dev"
+fi
+
# TODO: standardize the naming convention for prod/dev configs
if [ -n "${DEVELOPMENT}" ]; then
if [ "$CONFIG" = "qemu" ]; then
@@ -245,6 +261,10 @@ if [ -n "$JTAG" ]; then
set_config CONFIG_JADE_USE_USB_JTAG_SERIAL y
set_config CONFIG_LIBC_STDIN_LINE_ENDING_LF y
set_config CONFIG_LIBC_STDOUT_LINE_ENDING_LF y
+ # setting present on PROD configs but not on DEV configs
+ remove_config_if_present CONFIG_ESP_CONSOLE_NONE
+ set_config CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG y
+ remove_config_if_present CONFIG_USJ_ENABLE_USB_SERIAL_JTAG
fi
if [ -n "$PSRAM" ]; then
echo "updating config file for PSRAM support ..."
@@ -285,6 +305,35 @@ if [ -n "$UNAMALGAMATED" ]; then
echo "updating config file for unamalgamated build ..."
set_config CONFIG_AMALGAMATED_BUILD n
fi
+if [ -n "$FAKEPROD" ]; then
+ echo "updating config file for fakeprod build ..."
+ # Sign with the non-secret fakeprod key so the unit can always
+ # be re-flashed with newly-signed firmware. A single signature is accepted
+ # (CONFIG_SECURE_BOOT_V2_MIN_SIGNATURES=1).
+ SIGNING_KEY="tools/fakeprod_v2.pem"
+ if [ ! -f "$SIGNING_KEY" ]; then
+ echo "error: fakeprod signing key $SIGNING_KEY not found" >&2
+ exit 1
+ fi
+ set_config CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES y
+ set_config CONFIG_SECURE_BOOT_SIGNING_KEY "\"$SIGNING_KEY\""
+ # Single in-tree key is sufficient for the dev unit
+ set_config CONFIG_SECURE_BOOT_V2_MIN_SIGNATURES 1
+ # Show panic backtraces instead of rebooting silently
+ remove_config CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
+ set_config CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT y
+ # Development-mode flash encryption (re-flashable)
+ remove_config CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE
+ set_config CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT y
+ # Keep JTAG usable on the dev unit
+ set_config CONFIG_SECURE_BOOT_ALLOW_JTAG y
+ # Keep ROM download mode so esptool can still reflash the unit
+ remove_config CONFIG_SECURE_DISABLE_ROM_DL_MODE
+ # Skip the irreversible ROM-download-mode efuse write in ensure_boot_flags()
+ set_config CONFIG_JADE_FAKEPROD y
+ # Always enable debug mode in fakeprod
+ set_config CONFIG_DEBUG_MODE y
+fi
echo "============================================"
if [ -z "$SKIP_RECONFIGURE" ]; thenWhy this scored 21/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.