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.