diff --git a/ChangeLog b/ChangeLog index 470b85b..f399954 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2012-06-01 Niibe Yutaka + * tool/gnuk_put_binary_libusb.py (main): Support firmware update + key registration. + Update of reGNUal. * regnual/regnual.c (main): Follow the change of usb_lld_init. (regnual_config_desc): Include interface descriptor. diff --git a/tool/gnuk_put_binary_libusb.py b/tool/gnuk_put_binary_libusb.py index a6bb0e3..86de1e4 100755 --- a/tool/gnuk_put_binary_libusb.py +++ b/tool/gnuk_put_binary_libusb.py @@ -4,7 +4,7 @@ gnuk_put_binary.py - a tool to put binary to Gnuk Token This tool is for importing certificate, updating random number, etc. -Copyright (C) 2011 Free Software Initiative of Japan +Copyright (C) 2011, 2012 Free Software Initiative of Japan Author: NIIBE Yutaka This file is a part of Gnuk, a GnuPG USB Token implementation. @@ -41,13 +41,12 @@ CCID_PROTOCOL_0 = 0x00 def icc_compose(msg_type, data_len, slot, seq, param, data): return pack('BBBB', cls, ins, p1, p2) else: - return pack('>BBBBBh', cls, ins, p1, p2, 0, data_len) + data + return pack('>BBBBB', cls, ins, p1, p2, data_len) + data # This class only supports Gnuk (for now) class gnuk_token: @@ -150,27 +149,57 @@ class gnuk_token: else: raise ValueError, "icc_send_cmd" + def cmd_get_response(self, expected_len): + cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len) + response = self.icc_send_cmd(cmd_data) + return response[:-2] + def cmd_verify(self, who, passwd): cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd) sw = self.icc_send_cmd(cmd_data) if len(sw) != 2: - raise ValueError, "cmd_verify" + raise ValueError, sw if not (sw[0] == 0x90 and sw[1] == 0x00): - raise ValueError, "cmd_verify" + raise ValueError, sw + + def cmd_read_binary(self, fileid): + cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, '') + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError, sw + if sw[0] != 0x61: + raise ValueError, ("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) def cmd_write_binary(self, fileid, data): count = 0 data_len = len(data) while count*256 < data_len: if count == 0: - cmd_data = iso7816_compose(0xd0, 0x80+fileid, 0x00, data[:256]) + if len(data) < 128: + cmd_data0 = iso7816_compose(0xd0, 0x80+fileid, 0x00, data[:128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(0xd0, 0x80+fileid, 0x00, data[:128], 0x10) + cmd_data1 = iso7816_compose(0xd0, 0x80+fileid, 0x00, data[128:256]) else: - cmd_data = iso7816_compose(0xd0, count, 0x00, data[256*count:256*(count+1)]) - sw = self.icc_send_cmd(cmd_data) + if len(data[256*count:256*count+128]) < 128: + cmd_data0 = iso7816_compose(0xd0, count, 0x00, data[256*count:256*count+128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(0xd0, count, 0x00, data[256*count:256*count+128], 0x10) + cmd_data1 = iso7816_compose(0xd0, count, 0x00, data[256*count:256*(count+1)]) + sw = self.icc_send_cmd(cmd_data0) if len(sw) != 2: - raise ValueError, "cmd_write_binary" + raise ValueError, "cmd_write_binary 0" if not (sw[0] == 0x90 and sw[1] == 0x00): - raise ValueError, "cmd_write_binary" + raise ValueError, "cmd_write_binary 0" + if cmd_data1: + sw = self.icc_send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError, "cmd_write_binary 1" + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError, "cmd_write_binary 1" count += 1 def cmd_update_binary(self, fileid, data): @@ -178,31 +207,48 @@ class gnuk_token: data_len = len(data) while count*256 < data_len: if count == 0: - cmd_data = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[:256]) + if len(data) < 128: + cmd_data0 = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[:128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[:128], 0x10) + cmd_data1 = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[128:256]) else: - cmd_data = iso7816_compose(0xd6, count, 0x00, data[256*count:256*(count+1)]) - sw = self.icc_send_cmd(cmd_data) + if len(data[256*count:256*count+128]) < 128: + cmd_data0 = iso7816_compose(0xd6, count, 0x00, data[256*count:256*count+128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(0xd6, count, 0x00, data[256*count:256*count+128], 0x10) + cmd_data1 = iso7816_compose(0xd6, count, 0x00, data[256*count:256*(count+1)]) + sw = self.icc_send_cmd(cmd_data0) if len(sw) != 2: - raise ValueError, "cmd_update_binary" + raise ValueError, "cmd_write_binary 0" if not (sw[0] == 0x90 and sw[1] == 0x00): - raise ValueError, "cmd_update_binary" + raise ValueError, "cmd_write_binary 0" + if cmd_data1: + sw = self.icc_send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError, "cmd_write_binary 1" + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError, "cmd_write_binary 1" count += 1 def cmd_select_openpgp(self): - cmd_data = iso7816_compose(0xa4, 0x04, 0x00, "\xD2\x76\x00\x01\x24\x01") + cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\xD2\x76\x00\x01\x24\x01") sw = self.icc_send_cmd(cmd_data) if len(sw) != 2: - raise ValueError, "cmd_select_openpgp" + raise ValueError, sw if not (sw[0] == 0x90 and sw[1] == 0x00): - raise ValueError, "cmd_select_openpgp" + raise ValueError, ("%02x%02x" % (sw[0], sw[1])) def cmd_get_data(self, tagh, tagl): cmd_data = iso7816_compose(0xca, tagh, tagl, "") - result = self.icc_send_cmd(cmd_data) - sw = result[-2:] - if not (sw[0] == 0x90 and sw[1] == 0x00): - raise ValueError, "cmd_get_data" - return result[:-2] + sw = self.icc_send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError, sw + if sw[0] != 0x61: + raise ValueError, ("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) def compare(data_original, data_in_device): i = 0 @@ -211,7 +257,7 @@ def compare(data_original, data_in_device): raise ValueError, "verify failed at %08x" % i i += 1 -def get_device(): +def gnuk_devices(): busses = usb.busses() for bus in busses: devices = bus.devices @@ -222,33 +268,53 @@ def get_device(): if alt.interfaceClass == CCID_CLASS and \ alt.interfaceSubClass == CCID_SUBCLASS and \ alt.interfaceProtocol == CCID_PROTOCOL_0: - return dev, config, alt - raise ValueError, "Device not found" + yield dev, config, alt -def main(fileid, is_update, data): - dev, config, intf = get_device() - print "Device: ", dev.filename - print "Configuration: ", config.value - print "Interface: ", intf.interfaceNumber - icc = gnuk_token(dev, config, intf) +DEFAULT_PW3 = "12345678" +BY_ADMIN = 3 + +def main(fileid, is_update, data, passwd): + icc = None + for (dev, config, intf) in gnuk_devices(): + try: + icc = gnuk_token(dev, config, intf) + print "Device: ", dev.filename + print "Configuration: ", config.value + print "Interface: ", intf.interfaceNumber + break + except: + pass if icc.icc_get_status() == 2: raise ValueError, "No ICC present" elif icc.icc_get_status() == 1: icc.icc_power_on() - icc.cmd_verify(3, "12345678") + icc.cmd_verify(BY_ADMIN, passwd) if is_update: icc.cmd_update_binary(fileid, data) else: icc.cmd_write_binary(fileid, data) icc.cmd_select_openpgp() - if fileid == 1: - data = data[:-2] + if fileid == 0: + data_in_device = icc.cmd_get_data(0x00, 0x4f) + for d in data_in_device: + print "%02x" % d, + print + compare(data, data_in_device[8:]) + elif fileid >= 1 and fileid <= 4: + data_in_device = icc.cmd_read_binary(fileid) + compare(data, data_in_device) + else: data_in_device = icc.cmd_get_data(0x7f, 0x21) compare(data, data_in_device) icc.icc_power_off() return 0 if __name__ == '__main__': + passwd = DEFAULT_PW3 + if sys.argv[1] == '-p': + from getpass import getpass + passwd = getpass("Admin password: ") + sys.argv.pop(1) if sys.argv[1] == '-u': is_update = True sys.argv.pop(1) @@ -270,6 +336,13 @@ if __name__ == '__main__': exit(1) print "Writing serial number" data = binascii.unhexlify(serial_data_hex) + elif sys.argv[1] == '-k': # firmware update key + keyno = sys.argv[2] + fileid = 1 + int(keyno) + filename = sys.argv[3] + f = open(filename) + data = f.read() + f.close() else: fileid = 5 # Card holder certificate filename = sys.argv[1] @@ -278,4 +351,4 @@ if __name__ == '__main__': f.close() print "%s: %d" % (filename, len(data)) print "Updating card holder certificate" - main(fileid, is_update, data) + main(fileid, is_update, data, passwd)