PIV Agent

June 24, 2026 ยท View on GitHub

Release coverage Go Report Card User Documentation


โš ๏ธ๐Ÿšง BREAKING CHANGES ๐Ÿšงโš ๏ธ

piv-agent is currently going through a heavy refactor: I am removing GnuPG support.

This is for several reasons, including:

  • It is terribly complex, and its agent protocol moreso. The agent protocol is also poorly documented.
  • Supporting it requires maintenance and several additional dependencies, including one that I had to fork.
  • It has well-documented technical problems.
  • I don't use it anymore.

To that end, I am planning the following release schedule for piv-agent:

  1. โœ… v1.x will be released. It will support age via a plugin, in addition to SSH and GPG.
  2. โœ… v2.x will be released shortly after v1.x, with GPG support totally removed. Active development will only occur on v2.x.
  3. โณ v1.x will be maintained for a short period (6 months). This is so that anyone else using piv-agent has a chance to migrate away from GPG, or find another solution.
  4. โณ When v1.x maintenance ends, v2.x can be considered stable(ish).

Please test v2.x if you can, but be aware there may be breakage. In particular, the age plugin support is experimental: until this warning is removed, the identity format is unstable.

Discussion of this plan and updates happens here.


About

  • piv-agent is an SSH and age plugin agent providing simple integration of PIV hardware (e.g. a Yubikey) with ssh, and age workflows such as git signing and passage password storage.
  • piv-agent originated as a reimplementation of yubikey-agent because I needed some extra features, and also to gain a better understanding of the PIV applet on security key hardware.
  • piv-agent makes heavy use of the Go standard library and supplementary crypto packages, as well as piv-go and pcsclite. Thanks for the great software!

DISCLAIMER

I make no assertion about the security or otherwise of this software and I am not a cryptographer. If you are, please take a look at the code and send PRs or issues. :green_heart:


Platform support

piv-agent has a hard dependency on Linux and systemd. At this time no other OS stack is supported.

Features

  • implements ssh-agent functionality
  • classic cryptographic keys are generated on the hardware security key, rather than on your laptop
    • secret keys never touch your hard drive
  • implements an age plugin: age-plugin-piv-agent
  • ML-KEM key seeds that are used in the age plugin are sealed by your TPM using systemd credentials
    • secret key seeds never touch your hard drive unencrypted
  • support for multiple hardware security keys
  • support for multiple slots in those keys
  • support for multiple touch policies
  • uses systemd socket activation
    • as a result, automatically drop the transaction on the security key and cached passphrases after some period of disuse
  • provides "fall-back" to traditional SSH key files

Design philosophy

This agent should require no interaction and in general do the right thing when security keys are plugged/unplugged, laptop is power cycled, etc.

It is highly opinionated:

  • Only supports 256-bit ECC keys (P-256) on hardware tokens for SSH
  • Only supports ed25519 SSH keys on disk (~/.ssh/id_ed25519)
  • Only supports the mlkem768p256tag identity/recipient type for age
  • Requires socket activation

It tries to strike a balance between security and usability:

  • Takes a persistent transaction on the hardware token, effectively caching the PIN.
  • Caches passphrases for on-disk keys (i.e. ~/.ssh/id_ed25519) in memory, so these only need to be provided once after the agent starts.
  • After a period of inactivity it exits, dropping both the transaction and the passphrase. Socket activation restarts it automatically as required.

Hardware support

Tested with:

If you have tested another device or firmware version with piv-agent successfully, please send a PR adding it to this list.

Protocol / Encryption Algorithm support

SupportedNot Supported
โœ…โŒ

ssh-agent

Security KeyKeyfile
ecdsa-sha2-nistp256โœ…โŒ
ssh-ed25519โŒโœ…

age

piv-agent, and its age plugin age-plugin-piv-agent only support the mlkem768p256tag identity/recipient type.

Install and Use

Please see the documentation.

Develop

Prerequisites

Install build dependencies:

# debian/ubuntu
sudo apt install libpcsclite-dev

Build and test

make

Build and test manually

This D-Bus variable is required for pinentry to use a graphical prompt:

go build ./cmd/piv-agent && systemd-socket-activate -l /tmp/piv-agent.sock -E DBUS_SESSION_BUS_ADDRESS ./piv-agent serve --debug

Then in another terminal:

export SSH_AUTH_SOCK=/tmp/piv-agent.sock
ssh ...

Build and test the documentation

cd docs && make serve