Publishing an F-Droid Repo using a Hardware Security Module

The following is a tutorial for publishing apps through F-Droid using a hardware security module (HSM). The HSM used in this post is a Nitrokey HSM2. Other hardware tokens should have similar functionality, but you’ll have to use different commands for interacting with the HSM.

Storing signing keys in an HSM provides major security advantages once a key is generated inside a HSM it can never be extracted in plain text form. Instead the data to be signed is sent to the HSM which will produce the signature with the internally held key material. They also apply some tamper resistance technologies which makes physical extraction of the key very difficult.

A signing key for an Android application is part of the identity of said app. As a security measure an update for an app can only be installed when the new apk is signed with the same private key as the old one. Storing the keys in an HSM makes sure that even if an attacker compromises your infrastructure they will not be able to steal the signing keys. In fact there’s never any ambiguity if an attacker might have gotten access to the keys. The physical HSM would need to be stolen and that is at least obvious at that point. The intruder could still possibly use the HSM to sign malicious apps though.

F-Droid has two signing steps. All apks as well as the repository index are signed. All of those keys should ideally be stored inside a HSM.

Disclaimer: I asked Nitrokey if they could sponsor some Nitrokey HSMs to get F-Droid’s publishing workflow to work with it. They agreed to send me three of them and asked me to write a blog post about my results.

Step 0: Preparing the Nitrokey HSM2

It’s highly recommended to initialize the Nitrokey with a Device Key Encryption Key (DKEK) before use. This allows key backup and restore onto another Nitrokey initialized with the same DKEK in case of a Hardware failure. It’s also required for importing existing signing keys into the HSM (see below).

A step-by-step guide on how to set this up and the possible options for managing DKEK shares and passphrases can be found here

Needless to say that you should keep the DKEK stored at a very secure place.

(optional) Step 1: Import Existing Signing Keys into the HSM

The Nitrokey HSM2 can only import keys that are “wrapped” (encrypted) with the correct DKEK, so to import a plain key from a java keystore this key has to be manually wrapped with the DKEK first. The manufacturer of the SmartCard inside the Nitrokey HSMs (CardContact) has a tool called smartcard-shell which can do this. The tools isn’t that great to use (it’s a java GUI tool scripted with javascript). I ported this process to a small command-line python tool, which can be found here.

Step 2: Setting up the F-Droid Repo

You should use the latest 2.0a version of F-Droid or install it from git master. A few HSM usage issues have recently been ironed out.

Initialize a new repo as follows, this will setup the necessary config files for using fdroid with a HSM

$ fdroid init --keystore NONE  # NONE means using a HSM keystore

You’ll need to then at least change keystorepass in config.py which needs to be set to the Nitrokeys smartcard pin. If you have imported an existing repository signing key, this needs to be set as repo_keyalias. Otherwise you can create a new key directly on the HSM (change keydname to what the certificate details should look like for your repo.)

$ fdroid update --create-key  # This will take a while...

Step 3: Sign an app and publish an index

From now on everything should work as usual as long as the Nitrokey is plugged into your machine. F-Droid will automatically use it when singing apps with fdroid publish and when generating a signed repository index with fdroid update.

What about signing a lot of apps?

The Nitrokey HSM2 has only limited space for signing keys, it can store up to 38 2048 bit RSA keys. There’s a work-in-progress MR making use of the wrapped key functionality to only load app signing keys onto the HSM on demand. This allows using an unlimited amount of different keys with a single Nitrokey. (Many thanks to Jason Donenfeld (zx2c4) for coming up with this idea!)