I’d like to present the keycard
command line tool that we can use to interact with keycards. It’s written in Go using status-im/keycard-go, and you can download a release build for your system from the keycard-cli releases page.
TL;DR
# sign a message with the keycard shell and a usb reader
keycard shell <<END
keycard-select
keycard-set-secrets 123456 123456789012 KeycardTest
keycard-pair
keycard-open-secure-channel
keycard-verify-pin {{ session_pin }}
keycard-derive-key m/44'/60'/0'/0/0
keycard-sign-message Hello World
keycard-unpair {{ session_pairing_index }}
END
The keycard
CLI can be used to install a new applet on a keycard, initialize it, and call all the supported commands.
It can also be used to send GlobalPlatform commands or in general custom APDU commands, so we no longer need gpshell.
I’m going to write all the steps needed to:
- setup an empty card
- install a new version of the status-keycard applet
- initialize the applet
- generate a master key
- sign a message
warning
Use the following commands with a development card, where you don’t have a real key that you use for your funds. We are going to delete the keys and start from scratch, so if you don’t have a backup, it won’t be possible to recover the master key.
Requirements
- the keycard CLI
- a keycard
- usb smart card reader
- on linux you need to install and run the pcsc daemon
Setup an empty card
If you received a new blue keycard, you can skip this part, the applet is already installed. Though you can use this command to upgrade to a new applet version.
If you instead have a white card prototype, it should be empty.
Remember that re-installing the applet means losing the master key and starting from scratch.
Download the latest applet cap file.
After that, insert the keycard in the card reader and check its state:
keycard info
If you are curious about the raw APDU commands sent to the keycard, you can change the log level to debug:
keycard info -l debug
Or you can use the shell to send the select
command:
echo "keycard-select" | keycard shell
The output will tell you if the applet is installed, if it’s initialized, the applet instance UID, and other info.
Let’s install the latest applet (and permanently delete any key from the card):
keycard install -a path/to/keycard.cap
(if you are really sure you want to delete the current key, pass the -f
flag to force the overwrite of the current applet.)
If everything went fine, the keycard info
command should now tell us that the applet is installed, but not initialized yet.
Initialize a Keycard
We have 2 options:
- use the
init
command that generate random PUK, PIN, and pairing password
# init command
keycard init
or
- use the
shell
command to personalise the commands we send:
# initialize using the shell command and custom secrets
keycard shell <<END
keycard-select
keycard-set-secrets 123456 123456789012 KeycardTest
keycard-init
END
Generate the Keycard master key
Now that the applet is installed and initialized, we can start using our wallet.
The first thing to do is generating the master key:
keycard shell <<END
# select the keycard applet
keycard-select
# set the secrets we had from the initialization
keycard-set-secrets 123456 123456789012 KeycardTest
# pairing is usually done once per device
keycard-pair
keycard-open-secure-channel
# required for the key generation
keycard-verify-pin {{ session_pin }}
# generate the master key
keycard-generate-key
# we unpair the current device so that we don't use one of the 5 available slots.
keycard-unpair {{ session_pairing_index }}
END
We can also pair once and then use keycard-set-pairing PAIRING_KEY PAIRING_INDEX
passing the pairing info created with the keycard-pair
command. They must be set before calling keycard-open-secure-channel
.
Sign a message with Keycard
Finally, we can use the HD wallet we created to sign a message with a key derived from any derivation path:
keycard shell <<END
# select the keycard applet
keycard-select
# set the secrets we had from the initialization
keycard-set-secrets 123456 123456789012 KeycardTest
# pairing is usually done once per device
keycard-pair
keycard-open-secure-channel
keycard-verify-pin {{ session_pin }}
# derive the key we want to use
keycard-derive-key m/44'/60'/0'/0/0
# sign a message. a prefix is added like in web3.eth.sign
keycard-sign-message Hello World
# we unpair the current device so that we don't use one of the 5 available slots.
keycard-unpair {{ session_pairing_index }}
END
The output will show the signature and will automatically derive its public key and address.
If you are interested you can contribute to the keycard shell, the keycard Go SDK, the Java and Android SDK, and of course to the Status Keycard applet itself.
Happy hacking.