Using an OpenPGP card in the UbuntuPhone BQ E4.5


Figure 1: The device BQ E4.5 with the attached OpenPGP card

This small tutorial describes how to setup and use GnuPG together with an OpenPGP card in the mobile device BQ E4.5, running Ubuntu 15-04. The device root file system is for good reason mounted read-only. I.e. one can not just install any other piece of software into it. The way used here is an additional Linux system inside the phones system and chroot-ing into it for the to be installed software, and later calling the software from outside the chroot'ed file system.

When you login into the phone, either by SSH or via the terminal-app, your HOME directory is /home/phablet. phablet is the default user in the phone. I have created there an additional directory /home/phablet/myRoot and below this untar'ed a complete Debian based Linux. How to do this is described in a small Gitbook about the BQ E4.5.

In the following text as naming convention the shell prompt $ means, we are in the phones file system and something like root@ubuntu-phablet:/# or phablet@ubuntu-phablet:~$ means, we are in the chroot'ed file system, best to understand with these commands:

$ ssh phablet@

Welcome to Ubuntu 15.04 (GNU/Linux 3.4.67 armv7l)

Last login: Mon Sep 25 07:45:37 2017 from


phablet@ubuntu-phablet-bq:~$ PS1='$ '

$ sudo chroot /home/phablet/myRoot
[sudo] password for phablet:
root@ubuntu-phablet:/# su phablet
phablet@ubuntu-phablet:/$ cd
phablet@ubuntu-phablet:~$ pwd

Installing GnuPG 2.2.1 into the 'myRoot' system

In the chroot'ed system we install some additional packages:

phablet@ubuntu-phablet:~$ sudo apt-get install pinentry-curses
phablet@ubuntu-phablet:~$ sudo apt-get install pass
phablet@ubuntu-phablet:~$ sudo apt-get install libudev-dev
phablet@ubuntu-phablet:~$ sudo apt-get install libusb-dev
phablet@ubuntu-phablet:~$ sudo apt-get install libusb-1.0-0-dev

pass is a small password-storage manager which we will later use for our GnuPG encrypted tree of password, for example for websites or any other purpose, bank account PIN, …

pinentry-curses is used by the gpg-agent to ask for the OpenPGP card PIN, i.e. all the usage is later done or in a SSH session or in the terminal-app.

We now compile the following pieces of GnuPG software in that order:


always with ./configure && make && sudo make install The compiled software ends up below /usr/local (i.e. below /home/phablet/myRoot/usr/local when one looks from outside the chroot'ed phone system);

Now in the phone system we configure for GnuPG the following config files:

$ mkdir ~/.gnupg

$ cat .gnupg/gpg.conf
agent-program  /home/phablet/myRoot/usr/local/bin/gpg-agent

$ cat .gnupg/gpg-agent.conf
pinentry-program /home/phablet/myRoot/usr/bin/pinentry-curses
scdaemon-program /home/phablet/myRoot/usr/local/libexec/scdaemon
log-file /home/phablet/gpg-agent.log
log-file /dev/null
debug-level guru

Due to the nature of the installation in the chroot'ed system we need small wrapper scripts to set PATH, LD_LIBRARY_PATH, … and other stuff;

$ cat ~/
LD_LIBRARY_PATH=/home/phablet/myRoot/usr/local/lib export LD_LIBRARY_PATH
PATH=/home/phablet/myRoot/usr/local/bin:$PATH      export PATH
GNUPGHOME=/home/phablet/.gnupg    export GNUPGHOME
GPG_TTY=$(tty)                    export GPG_TTY
/home/phablet/myRoot/usr/local/bin/gpg-agent    \
	    --homedir /home/phablet/.gnupg      \
	    --daemon                            \
	    --pinentry-program /home/phablet/myRoot/usr/bin/pinentry-curses
/home/phablet/myRoot/usr/local/bin/gpg-connect-agent /bye
/home/phablet/myRoot/usr/local/bin/gpg $*

run and create for test a key pair (later we want to use the OpenPGP card key pair for instead of this)

$ ~/ --full-generate-key
gpg-agent[2973]: enabled debug flags: mpi crypto memory cache memstat hashing ipc
gpg (GnuPG) 2.2.1; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?

This starts the gpg-agent as:

$ ps ax | grep gpg-a
 2974 ?        Ss     0:00 /home/phablet/myRoot/usr/local/bin/gpg-agent --homedir /home/phablet/.gnupg --daemon --pinentry-program /home/phablet/myRoot/usr/bin/pinentry-curses

Now we can use the 'pass' command we installed in the chroot'es system with

$ cat
LD_LIBRARY_PATH=/home/phablet/myRoot/usr/local/lib export LD_LIBRARY_PATH
PATH=/home/phablet/myRoot/usr/local/bin:$PATH      export PATH
GNUPGHOME=/home/phablet/.gnupg    export GNUPGHOME
GPG_TTY=$(tty)                    export GPG_TTY
/home/phablet/myRoot/usr/bin/pass $*

Init the pass storage as:

$ ./ init Matthias

		 │ Please enter the passphrase to unlock the OpenPGP secret key:  │
		 │ "Matthias Apitz (test) <>"                        │
		 │ 2048-bit RSA key, ID 93A6FBF52FA76DB0,                         │
		 │ created 2017-09-22 (main key ID 3FECB79DDDA409E4).             │
		 │                                                                │
		 │                                                                │
		 │ Passphrase: ***_______________________________________________ │
		 │                                                                │
		 │         <OK>                                    <Cancel>       │

$ find .password-store/

Insert some password for test:

$ ./ insert -m web/bla
Enter contents of web/bla and press Ctrl+D when finished:

Username: guru

$ ./ web/bla
Username: guru

Final step is getting support for the OpenPGP card. We need the 'pcscd' daemon. Its build is a bit tricky because it must later, on start from outside the chroot'ed syste, find the ccid driver.

We compile the following pieces inside the chroot'ed system:

pcsc-lite-1.8.20 ccid-1.4.25

with the following options set on ./configure

phablet@ubuntu-phablet-bq:~$ cd pcsc-lite-1.8.20
phablet@ubuntu-phablet-bq:~/pcsc-lite-1.8.20$ ./configure --enable-usbdropdir=/home/phablet/myRoot/usr/local/lib/pcsc/drivers --enable-confdir=/home/phablet/myRoot/etc/reader.conf.d

PC/SC lite has been configured with following options:

Version:             1.8.20
System binaries:     /usr/local/sbin
Configuration dir:   /usr/local/etc/reader.conf.d

Host:                armv7l-unknown-linux-gnueabihf
Compiler:            gcc
Preprocessor flags:  -I${top_srcdir}/src
Compiler flags:      -Wall -fno-common -g -O2
Preprocessor flags:  -I${top_srcdir}/src
Linker flags:
Libraries:           -ldl  -lrt

PTHREAD_CFLAGS:      -pthread
PCSC_ARCH:           Linux

pcscd binary            /usr/local/sbin/pcscd
polkit support:         no
polkit policy dir:
libudev support:        yes
libusb support:         no
USB drop directory:     /home/phablet/myRoot/usr/local/lib/pcsc/drivers
ATR parsing messages:   false
ipcdir:                 /var/run/pcscd
use serial:             yes
use usb:                yes
systemd unit directory: /lib/systemd/system
serial config dir.:     /home/phablet/myRoot/etc/reader.conf.d
filter:                 no

PCSCLITE_FEATURES:       Linux armv7l-unknown-linux-gnueabihf serial usb libudev usbdropdir=/home/phablet/myRoot/usr/local/lib/pcsc/drivers ipcdir=/var/run/pcscd configdir=/home/phablet/myRoot/etc/reader.conf.d

checking that generated files are newer than configure... done

phablet@ubuntu-phablet-bq:~/ccid-1.4.25$ make
phablet@ubuntu-phablet-bq:~/ccid-1.4.25$ sudo make install

ok, now the 'ccid' driver, installed (copied) to be seen by the daemon:

phablet@ubuntu-phablet-bq:~$ cd ccid-1.4.25
phablet@ubuntu-phablet:~/ccid-1.4.25$ ./configure -enable-usbdropdir=/home/phablet/myRoot/usr/local/lib/pcsc/drivers
libccid has been configured with following options:

Version:             1.4.25
User binaries:       /usr/local/bin
Configuration files: /usr/local/etc

Host:                armv7l-unknown-linux-gnueabihf
Compiler:            gcc
Preprocessor flags:
Compiler flags:      -g -O2
Preprocessor flags:
Linker flags:

PCSC_CFLAGS:         -pthread -I/usr/local/include/PCSC
PCSC_LIBS:           -L/usr/local/lib -lpcsclite
PTHREAD_CFLAGS:      -pthread
BUNDLE_HOST:         Linux
DYN_LIB_EXT:         so
LIBUSB_CFLAGS:       -I/usr/include/libusb-1.0
LIBUSB_LIBS:         -lusb-1.0
SYMBOL_VISIBILITY:   -fvisibility=hidden

libusb support:          yes
composite as multislot:  no
multi threading:         yes
bundle directory name:   ifd-ccid.bundle
USB drop directory:      /home/phablet/myRoot/usr/local/lib/pcsc/drivers
serial Twin support:     no
serial twin install dir: /home/phablet/myRoot/usr/local/lib/pcsc/drivers/serial
serial config directory: /home/phablet/myRoot/etc/reader.conf.d
compiled for pcsc-lite:  yes
syslog debug:            no
class driver:            yes


phablet@ubuntu-phablet:~/ccid-1.4.25$ make
phablet@ubuntu-phablet:~/ccid-1.4.25$ sudo make install

the driver and its control file Info.plist ended up as configured in:

phablet@ubuntu-phablet:~$ find /home/phablet/myRoot/usr/local/lib/pcsc/drivers/ifd-ccid.bundle/Contents/

This is fine, but if we run the daemon from outside the chroot'ed system, the files must be in some other place because /home/phablet/myRoot is added in front; so we copy them over to the correct place:

phablet@ubuntu-phablet:~$ sudo mkdir -p /usr/local/lib/pcsc/drivers/ifd-ccid.bundle
phablet@ubuntu-phablet:~$ sudo cp -rp /home/phablet/myRoot/usr/local/lib/pcsc/drivers/ifd-ccid.bundle/Contents /usr/local/lib/pcsc/drivers/ifd-ccid.bundle
phablet@ubuntu-phablet:~$ find /usr/local/lib/pcsc/drivers/ifd-ccid.bundle

From outside the chroot'ed system we can now start the daemon as:

$ sudo /home/phablet/myRoot/usr/local/sbin/pcscd --foreground --debug | tee pcscd.log

and check the log file pcscd.log to see if it sees the card attaching (see at the very end of the write-up);

Now we start in the phone the pcscd daemon as:

$ sudo /home/phablet/myRoot/usr/local/sbin/pcscd
$ ps ax | grep pcscd
31669 pts/53   Sl     0:00 /home/phablet/myRoot/usr/local/sbin/pcscd

and run the gpg –card-status to see if it finds the card on attach:

$ ./ --card-status
gpg-agent[20254]: enabled debug flags: mpi crypto memory cache memstat hashing ipc
gpg-agent: a gpg-agent is already running - not starting a new one
gpg-agent: random usage: poolsize=600 mixed=0 polls=0/0 added=0/0
	      outmix=0 getlvl1=0/0 getlvl2=0/0
gpg-agent: secmem usage: 0/32768 bytes in 0 blocks
Reader ...........: Identiv uTrust 3512 SAM slot Token [CCID Interface] (55511514602745) 00 00
Application ID ...: D27600012401020100050000532B0000
Version ..........: 2.1
Manufacturer .....: ZeitControl
Serial number ....: 0000532B
Name of cardholder: Matthias Apitz
Language prefs ...: en
Sex ..............: unspecified
URL of public key :
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 32 32 32
PIN retry counter : 3 0 3
Signature counter : 457
Signature key ....: 5E69 FBAC 1618 562C B3CB  FBC1 47CC F7E4 76FE 9D11
      created ....: 2017-05-14 18:20:07
Encryption key....: EB62 00DA 13A1 9E80 679B  1A13 61F1 ECB6 25C9 A6C3
      created ....: 2017-05-14 18:20:07
Authentication key: E51D D2D6 C727 35D6 651D  EA4B 6AA5 C5C4 51A1 CD1C
      created ....: 2017-05-14 18:20:07
General key info..: [none]

Now we removed /home/phablet/.gnupg (saving the *.conf files) and copied over from my real netbook the /.password-store and the key material for the OpenPGP card; let's see if 'pass' can unlock the card (via the gpg-agent) and decipher the crypted information (uncrypted shown here as XXXXXXXX-XXXXXX). The gpg-agent will first ask for the card to be inserted and then for its PIN.

$ ./


Figure 2: The request for the card


Figure 3: The request for the PIN


on the 2nd run it does not need anymore the PIN:

$ ./

i.e. all is fine!

This is only the debug log of the pcscd daemon for reference.

00000000 debuglog.c:289:DebugLogSetLevel() debug level=debug
00001760 configfile.l:282:DBGetReaderListDir() Parsing conf directory: /home/phablet/myRoot/etc/reader.conf.d
00000840 configfile.l:319:DBGetReaderListDir() Skipping non regular file: .
00000349 configfile.l:319:DBGetReaderListDir() Skipping non regular file: ..
00000364 configfile.l:358:DBGetReaderList() Parsing conf file: /home/phablet/myRoot/etc/reader.conf.d/libccidtwin
00000568 pcscdaemon.c:655:main() pcsc-lite 1.8.20 daemon ready.
00007279 hotplug_libudev.c:294:get_driver() Looking for a driver for VID: 0x1D6B, PID: 0x0002, path: /dev/bus/usb/001/001
07475463 hotplug_libudev.c:648:HPEstablishUSBNotifications() USB Device add
00005501 hotplug_libudev.c:294:get_driver() Looking for a driver for VID: 0x04E6, PID: 0x5816, path: /dev/bus/usb/001/009
00000555 hotplug_libudev.c:433:HPAddDevice() Adding USB device: Identiv uTrust 3512 SAM slot Token
00000673 readerfactory.c:1079:RFInitializeReader() Attempting startup of Identiv uTrust 3512 SAM slot Token [CCID Interface] (55511514602745) 00 00 using /home/phablet/myRoot/usr/local/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux/
00001129 readerfactory.c:954:RFBindFunctions() Loading IFD Handler 3.0
00013183 ifdhandler.c:1953:init_driver() Driver version: 1.4.25
00004027 ifdhandler.c:1970:init_driver() LogLevel: 0x0003
00004427 ifdhandler.c:1981:init_driver() DriverOptions: 0x0000
00001127 ifdhandler.c:110:CreateChannelByNameOrChannel() Lun: 0, device: usb:04e6/5816:libudev:0:/dev/bus/usb/001/009
00001212 ccid_usb.c:287:OpenUSBByName() Using: /home/phablet/myRoot/usr/local/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
00005565 ccid_usb.c:305:OpenUSBByName() ifdManufacturerString: Ludovic Rousseau (
00001479 ccid_usb.c:306:OpenUSBByName() ifdProductString: Generic CCID driver
00000362 ccid_usb.c:307:OpenUSBByName() Copyright: This driver is protected by terms of the GNU Lesser General Public License version 2.1, or (at your option) any later version.
00003937 ccid_usb.c:621:OpenUSBByName() Found Vendor/Product: 04E6/5816 (Identiv uTrust 3512 SAM slot Token)
00000667 ccid_usb.c:623:OpenUSBByName() Using USB bus/device: 1/9
00000337 ccid_usb.c:680:OpenUSBByName() bNumDataRatesSupported is 0
00010195 ifdhandler.c:379:IFDHGetCapabilities() tag: 0xFB3, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00000626 readerfactory.c:395:RFAddReader() Using the reader polling thread
00000838 ifdhandler.c:379:IFDHGetCapabilities() tag: 0xFAE, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00000470 ifdhandler.c:470:IFDHGetCapabilities() Reader supports 1 slot(s)
00001264 ifdhandler.c:1146:IFDHPowerICC() action: PowerUp, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00032378 eventhandler.c:286:EHStatusHandlerThread() powerState: POWER_STATE_POWERED
00000596 Card ATR: 3B DA 18 FF 81 B1 FE 75 1F 03 00 31 C5 73 C0 01 40 00 90 00 0C
05001478 ifdhandler.c:1146:IFDHPowerICC() action: PowerDown, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00003148 eventhandler.c:479:EHStatusHandlerThread() powerState: POWER_STATE_UNPOWERED
14774363 hotplug_libudev.c:642:HPEstablishUSBNotifications() USB Device removed
00000796 hotplug_libudev.c:360:HPRemoveDevice() Removing USB device[0]: Identiv uTrust 3512 SAM slot Token [CCID Interface] (55511514602745) at /dev/bus/usb/001/009
00000053 readerfactory.c:608:RFRemoveReader() UnrefReader() count was: 1
00000024 eventhandler.c:176:EHDestroyEventHandler() Stomping thread.
00000026 ifdhandler.c:379:IFDHGetCapabilities() tag: 0xFB1, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00000024 ifdhandler.c:379:IFDHGetCapabilities() tag: 0xFB2, usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00000018 eventhandler.c:201:EHDestroyEventHandler() Request stopping of polling thread
00000020 ifdhandler.c:344:IFDHStopPolling() usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00397726 eventhandler.c:502:EHStatusHandlerThread() Die
00001909 eventhandler.c:216:EHDestroyEventHandler() Thread stomped.
00000049 readerfactory.c:1130:RFUnInitializeReader() Attempting shutdown of Identiv uTrust 3512 SAM slot Token [CCID Interface] (55511514602745) 00 00.
00000039 ifdhandler.c:282:IFDHCloseChannel() usb:04e6/5816:libudev:0:/dev/bus/usb/001/009 (lun: 0)
00000101 ccid_usb.c:797:WriteUSB() write failed (1/9): -4 LIBUSB_ERROR_NO_DEVICE
00000147 ccid_usb.c:189:close_libusb_if_needed() libusb_exit
00001864 readerfactory.c:991:RFUnloadReader() Unloading reader driver.