Repository navigation
Conversation
EnvelopedData carried an AEAD cipher: the content was encrypted with AES-GCM and the content key wrapped with RSAES-OAEP. RFC 5083/5084 put an AEAD cipher in AuthEnvelopedData, so a receiving client stops at the content-encryption algorithm before it reaches the recipient's key, and usually reports that as a missing recipient. OpenSSL calls it "cipher parameter initialisation error". Encrypt the content with AES-CBC and wrap the key with RSAES-PKCS1-v1_5, the RFC 8551 baseline. Reading AES-GCM in EnvelopedData stays untouched, so mail already sent still opens. CBC carries no integrity of its own, so a decrypted message now reports integrity: false unless it is signed. That is the standard S/MIME split: the signature answers for the plaintext, and the banner already says when nothing does. Authenticated encryption belongs in AuthEnvelopedData, which this plugin parses but does not build; an interoperable default is worth having before that. setCrypto takes the Crypto object and reads .subtle off it itself, so handing it subtle left liner without getRandomValues wherever the browser guard does not short-circuit. Closes bulwarkmail#41
|
I found one documentation gap and one test to watch once CI runs.
One side effect for whoever decides on this trade-off: mail is marked unprotected when Bulwark Warden is an AI assistant and can be wrong. It answers from the docs, the code and earlier issues, and links its sources. |
|
Thanks, merged. On top I added a test that mail sent with the old GCM format still opens, made the OpenSSL check work on Windows and cover an EC recipient, and made the send stop if pkijs fails to wrap a recipient's key, since it would otherwise leave that key empty without saying so. The help text now says CBC in all languages, and the changelog notes that encrypted mail without a signature now shows the no-integrity note. It will be in the next smime release. |
Fixes #41.
Encrypted mail currently goes out as
EnvelopedDatawith the content under AES-GCM and thecontent key wrapped with RSAES-OAEP. An AEAD cipher belongs in
AuthEnvelopedData(RFC 5083 / RFC 5084), so a receiving client stops at the content-encryption algorithm before it
ever reaches the recipient's key. It then usually reports a missing recipient, which points
everyone at certificates. OpenSSL is blunter:
cipher parameter initialisation error.This switches the wire format to the RFC 8551 baseline, AES-CBC for the content and
RSAES-PKCS1-v1_5 for the key. Both settings values are affected, so there is no configuration
that produces an interoperable message today.
What this gives up, and why
The GCM choice was deliberate:
smimeDecryptreturnsintegrity: truefor an AEAD cipher, thedoc comment names EFAIL, and a test is called "counts AES-GCM in EnvelopedData, as this plugin
sends it". So this is a real trade, not a cleanup, and it should be decided rather than merged
quietly.
recipient can open protects nothing.
EnvelopedDatacomes from the signature, and this plugin signs bydefault. Sign-then-encrypt is the standard answer to EFAIL and does not depend on the content
cipher.
external content off in that case. The mechanism for an unauthenticated
EnvelopedDatais inplace.
If authenticated encryption should stay available,
AuthEnvelopedDatais where RFC 5083 puts it.The plugin parses that format already but does not build it, so that is a separate change, and an
interoperable default seems worth having before it. Happy to follow up with it if you want it.
Changes
smime-encrypt.js: AES-CBC content encryption, key transport without OAEP. Both steps run onthe liner engine, because native WebCrypto has no RSAES-PKCS1-v1_5. Variant
1is passedexplicitly, since pkijs otherwise picks OAEP for an RSA certificate.
crypto-engine.js:setCrypto()takes theCryptoobject and reads.subtleoff it itself.Handing it
subtleleft liner withoutgetRandomValueswherever the browser guard does notshort-circuit. No effect in the browser; without it the encryption path throws under the test
runner.
smime-decrypt.jsis untouched on purpose: AES-GCM inEnvelopedDatastill reads, and stillcounts as integrity protected, so mail already sent keeps working.
encrypt-interop.test.mjs: new. Asserts the OIDs on the wire and has OpenSSL decrypt what theplugin produces, as recipient and as sender, with throwaway keypairs.
keystore.test.mjs: the one test that encoded the old wire format now asserts the new one.manifest.json: theenanddehelp text no longer names GCM. The other 25 locales carrythe same sentence and need their translators.
npx vitest run smime/test/: 12 files, 338 tests, all passing.