.. This file is part of PEPSI. Copyright (C) 2026 GNUnet e.V. PEPSI is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. ========== pepsi-keys ========== *Manage the end-to-end key store.* Role ==== ``pepsi-keys`` is the operator's tool for Pepsi's end-to-end cryptography key store: ``pepsi.crypto_identity`` (the key pairs of the addresses we serve, private half included and wrapped at rest), ``pepsi.peer_key`` (remote correspondents' cached public keys and certificates) and ``pepsi.ca_trust`` (the CA certificates inbound S/MIME chains are validated against). It connects through the shared ``[pepsi-postgres]`` section and reads the ``[pepsi]`` ``CRYPTO_*`` options and the ``[pepsi-crypto]`` section. It is **not** a stage. Reference: :manpage:`pepsi-keys(1)`; the design is explained in :doc:`../key-management`. It is one of the standalone binaries, outside the unified ``pepsi`` executable, for two reasons: it is the only program that generates key material (folding it in would link key generation and certificate issuance into every stage worker), and a site may install it setuid so users can manage their own identities — a bit a shared multi-call binary cannot carry. Features ======== * **identity list / show** — what material exists for an address: protocol, purpose, status, whether it is the primary for its capability, whether it is published, whether the private half is present, purged or was never held (``client`` for the user's own *MUA key*, whose private half lives in their mail client), and — in ``show`` — the custody, whether it is the address's public face and its key-server state. * **identity generate** — create key material and store it. OpenPGP produces one transferable key (Ed25519 by default, RSA optional); S/MIME produces a *signing* and an *encryption* certificate, each self-signed for immediate use, or one certificate with both key usages under ``CRYPTO_SMIME_SHARED_KEY``. ``--csr`` additionally emits a PKCS#10 request over each key, so a real CA can issue a replacement without re-keying. * **identity import / export / csr** — store material generated elsewhere (an OpenPGP public key imported without ``--private`` is registered as the user's own MUA key), write material back out in its own binary form (``--pem`` for X.509), or build a certificate request over an identity's *existing* key. * **identity revoke / retire / forget-private** — the lifecycle. Revocation and the periodic ``retire`` sweep destroy private **signing** material and retain decryption material; ``revoke --upload`` publishes a revocation certificate to the key server first, since that must happen while the signing key still exists. ``forget-private`` is the explicit, confirmed (``-y`` to skip the prompt) way to destroy a decryption key. * **identity set-primary / publish / unpublish / delete** — choose the material used for new messages, opt an address in or out of publication (``publish`` can also upload to a key server), or remove a row outright. * **peer list / show / import / pin / unpin / refresh / prune / remove** — the cached peer keys. ``refresh`` re-validates keys that already exist and never discovers one for an address that has none; ``prune`` deletes expired keys and aged-out discovery requests (``--dry-run`` to look first). ``show`` applies the matching rule (an exact address always beats a hand-entered ``*@domain`` fallback); ``import`` stores a key with source ``manual``, the only source permitted to name a whole domain. * **ca list / add / enable / disable / remove** — the S/MIME trust anchors. A certificate without ``basicConstraints CA:TRUE`` is refused, since no chain could ever end at it. Being accepted here is not the same as being usable: an anchor's own ``nameConstraints``, ``pathLenConstraint`` and policy constraints bind every chain through it (RFC 5937), and a CA carrying a critical extension Pepsi cannot process is skipped during path building (RFC 5280 §4.2). * **wrap rotate** — re-wrap every stored private key under a new key-encryption key, incrementally and with ``--dry-run`` to look first. * **dns** — print the ``OPENPGPKEY`` (RFC 7929) and ``SMIMEA`` (RFC 8162) records that publish an address, under the hashed owner names those standards define. Only worth publishing in a DNSSEC-signed zone, which the output says. Privilege model =============== Reaching a private key takes two independently guarded things: the ``pepsi-crypto`` database role — the only one granted the ``crypto_identity.private_wrapped`` column, every other service role holding a column-level grant that omits it — and the key-encryption key, which lives only in ``secrets.d/pepsi-crypto.secret``. A stolen database yields ciphertext; the secret alone yields nothing. ``pepsi-keys`` assumes that role only for the duration of each database call: started as ``root`` it *becomes* the account (``root`` has no database role of its own) and returns afterwards. PostgreSQL peer authentication keys off the effective **uid**, not the gid, so a setgid bit would grant the group that guards the secret fragment but not the database identity. The binary ships **without** a setuid bit, unlike :doc:`pepsi-whitelist`: one program able to open every private key in a deployment is a much larger prize than a single whitelist table. A site that wants users to manage their own identities installs it setuid ``pepsi-crypto``; identities are owned by the passwd login their address resolves to, and an unprivileged caller of the setuid binary may see and change only their own. Without the bit every caller connects as themselves and the database grants alone decide, so making it setuid is a site decision rather than a code change. Peer keys and trust anchors are deployment-wide and operator-only, listing included. See also ======== :doc:`../key-management`, :doc:`pepsi-setup`, :doc:`pepsi-whitelist`, :doc:`pepsi-settings`, :doc:`../architecture`, :manpage:`pepsi-keys(1)`, :manpage:`pepsi.conf(5)`.