11. Gestion des clés¶
La cryptographie de bout en bout — OpenPGP et S/MIME — a besoin d’un endroit où garder les clés. Pepsi les garde dans la base de données, dans trois tables qui forment ensemble le magasin de clés, et les gère avec un seul programme, pepsi-keys. Ce chapitre explique ce qui est stocké, comment la moitié privée est protégée, qui a le droit de l’atteindre, et les deux décisions qui surprennent le plus souvent : pourquoi le matériel de signature et celui de chiffrement sont séparés, et pourquoi une adresse qui ne fait que recevoir du courrier finit sans aucune clé.
Le magasin de clés n’est pas le magasin des clés DKIM/ARC. Celles-ci sont des clés par domaine, conservées comme fichiers sous [pepsi] KEY_DIR et lues par les étapes de signature ; voir Installation. Ce qui suit concerne le matériel de bout en bout par adresse, dont le cycle de vie est entièrement différent.
11.1. Trois sortes de matériel¶
pepsi.crypto_identity— nos propres adressesLes paires de clés des adresses que ce déploiement sert, moitié privée comprise, enveloppées au repos. C’est ce qui signe le courrier sortant et déchiffre le courrier entrant. Chaque ligne consigne l’adresse, le protocole (
openpgpousmime), ce dont le matériel est capable, son algorithme et son empreinte, le matériel public lui-même, le matériel privé enveloppé et l’identifiant de la clé qui l’a enveloppé, ainsi que les colonnes de cycle de vie (status,is_primary,published,expires_at,revoked_at,private_purged_at,vks_wanted). Elle contient aussi les clés propres des utilisateurs – des lignes publiques uniquement, dont la moitié privée vit dans leur client de messagerie (custody = client; voir Deux clés par utilisateur).pepsi.peer_key— les adresses des autresLes clés publiques et certificats des correspondants distants, mis en cache quelle qu’en soit la provenance. C’est ce qui chiffre le courrier sortant et vérifie les signatures entrantes. Chaque ligne porte la
sourcedont elle vient, l’indication de si la consultation qui l’a produite était validée par DNSSEC, le dernier verdict de validité rendu à son sujet, et une échéance de cache.pepsi.ca_trust— les ancres de confianceLes certificats d’autorité qu’une chaîne S/MIME entrante doit atteindre pour que sa signature compte comme digne de confiance. Pepsi ne livre aucun jeu d’ancres par défaut : une signature qui remonte jusqu’à une autorité que personne n’a choisie est rapportée comme non digne de confiance plutôt que mauvaise, de sorte qu’un magasin vide se dégrade en « nous ne pouvons pas nous en porter garants » plutôt qu’en « ceci est falsifié », et un opérateur qui ajoute une ancre prend une décision au lieu d’en hériter une.
11.2. Garde : comment une clé privée est stockée¶
La moitié privée d’une identité vit dans la base de données, mais jamais en clair. Elle est scellée en AES-256-GCM sous une clé de chiffrement de clés (KEK) dérivée d’un secret qui vit uniquement dans le système de fichiers, dans un fragment secrets.d que nomme [pepsi-crypto] KEY_WRAP_SECRET.
La propriété que cela procure :
La base seule ne livre jamais de clé privée. Un vidage dérobé, un réplicat, une bande de sauvegarde ou un point d’appui par injection SQL dans un composant sans rapport produisent du texte chiffré et rien d’autre. La clé qui l’ouvre est un second artefact, gardé différemment, et n’est jamais une valeur de la base.
Le blob stocké se compose d’un octet de version, d’un nonce aléatoire de 96 bits et du texte chiffré AES-GCM. L’octet de version existe pour qu’une construction future puisse coexister avec celle-ci ; une version non reconnue est une erreur, jamais un repli silencieux.
Les données authentifiées supplémentaires lient le texte chiffré à la ligne à laquelle il appartient — l’adresse, le protocole et la finalité, préfixés par leur longueur. Sans cette liaison, un attaquant disposant d’un accès en écriture à la table mais pas de la KEK pourrait déplacer la clé de signature enveloppée d’Alice dans la ligne de Bob et faire signer le courrier de Bob avec elle par le pipeline. Les AAD sont authentifiées mais non stockées : un tel blob échoue donc simplement à s’ouvrir au mauvais endroit, exactement comme un blob altéré.
La KEK elle-même est dérivée du secret configuré par HMAC-SHA-256 sous une étiquette fixe, en y mêlant l’identifiant de clé. Deux conséquences s’ensuivent : le secret peut être n’importe quelle chaîne (une phrase secrète, du base64, de l’hexadécimal — la dérivation s’en moque), et chaque KEY_WRAP_KEY_ID distinct produit une clé indépendante de 256 bits. Il n’y a délibérément aucune phrase secrète au démarrage : le redémarrage sans surveillance et l’activation par socket doivent continuer de fonctionner.
Avertissement
pepsi-setup refuse un KEY_WRAP_SECRET de moins de 16 caractères. C’est l’unique clé qui ouvre chaque clé privée stockée du déploiement ; engendrez-la depuis une véritable source d’entropie plutôt que d’en taper une.
11.3. La frontière de privilèges¶
L’enveloppement est une moitié du modèle de garde. L’autre moitié est une frontière de privilèges dans la base, provisionnée par pepsi-setup et réaffirmée à chaque pepsi-setup run :
un rôle de connexion pepsi-crypto est créé avec l’accès ordinaire du pipeline (
SELECT/INSERT/UPDATE/DELETEsur le schéma, les séquences, les fonctions) plus la colonnecrypto_identity.private_wrapped– le seul rôle à qui elle est accordée, hormis le propriétaire du schémapepsi-owner, qui peut la lire en tant que propriétaire mais ne peut pas lire la clé de chiffrement de clés, de sorte que rien d’autre ne détient les deux ;pepsi(le dispatcher et chaque worker d’étape) etpepsi-httpdse voient révoquer leur droit de niveau table surcrypto_identity, remplacé par un droit de niveau colonne listant chaque colonne saufprivate_wrapped;pepsi-ingressetpepsi-telemetryn’ont aucun accès à la table (voir La séparation des privilèges).
Une compromission d’une étape sans rapport — relais, rebond, alias — livre donc la moitié publique de chaque identité et rien de plus, même avec un accès complet à la base de données sous ce rôle. La liste des colonnes est relue depuis information_schema plutôt que codée en dur, de sorte qu’une colonne ajoutée par un changement ultérieur soit couverte automatiquement et que le droit ne puisse pas devenir discrètement périmé face au schéma.
DELETE n’a pas de granularité par colonne dans PostgreSQL et reste entier : une étape capable de supprimer une ligne d’identité peut refuser le service, mais elle ne peut toujours pas lire une clé.
Important
L’authentification par pair de PostgreSQL se fonde sur l’uid effectif, non sur le gid. Un bit setgid accorde l’appartenance à un groupe — ce que teste le mode du fragment de KEK — mais pas une identité de base de données. Un programme qui doit être le rôle pepsi-crypto doit être ce compte : s’exécuter sous lui, ou devenir setuid vers lui. pepsi-keys fait exactement cela, endossant l’identité le temps de chaque appel à la base de données et la rendant ensuite, sous la même forme que pepsi-whitelist emploie pour son propre compte.
Atteindre une clé privée exige donc les deux : le rôle de base de données et la clé de chiffrement de clés. Ni l’un ni l’autre ne suffit seul, et ils sont gardés par des mécanismes différents — un droit PostgreSQL et un mode de fichier.
11.3.1. Qui peut gérer quoi¶
Une identité est indexée sur l’adresse mise en minuscules, et le login passwd auquel cette adresse appartient est consigné à côté lorsque les règles de localité ([pepsi-crypto] LOCAL_DOMAINS, RECIPIENT_DELIMITER, TARGETS, nommées et dotées des mêmes valeurs par défaut que les options de la remise locale) en résolvent un. Ce sont les seules options qui en décident : pepsi-keys, l’applicateur de configuration ainsi que la création automatique et l’enregistrement de clés de l’étape de chiffrement lisent tous [pepsi-crypto], de sorte qu’une identité a le même propriétaire quel que soit celui d’entre eux qui l’a créée. Cette colonne est la règle d’accès : un appelant non privilégié de pepsi-keys ne peut voir et modifier que les identités dont le login est le sien, pris dans l’entrée passwd de l’uid réel. Une identité sans compte propriétaire — une adresse de rôle, un domaine hébergé, un déploiement placé devant un Exchange — n’appartient à personne en particulier et est réservée à l’opérateur ; traiter « sans propriétaire » comme « à tout le monde » rendrait chaque adresse de rôle inscriptible par chaque utilisateur local.
Les clés de pairs et les ancres de confiance n’ont aucun espace de noms par utilisateur. Une clé de pair est la clé de quelqu’un d’autre, mise en cache pour tout le déploiement, et le magasin de confiance est de la politique ; les deux sont réservés à l’opérateur, listage compris.
pepsi-keys est livré sans bit setuid, contrairement à pepsi-whitelist. Un binaire capable d’ouvrir chaque clé privée d’un déploiement est une prise bien plus grosse qu’une unique table de liste blanche : par défaut, seul l’opérateur l’exécute. Un site qui veut que les utilisateurs gèrent leurs propres identités l’installe setuid pepsi-crypto, et la règle de propriété ci-dessus prend effet dès que c’est le cas ; c’est donc une décision de site et non un changement de code. (Sans ce bit, chaque appelant se connecte en tant que lui-même et les droits accordés par PostgreSQL sont la seule autorité ; voir pepsi-keys(1).)
11.4. Ce contre quoi la garde protège¶
Le modèle de menace énonce les affirmations par mode de garde (Garde par la passerelle (custody = local, la clé de MTA), Garde par le client (custody = client, la clé de MUA)) ; voici comment elles se traduisent dans le magasin de clés.
Quelqu’un qui dispose de… |
Une clé de MTA ( |
Une clé de MUA ( |
|---|---|---|
une copie de la base de données ou une sauvegarde |
Uniquement du texte chiffré enveloppé ; la KEK n’y figure pas. |
Une clé publique ; il n’y a de moitié privée nulle part sur le serveur. |
le seul fragment KEK de |
Rien : les blocs sont dans la base de données. |
Rien. |
une exécution de code dans une étape ordinaire |
Pas la clé. Mais son usage : réécrire le |
Rien qu’elle puisse signer ou ouvrir. Elle peut toutefois lire le courrier arrivé en clair avant qu’il soit chiffré pour l’utilisateur. |
une exécution de code dans |
Chaque clé de MTA, en clair, et la possibilité de les emporter. Après une telle compromission, faites tourner la KEK et renouvelez les clés. |
Rien : le courrier chiffré pour une clé de MUA passe sans être ouvert, et une copie déjà classée rescellée pour elle (pepsi-stage-reencrypt) ne peut pas non plus être ouverte. |
un accès en écriture à |
Ne peut pas déplacer une clé enveloppée vers une autre adresse : le texte chiffré est lié à sa ligne. Peut supprimer des lignes (déni de service). |
Peut remplacer la clé publique, redirigeant le courrier futur vers une clé choisie par l’attaquant — le même pouvoir que celui d’un opérateur malveillant. Seul un correspondant qui a vérifié l’empreinte hors bande est protégé. |
Deux autres attaques portent sur quelle clé est utilisée plutôt que sur qui la détient :
Implanter une clé pour l’un de nos utilisateurs. Le moissonnage et le gossip apprennent des clés à partir de courrier que n’importe qui peut envoyer, de sorte qu’un
From: alice@our-domainfalsifié peut amorcer une ligne de cache pour Alice. Pour toute adresse pour laquelle nous détenons une identité, notre propre clé préempte tout le cache (Les clés de nos propres utilisateurs ne sont pas découvertes), de sorte que la clé implantée n’est jamais utilisée ; pour une adresse servie sans identité, c’est de la confiance au premier usage, à laquelle Repérer les utilisateurs qui apportent leur propre clé apporte le remède.Substituer la clé d’un correspondant. Seulement dans la mesure où l’échelle de confiance le permet : une source mieux classée l’emporte toujours dans un conflit, le gossip d’un tiers ne peut jamais déplacer ce que le propriétaire a annoncé, et
MIN_TRUSTdécide jusqu’où descendre dans l’échelle tout en utilisant encore le chiffrement (Clés des correspondants).
11.5. Signature et chiffrement sont séparés¶
Le magasin sépare le matériel de signature de celui de chiffrement partout où le protocole le permet. La raison est une asymétrie :
Pepsi remet du texte clair dans la boîte aux lettres. Une clé privée de déchiffrement n’est donc nécessaire que pour le courrier encore en vol et pour le texte chiffré qu’un opérateur a archivé — jamais pour lire sa propre boîte. Une clé privée de signature, une fois retirée, ne peut plus rien faire de légitime : aucun processus honnête ne resigne jamais du vieux courrier. La garder n’est qu’un risque de falsification.
Le retrait — à l’expiration, et de façon identique à la révocation — fait donc deux choses différentes :
la moitié privée d’une ligne de signature est détruite, la moitié publique restant afin que les signatures faites pendant la validité de la clé restent vérifiables ;
la moitié privée d’une ligne de chiffrement est conservée, afin que le texte chiffré en vol et archivé reste lisible.
private_purged_at consigne quand du matériel a été abandonné, ce qui garde « délibérément détruit » distinguable de « jamais détenu ».
Pour OpenPGP la séparation est intrinsèque et gratuite : une clé transférable est une clé primaire capable de signer assortie d’une sous-clé de chiffrement, de sorte qu’une seule ligne de finalité both la représente. Pour S/MIME elle signifie deux certificats, l’un avec digitalSignature et l’autre avec keyEncipherment/keyAgreement. Le chemin auto-signé comme celui de la demande de certificat traitent naturellement la paire — identity generate crée les deux, et --csr émet une demande pour chacun.
11.5.1. S/MIME à certificat unique, et ce qu’il coûte¶
Deux certificats par identité représentent une dépense réelle lorsque les certificats sont achetés à une autorité par adresse. [pepsi] CRYPTO_SMIME_SHARED_KEY = yes délivre donc un seul certificat portant les deux usages de clé à la place. C’est une option INI réservée à l’administrateur — un arbitrage sécurité/coût, non une préférence par utilisateur, et structurellement hors de portée de la couche de redéfinition par adresse.
L’arbitrage :
le bénéfice en matière de risque de falsification est abandonné. La moitié privée d’un certificat partagé suit la règle du déchiffrement au retrait, puisque la détruire ôterait la capacité de déchiffrer avec elle ; elle ne quitte le magasin que par un
pepsi-keys identity forget-privateexplicite ;la révocation devient tout ou rien. Révoquer une clé de signature compromise met aussi fin à la capacité de recevoir du nouveau courrier chiffré sur ce certificat.
Le mode partagé est RSA seulement. pepsi-setup le refuse en même temps qu’un CRYPTO_SMIME_ALGORITHM à courbes elliptiques plutôt que de produire silencieusement un certificat que la moitié du monde n’acceptera pas : une seule clé EC faisant à la fois ECDSA et ECDH est une réutilisation de clé entre algorithmes que plusieurs clients S/MIME rejettent d’emblée.
11.5.2. Capacité, jamais forme¶
CRYPTO_SMIME_SHARED_KEY ne décide que de l’allure des identités nouvellement créées. Le basculer ne réécrit, ne renouvelle les clés ni n’invalide jamais rien de déjà délivré : à tout moment la table peut donc légitimement contenir des identités séparées, des identités partagées, et les deux pour la même adresse — un ancien certificat partagé encore valide tandis que de nouveaux certificats séparés sont en service.
Chaque consultation se résout donc par capacité :
signing -> purpose IN ('sign', 'both')
encryption -> purpose IN ('encrypt', 'both')
et il n’existe délibérément aucune requête qui renvoie « l’identité » d’une adresse. Du code supposant qu’il y a exactement deux lignes, ou exactement une, deviendrait un bogue à l’instant où un opérateur bascule l’interrupteur. En lisant la sortie de pepsi-keys identity list, lisez la colonne PURPOSE de la même manière.
11.6. Créer des identités¶
Il y a quatre façons pour du matériel d’entrer dans crypto_identity.
Génération. pepsi-keys identity generate crée la paire de clés ici. OpenPGP emploie par défaut Ed25519 ([pepsi] CRYPTO_OPENPGP_ALGORITHM), instantané à engendrer ; RSA est disponible en 2048, 3072 ou 4096 bits. S/MIME emploie par défaut RSA à CRYPTO_GENERATE_RSA_BITS et peut utiliser NIST P-256 ou P-384. Chaque certificat engendré est auto-signé, ce qui suffit à démarrer immédiatement, et --csr émet en outre une demande PKCS#10 sur la même clé afin qu’une véritable autorité puisse délivrer un remplacement sans changer de clé.
Pepsi n’exploite pas d’autorité de certification à lui : il détiendrait alors une clé bien plus précieuse que celle de n’importe quel utilisateur, avec son propre problème de garde et de révocation.
Import. pepsi-keys identity import stocke du matériel engendré ailleurs — le certificat qu’une autorité a délivré sur une clé existante, ou une paire de clés qu’un utilisateur possédait déjà. Le --purpose déclaré est ce sur quoi porte chaque consultation ultérieure : il doit donc décrire ce que le matériel sait réellement faire.
Création automatique. [pepsi-crypto] AUTO_CREATE_IDENTITY (activé par défaut) permet d’engendrer une identité pour une adresse servie sans qu’un opérateur le demande. Quand elle se déclenche dépend de l’option ENABLE_PEP de l’étape de chiffrement :
De façon empressée, sous
ENABLE_PEP = yes(la valeur par défaut) : au premier message soumis localement par un expéditeur, qu’il ait demandé une protection ou non. Voir pEp-style automatic keys ci-dessous, qui traite aussi de ce que cela coûte.De façon paresseuse, sous
ENABLE_PEP = no: la première fois qu’un expéditeur local demande explicitement une protection — un marqueur dans le sujet, ou l’en-tête équivalent venu d’un module complémentaire de client de messagerie, qu’il demande une signature ou seulement un chiffrement — et jamais du seul fait qu’une adresse est apparue dans une enveloppe. Un utilisateur qui n’emploie jamais la fonctionnalité n’a donc jamais de matériel de clé et n’est jamais exposé par elle.SIGN = alwaysvaut une telle demande sur chaque message, de sorte que sous ce réglage un expéditeur obtient une clé dès sa première soumission, comme avec le préréglage.
Dans les deux cas, la création automatique n’a lieu que pour une adresse d’un domaine de [pepsi-ingress] ACCEPTED_DOMAINS qui n’a aucune ligne d’identité d’aucune sorte – ni une identité révoquée, ni la clé personnelle de l’utilisateur enregistrée depuis son client de messagerie. Si quelqu’un a révoqué une clé, Pepsi n’en frappe pas discrètement une de remplacement ; et un utilisateur qui a apporté sa propre clé n’en reçoit pas une seconde dans son dos. La création sur demande (pepsi-keys identity generate, la console web, la commande e-mail generate) n’est pas gardée ainsi. Les lignes créées automatiquement sont marquées auto_created.
Avertissement
L’unique conséquence surprenante. Le déclencheur est sur le chemin sortant. Une adresse qui ne fait que recevoir du courrier reste donc sans clé, n’obtient aucune entrée de Web Key Directory et ne publie aucun enregistrement DNS — les tiers n’ont donc rien pour chiffrer vers elle, et n’auront jamais rien, quelle que soit leur patience.
Un opérateur qui veut une couverture en entrée doit provisionner ces adresses à l’avance
pepsi-keys identity generate role@example.org --protocol openpgp
Une création pour chaque adresse jamais vue frapperait du matériel de clé – et le publierait – pour des gens qui n’ont jamais rien envoyé par ce serveur.
Enregistrement de la clé personnelle de l’utilisateur. Une clé publique sans moitié privée – pepsi-keys identity import sans --private, une clé enregistrée par la console web ou par e-mail, ou une clé apprise de l’en-tête Autocrypt: du courrier que l’utilisateur soumet lui-même – est stockée comme clé de MUA de l’utilisateur ; voir Two keys per user.
11.7. Clés automatiques à la manière de pEp¶
[stage-encrypt] ENABLE_PEP (yes par défaut) fait se comporter Pepsi comme le projet pretty Easy privacy (pEp) : chaque expéditeur local reçoit une clé, le courrier est chiffré dès que la clé d’un destinataire est connue ou découvrable, et la clé publique de l’expéditeur accompagne chaque message – dans un en-tête Autocrypt: par défaut, et en plus comme fichier joint avec ATTACH_KEYS_AS_FILES (voir Microsoft Exchange comme passerelle). C’est un préréglage de valeurs par défaut pour l’étape de chiffrement, non un mode contraignant ; le tableau complet figure dans pepsi-stage-encrypt(1). Ce qu’il change :
Création empressée des clés. Le premier message soumis localement depuis une adresse d’un domaine servi engendre une clé OpenPGP pour elle, sur toutes les voies de soumission, relais de confiance compris (
MYNETWORKS, certificats client), de sorte qu’un Pepsi déployé comme mandataire derrière un MTA qui authentifie les utilisateurs se comporte de la même façon. Le test de domaine est ce qui rend utile une clé que nous frappons : le courrier pour ce domaine entre par nous, si bien que l’étape de déchiffrement peut ouvrir les réponses chiffrées vers elle. Une application web qui envoie en tant quenoreply@d’un domaine pour lequel nous ne recevons pas de courrier n’obtient pas de clé.Le texte clair n’est pas signé. La valeur par défaut de
SIGNdevientencrypted-only: une signature réside à l’intérieur du chiffré, et le texte clair porte la clé mais pas de signature, de sorte que les destinataires qui n’utilisent pas la cryptographie ne voient jamais designature.asc.[sign]ouX-Pepsi-Sign: yessigne toujours le texte clair.Pas de téléversement vers un serveur de clés. Les clés automatiques sont servies par notre propre Web Key Directory et annoncées par Autocrypt ; elles ne sont jamais téléversées vers un serveur de clés à moins que leur propriétaire ne le demande (Key-server uploads happen on request).
Rien d’autre ne change : la découverte met toujours en attente le premier message vers un nouveau correspondant jusqu’à son délai, les messages sont du PGP/MIME et de l’Autocrypt standard, et il n’y a pas de format de transmission pEp (pas d’en-tête X-pEp-Version, pas d’enveloppe pEp 2.x, pas de trustwords ni de poignée de main, pas de synchronisation de clés). Le préréglage ne concerne que le courrier que les utilisateurs envoient – le texte clair part non signé, et les expéditeurs reçoivent des clés qu’ils n’ont pas demandées ; rien de ce que quiconque peut recevoir n’en dépend.
Avertissement
La création empressée élargit l’exposition, et c’est le comportement par défaut. Avec la création paresseuse, un utilisateur qui n’emploie jamais la cryptographie n’a aucun matériel de clé sur le serveur. Sous ENABLE_PEP, chaque expéditeur d’un domaine servi a, après son premier message, une clé privée enveloppée dans la base de données et une clé publique dans le Web Key Directory du déploiement. L’enveloppement et la frontière de privilèges décrits plus haut s’appliquent toujours, mais l’ensemble des clés qu’exposerait une compromission de la clé de chiffrement de clés est celui de tous les expéditeurs de courrier, et non seulement de ceux qui l’ont demandé.
Les vetos au niveau du site sont [pepsi-crypto] AUTO_CREATE_IDENTITY = no (une section réservée à l’administrateur, qu’aucun réglage par utilisateur ne peut donc redéfinir) et [stage-encrypt] ENABLE_PEP = no. Un utilisateur se retire en enregistrant sa propre clé avant d’envoyer (une adresse qui a une identité quelconque n’en reçoit jamais d’automatique), ou en réglant ENABLE_PEP = no dans ses propres pepsi.settings là où les EDITABLE_STAGES de l’opérateur le permettent (pepsi-stage-edit-settings). pepsi-setup avertit lorsque le préréglage est activé mais que AUTO_CREATE_IDENTITY est désactivé ou qu’aucun KEY_WRAP_SECRET n’est configuré, puisque le préréglage fait alors discrètement moins que ce qu’il annonce.
L’assistant de configuration (et la configuration dans le navigateur, qui pose la même question) soumet explicitement ce choix à l’opérateur, en exposant le risque avant de poser la question, et écrit la réponse – yes comme no – dans la section de l’étape de chiffrement de pepsi.conf, où elle prend effet ; aucun fichier config.d ne la définit.
11.8. Deux clés par utilisateur¶
Une adresse peut détenir deux sortes de clés :
- Clé de MTA (
custody = local) Pepsi détient la moitié privée. L’étape de chiffrement signe avec elle, l’étape de déchiffrement ouvre le courrier avec elle, et elle est annoncée dans les en-têtes
Autocrypt:. Engendrée automatiquement ou sur demande, ou importée avec sa moitié privée.- Clé de MUA (
custody = client) La clé personnelle de l’utilisateur, dont la moitié privée ne réside que dans son client de messagerie. La ligne reste à jamais purement publique et n’est jamais primaire. Pepsi chiffre vers elle et reconnaît le courrier chiffré vers 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é de MUA active la plus récente lorsqu’il y en a une, sinon la clé de MTA active primaire. Le Web Key Directory, l’en-tête Autocrypt:, le fichier de clé et le chiffrement local la lisent tous selon un même ordre, de sorte qu’ils ne peuvent pas diverger :
le Web Key Directory ne sert que la clé de MUA dès qu’il y en a une ;
le courrier d’un utilisateur local à un autre est chiffré vers elle ;
Pepsi n’ajoute aucun en-tête
Autocrypt:et ne joint aucun fichier de clé lorsqu’il s’agit d’une clé de MUA : le client de messagerie annonce sa propre clé, et si Pepsi en annonçait une autre sur certains messages de l’utilisateur, l’état de clé des correspondants basculerait sans cesse sous la règle « le plus récent l’emporte » d’Autocrypt.
La clé de MTA n’est pas retirée lorsqu’une clé de MUA apparaît. Elle continue de déchiffrer le courrier des correspondants qui l’utilisent encore, et elle signe toujours, à l’intérieur du chiffré, le courrier en clair que Pepsi chiffre pour le compte de l’utilisateur (un correspondant qui ne la possède pas voit une signature invérifiable, ce qui est sans danger).
Le courrier chiffré pour une clé de MUA est transmis au client de messagerie sans être ouvert, même lorsqu’une clé de MTA figure aussi parmi ses destinataires : l’utilisateur a enregistré sa propre clé pour obtenir une protection de bout en bout, et une copie en clair dans la boîte aux lettres l’annulerait silencieusement. Ce n’est pas un échec de déchiffrement (ON_DECRYPT_FAILURE ne s’applique pas) et il poursuit sa route dans le pipeline, de sorte que les étapes antispam et de liste blanche décident toujours de son sort. Voir pepsi-stage-decrypt(1). Le courrier arrivé chiffré pour la clé de MTA est en revanche ouvert, lu par ces étapes, puis — avec pepsi-stage-reencrypt dans le pipeline — scellé pour la clé de MUA avant d’être classé, de sorte que la copie de la boîte aux lettres est protégée de la même façon.
Note
Les fonctionnalités côté serveur deviennent aveugles pour le courrier de bout en bout. Un message transmis tel quel vers une clé de MUA est du chiffré pour toutes les étapes ultérieures : la détection de langue ne peut pas le lire, rien n’a été vérifié, donc state.signature_verified n’est jamais positionné, et une entrée de liste blanche avec signature_required ne lui correspond pas. C’est la conséquence directe du fait de ne pas l’ouvrir.
De même, un message que le client de messagerie de l’utilisateur a déjà chiffré est laissé tel quel à la sortie – pas de second chiffrement, pas de signature, pas d’en-tête Autocrypt: ni de fichier de clé de Pepsi – et un message qu’il a déjà signé peut être chiffré mais ne reçoit pas de seconde signature.
11.8.1. Enregistrer la clé personnelle de l’utilisateur¶
Une clé de MUA arrive par l’une de ces voies :
Automatiquement, depuis le courrier de l’utilisateur lui-même. Un message soumis dont l’en-tête
Autocrypt:(addr=égal auFrom:) porte une clé que l’adresse n’a pas encore l’enregistre ; pour la toute première clé de l’adresse, un fichierapplication/pgp-keysjoint qui nomme l’adresse compte aussi. Cela n’a lieu que lorsque l’émetteur de la soumission peut parler au nom de l’adresseFrom:: un relais de confiance (MYNETWORKS, un certificat client), ou un compte authentifié dont leFrom:correspondait à une entréeUSERNAME_MAPconcrète, sans joker, ou à sonlogin@HOSTNAMEpar défaut (state.origin.from_bound). Un compte auquel*@example.orgest accordé peut envoyer au nom de ses collègues mais n’enregistre jamais de clé pour eux ;pepsi-setupliste de tels comptes.Par l’utilisateur, via la console web ou par e-mail (ci-dessous).
Par l’opérateur, avec
pepsi-keys identity importsans--private.
Une empreinte que l’adresse possède déjà, retirée ou non, n’est jamais enregistrée à nouveau, de sorte qu’une clé retirée ne revient pas parce qu’un vieil appareil l’envoie encore. Un utilisateur ayant deux appareils, détenant chacun une clé, se retrouve avec deux clés de MUA ; la plus récente est la face publique, et les deux sont reconnues sur le courrier entrant.
Lorsqu’une clé est enregistrée automatiquement pour une adresse qui avait déjà une autre identité, l’utilisateur reçoit un avis (« une nouvelle clé de chiffrement a été enregistrée pour votre adresse », avec son empreinte et la manière de la retirer ; modèle key-registered.<lang>.body) via le RESPONSE_STAGE de l’étape de chiffrement.
Avertissement
Un mot de passe volé peut enregistrer une clé. Quiconque peut soumettre du courrier au nom d’un utilisateur peut faire de sa propre clé la face publique de cet utilisateur. L’avis est la parade, et une parade partielle : un attaquant qui peut soumettre du courrier peut souvent lire la boîte aux lettres par IMAP et supprimer l’avis aussi. Les dégâts sont bornés – le courrier chiffré vers la clé implantée n’est toujours remis que dans la boîte de la victime, il s’agit donc de la capacité à lire du courrier futur que l’attaquant peut déjà atteindre, plus des signatures qui se vérifient comme étant celles de l’utilisateur – et le remède est retire, disponible aussi par e-mail. Un déploiement sans RESPONSE_STAGE n’envoie aucun avis ; pepsi-setup en avertit.
Un utilisateur ferme cette voie en enrôlant un second facteur (ci-dessous) : dès lors, une clé n’est enregistrée que par une commande portant un code en cours de validité, et l’en-tête Autocrypt: d’un message ordinaire n’enregistre rien.
11.8.2. Libre-service sur trois surfaces¶
Un même ensemble d’opérations – status, generate (une clé gérée par le serveur, toujours autorisée sur demande), register (la clé personnelle de l’utilisateur), publish (demander un téléversement vers un serveur de clés) et retire – est disponible sur trois surfaces, chacune confinant un utilisateur à sa propre adresse comme elle le fait déjà :
pepsi-keysidentity show,identity generate,identity import(sans--private),identity publish --vks,identity revoke, etotppour le second facteur. Voir pepsi-keys(1) ; sur une installation setuid, un utilisateur non privilégié n’atteint que ses propres identités.- La console web et l’API
Pour un opérateur ou un principal détenant
own:<address>: la page des identités propose Generate a server-managed key et Register my own key (coller une clé publique en armure ASCII), et la page d’une identité Upload to the key server et la révocation. Le niveau web n’agit toujours jamais sur du matériel de clé : engendrer, enregistrer et révoquer mettent en file des tâchesgenerate-identity/register-client-key/revoke-identityquepepsi-setup applyrevalide et exécute sous le rôlepepsi-crypto– la révocation aussi, parce qu’elle détruit la moitié privée d’une clé de signature seule. Une révocation demandée là prend donc effet une fois l’applicateur passé, avec la même règle de retrait quepepsi-keys identity revoke. Sans le paquetpepsi-httpd-admin, ces trois opérations passent en lecture seule comme tout autre chemin de l’applicateur. Voir L’API d’administration et La console d’administration.Un message de l’utilisateur à
pepsi-keys@<their domain>([stage-encrypt] KEYS_CONTROL_LOCAL_PART) portant la commande dans leSubject:–status,generate,register(qui prend la clé dans l’en-têteAutocrypt:ou la pièce jointe de ce message),publish [FPR],retire FPR,otp enroll,otp remove– est consommé au lieu d’être relayé, et reçoit en réponse la liste des clés de l’adresse. Lorsqu’un second facteur est enrôlé, chaque commande saufstatusporte aussi le code en cours (Un second facteur pour les modifications de clés). Il exige queRESPONSE_STAGEsoit défini, et ne fonctionne que lorsque l’émetteur de la soumission peut parler au nom de l’adresseFrom:(comme ci-dessus). Il est traité dans l’étape de chiffrement elle-même, qui tourne déjà souspepsi-crypto; un processus d’étape ne doit jamais pouvoir mettre en file du travail pour l’applicateur root.
11.8.3. Un second facteur pour les modifications de clés¶
Un utilisateur peut protéger tout ce qui précède par un second facteur : un secret TOTP (RFC 6238 – six chiffres, un nouveau code toutes les 30 secondes, SHA-1, ce qu’implémente toute application d’authentification) enrôlé pour son adresse. Dès qu’il existe, chaque modification des clés de l’adresse qu’un utilisateur peut effectuer exige le code en cours de l’application :
par e-mail, chaque commande sauf
status– le code est un mot de six chiffres placé n’importe où dans leSubject:, par exempleregister 123456ouretire 89ABCDEF01234567 otp=123456. L’en-têteAutocrypt:ou le fichier de clé joint d’un message n’enregistre plus de clé pour cette adresse ;registeraccompagné d’un code le fait ;dans la console web ou l’API, la génération, l’enregistrement et la révocation sous
own:<address>prennent le code dans un champotp(les formulaires de la console en ont un), que vérifie l’applicateur de configuration –pepsi-httpdlui-même ne peut ni lire ni vérifier un second facteur ;avec un
pepsi-keysnon privilégié,pepsi-keys identity --otp CODE <command>.
L’enrôler, le remplacer et le supprimer sont des commandes à part entière, et le remplacer ou le supprimer exige aussi un code :
otp enroll(e-mail) /pepsi-keys otp enroll ADDRESSCrée un nouveau secret et le renvoie une seule fois : une URI
otpauth://, le secret en base32 pour une saisie à la main, et (par e-mail) un code QR à scanner. Supprimez cette réponse une fois le secret dans l’application : quiconque peut la lire peut produire les codes.otp remove CODE/pepsi-keys otp remove ADDRESS --otp CODELe supprime.
status indique si l’adresse en possède un, et s’il est verrouillé.
Codes erronés et rejeux. Un code n’est accepté qu’une fois : le même code une seconde fois, même dans ses 30 secondes, est refusé comme rejeu. Les horloges peuvent différer d’un pas dans un sens ou dans l’autre. Un code erroné ou rejoué compte comme un échec, un code correct remet le compteur à zéro, et dix échecs d’affilée verrouillent le second facteur – toute tentative ultérieure est refusée sans être vérifiée. Une commande ne portant aucun code est refusée sans être comptée.
L’opérateur peut toujours rétablir l’accès d’un utilisateur : pepsi-keys otp reset ADDRESS (ou DELETE /api/v1/otp/{address} avec keys:write, qui s’adresse à l’applicateur de configuration) supprime l’enrôlement quel que soit son état, et pepsi-keys otp enroll ADDRESS exécuté par l’opérateur le remplace sans code. Un nouvel enrôlement commence toujours à zéro échec. pepsi-keys otp status liste chaque enrôlement. Un code n’est jamais demandé à un opérateur : il peut de toute façon le réinitialiser.
Ce qu’il ne couvre pas, énoncé dans Modèle de menace : le premier enrôlement n’exige aucun code, de sorte que quelqu’un qui dispose du mot de passe en premier peut enrôler le sien (la réinitialisation par l’opérateur est alors le remède) ; le verrouillage peut être déclenché par quiconque peut envoyer au nom de l’utilisateur ; et les bascules de publication, de serveur de clés et de clé principale de la console web, que pepsi-httpd effectue directement, ne sont pas soumises à contrôle. Le secret est enveloppé sous la clé de chiffrement de clés du magasin de clés comme une clé privée, la table n’est lisible que par le rôle pepsi-crypto, et pepsi-keys wrap rotate le ré-enveloppe avec les clés.
Une identité engendrée ou importée est publiée par défaut (--no-publish s’y soustrait, et identity unpublish efface l’indicateur plus tard). On ne peut pas chiffrer vers une clé que personne ne peut récupérer : la publication est donc la valeur par défaut utile ; l’indicateur est l’échappatoire par adresse.
11.9. Publier les clés de nos utilisateurs¶
L’image en miroir de « Trouver la clé d’un correspondant » ci-dessous : comment les autres trouvent les clés de nos utilisateurs. Il y a trois canaux, et ils se comportent si différemment que les traiter comme un seul réglage serait une erreur.
Canal |
Qui le sert |
Réversible ? |
Activé par |
|---|---|---|---|
Web Key Directory |
nous ( |
oui, immédiatement |
l’indicateur |
Serveur de clés |
un tiers |
non |
l’indicateur |
DNS (OPENPGPKEY / SMIMEA) |
nous (notre zone) |
oui, à la prochaine publication de zone |
la publication de l’enregistrement à la main |
Chaque identité est published par défaut, quelle que soit la manière dont elle a été créée. Le retrait est par adresse, non par identité, parce que c’est l’unité à laquelle pense un utilisateur (« suis-je découvrable ? ») — et parce qu’un retrait par identité permettrait un demi-état inutile où le certificat de signature est publié et celui de chiffrement ne l’est pas, de sorte que les correspondants puissent vérifier mais pas chiffrer.
11.9.1. Le Web Key Directory¶
pepsi-httpd répond aux quatre chemins du Web Key Directory pour chaque domaine d”[pepsi-ingress] ACCEPTED_DOMAINS
GET /.well-known/openpgpkey/hu/<hash> direct (on <domain>)
GET /.well-known/openpgpkey/policy direct
GET /.well-known/openpgpkey/<domain>/hu/<hash> advanced (on openpgpkey.<domain>)
GET /.well-known/openpgpkey/<domain>/policy advanced
Chaque identité porte le hachage WKD de la partie locale de son adresse (z-base-32 du SHA-1 de la partie locale mise en minuscules), stocké et indexé avec le domaine, de sorte qu’une requête soit satisfaite par une seule consultation plutôt qu’en hachant chaque ligne candidate. Le SHA-1 employé là est une fonction de nommage fixée par la spécification, non une fonction de sécurité : il n’est donc pas soumis à CRYPTO_ALLOW_WEAK_DIGESTS.
La réponse est la clé publique transférable binaire — non blindée — avec Content-Type: application/octet-stream et Access-Control-Allow-Origin: *, ce qu’exige la spécification pour qu’un client fonctionnant dans un navigateur puisse récupérer une clé depuis un domaine autre que sa propre origine. Le fichier de politique est un 200 de longueur nulle : il ne porte aucun indicateur, mais il doit exister, car GnuPG lit son absence comme « ce domaine n’exploite pas de Web Key Directory » et abandonne avant même de demander une clé.
Le paramètre ?l=<local-part> qu’un client peut envoyer est lu puis jeté. L’honorer transformerait le point de terminaison en une consultation par adresse fournie par l’appelant, ce qui est bien plus large que de répondre à un hachage que l’appelant devait déjà connaître.
Le déploiement coûte un enregistrement DNS et un nom de certificat par domaine servi. La méthode advanced — celle que tout client actuel essaie en premier — est récupérée depuis openpgpkey.<domain> : ce nom doit donc se résoudre vers l’hôte pepsi-httpd et le certificat qu’il sert doit le couvrir. Les deux sont le même listener : openpgpkey.<domain> et l’apex sont deux noms SNI sur un seul listener HTTPS, non deux listeners. pepsi-setup run ajoute openpgpkey.<domain> aux noms qu’il demande à certbot et rappelle ceux qui ne se résolvent pas ; un domaine qui publie déjà des clés est signalé, car pour lui la consultation avancée échoue aujourd’hui plutôt que d’être simplement non configurée. La méthode direct sur l’apex fonctionne sans rien de tout cela : un hôte openpgpkey manquant est donc une dégradation, non une panne.
Important
Le point de terminaison ne sert que nos propres identités publiées. Il lit crypto_identity, la table des adresses pour lesquelles nous détenons du matériel de clé, et jamais peer_key, le cache des clés de correspondants qu’a rempli la découverte. Les servir ferait de Pepsi un serveur de clés pour du matériel qu’il n’a jamais vérifié et dont il ne se porte pas garant, publié sous son propre nom de domaine. Aucune option de configuration ne change cela.
Note
Un Web Key Directory répond à un nombre illimité de devinettes, et Pepsi ne prétend pas le contraire. Il n’y a pas de limitation de débit sur le point de terminaison et pas d’uniformisation des 404 : une requête pour une adresse qui publie une clé reçoit la clé, et une requête pour une adresse qui n’en publie pas reçoit un 404, promptement et aussi souvent que quiconque veut bien demander.
Le protocole est un oracle public par construction — tout son objet est que quiconque puisse demander si une adresse publie une clé — le hachage ne couvre que la partie locale, de sorte qu’il répond à des devinettes plutôt qu’il n’énumère une liste, et les adresses figurent de toute façon à l’extérieur de chaque message que le domaine envoie. Uniformiser les 404 coûterait à un client la capacité de distinguer « pas de clé » de « serveur cassé » pour acheter un obstacle qu’une liste de mots franchit sans ralentir. Si vous avez besoin que les adresses restent secrètes, un annuaire de clés est la mauvaise chose à exploiter.
11.9.2. Les téléversements vers un serveur de clés se font sur demande¶
keys.openpgp.org et les serveurs qui parlent son protocole acceptent une clé définitivement. On peut plus tard leur dire que la clé est révoquée ; on ne peut jamais leur dire de l’oublier, ni d’oublier l’adresse pour laquelle elle a été téléversée.
C’est pourquoi rien n’est téléversé à moins que quelqu’un ne l’ait demandé, et pourquoi la demande est enregistrée : chaque identité porte vks_wanted, et « non téléversée » est un état, affiché par pepsi-keys identity show, la console web et la réponse e-mail à status, plutôt qu’une absence.
Les clés créées automatiquement et les clés de MUA enregistrées automatiquement démarrent avec cet indicateur désactivé. Autocrypt et notre propre Web Key Directory (qui n’est pas un téléversement) sont les moyens de les trouver.
Les clés faites à la main –
pepsi-keys identity generate/import, legeneratedu web et de l’e-mail – prennent[pepsi-keys] VKS_PUBLISHcomme valeur par défaut, un interrupteur de niveau opérateur désactivé par défaut : la personne qui comprend qu’un téléversement ne peut pas être retiré est l’opérateur, non quiconque a engendré une clé.Une demande explicite est honorée quoi que dise VKS_PUBLISH :
pepsi-keys identity publish 42 --vks(qui affiche une confirmation nommant exactement ce qui ne pourra pas être repris), le bouton Upload to the key server de la console web,PATCH /api/v1/identities/{id}avecvks_wanted, ou la commande e-mailpublish.
La publication est un protocole en deux temps, et c’est pourquoi il existe une tâche de reprise. Le téléversement rend un jeton ; seul le jeton autorise à demander au serveur d’envoyer un lien de confirmation à l’adresse ; et ce n’est qu’une fois ce lien suivi que le serveur sert la clé par adresse. Une clé téléversée mais jamais confirmée est stockée et pourtant introuvable — l’échec qui a l’air d’un succès. Donc
pepsi-keys identity publish --retry # from cron, e.g. hourly
parcourt chaque identité OpenPGP publiée et active dont le téléversement a été demandé et qui n’est pas encore vérifiée – il n’est pas conditionné par VKS_PUBLISH, de sorte que la demande d’un utilisateur prend effet à la prochaine exécution de la tâche – à intervalles de VKS_RETRY_INTERVAL, et abandonne après VKS_MAX_ATTEMPTS, le motif restant dans la ligne pour que vous le lisiez (pepsi-keys identity show). Chaque tour demande au serveur de clés si l’adresse est déjà servie avant de téléverser à nouveau, de sorte qu’une confirmation faite par n’importe qui — l’étape ci-dessous, un humain lisant la boîte aux lettres, une exécution antérieure dont l’écriture en base de données n’a pas abouti — mette fin à la boucle.
Le courrier de confirmation lui-même est suivi par pepsi-stage-vks-confirm, une petite étape du chemin entrant. C’est un programme distinct parce que ce qu’il fait — récupérer une URL trouvée dans du courrier entrant — est une primitive véritablement dangereuse, et l’isoler signifie que les restrictions tiennent en un seul petit endroit dont la position dans le pipeline est visible dans la configuration. La page de ce programme les liste ; en bref, le courrier doit venir de l’hôte de serveur de clés configuré, être authentifié par SPF ou DMARC, ne pointer que vers cet hôte, et être adressé à quelqu’un pour qui nous attendons réellement une confirmation. Tout le reste est remis à l’utilisateur, qui peut cliquer le lien lui-même. Il en va de même d’un courrier dont les liens ont été suivis mais après lequel le serveur de clés ne sert toujours pas notre clé : il n’est écarté qu’une fois au moins une identité vérifiée comme publiée.
11.9.3. Dépublier, et ce que cela ne peut pas faire¶
pepsi-keys identity unpublish 42
cesse aussitôt de servir l’identité par le Web Key Directory, et efface sa comptabilité de serveur de clés afin que la tâche de reprise l’oublie.
Cela ne retire rien d’un serveur de clés, car rien ne le peut. Si la clé a été téléversée, la commande le dit et renvoie vers le seul véritable remède
pepsi-keys identity revoke 42 --upload
qui construit un certificat de révocation — signé par la clé qu’il retire — et le publie par-dessus la clé téléversée. Notez l’ordre à l’intérieur de cette commande : le téléversement a lieu d’abord, parce que révoquer une identité de signature détruit la clé privée dont le certificat de révocation a besoin. Si le téléversement échoue, rien n’est révoqué : vous pouvez donc réparer le serveur de clés et réessayer.
11.9.4. DNS : OPENPGPKEY et SMIMEA¶
pepsi-keys dns <address> affiche les enregistrements DNS qui publient les identités publiées et actives d’une adresse : OPENPGPKEY (RFC 7929) pour le matériel OpenPGP et SMIMEA (RFC 8162) pour les certificats S/MIME, tous deux sous un nom de propriétaire haché afin que la zone n’énumère pas les adresses qu’elle sert. pepsi-setup run affiche les mêmes enregistrements pour chaque identité publiée, aux côtés de ceux de DKIM, SPF et MTA-STS (plafonnés à un nombre lisible — pour un grand déploiement, demandez par adresse).
S/MIME n’a pas d’équivalent du Web Key Directory et pas de serveur de clés : SMIMEA est donc son canal de publication.
Avertissement
Ne publiez les enregistrements de clés que dans une zone signée par DNSSEC. Un enregistrement OPENPGPKEY non signé est une clé distribuée par quiconque peut répondre pour la zone, ce qui n’améliore en rien le fait de n’avoir aucune clé — et contrairement à une réponse WKD, une réponse DNS ne porte aucune autre preuve. Les deux commandes le répètent en commentaire dans leur propre sortie.
Le nom de propriétaire DNS et le hachage WKD ne sont pas interchangeables : celui du DNS est un SHA-256 tronqué à 28 octets et encodé en hexadécimal sur la partie locale sensible à la casse (RFC 7929 §3), tandis que celui du WKD est un z-base-32 du SHA-1 sur la partie locale mise en minuscules. Les confondre produit des enregistrements que rien n’interroge jamais, raison pour laquelle ce sont des fonctions distinctes assorties d’un test affirmant qu’elles diffèrent.
11.10. Sauvegarder et renouveler la clé de chiffrement de clés¶
Danger
Si la clé de chiffrement de clés est perdue, chaque clé privée stockée est perdue. Sauvegardez secrets.d/pepsi-crypto.secret séparément de la base de données, et traitez-la avec le soin que son contenu mérite.
Le coup est amorti par la même asymétrie qui gouverne le retrait : les boîtes aux lettres contiennent du texte clair, si bien que perdre la KEK ne rend pas illisible le courrier de qui que ce soit. Ce qui est perdu, c’est tout texte chiffré encore en vol ou archivé — et, bien plus perturbant, chaque identité publiée est invalidée d’un coup. Les correspondants détiennent toujours des clés publiques que le déploiement ne peut plus employer : toute l’organisation doit donc renouveler ses clés et republier. Prévoyez cela comme un scénario de reprise après sinistre, non comme un désagrément.
Le renouvellement, en revanche, est de la routine et incrémental par construction, car chaque ligne consigne l’identifiant de la clé qui l’a scellée :
mettez le nouveau secret dans
secrets.d/pepsi-crypto.secretsousKEY_WRAP_SECRET, et gardez l’ancien à côté sousKEY_WRAP_SECRET_<OLD-ID>(le suffixe est l’ancienwrap_key_id, en majuscules) ;faites pointer
[pepsi-crypto] KEY_WRAP_KEY_IDvers le nouvel identifiant ;lancez
pepsi-keys wrap rotate.
Entre les étapes 2 et 3, le déploiement continue de fonctionner : le nouveau matériel est scellé sous la nouvelle clé tandis que l’ancien s’ouvre toujours sous celle qui a été retirée. Ce n’est qu’après que l’étape 3 a rapporté que tout est renouvelé que le secret retiré peut être supprimé. Chaque réenveloppement est une mise à jour gardée sur l’ancien identifiant, de sorte qu’un renouvellement concurrent — ou une clé réengendrée pendant le balayage — ne soit jamais écrasé ; les lignes dont la clé n’a aucun secret configuré sont signalées et sautées plutôt que de faire échouer l’exécution. --dry-run liste d’abord le travail.
11.11. Clés de pairs¶
Une clé de pair est mise en cache avec la source dont elle vient, et la source décide de ce qui se passe lorsqu’une clé nouvellement découverte contredit une clé stockée. L’ordre de ces sources est l’échelle de confiance tabulée ci-dessous sous Trouver la clé d’un correspondant ; elle vit dans une fonction de base de données afin de pouvoir être réordonnée en réexécutant le fichier de procédures plutôt que par un patch de schéma, et la règle de conflit est appliquée côté serveur, dans la même instruction qui stocke la clé, de sorte qu’aucun client ne puisse la contourner.
Une clé qui surclasse tout ce qui est stocké pour une adresse le remplace ; à rang égal ou inférieur, la clé stockée est conservée et la nouvelle n’est pas insérée, de sorte qu’une source faible ne puisse jamais déloger discrètement une source mieux attestée. Une ligne épinglée n’est jamais délogée, quel que soit le rang — l’épinglage est la façon dont un opérateur rend permanente une décision de confiance au premier usage.
Une exception se glisse entre ces deux règles, et elle est confinée au palier Autocrypt (les échelons inbound et gossip) : lorsque la clé entrante vient de ce palier, que chaque ligne stockée pour l’adresse et le protocole en vient aussi, que le rang entrant n’est pas moins bon que celui qui est stocké, et que la date d’effet Autocrypt du message est strictement plus récente que la plus récente stockée au meilleur rang, alors la clé stockée est remplacée à rang égal. C’est la mise à jour d’état de pair « le plus jeune l’emporte » propre à Autocrypt Level 1 : sans elle, la deuxième clé Autocrypt: jamais vue pour une adresse serait refusée à jamais, et un correspondant qui réinstalle son client ou renouvelle sa clé continuerait de recevoir du courrier chiffré vers une clé qu’il ne peut pas lire. Le test porte sur le nom de la source, non sur le numéro de rang, et le terme no worse than maintient l’exception à l’intérieur du palier dans les deux sens — le gossip ne déloge toujours jamais ce que le propriétaire d’une adresse a dit de lui-même, et ni l’un ni l’autre ne déloge jamais une clé moissonnée ou mieux attestée.
Une clé issue de gossip que son propriétaire confirme est promue
Une clé issue de gossip ne reste pas pour toujours sur l’échelon le plus bas. Lorsque le propriétaire de l’adresse annonce plus tard lui-même la même clé — dans son propre en-tête Autocrypt:, ou sous forme de clé jointe le nommant — la ligne est promue à l’échelon inbound (le passage, selon Autocrypt Level 1 §5.3, du gossip à l’état propre du pair). La clé n’a pas changé, ce n’est donc pas une rotation : pas de ligne key.peer.rotate ni d’avis à quiconque, mais une ligne key.peer.promote (gravité info) dans le journal d’audit, écrite par la même instruction. La ligne se défend ensuite au rang du propriétaire, de sorte qu’un gossip ultérieur d’une clé différente est refusé face à elle, et MIN_TRUST = harvested l’admet désormais. Sa date d’effet devient celle du propriétaire, et non celle du message de gossip, de sorte qu’une rotation ultérieure par le propriétaire est jugée par rapport à ce que le propriétaire a dit. Ce que peut atteindre une signature vérifiée avec elle ne change pas : c’est toujours de la confiance au premier usage, rapportée au mieux comme valid-untrusted. Une clé différente venant du propriétaire n’est pas non plus une promotion ; elle surclasse simplement le gossip et le remplace. Une source réseau (WKD, DANE, …) ou un opérateur qui revoit une clé issue de gossip reprend la ligne à son compte comme un rafraîchissement ordinaire.
Une rotation n’est jamais silencieuse
L’exception est fondée sur l’en-tête From:, qu’écrit l’expéditeur, et l’authentification entrante est fail-open, de sorte qu’un message qui ne fait que prétendre venir d’un correspondant peut changer la clé avec laquelle nous lui chiffrons. Autocrypt accepte ce compromis (il protège contre la collecte passive, non contre un attaquant actif sur le chemin) et Pepsi aussi, mais il laisse trois traces :
l’instruction même qui remplace la clé écrit une ligne
key.peer.rotatedans le journal d’audit — l’adresse, le protocole, les empreintes déplacées et nouvelles et les deux dates d’effet — de sorte qu’il n’y a pas de rotation sans son enregistrement ;pepsi-statusrésume celles des 30 derniers jours ;lorsque
RESPONSE_STAGEest défini sur pepsi-stage-autocrypt-learn, chaque destinataire local du message qui l’a provoquée reçoit un avis nommant le correspondant et les deux empreintes (le modèlekey-rotation.<lang>.body), de sorte que la personne sur le point de répondre sait qu’elle doit vérifier avant d’envoyer quoi que ce soit de confidentiel ;un compteur de télémétrie
autocrypt-rotationet une ligne de journal.
Un opérateur qui préfère refuser une rotation authentique plutôt qu’en accepter une falsifiée définit ACCEPT_ROTATION = expired sur cette étape : la clé plus récente n’est alors prise que lorsque chaque clé stockée pour l’adresse est morte — consignée comme révoquée ou expirée, ou ayant dépassé son expires_at. Sinon l’offre est retenue : rien ne change, une ligne d’avertissement key.peer.rotate.held est écrite, et les destinataires sont informés que la nouvelle clé n’a pas été acceptée — ce qui compte, car un correspondant qui a réellement changé de clé ne peut pas lire ce qu’on lui envoie tant qu’un opérateur n’a pas supprimé l’ancienne ligne (pepsi-keys peer remove) ou importé la nouvelle clé à la main. Une clé apprise est stockée avec l’expiration qu’énonce son propre matériel (l’auto-signature d’une clé primaire OpenPGP, le notAfter X.509), de sorte qu’une clé échue compte comme morte sans que rien ne la marque ; une clé qui n’énonce aucune expiration n’est déplacée qu’une fois marquée révoquée ou expirée, ou à la main.
Deux règles bornent l’ampleur possible d’une entrée :
une entrée peut nommer un domaine entier (
*@domain), mais seulement si elle a été saisie à la main. C’est imposé par une contrainte de base de données, non par une convention, de sorte qu’aucun mécanisme de découverte ne puisse généraliser la réponse d’une adresse à un domaine ;une correspondance exacte d’adresse l’emporte toujours. Une ligne
*@domainn’est consultée qu’en l’absence de ligne exacte, etpepsi-keys peer showla marque comme le repli qu’elle est.
11.12. Trouver la clé d’un correspondant¶
Tout ce qui précède concerne les clés une fois qu’elles sont dans le magasin. Les y faire entrer pour l’adresse de quelqu’un d’autre s’appelle la découverte de clés : étant donné une adresse e-mail, trouver la clé publique ou le certificat, noter d’où il vient et ce que vaut cette source, et le mettre en cache. Le programme est pepsi-keydisc, et toute la fonctionnalité est configurée par [pepsi-keydiscovery].
Trouver une clé est facile. Savoir s’il faut y croire est tout le problème, ce qui explique que la source et l’indicateur DNSSEC soient stockés avec chaque clé.
11.12.1. D’où une clé peut venir¶
daneOPENPGPKEY(RFC 7929) etSMIMEA(RFC 8162) dans le DNS, sous une partie locale hachée. Refusé sans le bit AD DNSSEC du résolveur — voir Lorsqu’il n’y a pas de résolveur validant.wkd-advancedLe Web Key Directory à
openpgpkey.<domain>— un hôte que le domaine a dû déléguer délibérément.wkd-directLe Web Key Directory sur le domaine lui-même,
<domain>/.well-known/openpgpkey.ldapUn annuaire configuré. Facultatif, et compilé uniquement avec la fonctionnalité cargo
ldap, car un déploiement sans annuaire ne devrait pas en lier un.vksUn serveur de clés vérificateur (
keys.openpgp.orgpar défaut, ou tout serveur parlant les mêmes chemins). Notez qu’il s’agit de VKS, non de l’ancien HKP non vérifié : un serveur HKP sert une clé que n’importe qui a téléversée pour l’adresse de n’importe qui.inboundMoissonné dans un message déjà arrivé — une partie
application/pgp-keys, un certificat de signataire S/MIME, ou un en-têteAutocrypt:. Seul le matériel revendiquant l’adresse propre à l’expéditeur est conservé. Ce n’est pas un service de découverte : il s’exécute là où le message se trouve déjà et ne coûte aucune entrée-sortie réseau, raison pour laquelle il ne peut pas figurer dansSOURCES. Écrit par pepsi-stage-autocrypt-learn, et non par l’étape de déchiffrement.gossipLu dans les champs
Autocrypt-Gossip:situés à l’intérieur d’un message que pepsi-stage-decrypt a déchiffré : les clés des autres destinataires, que l’expéditeur a jointes afin qu’une réponse à tous puisse être chiffrée pour quelqu’un dont on n’a jamais reçu de courrier direct (Autocrypt Level 1 §5.3). Un champ ne compte que pour une adresse que nomme leTo:, leCc:ou leReply-To:du message lui-même — la règle du récepteur du §5.3 lui-même. L’unique voie par laquelle un correspondant peut parler d’un autre, et donc son propre échelon tout en bas de l’échelle. Commeinbound, ce n’est pas un service et il ne peut pas figurer dansSOURCES; voir pepsi-stage-autocrypt-learn, où sont écrits les deux échelons en ligne, pour le reste des règles qui décident si un champ compte.manual/apiUn opérateur exécutant
pepsi-keys peer import, ou une soumission authentifiée. Pas découvert du tout ; listé ici parce que c’est le sommet de la même échelle.
11.12.2. L’échelle de confiance¶
C’est l’ordre selon lequel tout est jugé — le meilleur d’abord. Il décide quelle clé gagne un conflit, à quoi MIN_TRUST se compare, et combien de temps supplémentaire la règle d’arrêt ci-dessous accorde à une méthode encore en cours.
Rang |
Source |
Pourquoi là |
|---|---|---|
0 |
|
Pas découverte du tout, et pas vraiment un échelon : la moitié publique d’une identité que ce déploiement détient pour l’adresse. Elle préempte l’échelle plutôt qu’elle ne la couronne — voir Les clés de nos propres utilisateurs ne sont pas découvertes — de sorte qu’elle franchit tout plancher et qu’aucune ligne |
1 |
|
Un opérateur qui a tapé une empreinte le pensait vraiment. Son orthographe |
2 |
|
La seule source à provenance cryptographique : le domaine l’a publiée et DNSSEC prouve que la réponse n’a pas été réécrite en chemin. |
3 |
|
Récupérée en HTTPS vérifié depuis |
4 |
|
La même chose, depuis le domaine lui-même. Un signal d’intention plus faible qu’un hôte dédié, d’où son propre rang. |
5 |
|
Un annuaire que l’opérateur a configuré, et donc à demi digne de confiance — mais toujours un annuaire exploité par quelqu’un d’autre. |
6 |
|
Ne prouve que ceci : quelqu’un contrôlant cette boîte aux lettres l’a téléversée — le serveur a envoyé un courrier de confirmation et quelqu’un a cliqué. Une vraie vérification, et bien plus faible que « le domaine publie ceci ». |
7 |
|
Confiance au premier usage, portant sur ce que le propriétaire de l’adresse a dit de sa propre adresse. C’est aussi le poids que porte une ligne |
8 |
|
La présentation d’un autre par un correspondant (Autocrypt Level 1 §5.3), lue dans le texte chiffré d’un message que cet hôte a déchiffré. Le même matériel que le rang 7 et une affirmation différente : non pas « voici ma clé » mais « voici la clé de quelqu’un d’autre ». Le §5.3 garde les deux dans des états de pair séparés précisément pour que la présentation par un tiers ne puisse jamais déloger ce dont un propriétaire s’est porté garant, et c’est ce qu’achète un échelon à part. |
[pepsi-keydiscovery] MIN_TRUST est le plancher, et il vaut any par défaut — l’échelon du bas, quel qu’il soit à l’instant présent, de sorte que les clés de gossip, moissonnées et Autocrypt soient toutes acceptées. L’alternative au chiffrement vers une clé de confiance au premier usage n’est pas le chiffrement vers une meilleure clé, c’est l’envoi en clair. Le chiffrement opportuniste vers une clé non authentifiée met tout de même en échec l’interception passive, qui est la menace à laquelle le courrier fait réellement face. Le classement n’en devient pas inutile pour autant — il arbitre les conflits, il est consigné par clé, et un opérateur qui préférerait ne rien envoyer plutôt que d’envoyer vers une clé non authentifiée a une option à relever.
La valeur par défaut suit le bas de l’échelle plutôt que de nommer un nombre. MIN_TRUST = harvested nomme exactement le rang 7 : il accepte donc ce qu’un correspondant a annoncé à son propre sujet et refuse la présentation de celui-ci par un inconnu. Les présentations sont stockées dans les deux cas ; le plancher décide seulement si l’on peut chiffrer vers elles.
Le nom de chaque échelon est accepté comme plancher (own, manual, dane, wkd, wkd-direct, ldap, vks, inbound, gossip), ainsi que les alias any/none pour le bas, harvested/autocrypt pour le rang 7, et owner-confirmed — qui nomme le rang 6 par ce que la preuve signifie plutôt que par la méthode qui l’a produite : à vks et au-dessus, quelqu’un qui pouvait prouver le contrôle de la boîte aux lettres ou du domaine a publié la clé volontairement ; en dessous, personne n’a rien prouvé.
11.12.3. Les clés de nos propres utilisateurs ne sont pas découvertes¶
Il y a une adresse pour laquelle toute l’échelle est la mauvaise question : celle dont ce déploiement a fabriqué les clés. Si pepsi-keys a délivré une identité pour alice@example.org, alors la clé d’Alice n’est pas quelque chose à trouver, peser et comparer — elle est sur l’étagère, et c’est la seule clé vers laquelle son courrier puisse être chiffré et qu’elle saura ouvrir.
Cela importe parce que peer_key n’est pas une table digne de confiance. Une partie de ce qui la remplit est la moisson depuis le courrier entrant, et la moisson des clés se fait sur l’en-tête From:, que l”expéditeur écrit. L’authentification entrante est délibérément fail-open — seul un échec DMARC avéré sous DMARC_ENFORCE rejette — de sorte que sur un déploiement qui n’applique pas DMARC à son propre domaine, un message prétendant From: alice@example.org et portant un en-tête Autocrypt: sèmera une clé de pair pour Alice contenant la clé d’un inconnu. Deux choses tournent alors mal, et elles sont la raison pour laquelle il s’agit d’une défense et non d’une règle d’ordre : le courrier d’un de nos utilisateurs vers un autre serait chiffré vers la clé plantée, et une signature faite avec elle se vérifierait, positionnant state.signature_verified pour un tiers.
Les deux étapes cryptographiques demandent donc « est-ce l’une des nôtres » avant de demander « qu’a trouvé la découverte ». Lorsque la réponse est oui, notre propre clé est la réponse : les lignes peer_key du correspondant ne sont pas consultées du tout pour cette adresse, quel que soit leur rang et quelle qu’ait été leur arrivée. state.crypto le note sous own.
Important
La règle est conditionnée au fait de détenir une clé, non au fait de servir l’adresse, et la différence est un déploiement plutôt qu’une subtilité. Un utilisateur qui exploite GnuPG dans son propre client, n’a jamais demandé à Pepsi de chiffrer quoi que ce soit et ne nous a jamais téléversé de clé n’a ici aucune 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. Une règle générale « ne jamais croire une clé découverte pour une adresse que nous servons » enverrait du texte clair à cet utilisateur pour toujours. Donc : si nous avons fabriqué les clés, ce sont celles-là qui servent ; sinon, l’adresse est traitée comme celle de n’importe quel correspondant, exposition à la confiance au premier usage comprise.
Le corollaire est le coût : pour une adresse dont nous détenons bel et bien une identité, une signature faite avec la clé personnelle distincte de l’utilisateur ne se vérifie pas, si bien attestée soit-elle – jusqu’à ce que cette clé soit elle-même enregistrée comme clé de MUA de l’utilisateur (Two keys per user). L’enregistrer (pepsi-keys identity import sans --private, la console web, la commande e-mail register, ou automatiquement depuis l’en-tête Autocrypt: de l’utilisateur lui-même) est la façon dont elle devient crue, et aussi la façon dont l’utilisateur commence à recevoir du courrier chiffré vers elle.
11.12.3.1. Repérer les utilisateurs qui apportent leur propre clé¶
La règle ci-dessus laisse un résidu par conception : une adresse servie pour laquelle nous ne détenons aucune identité reste en confiance au premier usage, car c’est le seul canal par lequel la vraie clé d’un utilisateur qui apporte son propre GnuPG puisse atteindre cet hôte. Ce qu’un opérateur peut y faire n’est pas un réglage — c’est une présentation. Importez la clé publique de cet utilisateur comme identité et l’exposition est terminée pour cette adresse : à partir de là, notre propre ligne préempte ce que le cache contient.
Le déploiement a donc besoin d’un moyen de les repérer, et c’est pepsi-keys peer unclaimed au terminal, ou GET /api/v1/peers/unclaimed (voir L’API d’administration) : les adresses qui
sont servies par ce déploiement (leur domaine est l’un d”
[pepsi-ingress] ACCEPTED_DOMAINS; la commande compte aussi[pepsi-crypto] LOCAL_DOMAINS),ne sont détenues par aucune identité de chiffrement active de notre part, et
sont néanmoins présentes dans
peer_key— d’ordinaire moissonnées depuis l’en-têteAutocrypt:de leur propre courrier sortant, ce qui est exactement ce que produit un utilisateur exploitant GnuPG dans son propre client.
Chaque ligne nomme les clés en cache avec leur empreinte et leur source, de sorte qu’un opérateur puisse distinguer une clé que l’utilisateur a lui-même annoncée (inbound) de la présentation par gossip qu’un tiers en a faite (gossip) avant d’agir, ainsi qu’un décompte du nombre d’identités — de n’importe quel usage ou état de cycle de vie — que l’adresse possède bel et bien. Le rapport est en lecture seule et ne change rien ; c’est une liste de travail, non une alarme, et une liste vide est l’état ordinaire d’un déploiement dont tous les utilisateurs détiennent ici des identités. La page Clés de correspondants de la console (/ui/peers) liste tout le cache peer_key, et non ce sous-ensemble.
Les deux diffèrent par une valeur par défaut. La commande ne liste que les clés moissonnées — source inbound ou gossip, les deux qu’un inconnu peut implanter simplement en nous envoyant un message avec un From: falsifié — parce que c’est l’exposition pour laquelle le rapport existe ; --all-sources l’élargit aux clés découvertes, manuelles et d’API, ce que le point d’accès renvoie toujours
pepsi-keys peer unclaimed # harvested and gossiped keys
pepsi-keys peer unclaimed --all-sources # every cached key
pepsi-keys peer unclaimed --json # [] when there is nothing to do
Pour chaque entrée, confirmez la clé auprès de son propriétaire puis enregistrez-la comme la sienne (pepsi-keys identity import ADDRESS --protocol P --purpose U --public FILE, sans --private) ou supprimez-la (pepsi-keys peer remove ID).
Note
Demandez à l’utilisateur avant d’importer. La clé du cache est une clé que quelqu’un a annoncée ; confirmer hors bande qu’elle est bien la sienne est ce qui transforme une ligne de confiance au premier usage en une ligne digne de confiance, et c’est toute la valeur de l’exercice.
Quels états de cycle de vie comptent diffère entre les deux usages, pour la raison même de l’existence de l’expiration :
Chiffrer vers l’adresse ne compte que les identités
active. Une clé retirée ne doit pas recevoir de nouveau courrier, et un déploiement qui a retiré la clé d’un utilisateur sans en délivrer une autre est dans la même situation qu’un déploiement qui n’a jamais détenu de clé pour lui — la découverte prend donc le relais.Vérifier une signature venant de l’adresse compte tout sauf
revoked. Une signature faite l’an dernier l’a été avec la clé de l’an dernier ; refuser de la regarder rapporterait le courrier de nos propres utilisateurs comme invérifiable à chaque renouvellement de clé.revokedest exclu parce que c’est l’opérateur disant que le matériel ne doit pas être employé du tout.
11.12.4. Le pipeline ne se bloque jamais sur un serveur de clés¶
La découverte est asynchrone et extérieure aux étapes. Une étape qui a besoin d’une clé qu’elle n’a pas n’effectue aucune entrée-sortie réseau : elle valide ce qu’elle a déjà produit, met le message en pause et met en file une demande indexée sur l’adresse. Une instance de pepsi-keydisc répond à la demande et, dans le même aller-retour qui stocke la clé, libère chaque message garé sur cette adresse.
Trois conséquences s’ensuivent, chacune voulue plutôt qu’un effet de bord :
Un serveur de clés lent ne peut pas retenir un worker d’étape. Le problème est supprimé plutôt que borné — il n’y a pas de worker en attente à borner.
Cent messages vers un même correspondant coûtent une consultation. La demande est indexée sur l’adresse : ils s’y dédupliquent donc et sont libérés ensemble.
Une demande qui se règle sans trouver de clé libère tout de même ceux qui l’attendent. Les services trouvent des clés ; les étapes décident du sort du courrier. Rien dans la couche de découverte ne décide jamais du destin d’un message.
Les méthodes s’exécutent en parallèle : chaque méthode de SOURCES démarre en même temps, chacune comme son propre processus (pepsi-keydisc@dane, @wkd-advanced, @wkd-direct, @vks, @ldap). Les deux méthodes de Web Key Directory sont des rangs distincts et donc véritablement concurrentes, non une chaîne de repli — n’exécuter la méthode directe que « si l’avancée échoue » accepterait silencieusement la réponse la plus faible chaque fois que la plus forte serait simplement lente. Un processus par méthode signifie aussi qu’un bind LDAP figé ne peut pas bloquer WKD, et que désactiver une méthode s’écrit systemctl mask --now pepsi-keydisc@<method> (mask, car pepsi.target démarre les quatre instances par défaut et redémarrerait une instance simplement désactivée).
Gardez SOURCES et les unités activées en accord. SOURCES est la liste que la règle d’arrêt attend : une instance dont la méthode n’y figure pas refuse donc de démarrer, et une méthode qui y figure sans que rien ne tourne coûte à chaque demande le TIMEOUT complet avant qu’elle n’abandonne.
11.12.5. RANK_GRACE : le curseur entre vitesse et exhaustivité¶
Tout exécuter en même temps soulève une question : une réponse est arrivée d’une source médiocre, et une meilleure est encore en cours. Attendre, ou partir ?
RANK_GRACE est l’unique réglage qui y répond. Lorsqu’une consultation réussit, chaque méthode encore en cours qui se classe mieux se voit accorder
RANK_GRACE × (how many ranks better it is)
de temps supplémentaire, compté depuis l’instant où la réponse en main est arrivée. Quand cette fenêtre expire, le meilleur résultat en main l’emporte. Ce réglage couvre tout l’éventail des politiques raisonnables :
Réglage |
Comportement |
|---|---|
|
La première réponse utilisable l’emporte d’emblée. Le plus rapide ; le classement n’arbitre alors que les conflits dans le magasin, jamais la course. |
|
Un rang au-dessus reçoit 5 s, trois rangs au-dessus 15 s. Un serveur WKD-advanced lent ne peut pas retenir un message derrière une réponse de serveur de clés déjà arrivée, mais une meilleure source presque aussi rapide peut encore l’emporter. |
|
On attend chaque méthode et le classement s’applique intégralement. Le plus lent et le plus exhaustif ; |
Deux autres échéances bornent le même éventail : METHOD_TIMEOUT (15 s) est le budget réseau propre à une méthode, et TIMEOUT (60 s) met fin à toute la demande quoi qu’il reste en cours. Une demande qui se termine sans rien n’est pas une erreur ; c’est un correspondant sans clé.
11.12.6. Le cache négatif¶
Lorsqu’une demande se règle sans clé, cette issue est retenue : pendant NEGATIVE_TTL (24 h) après un « il n’y a pas de clé ici » faisant autorité, et pendant le bien plus court ERROR_TTL (1 h) après une défaillance réseau — « le serveur était en panne » mérite d’être retenté plus tôt que « il n’y a pas de clé ».
La règle qui en découle :
Un message pour une adresse ayant une entrée négative fraîche ne se met jamais en pause. Il prend immédiatement le chemin « pas de clé » de l’étape.
Sans cela, chaque message vers un correspondant qui n’a tout simplement pas de clé se mettrait en pause pendant le TIMEOUT complet pour réapprendre une réponse déjà connue — transformant une clé absente en un délai permanent par message pour toute cette correspondance. Le coût accepté est dans l’autre sens : une clé publiée il y a cinq minutes reste invisible jusqu’à ce que l’entrée vieillisse. Abaissez NEGATIVE_TTL si cet arbitrage est mauvais pour votre déploiement ; c’est exactement à cela que sert l’option.
Le même court-circuit se déclenche lorsque l’échéance d’une demande en cours est passée sans que personne n’y ait répondu — la signature d’un déploiement n’exécutant aucun service de découverte. Le message prend le chemin « pas de clé » au lieu de se garer à nouveau, ce qui l’empêche de se garer pour toujours.
11.12.7. Confidentialité des consultations¶
Consulter une adresse indique à quelqu’un que vous êtes sur le point de lui écrire. À qui dépend de la méthode : le DNS le dit aux serveurs faisant autorité du domaine et au chemin de votre résolveur, le WKD le dit au domaine du correspondant lui-même, et un serveur de clés le dit à son exploitant.
Deux contrôles existent, et ils ne sont pas interchangeables :
[pepsi-keydiscovery] ALLOW_DOMAINS/DENY_DOMAINSCelui de l’opérateur, et celui du côté destinataire. Un
ALLOW_DOMAINSnon vide est exclusif — seuls ces domaines sont consultés — etDENY_DOMAINSl’emporte toujours sur lui. C’est le contrôle vers lequel se tourner quand la question est « ne jamais interroger ce domaine ».[stage-encrypt] DISCOVERY = noCelui par utilisateur, redéfinissable par adresse via la couche
pepsi.settings. Les clés en cache sont toujours employées ; un défaut de cache va droit au chemin « pas de clé » au lieu de se garer.
Important
Le nom de l’option invite à la mauvaise lecture. La couche de paramètres par adresse résout un message sortant sur son expéditeur d’enveloppe. Régler DISCOVERY = no pour une adresse signifie donc
« le courrier de cet utilisateur ne déclenche jamais de consultation » —
et non
« ne jamais consulter ce correspondant ».
Il n’existe délibérément pas de liste noire de destinataires par adresse : elle exigerait une nouvelle colonne et une seconde liste pour rester cohérente avec DENY_DOMAINS, et deux listes noires qui se recouvrent et peuvent se contredire valent moins qu’une seule qui ne le peut pas. Le contrôle côté destinataire est celui de l’opérateur, ci-dessus.
11.12.8. Lorsqu’il n’y a pas de résolveur validant¶
La découverte DANE est conditionnée au bit AD : une réponse OPENPGPKEY ou SMIMEA que le résolveur n’a pas validée par DNSSEC est refusée d’emblée. Ce n’est pas la même politique que pour les rapports TLS, où un rua falsifié coûte à un attaquant une copie d’un rapport. Un enregistrement de clé non validé est une clé fournie par quiconque peut répondre pour la zone, ce qui n’améliore en rien le fait de n’avoir aucune clé — une réponse non validée n’est donc pas une réponse plus faible, c’est une absence de réponse.
Pepsi fait confiance à un résolveur validant plutôt que de valider DNSSEC lui-même, le même modèle d’exploitation que celui de sa prise en charge DANE/TLSA pour la sécurité du transport. La conséquence :
Avertissement
Un déploiement sans résolveur validant n’obtient aucun résultat DANE — silencieusement, car la méthode répond consciencieusement « pas de clé » pour chaque adresse, et la source la mieux classée ne l’emporte simplement jamais. pepsi-setup sonde cela et le rapporte, mais le correctif est le vôtre : pointez Pepsi vers un résolveur validant (unbound, ou systemd-resolved avec DNSSEC=yes sur un chemin de confiance), ou retirez dane de SOURCES afin que rien ne l’attende.
11.12.9. Le courrier entrant attend aussi¶
La vérification se met en pause sur le même mécanisme que le chiffrement : lorsqu’une signature entrante vient d’un signataire dont la clé n’est pas en cache, le message se gare pendant que la découverte s’exécute, de sorte que le premier message d’un nouveau correspondant obtienne un vrai verdict au lieu d”unverifiable.
Cela coûte un vrai délai, précisément sur le courrier que quelqu’un attend. Trois choses l’atténuent, et la première est celle vers laquelle se tourner :
INBOUND_TIMEOUTest réglable séparément. Un opérateur qui ressent ce délai peut l’abaisser à quelques secondes sans toucher auTIMEOUTsortant.pepsi-setupavertit au-delà de cinq minutes.Le cache négatif fait que le deuxième message et les suivants d’un signataire inconnu ne se mettent jamais en pause — seul le premier paie.
Les demandes se dédupliquent par adresse, de sorte qu’un déluge venu d’un même signataire coûte une consultation plutôt qu’une par message.
Que la ligne garée contienne du texte clair dépend du protocole. Selon les règles d’empilement, une signature entrante se trouve à l’intérieur du texte chiffré : le déchiffrement doit donc avoir lieu avant même que le signataire — et donc la clé manquante — ne soit connu. Ce qui peut alors être validé diffère :
Une signature S/MIME imbriquée dans une enveloppe survit au déchiffrement comme une couche MIME ordinaire : le message est donc garé avec le texte clair validé et déchiffré exactement une fois.
Une signature OpenPGP ne vit que dans le flux de paquets et disparaît à l’instant où le texte clair en sort. Valider le texte clair détruirait la preuve même que la clé est allée chercher : un tel message est donc garé inchangé et redéchiffré à la reprise. Le second déchiffrement est le prix d’un verdict correct.
Note
Une exposition. Un message garé avec son texte clair validé — le cas S/MIME ci-dessus — reste déchiffré dans pepsi.workqueue pendant jusqu’à INBOUND_TIMEOUT.
C’est la même exposition que la remise créerait de toute façon un instant plus tard : la boîte aux lettres reçoit du texte clair. C’est une fenêtre plus longue, non une nouvelle. Si cette fenêtre compte pour vous, c’est INBOUND_TIMEOUT qui en fixe la durée. Voir pepsi-stage-decrypt pour la règle telle que l’étape l’applique.
11.12.10. Qui emploie le magasin¶
pepsi-keys peer refresh --address exerce tout l’éventail de la découverte — les sources, le classement, la file de demandes et le cache négatif — de bout en bout.
pepsi-stage-encrypt signe le courrier soumis localement au nom de l’auteur de son From: et le chiffre par destinataire ; un message dont la clé de destinataire n’est pas en cache est garé jusqu’à ce qu’un service de découverte règle la demande. pepsi-stage-decrypt est son homologue entrant : elle ouvre le courrier adressé aux destinataires que cet hôte sert — en essayant chacune de leurs identités non révoquées, y compris expirées, car le vieux courrier doit rester lisible — vérifie la signature face au cache peer_key et aux ancres ca_trust, et lit tout le matériel de clés que le message lui-même transporte. Elle se gare de la même façon sur la clé du signataire, de sorte que même le premier message d’un nouveau correspondant obtienne un vrai verdict plutôt qu”unverifiable. Ce qu’elle ne fait pas, c’est écrire dans le magasin : les certificats et les en-têtes Autocrypt: qu’un message transporte sont utilisés en mémoire pour vérifier la signature de ce message, puis abandonnés. pepsi-stage-autocrypt-learn est l’étape qui les apprend.
Rechiffrement pour le stockage. Un message que Pepsi déchiffre est classé par défaut en clair dans la boîte aux lettres, ce qui est précisément ce qui rend le déchiffrement côté serveur utile – le modèle de passerelle suppose que le stockage du courrier est digne de confiance. Pour un utilisateur qui a enregistré sa propre clé de MUA, pepsi-stage-reencrypt comble cette lacune : placé juste avant la remise locale, après chaque étape qui lit le contenu, il scelle un message que l’étape de déchiffrement a ouvert avec la clé de MTA pour la clé de MUA active la plus récente de l’utilisateur, de sorte que la boîte aux lettres ne contient que du texte chiffré que seul son client peut ouvrir. Il n’ajoute aucune signature (le verdict de l’étape de déchiffrement reste dans X-Pepsi-Crypto), ne requiert aucun privilège (la clé est publique), et laisse tranquille le courrier arrivé en clair. Pour un utilisateur sans clé de MUA, [stage-reencrypt] ON_NO_CLIENT_KEY décide : plaintext (la valeur par défaut) le classe comme avant ; bounce le refuse avec un DSN qui ne renvoie ni le corps ni le sujet. L’option se règle par destinataire via pepsi-settings, de sorte qu’un utilisateur peut l’exiger tandis que la valeur par défaut du site reste permissive
pepsi-settings set bob@example.org reencrypt ON_NO_CLIENT_KEY bounce
11.13. Une visite guidée en ligne de commande¶
Donner une identité OpenPGP à une adresse, la regarder, et vérifier que GnuPG convient que la moitié publique est bien une clé
pepsi-keys identity generate alice@example.org \
--protocol openpgp --name 'Alice Example'
pepsi-keys identity list --address alice@example.org
pepsi-keys identity show 1
pepsi-keys identity export 1 | gpg --import
Donner à la même adresse du matériel S/MIME — un certificat de signature et un de chiffrement, chacun auto-signé pour un usage immédiat, avec une demande de certificat sur chaque clé à envoyer à une autorité
pepsi-keys identity generate alice@example.org --protocol smime --csr
Quand l’autorité répond, stocker son certificat sur la clé déjà présente dans le magasin et en faire le matériel de signature principal. Aucune clé n’est renouvelée et rien de ce qui a déjà été envoyé ne cesse d’être lisible
pepsi-keys identity import alice@example.org \
--protocol smime --purpose sign --public alice-sign.pem --primary
Publier l’adresse dans une zone signée
pepsi-keys dns alice@example.org >> example.org.zone
Retirer une clé de signature après le vol d’un portable. La moitié privée est détruite aussitôt ; la moitié publique demeure afin que les signatures du mois dernier se vérifient encore
pepsi-keys identity revoke 1 --reason 'laptop stolen'
pepsi-keys identity show 1 # private material: purged at …
Retirer tout ce qui a dépassé sa validité — la même règle appliquée en masse, et le corps naturel d’une minuterie périodique
pepsi-keys identity retire
Faire confiance à la main à la clé d’un correspondant et l’épingler, afin qu’une réponse de découverte ultérieure ne puisse pas la remplacer
pepsi-keys peer import bob@example.com --protocol openpgp --file bob.pgp --pin
pepsi-keys peer show bob@example.com
Décider vers quelles autorités une signature S/MIME entrante peut remonter
pepsi-keys ca add /etc/ssl/certs/our-ca.pem --label 'Our CA'
pepsi-keys ca list
Et, une fois par an ou après un incident, renouveler la clé de chiffrement de clés
pepsi-keys wrap rotate --dry-run
pepsi-keys wrap rotate
11.14. Récapitulatif de configuration¶
La politique d’algorithmes et de sécurité vit dans [pepsi] et est réservée à l’administrateur : ce sont des décisions de sécurité à valeurs par défaut sûres, non des préférences, et comme la couche de redéfinition par adresse ne redéfinit que les sections [stage-<name>], elles sont structurellement hors de portée de pepsi-stage-edit-settings et de toute surface d’administration web.
Ces options sont CRYPTO_ALLOW_DOWNGRADE, CRYPTO_ALLOW_WEAK_DIGESTS, CRYPTO_MIN_RSA_BITS, CRYPTO_GENERATE_RSA_BITS, CRYPTO_INLINE_PGP, CRYPTO_OPENPGP_ALGORITHM, CRYPTO_SMIME_ALGORITHM et CRYPTO_SMIME_SHARED_KEY.
Les paramètres de garde et de cycle de vie propres au magasin de clés vivent dans [pepsi-crypto] : KEY_WRAP_SECRET et KEY_WRAP_KEY_ID (plus KEY_WRAP_SECRET_<ID> pour une clé retirée), AUTO_CREATE_IDENTITY, IDENTITY_VALIDITY_DAYS (730 par défaut), et les options de localité LOCAL_DOMAINS / RECIPIENT_DELIMITER / TARGETS qui décident à quel compte une adresse appartient. Un déploiement qui ne fait pas de cryptographie de bout en bout peut omettre entièrement la section ; dès qu’elle dit quoi que ce soit, pepsi-setup insiste sur un KEY_WRAP_SECRET utilisable, puisqu’un magasin de clés incapable de stocker des clés est une erreur de configuration plutôt qu’une préférence. La référence exhaustive est pepsi.conf(5).
Le comportement à la manière de pEp et le libre-service sont des options de [stage-encrypt], redéfinissables par adresse : ENABLE_PEP (yes par défaut), ATTACH_KEYS_AS_FILES (no par défaut), KEYS_CONTROL_LOCAL_PART (pepsi-keys par défaut) et RESPONSE_STAGE (non défini signifie ni avis ni commandes par e-mail).
La publication par serveur de clés a sa propre section, [pepsi-keys] : VKS_PUBLISH (désactivé par défaut ; détermine si les clés faites à la main démarrent avec leur téléversement demandé), VKS_SERVER (valant par défaut la première entrée d”[pepsi-keydiscovery] VKS_SERVERS, afin qu’un déploiement ayant déjà choisi un serveur de clés à lire ne le nomme pas deux fois), et les réglages de reprise VKS_MAX_ATTEMPTS, VKS_RETRY_INTERVAL et VKS_BATCH. La publication par Web Key Directory n’exige aucune configuration : elle suit l’indicateur published de chaque identité.
11.15. Voir aussi¶
Les deux étapes qui emploient ce magasin sont pepsi-stage-encrypt (sortante) et pepsi-stage-decrypt (entrante) ; Le portail de repli par lien sécurisé est ce qui se passe lorsqu’un correspondant n’a aucune clé, et Interopérabilité des clients consigne quels clients tiers ont réellement été mesurés face à ce que ces étapes émettent. pepsi-keydisc est le service de découverte, pepsi-keys l’interface en ligne de commande de l’opérateur, et pepsi-stage-vks-confirm le suivi de la confirmation par serveur de clés. Index des RFC associe chaque norme nommée ici au module qui la réalise, et Microsoft Exchange comme passerelle est la forme de déploiement pour laquelle ce magasin a été bâti.
pepsi-keys, pepsi-setup, Architecture, Configuration, pepsi-keys(1), pepsi.conf(5), pepsi-setup(1).