Invest in the Future You Want to See.

A First Public Offering of Purism Stock on StartEngine. Minimum $500.

Invest Now

Guido Günther

Guido Günther

Phone Developer
Guido Günther

Latest posts by Guido Günther (see all)

The Librem 5 features a built-in smartcard reader. While most people use it for GPG I wanted to see if it can be used as an authenticator for services like email (SMTP, IMAP), access to web pages (HTTPS), calendars, etc. without either having to retype a password often or having to store it on the device itself: Single Sign On for all my frequently used services.

Librem 5’s smart card reader

For that one can leverage a feature called PKINIT of Kerberos (a network authentication protocol meant to allow secure communication over a non-secure network which uses public key cryptography for initial user authentication (instead of a password). If the key material is saved on the smart card even a lost device can hardly be abused as the smart card is protected by a PIN and will lock itself after three incorrect tries (and you can easily revoke the certificate server side too).

On the service side one can use SASL/GSSAPI to make services like exim, postfix, dovecot, cyrus imapd, apache, nginx or prosodi use Kerberos to authenticate users and there’s many more (like CalDAV servers, etc). The most prominent missing bit for me is currently the Synapse matrix server not supporting GSSAPI yet.

I won’t go into the details of setting up the Kerberos server (KDC) or PKINIT itself or how to configure services as this is already done elsewhere (here and here) but rather focus on the Librem 5 and smart card side.

So for the following let’s assume you have a Kerberos realm called MS20.NIX set up and your KDC has the DNS name kdc.ms20.nix, and your Kerberos principal (your user in the Kerberos database) is called foo@MS20.NIX. You also have an smtp and imap servers called smtp.ms20.nix and imap.ms20.nix set up to authenticate users via Kerberos. Finally we call the CA that signs all your certificates ca.pem (we are following the naming from this).

What follows does look like lots of things need to be done on the command line. I’ll show a simplification at the very end but wanted those bits written down for reference/debugging.

Note that this post is not meant as an exhaustive introduction but more like a quick summary of commands. I’m happy if someone grabs it and adds more details.

Kerberos Setup on the Librem 5

Let’s first install the Kerberos client side packages on the phone and test that:

$ sudo apt install heimdal-clients

The krb5-config package will ask for your Kerberos realm, KDC etc. With that in place you should be able to test if you can get a Kerberos ticket via

$ kinit and entering your password.

If that worked the klist output will look something like:

$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: foo@MS20.NIX
Issued Expires Principal
Oct 24 12:55:13 2022 Oct 25 12:55:13 2022 krbtgt/MS20.NIX@MS20.NIX

Now that basic authentication worked We’re ready to switch to certificates stored them on the smart card.

Let’s drop the credentials first:

$ kdestroy

Smartcard Setup

I have used an OpenPGP card smartcard and inserted that into the Librem 5. In my case it was salvaged from a Librem Key. As PKINIT uses OpenSC the
OpenSC wiki was very helpful as it has details on how to get key material onto that particular card.

The software to use the Librem 5’s smart card reader is enabled by default as part of the librem5-base package. You just need to add OpenSC to the mix:

$ sudo apt install opensc

With that you should already be able to see the card:

$ opensc-tool -l
# Detected readers (pcsc)
Nr. Card Features Name
0 Yes L5 built-in SmartCard Reader 00 01 00 00
1 Yes L5 built-in SmartCard Reader 00 01 00 01

As there’s only a single reader and the second entry can confuse opensc let’s ignore that one:

$ cat <<EOF > /etc/opensc/opensc.conf
app default {
ignored_readers = "TTXS serial 00 01 00 01","L5 built-in SmartCard Reader 00 01 00 01"
}
EOF

Which should make the output look like

$ opensc-tool -l
# Detected readers (pcsc)
Nr. Card Features Name
0 Yes L5 built-in SmartCard Reader 00 01 00 00

With the reader up and running we can now store the certificate and private key on it. For simplicity we create these on the KDC and transfer them to the phone
(a more realistic setup would create those locally and use a certificate signing request so the private key never leaves your hands):

On the KDC:

$ hxtool issue-certificate --ca-certificate=FILE:krbca.pem --generate-key=rsa --key-bits=2048 --type="pkinit-client" --pk-init-principal="foo@MS20.NIX" --subject="uid=foo,DC=ms20,DC=nix" --certificate="FILE:user.pem"

Then you’d copy the file to your phone and install certificate and private key on the smartcard like (this will delete any already existing keys on the card):

$ # Install needed packages
$ sudo apt install opensc wipe
$ # Delete old keys and install certificate and private key on smartcard
$ pkcs15-init --delete-objects privkey,pubkey,cert --id 1 --auth-id 3
$ pkcs15-init --delete-objects privkey,pubkey,cert --id 2 --auth-id 3
$ pkcs15-init --delete-objects privkey,pubkey,cert --id 3 --store-private-key user.pem --store-certificate user.pem --id 3 --auth-id 3
$ # Wipe the file containing the private key
$ wipe user.pem

Afterward we can look at what’s on the card

$ pkcs15-tool -D

Using reader with a card:

L5 built-in SmartCard Reader 00 01 00 00
PKCS#15 Card [OpenPGP card]: Version : 0
Serial number : <serial number>
Manufacturer ID: ZeitControl
Language : de
Flags : PRN generation, EID compliant
PIN [User PIN]
Object Flags : [0x03], private, modifiable
Auth ID : 03
ID : 02
Flags : [0x13], case-sensitive, local, initialized
Length : min_len:6, max_len:64, stored_len:64
Pad char : 0x00
Reference : 2 (0x02)
Type : UTF-8
Path : 3f00
Tries left : 3
PIN [User PIN (sig)]
Object Flags : [0x03], private, modifiable
Auth ID : 03
ID : 01
Flags : [0x13], case-sensitive, local, initialized
Length : min_len:6, max_len:64, stored_len:64
Pad char : 0x00
Reference : 1 (0x01)
Type : UTF-8
Path : 3f00
Tries left : 3
PIN [Admin PIN]
Object Flags : [0x03], private, modifiable
ID : 03
Flags : [0x9B], case-sensitive, local, unblock-disabled, initialized, soPin
Length : min_len:8, max_len:64, stored_len:64
Pad char : 0x00
Reference : 3 (0x03)
Type : UTF-8
Path : 3f00
Tries left : 3
Private RSA Key [Authentication key]
Object Flags : [0x03], private, modifiable
Usage : [0x222], decrypt, unwrap, nonRepudiation
Access Flags : [0x1D], sensitive, alwaysSensitive, neverExtract, local
Algo_refs : 0
ModLength : 2048
Key ref : 2 (0x02)
Native : yes
Auth ID : 02
ID : 03
MD:guid : 135c302c-ddad-1a3c-f558-8beae7612f9f
Public RSA Key [Authentication key]
Object Flags : [0x02], modifiable
Usage : [0x51], encrypt, wrap, verify
Access Flags : [0x02], extract
ModLength : 2048
Key ref : 0 (0x00)
Native : no
Path : a401
ID : 03
X.509 Certificate [Cardholder certificate]
Object Flags : [0x00]
Authority : no
Path : 3f007f21
ID : 03
Encoded serial : <serial> 

The OpenPGP card’s default admin PIN is 12345678 and the user pin 123456.

PKINIT Setup

Now add [libdefaults]
pkinit_anchors = FILE:/etc/ssl/certs/ca.pem
to your /etc/krb5.conf and place the ca.pem in /etc/ssl/certs.

With that you should be able to use pkinit. The -C option tells kinit to use your smart card for the crypto:

$ kinit -C PKCS11:/usr/lib/aarch64-linux-gnu/pkcs11/onepin-opensc-pkcs11.so foo@MS20.NET
PIN code for L5 built-in SmartCard Reader 00 01 00 00:

After entering the pin and typing klist the output should be similar to the klist output above. If you connect to various Kerberized services in your
realm (e.g. by sending mail) the list will grow without having to retype any PINs or passwords:

$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: foo@ms20.nix

Issued Expires Principal

Oct 24 12:55:13 2022 Oct 25 12:55:13 2022 krbtgt/MS20.NIX@MS20.NIX
Oct 24 12:55:57 2022 Oct 25 12:55:13 2022 imap/imap.ms20.nix@
Oct 24 12:57:45 2022 Oct 25 12:55:13 2022 smtp/smtp.ms20.nix@
Oct 24 13:00:32 2022 Oct 25 12:55:13 2022 HTTP/http.ms20.nix@

Can we make this simpler?

The above is very command line heavy and thus hard to use on a phone. With krb5-auth-dialog  things become easier to set up. There’s no need to adjust /etc/krb5.conf or to copy any certificates. PKINIT via smartcard is being enabled by a single click and you can select your CA (KDC trust anchor) via UI too:

 

There’s a graphical dialog to enter your PIN that can either be started via the menu or triggered via a simple DBus call:

$ gdbus call -e -d org.gnome.KrbAuthDialog -o /org/gnome/KrbAuthDialog -m org.gnome.KrbAuthDialog.acquireTgt ''

and the good part is that it won’t prompt you if you already have a valid ticket. So you can e.g. add this as a hook to your offlineimap config.

As far as I know there’s no end user friendly tool to transfer the certificates to the smartcard yet but note that this doesn’t even have to happen on the phone itself. You can provision those elsewhere and then add the card to the phone but it might make sense to add that functionality to either krb5-auth-dialog or seahorse.

I hope this post motivates people to try this out and to leverage Kerberos for authenticating to services via the Librem 5’s built-in smartcard reader.

Pitfalls

If you get errors like:

kinit: krb5_get_init_creds_opt_set_pkinit: Failed to init cert certs: Failed to init PKCS11 slot 0

Then make sure gpg-agent didn’t grab the card (e.g. because you ran gpg --card-status).

If Firefox won’t authenticate to your server make sure you’ve set negotiate.auth.trusted-uris in about:config

 

The original post was published on Colors of Noisehere.

Recent Posts

Related Content

Tags