$2 USB crypto token for use with GPG and SSH

If you are interrested in security devices this post may be for you. Recently I’ve found a software which makes a GPG security device out of a STM32. It’s called Gnuk. You just need a compatible board, programmer, flash the firmware and you are ready to go. I was just hoping to find some cheap device to flash to play with it.

My next condition was it has to come with nice casing – I won’t carry some unprotected dev board in my pocket – ideally an USB key. Devices recommended in project itself were either not available anymore or expensive or just plain boards.


After a short research on google and aliexpress I found ST-link programmer clones. They are very cheap, have nice aluminium case and should contain STM32F103. So I ordered one. The advantage is that if you don’t have STM programmer, you can order two and you will have everything needed.

When it came it looked very nice so I tore it right down:

It was a little disappointment when I saw the chip – STM32F101CBT6 (128kB flash). I was afraid that I’ll need to port the software and moreover according to specs it shouldn’t have USB phy so I though they bit-banged the USB interface. Then I found one page where they discussed these devices and and they mentioned that in fact they have USB and they are compatible with F103.

So I gave it a try.


First I built the firmware:

danman@silverhorse:~$ git clone git://anonscm.debian.org/gnuk/gnuk/gnuk.git
Cloning into 'gnuk'...
remote: Counting objects: 11644, done.
remote: Compressing objects: 100% (3812/3812), done.
remote: Total 11644 (delta 8560), reused 10561 (delta 7749)
Receiving objects: 100% (11644/11644), 12.04 MiB | 1.69 MiB/s, done.
Resolving deltas: 100% (8560/8560), done.
danman@silverhorse:~$ cd gnuk/
danman@silverhorse:~/gnuk$ git submodule update --init
Submodule 'chopstx' (git://anonscm.debian.org/gnuk/chopstx/chopstx.git) registered for path 'chopstx'
Cloning into '/home/danman/gnuk/chopstx'...
Submodule path 'chopstx': checked out '87767f1be5d8d4adac8d23a285250299eccee3fb'
danman@silverhorse:~/gnuk$ cd src/
danman@silverhorse:~/gnuk/src$ ./configure --vidpid=234b:0000 --target=ST_DONGLE
Header file is: board-st-dongle.h
Debug option disabled
Configured for bare system (no-DFU)
PIN pad option disabled
CERT.3 Data Object is NOT supported
Card insert/removal by HID device is NOT supported
Life cycle management is NOT supported
danman@silverhorse:~/gnuk/src$ make
mkdir -p build

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/main.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/main.o.d -I. -I../polarssl/include -I../chopstx main.c -o build/main.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/call-rsa.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/call-rsa.o.d -I. -I../polarssl/include -I../chopstx call-rsa.c -o build/call-rsa.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/mcu-stm32f103.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/mcu-stm32f103.o.d -I. -I../polarssl/include -I../chopstx mcu-stm32f103.c -o build/mcu-stm32f103.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/usb_desc.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/usb_desc.o.d -I. -I../polarssl/include -I../chopstx usb_desc.c -o build/usb_desc.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/usb_ctrl.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/usb_ctrl.o.d -I. -I../polarssl/include -I../chopstx usb_ctrl.c -o build/usb_ctrl.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/usb-ccid.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/usb-ccid.o.d -I. -I../polarssl/include -I../chopstx usb-ccid.c -o build/usb-ccid.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/openpgp.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/openpgp.o.d -I. -I../polarssl/include -I../chopstx openpgp.c -o build/openpgp.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/ac.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/ac.o.d -I. -I../polarssl/include -I../chopstx ac.c -o build/ac.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/openpgp-do.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/openpgp-do.o.d -I. -I../polarssl/include -I../chopstx openpgp-do.c -o build/openpgp-do.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/flash.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/flash.o.d -I. -I../polarssl/include -I../chopstx flash.c -o build/flash.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/bn.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/bn.o.d -I. -I../polarssl/include -I../chopstx bn.c -o build/bn.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/mod.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/mod.o.d -I. -I../polarssl/include -I../chopstx mod.c -o build/mod.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/modp256r1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/modp256r1.o.d -I. -I../polarssl/include -I../chopstx modp256r1.c -o build/modp256r1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/jpc_p256r1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/jpc_p256r1.o.d -I. -I../polarssl/include -I../chopstx jpc_p256r1.c -o build/jpc_p256r1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/ec_p256r1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/ec_p256r1.o.d -I. -I../polarssl/include -I../chopstx ec_p256r1.c -o build/ec_p256r1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/call-ec_p256r1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/call-ec_p256r1.o.d -I. -I../polarssl/include -I../chopstx call-ec_p256r1.c -o build/call-ec_p256r1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/modp256k1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/modp256k1.o.d -I. -I../polarssl/include -I../chopstx modp256k1.c -o build/modp256k1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/jpc_p256k1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/jpc_p256k1.o.d -I. -I../polarssl/include -I../chopstx jpc_p256k1.c -o build/jpc_p256k1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/ec_p256k1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/ec_p256k1.o.d -I. -I../polarssl/include -I../chopstx ec_p256k1.c -o build/ec_p256k1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/call-ec_p256k1.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/call-ec_p256k1.o.d -I. -I../polarssl/include -I../chopstx call-ec_p256k1.c -o build/call-ec_p256k1.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/mod25638.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/mod25638.o.d -I. -I../polarssl/include -I../chopstx mod25638.c -o build/mod25638.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/ecc-edwards.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/ecc-edwards.o.d -I. -I../polarssl/include -I../chopstx ecc-edwards.c -o build/ecc-edwards.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/ecc-mont.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/ecc-mont.o.d -I. -I../polarssl/include -I../chopstx ecc-mont.c -o build/ecc-mont.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/sha512.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/sha512.o.d -I. -I../polarssl/include -I../chopstx sha512.c -o build/sha512.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/random.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/random.o.d -I. -I../polarssl/include -I../chopstx random.c -o build/random.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/neug.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/neug.o.d -I. -I../polarssl/include -I../chopstx neug.c -o build/neug.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/sha256.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/sha256.o.d -I. -I../polarssl/include -I../chopstx sha256.c -o build/sha256.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -g -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/bignum.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/bignum.o.d -I. -I../polarssl/include -I../chopstx ../polarssl/library/bignum.c -o build/bignum.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/rsa.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/rsa.o.d -I. -I../polarssl/include -I../chopstx ../polarssl/library/rsa.c -o build/rsa.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/aes.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/aes.o.d -I. -I../polarssl/include -I../chopstx ../polarssl/library/aes.c -o build/aes.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/entry.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/entry.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/entry.c -o build/entry.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/chopstx.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/chopstx.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/chopstx.c -o build/chopstx.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/eventflag.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/eventflag.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/eventflag.c -o build/eventflag.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/sys-stm32f103.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/sys-stm32f103.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/mcu/sys-stm32f103.c -o build/sys-stm32f103.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/usb-stm32f103.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/usb-stm32f103.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/mcu/usb-stm32f103.c -o build/usb-stm32f103.o

arm-none-eabi-gcc -c -mcpu=cortex-m3 -O3 -Os -g -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wstrict-prototypes -Wa,-alms=build/adc-stm32f103.lst -mthumb -mno-thumb-interwork -DTHUMB -MD -MP -MF .dep/adc-stm32f103.o.d -I. -I../polarssl/include -I../chopstx ../chopstx/contrib/adc-stm32f103.c -o build/adc-stm32f103.o

arm-none-eabi-gcc build/main.o build/call-rsa.o build/mcu-stm32f103.o build/usb_desc.o build/usb_ctrl.o build/usb-ccid.o build/openpgp.o build/ac.o build/openpgp-do.o build/flash.o build/bn.o build/mod.o build/modp256r1.o build/jpc_p256r1.o build/ec_p256r1.o build/call-ec_p256r1.o build/modp256k1.o build/jpc_p256k1.o build/ec_p256k1.o build/call-ec_p256k1.o build/mod25638.o build/ecc-edwards.o build/ecc-mont.o build/sha512.o build/random.o build/neug.o build/sha256.o build/bignum.o build/rsa.o build/aes.o build/entry.o build/chopstx.o build/eventflag.o build/sys-stm32f103.o build/usb-stm32f103.o build/adc-stm32f103.o -mcpu=cortex-m3 -nostartfiles -Tgnuk.ld -Wl,-Map=build/gnuk.map,--cref,--no-warn-mismatch,--gc-sections -mthumb -mno-thumb-interwork -o build/gnuk.elf
arm-none-eabi-objcopy -O binary build/gnuk.elf build/gnuk.bin


Then I connected my J-link programmer. VCC and GND was easy to probe and the rest two pins (SWD, SWCLK) were identified by trying. The order of pins is following (first is closest to USB):

  • GND
  • 3V3

For connecting using openocd I used a config which I had used before for F103.

#daemon configuration
telnet_port 4444
gdb_port 3333

interface jlink
transport select swd

# The chip has 64KB sram
set WORKAREASIZE 0x10000

source [find target/stm32f1x.cfg]
#adapter_khz 100
gdb_breakpoint_override hard

After starting openocd, I unlocked flash, and burned new firmware:

$ openocd stm32f103.cfg

#in new shell
$ telnet localhost 4444> stm32f1x unlock 0
Device Security Bit Set
stm32x unlocked.
INFO: a reset or power cycle is required for the new settings to take effect.
> reset halt
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
> stm32f1x unlock 0
Device Security Bit Set
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000800
stm32x unlocked.
INFO: a reset or power cycle is required for the new settings to take effect.
> reset halt 
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
> flash write_bank 0 ./src/build/gnuk.bin 0 
wrote 115712 bytes from file ./src/build/gnuk.bin to flash bank 0 at offset 0x00000000 in 3.776280s (29.924 KiB/s)
> reset

The device identified to PC as Gnuk!

3011.693402] usb 2-3: New USB device found, idVendor=234b, idProduct=0000
[ 3011.693406] usb 2-3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 3011.693408] usb 2-3: Product: Gnuk Token
[ 3011.693410] usb 2-3: Manufacturer: Free Software Initiative of Japan
[ 3011.693412] usb 2-3: SerialNumber: FSIJ-1.2.4-67055638

After some brief tests it was clear that it works.

Physical button

This was nice but I wanted to push it further – I wanted to implement HW button to confirm crypto operations. For this I used the breakout IO pins. First I used just some jumper wires to test my patches to gnuk:

When I was happy with the function I was going to add a real button, so I desoldered the pin header.

I’ve had some PCB button at hand and I wanted to solder it directly to the edge of the board. For this I needed one IO pin which will be in the right distance from GND pin. Unfortunately there no such pin. So as a workaround, I soldered the button between two IO pins (NRST, SWIM) and I set one of them (SWIM) to zero by firmware.

Now I have USB GPG token which is small, looks nice and have desired features.

You can download my patches here gnuk-button.


Now lets see how the whole thing works with SSH. You can get more info on these sites:

I’m going to provide just some crash-course here.

  1. disable pcscd
    $ sudo systemctl disable pcscd
    Synchronizing state of pcscd.service with SysV service script with /lib/systemd/systemd-sysv-install.
    Executing: /lib/systemd/systemd-sysv-install disable pcscd
    Removed /etc/systemd/system/sockets.target.wants/pcscd.socket.
  2. install packages:
    $ sudo apt-get install opensc pcscd paperkey haveged gnupg2 gnupg-agent pinentry-curses libccid scdaemon libksba8 libpth20
  3. check token info
    $ gpg --card-status
    Reader ...........: 234B:0000:FSIJ-1.2.4-67055638:0
    Application ID ...: D276000124010200FFFE670556380000
    Version ..........: 2.0
    Manufacturer .....: unmanaged S/N range
    Serial number ....: 67055638
    Name of cardholder: [not set]
    Language prefs ...: [not set]
    Sex ..............: unspecified
    URL of public key : [not set]
    Login data .......: [not set]
    Signature PIN ....: forced
    Key attributes ...: rsa2048 rsa2048 rsa2048
    Max. PIN lengths .: 127 127 127
    PIN retry counter : 3 3 3
    Signature counter : 0
    Signature key ....: [none]
    Encryption key....: [none]
    Authentication key: [none]
    General key info..: [none]
  4. generate keys on token (private key never leaves token)
    $ gpg --card-edit
    gpg/card> admin
    Admin commands are allowed
    gpg/card> generate
    Make off-card backup of encryption key? (Y/n) n
    Please note that the factory settings of the PINs are
     PIN = '123456' Admin PIN = '12345678'
    You should change them using the command --change-pin
    What keysize do you want for the Signature key? (2048) 
    What keysize do you want for the Encryption key? (2048) 
    What keysize do you want for the Authentication key? (2048) 
    Please specify how long the key should be valid.
     0 = key does not expire
     <n> = key expires in n days
     <n>w = key expires in n weeks
     <n>m = key expires in n months
     <n>y = key expires in n years
    Key is valid for? (0) 
    Key does not expire at all
    Is this correct? (y/N) y
    GnuPG needs to construct a user ID to identify your key.
    Real name: Igor Timko
    Email address: 
    You selected this USER-ID:
     "Igor Timko"
    Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
    ...this will take about 5 min and no progress is shown instead of shining LED on token...
    gpg: key F0A5780AA5B8E99F marked as ultimately trusted
    gpg: revocation certificate stored as '/home/danman/.gnupg/openpgp-revocs.d/322A0733037B0B3854A4959EF0A5780AA5B8E99F.rev'
    public and secret key created and signed.
    gpg/card> quit
    pub rsa2048 2017-08-01 [SC]
    uid Igor Timko
    sub rsa2048 2017-08-01 [A]
    sub rsa2048 2017-08-01 [E]
  5. create or edit ~/.gnupg/gpg-agent.conf
    default-cache-ttl 600
    max-cache-ttl 7200
  6. (re)start gpg-agent
    $ killall -9 gpg-agent ; gpg-agent --daemon
    SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh; export SSH_AUTH_SOCK;
  7. add this line into your ~/.bashrc
    SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh; export SSH_AUTH_SOCK;
  8. open new shell and list your key
    $ ssh-add -L
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzKNLGBEjXxFVrKS5/ivBMBjfnCJn404bUpoHFbhUXiuIyklWSwc6uhA+HO3pLvQPESkN834DYEy4uJoJIdzKOIBia+JtgtWOItKWVIvd90QThYsVtPdEYpbiMLS/hJ7BrFOuJ2A0L9sPPm9+8QiOZtXc5Qv5E6ua1skn6whwPxSx4QFqVL9TQ7pAxYusDGVmdPwUMSJyKj0sICPGYJBb+PSGa3KuDV3O53keJMj0SaLlVrruBeTUZXNsZ44s58g8TEC6hrKDPoZCxNyGf3l97iX2a0BB8HbmjXSyaaMQyHye09ITeQTAZqArxLxYd21m82iO+d2or9K4y/7S/DD/5 cardno:FFFE67055638
  9. Now you can add this key to your server and try to log-in.

If you have applied my patch with button, ssh starts logging in and then LED on your token starts to flash quickly. Now you have to press button on token to confirm authentication. You can see it on this video:

Feel free to ask any questions or comment.




49 thoughts on “$2 USB crypto token for use with GPG and SSH”

  1. I saw this article a few weeks ago and ordered two of these dongles almost immediately. They arrived today and I plan to get my hands dirty and test your project in a few days. One thing I noticed while setting up the code to compile is that the repos of gnuk and chopstx are a bit ahead and your code now has some limited “patcheability”. I’d suggest that instead of making the patches available, set up a repository with them already applied. This way everyone taking advantage of this wonderful discover can get the functionality you implemented and also receive the latest fixes for the original code. Nice work. Thank you!

    1. I wanted to include it in upstream but NIIBE doesn’t like this way of confirmation. Let me know if it worked for you.

      1. I think that keeping it as a fork is pretty fair for now. Anyway, I’ll try to merge your patches. Cheers.

  2. You did a good job detailing this post to a level, when even a novice can reproduce this project. I have ordered a pair of this tokens and successfully flashed the firmware.

    Here is some tweaks that I had to do.
    1) I have used this configuration for openocd:
    telnet_port 4444
    source [find interface/stlink-v2.cfg]
    source [find target/stm32f1x.cfg]

    2) openocd was giving me an error:
    Error: jtag status contains invalid mode value – communication failure
    To fix this just plug in both programmer and token to usb when flashing.

    3) Next flashes require erasing memory
    flash erase_sector 0 last

    4) patch could be applied still (12 dec 2017, revision c81544fffd2f89cb3cf1b791fe7adf21c75200ad)

    5) Button confirmation doesn’t work for me. The LED is flashing, but shorting pins RST and SWIM makes no effect (RST to GND didn’t work either)

    6) If you wish to remove keys from token, you can use tool/gnuk_remove_keys_libusb.py from gnuk.
    Don’t forget to kill gpg-agent/scdaemon etc (whatever could be using the usb device).

    1. Thanks for your notes sarg.

      ad 5) : which operation caused the LED to blink? key generation or authentication?

      1. It was authentication. Key generation was way too slow (I have waited 10 minutes without success), so I have ended with generating key on my PC.

        Btw, do you think it is a good idea to add some timeout to wait_button function?

  3. I’m currently stuck with this. I’m using a SL-Link V2 programmer to flash another one.
    My openocd config is:
    telnet_port 4444
    gdb_port 3333

    source [find interface/stlink-v2.cfg]

    set WORKAREASIZE 0x10000
    transport select “hla_swd”

    source [find target/stm32f1x.cfg]

    Flashing works fine but lsusb reports
    Bus 003 Device 007: ID 0000:0000

    and usb-devices
    T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 14 Spd=12 MxCh= 0
    D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
    P: Vendor=0000 ProdID=0000 Rev=00.00
    S: Manufacturer=Free Software Initiative of Japan
    S: Product=Gnuk Token
    S: SerialNumber=FSIJ-1.2.8-87014124
    C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
    I: If#=0x0 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)

    So it seems that the flashing worked, but the vendor id was not set.
    I ran ./configure –vidpid=234b:0000 –target=ST_DONGLE so it should be there.

    When I flashed the latest gnu.bin I got the following:
    flash write_bank 0 /home/user/gnuk/src/build/gnuk.bin 0
    target halted due to breakpoint, current mode: Thread
    xPSR: 0x61000000 pc: 0x2000003a msp: 0x20005000
    wrote 111616 bytes from file /home/user/gnuk/src/build/gnuk.bin to flash bank 0 at offset 0x00000000 in 3.619718s (30.113 KiB/s)

    What can I do to get this working?

    1. Hello, try running:
      > reset halt
      > stm32f1x mass_erase 0
      > flash write_bank 0 /home/user/gnuk/src/build/gnuk.bin 0

  4. Still doesn’t work. Can you share a working version of gnuk.bin?
    Now dmesg shows now, after erased the chip reset and tried to reflash it:
    [ 32.370362] usb 3-6: Device not responding to setup address.
    [ 32.590349] usb 3-6: Device not responding to setup address.
    [ 32.810142] usb 3-6: device not accepting address 7, error -71
    [ 32.810178] usb usb3-port6: unable to enumerate USB device

    I tired also to flash the gnuk.bin with stlink. The flashing itself worked, but same issue still exists.
    I don’t know if the cheap ST-Link V2 flasher is the problem or something else, because I currently only have this one.

    1. Ok with the help of danman i figured it out.

      tl;dr: run make build/gnuk-vidpid.elf and flash gnuk-vidpid.bin instead of gnuk.bin

      Here is the way to flash the latest version of gnuk with a ST-Link V2 on a ST-Link V2.

      1. compile gnuk
      git clone git://anonscm.debian.org/gnuk/gnuk/gnuk.git
      cd gnuk
      git submodule update –init
      cd src
      ./configure –vidpid=234b:0000 –target=ST_DONGL
      make build/gnuk-vidpid.elf

      2. unlock ST-Link V2 that we want to flash gnuk on
      My openocd config:
      telnet_port 4444

      source [find interface/stlink-v2.cfg]

      set WORKAREASIZE 0x1000
      transport select “hla_swd”

      source [find target/stm32f1x.cfg]

      reset_config none

      Now run openocd and connect to it via telnet as above. Follow it but leave out the flash step, because I used stlink(https://github.com/texane/stlink) for flashing, but it might work.
      If you have issues flashing something new you can also add this commands:
      reset halt
      flash protect 0 0 127 off

      This worked for me and I found it here: http://www.openstm32.org/forumthread2221#threadId2226

      3. flash gnuk
      Connect your ST-Link V2 as shown in the post and run:
      sudo st-flash write build/gnuk-vidpid.bin 0x8000000

      Now you should have a working version that you can test if it is detected with usb-devices under Linux. The output should look something like this:
      T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 40 Spd=12 MxCh= 0
      D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
      P: Vendor=234b ProdID=0000 Rev=02.00
      S: Manufacturer=Free Software Initiative of Japan
      S: Product=Gnuk Token
      S: SerialNumber=FSIJ-1.2.8-87014124
      C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
      I: If#=0x0 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)

  5. Hi danman,
    I programmed gnuk on the key, but as soon as it got written, I could not connect to the key anymore…
    Now I’m wondering how to upgrade for example.
    When I try to connect I get this :

    Info : auto-selecting first available session transport “hla_swd”. To override use ‘transport select ‘.
    Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
    adapter speed: 1000 kHz
    adapter_nsrst_delay: 100
    none separate
    srst_only separate srst_nogate srst_open_drain connect_assert_srst
    Info : Unable to match requested speed 1000 kHz, using 950 kHz
    Info : Unable to match requested speed 1000 kHz, using 950 kHz
    Info : clock speed 950 kHz
    Info : STLINK v2 JTAG v21 API v2 SWIM v4 VID 0x0483 PID 0x3748
    Info : using stlink api v2
    Info : Target voltage: 3.260766
    Error: jtag status contains invalid mode value – communication failure
    Polling target stm32f1x.cpu failed, trying to reexamine
    Examination failed, GDB will be halted. Polling again in 100ms
    Info : Previous state query failed, trying to reconnect
    Error: jtag status contains invalid mode value – communication failure
    Polling target stm32f1x.cpu failed, trying to reexamine
    Examination failed, GDB will be halted. Polling again in 300ms
    Info : Previous state query failed, trying to reconnect
    Error: jtag status contains invalid mode value – communication failure

    Do you know what to do ? What’s needed to erase/write a programmed flash ?
    On some web sites, they talk about being able to use the SWD RST which is not available on those cheap clones. I tried looping “openocd” and unplugging/replugging the programming key without success.
    You guys seem to erase sometimes ; I’m not sure that’s done in the same situation…
    Tips and tracks to dig would be very welcome.
    Also, thanks a lot for sharing about those cheap keys, that makes very affordable tokens!


    1. For me, the connection worked everytime. I have no idea what could help you. Perhaps there’s a way how to trigger SWD RST manually?

    2. I had a similar issue with flashing back from the Arduino firmware to gnuk.
      I solved this by:
      1. only connecting SWCLK and SWDIO
      2. Start openocd with your command and plug the ST-Link V2 in a usb port at the same time
      3. connect immediately to telnet and send “reset halt” and then flashing worked normally.

      Maybe this helps.

  6. The ST-Link V2 I got doesn’t seem to have enough flash to handle the gnuk image:

    > flash banks
    #0 : stm32f1x.flash (stm32f1x) at 0x08000000, size 0x00010000, buswidth 0, chipwidth 0

    0x10000 is 65536 in decimal.

    $ ls -l build/gnuk-vidpid.bin
    -rwxrwxr-x 1 martin martin 111616 Feb 25 21:23 build/gnuk-vidpid.bin

    111616 > 65536

    I’m going to look into building a smaller gnuk image, but there may be different ST-Link sticks to buy.

  7. First of all thank you for this wonderful project!
    I’ve build several gnuk tokens before but the STLink clones seem to be quite nice as they already come with a case. Thanks for sharing this information!

    The stlink clones arrived and I was eager, to try to install a gnuk.bin to them. However the target board cannot be detected. My main workstation is a macbook so I use a VM to work with this, however I also tested with a plain Linux box with the same results.

    While I was troubleshooting, I tried to document all the steps so that it becomes reproduceable:

    Can you see any obvious mistakes? If you have multiple STlinks yourself, I would greatly appreciate when you could create a follow up article describing how to use one stlink to flash another.

    Seems like several folks followed your guide already and I am not sure whether one needs better JTAG programmer to repurpose the stlinks 🙂

  8. Found the issue. SWCLK was not well connected. However seems like the vendors learned that the stlinks get repurpossed. Seems like they set the Device Security Bit in level 2 that cannot be undone.


    Shows that it got unlocked. But flashing fails. I’ve also tried the Windows ST Link Utility that also complained about the Device Protection and said that it will automatically remove it. But it does not work either way.


    1. STM32F1 serial only level 1 protect.
      Just use “STM32 ST-LINK Utility” disable protect.

  9. Got it working 🙂
    * Updated firmware of stlink to latest upstream version.
    * Removed Read Out Protection (it was version 1 only; check it with “C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ST-LINK_CLI.exe” -c SWD -rOB)
    * Built and flashed https://github.com/devanlai/dapboot using ST-LINK Windows Tool
    * Erased chip
    * Flashed gnuk.bin
    * …
    * Profit
    The device is being reported as a 64kb flash mcu. Only after flashing another bin file, I could flash full 128kB bin without modification of the code (however i looked into it as another user reported problems; seems like current releases cannot easily patched to remove ECC code to fit 64kb)

    Learned a lot in the process and again thanks for pointing to this neat device. However Marble Minis (baite) are easier to flash – especially as they do not come with Read Out Protection (and I think they set a flag to have a proprietary protection, as the Linux/GNU tools could not easily revert them).

    1. Thanks for sharing your experience. Maple minis are good choice but they do not come with nice aluminium case and are not so compact. But yes, they are much easier to flash.

  10. Hi,

    Thanks for this nice post.

    I’m struck at programming the board. I’ve bought few of these boards. So my plan is to use one to programs others. But I don’t know what to connect with what. Shall I connect SWDIO, GND, SWCLK, 3V3 of the programmer to SWDIO, GND, SWCLK, 3V3 the programmed?


    Colin Brosseau

    1. Hi Colin,

      that’s exactly what you need to do, see the first picture in section Programming. You just replace J-Link with ST-link and that’s it.

      1. On the picture, I don’t understand why the ST-Link is plugged in the USB port of a laptop. I’d expect it to be powered by the wires.

        1. You are right, it’s a bit confusing, it should be disconnected but as soon as the STM32 CPU is halted by debugger it doesn’t matter.

  11. Where do I get the correct st-link?

    There seem to be versions with 64K and with 128K but the sellers don’t specify what version of the chip is used on their devices/clones.

    Anybody got a link where I can get the 128K version that is verified to be working with this?

    1. Hey,

      I’ve been tracing the ack button support in the latest commits and can’t for the life of me figure out how to use it. I can see that the button pin is defined under chopstx to be either PA2 or PA3 depending on the board id but it seems like the functionality has been implemented as part of the host token interaction as part of usb-ccid. If that’s the case then it is necessary for the host to request user ack rather than the token being set to use it right?

      If anyone has any light on the situation then feel free to reply!

  12. I’ve spent two days trying to get this to work, compiling on Ubuntu 18.04.01 but the device would just not be detected after writing the bin file to it:

    [2852075.564929] usb 1-1.3: new full-speed USB device number 23 using ehci-pci
    [2852075.644938] usb 1-1.3: device descriptor read/64, error -32
    [2852075.832937] usb 1-1.3: device descriptor read/64, error -32
    [2852075.941109] usb 1-1-port3: attempt power cycle

    I finally found the issue which was due to my compiler – fixed using:

    sudo apt-get remove binutils-arm-none-eabi (below install will fail if we leave binutils)
    sudo apt-add-repository ppa:team-gcc-arm-embedded/ppa
    sudo apt update
    sudo apt install gcc-arm-embedded

    Now when I compile, my MD5 is different on the bin and all works 🙂

  13. Hello,

    Thanks a lot for this great article! The confirmation button was a very valuable addition 🙂

    The ST dongle I received had a slightly different design, it’s using an STM32F103CBU6, and the RST and SWIM pins are connected to different GPIO pins. I’ve adapted your patch to this model for the latest Gnuk release and documented everything in case someone else has the same model.


  14. Will GnuK work and generate keys if flash is fully protected to disallow read-out?
    Without Level 2 protection it could not be considered HSM 🙁

  15. Hello!
    Does it possible to sign something by this token? I pushed subkey [S] to token. Now, I want to sign a small file by this subkey.
    gpg2 –sign file.txt
    It promt me for PIN code. I enter the PIN code, and nothing happens. Terminal waits for something, and ST-Link just blinking red and blue lights. If I interrupt this process by Ctrl+C, I found empty file file.txt.gpg.

  16. Hi,

    seems the git URL isn’t valid any more….:

    git clone git://anonscm.debian.org/gnuk/gnuk/gnuk.git
    Klone nach ‘gnuk’ …
    fatal: could not conect to anonscm.debian.org :
    anonscm.debian.org[0: 2001:648:2ffc:deb::211:202]: errno=connection refused
    anonscm.debian.org[1:]: errno=connection refused

    But…git clone https://salsa.debian.org/gnuk-team/gnuk/gnuk.git
    is working (at least for cloning) Is this the same project or a different one?

    thanks a lot!

    1. Check the git history, if you see this, it’s the same:

      commit fa69a85826ca030128fa0108221c2a6f773891f2 (tag: release/1.2.4, origin/master, origin/HEAD)
      Author: NIIBE Yutaka
      Date: Fri May 12 17:22:20 2017 +0900

      Version 1.2.4.

      Signed-off-by: NIIBE Yutaka

  17. This looks amazing! One question, what’s is the biggest key size that is usable? Can I use ECC with it? Or can any of the hardware be upgraded?

    I’m sorry for all this questions but I know basically nothing about electronics, I want to learn though.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.