Introducing the Keycard shell


#1

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:

  1. setup an empty card
  2. install a new version of the status-keycard applet
  3. initialize the applet
  4. generate a master key
  5. sign a message

:fire: warning :fire:

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

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.
:rotating_light: 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 (:rotating_light: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:

  1. use the init command that generate random PUK, PIN, and pairing password
# init command
keycard init

or

  1. 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.


#2

This is awesome.

Totally shipping the mission with Keycard, super proud of everyone involved.

Great work team!