Hi gnupg-users, In short: I experimented and found that `gpg -d > results.txt` will write to `results.txt` even if the verification (signature or MDC) of the wrapped message fails.
This is confirmed by the output[6] of a short script that I wrote[5], which was inspired by a Qubes thread[1]. I think that I understand why this happens[2], why integrity is important[3], and that writing to a temporary file (and checking the exit code before use) is the recommended way to handle this. (Although, I don't think that this behavior is as prominently documented as I think that it should be[4]) However, I notice that gpg's `--decrypt` flag does not have a way to specify the expected signer, meaning that (without parsing stderr) there is no way to automatically verify the authenticity of an encrypted file, only the integrity (by observing the exit code). I guess that my questions are: 1. Am I missing something? 2. Do I need to manually verify that PGP blocks in my emails match the sender to avoid https://articles.59.ca/doku.php?id=pgpfan:forwarding or simply email sender spoofing? 3. May I, respectfully, request improvement to the documentation? Warmly, Tennyson 🌸 P.S. Should I start my emails with "Hello [name] <[email@address]>" to ensure that the intended sender is included in the signed message? [1] https://groups.google.com/g/qubes-devel/c/TQr_QcXIVww [2] My understanding/mental-model is that our order of operations is: 1. Decrypted text is streamed and a live hash value is computed while the text is sent to /dev/stdout 2. The live hash value is checked 3. GPG exits with zero or non-zero code depending on the results of the check [3] I often see it recommended to sign messages before encrypting to ensure integrity (and, as a byproduct, authentication). It seems that this was so crucial that MDCs are now enabled by default. My understanding is that MDCs provide integrity guarantees without signing. It seems that a lack of integrity allows the injection of text into the message, such as Efail: https://efail.de/ [4] AI recommended that I pipe directly from GPG to my consumption program, and made the assertion that no text would be output until verification passed. Asserting the negative to AI in a new chat and asking for it to find a citation didn't immediately work, nor did my own websearching. As a result, I felt compelled to write the attached script to verify, and to email this list. I did not see the answer in the man page, nor the manual: https://www.gnupg.org/gph/en/manual.html And it seems like there is confusion/uncertainty from the creator of age: https://words.filippo.io/age-authentication/#fn:mdc As well as in the (old) Qubes thread that I linked. [5] ```bash #!/usr/bin/env bash set -xeuo pipefail IFS=$'\n\t' gpg --version cd "$(mktemp -d)" dd if=/dev/zero of=test.bin bs=1k count=1k gpg -c --batch --yes --passphrase pass --compress-level 0 test.bin cp test.bin.gpg test-hacked.bin.gpg ls -al dd if=/dev/zero of=test-hacked.bin.gpg bs=1 count=1 seek=666999 conv=notrunc xxd test.bin.gpg > test.bin.gpg.xxd xxd test-hacked.bin.gpg > test-hacked.bin.gpg.xxd diff test.bin.gpg.xxd test-hacked.bin.gpg.xxd || : gpg -d --batch --yes --passphrase pass test-hacked.bin.gpg > dangerousOutput.bin || : ls -al du -hs dangerousOutput.bin ``` [6] ``` $ ./gpg-test.sh + IFS=' ' + gpg --version gpg (GnuPG) 2.4.8 libgcrypt 1.10.3 Copyright (C) 2025 g10 Code GmbH License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Home: /Users/tennyson/.gnupg Supported algorithms: Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256 Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 Compression: Uncompressed, ZIP, ZLIB, BZIP2 ++ mktemp -d + cd /var/folders/30/9ql0lf1n509_v6q0br5pvv_r0000gn/T/tmp.6iExzO756J + dd if=/dev/zero of=test.bin bs=1k count=1k 1024+0 records in 1024+0 records out 1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.002648 s, 396 MB/s + gpg -c --sign --batch --yes --passphrase pass --compress-level 0 test.bin gpg: AES256.CFB encryption will be used + cp test.bin.gpg test-hacked.bin.gpg + ls -al total 3080 drwx------ 5 tennyson staff 160 Oct 16 18:59 . drwx------ 417 tennyson staff 13344 Oct 16 18:59 .. -rw-r--r-- 1 tennyson staff 1048900 Oct 16 18:59 test-hacked.bin.gpg -rw-r--r-- 1 tennyson staff 1048576 Oct 16 18:59 test.bin -rw-r--r-- 1 tennyson staff 1048900 Oct 16 18:59 test.bin.gpg + dd if=/dev/zero of=test-hacked.bin.gpg bs=1 count=1 seek=666999 conv=notrunc 1+0 records in 1+0 records out 1 byte copied, 0.000159 s, 6.3 kB/s + xxd test.bin.gpg + xxd test-hacked.bin.gpg + diff test.bin.gpg.xxd test-hacked.bin.gpg.xxd 41688c41688 < 000a2d70: 720c cfea 9d8e e778 4373 768f c741 f207 r......xCsv..A.. --- > 000a2d70: 720c cfea 9d8e e700 4373 768f c741 f207 r.......Csv..A.. + : + gpg -d --batch --yes --passphrase pass test-hacked.bin.gpg gpg: AES256.CFB encrypted data gpg: encrypted with 1 passphrase gpg: Signature made Thu Oct 16 18:59:32 2025 CDT gpg: using EDDSA key AFFC0B718C7AF7AAF8B6EC2C76FA7C3D275E4D55 gpg: BAD signature from "Tennyson T Bardwell (onxy machine) <[email protected]>" [ultimate] + : + ls -al total 12816 drwx------ 8 tennyson staff 256 Oct 16 18:59 . drwx------ 417 tennyson staff 13344 Oct 16 18:59 .. -rw-r--r-- 1 tennyson staff 1048576 Oct 16 18:59 dangerousOutput.bin -rw-r--r-- 1 tennyson staff 1048900 Oct 16 18:59 test-hacked.bin.gpg -rw-r--r-- 1 tennyson staff 4457864 Oct 16 18:59 test-hacked.bin.gpg.xxd -rw-r--r-- 1 tennyson staff 1048576 Oct 16 18:59 test.bin -rw-r--r-- 1 tennyson staff 1048900 Oct 16 18:59 test.bin.gpg -rw-r--r-- 1 tennyson staff 4457864 Oct 16 18:59 test.bin.gpg.xxd + du -hs dangerousOutput.bin 1.0M dangerousOutput.bin ``` _______________________________________________ Gnupg-users mailing list [email protected] https://lists.gnupg.org/mailman/listinfo/gnupg-users
