GPG SmartCard Cache Checking

In previous posts I talked about checking if a gpg key was cached, and used a broad check with my awk to do so. By 'broad', I just wish to point out that you could build the check around a specific key-grip if you cared to. And recently I got around to ordering a 'traditional' smartcard from kernel concepts. While I've had and used a yubikey neo for a while with my laptop/phone, I have been utilizing an on laptop subkey for 'convenience'. The local subkey was handling my pass (password store) datebase, and in the past my cryptshotr keyfile.

Personal Desires

My reason for getting a classic style smartcard are as follows:

  1. I have the card slot on my laptop.
  2. I don't want to have a fullsized yubikey sticking out all the time.
  3. I don't want to burn a USB port by always having a yubikey nano living in it.

Hands on

After getting the card I trimmed the edge down so that it barely sticks out from the laptop.

Multiple SmartCard Woes

To set up my new card, I pulled a copy of my non-stub laptop keys (encryption, signature, and authentication) out of secure storage, and ran a export GNUPGHOME=... to point at the copy. Once my keys and configurations are finished on the new card and I was back to my normal keyring (temp dir having been shreaded), I ran into an issue where it kept asking for the yubikey by serial... Unfortunately running gpg --card-edit and fetch did not help. To fix this I ran good old gpg-connect-agent 'keyinfo --list' /bye 2>/dev/null (from the previous posts: Part1, Part2) to identify the key-grips, and deleted the three offending /.gnupg/private-keys-v1.d/KEY_GRIP_HERE.key files. After they were cleared out, I was successfully about to gpg --card-edit and fetch from the new key.

Verifying Functionality

While testing that the keys on the smartcard worked, I also checked on my previous cache check solution. Sadly I found that gpg-connect-agent 'keyinfo --list' /bye 2>/dev/null doesn't show the smartcard status.

While reading thought the output of gpg-connect-agent 'help' /bye, and each individual help page, I see scd.

~ -> gpg-connect-agent 'help scd' /bye
# SCD <commands to pass to the scdaemon>
#
# This is a general quote command to redirect everything to the
# SCdaemon.
OK

Alice Falling Down the Rabbit Hole

So now, here I am in my bash shell, sending commands to the gpg-agent shell, sending commands to the scdaemon shell.

~ -> gpg-connect-agent 'scd help' /bye
# NOP
# CANCEL
# OPTION
# BYE
# AUTH
# RESET
# END
# HELP
# SERIALNO [--demand=<serialno>] [<apptype>]
# LEARN [--force] [--keypairinfo]
# READCERT <hexified_certid>|<keyid>
# READKEY [--advanced] <keyid>
# SETDATA [--append] <hexstring>
# PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>
# PKAUTH <hexified_id>
# PKDECRYPT <hexified_id>
# INPUT
# OUTPUT
# GETATTR <name>
# SETATTR <name> <value>
# WRITECERT <hexified_certid>
# WRITEKEY [--force] <keyid>
# GENKEY [--force] [--timestamp=<isodate>] <no>
# RANDOM <nbytes>
# PASSWD [--reset] [--nullpin] <chvno>
# CHECKPIN <idstr>
# LOCK [--wait]
# UNLOCK
# GETINFO <what>
# RESTART
# DISCONNECT
# APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]
# KILLSCD
OK

While reading through the help pages on each of these, GETINFO looks promising.

~ -> gpg-connect-agent 'scd help getinfo' /bye
# GETINFO <what>
#
# Multi purpose command to return certain information.
# Supported values of WHAT are:
#
# version     - Return the version of the program.
# pid         - Return the process id of the server.
#
# socket_name - Return the name of the socket.
#
# status - Return the status of the current reader (in the future, may
# also return the status of all readers).  The status is a list of
# one-character flags.  The following flags are currently defined:
#   'u'  Usable card present.  This is the normal state during operation.
#   'r'  Card removed.  A reset is necessary.
# These flags are exclusive.
#
# reader_list - Return a list of detected card readers.  Does
#               currently only work with the internal CCID driver.
#
# deny_admin  - Returns OK if admin commands are not allowed or
#               GPG_ERR_GENERAL if admin commands are allowed.
#
# app_list    - Return a list of supported applications.  One
#               application per line, fields delimited by colons,
#               first field is the name.
#
# card_list   - Return a list of serial numbers of active cards,
#               using a status response.
OK

Particularly, I noted card_list and its used of the word 'active'.

Upon testing, I find that gpg-connect-agent 'scd getinfo card_list' /bye is a seemingly reliable way of seeing if a smartcard key is in cache.

If the card is inserted AND the card has been used to decrypt you get:

~ -> gpg-connect-agent 'scd getinfo card_list' /bye
S SERIALNO REDACTEDREDACTEDREDACTEDREDACTED
OK

If the card is removed, if scdaemon wasn't running, or if the card is inserted AND the card has not been used:

~ -> gpg-connect-agent 'scd getinfo card_list' /bye
OK

Implementing

The following is a tasty little piece of awk that check is any smartcard is listed as active, and returns '1' if yes.

gpg-connect-agent 'scd getinfo card_list' /bye 2>/dev/null | awk 'BEGIN {CH=0} /SERIALNO/ {if($0!=""){CH=1}} END {print CH}'

And for a cherry on top, there is a 'oneliner' that will check if either a local key or a smartcard is cached:

{ gpg-connect-agent 'keyinfo --list' /bye 2>/dev/null; gpg-connect-agent 'scd getinfo card_list' /bye 2>/dev/null; } | awk 'BEGIN{CH=0} /^S/ {if($7==1){CH=1}; if($2=="SERIALNO"){CH=1}} END{if($0!=""){print CH} else {print "none"}}'

Bonus: Pictures of smartcards in a Thinkpad.

Because it is impossible to find a picture of a smartcard in a Thinkpad laptop on the internet, here is my x260 with a card in it.

  • normal thinkpad_smartcard_full
  • trimmed thinkpad_smartcard_cut

kernel concepts
yubikey neo
password store
Part1
Part2

- demure