diff --git a/CHANGELOG.md b/CHANGELOG.md index be68d47..3edf7f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `factory piv preperso load-config` warns that structural operations are + permanent (existing elements cannot be changed or removed; only new ones can + be added) instead of calling them reversible before finalize. +- Error messages and docs no longer suggest reinstalling the applet as a + recovery step; a full reset of the PIV applet is a Cryptnox operation. The + `load-config` failure message points at adding missing key objects with + `--create-key-object`. - `PivPersonalized` no longer requires the optional Discovery Object; CHUID, CCC and a set PIN suffice. Discovery is still probed and listed. - `factory piv preperso status` reports `finalize_allowed` under the rule diff --git a/docs/factory/factory-commands.rst b/docs/factory/factory-commands.rst index 669579c..6a55bfe 100644 --- a/docs/factory/factory-commands.rst +++ b/docs/factory/factory-commands.rst @@ -42,7 +42,8 @@ coexists on 9A — see the .. warning:: ``finalize`` is **irreversible** — it locks the applet's structure for the - card's lifetime (recovery means reinstalling the applet). It is gated by a + card's lifetime. A full reset of the PIV applet is a Cryptnox operation: + contact Cryptnox support. It is gated by a typed confirmation token when run interactively, or the ``--i-understand-this-is-irreversible`` flag when run non-interactively — either satisfies the gate. Never run it on a card you are still developing diff --git a/docs/factory/pre-personalization-profiles.rst b/docs/factory/pre-personalization-profiles.rst index bece82a..2baf9ac 100644 --- a/docs/factory/pre-personalization-profiles.rst +++ b/docs/factory/pre-personalization-profiles.rst @@ -137,10 +137,11 @@ give a YAML list): ``AUTHENTICATE``, ``KEY_ESTABLISH``, ``SIGN``. worse than an absent one: the Windows inbox PIV minidriver rejects the whole card. This tool never writes that form. - The container also cannot be removed afterwards, because ``load-config`` - stops at the first element that already exists. Recovering a card requires - reinstalling the PIV applet, which erases its keys and certificates. - Discovery is optional in PIV, and a card without it works normally. + The container also cannot be removed afterwards: existing elements cannot + be changed or removed, only new ones added, and ``load-config`` stops at + the first element that already exists. A full reset of the PIV applet + erases its keys and certificates and is a Cryptnox operation (contact + Cryptnox support). ``from_yaml`` rejects unknown mode/role/mechanism names and validates the whole profile before anything is sent to a card: diff --git a/docs/piv/piv-commands.rst b/docs/piv/piv-commands.rst index f083388..d1ec5cb 100644 --- a/docs/piv/piv-commands.rst +++ b/docs/piv/piv-commands.rst @@ -56,8 +56,9 @@ prompts or the ``CRYPTNOX_PIV_PIN`` / ``CRYPTNOX_PIV_NEW_PIN`` / ``CRYPTNOX_PIV_ The PUK has its own retry counter, and every wrong PUK — entered through ``piv pin unblock``, ``piv puk change``, or any other tool — consumes one retry. Exhausting the PUK retries is **permanent**: a blocked PUK has no - recovery path short of reinstalling the applet, which erases all keys and - certificates. Check ``piv pin status`` (non-decrementing) before guessing. + recovery path. A full reset of the PIV applet, which erases all keys and + certificates, is a Cryptnox operation (contact Cryptnox support). Check + ``piv pin status`` (non-decrementing) before guessing. Admin channel ------------- diff --git a/docs/piv/piv-personalization.rst b/docs/piv/piv-personalization.rst index ab96adf..3117f50 100644 --- a/docs/piv/piv-personalization.rst +++ b/docs/piv/piv-personalization.rst @@ -217,7 +217,8 @@ Step 6 — finalize (irreversible, factory) :doc:`/factory/factory-commands`. ``finalize`` transitions the applet to its ``SECURED`` operational state. It -is irreversible — recovery is a full applet reinstall. Interactively it asks +is irreversible; a full reset of the PIV applet is a Cryptnox operation +(contact Cryptnox support). Interactively it asks you to type the token ``FINALIZE-PIV``; non-interactively it requires the ``--i-understand-this-is-irreversible`` flag instead — either satisfies the gate. diff --git a/docs/piv/ssh-public-key-authentication.rst b/docs/piv/ssh-public-key-authentication.rst index a77e8d4..ba50b9d 100644 --- a/docs/piv/ssh-public-key-authentication.rst +++ b/docs/piv/ssh-public-key-authentication.rst @@ -92,10 +92,12 @@ SIGN``); everything else is identical to ``cryptnox-default``. See This only applies at **pre-personalization** — it defines the key's role at the structural level, not something a later ``piv perso`` step can change. If your card already has a different profile applied (e.g. it already went -through :doc:`/piv/quick-start-a-working-piv-card` on 9C), it needs a factory-level applet -reinstall before this profile can be loaded — that's a manufacturing -operation outside this guide's (and this CLI's) scope; a blank/pre-perso -card needs no such step. +through :doc:`/piv/quick-start-a-working-piv-card` on 9C), this profile +cannot be loaded over it: existing key objects keep their role, and +``--create-key-object`` creates new ones with the default profile's roles +(AUTHENTICATE only on 9A). A full reset of the PIV applet is a Cryptnox +operation: contact Cryptnox support. A blank/pre-perso card needs no such +step. .. code-block:: console diff --git a/src/cryptnox_id_cli/cli/commands/factory.py b/src/cryptnox_id_cli/cli/commands/factory.py index 6971066..19c532d 100644 --- a/src/cryptnox_id_cli/cli/commands/factory.py +++ b/src/cryptnox_id_cli/cli/commands/factory.py @@ -247,8 +247,9 @@ def human(con: Console) -> None: # Real write. app.out.warn( - f"This writes {len(ops)} structural operations to the card's data model " - "(reversible only before finalize)." + f"This writes {len(ops)} structural operations. They are permanent: existing " + "elements cannot be changed or removed later (only new ones can be added), and " + "finalize locks the structure completely." ) if not app.yes and not click.confirm("Proceed with writing to the card?", default=False): raise click.Abort() @@ -287,8 +288,10 @@ def report(con: Console) -> None: raise RuntimeError("stopped without a failing operation") con.print(f"\n[red]Stopped at[/red] {failed[0]} (SW={failed[1]}).") con.print( - " Operations before it were applied. An existing object/verifier/key is " - "rejected; reinstall the PIV applet for a clean load, or edit the profile." + " Operations before it were applied. An existing element cannot be changed " + "or removed; add missing key objects with `piv perso generate-key " + "--create-key-object` or `piv perso import-key --create-key-object`. A full " + "reset of the PIV applet is a Cryptnox operation: contact Cryptnox support." ) app.out.result({"profile": profile.name, "applied": applied_ok, "operations": sent}, report) diff --git a/src/cryptnox_id_cli/cli/commands/piv.py b/src/cryptnox_id_cli/cli/commands/piv.py index ba91201..5112d9f 100644 --- a/src/cryptnox_id_cli/cli/commands/piv.py +++ b/src/cryptnox_id_cli/cli/commands/piv.py @@ -1247,7 +1247,8 @@ def _create_key_object( if resp.sw == 0x6985: raise CryptnoxError( "the applet is finalized (SECURED): key objects can no longer be created. " - "Import into an existing object, or reinstall the applet." + "Import into an existing object. A full reset of the PIV applet is a Cryptnox " + "operation: contact Cryptnox support." ) if not resp.ok: raise StatusWordError(resp.sw1, resp.sw2, context=f"CREATE KEY {slot}")