Try   HackMD

Solo 2 Getting Started (for developers)

Monorepo: https://github.com/solokeys/solo2
Hardware: https://github.com/solokeys/solo2-hw

DO NOT FOLLOW THIS GUIDE. IT WILL BRICK YOUR KEY.

If you follow this document, it will reflash your hacker key into a state where you can not reset into the bootloader, and can not reflash.

This document contains many incorrect and outdated commands.

Do not proceed with this guide unless you can recover a key!!!

DO NOT FOLLOW THIS GUIDE. IT WILL BRICK YOUR KEY.

Feel free to edit or comment on this getting started page directly
Shortlink: https://solo2.dev

This documentation is CC BY-SA licensed.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Contact

Other chat rooms of interest:

Overview

  1. setup machine with Rust + dependencies
  2. important: get an NXP LPC55 devkit. While working with the hacker edition is certainly possible, recovering the devkit from a bricked state is easier than with the Solokey itself.
  3. configure debugging
  4. checkout github/solokeys/solo2 and run make run-dev
    This descends into runners/lpc55 and calls
    ​​​​cargo run --release --features board-lpcxpresso55,develop
    
    using arm-none-eabi-gdb as runner as configured in .cargo/config.

The feature develop activates useful features during development

  • no PRINCE encryption of data section (so no need for PUF enrolment, secure boot, etc.)
  • silent authentication (buttons are "pressed" without pressing them)
  • disables the FIDO app's reset time window

See runners/lpc55/Cargo.toml for all features.

To just build: make build-dev
Helpful during development: cargo install bacon; cd runners/lpc55; bacon -j check-dev

Very basic test:

  • pip install 'fido2~=0.9'
  • python tests/basic.py

Initial Setup

Install Rust/Cargo from rustup.rs:
https://www.rust-lang.org/tools/install

rustup target install thumbv8m.main-none-eabi
cargo install flip-link
cargo install cargo-binutils
rustup component add llvm-tools-preview

Then perform OS/distro specific steps listed below.

Both VS Code + neovim (nightly 0.5 preview) work well with rust-analyzer.

For debugging setup, please see the debugging section.

Debian/Ubuntu

sudo apt-get install git llvm clang libclang-dev gcc-arm-none-eabi gdb-arm-none-eabi libc6-dev-i386

On Debian, you need to use the gdb-multiarch executable instead of arm-none-eabi-gdb, so either create a symlink or update .cargo/config.

Arch Linux

yay clang llvm arm-none-eabi-gdb

NixOS

You can use the following mkShell expression inside a flake.nix or shell.nix, as long as you provide nixpkgs and nixpkgs-mozilla.

{ nixpkgs, mozilla }:

let
    rust-overlay = import "${mozilla}/rust-overlay.nix";
    pkgs = import nixpkgs {
        overlays = [ rust-overlay ];
    };
    
    rust_stable = pkgs.latest.rustChannels.stable.rust;

    rust_thumbv8m = rust_stable.override {
        targets = [ "thumbv8m.main-none-eabi" ];
    };

    pkgs_cross = pkgs.pkgsCross.arm-embedded;

    gcc = pkgs_cross.buildPackages.gcc;

    libc_include = "${gcc.libc}/${gcc.libc.incdir}";

    gcc-unwrapped = pkgs_cross.buildPackages.gcc-unwrapped;

    gcc_include = "${gcc-unwrapped}/lib/gcc/${gcc-unwrapped.targetConfig}/${gcc-unwrapped.version}/include";

in pkgs.mkShell {
    nativeBuildInputs = [
        gcc
        rust_thumbv8m

        # Is being added to nixpkgs: https://github.com/NixOS/nixpkgs/pull/121184
        #pkgs.flip-link

        pkgs.llvm
        pkgs.wget
    ];

    LIBCLANG_PATH = "${pkgs.llvmPackages.libclang}/lib";

    TARGET_CC = "${gcc.targetPrefix}cc";
    TARGET_AR = "${gcc.targetPrefix}ar";

    TARGET_CFLAGS = "-I${libc_include}";

    BINDGEN_EXTRA_CLANG_ARGS = "-I${libc_include} -I${gcc_include}";
}

macOS

TODO (doesn't work currently, because of littlefs2 which cannot be easily compiled on macOS)

Win10

TODO

Bootloader

There are multiple possibilities for writing firmware to the Solo 2.

lpc55-host

Install the lpc55 utility:

cargo install lpc55

Bring the device into bootloader mode.
It should then show up using the lpc55 tool.

$ lpc55 ls
bootloaders:
Bootloader { vid: 1209, pid: B000, uuid: A2... }

Making a backup of the flash

Before writing custom firmware and data onto your Solo's flash, make a backup to ensure that you can go back to a sane state later.

# Read the program memory flash
lpc55 read-memory -vvv 0 524288 -o solo2-fw-backup-program.bin
# Also read the Protected Flash Region pages.
# CFPA scratch 0x9de00
lpc55 read-memory -vvv 646656 512 -o solo2-fw-backup-pfr-cfpa-scratch.bin
# CFPA ping 0x9e000
lpc55 read-memory -vvv 647168 512 -o solo2-fw-backup-pfr-cfpa-ping.bin
# CFPA pong 0x9e200
lpc55 read-memory -vvv 647680 512 -o solo2-fw-backup-pfr-cfpa-pong.bin
# CMPA 0x9e400
lpc55 read-memory -vvv 648192 512 -o solo2-fw-backup-pfr-cmpa.bin

Disabling Secure Boot

To be able to run custom firmware, we need to disable secure boot.
This is done by disabling the SECURE_BOOT_CFG / SEC_BOOT_EN flag in the CMPA protected flash region page.

First, read the PFR with the lpc55-host tool:

lpc55 pfr --format yaml > pfr-settings.yaml
cp pfr-settings.yaml pfr-settings-no-secure-boot.yaml

⚠️ IMPORTANT: You now need to modify the copied file. Only keep the factory top level object and set secure-boot-configurationsecure-boot-enabled: false.

Your file should look something like this:

factory:
  boot-configuration:
    speed: 48MHz
    mode: Usb
  usb-id:
    vid: 4617
    pid: 45056
  secure-boot-configuration:
    secure-boot-enabled: false # this was changed
    dice-computation-disabled: true
  rot-fingerprint: ... # omitted, don't change in your file
  customer-data: ... # omitted, don't change in your file

After that, we can write the CMPA settings:

lpc55 configure factory-settings -vvv pfr-settings-no-secure-boot.yaml

Flashing The Firmware

After secure boot was disabled, you can flash the self-built firmware.

lpc55 write-flash -vvv path/to/provisioner.bin

When you are really sure that the flashing process worked fine, restart the Solo2.

lpc55 reboot

mboot

Install cargo-binutils:

$ cargo install cargo-binutils
$ rustup component add llvm-tools-preview

Install mboot, for example using pip and a virtual environemnt:

$ python3 -m venv mboot
$ source mboot/bin/activate
$ pip install mboot

If you want to perform the following steps without superuser rights, install these UDEV rules:

$ curl https://raw.githubusercontent.com/molejar/pyIMX/master/udev/90-imx-sdp.rules | sudo tee /etc/udev/rules.d/90-imx-sdp.rules
$ sudo udevadm control --reload-rules

Check that the device is connected and in bootloader mode:

$ mboot info
                                       
 DEVICE: USB COMPOSITE DEVICE (0x1FC9, 0x0021)

...

Compile the firmware and prepare a binary image (see the Overview section for information about the features):

$ cargo objcopy --release --features board-lpcxpresso55,develop -- -O binary firmware.bin

Flash the firmware image to the MCU:

$ mboot erase --mass
$ mboot write firmware.bin

Application-specific Setup

fido-authenticator

fido-authenticator needs an attestation key and certificate, both with the ID 0. These can be generated using solo2-cli (requires the dev-pki feature):

$ solo2-cli dev-pki fido fido.key fido.cert

These files (or any other files in the required format) can then be written to the device using the provisioner app. First, run the provisioner app on the prototype:

$ cd runners/lpc55 && make run-pro

Then use solo2-cli to write the key and the certificate to the device:

$ solo2-cli app provisioner write-file fido.cert fido/x5c/00
$ solo2-cli app provisioner write-file fido.key fido/sec/00

(Make sure that there is no other pcsc smartcard connected to the device becuase currently, solo2-cli can’t handle multiple available smartcards.)

Debugging

To debug the firmware on the board, the arm version of GDB (Debian gdb-multiarch and Arch arm-none-eabi-gdb) is required.

Initial Debugging Setup

If you want to use a J-Link (highly recommended, as it can be used to flash the firmware without bootloader mode), install the J-Link Software and Documentation Pack providing JLinkGDBServer.

DevKit

Flash the JLink debugger firmware to the NXP LPC55 devkit (guide – if you figure out how to use probe.rs instead, please share!)
Then you need to connect both the debug port via USB and USB for the LPC55 itself.

Solo 2 Hacker

For debugging with the Solo 2 Hacker edition, you need a special cable (TC2030-CTX No Legs, 6 PIN, 10 PIN - note the number of pins for the ribbon connector).
This can be used with the Segger JLink EDU mini, which you are allowed to use in private or educational contexts.
Note: The outer metal pins are too long for the spring pins to make contact with the Solo 2. You need to shorten them carefully.

Debugging With GDB

  1. With the devboard connected to the Debug Link port, run JLinkGDBServer -strict -device LPC55S69 -if SWD -vd.
  2. In a separate terminal, run nc localhost 19021 to connect to the RTT port of the J-Link server. This will show you the delog log output of the firmware via RTT.
  3. Build the Solo 2 firmware for the devboard using make build-dev (see runners/lpc55 for more information on building with logging enabled).
  4. Inside the folder of the Solo 2 firmware, run arm-none-eabi-gdb -x runners/lpc55/jlink.gdb runners/lpc55/target/thumbv8m.main-none-eabi/release/runner. This executes the GDB commands from the runners/lpc55/jlink.gdb file and loads the symbols from the specified runner binary.
  5. Finally, you should see the output of the firmware in the running netcat.

Debugging with VS Code

If you're not the command-line person but would still like to debug the firmware, the marus25.cortex-debug VS Code extension will do the heavy-lifting for you.
Note, however, that is does not work as well as plain GDB does.
When setting breakpoints using the VS Code interface, make sure to open the files that are actually compiled in the firmware.
Thus, setting a breakpoint in the fido-authenticator for example, will require you to open the corresponding file in your .cargo folder, unless you modified the Cargo.toml to use a local copy of the source code.

The following is an examplary launch configuration for VS Code:

{
    "type": "cortex-debug",
    "request": "launch",
    "servertype": "jlink",
    "executable": "./solo2/runners/lpc55/target/thumbv8m.main-none-eabi/release/runner",
    "name": "Debug (J-Link)",
    "device": "LPC55S69",
    "interface": "swd",
    "cwd": "${workspaceRoot}",
    "serverArgs": [
        "-strict",
        "-vd",
    ],
    "rttConfig": {
        "enabled": true,
        "address": "auto",
        "decoders": [
            {
                "label": "",
                "port": 0,
                "type": "console"
            }
        ]
    }
}