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