85.1.6. pepsi-stage-encrypt

sign outgoing mail as its author and encrypt it to each recipient

Section du manuel:

1

85.1.6.1.1. Nom

pepsi-stage-encrypt - l’étape sortante de signature et de chiffrement de bout en bout du pipeline Pepsi.

85.1.6.1.2. Synopsis

pepsi-stage-encrypt [GLOBAL-OPTIONS] worker

85.1.6.1.3. Description

pepsi-stage-encrypt est un programme d’étape exécuté par pepsi-dispatch(1) comme un worker persistant lisant les identifiants de message sur l’entrée standard. Pour chaque message soumis localement, il décide de la protection à appliquer, l’applique, et achemine chaque destinataire plus loin.

Le nom sous-estime l’étape : elle signe autant qu’elle chiffre, et dans cet ordre (signer dedans, chiffrer dehors). La signature emploie la clé de l’auteur du From:, jamais celle de l’expéditeur d’enveloppe — la signature porte sur l’auteur que voit le destinataire, et une signature dont l’identité ne correspond pas est ce que les vérificateurs signalent. Si l’adresse From: n’est pas une adresse que ce déploiement sert, ou ne détient aucun matériel de signature, le message est envoyé non signé plutôt que rebondi : un auteur que l’on ne sert pas reste du courrier légitime, et DKIM porte déjà la revendication au niveau du domaine.

Le chiffrement se fait par destinataire. Chaque destinataire qui reçoit du texte chiffré reçoit son propre texte chiffré et sa propre ligne de file d’attente : aucune divulgation de la liste des destinataires, aucune clé de contenu partagée entre destinataires, et une fuite de Bcc est structurellement impossible. Regrouper les destinataires S/MIME dans un unique EnvelopedData serait moins coûteux et c’est ce à quoi le format sert, mais cela révèle l’ensemble des destinataires à chacun d’eux et laisse une seule clé compromise ouvrir le message pour tous.

L’étape ne fait aucune E/S réseau d’aucune sorte. Un destinataire dont la clé n’est pas en cache met le message en pause et met en file une demande de découverte ; un service pepsi-keydisc(1) le libère lorsque la demande se conclut, qu’une clé ait été trouvée ou non. Un destinataire ayant une entrée de cache négatif fraîche prend immédiatement le chemin sans clé, sans pause, puisqu’il n’y a rien à attendre.

Un message dont ce déploiement n’est pas à l’origine (pas de state.local_origin) est avancé sans être touché. Chiffrer un message tiers relayé invaliderait la signature DKIM et la chaîne ARC de l’auteur, et le signer au nom de l’un de nos utilisateurs serait un mensonge. Ses en-têtes de demande X-Pepsi-* sont tout de même retirés, de sorte qu’un expéditeur extérieur ne puisse pas laisser le canal de contrôle propre à Pepsi sur le fil.

Avertissement

Placez cette étape avant pepsi-stage-dkim-sign(1), non après.

La chaîne sortante recommandée est

submission -> ... -> encrypt -> srs -> dkim-sign -> relay

DKIM doit signer les octets réellement transmis. Si la signature DKIM s’exécute d’abord, cette étape réécrit le corps sous la signature et le résultat est du courrier d’apparence parfaitement valide dont la signature ne vérifie pas — ce qui est pire que pas de signature du tout, car une signature cassée est un signal négatif plus fort pour un destinataire qu’une signature absente. pepsi-setup(1) avertit lorsqu’il trouve une étape de signature DKIM qui mène à celle-ci.

Deux autres voisines veulent du texte clair et l’obtiennent : la barrière de péage à l’envoi (pepsi-stage-anti-spam(1)) et la détection de langue (pepsi-stage-detect-language(1)) lisent le corps du message, et sur le chemin sortant elles s’exécutent avant cette étape. C’est pourquoi le chiffrement vient en dernier.

85.1.6.1.3.1. Résolution de la politique

La protection qu’un message demande est résolue à partir de quatre entrées, la plus spécifique d’abord :

  1. la barrière state.local_origin — rien de ce qui suit ne s’applique à un message que nous ne faisons que rediriger ;

  2. les en-têtes de demande qu’un module complémentaire a posés (X-Pepsi-Sign, X-Pepsi-Encrypt, chacun yes / no / required) ;

  3. un mot-clé de sujet (SUBJECT_KEYWORDS_SIGN / _ENCRYPT / _BOTH) ;

  4. les valeurs par défaut SIGN et ENCRYPT de [stage-encrypt], qui portent déjà toute redéfinition par adresse issue de la table pepsi.settings.

Les deux en-têtes de demande sont retirés du message sortant quoi qu’ils disent et quelle que soit la valeur de STRIP_REQUEST_HEADERS : un canal de contrôle qui survit jusque sur le fil est un canal de contrôle auquel on peut ordonner au saut suivant d’obéir. Un mot-clé de sujet reconnu est de même retiré du Subject.

Un mot-clé de sujet de chiffrement (SUBJECT_KEYWORDS_ENCRYPT ou _BOTH) ou un en-tête X-Pepsi-Encrypt: required élève la politique à required pour ce message, et required ne se dégrade jamais en texte clair — si l”ON_NO_KEY résolu dit cleartext, il est élevé au lien sécurisé (ou à un rebond) pour ce message. Un utilisateur qui a explicitement demandé une protection n’est pas silencieusement ignoré.

Avec ENABLE_PEP = no, du matériel de clé n’est créé que lorsqu’un message le demande : une demande explicite de quelque sorte que ce soit (X-Pepsi-Sign/X-Pepsi-Encrypt autre que no, ou n’importe quel mot-clé de sujet – y compris un mot-clé [encrypt] sous SIGN = no), ou SIGN = always sauf si X-Pepsi-Sign: no a refusé la signature. Lorsque [pepsi-crypto] AUTO_CREATE_IDENTITY est activé, qu’un KEY_WRAP_SECRET est configuré, que le domaine du From: fait partie de [pepsi-ingress] ACCEPTED_DOMAINS et que l’auteur n’a aucune identité de quelque sorte que ce soit, une identité est engendrée en ligne, dans le protocole de la clé du destinataire lorsque le message est chiffré et dans le protocole de PREFER sinon. Une adresse qui n’a fait qu’apparaître dans une enveloppe ne déclenche jamais cela : un utilisateur qui n’emploie jamais la fonctionnalité n’a donc jamais de matériel de clé et n’y est jamais exposé. Sous la valeur par défaut ENABLE_PEP = yes, une clé est au contraire créée à la première soumission de l’expéditeur, demande ou non ; voir Le préréglage pEp ci-dessous.

Quelle que soit la manière dont elle est créée, le propriétaire de l’identité est le login passwd vers lequel les LOCAL_DOMAINS, TARGETS et RECIPIENT_DELIMITER de [pepsi-crypto] résolvent l’adresse, exactement comme pour pepsi-keys(1) ; aucun lorsque l’adresse est une adresse de rôle ou un domaine hébergé.

Dans les deux cas, la création automatique est refusée pour une adresse qui a déjà une ligne crypto_identity de quelque sorte que ce soit – une clé MUA enregistrée par l’utilisateur, une clé importée uniquement publique, ou une clé révoquée. Un mot-clé [sign] d’un utilisateur qui a apporté sa propre clé ne crée donc pas de seconde clé dans son dos ; un utilisateur qui veut une clé gérée par le serveur à côté de la sienne en demande une explicitement (la commande generate ci-dessous, pepsi-keys identity generate, ou la console web).

Une clé qui devrait être créée et ne peut pas l’être – la base de données refuse l’écriture, la génération elle-même échoue – est journalisée au niveau warn, et le message poursuit comme pour une adresse sans clé : sa politique décide s’il part non signé ou s’il est refusé. L’utilisateur n’a pas demandé que la clé soit créée, et prendre son courrier en otage d’un magasin de clés cassé est pire que de l’envoyer sans. (Un déploiement sans KEY_WRAP_SECRET ne crée aucune clé et n’est pas concerné.) L’enregistrement de la clé que le client d’un utilisateur annonce relève du meilleur effort de la même façon.

Dès qu’une clé de signature existe et ne peut pas être ouverte (une clé de chiffrement de clés illisible, un droit révoqué), le message n’est pas envoyé non signé : cette erreur est réessayée jusqu’au MAX_LIFETIME de l’étape (voir pepsi-dispatch(1)), comme toute autre défaillance de cet hôte, puisque envoyer sans signature masquerait un déploiement cassé. La section de l’étape, la politique crypto [pepsi], [pepsi-keydiscovery] et, lorsqu’une clé de chiffrement de clés est configurée, le trousseau construit à partir d’elle sont vérifiés au démarrage du worker : un élément qui ne fonctionne pas fait refuser au worker de démarrer (sortie 78), et le dispatcher retient la file jusqu’à sa correction.

85.1.6.1.3.2. Le préréglage pEp

ENABLE_PEP (par défaut yes) fait se comporter l’étape comme le projet pretty Easy privacy (pEp) : chaque expéditeur local reçoit une clé, le courrier est chiffré chaque fois que la clé d’un destinataire est connue ou découvrable, et la clé publique de l’expéditeur part avec chaque message. C’est un préréglage de valeurs par défaut, pas un mode contraignant : il change les valeurs par défaut d’autres options, et toute option écrite explicitement dans la section (ou dans une redéfinition pepsi.settings par adresse) l’emporte toujours.

Comportement

ENABLE_PEP = yes

ENABLE_PEP = no

Valeur par défaut de SIGN

encrypted-only

opportunistic

Création de clé

anticipée (première soumission)

sur demande, ou SIGN = always

Valeur par défaut de ENCRYPT

opportunistic

opportunistic

Valeur par défaut de AUTOCRYPT

yes

yes

ATTACH_KEYS_AS_FILES

no (indépendant)

no (indépendant)

Le préréglage ne concerne que le courrier que les utilisateurs envoient : sous celui-ci, le courrier en clair d’un utilisateur qui détient une clé n’est pas signé par Pepsi, et les expéditeurs reçoivent des clés qu’ils n’ont pas demandées. Ce que quiconque peut recevoir n’en dépend pas : le courrier entrant est traité de la même manière dans les deux cas.

Les formats standard sur le réseau sont inchangés : les messages sont du PGP/MIME et de l’Autocrypt ordinaires. Il n’y a ni en-têtes X-pEp-Version, ni enveloppement de message pEp 2.x, ni trustwords ou poignée de main, ni synchronisation de clés entre appareils, et le préréglage ne change pas PROTECT_HEADERS.

Création de clé anticipée. Sur chaque message soumis localement et validé – le passage en clair aussi bien que la route protégée, afin que le premier message porte déjà la nouvelle clé dans son en-tête Autocrypt: – l’étape génère une clé MTA OpenPGP pour l’adresse From: lorsque toutes ces conditions sont réunies :

  • ENABLE_PEP est activé pour le message (il est redéfinissable par adresse) ;

  • le domaine du From: fait partie de [pepsi-ingress] ACCEPTED_DOMAINS : une clé n’est jamais créée que pour une adresse dont le courrier entrant passe par ce déploiement, ce qui permet à pepsi-stage-decrypt(1) d’ouvrir les réponses chiffrées pour elle ;

  • [pepsi-crypto] AUTO_CREATE_IDENTITY est activé – le veto de l’opérateur, qu’aucun paramètre par adresse ne peut redéfinir ;

  • un [pepsi-crypto] KEY_WRAP_SECRET est configuré ;

  • l’adresse n’a aucune ligne crypto_identity de quelque sorte que ce soit, quels que soient son statut, son protocole ou sa garde. Une clé révoquée compte : si quelqu’un a révoqué une clé, Pepsi n’en crée pas discrètement une de remplacement.

Cela couvre toutes les routes de soumission, y compris les relais mynetworks et client-cert, de sorte qu’un Pepsi déployé comme proxy derrière un MTA qui authentifie les utilisateurs fonctionne de la même manière. Une application web qui envoie en tant que noreply@ d’un domaine pour lequel Pepsi ne reçoit pas de courrier n’obtient pas de clé. Deux workers voyant en même temps le même expéditeur nouveau stockent exactement une clé : le test et l’insertion s’exécutent sous un verrou par adresse dans la fonction SQL crypto_identity_create_if_absent, et le perdant jette ce qu’il a généré. Une clé créée automatiquement est marquée auto_created, publiée sur le WKD propre au déploiement, et jamais téléversée vers un serveur de clés à moins que son propriétaire ne le demande (vks_wanted = false ; voir pepsi-keys(1)).

SIGN = encrypted-only (la valeur par défaut du préréglage) ne signe un message que lorsque cette étape le chiffre, et alors à l’intérieur du texte chiffré. Le courrier en clair porte la clé de l’expéditeur (l’en-tête Autocrypt:, plus le fichier de clé lorsque ATTACH_KEYS_AS_FILES est activé) mais aucune signature, de sorte qu’un destinataire qui n’utilise pas la cryptographie ne voit jamais de signature.asc. Une demande explicite (X-Pepsi-Sign: yes, un mot-clé de sujet [sign]) signe toujours le texte en clair. Écrire SIGN = opportunistic conserve le comportement « tout signer » sous le préréglage.

Le premier message vers un nouveau correspondant peut encore attendre la découverte de clé jusqu’à DISCOVERY_TIMEOUT puis partir en clair ; le préréglage ne change pas la découverte.

85.1.6.1.3.3. Deux clés par utilisateur : clés MTA, clés MUA et face publique

Une adresse peut détenir deux sortes d’identité :

Clé MTA

custody = local : Pepsi détient la moitié privée (enveloppée dans crypto_identity.private_wrapped). Pepsi signe, déchiffre et annonce avec elle. Générée automatiquement, sur demande, ou importée avec sa moitié privée.

Clé MUA

custody = client : la propre clé de l’utilisateur, dont la moitié privée ne réside que dans son client de messagerie. La ligne reste à jamais uniquement publique. Pepsi chiffre pour elle et reconnaît le courrier entrant chiffré pour elle, mais ne peut jamais l’ouvrir, signer avec elle ni l’annoncer.

La face publique d’une adresse est la clé qu’un correspondant devrait utiliser : la clé MUA active la plus récente lorsque l’utilisateur en a une, sinon la clé MTA active principale qui détient encore sa moitié privée. L’en-tête Autocrypt:, le fichier de clé, le WKD du déploiement et le chiffrement local la lisent tous selon le même ordre, de sorte qu’ils ne peuvent pas diverger :

  • le WKD ne sert que la clé MUA lorsqu’il y en a une ;

  • le courrier d’un utilisateur local vers un autre est chiffré pour la face publique ;

  • Pepsi n’ajoute aucun en-tête Autocrypt: et ne joint aucun fichier de clé lorsque la face publique est une clé MUA. Le client de messagerie annonce sa propre clé, et si Pepsi en annonçait une autre sur une partie des messages du même utilisateur, l’état des clés des correspondants basculerait sous la règle « le plus récent l’emporte » d’Autocrypt.

La clé MTA n’est pas retirée lorsqu’une clé MUA apparaît. Elle continue de déchiffrer le courrier des correspondants qui l’utilisent encore, et elle signe toujours, à l’intérieur du texte chiffré, le courrier en clair que Pepsi chiffre pour le compte de l’utilisateur. Le courrier entrant chiffré pour une clé MUA est transmis au client de messagerie sans être ouvert ; voir pepsi-stage-decrypt(1).

85.1.6.1.3.4. Enregistrer la propre clé de l’utilisateur

Un message soumis localement dont l’adresse From: est dans un domaine servi peut enregistrer comme clé MUA la clé qu’utilise son client de messagerie. L’enregistrement ne dépend pas d”ENABLE_PEP. La clé est prise dans :

  1. l’unique en-tête Autocrypt: valide du message, dont le addr= doit être égal à l’adresse From: ; ou, à défaut,

  2. une partie application/pgp-keys jointe dont les User IDs nomment l’adresse – mais seulement pour la toute première clé de l’adresse. Une pièce jointe est un signal plus faible qu’un en-tête que gère le client de messagerie : une seconde clé n’est donc enregistrée qu’à partir d”Autocrypt:.

Cela n’a lieu que lorsque l’émetteur de la soumission peut parler au nom de l’adresse From:, ce qui est plus strict que d’être autorisé à envoyer en son nom : le message est arrivé d’un relais de confiance (state.origin.auth_method vaut mynetworks ou client-cert), ou d’un compte authentifié (sasl, peercred) pour lequel l’ingress a noté state.origin.from_bound = true – le From: est exactement une boîte aux lettres qui a correspondu à une entrée USERNAME_MAP concrète et sans joker du compte, ou à sa valeur par défaut login@HOSTNAME. Un compte à qui l’on a accordé un joker (*@example.org, *) peut envoyer au nom de ses collègues mais n’enregistre jamais de clé pour eux.

Une empreinte que l’adresse possède déjà, retirée ou non, n’est pas enregistrée de nouveau, de sorte qu’une clé que l’utilisateur a retirée ne revient pas parce qu’un ancien appareil l’envoie encore. Une nouvelle clé MUA est stockée avec vks_wanted = false et devient aussitôt la face publique. Lorsque l’adresse avait déjà une autre identité, l’utilisateur reçoit un avis à expéditeur nul (Auto-Submitted: auto-generated, le modèle key-registered.<lang>.body) à RESPONSE_STAGE, qui nomme l’empreinte et indique comment la retirer – afin qu’une clé ajoutée avec un mot de passe volé ne passe pas inaperçue. Sans RESPONSE_STAGE, la clé est quand même enregistrée mais aucun avis ne peut être envoyé. Le message lui-même poursuit son chemin inchangé, hormis l’hygiène habituelle des en-têtes.

Une adresse qui a enrôlé un second facteur (otp enroll, ci-dessous) n’enregistre rien de cette manière : un message ne peut pas porter de code, et ce chemin est exactement celui qu’emprunterait un mot de passe volé. Son propriétaire enregistre plutôt une nouvelle clé avec register CODE.

85.1.6.1.3.5. Courrier que le client a déjà protégé

Seule la couche la plus externe du message soumis est consultée :

Déjà chiffré

PGP/MIME, un application/pkcs7-mime S/MIME enveloppé, un -----BEGIN PGP MESSAGE en ligne, une signature enveloppant du texte chiffré, ou tout ce qu’aucun classificateur n’a pu identifier. L’étape ne chiffre, ne signe et ne joint rien, et n’ajoute aucun en-tête Autocrypt:. L’hygiène des en-têtes et l’enregistrement de clé MUA ci-dessus s’exécutent quand même. state.crypto.out.client_protected = true est noté.

Déjà signé (multipart/signed, PGP en ligne signé)

L’étape peut encore le chiffrer, sous la forme encrypted(signed), mais n’ajoute aucune signature propre ni aucun fichier de clé en clair.

Un message chiffré transféré, joint en message/rfc822, est la protection de quelqu’un d’autre et ne compte pas.

85.1.6.1.3.6. Joindre la clé sous forme de fichier

ATTACH_KEYS_AS_FILES (par défaut no, indépendant du préréglage) joint aussi la clé publique de l’expéditeur à la manière OpenPGP classique, en plus de l’en-tête Autocrypt:, pour les correspondants dont le client ne lit pas Autocrypt. La partie est un application/pgp-keys nommé OpenPGP_0x<long key ID>.asc (les 16 derniers chiffres hexadécimaux de l’empreinte, le nom qu’utilise Thunderbird), contenant le même certificat minimal que porte l’en-tête, en armure ASCII.

  • Sur la route de chiffrement, elle va à l’intérieur du texte chiffré, placée comme le gossip.

  • Sur la route en clair, le message validé est enveloppé dans un nouveau multipart/mixed contenant l’entité d’origine et la partie de clé, ou la clé devient une partie de plus d’un multipart/mixed existant. L’étape s’exécute avant pepsi-stage-dkim-sign(1), de sorte que la signature DKIM du déploiement couvre le résultat.

  • Elle est jointe jusqu’à ce que chaque destinataire de la ligne détienne la clé de manière prouvée (l’enregistrement pepsi.peer_has_own_key que pepsi-stage-autocrypt-learn(1) écrit lorsqu’un correspondant envoie une réponse chiffrée et signée). L’enregistrement est indexé sur l’empreinte, de sorte qu’une nouvelle clé recommence à être jointe. Une ligne en clair à plusieurs destinataires n’est pas scindée pour cela : la clé part vers tous si l’un des destinataires n’a pas de preuve.

  • Jamais lorsque la face publique est une clé MUA, jamais sur un message que le client a chiffré, et pas sur du texte en clair que le client a signé. Un message portant déjà le fichier de la clé n’en reçoit pas un second.

state.crypto.out.key_attached = true note que le fichier est parti.

85.1.6.1.3.7. Gérer vos clés par e-mail

Lorsque RESPONSE_STAGE est défini et que KEYS_CONTROL_LOCAL_PART ne vaut pas none, un message soumis localement dont le seul destinataire est <KEYS_CONTROL_LOCAL_PART>@<served domain> (par défaut pepsi-keys@) est une commande de clé. Il est consommé – supprimé, jamais relayé – et reçoit une réponse à RESPONSE_STAGE avec le modèle keys.<lang>.body, qui liste les clés de l’adresse. La commande est le Subject:, insensible à la casse, les éventuels préfixes Re:/Fwd: étant ignorés :

status

Liste les clés de l’adresse : sorte (gérée par le serveur ou propre clé de l’utilisateur), statut, état sur le serveur de clés, et laquelle est donnée aux correspondants.

generate

Crée une clé MTA OpenPGP gérée par le serveur. Toujours permis sur demande, même à côté de la propre clé de l’utilisateur ou d’une clé révoquée. Son téléversement vers le serveur de clés est demandé lorsque [pepsi-keys] VKS_PUBLISH est activé.

register

Enregistre comme propre clé MUA de l’utilisateur la clé de l’en-tête Autocrypt: de ce message, ou son fichier de clé joint.

publish [FPR]

Demande un téléversement vers le serveur de clés (et une publication sur le WKD), par défaut de la face publique. Seule une clé OpenPGP active peut être téléversée. Le téléversement lui-même est effectué par la prochaine exécution de pepsi-keys identity publish --retry, et ne peut pas être annulé.

retire FPR

Révoquer une clé ; c’est aussi ainsi qu’une clé de MUA enregistrée par erreur est supprimée.

otp enroll

Enrôler (ou remplacer) un second facteur pour ces commandes : un secret TOTP (RFC 6238, SHA-1, six chiffres, pas de 30 secondes). La réponse le porte une seule fois – sous forme d’URI otpauth://, en base32, et sous forme de code QR joint (second-factor.png) pour une application d’authentification – et devrait être supprimée une fois le secret dans l’application. Remplacer un facteur existant nécessite son code actuel. Étant la seule copie du secret, la réponse est validée dans une même transaction avec le secret qu’elle porte, et rendue avant l’un comme l’autre : si elle ne peut être construite ou mise en file, rien n’est stocké, le second facteur précédent reste tel quel, et la commande est réessayée.

otp remove

Supprimer le second facteur ; nécessite son code actuel. otp seul (ou otp status) équivaut à status.

Avec un second facteur enrôlé, chaque commande sauf status doit porter le code actuel de l’application : un mot d’exactement six chiffres n’importe où dans le Subject: (register 123456 ; otp=123456 est aussi accepté). Une commande sans code est refusée et n’est pas comptée. Un code erroné, ou un code déjà utilisé (un code n’est valable qu’une fois), compte comme un échec et un code correct remet le compteur à zéro ; après dix échecs consécutifs, le second facteur est verrouillé et chaque commande sauf status est refusée jusqu’à ce que l’opérateur le réinitialise (pepsi-keys otp reset). Le secret est enveloppé sous la clé de chiffrement de clés du magasin de clés et lisible par le seul rôle pepsi-crypto ; voir pepsi-keys(1). La réponse à status indique si l’adresse en a un et s’il est verrouillé.

Une commande reçoit une seule réponse. Un message de commande dont la réponse est déjà en file (une passe antérieure l’a exécutée et a été interrompue avant de le consommer) est seulement consommé, de sorte qu’un réessai ne répète jamais une commande – en particulier ne crée jamais un second secret derrière la réponse qui porte le premier. Une commande qui échoue pour une raison propre à ce serveur reçoit une réponse générique « réessayez plus tard » ; l’erreur elle-même est dans le journal, non dans le courrier.

FPR est l’empreinte complète, ou au moins ses 16 derniers chiffres hexadécimaux, écrite en un seul mot (un préfixe 0x est accepté) ; comptant au moins 16 chiffres, elle n’est jamais confondue avec un code. list, upload et revoke sont acceptés pour status, publish et retire, et 2fa/totp pour otp ; tout autre sujet reçoit en réponse la liste des clés et les commandes valides. La commande agit sur l’adresse From:, et seulement lorsque l’auteur de la soumission peut parler en son nom (voir Enregistrer la propre clé de l’utilisateur) ; sinon, et pour une adresse d’un domaine que le déploiement ne sert pas, la réponse indique que la demande a été refusée. Sans RESPONSE_STAGE, il n’y a nulle part où répondre : cette interface est alors désactivée et un tel message est transmis comme n’importe quel autre.

85.1.6.1.3.8. Choisir la clé du destinataire

Normalement la clé provient du cache pepsi.peer_key, source la mieux classée d’abord et sous réserve de MIN_TRUST — voir pepsi.conf(5) et pepsi-keydisc(1).

Il y a une exception, et c’est une limite de sécurité plutôt qu’une optimisation. Si ce déploiement détient une identité ``active`` pour le destinataire, cette clé est utilisée et aucune ligne ``peer_key`` pour l’adresse n’est consultée — quel que soit son rang et quelle qu’ait été son arrivée. state.crypto note la source comme own.

La raison est que peer_key est en partie alimenté par la moisson du courrier entrant, et que la moisson s’indexe sur l’en-tête From: écrit par l’expéditeur. L’authentification entrante est délibérément fail-open : un message prétendant venir de l’un des propres utilisateurs de ce déploiement peut donc semer pour cet utilisateur une clé de pair contenant la clé d’un inconnu ; sans l’exception, le courrier d’un utilisateur local vers un autre serait alors chiffré vers la clé plantée et ressemblerait exactement à un succès.

La règle tient au fait de détenir une clé, non au fait de servir l’adresse. Un utilisateur qui exploite son propre client OpenPGP et n’a jamais téléversé de clé ici n’a pas d’identité, et sa vraie clé est une clé que cet hôte ne peut apprendre que par la découverte, la moisson ou le gossip — l’exception ne s’applique donc pas à lui. Voir Les clés de nos propres utilisateurs ne sont pas découvertes dans le manuel pour le compromis complet, y compris son coût : une signature faite avec la clé personnelle distincte d’un utilisateur cesse de vérifier dès qu’une identité est détenue pour cette adresse.

La même préférence s’applique aux champs de gossip ci-dessous : un sujet qui est l’un des nôtres est présenté avec la clé que nous lui avons délivrée, jamais avec une clé plantée par quelqu’un.

85.1.6.1.3.9. Issues par destinataire

Par destinataire, exactement l’une de :

encrypted

Une clé utilisable a été trouvée (sous réserve de MIN_TRUST) et le message a été chiffré vers elle. Le conteneur réellement émis est noté, avec l’indication d’une éventuelle rétrogradation depuis l’AES-256-GCM par défaut.

signed-only

Aucune clé de destinataire utilisable, la politique autorise du courrier ordinaire, et la signature de l’auteur a été appliquée.

cleartext

Ni signé ni chiffré.

secure-link

Acheminé vers SECURE_LINK_STAGE au lieu d’être envoyé.

bounced

Le chiffrement était exigé et n’a pas pu être appliqué ; acheminé vers BOUNCE_STAGE avec un statut étendu RFC 3463 5.7.0. Sans BOUNCE_STAGE, le message est mis en échec plutôt qu’envoyé sans protection.

oversize

Le message était plus grand que MAX_SIZE et a pris la route ON_OVERSIZE.

Lorsque les destinataires d’un message ne partagent pas tous une même issue, l’étape les scinde à raison d’un par ligne (le state.dsn.rcpt de chaque ligne étant découpé en phase avec son rcpt_to) et rend le message à la file d’attente ; chaque ligne réentre ensuite seule dans cette étape. Un message dont les destinataires partagent tous une même issue — le cas courant « personne n’a de clé, envoyer comme d’habitude » — n’est jamais scindé.

Chaque issue qui met du contenu non protégé sur le fil alors que le chiffrement était souhaité est journalisée en WARN et consignée par destinataire dans state. Envoyer en clair n’est jamais une issue silencieuse.

85.1.6.1.3.10. Annonce de clé Autocrypt

À moins que AUTOCRYPT ne soit désactivé, chaque message soumis localement quitte cette étape en portant un en-tête Autocrypt: annonçant la clé OpenPGP propre à l’auteur du From:

Autocrypt: addr=alice@example.org; prefer-encrypt=mutual;
 keydata=xjMEZ8kAABYJKwYBBAHaRw8BAQdA…

Cela se produit sur chaque message — signé, chiffré, acheminé vers le lien sécurisé ou en clair. Le cas du passage tel quel est celui qui compte : un correspondant doit pouvoir apprendre la clé depuis du courrier ordinaire, car tant qu’il ne l’a pas, il n’y a rien avec quoi chiffrer. N’annoncer que sur du courrier déjà protégé dirait la clé exactement à ceux qui la connaissent déjà.

La clé annoncée est la clé publique transférable minimale d’Autocrypt Level 1 §2.1.1 — clé primaire, identifiant utilisateur primaire, son auto-signature, une sous-clé de chiffrement de transport et sa signature de liaison, et rien d’autre. Les identifiants utilisateur supplémentaires, la sous-clé de signature et toute certification tierce sont retirés : l’en-tête voyage sur chaque message, sa taille est donc payée encore et encore, et republier des certifications publierait le graphe social de l’expéditeur comme effet de bord de l’envoi de courrier.

Cinq règles le bornent :

  • OpenPGP uniquement. Autocrypt est défini sur une clé publique transférable OpenPGP et n’a pas de forme S/MIME : un expéditeur ne détenant qu’une identité X.509 n’annonce donc rien.

  • Uniquement du courrier soumis localement, comme tout le reste de ce que fait cette étape.

  • Jamais par-dessus un en-tête existant. Un véritable client (Thunderbird, K-9, Delta Chat) peut avoir posé son propre champ Autocrypt: nommant la clé dont il détient la moitié secrète. Celui-là vaut mieux que le nôtre et est laissé tranquille — et un second champ serait pire qu’inutile, car Autocrypt Level 1 écarte purement et simplement un message qui en porte deux.

  • La clé doit encore être ouvrable ici. Seule une identité qui détient encore son matériel privé est annoncée : le chiffré envoyé vers une clé annoncée est donc toujours du chiffré que pepsi-stage-decrypt(1) peut ouvrir.

  • Seulement la face publique. Lorsque l’utilisateur a enregistré sa propre clé de MUA, c’est elle la face publique et Pepsi n’annonce rien ; le client de messagerie annonce lui-même sa clé. Un message que le client a déjà chiffré ne reçoit pas non plus d’en-tête.

prefer-encrypt=mutual n’est émis que lorsque l”ENCRYPT effectif de l’étape vaut required — y compris lorsque cela vient d’une redéfinition pepsi.settings par adresse, de sorte qu’un utilisateur puisse l’annoncer sans que le déploiement le fasse. Selon Autocrypt Level 1 §2.4, le client d’un correspondant n’active le chiffrement par défaut que lorsque les deux extrémités disent mutual ; le revendiquer sous opportunistic, où ON_NO_KEY = cleartext dit que le texte clair est une issue acceptable, annoncerait une préférence que ce déploiement n’applique pas. L’attribut est alors omis, ce que le Level 1 lit comme nopreference : le correspondant note tout de même la clé et se voit toujours proposer le chiffrement, simplement pas par défaut. La valeur suit le mode configuré plutôt que celui résolu pour un message isolé, de sorte qu’un mot-clé de sujet [secure] ne fasse pas osciller l’état de clé d’un correspondant au gré des lignes de sujet de nos utilisateurs.

Comme cette étape s’exécute avant pepsi-stage-dkim-sign(1), l’en-tête est couvert par la signature DKIM propre au déploiement.

85.1.6.1.3.11. Gossip de clés Autocrypt

AUTOCRYPT_GOSSIP (NO par défaut) active l’autre moitié d’Autocrypt Level 1 : des champs Autocrypt-Gossip: annonçant les clés des autres destinataires. Ils répondent au cas que l’en-tête ci-dessus ne peut pas traiter. Alice écrit à Bob et Carol, qui n’ont jamais correspondu. Tous deux apprennent la clé d’Alice depuis son en-tête Autocrypt:, mais aucun n’apprend celle de l’autre — de sorte que la réponse à tous de Bob ne peut pas être chiffrée vers Carol tant qu’un message ultérieur ne les présente pas par hasard. Un message avec gossip comble cette lacune

Autocrypt-Gossip: addr=carol@example.org;
 keydata=xjMEZ8kAABYJKwYBBAHaRw8BAQdA…

C’est désactivé par défaut parce que c’est un acte d’une autre nature qu”AUTOCRYPT : celui-ci publie une clé que son propriétaire a demandé à ce déploiement de détenir, tandis que celui-là redistribue le matériel de clé de tiers — mis en cache ici pour notre propre usage — à des correspondants qui ne l’ont jamais demandé. C’est la décision d’un opérateur.

Trois règles découlent de la spécification, et une de la forme propre à cette étape.

  • Uniquement à l’intérieur d’un chiffré. Le Level 1 §5.3 place ces champs dans la section d’en-têtes de la partie chiffrée, et pas par coquetterie : un champ Autocrypt-Gossip: extérieur publierait l’ensemble des destinataires d’un message chiffré à chaque relais du chemin, ce qui est l’essentiel de ce pour quoi le chiffrement existait. Il n’y a donc pas de gossip sur le passage en clair, aucun sur un message seulement signé, et aucun dans un conteneur S/MIME — Autocrypt est défini sur OpenPGP et n’a pas de forme S/MIME, de sorte qu’un champ de gossip y serait une redistribution vers un endroit où aucun client ne regarde.

  • Plusieurs sont attendus. Un champ par destinataire présenté, dans l’ordre To:/Cc: — l’inverse d”Autocrypt:, où un second champ fait écarter le message par un analyseur Level 1.

  • Pas de ``prefer-encrypt``. L’attribut énonce la politique de l”expéditeur, et ces champs parlent pour quelqu’un d’autre. En affirmer un au nom d’un correspondant est une affirmation que ce déploiement n’a pas qualité à faire, et le Level 1 l’exclut de la forme gossip pour la même raison.

  • Une clé en cache ou rien. Un destinataire visible pour lequel ce déploiement ne détient aucune clé OpenPGP utilisable est laissé hors de l’ensemble. L’étape n’effectue aucune découverte pour son compte et ne met jamais un message en pause pour aller chercher la clé d’un tiers : une présentation est une courtoisie, et retarder le courrier de l’expéditeur pour en faire une n’en serait pas une. Le plancher est le plus strict de MIN_TRUST et de GOSSIP_MIN_TRUST (par défaut owner-confirmed) : une clé qui n’aurait pas été assez bonne pour chiffrer vers elle n’est pas assez bonne pour être transmise non plus, et par défaut une clé que ce déploiement a simplement moissonnée depuis du courrier entrant — ou qui lui a elle-même été gossipée — n’est pas republiée du tout. Présenter à un correspondant une clé dont personne ne s’est porté garant est une affirmation plus forte que de l’employer soi-même.

Avertissement

Une copie carbone invisible n’est jamais gossipée. Les adresses des sujets proviennent des champs To: et Cc: analysés du message et de nulle part ailleurs. La liste des destinataires d’enveloppe — qui contient bel et bien les destinataires en Bcc, ce qui est tout le sens d’une copie invisible — n’est consultée que pour retirer une adresse de l’ensemble (le destinataire propre de cette copie, et l’auteur du From:).

Un destinataire en Bcc ne peut donc apparaître dans aucune copie du message, et la garantie ne dépend pas d’un champ Bcc: retiré en amont : le champ n’est pas lu du tout. Le Level 1 §5.3 exige la même chose d’un client récepteur, qui ignore un en-tête de gossip dont l”addr n’est pas un destinataire To:/Cc: — un tel en-tête divulguerait donc l’adresse sans rien accomplir.

L’inverse est délibéré. La copie propre d’un destinataire invisible porte bel et bien du gossip sur les destinataires To:/Cc: : il a reçu ces champs d’en-tête comme tout le monde, cela ne lui apprend donc rien qu’il ne puisse déjà lire, et le lui refuser casserait la réponse à tous précisément pour le destinataire le moins susceptible d’avoir correspondu avec les autres auparavant.

Un message comptant plus de 50 destinataires visibles ne reçoit aucun gossip. Chaque clé présentée est répétée dans le texte chiffré propre de chaque destinataire : le coût croît donc avec le carré du nombre d’adresses, et à cette taille le message est une diffusion à laquelle personne ne répondra à tous. En présenter cinquante arbitraires serait un choix silencieux sans règle défendable derrière lui : l’étape n’en présente donc aucun et le dit dans le journal.

85.1.6.1.3.12. Négociation de l’algorithme de contenu

Pour OpenPGP, le certificat du destinataire indique s’il sait lire le SEIPDv2/AEAD de la RFC 9580 ; la majeure partie du parc installé (GnuPG 2.4 et antérieurs) ne le sait pas. Sous [pepsi] CRYPTO_ALLOW_DOWNGRADE = negotiate (la valeur par défaut), l’étape n’émet du SEIPDv1+MDC que lorsque la clé propre du destinataire prouve qu’il ne peut pas faire mieux, le journalise en INFO et le note — une rétrogradation est un fait sur le correspondant, non un échec. Sous no, le même destinataire est refusé et tombe dans ON_NO_KEY ; voir la mise en garde sous cette option.

S/MIME n’a rien contre quoi négocier — un certificat X.509 n’annonce aucune capacité de chiffrement de contenu — il émet donc de l”AuthEnvelopedData RFC 5083 à moins que CRYPTO_ALLOW_DOWNGRADE = yes ne force l’AES-256-CBC. Sur un Pepsi installé, c’est la valeur par défaut livrée : ${DATADIR}/config.d/thunderbird.conf règle yes, parce que Thunderbird 140.12.0esr ne sait pas lire l”AuthEnvelopedData et échoue en silence lorsqu’il essaie. Supprimer ce fichier restaure le negotiate compilé, et avec lui l’AEAD. Le comportement OpenPGP ci-dessus est le même sous l’une ou l’autre valeur — la demande de conteneur est auto, que les deux résolvent par destinataire — de sorte que la valeur par défaut S/MIME ne coûte rien à OpenPGP. Voir pepsi.conf(5).

85.1.6.1.4. Configuration

L’étape lit sa propre section [stage-<name>] (PROGRAM = pepsi-stage-encrypt, un NEXT_STAGE obligatoire — chacune des issues de cette étape est un saut vers l’avant — et BOUNCE_STAGE, que pepsi-setup(1) exige à son tour dès que ON_NO_KEY/ON_OVERSIZE se résout en bounce, plus ENABLE_PEP, SIGN, ENCRYPT, ATTACH_KEYS_AS_FILES, KEYS_CONTROL_LOCAL_PART, RESPONSE_STAGE, PREFER, ON_NO_KEY, ON_OVERSIZE, MIN_TRUST, SUBJECT_KEYWORDS_*, STRIP_REQUEST_HEADERS, PROTECT_HEADERS, AUTOCRYPT, AUTOCRYPT_GOSSIP, GOSSIP_MIN_TRUST, MAX_SIZE, SECURE_LINK_STAGE, DISCOVERY, DISCOVERY_TIMEOUT), toutes documentées dans pepsi.conf(5). Ce sont des options de comportement, redéfinissables par adresse via la table pepsi.settings.

La politique cryptographique — choix des algorithmes, permission de rétrogradation, acceptation des condensats faibles, tailles de clé minimales — réside dans les options CRYPTO_* de [pepsi] et dans [pepsi-crypto]. Elles relèvent du seul administrateur système et ne sont délibérément pas accessibles depuis pepsi.settings ni pepsi-stage-edit-settings(1).

85.1.6.1.5. Privilège

Le binaire est installé setuid pepsi-crypto, mode 4750 pepsi-crypto:pepsi, et n’est donc pas plié dans le binaire multi-appel pepsi unifié.

Les deux moitiés de la limite de garde des clés reposent sur cette identité. Le rôle de base de données pepsi-crypto est le seul à qui crypto_identity.private_wrapped est accordé, et l’authentification par pair de PostgreSQL se fonde sur l”uid effectif ; la clé de chiffrement de clés réside dans secrets.d/pepsi-crypto.secret, mode 0640, appartenant au même compte. Un binaire setgid gagnerait le groupe — de quoi lire le fragment — et s’authentifierait tout de même auprès de la base de données en tant que pepsi, précisément le rôle à qui la colonne est refusée.

Le groupe dans le mode restreint l’exécution au compte du dispatcher et à root ; le programme refuse en outre de s’exécuter si son uid réel n’est ni root ni l’un des comptes pepsi, pepsi-crypto et pepsi-owner. Cette vérification à l’exécution ne s’applique que lorsque le binaire porte réellement le bit setuid — un build installé sans lui (un arbre de développement, cargo test) laisse n’importe qui l’exécuter, au motif qu’un tel appelant atteint la base de données en son propre nom et que les droits de PostgreSQL font alors autorité. -c est accepté de ces appelants (la forme de lancement documentée du dispatcher est PROGRAM -c cfg worker) et l’environnement est assaini avant le chargement de la configuration, car la couche de configuration est pilotable par XDG_CONFIG_HOME, HOME, PATH et la famille PG*.

85.1.6.1.6. État

Entrées : state.local_origin (la barrière), state.dsn (honoré et découpé en parallèle de toute scission de destinataires), state.origin.auth_method et state.origin.from_bound (si l’auteur de la soumission peut parler au nom de l’adresse From:, pour l’enregistrement de clés et les commandes de clés par e-mail).

Sorties : state.crypto.out — si le message a été signé, par quelle identité, et une entrée par destinataire portant l’issue, le protocole, l’empreinte, la source de clé, le rang de confiance, l’algorithme de contenu et l’indicateur de rétrogradation ; key_attached = true lorsque la clé de l’expéditeur est partie sous forme de fichier, et client_protected = true lorsque le client à l’origine de la soumission avait déjà chiffré le message et que l’étape n’y a pas touché. Également state.encrypted = true lorsque le message d’un destinataire a été chiffré, que lit pepsi-stage-auto-whitelist(1). Sur un rebond, state.bounce. La disposition de l’état est décrite dans pepsi.state(7).

Transitions : NEXT_STAGE pour un message envoyé (chiffré, seulement signé ou en clair) ; SECURE_LINK_STAGE ou BOUNCE_STAGE pour un destinataire qui n’a pas pu être protégé ; paused pendant l’attente de la découverte de clés ; pending à cette même étape après une scission de destinataires. Une commande de clé adressée à l’adresse de contrôle est supprimée (finish) une fois sa réponse injectée à RESPONSE_STAGE ; les avis d’enregistrement de clé y sont injectés aussi, comme nouveaux messages à expéditeur nul.

85.1.6.1.7. Commandes

worker

Exécuté comme worker persistant du dispatcher, lisant les identifiants de message sur l’entrée standard et écrivant une ligne de statut par message. C’est le seul mode ; il n’y a pas de forme ponctuelle. Pour traiter un seul message à la main, envoyez son identifiant dans un tube

echo 1234 | pepsi-stage-encrypt -c /etc/pepsi/pepsi.conf worker

85.1.6.1.8. Code de sortie

0

Le worker s’est terminé proprement (entrée standard fermée).

1

Une erreur s’est produite (mauvaise configuration, magasin de clés illisible, ou erreur de base de données). La raison est écrite dans le journal.

78

Le worker a refusé de démarrer : sa configuration ou son trousseau ne fonctionne pas. Le dispatcher remet en file ce qu’il lui avait confié et réessaie l’étape plus tard.

85.1.6.1.9. Voir aussi

pepsi-stage-dkim-sign(1), pepsi-stage-srs(1), pepsi-keys(1), pepsi-keydisc(1), pepsi-stage-auto-whitelist(1), pepsi-stage-bounce(1), pepsi-dispatch(1), pepsi-setup(1), pepsi.conf(5), pepsi.state(7)

85.1.6.1.10. Bogues

Signalez les bogues au gestionnaire de tickets de Pepsi.