This blog entry is part of a series covering:
Of course, this is not 100% secure and this guide does not cover all corners, nor is 3 letter agencies-proof. Think of it as a hobby from which you can learn: in the end is all about compromise, and I choose to draw the line in an arbitrary place, much like anyone else. Feel free to contact me if you see a hole or want to make suggestions.
In this part we will:
Before starting, lets learn some concepts. There are 3 different types of information that can be used for authentication:
The first class, things you only are, is a bit blurry, as it can be morfed into the second, things you only have: a fingerprint for example can be easily obtained from left-overs (a plastic bottle or a cup), can be copied easily with household items and cannot be changed at will if compromised. Or someone can remove your finger from your hand, and most scanners won't notice. That's why biometrics should be used only as usernames. In contrast, a strong private password can only be sniffed out when you use it, and you are not leaving it anywhere you go.
The most comprehensive solution would be to use the 3 types of information, called as you know a 2-step authentication scheme: you use the thing you only are and you only know (login and password) and then a token you only have in that moment (a one-use code, or a code generated by a device that needs your credit card or your phone). An attacker would need to guess the password an steal your device to succeed.
Our token that we only have is going to be a yubikey NEO, which is a little durable USB key with one capacitive button. You can put it in your keyring, or better, on a bracelet that you keep always with you. Is quite possible that this kind of device will be the norm in 10 years: NFC-capable rings that store your passwords and log you in every thing imaginable, automatically.
The Yubikey Neo provides us with:
For setting up our security scheme and our Yubikey, we need some foundations: an air-gapped computer.
An air-gapped computer is one that is neither connected to the internet nor connected to any other system that is connected to the internet. For this purpose, a computer on a board is quite useful, such as a Raspberry Pi, a Beaglebone Black, etc. All of these are FOSS hardware, cheap (~40), portable (credit card sized), have no on-chip wireless network connections so when it is not networked you are sure of it (unlike, lets say, a smartphone and their broadband chips), their microSD cards can be easily changed to carry different air-gapped computers and can be stored easily or even made tamper-proof with epoxy.
I'm going to use an Olimex A20 Olinuxino Lime , as is what I currently have.
This computer will be used to generate our master GPG keys and subkeys, signing with our master keys, building and signing a binary, etc. We will move files in and out of our offline computer by an usb stick, which shouldn't be too much of a handicap as these operations are rarely done.
For that, we are going to install Debian Jessie with no blobs (privative software) and simple frame buffer support on the A20 Olinuxino Lime. That will let us have a console on the HDMI output. For that, we need U-Boot bootloader version 2015.01 or more, and Linux kernel 3.19 or more. We have two options, either you use the weekly/daily Debian ARM ISO images, or we make the installation ourselves.
If you choose to make the installation, follow the steps for making the SD card image as instructed in the Linux-sunxi wiki (shameless plug: I contributed to those pages of the Linux-sunxi wiki). For building U-Boot, use the
A20-OLinuXino-Lime_config, and use
Olimex A20-OLinuXino-LIME as the device name for flash-kernel.
After you have your system running, update and install the following packages (if in the future you want to install new packages, remember that you will need to download the .debs and install them by USB).
# Update the system: $ sudo apt-get update && sudo apt-get dist-upgrade # For console and keyboard configuration: $ sudo apt-get install console-data keyboard-configuration # For checking GPG keys: $ sudo apt-get install hopenpgp-tools # as of now, not yet on armhf # For backing up the keys: $ sudo apt-get install paperkey qrencode # For using and configuring the Yubikey: $ sudo apt-get install python-yubico python-yubico-tools yubikey-personalization # For smartcards: $ sudo apt-get install pcscd scdaemon pcsc-tools gpgsm # Install gnupg2: $ sudo apt-get install gnupg2 # For encrypted filesystems: $ sudo apt-get install cryptsetup libpam-mount # And install the packages you want: $ sudo apt-get install git zsh tmux vim w3m
In the future, this will install gnupg2 v2.1 and up, which will simplify things. At this point, with Debian Jessie, this gives us:
$ aptitude versions gnupg2 scdaemon gpgsm pcscd Package gnupg2: 2.0.26-6 Package gpgsm: 2.0.26-6 Package pcscd: 1.8.13-1 Package scdaemon: 2.0.26-6
As stated in this openPGP best practices guide, Put Jacob Appelbaum's hardened gpg.conf as yours and read and understand it:
$ wget https://github.com/ioerror/duraconf/raw/master/configs/gnupg/gpg.conf ~/.gnupg/gpg.conf # Enable ssh support: $ echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
Now we can disconnect from LAN and be off-line. Of course from now on, don't interface to your Olinuxino by UART, ssh or else: connect it to an HDMI screen and its own cabled keyboard.
If in the future you need to use again the computer, you will surely need to set the watch again with:
$ sudo date -s "15 MAY 2015 11:14:00"
A better option would be to connect a Realtime Clock Module with a battery to it, such as this one, or this other one, as I2C has been in the Kernel since 3.13 and it should just work() . There are many other modules, and are quite cheap. In our case, this SoC board has a RTC included: you only need to connect a battery to the Olinuxino and it will mantain the time (the controller is not already in the Mainline Kernel, so you will need to wait a bit for it).
In case you need to configure the console or connect a keyboard with different locale than what it is currently configured you can run:
$ sudo dpkg-reconfigure console-data $ sudo dpkg-reconfigure console-setup $ sudo dpkg-reconfigure keyboard-configuration && service keyboard-setup restart
Now you can lay back and contemplate your air-gapped secure computer up and running!
We are going to generate a master GPG key pair, and a subkey pair. If you want to get a peek on how public key cryptography works, this video is quite good. For more information about Subkeys I recommend reading the Debian page about Subkeys and this StackOverflow post. And now that you are at it, take a peak at this OpenPGP best practices recopilation. The master key pair is going to be used for signing other people keys and issuing new subkeys. The subkey is linked to the master keys, is going to be stored inside our Yubikey Neo, and will be the one that we will use for encrypting and decrypting. You can make more subkey pairs if you want and because they are linked to the master pair your friends don't need to re-sign them if you lose them or become compromised. When generating the subkey pair remember that the Yubikey Neo can only store and work with keys up to 2048 bits.
As the Yubikey doesn't allow us to make a 4096 master keypair, and 3 subkeys to that keypair that we can backup, we are going to generate the keys inside the air-gapped computer.
The expire date will be 1 year, with manual extending every year (see here the reasons why).
Generate the master keys:
# Generate a new master key: $ gpg2 --full-gen-key Your selection? 4 (RSA sign only) What keysize do you want? (2048) 4096 Key is valid for? (0) 1y # Add more user ids as desired: $ gpg2 --edit-key <key id here> gpg> adduid # Select your primary uid: gpg> uid 1 gpg> primary gpg> save
Make a revocation certificate:
$ gpg2 --output \<email@example.com\>.gpg-revocation-certificate --gen-revoke your@email
Make a backup of the master keys:
$ gpg2 --export-secret-keys --armor firstname.lastname@example.org > \<email@example.com\>.private.gpg-key $ gpg2 --export --armor firstname.lastname@example.org > \<email@example.com\>.public.gpg-key $ mkdir backup-master-keys $ mv \<* backup-master-keys/
Generate the subkeys:
$ gpg2 --expert --edit-key <key id here> gpg> addkey Your selection? 4 (RSA sign only) What keysize do you want? (2048) 2048 Key is valid for? (0) 1y gpg> addkey Your selection? 6 (RSA encript only) What keysize do you want? (2048) 2048 Key is valid for? (0) 1y gpg> addkey Your selection? 8 (set your own capabilities) Toggle: a,s,e so it is for Authentication What keysize do you want? (2048) 2048 Key is valid for? (0) 1y gpg> save
Make backups of the subkeys:
$ gpg2 --export-secret-keys --armor firstname.lastname@example.org > \<email@example.com\>.master-subkeys.gpg-key $ gpg2 --export-secret-subkeys --armor firstname.lastname@example.org > \<email@example.com\>.subkeys.gpg-key $ mkdir backup-subkeys $ mv \<* backup-subkeys/
Put it in the keyservers and in your web page:
# On your air-gapped computer: $ mkdir ~/usb $ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw $ cp publickey.txt ~/usb/ $ sudo umount /dev/sda1 # On your networked computer: $ gpg2 --import < /media/8GB/publickey.txt $ gpg2 --send-keys <key id here>
Connect the Yubikey to your air-gapped computer. Make sure the eject flag in the Yubikey is set to 82 for OTP and CCID compatibility:
$ ykpersonalize -m82 # take it out and plug it back to restart it
and that you have a recent firmware version, 3.1.8 or later; use
lsusb -v to find out. Also, make sure that you are using a recent Yubikey Neo that is not vulnerable to CVE-2015-3298. If it is, know that you are entitled to a free replacement + free shipping (even if you can just patch it).
After that, configure your smartcard:
$ gpg2 --card-edit gpg/card> admin # Change PIN and Admin PIN: (default are 123456 and 12345678 respectively) gpg/card> passwd 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit ... Your selection? 1 PIN changed. ... Your selection? 3 PIN changed. ... Your selection? q # Change the card's info: gpg/card> name ... gpg/card> lang ... gpg/card> sex ... # Set the url to where your public key is (keyserver, your page, etc): gpg/card> url ... gpg/card> login ... gpg/card> quit
And move the subkeys to the smartcard (this will really move the subkeys, not copy them, so make sure you have the backups if you want to use this same subkeys in another Yubikey for example):
$ gpg2 --edit-key <key id here> ... gpg> toggle ... gpg> key 1 # select 1 ... gpg> keytocard ... Please select where to store the key: (1) Signature key (3) Authentication key Your selection? 1 ... gpg> key 1 # deselect 1 ... gpg> key 2 # select 2 ... gpg> keytocard ... Please select where to store the key: (2) Encryption key Your selection? 2 ... gpg> key 2 # deselect 2 ... gpg> key 3 # select 3 ... gpg> keytocard ... Please select where to store the key: (3) Authentication key Your selection? 3 ... gpg> save
Make backups of the stubs:
mkdir backup-masterstubs $ gpg2 --export-secret-keys --armor firstname.lastname@example.org > \<email@example.com\>.master-stubs.gpg-key $ gpg2 --export-secret-subkeys --armor firstname.lastname@example.org > \<email@example.com\>.subkeys-stubs.gpg-key $ gpg2 --export --armor firstname.lastname@example.org > publickey.txt $ mv \<* publickey.txt backup-masterstubs/
Take the ~/backup-masterstubs/publickey.txt and put it on the URL you specified on the Yubikey Neo.
And you have your Yubikey set up! Hook it to your keychain or as I have done, put it on a wristband:
You should backup you primary keypair and revocation certificates inside of your air-gapped computer in an USB flash drive for example:
$ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw $ cp backup-* ~/usb/ $ sudo umount /dev/sda1
You can also put the keys on paper using Paperkey, which displays just the secret bits of the secret key for you to copy by hand ( 149 bytes). Remember, if you use a printer, the key can be disclosed on the printer's memory. You should also store your air-gapped computer in a safe place too, or at least its microSD.
Install the needed packages:
# For smartcards: $ sudo apt-get install pcscd scdaemon pcsc-tools gpgsm # Install gnupg2 v2, for Yubikey, and ssh auth compatibility: $ sudo apt-get install gnupg2
If you are using Gnome 3 (or something based on it), we have a problem. The
gpg-agent protocol implementation of
gnome-keyring is very incomplete and hence breaks at least the smartcard functions of gpg and most functions of gpgsm. Instead of using it, we are going to deactivate
gnome-keyring-daemon ang use
gpg-agent instead. The best solution I have found is the one in the gnupg mail list:
mkdir ~/.config/autostart/ cp /etc/xdg/autostart/gnome-keyring-gpg.desktop ~/.config/autostart/ echo 'Hidden=true' >> ~/.config/autostart/gnome-keyring-gpg.desktop
If you want to have a look at how I have configured it, you can peek at my dotfiles, which include the gpg-agent workaround and my shareable configs.
Fetch the public key and make the stub keys:
$ gpg2 --card-edit gpg/card> fetch gpg: solicitando clave 0x223F15CA5AAF0E78 de http servidor viccuad.me gpg: clave 0xA2591E231E251F36: clave pblica "Vctor Cuadrado Juan <me@redacted>" importada gpg: Cantidad total procesada: 1 gpg: importadas: 1 (RSA: 1) gpg/card> quit
Now you should see that you have stubs in your gpg private key (sec#, with # meaning stub, nude key). In my case:
$ gpg2 -K /home/vic/.gnupg/secring.gpg ---------------------------- sec# 4096R/0xA2591E231E251F36 2015-05-13 [caduca: 2016-05-12] Huella de clave = E3C5 114C 0C5B 4C49 BA03 0991 A259 1E23 1E25 1F36 uid Vctor Cuadrado Juan <me@redacted> uid Vctor Cuadrado Juan <victorcuad@redacted> ssb> 2048R/0x223F15CA5AAF0E78 2015-05-13 ssb> 2048R/0x7413065A8F779586 2015-05-13 ssb> 2048R/0x3B1500BD8C0124E3 2015-05-13
Mark the key as ultimately trusted:
$ gpg2 --edit-key <your key id here> gpg> trust your decision? 5 Do you really want to set this key to ultimate trust? (y/N) y gpg> quit
If you are moving from an old keyset to the new ones we have just created, you should sign the old key with the new one, write up a transition statement and revoke the old key. A sane thing to do is to also backup that old keypair, so you can decrypt old files in case you need it.
To sign the old key, export it, move it to the air-gapped computer, sign it and share it:
# On your networked computer: $ gpg2 --export --armor <old key id here> > oldkey.txt # Copy oldkey.txt to a USB stick and connect the stick to your air-gapped computer # On your air-gapped computer: $ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw # Import the key and sign it: $ gpg2 --import < ~/usb/oldkey.txt $ gpg2 --sign-key <old key id here> Really sign all user IDs? (y/N) y Really sign? (y/N) y # Export the old key and copy it to the USB: $ gpg2 --export <old key id here> > ~/USB/signed-oldkey.txt $ sudo umount /dev/sda1 # On your networked computer: $ gpg2 --import < /media/8GB/signed-oldkey.txt $ gpg2 --send-keys <old key id here>
About transition statements, This is a good example of one. Clearsign it with both keys (old and new), and then publish it in a stable place. Dont forget to update the references you have to your GPG key too (email signatures, about page, presentation cards, etc).
# On your networked computer: $ gpg2 --export --armor <old key id here> > oldkey.txt $ gpg2 --export-secret-keys --armor <your old key id here> > secret-oldkey.txt # Copy oldkey.txt and your transition statement to a USB stick and # connect the stick to your air-gapped computer # On your air-gapped computer: $ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw # Import the key and sign it: $ gpg2 --import < ~/usb/secret-oldkey.txt # Clearsign the statement. Note that we used <new master key>!" to make GnuPG # use the master key for signing rather than a subkey, which it would normally do: $ cat unsigned_GPG_transition_statement_2015-5-15.txt | gpg --clearsign --personal-digest-preferences "SHA512" --local-user <old key here> --local-user <new master key>! > GPG_transition_statement_2015-5-15.txt # Export the old key and copy it to the USB: $ cp GPG_transition_statement_2015-5-15.txt ~/usb/ $ sudo umount /dev/sda1 # Put the transition statement somewhere public
$ gpg2 --edit-key <old key id here> gpg> revkey # select a reason gpg> save # share the revoked key with the key servers: $ gpg2 --send-keys <old key id here>
If your subkeys become compromised, stolen or lost, we will need to revoke them. Boot up your air-gapped computer, where the master keypair is (and if not, import them with
gpg --import) and then revoke the subkeys:
$ gpg2 --edit-key <your key id here> # toggle the subkeys: gpg> key 1 gpg> key 2 gpg> key 3 gpg> revkey # select a reason gpg> save
And now you need to export that keys, and publish them on a key server.
If you want a Markdown version of this blog post for further reading or backup you can download it from here.