Use tool/gnuk_token.py
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 19 Dec 2012 01:44:50 +0000 (10:44 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 19 Dec 2012 01:44:50 +0000 (10:44 +0900)
ChangeLog
test/features/steps.py
test/gnuk.py [deleted file]
test/gnuk_token.py [new symlink]
tool/gnuk_put_binary_libusb.py
tool/gnuk_token.py

index f005323..ccc17a0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2012-12-19  Niibe Yutaka  <gniibe@fsij.org>
+
+       * test/features/steps.py: Use tool/gnuk_token.py.
+
+       * tool/gnuk_put_binary_libusb.py: Use gnuk_token.py.
+       (main): Follow the API change.
+
+       * tool/gnuk_token.py (list_to_string): New.
+       (gnuk_token.get_string, gnuk_token.increment_seq)
+       (gnuk_token.reset_device, gnuk_token.release_gnuk): New.
+       (gnuk_token.icc_power_on): Set self.atr and it's now string.
+       (gnuk_token.icc_send_cmd): Handle time extension.
+       (gnuk_token.cmd_get_response): Return string (was: list).
+       (gnuk_token.cmd_get_data): Return "" when success.
+       (gnuk_token.cmd_change_reference_data, gnuk_token.cmd_put_data)
+       (gnuk_token.cmd_put_data_odd)
+       (gnuk_token.cmd_reset_retry_counter, gnuk_token.cmd_pso)
+       (gnuk_token.cmd_pso_longdata)
+       (gnuk_token.cmd_internal_authenticate, gnuk_token.cmd_genkey)
+       (gnuk_token.cmd_get_public_key): New.
+       (compare): New.
+       (get_gnuk_device): New.
+
 2012-12-14  Niibe Yutaka  <gniibe@fsij.org>
 
        * src/openpgp.c (cmd_change_password): Check password length
index 70e3046..dc915e2 100644 (file)
@@ -5,7 +5,7 @@ from binascii import hexlify
 
 import ast
 
-import gnuk
+import gnuk_token as gnuk
 import rsa_keys
 
 @Before
diff --git a/test/gnuk.py b/test/gnuk.py
deleted file mode 100644 (file)
index 13a9320..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-"""
-gnuk.py - a library for Gnuk Token
-This tool is for importing certificate, writing serial number, etc.
-
-Copyright (C) 2011, 2012 Free Software Initiative of Japan
-Author: NIIBE Yutaka <gniibe@fsij.org>
-
-This file is a part of Gnuk, a GnuPG USB Token implementation.
-
-Gnuk is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Gnuk is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-from struct import *
-import string
-
-# Assume only single CCID device is attached to computer, and it's Gnuk Token
-
-import usb
-
-# USB class, subclass, protocol
-CCID_CLASS = 0x0B
-CCID_SUBCLASS = 0x00
-CCID_PROTOCOL_0 = 0x00
-
-def icc_compose(msg_type, data_len, slot, seq, param, data):
-    return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
-
-def iso7816_compose(ins, p1, p2, data, cls=0x00):
-    data_len = len(data)
-    if data_len == 0:
-        return pack('>BBBB', cls, ins, p1, p2)
-    else:
-        return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
-
-def list_to_string(l):
-    return string.join([chr(c) for c in l], '')
-
-class gnuk_token(object):
-    def __init__(self, device, configuration, interface):
-        """
-        __init__(device, configuration, interface) -> None
-        Initialize the device.
-        device: usb.Device object.
-        configuration: configuration number.
-        interface: usb.Interface object representing the interface and altenate setting.
-        """
-        if interface.interfaceClass != CCID_CLASS:
-            raise ValueError("Wrong interface class")
-        if interface.interfaceSubClass != CCID_SUBCLASS:
-            raise ValueError("Wrong interface sub class")
-        self.__devhandle = device.open()
-        try:
-            self.__devhandle.setConfiguration(configuration)
-        except:
-            pass
-        self.__devhandle.claimInterface(interface)
-        self.__devhandle.setAltInterface(interface)
-
-        self.__intf = interface.interfaceNumber
-        self.__alt = interface.alternateSetting
-        self.__conf = configuration
-
-        self.__bulkout = 1
-        self.__bulkin  = 0x81
-
-        self.__timeout = 10000
-        self.__seq = 0
-
-    def get_string(self, num):
-        return self.__devhandle.getString(num, 512)
-
-    def increment_seq(self):
-        self.__seq = (self.__seq + 1) & 0xff
-
-    def reset_device(self):
-        try:
-            self.__devhandle.reset()
-        except:
-            pass
-
-    def release_gnuk(self):
-        self.__devhandle.releaseInterface()
-
-    def icc_get_result(self):
-        msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
-        if len(msg) < 10:
-            print msg
-            raise ValueError("icc_get_result")
-        msg_type = msg[0]
-        data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
-        slot = msg[5]
-        seq = msg[6]
-        status = msg[7]
-        error = msg[8]
-        chain = msg[9]
-        data = msg[10:]
-        # XXX: check msg_type, data_len, slot, seq, error
-        return (status, chain, data)
-
-    def icc_get_status(self):
-        msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.increment_seq()
-        status, chain, data = self.icc_get_result()
-        # XXX: check chain, data
-        return status
-
-    def icc_power_on(self):
-        msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.increment_seq()
-        status, chain, data = self.icc_get_result()
-        # XXX: check status, chain
-        self.atr = list_to_string(data) # ATR
-
-    def icc_power_off(self):
-        msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.increment_seq()
-        status, chain, data = self.icc_get_result()
-        # XXX: check chain, data
-        return status
-
-    def icc_send_data_block(self, data):
-        msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.increment_seq()
-        return self.icc_get_result()
-
-    def icc_send_cmd(self, data):
-        status, chain, data_rcv = self.icc_send_data_block(data)
-        if chain == 0:
-            while status == 0x80:
-                status, chain, data_rcv = self.icc_get_result()
-            return data_rcv
-        elif chain == 1:
-            d = data_rcv
-            while True:
-                msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
-                self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-                self.increment_seq()
-                status, chain, data_rcv = self.icc_get_result()
-                # XXX: check status
-                d += data_rcv
-                if chain == 2:
-                    break
-                elif chain == 3:
-                    continue
-                else:
-                    raise ValueError("icc_send_cmd chain")
-            return d
-        else:
-            raise ValueError("icc_send_cmd")
-
-    def cmd_get_response(self, expected_len):
-        result = []
-        while True:
-            cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len)
-            response = self.icc_send_cmd(cmd_data)
-            result += response[:-2]
-            sw = response[-2:]
-            if sw[0] == 0x90 and sw[1] == 0x00:
-                return list_to_string(result)
-            elif sw[0] != 0x61:
-                raise ValueError("%02x%02x" % (sw[0], sw[1]))
-            else:
-                expected_len = sw[1]
-
-    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(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    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, is_update):
-        count = 0
-        data_len = len(data)
-        if is_update:
-            ins = 0xd6
-        else:
-            ins = 0xd0
-        while count*256 < data_len:
-            if count == 0:
-                if len(data) < 128:
-                    cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
-                    cmd_data1 = None
-                else:
-                    cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
-                    cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
-            else:
-                if len(data[256*count:256*count+128]) < 128:
-                    cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
-                    cmd_data1 = None
-                else:
-                    cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
-                    cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
-            sw = self.icc_send_cmd(cmd_data0)
-            if len(sw) != 2:
-                raise ValueError("cmd_write_binary 0")
-            if not (sw[0] == 0x90 and sw[1] == 0x00):
-                raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
-            if cmd_data1:
-                sw = self.icc_send_cmd(cmd_data1)
-                if len(sw) != 2:
-                    raise ValueError("cmd_write_binary", sw)
-                if not (sw[0] == 0x90 and sw[1] == 0x00):
-                    raise ValueError("cmd_write_binary", "%02x%02x" % (sw[0], sw[1]))
-            count += 1
-
-    def cmd_select_openpgp(self):
-        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, sw
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    def cmd_get_data(self, tagh, tagl):
-        cmd_data = iso7816_compose(0xca, tagh, tagl, "")
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError, sw
-        if sw[0] == 0x90 and sw[1] == 0x00:
-            return ""
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return self.cmd_get_response(sw[1])
-
-    def cmd_change_reference_data(self, who, data):
-        cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    def cmd_put_data(self, tagh, tagl, content):
-        cmd_data = iso7816_compose(0xda, tagh, tagl, content)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    def cmd_put_data_odd(self, tagh, tagl, content):
-        cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
-        cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
-        sw = self.icc_send_cmd(cmd_data0)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        sw = self.icc_send_cmd(cmd_data1)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    def cmd_reset_retry_counter(self, how, data):
-        cmd_data = iso7816_compose(0x2c, how, 0x00, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return True
-
-    def cmd_pso(self, p1, p2, data):
-        cmd_data = iso7816_compose(0x2a, p1, p2, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if sw[0] == 0x90 and sw[1] == 0x00:
-            return ""
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return self.cmd_get_response(sw[1])
-
-    def cmd_pso_longdata(self, p1, p2, data):
-        cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
-        cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
-        sw = self.icc_send_cmd(cmd_data0)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        sw = self.icc_send_cmd(cmd_data1)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return self.cmd_get_response(sw[1])
-
-    def cmd_internal_authenticate(self, data):
-        cmd_data = iso7816_compose(0x88, 0, 0, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if sw[0] == 0x90 and sw[1] == 0x00:
-            return ""
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        return self.cmd_get_response(sw[1])
-
-    def cmd_genkey(self, keyno):
-        if keyno == 1:
-            data = '\xb6\x00'
-        elif keyno == 2:
-            data = '\xb8\x00'
-        else:
-            data = '\xa4\x00'
-        cmd_data = iso7816_compose(0x47, 0x80, 0, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        if sw[0] == 0x90 and sw[1] == 0x00:
-            return ""
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        pk = self.cmd_get_response(sw[1])
-        return (pk[9:9+256], pk[9+256+2:9+256+2+3])
-
-    def cmd_get_public_key(self, keyno):
-        if keyno == 1:
-            data = '\xb6\x00'
-        elif keyno == 2:
-            data = '\xb8\x00'
-        else:
-            data = '\xa4\x00'
-        cmd_data = iso7816_compose(0x47, 0x81, 0, data)
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError(sw)
-        elif sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
-        pk = self.cmd_get_response(sw[1])
-        return (pk[9:9+256], pk[9+256+2:9+256+2+3])
-
-
-def compare(data_original, data_in_device):
-    i = 0 
-    for d in data_original:
-        if ord(d) != data_in_device[i]:
-            raise ValueError, "verify failed at %08x" % i
-        i += 1
-
-def gnuk_devices():
-    busses = usb.busses()
-    for bus in busses:
-        devices = bus.devices
-        for dev in devices:
-            for config in dev.configurations:
-                for intf in config.interfaces:
-                    for alt in intf:
-                        if alt.interfaceClass == CCID_CLASS and \
-                                alt.interfaceSubClass == CCID_SUBCLASS and \
-                                alt.interfaceProtocol == CCID_PROTOCOL_0:
-                            yield dev, config, alt
-
-def get_gnuk_device():
-    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 not icc:
-        raise ValueError("No ICC present")
-    status = icc.icc_get_status()
-    if status == 0:
-        pass                    # It's ON already
-    elif status == 1:
-        icc.icc_power_on()
-    else:
-        raise ValueError("Unknown ICC status", status)
-    return icc
diff --git a/test/gnuk_token.py b/test/gnuk_token.py
new file mode 120000 (symlink)
index 0000000..c89e721
--- /dev/null
@@ -0,0 +1 @@
+../tool/gnuk_token.py
\ No newline at end of file
index 5b1455f..057a4b9 100755 (executable)
@@ -25,266 +25,46 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from struct import *
 import sys, time, os, binascii, string
+from gnuk_token import *
 
 # INPUT: binary file
 
 # Assume only single CCID device is attached to computer, and it's Gnuk Token
 
-import usb
-
-# USB class, subclass, protocol
-CCID_CLASS = 0x0B
-CCID_SUBCLASS = 0x00
-CCID_PROTOCOL_0 = 0x00
-
-def icc_compose(msg_type, data_len, slot, seq, param, data):
-    return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
-
-def iso7816_compose(ins, p1, p2, data, cls=0x00):
-    data_len = len(data)
-    if data_len == 0:
-        return pack('>BBBB', cls, ins, p1, p2)
-    else:
-        return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
-
-# This class only supports Gnuk (for now) 
-class gnuk_token(object):
-    def __init__(self, device, configuration, interface):
-        """
-        __init__(device, configuration, interface) -> None
-        Initialize the device.
-        device: usb.Device object.
-        configuration: configuration number.
-        interface: usb.Interface object representing the interface and altenate setting.
-        """
-        if interface.interfaceClass != CCID_CLASS:
-            raise ValueError, "Wrong interface class"
-        if interface.interfaceSubClass != CCID_SUBCLASS:
-            raise ValueError, "Wrong interface sub class"
-        self.__devhandle = device.open()
-        try:
-            self.__devhandle.setConfiguration(configuration)
-        except:
-            pass
-        self.__devhandle.claimInterface(interface)
-        self.__devhandle.setAltInterface(interface)
-
-        self.__intf = interface.interfaceNumber
-        self.__alt = interface.alternateSetting
-        self.__conf = configuration
-
-        self.__bulkout = 1
-        self.__bulkin  = 0x81
-
-        self.__timeout = 10000
-        self.__seq = 0
-
-    def icc_get_result(self):
-        msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
-        if len(msg) < 10:
-            print msg
-            raise ValueError, "icc_get_result"
-        msg_type = msg[0]
-        data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
-        slot = msg[5]
-        seq = msg[6]
-        status = msg[7]
-        error = msg[8]
-        chain = msg[9]
-        data = msg[10:]
-        # XXX: check msg_type, data_len, slot, seq, error
-        return (status, chain, data)
-
-    def icc_get_status(self):
-        msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
-        status, chain, data = self.icc_get_result()
-        # XXX: check chain, data
-        return status
-
-    def icc_power_on(self):
-        msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
-        status, chain, data = self.icc_get_result()
-        # XXX: check status, chain
-        return data             # ATR
-
-    def icc_power_off(self):
-        msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
-        status, chain, data = self.icc_get_result()
-        # XXX: check chain, data
-        return status
-
-    def icc_send_data_block(self, data):
-        msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
-        self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
-        return self.icc_get_result()
-
-    def icc_send_cmd(self, data):
-        status, chain, data_rcv = self.icc_send_data_block(data)
-        if chain == 0:
-            return data_rcv
-        elif chain == 1:
-            d = data_rcv
-            while True:
-                msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
-                self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-                self.__seq += 1
-                status, chain, data_rcv = self.icc_get_result()
-                # XXX: check status
-                d += data_rcv
-                if chain == 2:
-                    break
-                elif chain == 3:
-                    continue
-                else:
-                    raise ValueError, "icc_send_cmd chain"
-            return d
-        else:
-            raise ValueError, "icc_send_cmd"
-
-    def cmd_get_response(self, expected_len):
-        result = []
-        while True:
-            cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len)
-            response = self.icc_send_cmd(cmd_data)
-            result += response[:-2]
-            sw = response[-2:]
-            if sw[0] == 0x90 and sw[1] == 0x00:
-                return result
-            elif sw[0] != 0x61:
-                raise ValueError, ("%02x%02x" % (sw[0], sw[1]))
-            else:
-                expected_len = sw[1]
-
-    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, sw
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            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, is_update):
-        count = 0
-        data_len = len(data)
-        if is_update:
-            ins = 0xd6
-        else:
-            ins = 0xd0
-        while count*256 < data_len:
-            if count == 0:
-                if len(data) < 128:
-                    cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128])
-                    cmd_data1 = None
-                else:
-                    cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10)
-                    cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256])
-            else:
-                if len(data[256*count:256*count+128]) < 128:
-                    cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128])
-                    cmd_data1 = None
-                else:
-                    cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10)
-                    cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
-            sw = self.icc_send_cmd(cmd_data0)
-            if len(sw) != 2:
-                raise ValueError, "cmd_write_binary 0"
-            if not (sw[0] == 0x90 and sw[1] == 0x00):
-                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, 0x0c, "\xD2\x76\x00\x01\x24\x01")
-        sw = self.icc_send_cmd(cmd_data)
-        if len(sw) != 2:
-            raise ValueError, sw
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError, ("%02x%02x" % (sw[0], sw[1]))
-
-    def cmd_get_data(self, tagh, tagl):
-        cmd_data = iso7816_compose(0xca, tagh, tagl, "")
-        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 
-    for d in data_original:
-        if ord(d) != data_in_device[i]:
-            raise ValueError, "verify failed at %08x" % i
-        i += 1
-
-def gnuk_devices():
-    busses = usb.busses()
-    for bus in busses:
-        devices = bus.devices
-        for dev in devices:
-            for config in dev.configurations:
-                for intf in config.interfaces:
-                    for alt in intf:
-                        if alt.interfaceClass == CCID_CLASS and \
-                                alt.interfaceSubClass == CCID_SUBCLASS and \
-                                alt.interfaceProtocol == CCID_PROTOCOL_0:
-                            yield dev, config, alt
-
 DEFAULT_PW3 = "12345678"
 BY_ADMIN = 3
 
 def main(fileid, is_update, data, passwd):
-    icc = None
+    gnuk = None
     for (dev, config, intf) in gnuk_devices():
         try:
-            icc = gnuk_token(dev, config, intf)
+            gnuk = 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:
+    if gnuk.icc_get_status() == 2:
         raise ValueError, "No ICC present"
-    elif icc.icc_get_status() == 1:
-        icc.icc_power_on()
-    icc.cmd_verify(BY_ADMIN, passwd)
-    icc.cmd_write_binary(fileid, data, is_update)
-    icc.cmd_select_openpgp()
+    elif gnuk.icc_get_status() == 1:
+        gnuk.icc_power_on()
+    gnuk.cmd_verify(BY_ADMIN, passwd)
+    gnuk.cmd_write_binary(fileid, data, is_update)
+    gnuk.cmd_select_openpgp()
     if fileid == 0:
-        data_in_device = icc.cmd_get_data(0x00, 0x4f)
+        data_in_device = gnuk.cmd_get_data(0x00, 0x4f)
         for d in data_in_device:
-            print "%02x" % d,
+            print "%02x" % ord(d),
         print
         compare(data, data_in_device[8:])
     elif fileid >= 1 and fileid <= 4:
-        data_in_device = icc.cmd_read_binary(fileid)
+        data_in_device = gnuk.cmd_read_binary(fileid)
         compare(data, data_in_device)
     else:
-        data_in_device = icc.cmd_get_data(0x7f, 0x21)
+        data_in_device = gnuk.cmd_get_data(0x7f, 0x21)
         compare(data, data_in_device)
-    icc.icc_power_off()
+    gnuk.icc_power_off()
     return 0
 
 if __name__ == '__main__':
index 95541e5..d1ce053 100644 (file)
@@ -21,6 +21,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
 from struct import *
+import string
 import usb
 
 # USB class, subclass, protocol
@@ -38,6 +39,9 @@ def iso7816_compose(ins, p1, p2, data, cls=0x00):
     else:
         return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
 
+def list_to_string(l):
+    return string.join([chr(c) for c in l], '')
+
 # This class only supports Gnuk (for now) 
 class gnuk_token(object):
     def __init__(self, device, configuration, interface):
@@ -49,9 +53,9 @@ class gnuk_token(object):
         interface: usb.Interface object representing the interface and altenate setting.
         """
         if interface.interfaceClass != CCID_CLASS:
-            raise ValueError, "Wrong interface class"
+            raise ValueError("Wrong interface class")
         if interface.interfaceSubClass != CCID_SUBCLASS:
-            raise ValueError, "Wrong interface sub class"
+            raise ValueError("Wrong interface sub class")
         self.__devhandle = device.open()
         try:
             self.__devhandle.setConfiguration(configuration)
@@ -70,11 +74,26 @@ class gnuk_token(object):
         self.__timeout = 10000
         self.__seq = 0
 
+    def get_string(self, num):
+        return self.__devhandle.getString(num, 512)
+
+    def increment_seq(self):
+        self.__seq = (self.__seq + 1) & 0xff
+
+    def reset_device(self):
+        try:
+            self.__devhandle.reset()
+        except:
+            pass
+
+    def release_gnuk(self):
+        self.__devhandle.releaseInterface()
+
     def icc_get_result(self):
         msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
         if len(msg) < 10:
             print msg
-            raise ValueError, "icc_get_result"
+            raise ValueError("icc_get_result")
         msg_type = msg[0]
         data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
         slot = msg[5]
@@ -89,7 +108,7 @@ class gnuk_token(object):
     def icc_get_status(self):
         msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
+        self.increment_seq()
         status, chain, data = self.icc_get_result()
         # XXX: check chain, data
         return status
@@ -97,15 +116,16 @@ class gnuk_token(object):
     def icc_power_on(self):
         msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
+        self.increment_seq()
         status, chain, data = self.icc_get_result()
         # XXX: check status, chain
-        return data             # ATR
+        self.atr = list_to_string(data) # ATR
+        return self.atr
 
     def icc_power_off(self):
         msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
+        self.increment_seq()
         status, chain, data = self.icc_get_result()
         # XXX: check chain, data
         return status
@@ -113,19 +133,21 @@ class gnuk_token(object):
     def icc_send_data_block(self, data):
         msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
         self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-        self.__seq += 1
+        self.increment_seq()
         return self.icc_get_result()
 
     def icc_send_cmd(self, data):
         status, chain, data_rcv = self.icc_send_data_block(data)
         if chain == 0:
+            while status == 0x80:
+                status, chain, data_rcv = self.icc_get_result()
             return data_rcv
         elif chain == 1:
             d = data_rcv
             while True:
                 msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
                 self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
-                self.__seq += 1
+                self.increment_seq()
                 status, chain, data_rcv = self.icc_get_result()
                 # XXX: check status
                 d += data_rcv
@@ -134,10 +156,10 @@ class gnuk_token(object):
                 elif chain == 3:
                     continue
                 else:
-                    raise ValueError, "icc_send_cmd chain"
+                    raise ValueError("icc_send_cmd chain")
             return d
         else:
-            raise ValueError, "icc_send_cmd"
+            raise ValueError("icc_send_cmd")
 
     def cmd_get_response(self, expected_len):
         result = []
@@ -147,9 +169,9 @@ class gnuk_token(object):
             result += response[:-2]
             sw = response[-2:]
             if sw[0] == 0x90 and sw[1] == 0x00:
-                return result
+                return list_to_string(result)
             elif sw[0] != 0x61:
-                raise ValueError("%02x%02x" % (sw[0], sw[1]))
+                raise ValueError("%02x%02x" % (sw[0], sw[1]))
             else:
                 expected_len = sw[1]
 
@@ -157,17 +179,18 @@ class gnuk_token(object):
         cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd)
         sw = self.icc_send_cmd(cmd_data)
         if len(sw) != 2:
-            raise ValueError, sw
+            raise ValueError(sw)
         if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError, sw
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
 
     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
+            raise ValueError(sw)
         if sw[0] != 0x61:
-            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
         return self.cmd_get_response(sw[1])
 
     def cmd_write_binary(self, fileid, data, is_update):
@@ -194,15 +217,15 @@ class gnuk_token(object):
                     cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)])
             sw = self.icc_send_cmd(cmd_data0)
             if len(sw) != 2:
-                raise ValueError, "cmd_write_binary 0"
+                raise ValueError("cmd_write_binary 0")
             if not (sw[0] == 0x90 and sw[1] == 0x00):
-                raise ValueError, "cmd_write_binary 0"
+                raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1]))
             if cmd_data1:
                 sw = self.icc_send_cmd(cmd_data1)
                 if len(sw) != 2:
-                    raise ValueError, "cmd_write_binary 1"
+                    raise ValueError("cmd_write_binary 1", sw)
                 if not (sw[0] == 0x90 and sw[1] == 0x00):
-                    raise ValueError, "cmd_write_binary 1"
+                    raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1]))
             count += 1
 
     def cmd_select_openpgp(self):
@@ -211,17 +234,133 @@ class gnuk_token(object):
         if len(sw) != 2:
             raise ValueError, sw
         if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError, ("%02x%02x" % (sw[0], sw[1]))
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
 
     def cmd_get_data(self, tagh, tagl):
         cmd_data = iso7816_compose(0xca, tagh, tagl, "")
         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]))
+        if sw[0] == 0x90 and sw[1] == 0x00:
+            return ""
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return self.cmd_get_response(sw[1])
+
+    def cmd_change_reference_data(self, who, data):
+        cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
+
+    def cmd_put_data(self, tagh, tagl, content):
+        cmd_data = iso7816_compose(0xda, tagh, tagl, content)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
+
+    def cmd_put_data_odd(self, tagh, tagl, content):
+        cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
+        cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
+        sw = self.icc_send_cmd(cmd_data0)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        sw = self.icc_send_cmd(cmd_data1)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
+
+    def cmd_reset_retry_counter(self, how, data):
+        cmd_data = iso7816_compose(0x2c, how, 0x00, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return True
+
+    def cmd_pso(self, p1, p2, data):
+        cmd_data = iso7816_compose(0x2a, p1, p2, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if sw[0] == 0x90 and sw[1] == 0x00:
+            return ""
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return self.cmd_get_response(sw[1])
+
+    def cmd_pso_longdata(self, p1, p2, data):
+        cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
+        cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
+        sw = self.icc_send_cmd(cmd_data0)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        sw = self.icc_send_cmd(cmd_data1)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        return self.cmd_get_response(sw[1])
+
+    def cmd_internal_authenticate(self, data):
+        cmd_data = iso7816_compose(0x88, 0, 0, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if sw[0] == 0x90 and sw[1] == 0x00:
+            return ""
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
         return self.cmd_get_response(sw[1])
 
+    def cmd_genkey(self, keyno):
+        if keyno == 1:
+            data = '\xb6\x00'
+        elif keyno == 2:
+            data = '\xb8\x00'
+        else:
+            data = '\xa4\x00'
+        cmd_data = iso7816_compose(0x47, 0x80, 0, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        if sw[0] == 0x90 and sw[1] == 0x00:
+            return ""
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        pk = self.cmd_get_response(sw[1])
+        return (pk[9:9+256], pk[9+256+2:9+256+2+3])
+
+    def cmd_get_public_key(self, keyno):
+        if keyno == 1:
+            data = '\xb6\x00'
+        elif keyno == 2:
+            data = '\xb8\x00'
+        else:
+            data = '\xa4\x00'
+        cmd_data = iso7816_compose(0x47, 0x81, 0, data)
+        sw = self.icc_send_cmd(cmd_data)
+        if len(sw) != 2:
+            raise ValueError(sw)
+        elif sw[0] != 0x61:
+            raise ValueError("%02x%02x" % (sw[0], sw[1]))
+        pk = self.cmd_get_response(sw[1])
+        return (pk[9:9+256], pk[9+256+2:9+256+2+3])
+
     def cmd_put_data_remove(self, tagh, tagl):
         cmd_data = iso7816_compose(0xda, tagh, tagl, "")
         sw = self.icc_send_cmd(cmd_data)
@@ -240,6 +379,11 @@ class gnuk_token(object):
         if sw[0] != 0x90 and sw[1] != 0x00:
             raise ValueError, ("%02x%02x" % (sw[0], sw[1]))
 
+def compare(data_original, data_in_device):
+    if data_original == data_in_device:
+        return True
+    raise ValueError("verify failed")
+
 def gnuk_devices():
     busses = usb.busses()
     for bus in busses:
@@ -252,3 +396,25 @@ def gnuk_devices():
                                 alt.interfaceSubClass == CCID_SUBCLASS and \
                                 alt.interfaceProtocol == CCID_PROTOCOL_0:
                             yield dev, config, alt
+
+def get_gnuk_device():
+    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 not icc:
+        raise ValueError("No ICC present")
+    status = icc.icc_get_status()
+    if status == 0:
+        pass                    # It's ON already
+    elif status == 1:
+        icc.icc_power_on()
+    else:
+        raise ValueError("Unknown ICC status", status)
+    return icc