13. Interoperabilität mit Clients¶
Ende-zu-Ende-Verschlüsselung lohnt sich nur, wenn die Gegenseite sie lesen kann. Dieses Kapitel hält fest, was tatsächlich gemessen wurde, gegen welche Versionen und welche Zellen nie ausgeführt wurden.
Wichtig
Eine Zelle in diesen Tabellen bedeutet eines von drei Dingen, und sie sind nicht austauschbar:
- gemessen
Ein automatisierter Test hat das Fremdwerkzeug ausgeführt und das Ergebnis geprüft. Der Testname ist angegeben, sodass die Behauptung erneut ausgeführt werden kann.
- beobachtet
Ein Mensch hat es einmal ausgeführt und aufgeschrieben, was geschah. Kein Test bewacht es; es kann bereits falsch sein.
- nicht ausgeführt
Niemand hat es versucht. Es ist keine Behauptung eines Fehlschlags und ausdrücklich keine Behauptung eines Erfolgs.
Zeilen werden nie durch Schlussfolgerung ausgefüllt. Wenn Pepsis S/MIME-Ausgabe von OpenSSL lesbar ist, folgt daraus nicht, dass Outlook sie liest, und dieses Kapitel tut nicht so, als ob.
13.1. Zusammenfassung¶
OpenPGP gegen gpg 2.4.7 — gemessen, in beide Richtungen, PGP/MIME und inline. Alle sechs Zellen bestehen.
S/MIME gegen OpenSSL 3.5.6 — gemessen, in beide Richtungen, einschließlich
AuthEnvelopedData(AES-GCM),EnvelopedData(AES-CBC), RSA-OAEP, RSA PKCS#1 v1.5, ECDH-KARI sowie abgetrennter und undurchsichtigerSignedData.S/MIME gegen Thunderbird 140.12.0esr — gemessen, nur Entschlüsselung, und das Ergebnis ist ein Negativbefund: Thunderbird liest unsere
EnvelopedData(AES-256-CBC) und kann unseren einkompilierten StandardwertAuthEnvelopedData(AES-256-GCM) überhaupt nicht lesen — stillschweigend, indem es einen leeren Klartext statt eines Fehlers zurückgibt. Deshalb liefert eine paketierte InstallationCRYPTO_ALLOW_DOWNGRADE = yesaus. Signaturverifikation und die OpenPGP-Hälfte sind weiterhin nicht ausgeführt. Siehe Thunderbird: die Schranke, und wie ein GUI-Client ohne GUI getestet wird.Outlook, Apple Mail — nicht ausgeführt. Siehe Outlook und Apple Mail: festgehaltene Beobachtungen, keine Schranken.
Die größte offene Frage — welche Clients S/MIME-AuthEnvelopedData lesen können — hat ihre wichtigste Antwort, und sie lautet nein. Daher wird CRYPTO_ALLOW_DOWNGRADE = yes als paketierter Standardwert in ${DATADIR}/config.d/thunderbird.conf ausgeliefert, während der einkompilierte Standardwert negotiate ist: Eine Installation ist von Haus aus interoperabel, und eine Installation, die AEAD will, löscht eine Datei. Outlook und Apple Mail bleiben ungemessen; nehmen Sie angesichts dieses Ergebnisses an, dass sie es ebenfalls brauchen, bis jemand nachsieht.
13.2. OpenPGP: gpg¶
Getestet gegen gpg (GnuPG) 2.4.7, libgcrypt 1.11.0, wie unter Debian 13 paketiert. Jede Zelle wird von cargo test -p pepsi-crypto ausgeführt; die Tests überspringen sauber (sie scheitern nicht) auf einer Maschine ohne gpg.
Fähigkeit |
Status |
Test |
Anmerkungen |
|---|---|---|---|
gpg verifiziert unsere PGP/MIME-Signatur |
gemessen |
|
RFC 3156 |
gpg entschlüsselt unser PGP/MIME-Chiffrat |
gemessen |
|
Nur nach der SEIPD-Aushandlung weiter unten. |
Wir verifizieren eine PGP/MIME-Signatur von gpg |
gemessen |
|
gpgs eigenes Armor und |
Wir entschlüsseln ein PGP/MIME-Chiffrat von gpg |
gemessen |
|
|
Wir verifizieren einen von gpg klarsignierten Nachrichtentext |
gemessen |
|
Inline, nicht MIME. |
Wir entschlüsseln ein Inline-Chiffrat von gpg |
gemessen |
|
Inline, nicht MIME. |
gpg liest unsere Inline-Ausgabe |
entfällt |
— |
Pepsi liest Inline-OpenPGP, schreibt es aber nie; alles Ausgegebene ist PGP/MIME. Es gibt nichts, was man gpg reichen könnte. |
13.2.1. Die SEIPDv2-Aushandlung, gegen das echte gpg bestätigt¶
CRYPTO_ALLOW_DOWNGRADE steht aus einem konkreten Grund standardmäßig auf negotiate:
INTEROP OBSERVATION: gpg (GnuPG) 2.4.7 CANNOT read our SEIPDv2/AEAD ciphertext
Diese Zeile wird bei jedem Lauf von interop.rs::gpg_cannot_read_seipd_v2 ausgegeben. gpg 2.4.x — also der größte Teil der ausgerollten Basis — kann eine RFC-9580-SEIPDv2-Nachricht überhaupt nicht öffnen. Bedingungslos mit AES-GCM zu verschlüsseln würde daher Mail erzeugen, die die meisten OpenPGP-Benutzer nicht lesen können.
negotiate löst dies aus dem Zertifikat des Empfängers selbst: Ein OpenPGP-Zertifikat nennt, welche SEIPD-Versionen sein Eigentümer lesen kann, sodass Pepsi SEIPDv2 an einen Korrespondenten ausgibt, dessen Schlüssel sagt, dass er damit umgehen kann, und SEIPDv1+MDC an einen, dessen Schlüssel sagt, dass er es nicht kann — und festhält, was geschah (LayerDetail::downgraded), statt stillschweigend herabzustufen. Genau das beweist gpg_decrypts_what_we_emit durchgängig: An ein Nur-SEIPDv1-Zertifikat zu verschlüsseln erzeugt etwas, das das installierte gpg öffnet.
Beachten Sie die bewusst in die beiden Tests eingebaute Asymmetrie. Die positive Behauptung („gpg öffnet, was wir nach der Aushandlung ausgeben“) wird geprüft, denn sie ist eine Behauptung über unser Verhalten. Die negative Behauptung („gpg kann SEIPDv2 nicht lesen“) wird nur ausgegeben, denn sie ist eine Behauptung über fremde Pläne: Ein künftiges gpg, das SEIPDv2 unterstützt, darf diese Suite nicht rot werden lassen.
13.3. S/MIME: OpenSSL¶
Getestet gegen OpenSSL 3.5.6 (7. April 2026). Ausgeführt von cargo test -p pepsi-crypto; überspringt sauber, wenn openssl fehlt.
Fähigkeit |
Status |
Test |
Anmerkungen |
|---|---|---|---|
|
gemessen |
|
AES-GCM- |
|
gemessen |
|
Abgetrennt und undurchsichtig. |
|
gemessen |
|
|
Wir öffnen, was |
gemessen |
|
Führt das installierte OpenSSL aus, verfolgt also Änderungen stromaufwärts. |
Wir öffnen eingefrorene OpenSSL-Ausgabe |
gemessen |
|
Sechs eingecheckte |
Wir verifizieren eingefrorene OpenSSL-Signaturen |
gemessen |
|
Abgetrennt, undurchsichtig und EC. |
…einschließlich P-384 |
gemessen |
|
Ein separater Test, weil mehrere nationale S/MIME-PKIs P-384-Zertifikate ausstellen. |
13.3.1. Die AuthEnvelopedData-Frage¶
Ein Client hat sie beantwortet — Thunderbird, weiter unten, und die Antwort war nein, weshalb eine paketierte Installation CRYPTO_ALLOW_DOWNGRADE = yes ausliefert. Der Rest der Tabelle ist weiterhin offen.
Pepsis einkompilierter Standardwert ist S/MIME-AuthEnvelopedData (AES-256-GCM, RFC 5083/5084). Anders als bei OpenPGP gibt es nichts auszuhandeln: Ein X.509-Zertifikat kündigt keine Inhaltsverschlüsselungsfähigkeit an, sodass es im Schlüssel des Empfängers kein Signal zu lesen gibt. CRYPTO_ALLOW_DOWNGRADE = negotiate verhält sich für S/MIME daher wie no, und eine Installation, deren Korrespondenten AEAD nicht lesen können, muss CRYPTO_ALLOW_DOWNGRADE = yes global setzen, was jede S/MIME-Nachricht auf AES-256-CBC-EnvelopedData herabstuft.
Bemerkung
Eine installierte Pepsi setzt diesen Schalter bereits. Das Thunderbird-Ergebnis weiter unten ist schwerwiegend genug — unlesbar, und das stillschweigend —, dass yes als paketierter Standardwert in ${DATADIR}/config.d/thunderbird.conf ausgeliefert wird, die vor pepsi.conf geparst wird. Der Code-Standardwert bleibt negotiate: Löschen Sie diese eine Datei, und die Installation ist sofort wieder auf AEAD, was für eine Installation, die ihre Korrespondenten kennt, der richtige Zug ist. Dies ist der einzige Ort, an dem die Wahl getroffen wird — pepsi-setup schreibt die Option bewusst nicht, denn alles, was es in pepsi.conf setzte, überschriebe die paketierte Datei, ohne es zu sagen.
Ob eine gegebene Installation diesen Schalter braucht, hängt vollständig davon ab, welche Clients ihre Korrespondenten verwenden — und dafür ist diese Tabelle da:
Client |
Liest |
Grundlage |
|---|---|---|
OpenSSL 3.5.6 |
ja |
gemessen ( |
Thunderbird 140.12.0esr (NSS) |
nein |
gemessen ( |
Outlook (Desktop) |
unbekannt |
nicht ausgeführt |
Outlook Web |
unbekannt |
nicht ausgeführt |
Apple Mail / iOS Mail |
unbekannt |
nicht ausgeführt |
13.3.2. Thunderbird kann es nicht lesen¶
Gemessen gegen Thunderbird 140.12.0esr, sowohl für RSA-Schlüsseltransport als auch für ECDH-Schlüsselvereinbarung, an Chiffrat, das dieses Crate erzeugt hat:
EnvelopedDatamit AES-256-CBC — dieCRYPTO_ALLOW_DOWNGRADE = yes-Ausgabe — wird korrekt gelesen;AuthEnvelopedDatamit AES-256-GCM — die Standardausgabe — wird überhaupt nicht gelesen.
Thunderbirds S/MIME ist NSS, sodass dies eine Eigenschaft von NSS ist und nicht der Benutzeroberfläche, und es ist sehr unwahrscheinlich, dass sie sich in einer ESR-Serie ändert.
Warnung
NSS scheitert stillschweigend. Es wirft bei einer AuthEnvelopedData-Nachricht keinen Fehler; es gibt einen leeren Klartext zurück. Ein Aufrufer, der nur auf eine Ausnahme prüft, schließt daraus, dass die Nachricht zu nichts entschlüsselt wurde. Unser eigener Test musste so geschrieben werden, dass er „geöffnet, null Bytes“ als Fehlschlag behandelt — siehe die Anmerkung zu plaintext() in thunderbird.rs — und ein Operator, der die Meldung eines Benutzers „die Nachricht ist leer“ untersucht, sollte dies verdächtigen, bevor er die Nachricht verdächtigt.
Die Konsequenz für Installationen ist konkret: Wenn Ihre Korrespondenten Mail in Thunderbird lesen, brauchen Sie CRYPTO_ALLOW_DOWNGRADE = yes. Anders als bei OpenPGP gibt es nichts auszuhandeln — ein X.509-Zertifikat kündigt keine Inhaltsverschlüsselungsfähigkeit an —, sodass Pepsi dies nicht je Empfänger erkennen und für Sie wählen kann.
Weil es nicht erkannt werden kann und weil der Fehlschlag stillschweigend erfolgt, ist das der Wert, den eine paketierte Installation ausliefert: ${DATADIR}/config.d/thunderbird.conf setzt ihn, und config.d wird vor pepsi.conf geparst. Der Tausch ist bewusst und in einem Schritt umkehrbar:
Die Datei ist ein Standardwert, keine Entscheidung —
rmsie (oder schreiben SieCRYPTO_ALLOW_DOWNGRADE = negotiateinpepsi.conf, das danach geparst wird und gewinnt), und jede S/MIME-Nachricht ist wieder authentifizierte Verschlüsselung;auf der OpenPGP-Seite kostet es nichts. Die Verschlüsselungsstage fordert den Container
autoan, undnegotiateundyeslösen das identisch auf — SEIPDv2 für ein Zertifikat, das es ankündigt, SEIPDv1+MDC nur für eines, das es nicht tut. Die Aushandlung je Empfänger, die OpenPGP leisten kann, wird nicht aufgegeben, um ein Problem zu beheben, das S/MIME nicht vermeiden kann;das einzige andere, was
yesändert, ist, dass eine eingehende 3DES-S/MIME-Nachricht entschlüsselbar wird statt abgelehnt. 3DES wird unter keiner Einstellung je ausgegeben, und RC2, einfaches DES, ungeschütztes OpenPGP-SED und MD2 bleiben unter allen abgelehnt.
Wenn NSS AEAD-Unterstützung bekommt, sagt die Schranke es beim ersten erfolgreichen Lauf — sie gibt die AES-256-GCM-Zelle als READ CORRECTLY aus, statt rot zu werden — und die paketierte Datei kann entfallen.
Warnung
Outlook und Apple Mail bleiben ungemessen. Ein Operator, dessen Benutzer mit ihnen S/MIME austauschen, sollte CRYPTO_ALLOW_DOWNGRADE = yes als die sichere Einstellung behandeln und es mit einer echten Nachricht von Hand überprüfen. Pepsi hält den ausgegebenen Container im state.crypto jeder Nachricht fest, sodass im Nachhinein prüfbar statt geraten ist, was tatsächlich hinausging.
13.4. Thunderbird: die Schranke, und wie ein GUI-Client ohne GUI getestet wird¶
Thunderbird ist neben gpg und OpenSSL eine Freigabeschranke: Es ist der am weitesten verbreitete Client, der beide Protokolle spricht, und derjenige, der am ehesten ein MIME-, Content-Transfer-Encoding- oder micalg-Problem aufdeckt, das gpg und OpenSSL bereitwillig hinnehmen.
Die Schranke ist pepsi-crypto/tests/thunderbird.rs, ausgeführt von make integrationtests (oder direkt mit PEPSI_INTEROP_THUNDERBIRD=1 cargo test -p pepsi-crypto --test thunderbird -- --nocapture). Getestete Version: Thunderbird 140.12.0esr.
Die Schranke braucht weder ein echtes Profil noch ein Display. Thunderbirds S/MIME ist NSS, und NSS‘ CMS-Dekodierer ist aus chrome-privilegiertem JavaScript als @mozilla.org/nsCMSDecoderJS;1 erreichbar, sodass der Test überhaupt keine Benutzeroberfläche automatisiert:
pepsi-cryptogibt die Nachricht aus — unsere Ausgabe, kein Fixture;openssl pkcs12paketiert den passenden Fixture-Schlüssel;Thunderbird startet mit
--headless --marionette -remote-allow-system-accessgegen ein Wegwerfprofil;ein kleiner Marionette-Client (längenpräfigiertes JSON über TCP, im Test selbst implementiert — er ist zu klein, um eine Abhängigkeit wert zu sein) führt ein Chrome-Skript aus, das den Schlüssel in die NSS-Datenbank dieses Profils importiert und den Dekodierer aufruft;
was zurückkommt, wird mit dem Klartext verglichen, der verschlüsselt wurde.
Er lebt in make integrationtests statt in make check, nur weil das Starten von Thunderbird etwa fünfzehn Sekunden kostet. Ohne PEPSI_INTEROP_THUNDERBIRD=1 oder ohne thunderbird und openssl auf dem PATH gibt er aus, warum er überspringt, und kehrt zurück.
Zwei Fallen, auf die jeder stößt, der diesen Test erweitert:
nsICMSDecoderJSakkumuliert über Aufrufe hinweg. Eine Dekodierer-Instanz für mehrere Nachrichten wiederzuverwenden lässt die zweite den Klartext der ersten an ihren eigenen angehängt melden — was sich als Erfolg liest. Eine Instanz je Nachricht.NSS meldet einen unlesbaren Container als leeres Ergebnis, nicht als Fehler. Ein Test, der nur auf eine geworfene Ausnahme prüft, besteht bei einer Nachricht, die nie entschlüsselt wurde.
Was weiterhin nicht gemessen ist: ob Thunderbird verifiziert, was wir signieren. nsICMSDecoderJS legt nur decrypt offen, und NSS‘ Signaturverifikation aus einem Chrome-Skript zu erreichen bedeutet, durch die Nachrichtenanzeige-Pipeline zu gehen, die sehr wohl ein Profil mit einem konfigurierten Konto braucht. Die OpenPGP-Hälfte ist gegen Thunderbirds RNP ebenfalls ungemessen, wenngleich gpg — eine völlig unabhängige Implementierung — für dieses Protokoll beide Richtungen abdeckt.
Die Hälfte des Mechanismus mit eingefrorenem Korpus ist gebaut und funktioniert: pepsi-crypto/tests/data/interop/ enthält Exemplare samt dem Urteil, das jedes erzeugen muss, und interop.rs::the_frozen_third_party_corpus_still_opens läuft sie ab. Er enthält derzeit drei gpg-2.4.7-Exemplare und kein Thunderbird-Exemplar — eines hinzuzufügen erfordert keine Codeänderung; siehe die README in jenem Verzeichnis.
13.4.1. Warum der eingefrorene Korpus wichtig ist¶
Wenn die Schranke fehlschlägt, muss sie sagen, wessen Seite sich geändert hat, und ein Live-Test kann das nicht: Ein rotes Ergebnis bedeutet entweder, dass unser Erzeuger regressiert ist, oder dass der Client sich bewegt hat, und der Lauf kann sie nicht unterscheiden. Die beiden Hälften zusammen können es —
der eingefrorene Korpus wird von unserem Leser geöffnet, und die Bytes liegen in Git, sodass ein Fehlschlag dort bedeutet, dass wir regressiert sind; und
die Live-Zellen führen das aktuelle Werkzeug aus, sodass ein Fehlschlag dort bedeutet, dass sie sich bewegt haben.
Die getestete Version muss in diesem Kapitel festgehalten und im Harness gepinnt werden, und ein Exemplar ihrer Ausgabe muss in den Korpus eingefroren werden. Diesen Pin anzuheben ist dann eine bewusste Handlung mit einem eigenen festzuhaltenden Ergebnis, und das bewahrt die Schranke vor dem Verfaulen.
13.5. Outlook und Apple Mail: festgehaltene Beobachtungen, keine Schranken¶
Diese sind absichtlich aus der Automatisierung herausgehalten: Sie können nicht auf Hardware laufen, die sich in der CI halten lässt, und eine Schranke, die nicht tatsächlich geprüft wird, ist schlimmer als eine ehrliche Beobachtung. Sie sollen zur Release-Zeit von Hand ausgeübt werden, mit hier festgehaltenen Versionen und Fehlschlägen.
Client |
Protokoll |
Status |
Anmerkungen |
|---|---|---|---|
Outlook (Desktop) |
S/MIME |
nicht ausgeführt |
Keine Version getestet. Siehe Die AuthEnvelopedData-Frage. |
Outlook Web |
S/MIME |
nicht ausgeführt |
Keine Version getestet. |
Apple Mail / iOS Mail |
S/MIME |
nicht ausgeführt |
Keine Version getestet. |
Ein Webmail ohne Krypto-Unterstützung |
— |
nicht ausgeführt |
Dies ist der Fall, für den es Das Secure-Link-Ausweichportal gibt: Der Empfänger hat weder Schlüssel noch Client-Unterstützung und bekommt eine Benachrichtigungsmail mit einem Link statt eines Chiffrats, das er nicht öffnen kann. |
13.6. Kodierungsregeln, die die Interoperabilität erzwungen hat¶
Mehrere Regeln im Erzeuger existieren nur, weil eine zweite Implementierung das Naheliegende ablehnte. Sie werden hier festgehalten, weil sie im Code willkürlich aussehen und teuer wiederzuentdecken sind:
Kanonisches CRLF vor dem Signieren. Eine Signatur deckt die MIME-Entität genau so ab, wie sie auf der Leitung erscheint. Sequoias Armor-Writer gibt bloßes LF aus, sodass signierte Bytes vor dem Hashen durch einen CRLF-Kanonisierer laufen; ohne ihn berechnet gpg einen anderen Digest und meldet eine schlechte Signatur über korrekten Daten.
Die signierte Entität wird byteweise bewahrt. Die dem Verifizierer gereichten Bytes sind die Bytes zwischen den MIME-Grenzen, unangetastet — kein Neu-Umbruch, keine Header-Umordnung, keine Änderung der Transferkodierung. Jedes „Aufräumen“ eines
multipart/signed-Teils macht ihn ungültig.``micalg`` ist beratend. Es wird bei der Ausgabe korrekt geschrieben und bei der Eingabe ignoriert: Der echte Digest steckt in der Signatur. Clients machen es falsch, und wegen eines abweichenden
micalgabzulehnen lehnt gültige Mail ab.``-recip`` statt eines nachgestellten Positionsarguments, im OpenSSL-Testharness: OpenSSLs Optionsparser hört beim ersten Positionsargument auf, was
-binarystillschweigend verwirft und verwirrende Fehlschläge erzeugt.
13.7. Die gemessenen Zeilen reproduzieren¶
Alles als gemessen Markierte läuft aus einem Checkout ohne Netz und ohne Installation:
cargo test -p pepsi-crypto # every measured cell
cargo test -p pepsi-crypto --test interop -- --nocapture # OpenPGP, verbose
Beide überspringen, statt zu scheitern, wenn gpg oder OpenSSL fehlt, sodass eine Maschine ohne sie dennoch den Rest der Suite ausführt. Um zu sehen, welche Zellen tatsächlich ausgeführt wurden, geben Sie --nocapture an: Eine übersprungene Zelle gibt ihren Grund aus.
Um die eingefrorenen gpg-Exemplare gegen ein neueres gpg zu erneuern:
PEPSI_CRYPTO_WRITE_INTEROP_CORPUS=1 cargo test -p pepsi-crypto --test interop
Das schreibt die Exemplare und die festgehaltene Erzeugerversion neu; es ist eine bewusste Handlung, deren Diff ein Prüfer sieht, nie Teil eines gewöhnlichen Laufs.
13.8. Siehe auch¶
Schlüsselverwaltung — die Identitäts-, Verwahrungs- und Entdeckungsschicht hinter den hier gemessenen Nachrichten. pepsi-stage-encrypt und pepsi-stage-decrypt — die beiden Stages, deren Aus- und Eingabe diese Tabellen beschreiben. RFC-Index — aus welcher Spezifikation jeder Container und Parameter stammt. Testsuite — wie der Rest der Suite aufgebaut ist.