From 3425aca54d06c9accd36856c732b3c3b1789b99f Mon Sep 17 00:00:00 2001 From: Mladen Milankovic Date: Thu, 8 Oct 2026 19:12:45 +0200 Subject: [PATCH] Document rotating the PIV admin keys Add a section on replacing key version 1 of the PIV security domain before production: the keyless key-table read, the in-place PUT KEY, verification with the new keys, the key version 2 recovery set and its deletion. Link it from the getting-started warning, the quick start, the PIV and factory command pages and the troubleshooting KVN note. State that finalize locks the structure only and that administration with the security domain keys continues afterwards. Co-Authored-By: Claude Fable 5.1 --- docs/factory/factory-commands.rst | 10 +- docs/getting-started.rst | 24 +++-- docs/piv/piv-commands.rst | 9 +- docs/piv/piv-personalization.rst | 114 +++++++++++++++++++- docs/piv/quick-start-a-working-piv-card.rst | 12 +-- docs/troubleshooting.rst | 4 +- 6 files changed, 145 insertions(+), 28 deletions(-) diff --git a/docs/factory/factory-commands.rst b/docs/factory/factory-commands.rst index 6a55bfe..0edc65a 100644 --- a/docs/factory/factory-commands.rst +++ b/docs/factory/factory-commands.rst @@ -3,10 +3,12 @@ Factory commands Manufacturing-stage commands. Pre-personalization lays down the PIV applet's *structure* — data containers, PIN/PUK verifiers, key objects — from a profile, -over the SCP03 admin channel. Operators normally never need these; development -and evaluation cards use them with ``--default-keys``. Production cards take -their admin keys from environment variables; production key management is a -manufacturing procedure outside this documentation. +over the SCP03 admin channel. Operators normally never need these. As shipped, +every card opens the admin channel with the GlobalPlatform test keys +(``--default-keys``); replacing them before production is described in +:ref:`piv-admin-key-rotation`, after which the commands take the keys from the +``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` environment +variables. .. code-block:: text diff --git a/docs/getting-started.rst b/docs/getting-started.rst index f4c1dd9..d42c09a 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -99,11 +99,14 @@ Development keys .. warning:: - Development and evaluation cards use well-known **default** keys, and the - examples in this documentation use them too: - - * **SCP03 / GlobalPlatform ISD** — the published GlobalPlatform test key - ``40 41 42 … 4F`` (``--default-keys``); + Cards ship with well-known **default** keys so that the first + personalization works out of the box, and the examples in this + documentation use them too: + + * **PIV admin channel (SCP03)** — key version 1 of the card's PIV security + domain (``A00000015153504101``) holds the published GlobalPlatform test + key ``40 41 42 … 4F`` (``--default-keys``). The card manager (ISD) is a + separate domain with per-card keys that the CLI never uses; * **PIV management key (9B)** — the same published GlobalPlatform test value, doubled for an AES-256 key object (``--default-keys``); * **DESFire** application keys after ``app create`` — all-zero AES @@ -111,12 +114,13 @@ Development keys * **PIV PIN / PUK** in the examples — ``123456`` / ``12345678``. These are **not secrets** — they are public, published values. A production - deployment must rotate all of them, which is exactly what the - environment-variable key input is for: ``PIV_SCP03_ENC`` / - ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` for the admin channel, - ``PIV_MGMT_KEY`` for the PIV management key, + deployment must replace them. The environment variables only + *authenticate* with keys that are already on the card; putting new admin + keys on the card is a separate step, see :ref:`piv-admin-key-rotation`. + After it, the admin channel takes ``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / + ``PIV_SCP03_DEK``; ``PIV_MGMT_KEY`` carries the PIV management key, ``CRYPTNOX_PIV_PIN`` / ``CRYPTNOX_PIV_NEW_PIN`` / ``CRYPTNOX_PIV_NEW_PUK`` - for PINs and PUKs, and the DESFire ``--key-env NAME`` form. + the PINs and PUKs, and DESFire keys use the ``--key-env NAME`` form. Next ------ diff --git a/docs/piv/piv-commands.rst b/docs/piv/piv-commands.rst index d1ec5cb..db0d29d 100644 --- a/docs/piv/piv-commands.rst +++ b/docs/piv/piv-commands.rst @@ -3,10 +3,11 @@ PIV commands Operator and personalization commands for the PIV function (SP 800-73). Administration runs over an SCP03 secure channel and is **contact-only**; -admin keys come from ``--default-keys`` (development cards) or the -``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` environment -variables. PIN values come from masked prompts or ``CRYPTNOX_PIV_PIN`` / -``CRYPTNOX_PIV_NEW_PIN`` / ``CRYPTNOX_PIV_NEW_PUK`` — never the command line. +admin keys come from ``--default-keys`` (the test keys every card ships with) +or, once replaced (:ref:`piv-admin-key-rotation`), the ``PIV_SCP03_ENC`` / +``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` environment variables. PIN values come +from masked prompts or ``CRYPTNOX_PIV_PIN`` / ``CRYPTNOX_PIV_NEW_PIN`` / +``CRYPTNOX_PIV_NEW_PUK`` — never the command line. On-card generation of a large public key can also use the card's **PIV management key** (key reference ``9B``), whose value comes from diff --git a/docs/piv/piv-personalization.rst b/docs/piv/piv-personalization.rst index 3117f50..8a05b27 100644 --- a/docs/piv/piv-personalization.rst +++ b/docs/piv/piv-personalization.rst @@ -15,9 +15,9 @@ For the condensed version, see the :doc:`/piv/quick-start-a-working-piv-card`. application's PIN has its own retry counter and is managed separately. Operator commands live under ``piv``; factory pre-personalization lives under -``factory piv preperso``. The SCP03 admin channel uses the card's GlobalPlatform -keys — development/evaluation cards use the GlobalPlatform test keys -(``--default-keys``); provisioned cards take theirs from the +``factory piv preperso``. The SCP03 admin channel uses the keys of the card's +PIV security domain — as shipped, the GlobalPlatform test keys +(``--default-keys``); after :ref:`piv-admin-key-rotation`, the ``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` environment variables. ``PIV_MGMT_KEY`` carries the PIV management key (``9B``) for cards that need it (see Step 2). @@ -223,12 +223,120 @@ you to type the token ``FINALIZE-PIV``; non-interactively it requires the ``--i-understand-this-is-irreversible`` flag instead — either satisfies the gate. +``finalize`` locks the **structure** only: which containers, verifiers and +key objects exist, and their access rules. Administration with the security +domain keys continues afterwards — key generation and import, certificate +import, setting a new PIN or PUK, and rotating the admin keys themselves. +Rotate the admin keys *before* finalizing a production card +(:ref:`piv-admin-key-rotation`), so that finalize already runs with your +production keys. + .. code-block:: console $ cryptnox-id factory piv preperso finalize --default-keys # at the prompt: Type FINALIZE-PIV to continue # (scripted/non-interactive: add --i-understand-this-is-irreversible) +.. _piv-admin-key-rotation: + +Rotating the PIV admin keys +--------------------------- + +Every card ships with the published GlobalPlatform test key on **key version +1** of its PIV security domain (AID ``A00000015153504101``), so that the first +personalization works out of the box. Replace it before a card goes into +production. The key set is three AES-128 keys, key identifiers 1 = ENC, +2 = MAC, 3 = DEK. + +**Key version 2** of the same domain holds a per-card Cryptnox key set. It +gives the same PIV administration rights and is the recovery path if your own +keys are lost. Leave it in place (see *Deleting key version 2* below). The +card manager (ISD) is a separate domain and is not involved. + +The security domain keys *are* PIV administration: whoever holds them can +generate or replace keys, import certificates and set a new PIN or PUK +without knowing the current one, before and after ``finalize``. Protect them +like CA issuing keys, and use one key set per card. + +The CLI has no rotation command. The rotation is a single GlobalPlatform +PUT KEY that replaces key version 1 in place; the steps below run it with +GlobalPlatformPro (``gp``, v25.10.20 or later), card on a contact reader. +The CLI keeps finding your keys afterwards without any configuration beyond +the environment variables. + +1. Read the key table. No key is needed: + + .. code-block:: text + + SELECT 00 A4 04 00 09 A0 00 00 01 51 53 50 41 01 + GET DATA 80 CA 00 E0 00 + + Expect key versions 1 and 2, three AES-128 keys each (key type ``88``, + length 16). The replacement key set must be AES-128 as well; a DES key set + is refused, and some tools send DES keys unless told otherwise. + +2. Replace key version 1 in place, keeping version number 1. Generate three + independent random 16-byte keys first (in an HSM or key vault): + + .. code-block:: console + + $ gp -c A00000015153504101 --key 404142434445464748494A4B4C4D4E4F --key-ver 1 \ + --lock-enc --lock-mac --lock-dek --new-keyver 1 + + The card answers with the key version and the key check values of the + three new keys. + + .. warning:: + + Never pass ``--new-keyver 2``. The card accepts it and silently + overwrites the Cryptnox key set in version 2, while version 1 stays on + the test key; the key table looks the same afterwards. Omitting + ``--new-keyver`` is also correct — ``gp`` then replaces version 1. + +3. Verify in a **new** session, with the new keys. The key table must still + list versions 1 and 2: + + .. code-block:: console + + $ gp -c A00000015153504101 --key-enc --key-mac --key-dek --key-ver 1 -i + +4. Switch the CLI to the new keys and stop passing ``--default-keys``: + + .. code-block:: console + + $ export PIV_SCP03_ENC= PIV_SCP03_MAC= PIV_SCP03_DEK= + $ cryptnox-id piv admin authenticate + +Do not try the old keys to confirm the rotation; the key table and the +successful authentication are the proof. The card counts every +authentication that does not end in a successful EXTERNAL AUTHENTICATE, +including attempts a host abandons after a card-cryptogram mismatch. After +five consecutive ones, every INITIALIZE UPDATE on the card, in any security +domain, is delayed by about 24 seconds until the next successful +authentication. There is no lockout, only the delay. + +A PUT KEY with a malformed key block, a wrong key check value or keys wrapped +with the wrong DEK is refused (``6982``) and the key table is unchanged. If a +rotation is interrupted, read the key table, find out which MAC key the card +holds (INITIALIZE UPDATE with key version 1, then check the card cryptogram on +the host with the new MAC key and with the old one), and re-run step 2 +authenticated with that key set. + +Deleting key version 2 +~~~~~~~~~~~~~~~~~~~~~~ + +If your policy requires sole custody, delete the Cryptnox key set after +rotating version 1, authenticated with your new keys: + +.. code-block:: console + + $ gp -c A00000015153504101 --key-enc --key-mac --key-dek --key-ver 1 --delete-key 2 + +This cannot be undone. Afterwards Cryptnox cannot recover PIV administration +on that card if your keys are lost; the only remaining path is a full reset +of the PIV applet, which erases its contents (a Cryptnox operation — contact +Cryptnox support). + Next steps ------------ diff --git a/docs/piv/quick-start-a-working-piv-card.rst b/docs/piv/quick-start-a-working-piv-card.rst index c1dedbe..b1d05fa 100644 --- a/docs/piv/quick-start-a-working-piv-card.rst +++ b/docs/piv/quick-start-a-working-piv-card.rst @@ -12,12 +12,12 @@ Prerequisites interface on this card; contactless works for reading once personalized. * |cli| installed, and ``yubico-piv-tool`` if you want to run the verification step (any recent version). -* The card's SCP03 administration keys. **Development/evaluation cards** use the - GlobalPlatform test keys — pass ``--default-keys``. **Provisioned cards** take - their keys from the ``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` - environment variables (hex). Keys never go on the command line, and the - default keys are for development cards only — see the warning in - :doc:`/getting-started`. +* The card's SCP03 administration keys. As shipped, every card uses the + GlobalPlatform test keys — pass ``--default-keys``. Before a card goes into + production, replace them (:ref:`piv-admin-key-rotation`); from then on the + keys come from the ``PIV_SCP03_ENC`` / ``PIV_SCP03_MAC`` / ``PIV_SCP03_DEK`` + environment variables (hex). Keys never go on the command line — see the + warning in :doc:`/getting-started`. Check what you have: diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index ad913e3..f8d3dfe 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -115,7 +115,9 @@ throwaway dev root: ``genuine verify --anchors ``. **Genuineness is not the ISD / PIV-SSD check.** ``genuine verify`` proves the attestation applet's device key and certificate chain. It does **not** verify the per-card ISD or PIV-SSD keys (KDF/HSM-derived) -— this tool has no access to those secrets, and the output says so. +— this tool has no access to those secrets, and the output says so. The +per-card PIV-SSD key set is key version 2 of the PIV security domain; see +:ref:`piv-admin-key-rotation` for what it is and why to leave it in place. yubico-piv-tool interop -------------------------