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 undurchsichtiger SignedData.

  • 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 Standardwert AuthEnvelopedData (AES-256-GCM) überhaupt nicht lesen — stillschweigend, indem es einen leeren Klartext statt eines Fehlers zurückgibt. Deshalb liefert eine paketierte Installation CRYPTO_ALLOW_DOWNGRADE = yes aus. 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

pgp.rs::gpg_verifies_our_detached_signature

RFC 3156 multipart/signed.

gpg entschlüsselt unser PGP/MIME-Chiffrat

gemessen

interop.rs::gpg_decrypts_what_we_emit

Nur nach der SEIPD-Aushandlung weiter unten.

Wir verifizieren eine PGP/MIME-Signatur von gpg

gemessen

interop.rs::we_verify_a_gpg_signature

gpgs eigenes Armor und micalg.

Wir entschlüsseln ein PGP/MIME-Chiffrat von gpg

gemessen

interop.rs::we_decrypt_what_gpg_emits

Wir verifizieren einen von gpg klarsignierten Nachrichtentext

gemessen

interop.rs::we_verify_a_gpg_clearsigned_message

Inline, nicht MIME.

Wir entschlüsseln ein Inline-Chiffrat von gpg

gemessen

interop.rs::we_decrypt_a_gpg_inline_message

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

openssl cms entschlüsselt unser Chiffrat

gemessen

smime.rs::openssl_decrypts_what_we_emit

AES-GCM-AuthEnvelopedData mit RSA-OAEP und mit PKCS#1 v1.5; AES-CBC-EnvelopedData; ECDH-KARI.

openssl cms verifiziert unsere Signatur

gemessen

smime.rs::openssl_verifies_what_we_sign

Abgetrennt und undurchsichtig.

openssl liest unsere Nur-Zertifikate-Nachricht

gemessen

smime.rs::openssl_reads_our_certs_only_message

Wir öffnen, was openssl jetzt ausgibt

gemessen

smime.rs::we_open_what_openssl_emits_right_now

Führt das installierte OpenSSL aus, verfolgt also Änderungen stromaufwärts.

Wir öffnen eingefrorene OpenSSL-Ausgabe

gemessen

smime.rs::every_openssl_produced_message_opens

Sechs eingecheckte smime-kat-openssl-*.der-Musterlösungen (RSA und EC, GCM und CBC, RSA-OAEP sowie beide ECDH-KDFs), sodass der Leser auch auf Maschinen ganz ohne OpenSSL getestet wird. Über die gesamte S/MIME-Suite sind zwölf solcher Exemplare eingecheckt.

Wir verifizieren eingefrorene OpenSSL-Signaturen

gemessen

smime.rs::every_openssl_produced_signature_verifies

Abgetrennt, undurchsichtig und EC.

…einschließlich P-384

gemessen

smime.rs::p384_signatures_verify

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 AuthEnvelopedData?

Grundlage

OpenSSL 3.5.6 cms

ja

gemessen (smime.rs::openssl_decrypts_what_we_emit, Fälle gcm-oaep und gcm-kari)

Thunderbird 140.12.0esr (NSS)

nein

gemessen (thunderbird.rs::thunderbird_reads_our_smime); siehe unten

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:

  • EnvelopedData mit AES-256-CBC — die CRYPTO_ALLOW_DOWNGRADE = yes-Ausgabe — wird korrekt gelesen;

  • AuthEnvelopedData mit 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 — rm sie (oder schreiben Sie CRYPTO_ALLOW_DOWNGRADE = negotiate in pepsi.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 auto an, und negotiate und yes lö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:

  1. pepsi-crypto gibt die Nachricht aus — unsere Ausgabe, kein Fixture;

  2. openssl pkcs12 paketiert den passenden Fixture-Schlüssel;

  3. Thunderbird startet mit --headless --marionette -remote-allow-system-access gegen ein Wegwerfprofil;

  4. 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;

  5. 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:

  • nsICMSDecoderJS akkumuliert ü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 micalg abzulehnen lehnt gültige Mail ab.

  • ``-recip`` statt eines nachgestellten Positionsarguments, im OpenSSL-Testharness: OpenSSLs Optionsparser hört beim ersten Positionsargument auf, was -binary stillschweigend 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.