Fix factory-reset.
[gnuk/gnuk.git] / tests / openpgp_card.py
1 """
2 openpgp_card.py - a library for OpenPGP card
3
4 Copyright (C) 2011, 2012, 2013, 2015, 2016
5               Free Software Initiative of Japan
6 Author: NIIBE Yutaka <gniibe@fsij.org>
7
8 This file is a part of Gnuk, a GnuPG USB Token implementation.
9
10 Gnuk is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 Gnuk is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18 License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 """
23
24 from struct import pack
25
26 def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
27     data_len = len(data)
28     if data_len == 0:
29         if not le:
30             return pack('>BBBB', cls, ins, p1, p2)
31         else:
32             return pack('>BBBBB', cls, ins, p1, p2, le)
33     else:
34         if not le:
35             if data_len <= 255:
36                 return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
37             else:
38                 return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \
39                     + data
40         else:
41             if data_len <= 255 and le < 256:
42                 return pack('>BBBBB', cls, ins, p1, p2, data_len) \
43                     + data + pack('>B', le)
44             else:
45                 return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \
46                     + data + pack('>H', le)
47
48 class OpenPGP_Card(object):
49     def __init__(self, reader):
50         """
51         __init__(reader) -> None
52         Initialize a OpenPGP card with a CardReader.
53         reader: CardReader object.
54         """
55
56         self.__reader = reader
57
58     def cmd_get_response(self, expected_len):
59         result = b""
60         while True:
61             cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + pack('>B', expected_len)
62             response = self.__reader.send_cmd(cmd_data)
63             result += response[:-2]
64             sw = response[-2:]
65             if sw[0] == 0x90 and sw[1] == 0x00:
66                 return result
67             elif sw[0] != 0x61:
68                 raise ValueError("%02x%02x" % (sw[0], sw[1]))
69             else:
70                 expected_len = sw[1]
71
72     def cmd_verify(self, who, passwd):
73         cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd)
74         sw = self.__reader.send_cmd(cmd_data)
75         if len(sw) != 2:
76             raise ValueError(sw)
77         if not (sw[0] == 0x90 and sw[1] == 0x00):
78             raise ValueError("%02x%02x" % (sw[0], sw[1]))
79         return True
80
81     def cmd_read_binary(self, fileid):
82         cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, b'')
83         sw = self.__reader.send_cmd(cmd_data)
84         if len(sw) != 2:
85             raise ValueError(sw)
86         if sw[0] != 0x61:
87             raise ValueError("%02x%02x" % (sw[0], sw[1]))
88         return self.cmd_get_response(sw[1])
89
90     def cmd_write_binary(self, fileid, data, is_update):
91         count = 0
92         data_len = len(data)
93         if is_update:
94             ins = 0xd6
95         else:
96             ins = 0xd0
97         while count*256 < data_len:
98             if count == 0:
99                 if len(data) < 128:
100                     cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
101                     cmd_data1 = None
102                 else:
103                     cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
104                     cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
105             else:
106                 if len(data[256*count:256*count+128]) < 128:
107                     cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
108                     cmd_data1 = None
109                 else:
110                     cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
111                     cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
112             sw = self.__reader.send_cmd(cmd_data0)
113             if len(sw) != 2:
114                 raise ValueError("cmd_write_binary 0")
115             if not (sw[0] == 0x90 and sw[1] == 0x00):
116                 raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
117             if cmd_data1:
118                 sw = self.__reader.send_cmd(cmd_data1)
119                 if len(sw) != 2:
120                     raise ValueError("cmd_write_binary 1", sw)
121                 if not (sw[0] == 0x90 and sw[1] == 0x00):
122                     raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1]))
123             count += 1
124
125     def cmd_select_openpgp(self):
126         cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01")
127         r = self.__reader.send_cmd(cmd_data)
128         if len(r) < 2:
129             raise ValueError(r)
130         sw = r[-2:]
131         r = r[0:-2]
132         if sw[0] == 0x61:
133             self.cmd_get_response(sw[1])
134             return True
135         if not (sw[0] == 0x90 and sw[1] == 0x00):
136             raise ValueError("%02x%02x" % (sw[0], sw[1]))
137         return True
138
139     def cmd_get_data(self, tagh, tagl):
140         cmd_data = iso7816_compose(0xca, tagh, tagl, b"", le=254)
141         sw = self.__reader.send_cmd(cmd_data)
142         if len(sw) < 2:
143             raise ValueError(sw)
144         if sw[0] == 0x61:
145             return self.cmd_get_response(sw[1])
146         elif sw[-2] == 0x90 and sw[-1] == 0x00:
147             return sw[0:-2]
148         if sw[0] == 0x6a and sw[1] == 0x88:
149             return None
150         else:
151             raise ValueError("%02x%02x" % (sw[0], sw[1]))
152
153     def cmd_change_reference_data(self, who, data):
154         cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
155         sw = self.__reader.send_cmd(cmd_data)
156         if len(sw) != 2:
157             raise ValueError(sw)
158         if not (sw[0] == 0x90 and sw[1] == 0x00):
159             raise ValueError("%02x%02x" % (sw[0], sw[1]))
160         return True
161
162     def cmd_put_data(self, tagh, tagl, content):
163         cmd_data = iso7816_compose(0xda, tagh, tagl, content)
164         sw = self.__reader.send_cmd(cmd_data)
165         if len(sw) != 2:
166             raise ValueError(sw)
167         if not (sw[0] == 0x90 and sw[1] == 0x00):
168             raise ValueError("%02x%02x" % (sw[0], sw[1]))
169         return True
170
171     def cmd_put_data_odd(self, tagh, tagl, content):
172         if self.__reader.is_tpdu_reader():
173             cmd_data = iso7816_compose(0xdb, tagh, tagl, content)
174             sw = self.__reader.send_cmd(cmd_data)
175         else:
176             cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
177             cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
178             sw = self.__reader.send_cmd(cmd_data0)
179             if len(sw) != 2:
180                 raise ValueError(sw)
181             if not (sw[0] == 0x90 and sw[1] == 0x00):
182                 raise ValueError("%02x%02x" % (sw[0], sw[1]))
183             sw = self.__reader.send_cmd(cmd_data1)
184         if len(sw) != 2:
185             raise ValueError(sw)
186         if not (sw[0] == 0x90 and sw[1] == 0x00):
187             raise ValueError("%02x%02x" % (sw[0], sw[1]))
188         return True
189
190     def cmd_reset_retry_counter(self, how, who, data):
191         cmd_data = iso7816_compose(0x2c, how, who, data)
192         sw = self.__reader.send_cmd(cmd_data)
193         if len(sw) != 2:
194             raise ValueError(sw)
195         if not (sw[0] == 0x90 and sw[1] == 0x00):
196             raise ValueError("%02x%02x" % (sw[0], sw[1]))
197         return True
198
199     def cmd_pso(self, p1, p2, data):
200         if self.__reader.is_tpdu_reader():
201             cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256)
202             r = self.__reader.send_cmd(cmd_data)
203             if len(r) < 2:
204                 raise ValueError(r)
205             sw = r[-2:]
206             r = r[0:-2]
207             if sw[0] == 0x61:
208                 return self.cmd_get_response(sw[1])
209             elif sw[0] == 0x90 and sw[1] == 0x00:
210                 return r
211             else:
212                 raise ValueError("%02x%02x" % (sw[0], sw[1]))
213         else:
214             if len(data) > 128:
215                 cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
216                 cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
217                 sw = self.__reader.send_cmd(cmd_data0)
218                 if len(sw) != 2:
219                     raise ValueError(sw)
220                 if not (sw[0] == 0x90 and sw[1] == 0x00):
221                     raise ValueError("%02x%02x" % (sw[0], sw[1]))
222                 sw = self.__reader.send_cmd(cmd_data1)
223                 if len(sw) != 2:
224                     raise ValueError(sw)
225                 elif sw[0] != 0x61:
226                     raise ValueError("%02x%02x" % (sw[0], sw[1]))
227                 return self.cmd_get_response(sw[1])
228             else:
229                 cmd_data = iso7816_compose(0x2a, p1, p2, data)
230                 sw = self.__reader.send_cmd(cmd_data)
231                 if len(sw) != 2:
232                     raise ValueError(sw)
233                 if sw[0] == 0x90 and sw[1] == 0x00:
234                     return b""
235                 elif sw[0] != 0x61:
236                     raise ValueError("%02x%02x" % (sw[0], sw[1]))
237                 return self.cmd_get_response(sw[1])
238
239     def cmd_internal_authenticate(self, data):
240         if self.__reader.is_tpdu_reader():
241             cmd_data = iso7816_compose(0x88, 0, 0, data, le=256)
242         else:
243             cmd_data = iso7816_compose(0x88, 0, 0, data)
244         r = self.__reader.send_cmd(cmd_data)
245         if len(r) < 2:
246             raise ValueError(r)
247         sw = r[-2:]
248         r = r[0:-2]
249         if sw[0] == 0x61:
250             return self.cmd_get_response(sw[1])
251         elif sw[0] == 0x90 and sw[1] == 0x00:
252             return r
253         else:
254             raise ValueError("%02x%02x" % (sw[0], sw[1]))
255
256     def cmd_genkey(self, keyno):
257         if keyno == 1:
258             data = b'\xb6\x00'
259         elif keyno == 2:
260             data = b'\xb8\x00'
261         else:
262             data = b'\xa4\x00'
263         cmd_data = iso7816_compose(0x47, 0x80, 0, data)
264         sw = self.__reader.send_cmd(cmd_data)
265         if len(sw) != 2:
266             raise ValueError(sw)
267         if sw[0] == 0x90 and sw[1] == 0x00:
268             return b""
269         elif sw[0] != 0x61:
270             raise ValueError("%02x%02x" % (sw[0], sw[1]))
271         pk = self.cmd_get_response(sw[1])
272         return (pk[9:9+256], pk[9+256+2:9+256+2+3])
273
274     def cmd_get_public_key(self, keyno):
275         if keyno == 1:
276             data = b'\xb6\x00'
277         elif keyno == 2:
278             data = b'\xb8\x00'
279         else:
280             data = b'\xa4\x00'
281         if self.__reader.is_tpdu_reader():
282             cmd_data = iso7816_compose(0x47, 0x81, 0, data, le=512)
283             r = self.__reader.send_cmd(cmd_data)
284         else:
285             cmd_data = iso7816_compose(0x47, 0x81, 0, data)
286             r = self.__reader.send_cmd(cmd_data)
287         if len(r) < 2:
288             raise ValueError(r)
289         sw = r[-2:]
290         r = r[0:-2]
291         if sw[0] == 0x61:
292             pk = self.cmd_get_response(sw[1])
293         elif sw[0] == 0x90 and sw[1] == 0x00:
294             pk = r
295         else:
296             raise ValueError("%02x%02x" % (sw[0], sw[1]))
297         return pk
298
299     def cmd_put_data_remove(self, tagh, tagl):
300         cmd_data = iso7816_compose(0xda, tagh, tagl, b"")
301         sw = self.__reader.send_cmd(cmd_data)
302         if sw[0] != 0x90 and sw[1] != 0x00:
303             raise ValueError("%02x%02x" % (sw[0], sw[1]))
304
305     def cmd_put_data_key_import_remove(self, keyno):
306         if keyno == 1:
307             keyspec = b"\xb6\x00"      # SIG
308         elif keyno == 2:
309             keyspec = b"\xb8\x00"      # DEC
310         else:
311             keyspec = b"\xa4\x00"      # AUT
312         cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" +  keyspec)
313         sw = self.__reader.send_cmd(cmd_data)
314         if sw[0] != 0x90 and sw[1] != 0x00:
315             raise ValueError("%02x%02x" % (sw[0], sw[1]))
316
317     def cmd_get_challenge(self):
318         cmd_data = iso7816_compose(0x84, 0x00, 0x00, '')
319         sw = self.__reader.send_cmd(cmd_data)
320         if len(sw) != 2:
321             raise ValueError(sw)
322         if sw[0] != 0x61:
323             raise ValueError("%02x%02x" % (sw[0], sw[1]))
324         return self.cmd_get_response(sw[1])
325
326     def cmd_external_authenticate(self, keyno, signed):
327         cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10)
328         sw = self.__reader.send_cmd(cmd_data)
329         if len(sw) != 2:
330             raise ValueError(sw)
331         if not (sw[0] == 0x90 and sw[1] == 0x00):
332             raise ValueError("%02x%02x" % (sw[0], sw[1]))
333         cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:])
334         sw = self.__reader.send_cmd(cmd_data)
335         if len(sw) != 2:
336             raise ValueError(sw)
337         if not (sw[0] == 0x90 and sw[1] == 0x00):
338             raise ValueError("%02x%02x" % (sw[0], sw[1]))