Page MenuHome GnuPG

authentication with USB token
Testing, NormalPublic


Possibly, PAM module with SCDaemon, replacing Poldi.

  • authentication for a user
    • host sends the token a challenge to ask signing by auth key
    • host examines the signature if its valid against the public key
  • no special configuration is preferred
    • let us consider use of ~/.ssh/authorized_keys to identify the public key of auth key?
      • it means, when ssh login can be done with the auth key, local login should be also allowed using the token

Event Timeline

gniibe triaged this task as Normal priority.Mar 1 2022, 8:38 AM
gniibe created this task.

Possibly, it could be done with pam_exec
developing a simple executable (or even small shell script).

It may be simpler if we can enhance scdaemon to have an option for PKAUTH, say, --challenge-response, so that it generates a challenge and verify signature internally.

Here is an experimental shell script for testing:

It doesn't support all possible use cases, but some, when popup of pinentry is OK.

It is not good for login manager and screen lock (when unlock), because use of GUI pinentry is not relevant.

Improvements are needed for:

  • Not using gpg-connect-agent (through gpg-agent), but connect scdaemon directly, by assuan protocol
  • It should accept "INQUIRE" of assuan protocol, for PIN interaction
  • Then, user interaction (asking PIN, or asking selection of keys) should be done stdio/stdout, so that it can be through PAM

BTW, there are various use cases for authentication(s), it is better to focus on the part of device and crypto (USB Token and scdaemon).

The rest should be handled by PAM configuration.

  • if it can skip usual passphrase authentication
  • if it's two factor authentication (that is, along with usual passphrase authentication)
  • if it can be used for a session by single time input of passphrase (PIN of USB token == login passphrase); enable USB token at the start of a session

More things to be considered:

  • How to connect scdaemon
  • How to invoke scdaemon

If it is compatible to Poldi, it is:

  • It depends on use case of authentication
    • For login authentication, it is by system, root
      • Invoke scdaemon spawing a process
    • For sudo/screen lock, it is by a user in question
      • Ask gpg-agent for the socket name to scdaemon
        • if no scdaemon, it is invoked by gpg-agent, by asking the sockent name
      • connect to that socket

I write a prototype in Python using pyassuan:

It is used like:

$ python3 -v /run/user/1000/gnupg/S.scdaemon
Please unlock the card

Hit RET means cancelling unlocking the card.

Wrote a pam module which interacts a user for auth:

And updated

This is the one for login authentication (which invokes scdaemon to authenticate, instead of connecting by socket).

Using the above, I configured /etc/pam.d/lightdm by:

--- /etc/pam.d/lightdm.bak	2022-05-17 17:52:50.362767660 +0900
+++ /etc/pam.d/lightdm	2022-05-17 17:53:02.090916824 +0900
@@ -7,7 +7,11 @@
 session      required readenv=1
 session      required readenv=1 envfile=/etc/default/locale
-@include common-auth
+# @include common-auth
+auth	[success=1 default=ignore] log=/tmp/x1 /usr/bin/python3 /home/gniibe/work/ -v
+auth	requisite
+auth	required
 -auth  optional

Then, it works as expected.

NOTE that when you change the PAM configuration file under /etc/pam.d/, you should have another terminal by root (for example, with a virtual console terminal), so that you can change back it even if failure. (Or else, your system will have no way to recover.)

This is updated version of gpg-auth, which clears the authentication state before trying PKAUTH.
Access is controlled by ~/.ssh/authorized_keys.

The exit status is 0 (true), when success. 1 (false) when somthing wrong.

It works with GnuPG 2.3.5 or later, when a user can use scdaemon through gpg-agent.

This may be useful for a user to restrict use of some program only with the card/token.

With cmatrix command and pinentry-gtk2, I now do experiment with this script:

By the command line:

gnome-terminal --hide-menubar --window --full-screen -- ./

it is possible for the full screen terminal to occupy the whole display.

A user can stop cmatrix screen saver, by typing a key. Then, it invokes script.

I think that if this 'screenlock' runs just after logging-in, it is virtually login with an authentication using a card/token, with no PAM configuration.

I added the last line, to recover tty state:

Note that this doesn't work if pinentry is pinentry-gnome3. pinentry-qt works well, too, because it supports curses fallback.

A concrete example use case in my mind is:

  • (Usual display manager (authentication by password or no-password))
  • session starts with "locked" state of screen
    • In the beginning, user needs to "unlock" the screen, by scdaemon authentication
  • (optionally, if needed) our-own-screen-locker should detect device removal, then, automatically locks the screen
  • our-own-screen-locker should detect idling user session, then, disabling the card, automatically locks the screen
  • our-own-screen-locker does authentication by scdaemon when it unlocks the screen

I did some research about scree lockers (xtrlock, slock, swaylock, etc.).

I think that writhing Authentication Module would be a good option, for now.

Wrote a shell script for xsecurelock's authproto (helper executable):

In the script, we specify another script (so, please edit the path):

I tested by:

export XSECURELOCK_AUTHPROTO=/home/gniibe/work/gpgauth/


Through the dialog by xsecurelock, entering PIN, it authenticates a user by smartcard/token.

I changed gpg-connect-agent (added --unbuffered option) so that we can write shell script interacting gpg-agent.

Updated (with T6012):

It prompts a user for the insertion of a device, if it's not available.


Now, it also supports a reader with pinpad.

Procedure for testing:

  1. Install xsecurelock
  2. Put, and somewhere. Don't forget to have +x mode.
  3. Edit so that scripts works (the paths of and
  4. Set environment variable XSECURELOCK_AUTHPROTO. For example, I do:
$ export XSECURELOCK_AUTHPROTO=/home/gniibe/work/gpgauth/
  1. Run xsecurelock command

5.1 Touch a key, then dialog shows up.
5.2. It shows a keygrip for key, when it's not yet inserted, to prompt the device insertion. After insertion, type RETURN. Or type ESC to cancel the device with the keygrip.
5.3 It shows the keygrip, then proceeds to PIN input by keyboard (if it's not with pinpad).
5.3.1 (keyboard) Type ESC to cancel the device, if there are multiple candidates, it proceeds to next device.
5.3.2 (keyboard) Type PIN, then it unlocks the screen.
5.4 It shows the keygrip, then proceeds to PIN input by pinpad on a cardreader.
5.4.1 (pinpad) Cancel PIN input by "X" cancel button on a cardreader.
5.4.2 (pinpad) Put PIN by pinpad, then it unlocks the screen.

Not possible in current scheme: Canceling by keyboard when it asks PIN by pinpad.

gniibe renamed this task from authentication with USB token, screen lock on token removal to authentication with USB token, ~~screen lock on token removal~~.Jun 8 2022, 4:07 AM
gniibe renamed this task from authentication with USB token, ~~screen lock on token removal~~ to authentication with USB token.

While playing with your scripts I figured that it would be useful to enhance the KEYINFO command. With
rG989eae648c8f3d2196517e8fc9cce247b21f9629 we could now

"- KEYINFO --list"
"+ KEYINFO --list --need-attr=Use-for-ssh:"

and only ssh keys will be considered. The attribute needs to be set, though. A script to convert sshcontrol to the new attributes would also be nice to have.

Here is a PAM module, which interact a spawned process using authproto protocol of xsecurelock.

Lines in /etc/pam.d/login can be modified like:

# Standard Un*x authentication.
#@include common-auth
auth [success=1 default=ignore] /usr/local/libexec/gpg-auth --use-scdaemon-directly
auth	requisite
auth	required
auth	optional 
# ######################

I tested and it works for me (normal token and card with pinpad reader).

Here are pam_authproto.c with Makefile, so that you can compile it with libpam:

Testing gpg-auth : There are two different use cases

  • test with xsecurelock for screen lock
  • test with pam-autoproto for login / gdm / etc.


  • Configure your token/card, so that you can use it with GnuPG
    • Check it's availability by gpg-card or gpg --card-status, if token/card can be seen by GnuPG
    • You should have "authentication key" on your token/card
  • Configure your SSH, so that you can login to the host by the "authentication key" on the token/card
    • There should be your key listed in ~/.ssh/authorized_keys
    • You should be able to login by token/card
      • Please test: ssh localhost and make sure it works

Test with xsecurelock

  1. Build and install GnuPG (2.3, from master)
    • gpg-auth should be installed
  2. Set environment variable XSECURELOCK_AUTHPROTO. For example, I do:

    $ export XSECURELOCK_AUTHPROTO=/usr/local/libexec/gpg-auth
  3. Run xsecurelock command

3.1 Touch a key, then dialog shows up, asking your PIN for token/card
3.2 (keyboard) if it's not with pinpad cardreader
3.2.1 (keyboard) Type ESC to cancel the token/card, if there are multiple candidates, it proceeds to next device.
3.2.2 (keyboard) Type PIN, then it unlocks the screen.
3.3 (pinpad) if it's with pinpad cardreader
3.3.1 (pinpad) Cancel PIN input by "X" cancel button on a cardreader.
3.3.2 (pinpad) Put PIN by pinpad, then it unlocks the screen.
3.3.3 (pinpad) Note that: you cannot cancel the dialog by keyboard input

If it works, you can configure xsecurelock to use gpg-auth.

Test with login (for example) using PAM

  1. gpg-auth should be available
  2. pam-authproto should be installed (download, extract by tar, make, make install (modify SECUREDIR if needed))
  3. Edit (by root) /etc/pam.d/login or some configuration file for login. Comment out common-auth to be replaced by... like:
# Standard Un*x authentication.
# @include common-auth
auth [success=1 default=ignore] /usr/local/libexec/gpg-auth --use-scdaemon-directly
auth	requisite
auth	required
  1. Don't logout from your Graphical Session (if you do some severe mistake, it may be possible you lose a way to login)
  2. Release your token/card from control by scdaemon of your session. Simply do:
$ gpgconf --kill scdaemon
  1. Using a virtual console (CTRL-ALT-F1 or F2...), try testing login
  2. Switch back to the graphical session (CTRL-ALT-F7, for example)
  3. Note that you can use gdm and lightdm, but sddm doesn't work well.
    • when you test gdm or lightdm, you should have another session in a virtual console, so that you keep a way to control your computer, even with wrong configuration.
werner changed the task status from Open to Testing.Sep 22 2022, 10:56 AM
werner removed a project: Restricted Project.