c4ffac76c910ec97cf95db2ffc21296814ea29dc
[gnuk/gnuk.git] / tool / gnuk_token.py
1 """
2 gnuk_token.py - a library for Gnuk Token
3
4 Copyright (C) 2011, 2012, 2013, 2015
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 *
25 import binascii
26 import usb, time
27 from array import array
28
29 # USB class, subclass, protocol
30 CCID_CLASS = 0x0B
31 CCID_SUBCLASS = 0x00
32 CCID_PROTOCOL_0 = 0x00
33
34 HID_CLASS = 0x03
35 HID_SUBCLASS_NO_BOOT = 0x00
36 HID_PROTOCOL_0 = 0x00
37
38 def icc_compose(msg_type, data_len, slot, seq, param, data):
39     return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
40
41 def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
42     data_len = len(data)
43     if data_len == 0:
44         if not le:
45             return pack('>BBBB', cls, ins, p1, p2)
46         else:
47             return pack('>BBBBB', cls, ins, p1, p2, le)
48     else:
49         if not le:
50             return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
51         else:
52             return pack('>BBBBB', cls, ins, p1, p2, data_len) \
53                 + data + pack('>B', le)
54
55 # This class only supports Gnuk (for now) 
56 class gnuk_token(object):
57     def __init__(self, device, configuration, interface):
58         """
59         __init__(device, configuration, interface) -> None
60         Initialize the device.
61         device: usb.Device object.
62         configuration: configuration number.
63         interface: usb.Interface object representing the interface and altenate setting.
64         """
65         if interface.interfaceClass != CCID_CLASS:
66             raise ValueError("Wrong interface class")
67         if interface.interfaceSubClass != CCID_SUBCLASS:
68             raise ValueError("Wrong interface sub class")
69         self.__devhandle = device.open()
70         self.__devhandle.claimInterface(interface)
71
72         self.__intf = interface.interfaceNumber
73         self.__alt = interface.alternateSetting
74         self.__conf = configuration
75
76         self.__hid_intf = None
77         for intf in configuration.interfaces:
78             for alt in intf:
79                 if alt.interfaceClass == HID_CLASS and \
80                         alt.interfaceSubClass == HID_SUBCLASS_NO_BOOT and \
81                         alt.interfaceProtocol == HID_PROTOCOL_0:
82                     self.__hid_intf = alt.interfaceNumber
83
84         self.__bulkout = 1
85         self.__bulkin  = 0x81
86
87         self.__timeout = 10000
88         self.__seq = 0
89
90     def get_string(self, num):
91         return self.__devhandle.getString(num, 512)
92
93     def increment_seq(self):
94         self.__seq = (self.__seq + 1) & 0xff
95
96     def reset_device(self):
97         try:
98             self.__devhandle.reset()
99         except:
100             pass
101
102     def release_gnuk(self):
103         self.__devhandle.releaseInterface()
104
105     def stop_gnuk(self):
106         self.__devhandle.releaseInterface()
107         if self.__hid_intf:
108             self.__devhandle.detachKernelDriver(self.__hid_intf)
109         self.__devhandle.setConfiguration(0)
110         return
111
112     def mem_info(self):
113         mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
114                                           buffer = 8, value = 0, index = 0,
115                                           timeout = 10)
116         start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0]
117         end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4]
118         return (start, end)
119
120     def download(self, start, data, verbose=False):
121         addr = start
122         addr_end = (start + len(data)) & 0xffffff00
123         i = int((addr - 0x20000000) / 0x100)
124         j = 0
125         print("start %08x" % addr)
126         print("end   %08x" % addr_end)
127         while addr < addr_end:
128             if verbose:
129                 print("# %08x: %d : %d" % (addr, i, 256))
130             self.__devhandle.controlMsg(requestType = 0x40, request = 1,
131                                         buffer = data[j*256:j*256+256],
132                                         value = i, index = 0, timeout = 10)
133             i = i+1
134             j = j+1
135             addr = addr + 256
136         residue = len(data) % 256
137         if residue != 0:
138             if verbose:
139                 print("# %08x: %d : %d" % (addr, i, residue))
140             self.__devhandle.controlMsg(requestType = 0x40, request = 1,
141                                         buffer = data[j*256:],
142                                         value = i, index = 0, timeout = 10)
143
144     def execute(self, last_addr):
145         i = int((last_addr - 0x20000000) / 0x100)
146         o = (last_addr - 0x20000000) % 0x100
147         self.__devhandle.controlMsg(requestType = 0x40, request = 2,
148                                     buffer = None, value = i, index = o, 
149                                     timeout = 10)
150
151     def icc_get_result(self):
152         usbmsg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
153         if len(usbmsg) < 10:
154             print(usbmsg)
155             raise ValueError("icc_get_result")
156         msg = array('B', usbmsg)
157         msg_type = msg[0]
158         data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
159         slot = msg[5]
160         seq = msg[6]
161         status = msg[7]
162         error = msg[8]
163         chain = msg[9]
164         data = msg[10:]
165         # XXX: check msg_type, data_len, slot, seq, error
166         return (status, chain, data)
167
168     def icc_get_status(self):
169         msg = icc_compose(0x65, 0, 0, self.__seq, 0, b"")
170         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
171         self.increment_seq()
172         status, chain, data = self.icc_get_result()
173         # XXX: check chain, data
174         return status
175
176     def icc_power_on(self):
177         msg = icc_compose(0x62, 0, 0, self.__seq, 0, b"")
178         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
179         self.increment_seq()
180         status, chain, data = self.icc_get_result()
181         # XXX: check status, chain
182         self.atr = data
183         return self.atr
184
185     def icc_power_off(self):
186         msg = icc_compose(0x63, 0, 0, self.__seq, 0, b"")
187         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
188         self.increment_seq()
189         status, chain, data = self.icc_get_result()
190         # XXX: check chain, data
191         return status
192
193     def icc_send_data_block(self, data):
194         msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
195         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
196         self.increment_seq()
197         return self.icc_get_result()
198
199     def icc_send_cmd(self, data):
200         status, chain, data_rcv = self.icc_send_data_block(data)
201         if chain == 0:
202             while status == 0x80:
203                 status, chain, data_rcv = self.icc_get_result()
204             return data_rcv
205         elif chain == 1:
206             d = data_rcv
207             while True:
208                 msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, b"")
209                 self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
210                 self.increment_seq()
211                 status, chain, data_rcv = self.icc_get_result()
212                 # XXX: check status
213                 d += data_rcv
214                 if chain == 2:
215                     break
216                 elif chain == 3:
217                     continue
218                 else:
219                     raise ValueError("icc_send_cmd chain")
220             return d
221         else:
222             raise ValueError("icc_send_cmd")
223
224     def cmd_get_response(self, expected_len):
225         result = array('B')
226         while True:
227             cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + pack('>B', expected_len)
228             response = self.icc_send_cmd(cmd_data)
229             result += response[:-2]
230             sw = response[-2:]
231             if sw[0] == 0x90 and sw[1] == 0x00:
232                 return result
233             elif sw[0] != 0x61:
234                 raise ValueError("%02x%02x" % (sw[0], sw[1]))
235             else:
236                 expected_len = sw[1]
237
238     def cmd_verify(self, who, passwd):
239         cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd)
240         sw = self.icc_send_cmd(cmd_data)
241         if len(sw) != 2:
242             raise ValueError(sw)
243         if not (sw[0] == 0x90 and sw[1] == 0x00):
244             raise ValueError("%02x%02x" % (sw[0], sw[1]))
245         return True
246
247     def cmd_read_binary(self, fileid):
248         cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, b'')
249         sw = self.icc_send_cmd(cmd_data)
250         if len(sw) != 2:
251             raise ValueError(sw)
252         if sw[0] != 0x61:
253             raise ValueError("%02x%02x" % (sw[0], sw[1]))
254         return self.cmd_get_response(sw[1])
255
256     def cmd_write_binary(self, fileid, data, is_update):
257         count = 0
258         data_len = len(data)
259         if is_update:
260             ins = 0xd6
261         else:
262             ins = 0xd0
263         while count*256 < data_len:
264             if count == 0:
265                 if len(data) < 128:
266                     cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
267                     cmd_data1 = None
268                 else:
269                     cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
270                     cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
271             else:
272                 if len(data[256*count:256*count+128]) < 128:
273                     cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
274                     cmd_data1 = None
275                 else:
276                     cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
277                     cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
278             sw = self.icc_send_cmd(cmd_data0)
279             if len(sw) != 2:
280                 raise ValueError("cmd_write_binary 0")
281             if not (sw[0] == 0x90 and sw[1] == 0x00):
282                 raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
283             if cmd_data1:
284                 sw = self.icc_send_cmd(cmd_data1)
285                 if len(sw) != 2:
286                     raise ValueError("cmd_write_binary 1", sw)
287                 if not (sw[0] == 0x90 and sw[1] == 0x00):
288                     raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1]))
289             count += 1
290
291     def cmd_select_openpgp(self):
292         cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01")
293         r = self.icc_send_cmd(cmd_data)
294         if len(r) < 2:
295             raise ValueError(r)
296         sw = r[-2:]
297         r = r[0:-2]
298         if sw[0] == 0x61:
299             self.cmd_get_response(sw[1])
300             return True
301         elif sw[0] == 0x90 and sw[1] == 0x00:
302             return True
303         else:
304             raise ValueError("%02x%02x" % (sw[0], sw[1]))
305
306     def cmd_get_data(self, tagh, tagl):
307         cmd_data = iso7816_compose(0xca, tagh, tagl, b"")
308         sw = self.icc_send_cmd(cmd_data)
309         if len(sw) != 2:
310             raise ValueError(sw)
311         if sw[0] == 0x90 and sw[1] == 0x00:
312             return array('B')
313         elif sw[0] != 0x61:
314             raise ValueError("%02x%02x" % (sw[0], sw[1]))
315         return self.cmd_get_response(sw[1])
316
317     def cmd_change_reference_data(self, who, data):
318         cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
319         sw = self.icc_send_cmd(cmd_data)
320         if len(sw) != 2:
321             raise ValueError(sw)
322         if not (sw[0] == 0x90 and sw[1] == 0x00):
323             raise ValueError("%02x%02x" % (sw[0], sw[1]))
324         return True
325
326     def cmd_put_data(self, tagh, tagl, content):
327         cmd_data = iso7816_compose(0xda, tagh, tagl, content)
328         sw = self.icc_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         return True
334
335     def cmd_put_data_odd(self, tagh, tagl, content):
336         cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
337         cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
338         sw = self.icc_send_cmd(cmd_data0)
339         if len(sw) != 2:
340             raise ValueError(sw)
341         if not (sw[0] == 0x90 and sw[1] == 0x00):
342             raise ValueError("%02x%02x" % (sw[0], sw[1]))
343         sw = self.icc_send_cmd(cmd_data1)
344         if len(sw) != 2:
345             raise ValueError(sw)
346         if not (sw[0] == 0x90 and sw[1] == 0x00):
347             raise ValueError("%02x%02x" % (sw[0], sw[1]))
348         return True
349
350     def cmd_reset_retry_counter(self, how, who, data):
351         cmd_data = iso7816_compose(0x2c, how, who, data)
352         sw = self.icc_send_cmd(cmd_data)
353         if len(sw) != 2:
354             raise ValueError(sw)
355         if not (sw[0] == 0x90 and sw[1] == 0x00):
356             raise ValueError("%02x%02x" % (sw[0], sw[1]))
357         return True
358
359     def cmd_pso(self, p1, p2, data):
360         cmd_data = iso7816_compose(0x2a, p1, p2, data)
361         sw = self.icc_send_cmd(cmd_data)
362         if len(sw) != 2:
363             raise ValueError(sw)
364         if sw[0] == 0x90 and sw[1] == 0x00:
365             return array('B')
366         elif sw[0] != 0x61:
367             raise ValueError("%02x%02x" % (sw[0], sw[1]))
368         return self.cmd_get_response(sw[1])
369
370     def cmd_pso_longdata(self, p1, p2, data):
371         cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
372         cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
373         sw = self.icc_send_cmd(cmd_data0)
374         if len(sw) != 2:
375             raise ValueError(sw)
376         if not (sw[0] == 0x90 and sw[1] == 0x00):
377             raise ValueError("%02x%02x" % (sw[0], sw[1]))
378         sw = self.icc_send_cmd(cmd_data1)
379         if len(sw) != 2:
380             raise ValueError(sw)
381         elif sw[0] != 0x61:
382             raise ValueError("%02x%02x" % (sw[0], sw[1]))
383         return self.cmd_get_response(sw[1])
384
385     def cmd_internal_authenticate(self, data):
386         cmd_data = iso7816_compose(0x88, 0, 0, data)
387         sw = self.icc_send_cmd(cmd_data)
388         if len(sw) != 2:
389             raise ValueError(sw)
390         if sw[0] == 0x90 and sw[1] == 0x00:
391             return array('B')
392         elif sw[0] != 0x61:
393             raise ValueError("%02x%02x" % (sw[0], sw[1]))
394         return self.cmd_get_response(sw[1])
395
396     def cmd_genkey(self, keyno):
397         if keyno == 1:
398             data = b'\xb6\x00'
399         elif keyno == 2:
400             data = b'\xb8\x00'
401         else:
402             data = b'\xa4\x00'
403         cmd_data = iso7816_compose(0x47, 0x80, 0, data)
404         sw = self.icc_send_cmd(cmd_data)
405         if len(sw) != 2:
406             raise ValueError(sw)
407         if sw[0] == 0x90 and sw[1] == 0x00:
408             return array('B')
409         elif sw[0] != 0x61:
410             raise ValueError("%02x%02x" % (sw[0], sw[1]))
411         pk = self.cmd_get_response(sw[1])
412         return (pk[9:9+256], pk[9+256+2:9+256+2+3])
413
414     def cmd_get_public_key(self, keyno):
415         if keyno == 1:
416             data = b'\xb6\x00'
417         elif keyno == 2:
418             data = b'\xb8\x00'
419         else:
420             data = b'\xa4\x00'
421         cmd_data = iso7816_compose(0x47, 0x81, 0, data)
422         sw = self.icc_send_cmd(cmd_data)
423         if len(sw) != 2:
424             raise ValueError(sw)
425         elif sw[0] != 0x61:
426             raise ValueError("%02x%02x" % (sw[0], sw[1]))
427         pk = self.cmd_get_response(sw[1])
428         return (pk[9:9+256], pk[9+256+2:9+256+2+3])
429
430     def cmd_put_data_remove(self, tagh, tagl):
431         cmd_data = iso7816_compose(0xda, tagh, tagl, b"")
432         sw = self.icc_send_cmd(cmd_data)
433         if sw[0] != 0x90 and sw[1] != 0x00:
434             raise ValueError("%02x%02x" % (sw[0], sw[1]))
435
436     def cmd_put_data_key_import_remove(self, keyno):
437         if keyno == 1:
438             keyspec = b"\xb6\x00"      # SIG
439         elif keyno == 2:
440             keyspec = b"\xb8\x00"      # DEC
441         else:
442             keyspec = b"\xa4\x00"      # AUT
443         cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" +  keyspec)
444         sw = self.icc_send_cmd(cmd_data)
445         if sw[0] != 0x90 and sw[1] != 0x00:
446             raise ValueError("%02x%02x" % (sw[0], sw[1]))
447
448     def cmd_get_challenge(self):
449         cmd_data = iso7816_compose(0x84, 0x00, 0x00, '')
450         sw = self.icc_send_cmd(cmd_data)
451         if len(sw) != 2:
452             raise ValueError(sw)
453         if sw[0] != 0x61:
454             raise ValueError("%02x%02x" % (sw[0], sw[1]))
455         return self.cmd_get_response(sw[1])
456
457     def cmd_external_authenticate(self, keyno, signed):
458         cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10)
459         sw = self.icc_send_cmd(cmd_data)
460         if len(sw) != 2:
461             raise ValueError(sw)
462         if not (sw[0] == 0x90 and sw[1] == 0x00):
463             raise ValueError("%02x%02x" % (sw[0], sw[1]))
464         cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:])
465         sw = self.icc_send_cmd(cmd_data)
466         if len(sw) != 2:
467             raise ValueError(sw)
468         if not (sw[0] == 0x90 and sw[1] == 0x00):
469             raise ValueError("%02x%02x" % (sw[0], sw[1]))
470
471
472 class regnual(object):
473     def __init__(self, dev):
474         conf = dev.configurations[0]
475         intf_alt = conf.interfaces[0]
476         intf = intf_alt[0]
477         if intf.interfaceClass != 0xff:
478             raise ValueError("Wrong interface class")
479         self.__devhandle = dev.open()
480         self.__devhandle.claimInterface(intf)
481
482     def mem_info(self):
483         mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
484                                           buffer = 8, value = 0, index = 0,
485                                           timeout = 10000)
486         start = ((mem[3]*256 + mem[2])*256 + mem[1])*256 + mem[0]
487         end = ((mem[7]*256 + mem[6])*256 + mem[5])*256 + mem[4]
488         return (start, end)
489
490     def download(self, start, data, verbose=False):
491         addr = start
492         addr_end = (start + len(data)) & 0xffffff00
493         i = int((addr - 0x08000000) / 0x100)
494         j = 0
495         print("start %08x" % addr)
496         print("end   %08x" % addr_end)
497         while addr < addr_end:
498             if verbose:
499                 print("# %08x: %d: %d : %d" % (addr, i, j, 256))
500             self.__devhandle.controlMsg(requestType = 0x40, request = 1,
501                                         buffer = data[j*256:j*256+256],
502                                         value = 0, index = 0, timeout = 10000)
503             crc32code = crc32(data[j*256:j*256+256])
504             res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
505                                               buffer = 4, value = 0, index = 0,
506                                               timeout = 10000)
507             r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
508             if (crc32code ^ r_value) != 0xffffffff:
509                 print("failure")
510             self.__devhandle.controlMsg(requestType = 0x40, request = 3,
511                                         buffer = None,
512                                         value = i, index = 0, timeout = 10000)
513             time.sleep(0.010)
514             res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
515                                               buffer = 4, value = 0, index = 0,
516                                               timeout = 10000)
517             r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
518             if r_value == 0:
519                 print("failure")
520             i = i+1
521             j = j+1
522             addr = addr + 256
523         residue = len(data) % 256
524         if residue != 0:
525             if verbose:
526                 print("# %08x: %d : %d" % (addr, i, residue))
527             self.__devhandle.controlMsg(requestType = 0x40, request = 1,
528                                         buffer = data[j*256:],
529                                         value = 0, index = 0, timeout = 10000)
530             crc32code = crc32(data[j*256:].ljust(256,b'\xff'))
531             res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
532                                               buffer = 4, value = 0, index = 0,
533                                               timeout = 10000)
534             r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
535             if (crc32code ^ r_value) != 0xffffffff:
536                 print("failure")
537             self.__devhandle.controlMsg(requestType = 0x40, request = 3,
538                                         buffer = None,
539                                         value = i, index = 0, timeout = 10000)
540             time.sleep(0.010)
541             res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
542                                               buffer = 4, value = 0, index = 0, 
543                                               timeout = 10000)
544             r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
545             if r_value == 0:
546                 print("failure")
547
548     def protect(self):
549         self.__devhandle.controlMsg(requestType = 0x40, request = 4,
550                                     buffer = None, value = 0, index = 0, 
551                                     timeout = 10000)
552         time.sleep(0.100)
553         res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
554                                           buffer = 4, value = 0, index = 0, 
555                                           timeout = 10000)
556         r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
557         if r_value == 0:
558             print("protection failure")
559
560     def finish(self):
561         self.__devhandle.controlMsg(requestType = 0x40, request = 5,
562                                     buffer = None, value = 0, index = 0, 
563                                     timeout = 10000)
564
565     def reset_device(self):
566         try:
567             self.__devhandle.reset()
568         except:
569             pass
570
571 def compare(data_original, data_in_device):
572     if data_original == data_in_device:
573         return True
574     raise ValueError("verify failed")
575
576 def gnuk_devices():
577     busses = usb.busses()
578     for bus in busses:
579         devices = bus.devices
580         for dev in devices:
581             for config in dev.configurations:
582                 for intf in config.interfaces:
583                     for alt in intf:
584                         if alt.interfaceClass == CCID_CLASS and \
585                                 alt.interfaceSubClass == CCID_SUBCLASS and \
586                                 alt.interfaceProtocol == CCID_PROTOCOL_0:
587                             yield dev, config, alt
588
589 USB_VENDOR_FSIJ=0x234b
590 USB_PRODUCT_GNUK=0x0000
591
592 def gnuk_devices_by_vidpid():
593     busses = usb.busses()
594     for bus in busses:
595         devices = bus.devices
596         for dev in devices:
597             if dev.idVendor != USB_VENDOR_FSIJ:
598                 continue
599             if dev.idProduct != USB_PRODUCT_GNUK:
600                 continue
601             yield dev
602
603 def get_gnuk_device():
604     icc = None
605     for (dev, config, intf) in gnuk_devices():
606         try:
607             icc = gnuk_token(dev, config, intf)
608             print("Device: %s" % dev.filename)
609             print("Configuration: %d" % config.value)
610             print("Interface: %d" % intf.interfaceNumber)
611             break
612         except:
613             pass
614     if not icc:
615         raise ValueError("No ICC present")
616     status = icc.icc_get_status()
617     if status == 0:
618         pass                    # It's ON already
619     elif status == 1:
620         icc.icc_power_on()
621     else:
622         raise ValueError("Unknown ICC status", status)
623     return icc
624
625 SHA256_OID_PREFIX="3031300d060960864801650304020105000420"
626
627 def UNSIGNED(n):
628     return n & 0xffffffff
629
630 def crc32(bytestr):
631     crc = binascii.crc32(bytestr)
632     return UNSIGNED(crc)