PIV Text Storage
This walkthrough shows how to securely store notes/secrets on the PIV application of a Yubikey/RP2350
Storage Requirements
The PIV application has a limited amount of storage space available for storing data. The maximum size of a single data object on a typical Yubikey is 1024 bytes, and the total storage capacity is 32 KB.
The PIV application on the RP2350-based keys like RS-Key have a maximum size of 2048 bytes for a single data object, and the total storage capacity is 64 KB. (or more)
Therefore, it is important to keep the notes/secrets small and concise.
Real-World Storage Example
I was able to manually prune a 12371-byte secrets file down to 3223 bytes by removing unnecessary whitespace and comments, and removing extraneous or redundant information. While my master secrets file is quite large and complex, this file targets only the most important secrets, in particular for services from which the others can later be retrieved.
That 3223-byte file is still too large to store in a single data object on a Yubikey, but I was able to compress it with gzip to 1465 bytes, which is small enough to store in a single data object on an RS-Key firmware.
The PIV Application
The PIV application on a Yubikey/RP2350 is designed to store cryptographic keys and certificates, but it can also be used to store arbitrary data in the form of "data objects". Each data object is identified by a unique Object Identifier (OID) and can be read or written using the PIV commands.
This works independently of the cryptographic functions of the PIV application, so you can use it to store notes/secrets without needing to pre-emptively configure your PIV authentication. (ie generate or manage keys/certificates)
PIV Data Objects
Complete List of PIV Data Objects
A number of these data objects are necessary for the PIV application to function properly as an authentication/cryptography device, and some are reserved for specific purposes. However, there are several data objects that can be used to store arbitrary data, such as notes or secrets. See the shorter list below for the most useful containers for this purpose.
| Container ID (Hex) | Documented Name | Default Read Access | Default Write Access | Documented Purpose & Usage |
|---|---|---|---|---|
0x5FC102 |
Cardholder Unique Identifier (CHUID) | Always (Never PIN) | Management Key | Fixed card identifier, expiry, and optional asymmetric signature; used by physical access control systems (PACS). |
0x5FC105 |
X.509 Certificate for PIV Authentication | Always (Never PIN) | Management Key | Stores the certificate corresponding to private key slot 9a; used for interactive workstation/system logins. |
0x5FC101 |
X.509 Certificate for Digital Signature | Always (Never PIN) | Management Key | Stores the certificate for slot 9c; used for signing documents, Git commits, and emails (S/MIME). |
0x5FC10B |
X.509 Certificate for Key Management | Always (Never PIN) | Management Key | Stores the certificate for slot 9d; used for asymmetric decryption (CMS, P/K encryption). |
0x5FC10A |
X.509 Certificate for Card Authentication | Always (Never PIN) | Management Key | Stores the certificate for slot 9e; used for physical door readers and automated services without PIN challenge. |
0x5FC109 |
Printed Information | PIN | Management Key | Human-readable badge text (name, employee ID); repurposed by Yubico to store the PIN-protected management key. |
0x5FC103 |
Cardholder Fingerprints | PIN | Management Key | Biometric fingerprint minutiae templates (CBEFF format); can be repurposed as arbitrary storage. |
0x5FC108 |
Cardholder Facial Image | PIN | Management Key | JPEG/JPEG 2000 facial photo for verification; large container often repurposed for arbitrary binary data. |
0x5FC121 |
Cardholder Iris Images | PIN | Management Key | Biometric iris pattern data (CBEFF format); large capacity container commonly used for general-purpose payload storage. |
0x5FC107 |
Card Capability Container (CCC) | Always (Never PIN) | Management Key | PIV data model discovery, supported card features, and registered data model URL. |
0x5FC106 |
Security Object | Always (Never PIN) | Management Key | Contains SHA-256 digests of all cardholder data containers, signed by the card issuer for tamper evidence. |
0x5FC10C |
Key History Object | Always (Never PIN) | Management Key | Tracks retired key management certificates across slots 82–95 for historical decrypts. |
0x5FFF01 |
YubiKey Attestation Certificate | Always (Never PIN) | Factory Locked / Device | Vendor-specific extension (slot f9); contains the factory batch certificate proving keys were generated on-chip. |
Short List: Top 3 Containers for Plaintext/Token Storage
These containers are rarely used for their intended purpose and are large enough to store arbitrary data. They are also PIN-protected, which is important for security.
| Container ID (Hex) | Documented Name | Typical Capacity | Default Read Access | Recommended Use Case |
|---|---|---|---|---|
0x5FC121 |
Cardholder Iris Images | ~1.5 KB – 3 KB | PIN Required | Primary Vault: Highest capacity container; no collision with management keys or PKI certificates. |
0x5FC108 |
Cardholder Facial Image | ~1 KB – 3 KB | PIN Required | Secondary Vault: High-capacity alternate container for a second split file or backup secrets. |
0x5FC103 |
Cardholder Fingerprints | ~1 KB – 2 KB | PIN Required | Tertiary Vault: Multi-template storage container; ideal third PIN-gated slot for emergency codes. |
My Experience
I personally use the 0x5FC121 container for my primary secrets vault. I may try storing a PGP private key in the 0x5FC108 container, but I have not yet done so. I intend to reserver the 0x5FC103 container for emergency codes and other small secrets that I may need to access quickly.
Write the Notes/Secrets to the PIV Application
Warning
None of the methods in this document are foolproof. It is important to remember to clean up any temporary files that may contain sensitive data after you are done using them. This includes any plaintext files, compressed files, or other temporary files that may have been created during the writing or reading process. If you opt to pass text directly in the command or from an input prompt, be aware that the secret may still be visible in the command history or in the process list while the command is running. Use caution when using these methods, especially on shared systems.
Pre-requisites:
- A Yubikey or RP2350 device with PIV application enabled.
- The
ykmancommand-line tool installed on your system. - A PIN configured for your PIV application (default is
123456).
To begin, create a /tmp/secret.txt file with your secret data.
# Create a secret file
nano /tmp/secret.txt
Check the size of your secret file to see if it fits as-is into one of the PIV containers.
# Check the size of the secret file
wc -c /tmp/secret.txt
# Output: <num-bytes> /tmp/secret.txt
Write File Directly
If the file is small enough to fit into one of the PIV containers, you can write it directly.
If not, you may need to compress it or split it into multiple parts.
Write the secret file directly to a PIV container (if it fits).
# Write the secret file to the Cardholder Iris Images container (0x5FC121)
ykman piv objects import 0x5fc121 /tmp/secret.txt
# Output: Enter PIN:
# Object imported.
Compress File Before Writing
Compress the secret file and write it to a PIV container (if it doesn't fit).
# Compress the secret file using gzip
gzip -9 -c /tmp/secret.txt > /tmp/secret.txt.gz
# Compress the file using xz
xz -9 -e -c /tmp/secret.txt > /tmp/secret.txt.xz
# Check the size of the compressed file(s)
wc -c /tmp/secret.txt.gz
wc -c /tmp/secret.txt.xz
# Output: <num-bytes> /tmp/secret.txt.gz
# <num-bytes> /tmp/secret.txt.xz
Hopefully, one of the compressed files is small enough to fit into a PIV container.
If not, you will need to further trim the file or split it into multiple parts.
Once you have a file that fits, you can write it to the PIV container as shown in Option A:
Gzip Example:
# Write the compressed secret file to the Cardholder Iris Images container (0x5FC121)
ykman piv objects import 0x5fc121 /tmp/secret.txt.gz
# Output: Enter PIN:
# Object imported.
Xz Example:
# Write the compressed secret file to the Cardholder Iris Images container (0x5FC121)
ykman piv objects import 0x5fc121 /tmp/secret.txt.xz
# Output: Enter PIN:
# Object imported.
Read the Notes/Secrets from the PIV Application
Option A: Uncompressed File
# Read the secret file from the Cardholder Iris Images container (0x5FC121)
ykman piv objects export 0x5fc121 /tmp/secret.txt
# Output: Enter PIN:
# Exported object 6275361 to /tmp/secret.txt.
Option B: Compressed with gzip
# Read the compressed secret file from the Cardholder Iris Images container (0x5FC121)
ykman piv objects export 0x5fc121 /tmp/secret.txt.gz
# Decompress the file
gunzip /tmp/secret.txt.gz
rm -f /tmp/secret.txt.gz
# Output: Enter PIN:
# Exported object 6275361 to /tmp/secret.txt.gz.
# NOTE: Output file will be /tmp/secret.txt after decompression.
Option C: Compressed with xz
# Read the compressed secret file from the Cardholder Iris Images container (0x5FC121)
ykman piv objects export 0x5fc121 /tmp/secret.txt.xz
# Decompress the file
xz -d /tmp/secret.txt.xz
rm -f /tmp/secret.txt.xz
# Output: Enter PIN:
# Exported object 6275361 to /tmp/secret.txt.xz.
# NOTE: File will be /tmp/secret.txt
Tips and Tricks
Using typed text or an input prompt instead of a file
Here are some examples of how to write a secret directly from typed text or an input prompt without creating a plaintext file on disk.
Include the secret text directly in the command:
# Write a secret directly from typed text using a here-document
ykman piv objects import 0x5fc121 <(echo -n "This is my secret text that I want to store in the PIV application.")
Use an input prompt to read the secret and write it directly:
# Write a secret directly from an input prompt using read
read -p "Enter your secret: " secret && ykman piv objects import 0x5fc121 <(echo -n "$secret")
Note
In both examples above, the secret text is never written to disk in plaintext form, which is more secure. However, be aware that the secret may still be visible in the command history or in the process list while the command is running. Use caution when using these methods, especially on shared systems.
On Compression
If you choose to use a compressed secret file, it is important to remember that you will need to decompress it after reading it from the PIV application.
The examples earlier in the document show how to do this using gunzip for gzip-compressed files and xz -d for xz-compressed files.
If you forget to decompress the file, you will not be able to read the original secret text until you do.
However, leaving a compressed file on disk is still a security risk as it is trivially easy to decompress it and recover the original secret text.
Therefore, it is important to always securely delete any temporary files after use.
IMPORTANT Security Considerations
After verifying the secret file was successfully written to the PIV application, it is important to securely delete the original plaintext file from your system. This can be done using a secure deletion tool such as shred or srm, or simply using rm -f is probably sufficient for most modern SSD drives.
Standard Cleanup
It's better to include everything even if we didn't use interimediary compressed files, so that the cleanup is complete and secure so the commands below will remove any temporary files that may have been created during the process.
Note
If you want to keep the generated file or need to reference it, do so and/or copy it to a secure location before running the cleanup commands below.
# Remove the original (or new) plaintext secret file
rm -f /tmp/secret.txt
# Remove the compressed secret file(s) (if used)
rm -f /tmp/secret.txt.gz
rm -f /tmp/secret.txt.xz
# Check for any other temporary files with similar names and remove them as needed
find /tmp -type f -name 'secret.txt*' -exec rm -f {} \;
find /tmp -type f -name 'secret.*' -exec rm -f {} \;
find /tmp -type f -name 'secrets*' -exec rm -f {} \;
# Sanity check to ensure no temporary secret files remain
ls -l /tmp | grep 'secret'
Paranoid Cleanup
This uses the shred command to overwrite the files before deletion, which is more secure than simply deleting them. This is especially important if you are using a traditional spinning hard drive, as it makes it much harder to recover the deleted data.
# Overwrite and remove the original (or new) plaintext secret file
shred -u /tmp/secret.txt
# Overwrite and remove the compressed secret file(s) (if used)
shred -u /tmp/secret.txt.gz
shred -u /tmp/secret.txt.xz
# Check for any other temporary files with similar names and shred them as needed
find /tmp -type f -name 'secret.txt*' -exec shred -u {}
find /tmp -type f -name 'secret.*' -exec shred -u {}
find /tmp -type f -name 'secrets*' -exec shred -u {}
# Sanity check to ensure no temporary secret files remain
ls -l /tmp | grep 'secret'
Conclusion
Now you should have a fairly good idea how to use a Yubikey or RP2350 device to securely store notes/secrets in the PIV application. Remember to always keep your secrets small and concise, and to securely delete any temporary files after use.