python: Add an idiomatic interface.
[gpgme.git] / lang / python / tests / t-verify.py
1 #!/usr/bin/env python3
2
3 # Copyright (C) 2016 g10 Code GmbH
4 #
5 # This file is part of GPGME.
6 #
7 # GPGME is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # GPGME is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
15 # Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this program; if not, see <http://www.gnu.org/licenses/>.
19
20 import os
21 import pyme
22 from pyme import core, constants, errors
23 import support
24
25 test_text1 = b"Just GNU it!\n"
26 test_text1f= b"Just GNU it?\n"
27 test_sig1 = b"""-----BEGIN PGP SIGNATURE-----
28
29 iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt
30 bGF1dGUgdW5kIGpldHp0IGVpbiBwcm96ZW50JS1aZWljaGVuNRSAAAAAAAgAJGZv
31 b2Jhci4xdGhpcyBpcyBhIG5vdGF0aW9uIGRhdGEgd2l0aCAyIGxpbmVzGhpodHRw
32 Oi8vd3d3Lmd1Lm9yZy9wb2xpY3kvAAoJEC1yfMdoaXc0JBIAoIiLlUsvpMDOyGEc
33 dADGKXF/Hcb+AKCJWPphZCphduxSvrzH0hgzHdeQaA==
34 =nts1
35 -----END PGP SIGNATURE-----
36 """
37
38 test_sig2 = b"""-----BEGIN PGP MESSAGE-----
39
40 owGbwMvMwCSoW1RzPCOz3IRxjXQSR0lqcYleSUWJTZOvjVdpcYmCu1+oQmaJIleH
41 GwuDIBMDGysTSIqBi1MApi+nlGGuwDeHao53HBr+FoVGP3xX+kvuu9fCMJvl6IOf
42 y1kvP4y+8D5a11ang0udywsA
43 =Crq6
44 -----END PGP MESSAGE-----
45 """
46
47 # A message with a prepended but unsigned plaintext packet.
48 double_plaintext_sig = b"""-----BEGIN PGP MESSAGE-----
49
50 rDRiCmZvb2Jhci50eHRF4pxNVGhpcyBpcyBteSBzbmVha3kgcGxhaW50ZXh0IG1l
51 c3NhZ2UKowGbwMvMwCSoW1RzPCOz3IRxTWISa6JebnG666MFD1wzSzJSixQ81XMV
52 UlITUxTyixRyKxXKE0uSMxQyEosVikvyCwpSU/S4FNCArq6Ce1F+aXJGvoJvYlGF
53 erFCTmJxiUJ5flFKMVeHGwuDIBMDGysTyA4GLk4BmO036xgWzMgzt9V85jCtfDFn
54 UqVooWlGXHwNw/xg/fVzt9VNbtjtJ/fhUqYo0/LyCGEA
55 =6+AK
56 -----END PGP MESSAGE-----
57 """
58
59 def check_result(result, summary, validity, fpr, status, notation):
60     assert len(result.signatures) == 1, "Unexpected number of signatures"
61     sig = result.signatures[0]
62     assert sig.summary == summary, \
63         "Unexpected signature summary: {}, want: {}".format(sig.summary,
64                                                             summary)
65     assert sig.fpr == fpr
66     assert errors.GPGMEError(sig.status).getcode() == status
67
68     if notation:
69         expected_notations = {
70             "bar": b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() +
71             " das waren Umlaute und jetzt ein prozent%-Zeichen",
72             "foobar.1":  "this is a notation data with 2 lines",
73             None: "http://www.gu.org/policy/",
74         }
75         assert len(sig.notations) == len(expected_notations)
76
77         for r in sig.notations:
78             assert not 'name_len' in dir(r)
79             assert not 'value_len' in dir(r)
80             assert r.name in expected_notations
81             assert r.value == expected_notations[r.name], \
82                 "Expected {!r}, got {!r}".format(expected_notations[r.name],
83                                                  r.value)
84             expected_notations.pop(r.name)
85
86         assert len(expected_notations) == 0
87
88     assert not sig.wrong_key_usage
89     assert sig.validity == validity, \
90         "Unexpected signature validity: {}, want: {}".format(
91             sig.validity, validity)
92     assert errors.GPGMEError(sig.validity_reason).getcode() == errors.NO_ERROR
93
94
95 support.init_gpgme(constants.PROTOCOL_OpenPGP)
96 c = core.Context()
97 c.set_armor(True)
98
99 # Checking a valid message.
100 text = core.Data(test_text1)
101 sig = core.Data(test_sig1)
102 c.op_verify(sig, text, None)
103 result = c.op_verify_result()
104 check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
105              constants.VALIDITY_FULL,
106              "A0FF4590BB6122EDEF6E3C542D727CC768697734",
107              errors.NO_ERROR, True)
108
109
110 # Checking a manipulated message.
111 text = core.Data(test_text1f)
112 sig.seek(0, os.SEEK_SET)
113 c.op_verify(sig, text, None)
114 result = c.op_verify_result()
115 check_result(result, constants.SIGSUM_RED, constants.VALIDITY_UNKNOWN,
116              "2D727CC768697734", errors.BAD_SIGNATURE, False)
117
118 # Checking a normal signature.
119 text = core.Data()
120 sig = core.Data(test_sig2)
121 c.op_verify(sig, None, text)
122 result = c.op_verify_result()
123 check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
124              constants.VALIDITY_FULL,
125              "A0FF4590BB6122EDEF6E3C542D727CC768697734",
126              errors.NO_ERROR, False)
127
128 # Checking an invalid message.
129 text = core.Data()
130 sig = core.Data(double_plaintext_sig)
131 try:
132     c.op_verify(sig, None, text)
133 except Exception as e:
134     assert type(e) == errors.GPGMEError
135     assert e.getcode() == errors.BAD_DATA
136 else:
137     assert False, "Expected an error but got none."
138
139
140 # Idiomatic interface.
141 with pyme.Context(armor=True) as c:
142     # Checking a valid message.
143     _, result = c.verify(test_text1, test_sig1)
144     check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
145                  constants.VALIDITY_FULL,
146                  "A0FF4590BB6122EDEF6E3C542D727CC768697734",
147                  errors.NO_ERROR, True)
148
149     # Checking a manipulated message.
150     try:
151         c.verify(test_text1f, test_sig1)
152     except errors.BadSignatures as e:
153         check_result(e.result, constants.SIGSUM_RED,
154                      constants.VALIDITY_UNKNOWN,
155                      "2D727CC768697734", errors.BAD_SIGNATURE, False)
156     else:
157         assert False, "Expected an error but got none."
158
159     # Checking a normal signature.
160     sig = core.Data(test_sig2)
161     data, result = c.verify(test_sig2)
162     check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
163                  constants.VALIDITY_FULL,
164                  "A0FF4590BB6122EDEF6E3C542D727CC768697734",
165                  errors.NO_ERROR, False)
166     assert data == test_text1
167
168     # Checking an invalid message.
169     try:
170         c.verify(double_plaintext_sig)
171     except errors.GPGMEError as e:
172         assert e.getcode() == errors.BAD_DATA
173     else:
174         assert False, "Expected an error but got none."
175
176     alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
177     bob = c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False)
178
179     # Checking a valid message.
180     c.verify(test_text1, test_sig1, verify=[alpha])
181
182     try:
183         c.verify(test_text1, test_sig1, verify=[alpha, bob])
184     except errors.MissingSignatures as e:
185         assert len(e.missing) == 1
186         assert e.missing[0] == bob
187     else:
188         assert False, "Expected an error, got none"