.. This file is part of PEPSI. Copyright (C) 2026 Pepsi contributors 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. .. _rfc-index: ========= RFC Index ========= Pepsi aims to be a standards-compliant e-mail forwarder **and** a standards-compliant end-to-end crypto gateway. This chapter is the single place that maps every RFC Pepsi implements (or deliberately does not yet) to **where** in the system it is realised, so a reviewer can jump from a standard to its code and configuration. The :ref:`summary table ` lists every applicable RFC at a glance; the :ref:`per-RFC notes ` below give the detail. RFCs that are planned but not yet implemented are collected under :ref:`rfc-not-yet`. Not everything indexed here is an RFC, and that is deliberate. The OpenPGP Web Key Directory is an Internet-Draft; Autocrypt and the VKS key-server protocol are de-facto standards; NTLM is a Microsoft Open Specification; XOAUTH2 has no specification body at all; SRS and Maildir are published schemes that were never put through the IETF; FIPS 186-4 is a NIST publication. Each is what the deployed world actually speaks, so leaving them out would make this chapter describe a system nobody runs. They are listed after the RFCs, italicised, with their status named. Implementing a standard is not the same as interoperating with the clients that claim to implement it. :doc:`interoperability` records which of these have been **measured** against real software, which are one-off observations, and which have never been run at all. A note on what "implemented in" means: it names the module or program where the standard is *realised*, not every caller. Where a standard is only partly implemented — ``BINARYMIME`` of RFC 3030, Autocrypt gossip without its inbound-gossip *reading* half — the cell says so, and the boundary is stated rather than left for the reader to discover. Standards Pepsi deliberately does not implement are under :ref:`rfc-not-yet` with the reason. .. _rfc-summary: Summary table ============= .. list-table:: :header-rows: 1 :widths: 12 40 48 * - RFC - Title (subject) - Implemented in * - RFC 1035 - Domain names — implementation and specification (DNS) - All DNS lookups; domain/label syntax in ``pepsi-common::domain`` * - RFC 1870 - SMTP service extension for message size declaration (``SIZE``) - Advertised and enforced by :doc:`programs/pepsi-ingress`; the outbound client honours the next hop's announced limit, ``pepsi-common::smtp::client`` * - RFC 1918 - Address allocation for private internets - Together with RFC 6598 and RFC 2544, the address ranges an outbound fetch refuses so a key-discovery URL cannot reach inside the deployment (``pepsi-keydisc``'s SSRF guard) * - RFC 1951 - ``DEFLATE`` compressed data format - The bound on decompressing fetched key material — a compression bomb is refused rather than expanded (``pepsi-keydisc``) * - RFC 2033 - Local Mail Transfer Protocol (LMTP) - Local delivery to an MDA (Dovecot), :doc:`programs/pepsi-stage-relay-to-lmtp` * - RFC 2034 - SMTP service extension for returning enhanced error codes - ``ENHANCEDSTATUSCODES``, advertised by :doc:`programs/pepsi-ingress`; every reply carries an RFC 3463 code * - RFC 2045 - MIME Part One: format of message bodies (transfer-encodings) - 8-bit→7-bit body downgrade, ``pepsi-common::mime`` * - RFC 2046 - MIME Part Two: media types (``multipart`` and boundary syntax) - MIME tree parsing and boundary validation, ``pepsi-common::mime`` and ``pepsi-crypto::mime`` * - RFC 2047 - MIME Part Three: message header extensions (encoded-words) - UTF-8 header downgrade, ``pepsi-common::mime`` * - RFC 2104 - HMAC: keyed-hashing for message authentication - The SRS envelope-sender MAC (``pepsi-common::srs``), the ``Pepsi-Origin`` proof-of-origin header, and the key-encryption-key derivation in ``pepsi-common::keystore::wrap`` (an HKDF-Extract-shaped construction with a fixed label) * - RFC 2195 - IMAP/POP ``AUTHorize`` extension for simple challenge/response (``CRAM-MD5``) - Outbound SMTP ``AUTH`` to a smarthost, ``pepsi-common::smtp::sasl`` * - RFC 2231 - MIME parameter value and encoded word extensions - Continued and character-set-tagged header parameters, ``pepsi-common::mime`` * - RFC 2392 - Content-ID and Message-ID uniform resource locators - The ``cid:`` reference from the payment-request auto-reply's HTML to its inline QR-code image, ``pepsi-stage-anti-spam`` * - RFC 2831 - Using digest authentication as a SASL mechanism (``DIGEST-MD5``) - Outbound SMTP ``AUTH`` (deprecated; see RFC 6331), ``pepsi-common::smtp::sasl`` * - RFC 2920 - SMTP service extension for command pipelining - ``PIPELINING``, advertised by :doc:`programs/pepsi-ingress`; the outbound client writes the envelope and ``DATA`` in one go * - RFC 2986 - PKCS #10: certification request syntax - Certificate-signing requests for externally-issued S/MIME certificates, ``pepsi-crypto::keygen`` * - RFC 3030 - SMTP service extensions for large and binary messages (``CHUNKING``/``BDAT``) - :doc:`programs/pepsi-ingress` (``BINARYMIME`` not advertised) * - RFC 3156 - MIME security with OpenPGP (PGP/MIME) - ``multipart/encrypted`` and ``multipart/signed`` containers built and parsed by ``pepsi-crypto::mime``; ``micalg`` from ``pepsi-crypto::pgp`` * - RFC 3207 - SMTP service extension for secure SMTP over TLS (``STARTTLS``) - :doc:`programs/pepsi-ingress` listeners; outbound SMTP client * - RFC 3339 - Date and time on the Internet: timestamps - Report generation timestamps, ``pepsi-common::tlsrpt`` * - RFC 3370 - Cryptographic Message Syntax (CMS) algorithms - S/MIME digest and signature algorithm identifiers, ``pepsi-crypto::smime`` * - RFC 3394 - Advanced Encryption Standard (AES) key wrap algorithm - Wrapping the content-encryption key in an ECDH ``KeyAgreeRecipientInfo``, ``pepsi-crypto::smime`` * - RFC 3461 - SMTP service extension for Delivery Status Notifications (``DSN``) - ``pepsi-common::dsn``; ingress, relay stages, bounce stage * - RFC 3463 - Enhanced mail system status codes - DSN status codes, ``pepsi-stage-bounce`` * - RFC 3464 - An extensible message format for Delivery Status Notifications - ``multipart/report`` generation, ``pepsi-stage-bounce`` * - RFC 3492 - Punycode - IDNA label encoding for internationalized domains, ``pepsi-common::domain`` and ``pepsi-common::dns`` * - RFC 3565 - Use of AES encryption in CMS - AES-256-CBC ``EnvelopedData``, the downgrade container when a recipient cannot read AEAD (``pepsi-crypto::smime``) * - RFC 3676 - The text/plain format=flowed media type - Body-text extraction and the ``--`` signature cut, ``pepsi-common::mime_text`` * - RFC 3834 - Recommendations for automatic responses to electronic mail - ``Auto-Submitted: auto-replied`` auto-replies (anti-spam, edit-settings) * - RFC 3848 - ESMTP and LMTP transmission types (``with`` clause) - ``Received:`` header (``SMTP``/``ESMTP``/``ESMTPS``), ingress * - RFC 3864 - Registration procedures for message header fields - The procedure Pepsi's two private header fields (:doc:`smtp-extensions`) are **not** registered under; both are unregistered by design * - RFC 4013 - SASLprep: stringprep profile for user names and passwords - SCRAM username/password preparation, ``pepsi-common::smtp::sasl`` * - RFC 4055 - Additional algorithms and identifiers for RSA in PKIX - RSAES-OAEP and RSASSA-PSS parameter encoding, ``pepsi-crypto::smime`` * - RFC 4121 - The Kerberos 5 GSS-API mechanism, v2 - Outbound smarthost ``AUTH = gssapi`` (via system Kerberos), ``pepsi-common::smtp::gssapi`` * - RFC 4155 - The application/mbox media type - ``mbox`` mailbox reading for whitelist import, ``pepsi-helper-mailbox-scan`` * - RFC 4422 - Simple Authentication and Security Layer (SASL) — ``EXTERNAL`` mechanism - Outbound SMTP ``AUTH EXTERNAL`` via TLS client certificate (smarthost) * - RFC 4511 - LDAP: the protocol - LDAP key discovery, ``pepsi-keydisc``'s ``ldap`` module (behind the ``ldap`` cargo feature, off by default) * - RFC 4514 - LDAP: string representation of distinguished names - Certificate subject/issuer DN rendering, ``pepsi-crypto::smime`` * - RFC 4515 - LDAP: string representation of search filters - Filter escaping for the LDAP discovery query — the injection guard, ``pepsi-keydisc`` * - RFC 4616 - The ``PLAIN`` SASL mechanism - Inbound (ingress) and outbound (smarthost) ``AUTH PLAIN`` * - RFC 4648 - The Base16, Base32, and Base64 data encodings - SRS hash encoding (``pepsi-common::srs``); key material * - RFC 4752 - The Kerberos V5 (``GSSAPI``) SASL mechanism - Outbound smarthost ``AUTH = gssapi``, ``pepsi-common::smtp::gssapi`` * - RFC 4954 - SMTP service extension for authentication (``AUTH``) - Inbound ``AUTH PLAIN``/``LOGIN`` (ingress, TLS-only) and the full outbound smarthost SASL suite * - RFC 5083 - CMS ``AuthEnvelopedData`` content type - The default S/MIME encryption container (AES-256-GCM), ``pepsi-crypto::smime`` * - RFC 5084 - Using AES-CCM and AES-GCM in CMS - AES-256-GCM ``AuthEnvelopedData``, the **default** S/MIME container, ``pepsi-crypto::smime`` * - RFC 5228 - Sieve: an email filtering language - Run by the MDA (Dovecot) on LMTP delivery; Pepsi implements no Sieve itself * - RFC 5280 - Internet X.509 PKI certificate and CRL profile - Certificate parsing, extension handling and path validation, ``pepsi-crypto::trust``; issuance in ``pepsi-crypto::keygen`` * - RFC 5321 - Simple Mail Transfer Protocol - Core of ingress and both relay stages; envelope handling, MX, null sender * - RFC 5322 - Internet Message Format - Header/body model (``pepsi-common::message``); parsing and signing * - RFC 5480 - Elliptic curve cryptography subject public key information - EC public-key encoding in certificates, ``pepsi-crypto::smime`` * - RFC 5652 - Cryptographic Message Syntax (CMS) - ``SignedData``/``EnvelopedData``/``certs-only`` built and opened by ``pepsi-crypto::smime`` (the ``cms`` crate supplies the types only) * - RFC 5753 - Use of ECC algorithms in CMS - ECDH key-agreement ``RecipientInfo`` (P-256/P-384 + X9.63 KDF + AES-KW), ``pepsi-crypto::smime`` * - RFC 5758 - Additional algorithms and identifiers for DSA and ECDSA in PKIX - ``ecdsa-with-SHA256``/``-SHA384`` signature identifiers, ``pepsi-crypto::smime`` * - RFC 5802 - Salted Challenge Response Authentication Mechanism (``SCRAM``) - Outbound ``SCRAM-SHA-1``/``-SHA-256`` (±``-PLUS``), ``pepsi-common::smtp::sasl`` * - RFC 5890 - Internationalized Domain Names for Applications (IDNA2008): definitions - Domain handling throughout, via the ``idna`` crate; see also RFC 3492 * - RFC 5915 - Elliptic curve private key structure - EC private-key encoding, ``pepsi-crypto::keygen`` * - RFC 5929 - Channel bindings for TLS (``tls-server-end-point``) - SCRAM ``-PLUS`` channel binding, ``pepsi-common::smtp::sasl`` * - RFC 5958 - Asymmetric key packages (PKCS #8) - Private-key encoding at rest, ``pepsi-crypto::keygen`` and ``pepsi-common::keys`` * - RFC 5987 - Character set and language encoding for HTTP header field parameters - ``filename*`` decoding in multipart form uploads, ``pepsi-httpd`` * - RFC 6125 - Service identity in TLS (certificate name checking) - MTA-STS certificate validation, ``pepsi-stage-relay-to-internet`` * - RFC 6152 - SMTP service extension for 8-bit MIME transport (``8BITMIME``) - Advertised by ingress; per-hop re-advertise/downgrade in the relay stages * - RFC 6265 - HTTP state management mechanism (cookies) - The administrative session cookie and the secure-link portal cookie, both ``__Host-`` prefixed; ``pepsi-httpd`` * - RFC 6331 - Moving ``DIGEST-MD5`` to Historic - The reason ``DIGEST-MD5`` is deprecated and excluded from ``AUTH = auto`` * - RFC 6376 - DomainKeys Identified Mail (DKIM) signatures - Verification (ingress) and signing (``pepsi-stage-dkim-sign``) * - RFC 6409 - Message submission for mail - Submission listeners (``SUBMISSION = yes``): mandatory ``AUTH``, ``Date:``/``Message-ID:`` fixups, ``Sender:`` fixup and submission-identity enforcement * - RFC 6531 - SMTP extension for internationalized email (``SMTPUTF8``) - Advertised by ingress; per-hop re-advertise/downgrade in the relay stages * - RFC 6598 - IANA-reserved IPv4 prefix for shared address space - Part of the outbound-fetch address guard; see RFC 1918 * - RFC 6648 - Deprecating the ``X-`` prefix in application protocols - Why ``Pepsi-Origin:`` and ``Taler:`` carry no ``X-`` prefix, while the verdict headers :doc:`programs/pepsi-stage-decrypt` strips deliberately do (``X-Pepsi-*`` is a namespace to be removed, not a field to be adopted) * - RFC 6698 - DNS-based authentication of named entities (DANE): TLSA - ``TLSA`` record types and matching, ``pepsi-common::smtp::dane``; the SMTP profile is RFC 7672 * - RFC 6749 - The OAuth 2.0 authorization framework - Smarthost token acquisition and refresh, ``pepsi-helper-token-refresh`` * - RFC 6750 - The OAuth 2.0 authorization framework: bearer token usage - The ``Authorization: Bearer`` form of the administrative API's tokens, :doc:`admin-api` — the wire form only; Pepsi mints them locally and runs no authorization server * - RFC 6761 - Special-use domain names - Names that must not be resolved on the public DNS, short-circuited in ``pepsi-common::dns`` * - RFC 6840 - Clarifications and implementation notes for DNSSEC - The ``AD`` bit read from the validating resolver's response — Pepsi trusts a validating resolver rather than validating itself; ``pepsi-common::dns`` * - RFC 6891 - Extension mechanisms for DNS (EDNS(0)) - UDP payload size on outgoing queries, ``pepsi-common::dns`` * - RFC 6979 - Deterministic usage of DSA and ECDSA - Nonce generation for ECDSA signatures, ``pepsi-crypto::smime`` * - RFC 7208 - Sender Policy Framework (SPF) for authorizing use of domains - Inbound SPF verification (ingress, via ``mail_auth``) * - RFC 7235 - HTTP/1.1: authentication - The ``Authorization: Bearer`` scheme on ``/api/v1``, ``pepsi-httpd`` * - RFC 7489 - Domain-based Message Authentication, Reporting, and Conformance (DMARC) - Inbound DMARC evaluation and optional enforcement (ingress) * - RFC 7505 - A "Null MX" no-service resource record - MX classification, ``pepsi-stage-relay-to-internet`` * - RFC 7595 - Guidelines and registration procedures for URI schemes - The procedure the ``taler://`` scheme is **not** registered under; noted so its status is not mistaken for IANA-registered * - RFC 7628 - A set of SASL mechanisms for OAuth (``OAUTHBEARER``) - Outbound smarthost ``AUTH = oauth`` (``OAUTHBEARER``, de-facto ``XOAUTH2``) * - RFC 7672 - SMTP security via opportunistic DANE TLS (``TLSA``) - ``DANE = off|warn|strict`` on both relay stages; ``pepsi-common::smtp::dane`` * - RFC 7677 - ``SCRAM-SHA-256`` and ``SCRAM-SHA-256-PLUS`` SASL mechanisms - Outbound ``SCRAM-SHA-256`` (±``-PLUS``), ``pepsi-common::smtp::sasl`` * - RFC 7748 - Elliptic curves for security (X25519) - Curve25519 key agreement in OpenPGP and TLS * - RFC 7929 - DNS-based authentication of named entities (DANE) bindings for OpenPGP - ``OPENPGPKEY`` records printed by ``pepsi-keys dns``; AD-gated lookups by ``pepsi-keydisc@dane`` (:doc:`programs/pepsi-keydisc`) * - RFC 8017 - PKCS #1 v2.2: RSA cryptography specification - RSA signature and key-transport primitives (via the ``rsa`` crate) for DKIM, ARC and S/MIME * - RFC 8018 - PKCS #5: password-based cryptography (PBKDF2) - ``SCRAM-SHA-*`` salted password derivation, ``pepsi-common::smtp::sasl`` * - RFC 8162 - Using secure DNS to associate certificates with domain names for S/MIME - ``SMIMEA`` records printed by ``pepsi-keys dns``; AD-gated lookups by ``pepsi-keydisc@dane`` (:doc:`programs/pepsi-keydisc`) * - RFC 8314 - Cleartext considered obsolete: use of TLS for submission and access - Implicit-TLS submission listener (``MODE = tls``), ingress; negotiated TLS version/cipher recorded in the ``Received:`` trace header (§4.3) * - RFC 8446 - The Transport Layer Security (TLS) protocol version 1.3 - Every TLS listener and client, over ``rustls`` — inbound ``STARTTLS``/implicit TLS, outbound relay, LMTP, HTTP and LDAP * - RFC 8460 - SMTP TLS Reporting (TLSRPT) - Outbound TLS-session recording (relay stages) + daily reports (``pepsi-tlsrpt``) * - RFC 8461 - SMTP MTA Strict Transport Security (MTA-STS) - Policy enforcement (``pepsi-stage-relay-to-internet``); publishing (``pepsi-setup``) * - RFC 8463 - A new cryptographic signature method for DKIM (Ed25519) - Ed25519 DKIM keys/signatures, ``pepsi-common::keys`` * - RFC 8550 - S/MIME 4.0 certificate handling - Path building, ``emailProtection`` EKU and ``rfc822Name`` binding (``pepsi-crypto::trust``); certificate issuance (``pepsi-crypto::keygen``, ``pepsi-keys::ca``) * - RFC 8551 - S/MIME 4.0 message specification - ``application/pkcs7-mime`` / ``-signature`` and the ``smime-type`` parameter, ``pepsi-crypto::mime`` + ``pepsi-crypto::smime`` * - RFC 8601 - Message header field for indicating message authentication status - ``Authentication-Results`` (incl. ``iprev``), ``pepsi-common::auth`` * - RFC 8617 - The Authenticated Received Chain (ARC) protocol - ARC verify/sign/seal, :doc:`programs/pepsi-stage-arc` * - RFC 8905 - The ``payto`` URI scheme - Wire-transfer targets in the wallet self-service reply, ``pepsi-stage-auto-pay`` * - RFC 8959 - The ``secret-token`` URI scheme - The merchant access token's form, ``pepsi-common::config`` * - RFC 9051 - Internet Message Access Protocol (IMAP) version 4rev2 - Read-only mailbox scanning for whitelist import (``EXAMINE`` + ``BODY.PEEK[HEADER]``, so nothing is marked ``\Seen``), ``pepsi-helper-mailbox-scan`` * - RFC 9106 - Argon2 memory-hard function for password hashing and PoW - The secure-link content key derived from the recipient's PIN (``pepsi-secure-link::crypto``) and administrator password hashing (``pepsi-httpd``'s ``admin`` module) * - RFC 9110 - HTTP semantics - Request/response handling in :doc:`programs/pepsi-httpd` * - RFC 9112 - HTTP/1.1 - The wire protocol served by :doc:`programs/pepsi-httpd`, over ``hyper`` * - RFC 9580 - OpenPGP (the crypto refresh; successor to RFC 4880) - ``pepsi-crypto::pgp`` over ``sequoia-openpgp``; explicit SEIPDv2/v1 container negotiation * - *Autocrypt Level 1* - The ``Autocrypt:`` key-advertisement header (a de-facto standard, not an RFC) - Harvested from inbound mail at the lowest trust rank, ``pepsi-keydisc``'s ``harvest`` module; emitted on outbound mail by ``pepsi-stage-encrypt``'s ``autocrypt`` module, under ``[stage-encrypt] AUTOCRYPT``. §5.3 key gossip is emitted by the same stage's ``gossip`` module under ``[stage-encrypt] AUTOCRYPT_GOSSIP`` (default off) and read by ``pepsi-stage-autocrypt-learn`` under ``LEARN_GOSSIP`` (default on), onto a trust rung of its own * - *FIPS 186-4* - Digital Signature Standard (a NIST publication) - The ``bits2int`` truncation rule shared with RFC 6979, ``pepsi-crypto::smime`` * - *draft-ietf-mailmaint-autoconfig* - Mail account autoconfiguration (an Internet-Draft of the IETF ``mailmaint`` working group, intended for Informational status) - The ``config-v1.1.xml`` document served by :doc:`programs/pepsi-httpd` on ``autoconfig.`` and under ``/.well-known/autoconfig/``, built from ``[pepsi-autoconfig]`` and the submission listener * - *MS-NLMP* - The NT LAN Manager authentication protocol (a Microsoft Open Specification, not an RFC) - Outbound smarthost ``AUTH NTLM``, ``pepsi-common::smtp::sasl``. **NTLMv2 only** — NTLMv1 is cryptographically broken — with channel binding, and never offered outside TLS * - *Maildir* - D. J. Bernstein's mailbox format (a specification, not an RFC — ``tmp``/``new``/``cur`` with the rename-into-``new`` delivery rule) - :doc:`programs/pepsi-stage-relay-to-maildir` via the setuid helper; Maildir++ recursion when scanning for whitelist import * - *SRS* - The Sender Rewriting Scheme (a published scheme with no RFC; the ``SRS0``/``SRS1`` envelope-address format) - ``pepsi-common::srs``, :doc:`programs/pepsi-stage-srs`, and the reverse decode at ingress. The MAC is RFC 2104 HMAC * - *VKS* (``keys.openpgp.org``) - The verifying key server's ``/vks/v1`` HTTP protocol (a de-facto standard, not an RFC — it is not HKP) - Lookup by ``pepsi-keydisc@vks``; upload and address verification by :doc:`programs/pepsi-keys`, whose confirmation mail :doc:`programs/pepsi-stage-vks-confirm` answers * - *XOAUTH2* - Google's and Microsoft's pre-standard SASL bearer-token mechanism (no specification body; superseded by RFC 7628 ``OAUTHBEARER``, which the same servers also accept) - Outbound smarthost ``AUTH``, ``pepsi-common::smtp::sasl``. Offered because some tenants still refuse ``OAUTHBEARER`` * - *draft-koch-openpgp-webkey-service* - OpenPGP Web Key Directory (an Internet-Draft, not an RFC) - Client side: ``pepsi-keydisc@wkd-advanced`` / ``@wkd-direct``. Serving side: the four endpoints of :doc:`programs/pepsi-httpd`, answered from the stored ``pepsi_common::keystore::wkd_local_part_hash`` .. _rfc-notes: Per-RFC notes ============= .. index:: single: RFC 1035 RFC 1035 — Domain Names (DNS) ----------------------------- All DNS resolution (``MX``, ``A``/``AAAA``, ``TXT`` for SPF/DKIM/DMARC/MTA-STS, and reverse ``PTR``) follows RFC 1035. Domain-name and label syntax limits are enforced in ``pepsi-common::domain``. DNS is used by ingress (authentication), the ARC stage (re-verification) and ``pepsi-stage-relay-to-internet`` (MX discovery and address caching in the ``pepsi.dns_address`` table). .. index:: single: RFC 2045 .. index:: single: RFC 2047 RFC 2045 / RFC 2047 — MIME bodies and header encoded-words ---------------------------------------------------------- When a next hop does not advertise ``8BITMIME`` (RFC 6152), ``pepsi-common::mime`` walks the MIME tree and re-encodes each 8-bit leaf part with a 7-bit content-transfer-encoding (RFC 2045: quoted-printable for ``text/*``, base64 otherwise). When a hop does not advertise ``SMTPUTF8`` (RFC 6531), UTF-8 header fields are rewritten as RFC 2047 encoded-words. See :doc:`programs/pepsi-stage-relay-to-internet`. .. index:: single: RFC 3030 RFC 3030 — CHUNKING / BDAT -------------------------- Ingress advertises ``CHUNKING`` and accepts message bodies framed with ``BDAT`` chunks (in addition to ``DATA``). The ``BODY=BINARYMIME`` body value of the same RFC is **not** advertised or accepted. See :doc:`programs/pepsi-ingress`. .. index:: single: RFC 3207 RFC 3207 — STARTTLS ------------------- Ingress listeners in ``starttls`` mode advertise the ``STARTTLS`` verb and upgrade a cleartext session in place. The outbound SMTP client uses ``STARTTLS`` opportunistically (or as required by MTA-STS). Implicit TLS is governed by RFC 8314 instead. .. index:: single: RFC 3461 .. index:: single: RFC 3463 .. index:: single: RFC 3464 RFC 3461 / RFC 3463 / RFC 3464 — Delivery Status Notifications -------------------------------------------------------------- Ingress advertises the ``DSN`` extension and validates/stores ``RET``/``ENVID``/``NOTIFY``/``ORCPT`` under ``state.dsn`` (``pepsi-common::dsn``). The relay stages propagate the parameters to a next hop that also advertises ``DSN`` (RFC 3461 §5–6). ``pepsi-stage-bounce`` emits the notification as an RFC 3464 ``multipart/report`` with RFC 3463 enhanced status codes, honouring ``NOTIFY`` (failure by default; success/delay only on explicit request). See :doc:`features` and :doc:`programs/pepsi-stage-bounce`. .. index:: single: RFC 3834 RFC 3834 — Automatic responses ------------------------------ The auto-replies Pepsi originates — the ``pepsi-stage-anti-spam`` payment-request message and the ``pepsi-stage-edit-settings`` confirmation/error replies — set ``Auto-Submitted: auto-replied`` per RFC 3834 and are sent with the null sender. A message that is itself an automatic response (null sender / ``Auto-Submitted``) is never gated, bounced or auto-replied to, so the stages cannot form a reply loop. See :doc:`programs/pepsi-stage-anti-spam`. .. index:: single: RFC 3848 RFC 3848 — ESMTP transmission types ----------------------------------- The ``Received:`` trace header ingress prepends records the transport in the ``with`` clause as ``SMTP``, ``ESMTP``, ``ESMTPS`` or — when the session authenticated over TLS with SMTP ``AUTH`` — ``ESMTPSA``, per RFC 3848. .. index:: single: RFC 4648 RFC 4648 — Base16/Base32/Base64 ------------------------------- SRS addresses encode their truncated HMAC in base32 (RFC 4648) so the token is case-insensitive and address-safe; key material uses base64. See :doc:`programs/pepsi-stage-srs`. .. index:: single: RFC 4954 .. index:: single: RFC 2195 .. index:: single: RFC 2831 .. index:: single: RFC 4013 .. index:: single: RFC 4121 .. index:: single: RFC 4422 .. index:: single: RFC 4616 .. index:: single: RFC 4752 .. index:: single: RFC 5802 .. index:: single: RFC 5929 .. index:: single: RFC 6331 .. index:: single: RFC 7628 .. index:: single: RFC 7677 RFC 4954 — SMTP AUTH -------------------- Both directions are supported. **Inbound:** ingress implements the ``AUTH`` verb with the ``PLAIN`` (RFC 4616) and ``LOGIN`` mechanisms (SASL ``PLAIN`` initial-response and the prompted ``LOGIN`` exchange). It is advertised and accepted **only** on an encrypted session (implicit TLS or after ``STARTTLS``); an ``AUTH`` attempt in cleartext is refused with ``538``. Credentials are verified against a configured SASL backend (Dovecot's auth-client socket, ``SASL_TYPE``/``SASL_PATH``); a successful ``AUTH`` marks the session locally originated, lets it relay to any domain, and stamps ``ESMTPSA`` in the ``Received:`` ``with`` clause. **Outbound:** the SMTP client authenticates to a smarthost via the ``AUTH`` option of an ``[…-mta-*]`` section. The full mechanism suite (all in ``pepsi-common::smtp::sasl``, hand-rolled on the RustCrypto primitives already in-tree — no SASL/SCRAM crate) is: * ``plain`` — ``PLAIN`` (RFC 4616) and ``login`` — ``LOGIN`` (cleartext password inside the TLS tunnel). * ``cram-md5`` — ``CRAM-MD5`` (RFC 2195), HMAC-MD5 challenge-response. * ``scram-sha-1`` / ``scram-sha-256`` and their ``-plus`` variants — ``SCRAM`` (RFC 5802) and ``SCRAM-SHA-256`` (RFC 7677); the ``-PLUS`` forms add ``tls-server-end-point`` channel binding (RFC 5929). The server signature is verified for mutual authentication; usernames/passwords are SASLprep'd (RFC 4013). * ``digest-md5`` — ``DIGEST-MD5`` (RFC 2831), **deprecated** (RFC 6331 moved it to Historic): supported for legacy smarthosts but excluded from ``auto`` and warned about both at ``pepsi-setup`` validation and at runtime. * ``oauth`` — ``OAUTHBEARER`` (RFC 7628), falling back to the de-facto ``XOAUTH2``; the bearer token is read from ``TOKEN_FILE`` (see :doc:`programs/pepsi-helper-token-refresh`). * ``external`` — SASL ``EXTERNAL`` (RFC 4422), authenticated by the configured TLS client certificate rather than a password. * ``ntlm`` — the de-facto Microsoft ``NTLM`` mechanism (no RFC; **NTLMv2** only). Run only under TLS, so it always carries Extended Protection for Authentication: the ``tls-server-end-point`` (RFC 5929) channel-binding and ``smtp/`` SPN AV pairs plus the MIC. Weak and without server authentication — flagged by ``pepsi-setup``. * ``gssapi`` — SASL ``GSSAPI`` / Kerberos (RFC 4752, over the RFC 4121 Kerberos 5 GSS mechanism), via the system Kerberos library (``pepsi-common::smtp::gssapi``). The ticket comes from a credential cache kept fresh out of band; the SASL security layer is negotiated to *no security layer* (TLS already protects the session). * ``auto`` — negotiate the strongest mechanism the smarthost's EHLO offers (``SCRAM-SHA-256-PLUS`` > ``SCRAM-SHA-256`` > ``SCRAM-SHA-1-PLUS`` > ``SCRAM-SHA-1`` > ``CRAM-MD5`` > ``LOGIN`` > ``PLAIN``; ``DIGEST-MD5``, ``NTLM`` and ``GSSAPI`` are never auto-selected). As inbound, the outbound client refuses to send credentials over an unencrypted connection. A ``5xx`` rejection of a password mechanism is treated as a permanent failure (bounce). See :doc:`programs/pepsi-ingress` and :doc:`programs/pepsi-stage-relay-to-smarthost`. GSSAPI/Kerberos is the one mechanism that is **not** hand-rolled: it links the system Kerberos GSS implementation (MIT krb5 / Heimdal). The Debian package therefore build-depends on ``libkrb5-dev`` and depends on ``libgssapi-krb5-2``. .. index:: single: RFC 2033 .. index:: single: RFC 5228 RFC 2033 / RFC 5228 — LMTP local delivery and Sieve --------------------------------------------------- :doc:`programs/pepsi-stage-relay-to-lmtp` delivers a message to a local **Mail Delivery Agent** — typically **Dovecot** — over **LMTP** (RFC 2033): it offers every recipient in a single LMTP transaction (``LHLO``, optional ``STARTTLS`` and SASL, multi-reply ``DATA``) and routes each recipient onward by the MDA's per-recipient reply (delivered, or classified by its RFC 3463 status). The MDA, not Pepsi, runs each recipient's **Sieve** (RFC 5228) filter as it files the mail; Pepsi implements no Sieve itself. The shared LMTP client is ``pepsi-common::smtp::deliver_lmtp``. Because the MDA drops privilege, this stage needs no setuid/setgid helper (unlike :doc:`programs/pepsi-stage-relay-to-maildir`). See :doc:`features` and :doc:`programs/pepsi-stage-relay-to-lmtp`. .. index:: single: RFC 5321 RFC 5321 — SMTP --------------- RFC 5321 is the backbone: the ingress command grammar and replies; the envelope (``MAIL FROM``/``RCPT TO``, the null sender ``<>``); the domainless ```` mailbox (§4.5.1); the ``Received:`` trace header (§4.4); implicit MX via address records (§5.1); and the "never bounce a bounce" rule (§6.1). It governs ingress and both relay stages. .. index:: single: RFC 5322 RFC 5322 — Internet Message Format ---------------------------------- Messages are stored split into a header block and a body at the first blank line (``pepsi-common::message``); header selection for signing and the parsed ``From:``/``Subject:`` follow RFC 5322. DKIM/ARC ``h=`` selection is bottom-up so a prepended header never disturbs an existing signature. .. index:: single: RFC 3156 .. index:: single: RFC 9580 RFC 3156 / RFC 9580 — OpenPGP and PGP/MIME ------------------------------------------ RFC 9580 is the 2024 OpenPGP specification (the "crypto refresh", succeeding RFC 4880); RFC 3156 is how an OpenPGP message is carried in MIME. Pepsi implements both over the **sequoia-openpgp** library, in ``pepsi-crypto::pgp`` (cryptography) and ``pepsi-crypto::mime`` (containers). **The MIME half (RFC 3156).** Encryption emits ``multipart/encrypted; protocol="application/pgp-encrypted"`` with the ``Version: 1`` control part and an ``application/octet-stream`` ciphertext part (§4). Detached signing emits ``multipart/signed; protocol="application/pgp-signature"`` with the ``micalg`` parameter computed from the hash actually used (§5) — ``pgp-sha256`` for the default. Both forms are recognised on the way in, as is non-MIME **inline** armor, which ``[pepsi] CRYPTO_INLINE_PGP`` may refuse. **The container question (RFC 9580).** The choice between the RFC 9580 SEIPDv2 container (AEAD) and the RFC 4880 SEIPDv1 container (an MDC) is made **explicitly** from the recipient keys' declared capabilities rather than left to the library, so a downgrade is a decision that gets recorded rather than a silent one; ``[pepsi] CRYPTO_ALLOW_DOWNGRADE`` governs whether it is permitted at all. Reading a SEIPDv1+MDC message is never gated — most of the installed base still produces them — but the pre-MDC ``SED`` packet is refused outright. See :doc:`key-management`, :doc:`programs/pepsi-stage-encrypt` and :doc:`programs/pepsi-stage-decrypt`; :doc:`interoperability` records what has been measured against **gpg**. .. index:: single: RFC 5280 RFC 5280 — X.509 certificates and path validation -------------------------------------------------- Every S/MIME decision rests on this one. ``pepsi-crypto::trust`` parses certificates, applies the extensions that matter for mail (``basicConstraints``, ``keyUsage``, the ``emailProtection`` extended key usage of §4.2.1.12, and the ``rfc822Name`` subject alternative name that binds a certificate to an address) and builds a path to a configured anchor. Two boundaries are worth stating because the code is deliberate about them: * **The trust store is searched before the message's own certificates.** A CMS ``SignerIdentifier`` names a certificate by issuer and serial number, so a message can carry a forged certificate bearing a pinned one's issuer and serial with a different key. A verifier that looked at the message first would accept it. * **Revocation is not checked.** Neither the CRL profile of §5 nor OCSP (RFC 6960) is consulted — see :ref:`rfc-not-yet`. The ``Revocation`` value on a validated path is always ``NotChecked``, which is reported honestly rather than rendered as "not revoked". A certificate that chains to no configured anchor is **untrusted, not invalid** (``Verdict::Untrusted``): most of the world's signed mail chains to a CA nobody handed us an anchor for, and reporting that as a failure would make the indicator useless. .. index:: single: RFC 5652 .. index:: single: RFC 5083 .. index:: single: RFC 5753 .. index:: single: RFC 8550 .. index:: single: RFC 8551 RFC 5652 / 5083 / 5753 / 8550 / 8551 — CMS and S/MIME ------------------------------------------------------ S/MIME is Pepsi's second end-to-end protocol, and its four standards divide the work cleanly. **RFC 5652 (CMS)** is the container syntax. ``pepsi-crypto::smime`` builds and opens ``SignedData`` (detached and opaque), ``EnvelopedData``, and the ``certs-only`` form used to publish a certificate. The ``cms`` crate supplies the ASN.1 *type definitions* only; the builders and openers are Pepsi's own, so that what is emitted and what is accepted are decided here rather than inherited. **RFC 5083 (``AuthEnvelopedData``)** is the authenticated-encryption container, and it is what Pepsi emits by default (AES-256-GCM). The RFC 5652 ``EnvelopedData`` with AES-CBC remains as the downgrade path for a recipient that cannot do better — again gated by ``[pepsi] CRYPTO_ALLOW_DOWNGRADE``. Which clients can actually read ``AuthEnvelopedData`` is the largest open interoperability question in the tree; see :ref:`interop-aead`. **RFC 5753** covers the elliptic-curve ``RecipientInfo``: ECDH key agreement over P-256/P-384 with the X9.63 KDF and AES key wrap, alongside the RSA forms (RSAES-OAEP-SHA-256, and PKCS#1 v1.5 for a recipient that needs it). **RFC 8550 (certificate handling)** is ``pepsi-crypto::trust``: path building to a configured anchor, validity dates, ``basicConstraints``, ``keyUsage`` against the operation being performed, the ``id-kp-emailProtection`` extended key usage, and the ``rfc822Name`` subject-alternative-name that binds a certificate to the ``From:`` address. Revocation is **tri-state** — "not checked" is never rendered as "not revoked" — and the module opens no sockets: there is no OCSP and no CRL fetching, only CRLs supplied to it. **RFC 8551 (message specification)** is the MIME surface: ``application/pkcs7-mime`` with the ``smime-type`` parameter (``signed-data``/``enveloped-data``/``authEnveloped-data``/``certs-only``) and ``application/pkcs7-signature`` inside ``multipart/signed``, with the conventional ``smime.p7m``/``smime.p7s``/``smime.p7c`` file names. The legacy ``application/x-pkcs7-*`` spellings are accepted on the key-harvesting path only. Certificates for this deployment's own users are issued by ``pepsi-crypto::keygen`` through the local CA in :doc:`programs/pepsi-keys`. See :doc:`key-management`; :doc:`interoperability` records what has been measured against **OpenSSL**. .. index:: single: RFC 6125 RFC 6125 — TLS service identity ------------------------------- When MTA-STS requires authenticated TLS, the recipient MX certificate is checked to validate for the expected host name per RFC 6125. See :doc:`programs/pepsi-stage-relay-to-internet`. .. index:: single: RFC 6152 RFC 6152 — 8BITMIME ------------------- Ingress advertises ``8BITMIME`` and records the ``BODY=`` value under ``state.origin``. Each relay stage re-advertises ``BODY=8BITMIME`` to a hop that supports it and otherwise downgrades the body (RFC 2045). See :doc:`features`. .. index:: single: RFC 6376 .. index:: single: RFC 8463 RFC 6376 / RFC 8463 — DKIM (RSA and Ed25519) -------------------------------------------- Ingress verifies inbound DKIM signatures; ``pepsi-stage-dkim-sign`` signs outbound mail. Each domain has both an RSA-2048 key (RFC 6376) and an Ed25519 key (RFC 8463), provisioned by ``pepsi-setup``; both signatures are added. The ``l=`` body-length tag is optional (``COVER_BODY``). See :doc:`programs/pepsi-stage-dkim-sign`. .. index:: single: RFC 6409 RFC 6409 — Message submission ----------------------------- A listener marked ``SUBMISSION = yes`` acts as a Message Submission Agent. It **requires** ``AUTH`` as RFC 6409 expects (an unauthenticated ``MAIL FROM`` is refused with ``530``; see `RFC 4954 — SMTP AUTH`_) and applies the submission fixups to every accepted message: a missing ``Date:`` is added (§8.2) and a missing ``Message-ID:`` is generated (§8.3). Submission-identity enforcement (§6) and the ``Sender:`` fixup (§8.1) are driven by the ``USERNAME_MAP`` mapping of the authenticated SASL user to its allowed addresses: an envelope ``MAIL FROM`` or a ``From:`` the user is not permitted to use is refused with ``550``, and an allowed ``From:`` that differs from the user's canonical identity gets a ``Sender:`` header naming that identity. See :doc:`programs/pepsi-ingress`. .. index:: single: RFC 6531 RFC 6531 — SMTPUTF8 ------------------- Ingress advertises ``SMTPUTF8`` and accepts UTF-8 in headers and the envelope. Each relay stage re-advertises it to a supporting hop and otherwise downgrades UTF-8 headers (RFC 2047); a non-ASCII *address* that cannot be represented to a non-``SMTPUTF8`` hop is a permanent failure (bounce). See :doc:`features`. .. index:: single: RFC 7208 RFC 7208 — SPF -------------- Ingress verifies SPF on the ``MAIL FROM`` identity using the connecting client's IP (via the ``mail_auth`` library) and records the verdict in ``Authentication-Results`` and under ``state.auth.spf``. .. index:: single: RFC 7489 RFC 7489 — DMARC ---------------- Ingress evaluates DMARC and records the verdict. With ``DMARC_ENFORCE`` set, a definite DMARC failure under a ``quarantine``/``reject`` policy is rejected at SMTP time (``550``); otherwise the failing verdict is recorded and the mail is accepted (fail-open, suited to a forwarder). The organizational domain that relaxed alignment compares against is found by the RFC 9989 (DMARCbis) tree walk, which ``mail_auth`` performs internally. Note this changed with ``mail-auth`` 0.11: the older interface asked the caller for an organizational-domain function and defaulted it to the identity, so before that upgrade relaxed alignment compared the two domains as given and a subdomain never aligned with its parent. .. index:: single: RFC 7505 RFC 7505 — Null MX ------------------ ``pepsi-stage-relay-to-internet`` classifies a ``0 .`` exchange as a permanent failure (an immediate bounce, never a retry). .. index:: single: RFC 7672 RFC 7672 — DANE / TLSA for SMTP ------------------------------- Both relay stages support opportunistic DANE: a ``DANE = off|warn|strict`` option (default ``warn``) looks up the next hop's ``TLSA`` records — per-MX for ``pepsi-stage-relay-to-internet``, per-``[…-mta-*]`` for ``pepsi-stage-relay-to-smarthost`` — and matches them against the presented certificate chain. The verifier and TLSA types live in ``pepsi-common::smtp::dane``; the AD-bit-aware ``TLSA``/``MX`` lookups in ``pepsi-common::dns`` (Pepsi trusts a validating resolver and reads the response AD bit rather than validating DNSSEC itself). DANE takes precedence over MTA-STS and ``TLS_VERIFY``; a usable-record mismatch defers in ``strict`` and only warns in ``warn``. .. index:: single: RFC 7929 .. index:: single: RFC 8162 RFC 7929 / RFC 8162 — OPENPGPKEY and SMIMEA key records -------------------------------------------------------- Pepsi implements both halves: publishing our own addresses' key records, and looking a correspondent's up. **Publishing.** ``pepsi-keys dns
`` prints the DNS records that publish an address's stored public key material: an ``OPENPGPKEY`` record (RFC 7929) carrying an OpenPGP transferable public key, and an ``SMIMEA`` record (RFC 8162) carrying an S/MIME certificate in the TLSA ``(usage, selector, matching-type)`` shape — ``3 0 0`` (domain-issued certificate, full certificate, exact match), the form a self-signed or directly pinned certificate takes. Only published, active identities are emitted. ``pepsi-setup run`` prints the same records for every published identity alongside the DKIM, SPF and MTA-STS ones. **Looking up.** The ``pepsi-keydisc@dane`` discovery instance queries the same two record types for a correspondent's address, ``OPENPGPKEY`` first and ``SMIMEA`` if the domain publishes no OpenPGP record. It ranks second on the trust ladder — below only material an operator entered by hand — because DNSSEC is the one source with cryptographic provenance. Both directions use the same hashed owner name — the hex of the first 28 octets of the SHA-256 of the local part, followed by ``._openpgpkey.`` or ``._smimecert.`` — so a zone does not enumerate the addresses it serves. That hash is **case-sensitive** (RFC 7929 §3), unlike the Web Key Directory hash below. Both standards presuppose a **DNSSEC-signed zone**, and Pepsi treats that as binding in both directions. ``pepsi-keys dns`` says so in its own output, and a lookup is **AD-gated**: an answer the resolver did not validate is refused outright rather than accepted at a lower rank, because an unsigned key record is a key from whoever can answer for the zone. Pepsi trusts a validating resolver rather than validating DNSSEC itself, so a deployment without one gets no DANE results at all — ``pepsi-setup`` probes for this and reports it. See :doc:`programs/pepsi-keys`, :doc:`programs/pepsi-keydisc` and :doc:`key-management`. .. index:: single: OpenPGP Web Key Directory OpenPGP Web Key Directory (draft-koch-openpgp-webkey-service) -------------------------------------------------------------- The Web Key Directory is the HTTPS half of the same job: a correspondent's key is fetched from the address's own domain under a hash of its local part. Pepsi computes that hash in ``pepsi_common::keystore::wkd_local_part_hash`` — z-base-32 of the SHA-1 of the **lower-cased** local part — and stores it on each identity, indexed, so a request is answered with one lookup instead of by hashing every candidate row. (The SHA-1 there is a naming function the specification fixes, not a security one, so it is deliberately not gated by ``CRYPTO_ALLOW_WEAK_DIGESTS``.) The **client** side is implemented: ``pepsi-keydisc@wkd-advanced`` queries ``openpgpkey.`` and ``pepsi-keydisc@wkd-direct`` the domain itself. They are separate ranks and therefore separate concurrent lookups rather than a fallback chain, since running direct only "if advanced fails" would accept the weaker answer whenever the stronger one was merely slow. The fetch is hardened beyond the draft's own requirements: HTTPS only with certificate validation and no insecure mode, at most one HTTPS-to-HTTPS redirect to the same host, refusal to connect to loopback/private/link-local addresses, a body cap enforced while streaming, a deadline on every step, and IDNA A-label conversion. A returned key whose User ID does not name the queried address is discarded — without that, a domain could answer every request with one key of its choosing — and the policy file's ``mailbox-only`` and ``dane-only`` directives are honoured. The **serving** side is implemented too, by :doc:`programs/pepsi-httpd`: the direct and advanced forms of both the key and the policy path, answered by an indexed ``(domain, local-part hash)`` lookup over identities this deployment holds and has marked ``published``. The response is the binary transferable key with ``Access-Control-Allow-Origin: *``; the policy file is a zero-length ``200``, which has to exist because GnuPG reads its absence as "no Web Key Directory here"; an unknown hash is an empty-bodied ``404``. The ``?l=`` parameter is read and ignored — honouring it would turn the endpoint into a lookup by caller-supplied address rather than by a hash the caller had to know. Two properties of the serving side are deliberate and worth stating. It never serves a **cached correspondent key**, only our own identities: publishing material we did not verify, under our own domain name, is what a key server does and we are not one. And it applies **no rate limiting**, because a Web Key Directory is a public oracle by construction — see :doc:`key-management`. The specification is an Internet-Draft rather than an RFC. .. index:: single: Autocrypt Autocrypt — the ``Autocrypt:`` header ------------------------------------- Autocrypt is not an RFC but a de-facto standard, and it is how most PGP-using mail clients advertise a key today, which is reason enough to read it. Pepsi harvests the header — together with ``application/pgp-keys`` parts and the signer certificates inside an S/MIME ``SignedData`` — from mail that has already arrived. Harvesting is deliberately **not** a discovery service: it costs no network I/O because the material is already in front of it, so it runs inline rather than as a ``pepsi-keydisc`` instance, and ``inbound`` is rejected in ``[pepsi-keydiscovery] SOURCES``. It does write through the same path, so a harvested key also releases any message parked waiting on that address. A harvested key sits at the **bottom** of the trust ladder — trust on first use — and the server-side conflict rule stops it displacing anything better attested. It is nevertheless usable by default, because the alternative to encrypting to a TOFU key is sending cleartext. See :doc:`key-management`. Pepsi **writes** the header as well. Unless ``[stage-encrypt] AUTOCRYPT`` is turned off, **pepsi-stage-encrypt** advertises the ``From:`` author's own OpenPGP key on every locally submitted message — including plain cleartext mail, which is the case that matters, since a correspondent has to learn the key before there is anything to encrypt with. What is advertised is the minimal five-packet transferable public key of Level 1 §2.1.1, only for an identity that still holds its private material, and never on a message that already carries an ``Autocrypt:`` field from the sender's own client. Because the stage runs immediately before DKIM signing, the header is covered by our own signature. ``prefer-encrypt=mutual`` is claimed only when the effective ``ENCRYPT`` policy is ``required``. Level 1 §2.4 has a correspondent's client encrypt *by default* only when both ends say ``mutual``, so claiming it under an opportunistic policy — one whose ``ON_NO_KEY = cleartext`` says unencrypted mail is acceptable — would announce a preference the deployment does not enforce. Level 1 §5.3 key gossip — the mechanism by which one message advertises the keys of the *other* recipients, so that two correspondents who have never written to each other can nevertheless answer encrypted — is implemented too, under ``[stage-encrypt] AUTOCRYPT_GOSSIP``. It is **off by default**: unlike the ``Autocrypt:`` header, which publishes a key its owner asked this deployment to hold, gossip redistributes third parties' cached key material to correspondents who did not ask for it, and that is an operator's decision to take deliberately. Two properties are worth stating, because both are what makes the feature safe to turn on. The fields ride **inside the ciphertext** and only there — an outer one would publish the recipient set of an encrypted message to every relay on the path — so there is no gossip on cleartext, on signed-only mail, or in an S/MIME container, which Autocrypt does not define. And the addresses gossiped are read from the message's ``To:`` and ``Cc:`` fields alone: the envelope recipient list, which carries the ``Bcc`` recipients, is consulted only to *remove* an address from the set. A blind recipient can therefore never appear in any copy, which is exactly the rule Level 1 §5.3 also imposes on a receiver. The **receiving** half is implemented as well, in ``pepsi-stage-autocrypt-learn`` under ``LEARN_GOSSIP`` (default on) — a stage of its own precisely so that it can run after the spam checks, which §5.3 also asks for and which ``pepsi-stage-decrypt``, first on the inbound path by design, could never have honoured. The interesting part of it is not the parser. §5.3 requires a gossiped key to be kept in a peer state *separate* from one learnt from an ``Autocrypt:`` header, so that a third party's introduction can never displace what an address's owner said about themselves; Pepsi realises that as a rung of its own at the bottom of the discovery trust ladder (``gossip``, rank 8, below ``harvested``), which the database's conflict rule already knows how to enforce. A field counts only when it really was inside the ciphertext — an outer one could have been written by anything on the path — only when its ``addr`` is in the message's own ``To:``/``Cc:``, never for the sender's own address, and never for an address this host serves. It never carries ``prefer-encrypt``, and a signature checked against a gossiped key comes back ``valid-untrusted``, so an introduction opens no gate. .. index:: single: VKS VKS — the verifying key server protocol --------------------------------------- ``keys.openpgp.org`` and its work-alikes speak a small HTTP protocol under ``/vks/v1``, and it is deliberately **not** HKP: a key is only served for an address once the address holder has answered a confirmation mail, which is what makes the answer worth more than "somebody uploaded this". Pepsi speaks both halves. ``pepsi-keydisc@vks`` looks a correspondent up by address, discarding a key whose User ID does not name the address queried and skipping a revoked one; it ranks **below** DANE, both Web Key Directory methods and LDAP, because the confirmation proves control of the mailbox and nothing more. :doc:`programs/pepsi-keys` uploads our own identities and requests verification — off by default (``[pepsi-keys] VKS_PUBLISH``), because publishing a user's address to a third party is an operator's decision and, unlike the Web Key Directory, it cannot be undone. The server's confirmation mail then has to be answered, which is the whole job of :doc:`programs/pepsi-stage-vks-confirm` — a stage that follows a link only after six independent checks, including that the envelope sender's host is exactly the configured key server and that every link in the body points there too. .. index:: single: RFC 8446 RFC 8446 — TLS 1.3 ------------------ Every TLS-protected connection in Pepsi — inbound ``STARTTLS`` and implicit-TLS listeners, the outbound relay client, LMTP, the HTTP server and the LDAP discovery client — runs over ``rustls``, which speaks TLS 1.3 and 1.2 and nothing older. There is no configuration knob to re-enable SSLv3 or TLS 1.0/1.1, because ``rustls`` does not implement them. What varies between those connections is not the protocol but *what authentication is required of the peer*, and that is where the mail-specific standards take over: RFC 6125 for identity matching, RFC 7672 for DANE, RFC 8461 for MTA-STS, and the ``TLS_VERIFY`` option for the case where an operator knowingly relaxes it. Opportunistic STARTTLS to an arbitrary MX authenticates nothing by design — that is RFC 3207's model, not a weakness here — which is precisely why DANE and MTA-STS exist and why the outcome of every attempt is recorded for RFC 8460 reporting. .. index:: single: RFC 8314 RFC 8314 — Implicit TLS ----------------------- A listener with ``MODE = tls`` performs the TLS handshake before the SMTP greeting (implicit TLS, e.g. the submissions port 465), as recommended by RFC 8314. Following RFC 8314 §4.3, ingress also records the negotiated TLS version and cipher in the ``Received:`` trace header it prepends — as a ``(version=… cipher=…)`` comment after the ``ESMTPS`` keyword — since the bare ``ESMTPS`` transmission type does not convey which parameters were used. .. index:: single: RFC 8460 RFC 8460 — SMTP TLS Reporting (TLSRPT) -------------------------------------- When ``[pepsi-tlsrpt] SEND_REPORTS`` is on, the relay stages record the outcome of each outbound TLS session in the ``pepsi.tls_session`` aggregate-counter table. **pepsi-tlsrpt**\ (1), run daily from cron, groups a day's rows into one RFC 8460 JSON report per policy domain, gzips it, looks up the domain's ``_smtp._tls`` ``rua`` and ships it by ``mailto:`` (injected back into the pipeline so it is signed and relayed) or HTTPS ``POST``. The advertising half is ``pepsi-setup`` printing our own ``_smtp._tls`` TXT record from ``[pepsi-tlsrpt] RUA``. The shared types, failure classifier and report builders live in ``pepsi-common::tlsrpt``. See :doc:`programs/pepsi-tlsrpt`. .. index:: single: RFC 8461 RFC 8461 — MTA-STS ------------------ ``pepsi-stage-relay-to-internet`` discovers and enforces recipient-domain MTA-STS policies: an ``enforce`` policy restricts delivery to the listed MX hosts over authenticated ``STARTTLS``. ``pepsi-setup`` publishes our own ``_mta-sts`` TXT record and prints the policy file. See :doc:`programs/pepsi-setup`. .. index:: single: RFC 8601 RFC 8601 — Authentication-Results --------------------------------- Ingress prepends a standalone ``Authentication-Results`` header carrying the SPF, DKIM, DMARC and ``iprev`` (FCrDNS) verdicts, with the ingress hostname as the ``authserv-id``. The ARC stage reproduces the same assessment in its AAR. .. index:: single: RFC 8617 RFC 8617 — ARC -------------- :doc:`programs/pepsi-stage-arc` verifies any inbound ARC chain and seals the message as our ADMD by prepending an AAR/AMS/AS set, signed with the single ``ARC_ALGORITHM``. This lets the final receiver trust the boundary verdict even after Pepsi's forwarding breaks SPF/DKIM alignment. The ARC-Message-Signature's RFC 6376 parameters — canonicalization (``c=``), covered headers (``h=``), the ``l=`` body-length tag and an ``x=`` expiration — are configurable on the stage (as they are on :doc:`programs/pepsi-stage-dkim-sign`). .. index:: single: RFC 9051 .. index:: single: RFC 4155 RFC 9051 / RFC 4155 — reading a mailbox (IMAP and mbox) ------------------------------------------------------- Pepsi is not a mail store and serves neither protocol. It *reads* mailboxes in one place only: ``pepsi-helper-mailbox-scan``, which seeds a whitelist from the mail a user has already sent (see :doc:`programs/pepsi-whitelist`). The IMAP client is read-only in the strict sense — it issues ``EXAMINE`` rather than ``SELECT`` and fetches ``BODY.PEEK[HEADER]``, so no message is marked ``\Seen`` and no mailbox state changes. Only header blocks are read; every reader stops at the blank line, so message bodies never enter the process. The same helper reads local ``mbox`` (RFC 4155) and Maildir, and can consume a ``doveadm -f json`` stream. LMTP (RFC 2033) is the delivery counterpart and has no fetch verb at all, which is why a mailbox on an LMTP-only MDA cannot be scanned; IMAP is the network option. .. index:: single: RFC 9106 RFC 9106 — Argon2 ----------------- Argon2id appears twice, for two different threats. In the secure-link portal (:doc:`secure-link`) it derives the key that encrypts a stored message from the recipient's PIN plus a per-message salt and a server-side pepper held outside the database. The cost parameters are chosen so that a PIN short enough to read over the telephone is still expensive to grind, and the pepper is what makes a stolen database insufficient on its own — which is why disclosing it is treated as seriously as disclosing the messages. In the administrative API it hashes account passwords. That instance is also why the portal's rate limiter matters: at 19 MiB per derivation, an unauthenticated attacker who could retry freely would consume memory as fast as they consume guesses. .. _rfc-not-yet: Applicable RFCs not yet implemented =================================== These standards are in scope for a forwarder or a crypto gateway and are tracked on the roadmap (``docs/ROADMAP.md``) but are **not** implemented today. Several are partial in a way worth stating precisely: a status object exists and is reported, which is easy to mistake for the check having been performed. .. list-table:: :header-rows: 1 :widths: 12 40 48 * - RFC - Title - Status in Pepsi * - RFC 3030 - ``BINARYMIME`` body value - ``CHUNKING``/``BDAT`` is supported, but ``BODY=BINARYMIME`` is not advertised or accepted. * - RFC 6960 - Online Certificate Status Protocol (OCSP) - **Not implemented, and this is the one most likely to be misread.** ``pepsi-crypto::trust`` carries a ``Revocation`` status on every validated path, but it is only ever ``NotChecked`` — no OCSP responder is queried and no CRL (RFC 5280 §5) is fetched. A revoked S/MIME certificate therefore still validates. The three-valued status exists so a report can say "we did not check" rather than imply "not revoked"; do not read it as a check having happened. * - RFC 2634 - Enhanced Security Services for S/MIME (triple wrapping) - Not implemented, by decision (plan 01 D6). Pepsi signs then encrypts, or encrypts then signs, but never sign-encrypt-sign. Signed receipts and security labels are likewise absent. * - RFC 3161 - Time-Stamp Protocol (TSP) - Not implemented. The S/MIME ``signingTime`` attribute is the *signer's* own claim; it is clamped to the present so it cannot be postdated, but a backdated claim is indistinguishable from a genuinely old message. A deployment needing "signed when it says it was" needs a timestamp token, which Pepsi neither produces nor requires. * - RFC 6541 - DKIM Authorized Third-Party Signatures (ATPS) - Not implemented. The ``atps`` field is parsed off a DKIM record by the vendored ``mail-auth`` and carried in the verdict, but no ATPS lookup is performed and no policy consults it. * - RFC 6651 - Extensions to DKIM for failure reporting - Not implemented. The ``r=`` tag is parsed; no failure report is ever generated or sent. * - RFC 5321 - ``ETRN`` / on-demand queue starting - Not implemented and not planned. Pepsi's queue is a database table drained by the dispatcher; there is no per-domain queue to release. * - *Autocrypt Level 1* - Gossip **peer-state promotion** (§5.3) - Both halves of gossip are implemented, but one nuance of the peer-state model is not: Level 1 lets a client *promote* a gossiped key when the address's owner later confirms it, and Pepsi has no such transition. A better-sourced answer replaces the gossiped row outright (which is the safe direction), and until one arrives the key stays on the ``gossip`` rung however many messages introduce it. Nothing is lost that a real ``Autocrypt:`` header would not fix in one message.