Andrea Barisani
andrea.barisani@withsecure.com | andrea@inversepath.com
The GoKey application implements a USB smartcard in pure Go with support for:
In combination with the TamaGo framework GoKey is meant to be executed on ARM bare metal on hardware such as the USB armory Mk II.
⚠️ the SSH management console only works on Linux or macOS hosts.
When running on secure booted NXP i.MX6ULL devices, the built-in Data Co-Processor (DCP) is used to provide device specific hardware encryption.
A device specific random 256-bit OTPMK key is fused in each NXP i.MX6ULL SoC at manufacturing time, this key is unreadable and can only be used by the DCP for AES encryption/decryption of user data, through the Secure Non-Volatile Storage (SNVS) companion block.
The OTPMK is used to derive device specific keys, which can be used for the following operations:
-
Bundling of OpenPGP/SSH/U2F private keys, within the GoKey firmware, in encrypted form. This ensures that bundled keys are authenticated, confidential and only decrypted on a specific unit.
The SSH private key can be uniquely and deterministically generated for each hardware unit, rather than being bundled in the firmware, if not passed at compilation time.
-
Creation of the AES256 Data Object used by PSO:DEC (in AES mode) and PSO:ENC, this entails that AES encryption/decryption operations can only be executed on a specific unit.
On units which are not secure booted (not recommended):
-
The OpenPGP private key is bundled without hardware encryption, its sole protection can therefore be encryption with the user passphrase (if present in the key).
-
The SSH and U2F private keys are bundled without hardware encryption, and therefore readable from the firmware image.
The SSH private key is randomly generated at each boot if not passed at compilation time.
-
The U2F master key is derived from unique hardware serial numbers and the SoC unique ID, both are readable from a stolen device without secure boot in place.
-
PSO:DEC (in AES mode) and PSO:ENC are not available.
For certain users and uses, a non secure booted device might lead to an acceptable level of risk in case of a stolen device, nonetheless it is highly recommended to always use a secure booted device for all configurations and to leverage on SNVS features (see Compiling).
On a secure booted unit the GoKey firmware, bundled with private keys encrypted with the device unique key, can be built by compiling on the device itself with the mxs-dcp kernel module loaded.
The module is included in all USB armory Mk II standard Debian base image releases.
These are security features, not bugs:
-
PW3 is not implemented and card personalization is managed outside OpenPGP specifications to reduce the attack surface (see Management).
-
The VERIFY command user PIN (PW1) is the passphrase of the relevant imported key for the requested operation (the PSO:ENC operation does not use any OpenPGP key, however the decryption subkey passphrase is still used for cardholder authentication).
-
The optional key derived format (KDF) is not supported to avoid the transmission and internal storage of passwords in plain format, rather the user can issue the key passphrase over SSH for improved security (see Management).
-
To prevent plaintext transmission of the PIN/passphrase, the VERIFY command will take any PIN (>=6 characters) if the relevant OpenPGP key has been already unlocked over SSH (see Management).
-
On bare metal secure booted i.MX6ULL, the AES256 Data Object for PSO:DEC (in AES mode) and PSO:ENC is internally created with a device specific random 256-bit key (OTPMK). This means that PSO:DEC and PSO:ENC can only decrypt/encrypt, using AES, data on the same device.
These are current limitations:
-
Only signature (Sig) and decryption (Dec) keys are supported, authentication keys (Aut) are pending this pull request.
-
PW1 and DSO counters are volatile (e.g. not permanent across reboots), other such as RC and PW3 are unused due to lack of functionality.
-
Data Object 0x7f21 (Cardholder certificate) is not implemented.
A conventional smartcard authenticates the user typically with a numeric PIN, this cardholder authentication unlocks the code paths that allow use of key material and/or management functions. The secret key material is stored in an internal flash unencrypted, relaying on the physical barrier and simple read protection mechanisms for its security.
In other words a conventional smartcard does not employ encryption of data at rest and its code has internal access to key material unconditionally.
Futher, traditional smartcards require physical security measures to prevent tampering attacks such as glitching or memory extraction, as there is opportunity for an attacker in possession of a stolen card to try and extract key material.
The GoKey firmware, when running on the USB armory Mk II, employs a different security model as all data at rest is either authenticated, by secure boot, or encrypted. The private OpenPGP keys are actually encrypted twice, the first layer for the benefit of the hardware so that only authenticated code can unwrap the key and the second layer for the benefit of user authentication.
Therefore the GoKey firmware does not need to be stored on an internal flash with read protection but is meant to be accessible by anyone, as it is authenticated by the hardware and only holds encrypted content which can be unlocked by a specific device and a specific user.
Additionally, to help mitigating attacks against the first layer of hardware key wrapping, hardware decryption can be configured to take place only when a user is successfully authenticated through the management interface.
The security model of GoKey, opposed to conventional smartcards, entails that a stolen device gives no opportunity for an attacker to extract private key material unless the user private SSH key (or secure boot process) as well as OpenPGP key passphrases are compromised, when all security features are used.
Last but not least, thanks to the TamaGo framework, GoKey on the USB armory Mk II employs a runtime environment written in pure high-level, memory safe, Go code and without the dependency of an OS, or any other C based dependency. This dramatically reduces the attack surface while increasing implementation trustworthiness.
The following table summarizes the fundamental technological differences with a traditional smartcard:
Hardware type | Trust anchor | Data protection | Runtime environment | Application environment | Requires tamper proofing | Encryption at rest |
---|---|---|---|---|---|---|
Traditional smartcards | Flash protection | Flash protection | JCOP | JCOP applets | Yes | No |
GoKey on USB armory Mk II | Secure boot | SoC security element | Bare metal Go | Bare metal Go | No | Yes |
The following table summarizes the multiple authentication options available, depending on OpenPGP and GoKey configuration, which enhance the traditional smartcard authentication model:
OpenPGP passphrase | GoKey authentication (see SNVS=ssh ) |
Comment |
---|---|---|
None | None | No security, device can be used without any authentication |
Yes over VERIFY | None | Low security, passphrase transmitted in plaintext over USB |
Yes over SSH | None | Better security, passphrase transmitted securely |
Yes over VERIFY | Yes | Good security, plaintext passphrase but standard SSH authentication required to enable key use |
None | Yes | Good security and convenience, standard SSH authentication required for hardware key decryption |
Yes over SSH | Yes | High security, standard SSH authentication enables key use, passphrase transmitted securely |
The next sections detail the compilation, configuration, execution and operation for bare metal, virtualized and simulated environments.
A simplified tutorial for secure booted USB armory Mk II boards is available in the project wiki.
Unless otherwise stated, all commands shown are relative to this repository directory:
git clone https://github.com/usbarmory/GoKey && cd GoKey
As a pre-requisite for all compilation targets, the following environment variables must be set or passed to the make command:
-
SNVS
: when set to a non empty value, use hardware encryption for OpenPGP, SSH and U2F private keys wrapping, see the Security Model section for more information.If set to "ssh", OpenPGP and U2F key decryption, rather than executed at boot, must be initialized by the user over SSH (see Management). This improve resilience against physical hardware attacks as the SNVS decryption process cannot happen automatically on a stolen devices.
This option can only be used when compiling on a secure booted USB armory Mk II.
⚠️ the SSH management console only works on Linux or macOS hosts, it must be disabled for Windows hosts.
-
SSH_PUBLIC_KEY
: public key for SSH client authentication by the network management interface (see Management). If empty the SSH interface is disabled. -
SSH_PRIVATE_KEY
: private key for SSH client authentication of the management interface SSH server (see Management). The key must not have a passphrase. When SNVS is set the key is encrypted, before being bundled, for a specific hardware unit.On secure booted units, if left empty, the SSH server key is uniquely and deterministically generated for each hardware unit.
On units which are not secure booted, if left empty, the SSH server key is randomly generated at each boot.
-
PGP_SECRET_KEY
: OpenPGP secret keys in ASCII armor format, bundled in the output firmware. If empty OpenPGP smartcard support is disabled.When SNVS is set the key is encrypted, before being bundled, for a specific hardware unit.
-
URL
: optional public key URL. -
NAME
,LANGUAGE
,SEX
: optional cardholder related data elements.
OpenPGP smartcard secret keys are typically made of 3 subkeys: signature, decryption, authentication.
The GoKey card cannot import keys while running as any runtime change is not allowed (see Deviations from OpenPGP standard support), only key bundling at compile time is currently supported.
There are several resources on-line on OpenPGP key creation and should all be
applicable to GoKey as long as the smartcard specific keytocard
command is
not used, but rather keys are exported armored and passed via PGP_SECRET_KEY
at compile time.
Some good references to start:
Finally always ensure that existing keys are imported with minimal content, an example preparation is the following:
gpg --armor --export-options export-minimal,export-clean --export-secret-key ID
⚠️ Please note that only RSA, ECDSA, ECDH keys are supported. Any other key (such as ElGamal, Ed25519) will not work.
To enable U2F support using the fidati library, the following variables can be set:
-
U2F_PUBLIC_KEY
: U2F device attestation certificate, if empty the U2F interface is disabled. -
U2F_PRIVATE_KEY
: U2F device attestation private key, if empty the U2F interface is disabled.When SNVS is set the key is encrypted, before being bundled, for a specific hardware unit.
The attestation key material can be created using the gen-cert tool from the fidati library.
On USB armory Mk II rev. β models the ATECC608B security element is used as hardware backed monotonic counter for U2F purposes. The counter runs out at 2097151, which is considered a range sufficient for its intended purpose.
On USB armory Mk II rev. γ models a 32-bit monotonic counter is saved on the internal eMMC in an unused reserved sector.
The U2F library performs peer-specific key derivation using a master secret (U2F Key Wrapping), GoKey derives such master secret using the SNVS to obtain an authenticated device specific value.
When the management interface is disabled, FIDO U2F user presence is automatically acknowledged, otherwise it can be configured at initialization through the management interface (see Management).
Build the TamaGo compiler (or use the latest binary release):
wget https://github.com/usbarmory/tamago-go/archive/refs/tags/latest.zip
unzip latest.zip
cd tamago-go-latest/src && ./all.bash
cd ../bin && export TAMAGO=`pwd`/go
Please note that if performed on the USB armory Mk II, due to this issue, this requires adding some temporary swap space to be disabled and removed after this step is completed (to prevent eMMC wear), alternatively you can cross compile from another host or use the latest binary release).
Build the gokey.imx
application executable with the desired variables:
make imx CROSS_COMPILE=arm-none-eabi- NAME="Alice" PGP_SECRET_KEY=<secret key path> SSH_PUBLIC_KEY=<public key path> SSH_PRIVATE_KEY=<private key path>
For signed images to be executed on secure booted
USB armory Mk II devices, which enable use of Secure Non-Volatile Storage
(SNVS), the imx_signed
target should be used with the relevant HAB_KEYS
set:
make imx_signed CROSS_COMPILE=arm-none-eabi- NAME="Alice" PGP_SECRET_KEY=<secret key path> SSH_PUBLIC_KEY=<public key path> SSH_PRIVATE_KEY=<private key path> HAB_KEYS=<secure boot keys path> SNVS=ssh
The GoKey USB smartcard works out of the box on modern macOS installations with GPG Suite.
The GoKey USB smartcard has been tested with libccid, used by OpenSC on most Linux distributions.
The libccid library now supports GoKey vendor and product IDs, if your installed version is older than this change apply the following instructions.
To enable detection an entry must be added in libccid_Info.plist
(typically
located in /etc
):
- Locate the
ifdVendorID
array and add the following at its bottom:
<string>0x1209</string>
- Locate the
ifdProductID
array and add the following at its bottom:
<string>0x2702</string>
- Locate the
ifdFriendlyName
array and add the following at its bottom:
<string>USB armory Mk II</string>
The GoKey USB smartcard, once the CCID driver entries are added as in the previous section, can then be used as any other smartcard via OpenSC on Linux.
You can refer to Arch Linux smartcards documentation for configuration documentation.
Windows does not support Ethernet over USB devices implemented with CDC-ECM,
therefore the SSH management console must be disabled (e.g. SNVS
can be non
empty but not to ssh
, see Compiling)
Smartcard operation has not been tested but should be possible with software that uses up-to-date smartcard drivers.
Follow these instructions
using the built gokey.imx
or gokey_signed.imx
image.
Copy the built gokey
binary on an external microSD card (replace $dev
with
0
) or the internal eMMC (replace $dev
with 1
), then launch it from the
U-Boot console as follows:
ext2load mmc $dev:1 0x90000000 gokey
bootelf -p 0x90000000
For non-interactive execution modify U-Boot configuration accordingly.
GoKey can be conveniently accessed from either the USB armory Type-C plug (e.g. USB armory directly connected to a Type-C host port) or receptacle (e.g. USB armory connected with a Type-C to Type-A cable).
When running on bare metal the GoKey firmware exposes, on top of the USB CCID smartcard and/or U2F token interfaces, an SSH server started on Ethernet over USB.
The SSH server authenticates the user using the public key passed at
compilation time with the SSH_PUBLIC_KEY
environment variable. Any username
can be passed when connecting. If empty the SSH interface is disabled.
A private key for the SSH server can be optionally passed at compilation time
with the SSH_PRIVATE_KEY
environment variable, on secure booted units it can
also be deterministically generated at each boot (see Compiling).
The server responds on address 10.0.0.10, with standard port 22, and can be used to securely message passphrase verification, in alternative to smartcard clients which issue unencrypted VERIFY commands with PIN/passphrases, signal U2F user presence and perform additional management functions.
help # this help
exit, quit # close session
rand # gather 32 bytes from TRNG via crypto/rand
reboot # restart
status # display smartcard/token status
init # initialize OpenPGP smartcard
lock (all|sig|dec) # OpenPGP key(s) lock
unlock (all|sig|dec) # OpenPGP key(s) unlock, prompts passphrase
u2f # initialize U2F token w/ user presence test
u2f !test # initialize U2F token w/o user presence test
p # confirm user presence
Note that to prevent plaintext transmission of the PIN/passphrase, the VERIFY command requested by any OpenPGP host client will take any PIN (>= 6 characters) if the relevant OpenPGP key has been already unlocked over SSH.
You should be able to use the GoKey smartcard like any other OpenPGP card, you can test its operation with the following commands:
-
OpenSC detection:
pcsc_scan
-
OpenSC explorer:
opensc-explorer
-
OpenPGP tool key information:
openpgp-tool -K
-
GnuPG card status:
gpg --card-status
When checking card status note that a >
after key information tags indicate
that the key is stored on a smartcard.
The U2F functionality can be used with any website or application that supports FIDO U2F.
When the SSH interface is enabled (see Management) the U2F functionality must
be initialized with the u2f
command, user presence can be demonstrated with
the p
command (not required if u2f !test
is used for initialization).
When the SSH interface is disabled user presence is automatically acknowledged at each request.
On the USB armory Mk II the LEDs are used as follows:
LED | On | Off |
---|---|---|
blue + white | at startup: card is initializing¹ | card has been initialized |
blue | one or more OpenPGP private subkeys are unlocked | all OpenPGP private subkeys are locked |
white | OpenPGP security operation in progress | no security operation in progress |
white | blinking: U2F user presence is requested | no presence requested |
¹ With SNVS=ssh
both LEDs remain on until the init
command has been issued over SSH management interface.
The virtual smart card project allows testing of GoKey OpenPGP functionality in userspace.
Build the gokey_vpcd
application executable:
make gokey_vpcd PGP_SECRET_KEY=<secret key path>
On the host install vsmartcard, Arch Linux users can use the virtualsmartcard AUR package.
Ensure that a configuration for vpcd
is added in your pcsc
configuration.
On Arch Linux the following is automatically created in /etc/reader.conf.d
:
FRIENDLYNAME "Virtual PCD"
DEVICENAME /dev/null:0x8C7B
LIBPATH //usr/lib/pcsc/drivers/serial/libifdvpcd.so
CHANNELID 0x8C7B
Launch the PC/SC daemon:
sudo systemctl start pcscd
Launch the built gokey_vpcd
executable:
./gokey_vpcd -c 127.0.0.1:35963
- Example for issuing a GET CHALLENGE request to get 256 random bytes (due to SCD protocol formatting some post processing required to extract the actual random output):
gpg-connect-agent "SCD RANDOM 256" /bye | perl -pe 'chomp;s/^D\s//;s/%(0[AD]|25)/chr(hex($1))/eg;if(eof&&/^OK$/){exit}'
GoKey | https://github.com/usbarmory/GoKey
Copyright (c) WithSecure Corporation
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation under version 3 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
See accompanying LICENSE file for full details.