2 gnuk_token.py - a library for Gnuk Token
4 Copyright (C) 2011, 2012, 2013, 2015
5 Free Software Initiative of Japan
6 Author: NIIBE Yutaka <gniibe@fsij.org>
8 This file is a part of Gnuk, a GnuPG USB Token implementation.
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.
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.
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/>.
27 from array import array
29 # USB class, subclass, protocol
32 CCID_PROTOCOL_0 = 0x00
35 HID_SUBCLASS_NO_BOOT = 0x00
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
41 def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
45 return pack('>BBBB', cls, ins, p1, p2)
47 return pack('>BBBBB', cls, ins, p1, p2, le)
50 return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
52 return pack('>BBBBB', cls, ins, p1, p2, data_len) \
53 + data + pack('>B', le)
55 # This class only supports Gnuk (for now)
56 class gnuk_token(object):
57 def __init__(self, device, configuration, interface):
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.
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)
72 self.__intf = interface.interfaceNumber
73 self.__alt = interface.alternateSetting
74 self.__conf = configuration
76 self.__hid_intf = None
77 for intf in configuration.interfaces:
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
87 self.__timeout = 10000
90 def get_string(self, num):
91 return self.__devhandle.getString(num, 512)
93 def increment_seq(self):
94 self.__seq = (self.__seq + 1) & 0xff
96 def reset_device(self):
98 self.__devhandle.reset()
102 def release_gnuk(self):
103 self.__devhandle.releaseInterface()
106 self.__devhandle.releaseInterface()
108 self.__devhandle.detachKernelDriver(self.__hid_intf)
109 self.__devhandle.setConfiguration(0)
113 mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
114 buffer = 8, value = 0, index = 0,
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]
120 def download(self, start, data, verbose=False):
122 addr_end = (start + len(data)) & 0xffffff00
123 i = int((addr - 0x20000000) / 0x100)
125 print("start %08x" % addr)
126 print("end %08x" % addr_end)
127 while addr < addr_end:
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)
136 residue = len(data) % 256
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)
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,
151 def icc_get_result(self):
152 usbmsg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
155 raise ValueError("icc_get_result")
156 msg = array('B', usbmsg)
158 data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
165 # XXX: check msg_type, data_len, slot, seq, error
166 return (status, chain, data)
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)
172 status, chain, data = self.icc_get_result()
173 # XXX: check chain, data
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)
180 status, chain, data = self.icc_get_result()
181 # XXX: check status, chain
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)
189 status, chain, data = self.icc_get_result()
190 # XXX: check chain, data
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)
197 return self.icc_get_result()
199 def icc_send_cmd(self, data):
200 status, chain, data_rcv = self.icc_send_data_block(data)
202 while status == 0x80:
203 status, chain, data_rcv = self.icc_get_result()
208 msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, b"")
209 self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
211 status, chain, data_rcv = self.icc_get_result()
219 raise ValueError("icc_send_cmd chain")
222 raise ValueError("icc_send_cmd")
224 def cmd_get_response(self, expected_len):
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]
231 if sw[0] == 0x90 and sw[1] == 0x00:
234 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
243 if not (sw[0] == 0x90 and sw[1] == 0x00):
244 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
253 raise ValueError("%02x%02x" % (sw[0], sw[1]))
254 return self.cmd_get_response(sw[1])
256 def cmd_write_binary(self, fileid, data, is_update):
263 while count*256 < data_len:
266 cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
269 cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
270 cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
272 if len(data[256*count:256*count+128]) < 128:
273 cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
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)
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]))
284 sw = self.icc_send_cmd(cmd_data1)
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]))
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)
299 self.cmd_get_response(sw[1])
301 elif sw[0] == 0x90 and sw[1] == 0x00:
304 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
311 if sw[0] == 0x90 and sw[1] == 0x00:
314 raise ValueError("%02x%02x" % (sw[0], sw[1]))
315 return self.cmd_get_response(sw[1])
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)
322 if not (sw[0] == 0x90 and sw[1] == 0x00):
323 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
331 if not (sw[0] == 0x90 and sw[1] == 0x00):
332 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
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)
346 if not (sw[0] == 0x90 and sw[1] == 0x00):
347 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
355 if not (sw[0] == 0x90 and sw[1] == 0x00):
356 raise ValueError("%02x%02x" % (sw[0], sw[1]))
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)
364 if sw[0] == 0x90 and sw[1] == 0x00:
367 raise ValueError("%02x%02x" % (sw[0], sw[1]))
368 return self.cmd_get_response(sw[1])
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)
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)
382 raise ValueError("%02x%02x" % (sw[0], sw[1]))
383 return self.cmd_get_response(sw[1])
385 def cmd_internal_authenticate(self, data):
386 cmd_data = iso7816_compose(0x88, 0, 0, data)
387 sw = self.icc_send_cmd(cmd_data)
390 if sw[0] == 0x90 and sw[1] == 0x00:
393 raise ValueError("%02x%02x" % (sw[0], sw[1]))
394 return self.cmd_get_response(sw[1])
396 def cmd_genkey(self, keyno):
403 cmd_data = iso7816_compose(0x47, 0x80, 0, data)
404 sw = self.icc_send_cmd(cmd_data)
407 if sw[0] == 0x90 and sw[1] == 0x00:
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])
414 def cmd_get_public_key(self, keyno):
421 cmd_data = iso7816_compose(0x47, 0x81, 0, data)
422 sw = self.icc_send_cmd(cmd_data)
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])
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]))
436 def cmd_put_data_key_import_remove(self, keyno):
438 keyspec = b"\xb6\x00" # SIG
440 keyspec = b"\xb8\x00" # DEC
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]))
448 def cmd_get_challenge(self):
449 cmd_data = iso7816_compose(0x84, 0x00, 0x00, '')
450 sw = self.icc_send_cmd(cmd_data)
454 raise ValueError("%02x%02x" % (sw[0], sw[1]))
455 return self.cmd_get_response(sw[1])
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)
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)
468 if not (sw[0] == 0x90 and sw[1] == 0x00):
469 raise ValueError("%02x%02x" % (sw[0], sw[1]))
472 class regnual(object):
473 def __init__(self, dev):
474 conf = dev.configurations[0]
475 intf_alt = conf.interfaces[0]
477 if intf.interfaceClass != 0xff:
478 raise ValueError("Wrong interface class")
479 self.__devhandle = dev.open()
480 self.__devhandle.claimInterface(intf)
483 mem = self.__devhandle.controlMsg(requestType = 0xc0, request = 0,
484 buffer = 8, value = 0, index = 0,
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]
490 def download(self, start, data, verbose=False):
492 addr_end = (start + len(data)) & 0xffffff00
493 i = int((addr - 0x08000000) / 0x100)
495 print("start %08x" % addr)
496 print("end %08x" % addr_end)
497 while addr < addr_end:
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,
507 r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
508 if (crc32code ^ r_value) != 0xffffffff:
510 self.__devhandle.controlMsg(requestType = 0x40, request = 3,
512 value = i, index = 0, timeout = 10000)
514 res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
515 buffer = 4, value = 0, index = 0,
517 r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
523 residue = len(data) % 256
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,
534 r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
535 if (crc32code ^ r_value) != 0xffffffff:
537 self.__devhandle.controlMsg(requestType = 0x40, request = 3,
539 value = i, index = 0, timeout = 10000)
541 res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
542 buffer = 4, value = 0, index = 0,
544 r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
549 self.__devhandle.controlMsg(requestType = 0x40, request = 4,
550 buffer = None, value = 0, index = 0,
553 res = self.__devhandle.controlMsg(requestType = 0xc0, request = 2,
554 buffer = 4, value = 0, index = 0,
556 r_value = ((res[3]*256 + res[2])*256 + res[1])*256 + res[0]
558 print("protection failure")
561 self.__devhandle.controlMsg(requestType = 0x40, request = 5,
562 buffer = None, value = 0, index = 0,
565 def reset_device(self):
567 self.__devhandle.reset()
571 def compare(data_original, data_in_device):
572 if data_original == data_in_device:
574 raise ValueError("verify failed")
577 busses = usb.busses()
579 devices = bus.devices
581 for config in dev.configurations:
582 for intf in config.interfaces:
584 if alt.interfaceClass == CCID_CLASS and \
585 alt.interfaceSubClass == CCID_SUBCLASS and \
586 alt.interfaceProtocol == CCID_PROTOCOL_0:
587 yield dev, config, alt
589 USB_VENDOR_FSIJ=0x234b
590 USB_PRODUCT_GNUK=0x0000
592 def gnuk_devices_by_vidpid():
593 busses = usb.busses()
595 devices = bus.devices
597 if dev.idVendor != USB_VENDOR_FSIJ:
599 if dev.idProduct != USB_PRODUCT_GNUK:
603 def get_gnuk_device():
605 for (dev, config, intf) in gnuk_devices():
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)
615 raise ValueError("No ICC present")
616 status = icc.icc_get_status()
618 pass # It's ON already
622 raise ValueError("Unknown ICC status", status)
625 SHA256_OID_PREFIX="3031300d060960864801650304020105000420"
628 return n & 0xffffffff
631 crc = binascii.crc32(bytestr)