This is an implementation of a simple PIV client for desktop Linux and OSX with
minimal dependencies. It contains a piv-tool binary which can conduct basic
operations using PIV cards, and the piv-agent, which implements the SSH agent
protocol as a drop-in replacement for the OpenSSH ssh-agent command (except
that the keys it contains are always on a PIV card).
"PIV cards" notably includes Yubico Yubikey devices such as the NEO and Yubikey4, which can store up to 24 keys by using the "retired key" slots (which this agent supports).
This project re-uses most of the agent and protocol parsing code from OpenSSH, where it’s been pretty thoroughly battle-hardened.
Using the PIV agent is identical to running the normal ssh-agent command,
with the exception that piv-agent requires a -g argument specifying the
GUID of the PIV card to attach to. You can also give a -K argument with
the public key of the "Card Authentication" slot (9E) for extra security.
For example, the GUID of my PIV card is 995E171383029CDA0D9CDBDBAD580813 (if
you don’t know your GUID, the piv-tool command can display it). I can use the
following command to start the piv-agent against my card:
$ piv-agent -g 995E171383029CDA0D9CDBDBAD580813 bash $ ssh-add -l 256 SHA256:PJ6ucGKUqlQhiJdArDaF65+AVImg8SVq77vL6nVE/ME PIV_slot_9C /CN=Digital Signature (ECDSA) 256 SHA256:U86TVxP/gxVk4CQibIWit3Q+/5i4aZuXa2NALIahjww PIV_slot_9E /CN=Card Authentication (ECDSA)
I can now use the 9E Card Authentication key with ssh or any other tools that
use speak the OpenSSH agent protocol (e.g. the Joyent manta and triton
tools). If I try to use the 9C Digital Signature key right now though, I will
get an error like this:
$ ssh user@host sign_and_send_pubkey: signing failed: agent refused operation user@host: Permission denied (publickey).
To use the 9C key I will have to give my PIV PIN to the agent using the
ssh-add -X command, so that it can unlock the card to use the key.
$ ssh-add -X Enter lock password: Agent unlocked. $ ssh user@host Last login: Wed Mar 28 21:56:11 2018 from laptop [user@host ~]$
The PIV PIN is stored in memory only with special guard pages allocated either side of it and marked non-swappable. On Linux the memory area is also marked as non-dumpable so that it does not appear in core files.
You can make the agent forget the PIN by using the ssh-add -x command to
"lock" it. You can supply any password you like (including an empty string)
for the "lock" command. The command ssh-add -D can also be used, and will not
prompt for a password (useful from scripts).
The agent will also forget the PIN automatically if the PIV card is unavailable for more than a few minutes, or if unusual conditions occur (e.g. an attacker tries to plug in a device with the same GUID that fails the 9E signature test).
Note that it’s perfectly fine to leave the piv-agent running and remove your
PIV card: the agent will just return errors on any attempt to use it until
you insert your card again (you will need to enter your PIN again). You can
even start the agent without the PIV card present at all.
One useful way to use the piv-agent is to set up a systemd unit file for it
to run whenever you log in, and adjust your shell profile to use it as your
normal SSH agent. Then your PIV keys are automatically ready for use in any
shell.
There are two other tools that can be built as part of this repository as well:
piv-tool and piv-zfs. The piv-tool utility is a general-purpose PIV
information and management tool that uses the same client code as piv-agent,
which can be useful for testing or debugging (you don’t have to use it for
setting up keys, you can use yubico-piv-tool or any other PIV client just as
well).
The piv-zfs tool is an early draft of a tool for managing ZFS
encryption-at-rest keys using PIV EC. You can see a brief demo of it in action
at https://asciinema.org/a/dsENdri85rPkOk6jpWsqCkUzn. The Makefile will only
try to build piv-zfs on Linux or illumos and only if it detects libzfs is
available.
On Linux you will need to have a compiler and basic build tools and headers
installed, as well as the libraries pcsclite and libbsd (and their -dev
packages if your distro does those). Some musl based distros will also require
installing libedit.
Then, clone this repository and use make to build the binaries:
$ git clone https://github.com/arekinath/piv-agent
$ cd piv-agent
$ make
$ ./piv-tool list
card: 995E1713
device: Yubico Yubikey NEO OTP+U2F+CCID 02 00
chuid: ok
guid: 995E171383029CDA0D9CDBDBAD580813
owner: 00000000000000000000000000000000
fasc-n: D4E739DA739CED39CE739D836858210842108421384210C3F5
expiry: 2030-01-01
yubico: implements YubicoPIV extensions (v1.0.4)
auth: PIN*
slots:
ID TYPE BITS CERTIFICATE
9c ECDSA 256 /CN=Digital Signature
9e ECDSA 256 /CN=Card Authentication
You can run make install to install the agent into /opt/piv-agent and set
up a user systemd service to start it automatically at login. The make install
command will also print out lines to add to your .profile or .bashrc to
make sure the agent is automatically available in all your shells (while still
preferring a forwarded SSH agent if you SSH into your machine later).
$ make install
Enter a GUID to use for piv-agent: 995E171383029CDA0D9CDBDBAD580813
sudo install -o root -g wheel -m 0755 -d /opt/piv-agent/bin
Password:
sudo install -o root -g wheel -m 0755 piv-agent piv-tool /opt/piv-agent/bin
install -d /home/alex/.config/systemd/user
install .dist/piv-agent.service /home/alex/.config/systemd/user
systemctl --user enable piv-agent.service
systemctl --user start piv-agent.service
Add the following lines to your .profile or .bashrc:
export PATH=/opt/piv-agent/bin:$PATH
if [[ ! -e "$SSH_AUTH_SOCK" || "$SSH_AUTH_SOCK" == *"/keyring/"* ]]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
fi
Compiling on OSX is even easier. Rather than depend on homebrew or MacPorts or
another similar system, we simply build libressl-portable in a subdirectory
and statically link the binaries against it. The Makefile in this repository
will handle it all for you.
Note there is no need to install PCSClite or OpenSC or any of the related
tools or libraries on OSX — the PCSC framework built into the operating system
itself works fine for piv-agent.
The commands you will need to run are as follows:
## Clone the piv-agent repository
$ git clone https://github.com/arekinath/piv-agent
$ cd piv-agent
## Build libressl and then piv-agent
$ make -j4
$ ./piv-tool list
card: 995E1713
device: Yubico Yubikey NEO OTP+U2F+CCID 02 00
chuid: ok
guid: 995E171383029CDA0D9CDBDBAD580813
owner: 00000000000000000000000000000000
fasc-n: D4E739DA739CED39CE739D836858210842108421384210C3F5
expiry: 2030-01-01
yubico: implements YubicoPIV extensions (v1.0.4)
auth: PIN*
slots:
ID TYPE BITS CERTIFICATE
9c ECDSA 256 /CN=Digital Signature
9e ECDSA 256 /CN=Card Authentication
## Install in /opt/piv-agent and add a launchd service for the agent
$ make install
Enter a GUID to use for piv-agent: 995E171383029CDA0D9CDBDBAD580813
sudo install -o root -g wheel -m 0755 -d /opt/piv-agent/bin
Password:
sudo install -o root -g wheel -m 0755 piv-agent piv-tool /opt/piv-agent/bin
install .dist/net.cooperi.piv-agent.plist /Users/alex/Library/LaunchAgents
launchctl load /Users/alex/Library/LaunchAgents/net.cooperi.piv-agent.plist
/Users/alex/Library/LaunchAgents/net.cooperi.piv-agent.plist: service already loaded
launchctl start net.cooperi.piv-agent
Add the following lines to your .profile or .bashrc:
export PATH=/opt/piv-agent/bin:$PATH
if [[ ! -e "$SSH_AUTH_SOCK" || "$SSH_AUTH_SOCK" == *"launchd"* ]]; then
source $HOME/.ssh/agent.env >/dev/null
fi
There is one known issue on OSX currently: the PCSC framework does not work
after calling fork(), which forces the piv-agent code to not be able to run
in the background (this means using piv-agent bash to start a shell doesn’t
work, for example). The best way to use it on OSX is set up as a launchd
service.
Like on Linux, there is a make install target that will set up a launchd
service for the piv-agent for you and advise you on what to add to .profile
to make it available in all new shells.