test/crypto: add test for corrupted signatures
[notmuch] / test / T350-crypto.sh
1 #!/usr/bin/env bash
2
3 # TODO:
4 # - decryption/verification with signer key not available
5 # - verification of signatures from expired/revoked keys
6
7 test_description='PGP/MIME signature verification and decryption'
8 . ./test-lib.sh || exit 1
9
10 add_gnupg_home ()
11 {
12     local output
13     [ -d ${GNUPGHOME} ] && return
14     _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
15     at_exit_function _gnupg_exit
16     mkdir -m 0700 "$GNUPGHOME"
17     gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
18     test_debug "cat $GNUPGHOME/import.log"
19     if (gpg --quick-random --version >/dev/null 2>&1) ; then
20         echo quick-random >> "$GNUPGHOME"/gpg.conf
21     elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
22         echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
23     fi
24     echo no-emit-version >> "$GNUPGHOME"/gpg.conf
25 }
26
27 ##################################################
28
29 add_gnupg_home
30 # Change this if we ship a new test key
31 FINGERPRINT="5AEAB11F5E33DCE875DDB75B6D92612D94E46381"
32
33 test_begin_subtest "emacs delivery of signed message"
34 test_expect_success \
35 'emacs_fcc_message \
36     "test signed message 001" \
37     "This is a test signed message." \
38     "(mml-secure-message-sign)"'
39
40 test_begin_subtest "signature verification"
41 output=$(notmuch show --format=json --verify subject:"test signed message 001" \
42     | notmuch_json_show_sanitize \
43     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
44 expected='[[[{"id": "XXXXX",
45  "match": true,
46  "excluded": false,
47  "filename": ["YYYYY"],
48  "timestamp": 946728000,
49  "date_relative": "2000-01-01",
50  "tags": ["inbox","signed"],
51  "headers": {"Subject": "test signed message 001",
52  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
53  "To": "test_suite@notmuchmail.org",
54  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
55  "body": [{"id": 1,
56  "sigstatus": [{"status": "good",
57  "fingerprint": "'$FINGERPRINT'",
58  "created": 946728000}],
59  "content-type": "multipart/signed",
60  "content": [{"id": 2,
61  "content-type": "text/plain",
62  "content": "This is a test signed message.\n"},
63  {"id": 3,
64  "content-type": "application/pgp-signature",
65  "content-length": "NONZERO"}]}]},
66  []]]]'
67 test_expect_equal_json \
68     "$output" \
69     "$expected"
70
71 test_begin_subtest "detection of modified signed contents"
72 emacs_fcc_message \
73     "bad signed message 001" \
74     "Incriminating stuff. This is a test signed message." \
75     "(mml-secure-message-sign)"
76
77 file=$(notmuch search --output=files subject:"bad signed message 001")
78
79 sed -i 's/Incriminating stuff. //' ${file}
80
81 output=$(notmuch show --format=json --verify subject:"bad signed message 001" \
82     | notmuch_json_show_sanitize \
83     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
84 expected='[[[{"id": "XXXXX",
85  "match": true,
86  "excluded": false,
87  "filename": ["YYYYY"],
88  "timestamp": 946728000,
89  "date_relative": "2000-01-01",
90  "tags": ["inbox","signed"],
91  "headers": {"Subject": "bad signed message 001",
92  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
93  "To": "test_suite@notmuchmail.org",
94  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
95  "body": [{"id": 1,
96  "sigstatus": [{"status": "bad",
97  "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'"}],
98  "content-type": "multipart/signed",
99  "content": [{"id": 2,
100  "content-type": "text/plain",
101  "content": "This is a test signed message.\n"},
102  {"id": 3,
103  "content-type": "application/pgp-signature",
104  "content-length": "NONZERO"}]}]},
105  []]]]'
106 test_expect_equal_json \
107     "$output" \
108     "$expected"
109
110 test_begin_subtest "corrupted pgp/mime signature"
111 emacs_fcc_message \
112     "bad signed message 002" \
113     "Incriminating stuff. This is a test signed message." \
114     "(mml-secure-message-sign)"
115
116 file=$(notmuch search --output=files subject:"bad signed message 002")
117
118 awk '/-----BEGIN PGP SIGNATURE-----/{flag=1;print;next} \
119      /-----END PGP SIGNATURE-----/{flag=0;print;next} \
120      flag{gsub(/[A-Za-z]/,"0");print}!flag{print}' $file > $file.new
121
122 rm $file
123 mv $file.new $file
124
125 output=$(notmuch show --format=json --verify subject:"bad signed message 002" \
126     | notmuch_json_show_sanitize \
127     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
128 expected='[[[{"id": "XXXXX",
129  "match": true,
130  "excluded": false,
131  "filename": ["YYYYY"],
132  "timestamp": 946728000,
133  "date_relative": "2000-01-01",
134  "tags": ["inbox","signed"],
135  "headers": {"Subject": "bad signed message 002",
136  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
137  "To": "test_suite@notmuchmail.org",
138  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
139  "body": [{"id": 1,
140  "sigstatus": [],
141  "content-type": "multipart/signed",
142  "content": [{"id": 2,
143  "content-type": "text/plain",
144  "content": "Incriminating stuff. This is a test signed message.\n"},
145  {"id": 3,
146  "content-type": "application/pgp-signature",
147  "content-length": "NONZERO"}]}]},
148  []]]]'
149 test_expect_equal_json \
150     "$output" \
151     "$expected"
152
153 test_begin_subtest "signature verification with full owner trust"
154 # give the key full owner trust
155 echo "${FINGERPRINT}:6:" | gpg --no-tty --import-ownertrust >>"$GNUPGHOME"/trust.log 2>&1
156 gpg --no-tty --check-trustdb >>"$GNUPGHOME"/trust.log 2>&1
157 output=$(notmuch show --format=json --verify subject:"test signed message 001" \
158     | notmuch_json_show_sanitize \
159     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
160 expected='[[[{"id": "XXXXX",
161  "match": true,
162  "excluded": false,
163  "filename": ["YYYYY"],
164  "timestamp": 946728000,
165  "date_relative": "2000-01-01",
166  "tags": ["inbox","signed"],
167  "headers": {"Subject": "test signed message 001",
168  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
169  "To": "test_suite@notmuchmail.org",
170  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
171  "body": [{"id": 1,
172  "sigstatus": [{"status": "good",
173  "fingerprint": "'$FINGERPRINT'",
174  "created": 946728000,
175  "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
176  "content-type": "multipart/signed",
177  "content": [{"id": 2,
178  "content-type": "text/plain",
179  "content": "This is a test signed message.\n"},
180  {"id": 3,
181  "content-type": "application/pgp-signature",
182  "content-length": "NONZERO"}]}]},
183  []]]]'
184 test_expect_equal_json \
185     "$output" \
186     "$expected"
187
188 test_begin_subtest "signature verification with signer key unavailable"
189 # move the gnupghome temporarily out of the way
190 mv "${GNUPGHOME}"{,.bak}
191 output=$(notmuch show --format=json --verify subject:"test signed message 001" \
192     | notmuch_json_show_sanitize \
193     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
194 expected='[[[{"id": "XXXXX",
195  "match": true,
196  "excluded": false,
197  "filename": ["YYYYY"],
198  "timestamp": 946728000,
199  "date_relative": "2000-01-01",
200  "tags": ["inbox","signed"],
201  "headers": {"Subject": "test signed message 001",
202  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
203  "To": "test_suite@notmuchmail.org",
204  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
205  "body": [{"id": 1,
206  "sigstatus": [{"status": "error",
207  "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",
208  "errors": {"key-missing": true}}],
209  "content-type": "multipart/signed",
210  "content": [{"id": 2,
211  "content-type": "text/plain",
212  "content": "This is a test signed message.\n"},
213  {"id": 3,
214  "content-type": "application/pgp-signature",
215  "content-length": "NONZERO"}]}]},
216  []]]]'
217 test_expect_equal_json \
218     "$output" \
219     "$expected"
220 mv "${GNUPGHOME}"{.bak,}
221
222 test_begin_subtest "emacs delivery of encrypted message with attachment"
223 # create a test encrypted message with attachment
224 cat <<EOF >TESTATTACHMENT
225 This is a test file.
226 EOF
227 test_expect_success \
228 'emacs_fcc_message \
229     "test encrypted message 001" \
230     "This is a test encrypted message.\n" \
231     "(mml-attach-file \"TESTATTACHMENT\") (mml-secure-message-encrypt)"'
232
233 test_begin_subtest "decryption, --format=text"
234 output=$(notmuch show --format=text --decrypt subject:"test encrypted message 001" \
235     | notmuch_show_sanitize_all \
236     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
237 expected='\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
238 \fheader{
239 Notmuch Test Suite <test_suite@notmuchmail.org> (2000-01-01) (encrypted inbox)
240 Subject: test encrypted message 001
241 From: Notmuch Test Suite <test_suite@notmuchmail.org>
242 To: test_suite@notmuchmail.org
243 Date: Sat, 01 Jan 2000 12:00:00 +0000
244 \fheader}
245 \fbody{
246 \fpart{ ID: 1, Content-type: multipart/encrypted
247 \fpart{ ID: 2, Content-type: application/pgp-encrypted
248 Non-text part: application/pgp-encrypted
249 \fpart}
250 \fpart{ ID: 3, Content-type: multipart/mixed
251 \fpart{ ID: 4, Content-type: text/plain
252 This is a test encrypted message.
253 \fpart}
254 \fattachment{ ID: 5, Filename: TESTATTACHMENT, Content-type: application/octet-stream
255 Non-text part: application/octet-stream
256 \fattachment}
257 \fpart}
258 \fpart}
259 \fbody}
260 \fmessage}'
261 test_expect_equal \
262     "$output" \
263     "$expected"
264
265 test_begin_subtest "decryption, --format=json"
266 output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
267     | notmuch_json_show_sanitize \
268     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
269 expected='[[[{"id": "XXXXX",
270  "match": true,
271  "excluded": false,
272  "filename": ["YYYYY"],
273  "timestamp": 946728000,
274  "date_relative": "2000-01-01",
275  "tags": ["encrypted","inbox"],
276  "headers": {"Subject": "test encrypted message 001",
277  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
278  "To": "test_suite@notmuchmail.org",
279  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
280  "body": [{"id": 1,
281  "encstatus": [{"status": "good"}],
282  "sigstatus": [],
283  "content-type": "multipart/encrypted",
284  "content": [{"id": 2,
285  "content-type": "application/pgp-encrypted",
286  "content-length": "NONZERO"},
287  {"id": 3,
288  "content-type": "multipart/mixed",
289  "content": [{"id": 4,
290  "content-type": "text/plain",
291  "content": "This is a test encrypted message.\n"},
292  {"id": 5,
293  "content-type": "application/octet-stream",
294  "content-disposition": "attachment",
295  "content-length": "NONZERO",
296  "content-transfer-encoding": "base64",
297  "filename": "TESTATTACHMENT"}]}]}]},
298  []]]]'
299 test_expect_equal_json \
300     "$output" \
301     "$expected"
302
303 test_begin_subtest "decryption, --format=json, --part=4"
304 output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted message 001" \
305     | notmuch_json_show_sanitize \
306     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
307 expected='{"id": 4,
308  "content-type": "text/plain",
309  "content": "This is a test encrypted message.\n"}'
310 test_expect_equal_json \
311     "$output" \
312     "$expected"
313
314 test_begin_subtest "decrypt attachment (--part=5 --format=raw)"
315 notmuch show \
316     --format=raw \
317     --part=5 \
318     --decrypt \
319     subject:"test encrypted message 001" >OUTPUT
320 test_expect_equal_file TESTATTACHMENT OUTPUT
321
322 test_begin_subtest "decryption failure with missing key"
323 mv "${GNUPGHOME}"{,.bak}
324 output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
325     | notmuch_json_show_sanitize \
326     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
327 expected='[[[{"id": "XXXXX",
328  "match": true,
329  "excluded": false,
330  "filename": ["YYYYY"],
331  "timestamp": 946728000,
332  "date_relative": "2000-01-01",
333  "tags": ["encrypted","inbox"],
334  "headers": {"Subject": "test encrypted message 001",
335  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
336  "To": "test_suite@notmuchmail.org",
337  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
338  "body": [{"id": 1,
339  "encstatus": [{"status": "bad"}],
340  "content-type": "multipart/encrypted",
341  "content": [{"id": 2,
342  "content-type": "application/pgp-encrypted",
343  "content-length": "NONZERO"},
344  {"id": 3,
345  "content-type": "application/octet-stream",
346  "content-length": "NONZERO"}]}]},
347  []]]]'
348 test_expect_equal_json \
349     "$output" \
350     "$expected"
351 mv "${GNUPGHOME}"{.bak,}
352
353 test_begin_subtest "emacs delivery of encrypted + signed message"
354 test_expect_success \
355 'emacs_fcc_message \
356     "test encrypted message 002" \
357     "This is another test encrypted message.\n" \
358     "(mml-secure-message-sign-encrypt)"'
359
360 test_begin_subtest "decryption + signature verification"
361 output=$(notmuch show --format=json --decrypt subject:"test encrypted message 002" \
362     | notmuch_json_show_sanitize \
363     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
364 expected='[[[{"id": "XXXXX",
365  "match": true,
366  "excluded": false,
367  "filename": ["YYYYY"],
368  "timestamp": 946728000,
369  "date_relative": "2000-01-01",
370  "tags": ["encrypted","inbox"],
371  "headers": {"Subject": "test encrypted message 002",
372  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
373  "To": "test_suite@notmuchmail.org",
374  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
375  "body": [{"id": 1,
376  "encstatus": [{"status": "good"}],
377  "sigstatus": [{"status": "good",
378  "fingerprint": "'$FINGERPRINT'",
379  "created": 946728000,
380  "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
381  "content-type": "multipart/encrypted",
382  "content": [{"id": 2,
383  "content-type": "application/pgp-encrypted",
384  "content-length": "NONZERO"},
385  {"id": 3,
386  "content-type": "text/plain",
387  "content": "This is another test encrypted message.\n"}]}]},
388  []]]]'
389 test_expect_equal_json \
390     "$output" \
391     "$expected"
392
393 test_begin_subtest "reply to encrypted message"
394 output=$(notmuch reply --decrypt subject:"test encrypted message 002" \
395     | grep -v -e '^In-Reply-To:' -e '^References:')
396 expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
397 Subject: Re: test encrypted message 002
398
399 On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
400 > This is another test encrypted message.'
401 test_expect_equal \
402     "$output" \
403     "$expected"
404
405 test_begin_subtest "Reply within emacs to an encrypted message"
406 test_emacs "(let ((message-hidden-headers '())
407       (notmuch-crypto-process-mime 't))
408   (notmuch-show \"subject:test.encrypted.message.002\")
409   (notmuch-show-reply)
410   (test-output))"
411 # the empty To: is probably a bug, but it's not to do with encryption
412 grep -v -e '^In-Reply-To:' -e '^References:' -e '^Fcc:' -e 'To:' < OUTPUT > OUTPUT.clean
413 cat <<EOF >EXPECTED
414 From: Notmuch Test Suite <test_suite@notmuchmail.org>
415 Subject: Re: test encrypted message 002
416 --text follows this line--
417 <#secure method=pgpmime mode=signencrypt>
418 Notmuch Test Suite <test_suite@notmuchmail.org> writes:
419
420 > This is another test encrypted message.
421 EOF
422 test_expect_equal_file EXPECTED OUTPUT.clean
423
424 test_begin_subtest "signature verification with revoked key"
425 # generate revocation certificate and load it to revoke key
426 echo "y
427 1
428 Notmuch Test Suite key revocation (automated) $(date '+%F_%T%z')
429
430 y
431
432 " \
433     | gpg --no-tty --quiet --command-fd 0 --armor --gen-revoke "0x${FINGERPRINT}!" 2>/dev/null \
434     | gpg --no-tty --quiet --import
435 output=$(notmuch show --format=json --verify subject:"test signed message 001" \
436     | notmuch_json_show_sanitize \
437     | sed -e 's|"created": [1234567890]*|"created": 946728000|')
438 expected='[[[{"id": "XXXXX",
439  "match": true,
440  "excluded": false,
441  "filename": ["YYYYY"],
442  "timestamp": 946728000,
443  "date_relative": "2000-01-01",
444  "tags": ["inbox","signed"],
445  "headers": {"Subject": "test signed message 001",
446  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
447  "To": "test_suite@notmuchmail.org",
448  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
449  "body": [{"id": 1,
450  "sigstatus": [{"status": "error",
451  "keyid": "6D92612D94E46381",
452  "errors": {"key-revoked": true}}],
453  "content-type": "multipart/signed",
454  "content": [{"id": 2,
455  "content-type": "text/plain",
456  "content": "This is a test signed message.\n"},
457  {"id": 3,
458  "content-type": "application/pgp-signature",
459  "content-length": "NONZERO"}]}]},
460  []]]]'
461 test_expect_equal_json \
462     "$output" \
463     "$expected"
464
465 test_done