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