Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions docs/exit-codes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,12 @@ driven by one tool: |cli|.
Guide <genuine/genuineness-guide>
Commands <genuine/genuineness-commands>

.. toctree::
:maxdepth: 2
:caption: Remote service

Guide <remote/remote-guide>
Commands <remote/remote-commands>

.. API Reference — RESERVED: autodoc of the Python package, if/when it is
publicly pip-installable (see the internal documentation plan, decision D4).
14 changes: 14 additions & 0 deletions docs/json-output.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,17 @@ re-present the card, and check the reader passes native DESFire APDUs.
A section may also report ``{"state": "Unknown", "error": "<message>"}`` 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.
119 changes: 119 additions & 0 deletions docs/remote/remote-commands.rst
Original file line number Diff line number Diff line change
@@ -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.
136 changes: 136 additions & 0 deletions docs/remote/remote-guide.rst
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 28 additions & 0 deletions docs/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,34 @@ throwaway dev root: ``genuine verify --anchors <dev-ca-dir>``.
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
-------------------------

Expand Down
Loading