Test messages rc-openpgp-*.eml are used to test HTML with remote content. This text documents how they were generated. Dependencies: rnp-cli, rnpkeys: Set PATH to the "bin" directory from a Thunderbird build. Used for generating most messages. gpg: Assumed to be present on the system. Used to generate a legacy no-MDC message and a AEAD/OCB message. Preparation: H_RNP="$(mktemp -d)"; chmod 700 "$H_RNP" H_GPG="$(mktemp -d)"; chmod 700 "$H_GPG" rnpkeys --homedir H_RNP \ --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc rnpkeys --homedir H_RNP \ --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-secret.asc GNUPGHOME=H_GPG gpg --batch --quiet \ --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc The encrypted plaintext (INNER_HTML) ------------------------------------ Every message encrypts the following MIME part (CRLF line endings). It refers to a remote image served by the mochitest http server. Tests looks for #testelement Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 8bit

Integrity-protected secret body.

MIME assembly (templates) ------------------------- The .asc outputs from the commands are wrapped by hand into the templates below. All lines are CRLF-terminated. In the templates, "--BOUNDARY" means two hyphens followed by that template's boundary string, and the final delimiter appends two more hyphens ("--BOUNDARY--"). The boundary strings are arbitrary but must be unique and match between the Content-Type header and the delimiter lines. Template A - PGP/MIME multipart/encrypted (RFC 3156) boundary string: ------------remoteimagepgpmime Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="------------remoteimagepgpmime" --BOUNDARY Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --BOUNDARY Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" --BOUNDARY-- Template B - PGP/MIME multipart/signed (RFC 3156) boundary string: ------------remoteimagesigwrap Content-Type: multipart/signed; micalg="pgp-sha256"; protocol="application/pgp-signature"; boundary="------------remoteimagesigwrap" --BOUNDARY --BOUNDARY Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature --BOUNDARY-- SIGNED_PART is a complete MIME part (e.g. a Template A block). The detached signature (sig.asc) must be computed over SIGNED_PART with its final CRLF removed - that CRLF belongs to the following boundary, not the content (RFC 3156). So the SIGNED_PART argument to the rnp-cli detached-sign command is the part minus its trailing CRLF. Template C - multipart/mixed (attacker wrapper for the sub-part case) boundary string: ------------remoteimagemixed Content-Type: multipart/mixed; boundary="------------remoteimagemixed" --BOUNDARY Content-Type: text/html; charset=utf-8

Attacker-controlled wrapper part.

--BOUNDARY --BOUNDARY-- Envelope: prepend these headers directly in front of the wrapper (the wrapper begins with its own Content-Type header, so there is NO blank line between MIME-Version and the wrapper): From: Alice To: Bob Subject: Date: Wed, 01 Jul 2026 12:00:00 +0000 Message-ID: MIME-Version: 1.0 Subjects (also used verbatim in the Message-ID, spaces -> dashes): rc-openpgp-mdc.eml rc mdc allow rc-openpgp-aead.eml rc aead allow rc-openpgp-nomdc.eml rc nomdc block rc-openpgp-mdc-tampered.eml rc mdc tampered block rc-openpgp-mixed-subpart.eml rc mixed subpart block rc-openpgp-sigwrap-mdc.eml rc sigwrap mdc allow rc-openpgp-sigwrap-nomdc.eml rc sigwrap nomdc block pgp-sig-wrapping-smime-enc.eml pgp sig wrapping smime enc block Per-file commands ----------------- rc-openpgp-mdc.eml (integrity-protected, allowed) Encrypt INNER_HTML with RNP (produces an MDC/SEIPD v1 message), then PGP/MIME multipart/encrypted wrap: rnp-cli --homedir H_RNP \ --encrypt --recipient bob@openpgp.example --armor --output - INNER_HTML rc-openpgp-aead.eml (AEAD/OCB, integrity-protected, allowed) Encrypt INNER_HTML with gpg forcing OCB, then PGP/MIME wrap: GNUPGHOME=H_GPG gpg --batch --yes --trust-model always \ -r bob@openpgp.example --armor --encrypt --force-ocb (INNER_HTML on stdin) rc-openpgp-nomdc.eml (legacy, no integrity, blocked) Encrypt INNER_HTML with gpg in legacy RFC 2440 mode with CAST5 (no MDC), then PGP/MIME wrap: GNUPGHOME=H_GPG gpg --batch --yes --trust-model always \ -r bob@openpgp.example --armor --encrypt --rfc2440 --cipher-algo CAST5 (INNER_HTML on stdin) rc-openpgp-mdc-tampered.eml (broken MDC, blocked) Corrupt the MDC ciphertext with the helper script, then PGP/MIME wrap (template A): python3 tamper-armor.py mdc.asc > mdc-tampered.asc rc-openpgp-mixed-subpart.eml (encrypted sub-part inside attacker MIME, blocked) A multipart/mixed body with two parts: (1) an attacker-controlled text/html part, and (2) the PGP/MIME multipart/encrypted MDC part (identical to rc-openpgp-mdc.eml's body) as a non-top-level sibling. No re-encryption; the MDC part is reused verbatim. rc-openpgp-sigwrap-mdc.eml (OpenPGP signature wrapping an MDC child, allowed) Detached-sign the PGP/MIME MDC part (rc-openpgp-mdc.eml's body) with the RNP detached-signature command above, then multipart/signed wrap. The outer signature need not verify; the child's encryption is what matters. rc-openpgp-sigwrap-nomdc.eml (OpenPGP signature wrapping a no-MDC child, blocked) Same as sigwrap-mdc, but sign the PGP/MIME no-MDC part (rc-openpgp-nomdc.eml's body) instead. Related S/MIME file (written into ../../smime/data/, not this dir) ----------------------------------------------------------------- pgp-sig-wrapping-smime-enc.eml (OpenPGP signature wrapping an inner S/MIME encrypted part; S/MIME is never integrity protected, so it stays blocked; read by smime/browser_remoteContent.js). This one depends on the S/MIME artifact ../../smime/data/alice.remoteimage.env.eml existing first (generated by NSS security/nss/tests/smime/smime.sh together with mailnews/test/data/smime/local-gen.sh). Take the application/pkcs7-mime enveloped part out of that .eml, detached-sign it with the RNP command above, and multipart/signed wrap it. Appendix: raw commands ---------------------- The crypto invocations plus the one tamper step. The MIME assembly (templates A/B/C) and the envelope headers are plain text, applied by hand to these outputs. rnpkeys --homedir "$H_RNP" --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc rnpkeys --homedir "$H_RNP" --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-secret.asc GNUPGHOME="$H_GPG" gpg --batch --quiet --import ../keys/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc rnp-cli --homedir "$H_RNP" --encrypt --recipient bob@openpgp.example --armor --output - INNER_HTML > mdc.asc GNUPGHOME="$H_GPG" gpg --batch --yes --trust-model always -r bob@openpgp.example --armor --encrypt --force-ocb < INNER_HTML > aead.asc GNUPGHOME="$H_GPG" gpg --batch --yes --trust-model always -r bob@openpgp.example --armor --encrypt --rfc2440 --cipher-algo CAST5 < INNER_HTML > nomdc.asc rnp-cli --homedir "$H_RNP" --sign --detach --armor --hash SHA256 -u bob@openpgp.example --output - SIGNED_PART > sig.asc python3 tamper-armor.py mdc.asc > mdc-tampered.asc (INNER_HTML is the encrypted plaintext part above; rnp-cli reads it as a file argument while gpg reads it on stdin. SIGNED_PART is the PGP/MIME part being detached-signed, with its trailing CRLF removed: the MDC part for sigwrap-mdc, the no-MDC part for sigwrap-nomdc, or the S/MIME enveloped part for pgp-sig-wrapping-smime-enc.)