diff --git a/README.md b/README.md index 5032cfe..b151d8a 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,18 @@ cryptnox-id doctor # PC/SC service, reader, per-function reachability cryptnox-id shell # run subcommands without re-typing the prefix ``` +### 6. Use the Cryptnox remote PIV service + +```bash +cryptnox-id remote inspect # what the service sees: applet, security domain, key versions +cryptnox-id remote attest --slot 9C # a key generated through the service, with its attestation +cryptnox-id remote reset # wipe and reinstall the PIV function (irreversible, gated) +``` + +The card stays in your reader; the tool relays the service's commands under a +policy that keeps the other card functions out of reach, and shows what the +service asserted apart from what it verified locally. + --- ## Documentation diff --git a/docs/exit-codes.rst b/docs/exit-codes.rst index 8a67096..662b6a9 100644 --- a/docs/exit-codes.rst +++ b/docs/exit-codes.rst @@ -51,3 +51,23 @@ machine token (see :doc:`/json-output`). * - 11 - ``ev2_error`` - DESFire secure-messaging (session/MAC) failure. + * - 12 + - ``remote_error`` + - A remote PIV service operation failed for a reason without a more + specific class. + * - 13 + - ``remote_connect`` + - The remote PIV service could not be reached, or the TLS handshake + failed. + * - 14 + - ``remote_protocol`` + - The remote PIV service sent something the client cannot interpret + safely. + * - 15 + - ``remote_policy`` + - The remote PIV service asked for a card command the relay policy + refuses to send. The message names the command header. + * - 16 + - ``remote_failed`` + - The exchange completed, but the remote PIV service reported the + operation as failed. diff --git a/docs/index.rst b/docs/index.rst index 8c3342e..8c1085e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -62,5 +62,12 @@ driven by one tool: |cli|. Guide Commands +.. toctree:: + :maxdepth: 2 + :caption: Remote service + + Guide + Commands + .. API Reference — RESERVED: autodoc of the Python package, if/when it is publicly pip-installable (see the internal documentation plan, decision D4). diff --git a/docs/json-output.rst b/docs/json-output.rst index 7415a0b..832fc1d 100644 --- a/docs/json-output.rst +++ b/docs/json-output.rst @@ -159,3 +159,17 @@ re-present the card, and check the reader passes native DESFire APDUs. A section may also report ``{"state": "Unknown", "error": ""}`` when a probe fails. Nothing in any report contains a PIN, key, or private material — ``safe_to_share`` is always ``true``. + +Remote service operations +------------------------- + +``remote`` commands return one object with a fixed top level: ``op``, +``endpoint``, ``card`` (reader, ATR, CPLC UID and serial, all read locally), +``result`` (the service's own result object, passed through unchanged), +``verified_locally`` (each cross-check as ``true``, ``false`` or ``null`` when +the service reported nothing to compare) and ``relay`` (commands relayed, +seconds, proof-of-work bits, the service's log lines). ``reset`` and +``dev-reset`` add ``before`` and ``after`` inventories; ``attest`` adds +``verification`` with every binding checked on the returned certificate. +Fields under ``result`` are the service's assertions; only ``verified_locally``, +``before``, ``after`` and ``verification`` are the tool's own findings. diff --git a/docs/remote/remote-commands.rst b/docs/remote/remote-commands.rst new file mode 100644 index 0000000..160b919 --- /dev/null +++ b/docs/remote/remote-commands.rst @@ -0,0 +1,119 @@ +Remote service commands +======================= + +The ``remote`` commands run operations through the Cryptnox remote PIV +service. The card stays in the local reader; |cli| relays the commands the +service sends to the card and reports the outcome. What the service asserts +and what |cli| verified locally are shown apart. The service, the relay policy +and what each operation does to the card are described in +:doc:`/remote/remote-guide`. + +Every command opens the local reader first, then connects to the service over +TLS and answers a small proof-of-work before any command is relayed. Only the +PIV function and its security domains are reachable through the relay; the +other card functions are refused by the relay policy. + +Group options +------------- + +.. code-block:: text + + remote [--full-transcript] COMMAND + + Options: + --full-transcript write relayed commands to --apdu-log and --verbose + without the extra masking the relay applies by + default; the standard secret masking still applies + +Identification and inspection +----------------------------- + +.. code-block:: text + + remote authenticate identify the card to the service: ATR and CPLC UID, + nothing else; key-free on both sides + + remote inspect probe the PIV function through the service: whether + the PIV applet and its security domain are present + and which key versions they carry; key-free but not + free: each key version enumerated costs one + INITIALIZE UPDATE the card counts as a failed + authentication (see the guide) + +Both commands report the card as read locally (reader, ATR, CPLC UID), the +service's result as the service asserted it, and a cross-check of the fields +the two have in common. + +Key attestation +--------------- + +.. code-block:: text + + remote attest [--slot 9C] [--algorithm ECCP256] [--ca-obj HEX] [--out FILE] + + generate a key in the slot through the service and + receive its key-attestation certificate; the service + also stores the certificate in the card's attestation + container. The slot's previous key and the container's + previous certificate are replaced: when either holds + something, the command asks, or needs --yes. + + Options: + --slot HEX 9A, 9C, 9D, 9E or a retired slot 82 to 95 (default 9C) + --algorithm NAME ECCP256 (default), ECCP384, RSA2048, RSA3072, RSA4096 + --ca-obj HEX signing sub-CA in the service; omit for the + key-attestation CA + --out FILE write the returned certificate (PEM) + +The returned certificate is verified locally before success is reported: the +chain must end at a pinned trust anchor, the subject serial number must be the +card's CPLC UID, the ``cryptnoxPivSlot`` extension must name the requested +slot, the trust model must be the server-driven value, and the certified +public key must equal the certificate read back from the card's attestation +container. That the key was generated on the card is the service's claim; the +tool cannot witness it and says so. + +Reset +----- + +.. code-block:: text + + remote reset [--default-keys] [--i-understand-this-is-irreversible] + + IRREVERSIBLY wipe and reinstall the PIV function: the + service deletes the PIV applet, its package and its + security domain, reinstalls the applet, recreates the + security domain and loads the card's keys. Every PIV + key and certificate is destroyed. Interactively the + phrase RESET-PIV must be typed; non-interactively the + flag is required. --yes is not accepted. + + remote dev-reset [--default-keys] [--i-understand-this-is-irreversible] + + Development cards only. As reset, but the card manager + and the PIV security domain are left on the public + default key, so the PIV function can be worked on + locally. Needs the development access credential in + $CRYPTNOX_REMOTE_DEV_TOKEN (never an option). The + phrase is DEV-RESET-PIV. + + Options: + --default-keys development cards only: the service authenticates + with the publicly known GlobalPlatform default key + instead of deriving this card's own. A production + card refuses it, and the card counts one failed + authentication. + +Before asking for consent, both commands read and show what the card holds: +the PIV personalization state and data objects, the PIV security domain and +its key versions, and the state of the FIDO2 and genuineness functions, which +stay outside the operation. The same inventory is read again afterwards and +shown next to the service's result. + +Endpoint +-------- + +The service endpoint is fixed to ``wss://piv.cryptnox.com``. For development +against a local stand-in, ``CRYPTNOX_REMOTE_URL`` redirects the identification +and inspection commands only; it must be a ``wss://`` URL, or ``ws://`` to +loopback. ``reset`` and ``dev-reset`` refuse to run while it is set. diff --git a/docs/remote/remote-guide.rst b/docs/remote/remote-guide.rst new file mode 100644 index 0000000..152a08d --- /dev/null +++ b/docs/remote/remote-guide.rst @@ -0,0 +1,136 @@ +Remote service guide +==================== + +Some PIV lifecycle operations need keys that never leave Cryptnox: the key +that makes a card genuine, and the certificate authority that attests keys +generated on it. The remote PIV service holds those keys in a hardware +security module and performs the operation over a relayed command channel, +with the card in the holder's own reader. + +How a relayed operation works +----------------------------- + +1. |cli| opens the local reader and reads the card's ATR and CPLC serial. +2. It connects to the service over TLS and names the operation. +3. The service issues a small proof-of-work; |cli| answers it. This is an + anti-abuse throttle, not a credential. +4. The service sends card commands one at a time. |cli| checks each one + against the relay policy, transmits it, and returns the card's answer + verbatim, status word included. |cli| never sends a command of its own and + never reassembles a chained response; the service sees the card exactly as + a local reader would. +5. The service returns a result. |cli| shows it as the service's assertion and + adds what it verified locally. + +The relay policy +---------------- + +The card carries more than the PIV function. A relay that executed whatever +it was sent would expose the wallet, the FIDO2 passkeys and the genuineness +key to the service. The relay policy is an allow-list, per operation, over +what a command header reveals: the selected applet, the class byte, the +instruction, and for a SELECT the target application. Anything not listed is +refused, the operation stops, and the refusal names the command header. + +The policy also caps the number of commands and the time an operation may +take, limits secure-channel authentication attempts per security domain, and +never relays a card-holder PIN or PUK command. Inside a secure channel only +the class and instruction bytes stay in the clear, so the policy fences which +security domain the channel was opened to and which instructions pass through +it; it cannot see what an encrypted command carries. + +Operations +---------- + +``authenticate`` + Reads the ATR and CPLC serial. Key-free. Allows only card-manager reads. + +``inspect`` + Probes whether the PIV applet and its security domain are present and which + key versions they carry. Key-free: the relay refuses every EXTERNAL + AUTHENTICATE, so the service never completes an authentication. Not free: + the service enumerates key versions with one INITIALIZE UPDATE each, two on + a card as shipped, and the card counts each of them as a failed + authentication (see below). Two or three runs in a row reach the delay. The + relay admits at most four INITIALIZE UPDATEs per run. + +``attest`` + Generates a key in one slot and returns a key-attestation certificate for + it, signed by the Cryptnox key-attestation CA and stored in the card's + attestation container as well. The relay policy admits the applet's admin + channel for the key-object setup, the management-key handshake (never a + slot key), key generation for the requested slot only, and the certificate + write. The tool then verifies the certificate against the pinned anchors and + binds it to the card, the slot and the certificate stored on the card. The + certificate write itself is not bound to the slot: PUT DATA names its object + inside the data, so the service could write any PIV data object during the + operation, and only the certificate read back is checked. The generation + itself happening on the card is the service's claim; nothing the relay sees + authenticates it. + +``reset`` + Wipes and reinstalls the PIV function. The service authenticates to the + card manager, deletes the PIV applet, its package and its security domain, + loads the applet again, recreates the security domain and loads the card's + keys, then verifies the result. Every PIV key and certificate is destroyed; + the keys were never exportable, so nothing can bring them back. Interactive + use requires typing ``RESET-PIV``; non-interactive use requires + ``--i-understand-this-is-irreversible``. ``--yes`` is not accepted. + + The relay policy for this operation admits card content management through + the card manager only, lets a DELETE name nothing but the PIV instance, its + package and its security domain, refuses a DELETE it cannot read unless the + channel encrypts commands, caps authentication attempts per security + domain, and stops at the first failed authentication so no further attempt + adds to the card's count. Inside an encrypted channel the policy + sees instructions, not targets; that limit is real and is why the FIDO2 and + genuineness state is read before and after. + +``dev-reset`` + As ``reset``, for development cards, leaving the card manager and the PIV + security domain on the publicly documented GlobalPlatform key so the PIV + function can be pre-personalized and personalized locally. Anyone with a + reader can then install or delete applets on the card, and the card is not + genuine until it is reset through the service again. The development access + credential comes from ``CRYPTNOX_REMOTE_DEV_TOKEN`` only. + +What a failed authentication costs +---------------------------------- + +The card counts every INITIALIZE UPDATE that is not followed by a successful +EXTERNAL AUTHENTICATE, whichever security domain it was sent to. After five in +a row, every INITIALIZE UPDATE on the card, in any security domain, is delayed +by about 24 seconds until the next successful authentication clears the count. +There is no lockout, only the delay. This is why the relay caps authentication +attempts per operation. ``piv admin authenticate`` with the card's own keys is +one way to clear the count. + +The ``--default-keys`` flag +--------------------------- + +By default the service authenticates to the card manager with the card's own +key, derived per card from its identity. That is the state a card leaves the +factory in. A development card, or one deliberately put on the published +GlobalPlatform default key, needs ``--default-keys`` instead; without it the +service presents a derived key the card does not hold, the authentication +fails, and the card counts one failed authentication (see above). The flag is +the caller's statement about the card, so the default is the safe one: +forgetting it on a development card costs a failed authentication and nothing +else, while the reverse mistake on a production card would present a published +key to a card that should only ever answer to its own. + +What is sent to the service +--------------------------- + +The card's ATR and CPLC serial identify the card to the service. They are the +same values any reader can obtain from the card without a key. Nothing else +about the card holder is sent. + +Transcripts +----------- + +With ``--apdu-log`` or ``--verbose``, relayed commands are written with their +data masked unless the instruction is a known-harmless read, because the +service chose the command and |cli| cannot vouch for what it carries. +``remote --full-transcript`` lifts that extra masking for troubleshooting; the +standard masking of secret-bearing commands still applies. diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index ad913e3..a8b7d5f 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -117,6 +117,34 @@ throwaway dev root: ``genuine verify --anchors ``. 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. +Remote service +-------------- + +``remote`` commands exit with a code that names the failure class (see +:doc:`/exit-codes`): + +- **13** ``remote_connect``: the service could not be reached or the TLS + handshake failed. Check the network; the endpoint is fixed to + ``wss://piv.cryptnox.com``. With ``CRYPTNOX_REMOTE_URL`` set, only + ``authenticate`` and ``inspect`` run, and only to ``wss://`` or loopback. +- **14** ``remote_protocol``: the service sent a frame the tool cannot + interpret. Update the tool; if the newest version fails the same way, + report it with the message. +- **15** ``remote_policy``: the service asked for a card command the relay + policy refuses; the message names the command header. Nothing was sent to + the card for that command and the operation stopped. This is the tool + protecting the card; report the header so the policy can be reviewed. +- **16** ``remote_failed``: the exchange completed and the service reported + the operation as failed; the error object carries the service's result. + A per-card rate limit exists; wait before retrying. + +``remote reset`` and ``remote dev-reset`` refuse to run non-interactively +without ``--i-understand-this-is-irreversible``, and ``--yes`` is not +accepted for them. ``remote attest`` refuses non-interactively without +``--yes`` when the slot or the attestation container already holds a +certificate. + + yubico-piv-tool interop -------------------------