serial number support is not at compile time
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 8 Feb 2011 05:20:20 +0000 (14:20 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 8 Feb 2011 05:20:20 +0000 (14:20 +0900)
ChangeLog
FSIJ_SERIAL_NUMBER [deleted file]
GNUK_SERIAL_NUMBER [new file with mode: 0644]
src/config.h.in
src/configure
src/flash.c
src/gnuk.h
src/openpgp-do.c
src/openpgp.c
tool/gnuk_put_binary.py [new file with mode: 0755]
tool/gnuk_update_binary.py [deleted file]

index 1b1e82f..54652f9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2011-02-08  NIIBE Yutaka  <gniibe@fsij.org>
+
+       * tool/gnuk_put_binary.py: Renamed (was: gnuk_update_binary.py).
+       (gnuk_token.cmd_write_binary): New.
+       (main): Support writing serial number.
+
+       * GNUK_SERIAL_NUMBER: Renamed (was: FSIJ_SERIAL_NUMBER).
+
+       * src/config.h.in (@SERIAL_DEFINE@): Removed.
+
+       * src/gnuk.h (FILEID_SERIAL_NO): New.
+
+       * src/openpgp.c (INS_WRITE_BINARY, cmd_write_binary): New.
+
+       * src/configure: Remove --with-fixed-serial support.
+
+       * src/openpgp-do.c (do_openpgpcard_aid): Remove support of
+       SERIAL_NUMBER_IN_AID.
+
+       * src/flash.c (flash_write_binary): Support FILEID_SERIAL_NO.
+
 2011-02-04  NIIBE Yutaka  <gniibe@fsij.org>
 
        * tool/gnuk_update_binary.py: Support updating random bits.
diff --git a/FSIJ_SERIAL_NUMBER b/FSIJ_SERIAL_NUMBER
deleted file mode 100644 (file)
index ecad1f5..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# Email         # 4-byte serial number, separated by ':'
-gniibe@fsij.org 00:00:00:01
diff --git a/GNUK_SERIAL_NUMBER b/GNUK_SERIAL_NUMBER
new file mode 100644 (file)
index 0000000..8e06dae
--- /dev/null
@@ -0,0 +1,2 @@
+# Email         # 6-byte serial number, separated by ':'
+gniibe@fsij.org f5:17:00:00:00:01
index f599b0d..4d77b5f 100644 (file)
@@ -2,7 +2,6 @@
 #ifdef DEBUG
 #define ENABLE_VIRTUAL_COM_PORT 1
 #endif
-@SERIAL_DEFINE@
 @DFU_DEFINE@
 @PINPAD_DEFINE@
 @PINPAD_MORE_DEFINE@
index b79fc59..5a15035 100755 (executable)
@@ -24,7 +24,6 @@ help=no
 target=OLIMEX_STM32_H103
 verbose=no
 with_dfu=default
-with_fixed_serial=no
 debug=no
 pinpad=no
 
@@ -62,10 +61,6 @@ for option; do
     with_dfu=yes ;;
   --without-dfu)
     with_dfu=no ;;
-  --with-fixed-serial)
-    with_fixed_serial=yes ;;
-  --without-fixed-serial)
-    with_fixed_serial=no ;;
   *)
     echo "Unrecognized option \`$option'" >&2
     echo "Try \`$0 --help' for more information." >&2
@@ -94,7 +89,6 @@ Configuration:
   --enable-pinpad={cir,dial}
                        PIN input device support        [no]
   --with-dfu           build image for DFU             [<target specific>]
-  --with-fixed-serial  Use fixed serial number         [no: chip unique ID]
 EOF
   exit 0
 fi
@@ -135,20 +129,6 @@ STM8S_DISCOVERY)
   ;;
 esac
 
-# --with-fixed-serial option
-if test "$with_fixed_serial" = "no"; then
-  echo "Using chip unique ID for card AID"
-  SERIAL_DEFINE="#undef SERIAL_NUMBER_IN_AID"
-else
-  echo "Using fixed serial number (at compile time) for card AID"
-  if test "x$MAIL" = "x"; then
-    echo "ERROR: Please set MAIL shell variable to select FSIJ serial number" >&2
-    exit 1
-  fi
-  SERIAL=`sed -n -e "/^$MAIL/s/^.* \(..\):\(..\):\(..\):\(..\)/0x\1, 0x\2, 0x\3, 0x\4/p" ../FSIJ_SERIAL_NUMBER`
-  SERIAL_DEFINE="#define SERIAL_NUMBER_IN_AID $SERIAL"
-fi
-
 # --enable-debug option
 if test "$debug" = "yes"; then
   DEBUG_MAKE_OPTION="ENABLE_DEBUG=1"
@@ -201,6 +181,5 @@ sed -e "s/@DEBUG_DEFINE@/$DEBUG_DEFINE/" \
     -e "s/@PINPAD_DEFINE@/$PINPAD_DEFINE/" \
     -e "s/@PINPAD_MORE_DEFINE@/$PINPAD_MORE_DEFINE/" \
     -e "s/@DFU_DEFINE@/$DFU_DEFINE/" \
-    -e "s/@SERIAL_DEFINE@/$SERIAL_DEFINE/" \
        < config.h.in > config.h
 exit 0
index a3bea09..986db89 100644 (file)
@@ -646,6 +646,11 @@ flash_write_binary (uint8_t file_id, const uint8_t *data,
       maxsize = FLASH_PAGE_SIZE;
       p = &random_bits_start;
     }
+  else if (file_id == FILEID_SERIAL_NO)
+    {
+      maxsize = 6;
+      p = &openpgpcard_aid[8];
+    }
   else
     return -1;
 
index b3163c8..94adf10 100644 (file)
@@ -118,6 +118,7 @@ extern void flash_reset_counter (uint8_t counter_tag_nr);
 
 #define FILEID_CH_CERTIFICATE  0
 #define FILEID_RANDOM          1
+#define FILEID_SERIAL_NO       2
 extern int flash_erase_binary (uint8_t file_id);
 extern int flash_write_binary (uint8_t file_id, const uint8_t *data, uint16_t len, uint16_t offset);
 
@@ -305,7 +306,7 @@ extern uint8_t pw1_keystring[KEYSTRING_SIZE_PW1];
 #define OPENPGP_CARD_INITIAL_PW3 "12345678"
 #endif
 
-extern const uint8_t openpgpcard_aid[17] __attribute__ ((aligned (1)));
+extern const uint8_t openpgpcard_aid[14];
 
 extern int gpg_get_pw1_lifetime (void);
 
index 7df6bc9..d7571ba 100644 (file)
@@ -406,23 +406,17 @@ do_kgtime_all (uint16_t tag, int with_tag)
   return 1;
 }
 
-const uint8_t openpgpcard_aid_template[] = {
+const uint8_t openpgpcard_aid[] = {
   0xd2, 0x76, 0x00, 0x01, 0x24, 0x01,
   0x02, 0x00,                  /* Version 2.0 */
-#if defined(SERIAL_NUMBER_IN_AID)
-  0xf5, 0x17,                  /* Manufacturer: FSIJ */
-  SERIAL_NUMBER_IN_AID
-#else
-  0xff, 0xfe,                  /* Random bytes */
-#endif
+  0xff, 0xff, 0xff, 0xff,  0xff, 0xff, /* To be overwritten */
+  /* v. id */ /*   serial number   */
 };
 
 static int
 do_openpgpcard_aid (uint16_t tag, int with_tag)
 {
-#if !defined(SERIAL_NUMBER_IN_AID)
-  const uint8_t *u = unique_device_id ();
-#endif
+  const uint16_t *vid_p = (const uint16_t *)&openpgpcard_aid[8];
 
   if (with_tag)
     {
@@ -430,14 +424,28 @@ do_openpgpcard_aid (uint16_t tag, int with_tag)
       *res_p++ = 16;
     }
 
-  memcpy (res_p, openpgpcard_aid_template, sizeof (openpgpcard_aid_template));
-  res_p += sizeof (openpgpcard_aid_template);
-#if !defined(SERIAL_NUMBER_IN_AID)
-  memcpy (res_p, u, 4);
-  res_p += 4;
-#endif
+  if (*vid_p == 0xffff || *vid_p == 0x0000)
+    {
+      const uint8_t *u = unique_device_id ();
+
+      memcpy (res_p, openpgpcard_aid, 8);
+      res_p += 8;
+
+      /* vid == 0xfffe: serial number is random byte */
+      *res_p++ = 0xff;
+      *res_p++ = 0xfe;
+      memcpy (res_p, u, 4);
+      res_p += 4;
+    }
+  else
+    {
+      memcpy (res_p, openpgpcard_aid, 14);
+      res_p += 14;
+    }
+
   *res_p++ = 0;
   *res_p++ = 0;
+
   return 1;
 }
 
index 172058c..8b10e3f 100644 (file)
@@ -38,6 +38,7 @@
 #define INS_SELECT_FILE                                0xa4
 #define INS_READ_BINARY                                0xb0
 #define INS_GET_DATA                           0xca
+#define INS_WRITE_BINARY                       0xd0
 #define INS_UPDATE_BINARY                      0xd6
 #define INS_PUT_DATA                           0xda
 #define INS_PUT_DATA_ODD                       0xdb    /* For key import */
@@ -916,6 +917,68 @@ cmd_update_binary (void)
 }
 
 
+static void
+cmd_write_binary (void)
+{
+  int len = cmd_APDU[4];
+  int data_start = 5;
+  uint16_t offset;
+  int r;
+
+  if (len == 0)
+    {
+      len = (cmd_APDU[5]<<8) | cmd_APDU[6];
+      data_start = 7;
+    }
+
+  DEBUG_INFO (" - WRITE BINARY\r\n");
+
+  if (gpg_passwd_locked (PW_ERR_PW3) || !ac_check_status (AC_ADMIN_AUTHORIZED))
+    {
+      DEBUG_INFO ("security error.");
+      GPG_SECURITY_FAILURE ();
+      return;
+    }
+
+  if ((cmd_APDU[2] & 0x80))
+    if ((cmd_APDU[2] & 0x7f) == FILEID_SERIAL_NO)
+      {
+       file_selection = FILE_EF_CH_CERTIFICATE + (cmd_APDU[2] & 0x7f);
+       offset = 0;
+      }
+    else
+      {
+       GPG_NO_FILE ();
+       return;
+      }
+  else
+    {
+      if (file_selection != FILEID_SERIAL_NO)
+       {
+         GPG_COMMAND_NOT_ALLOWED ();
+         return;
+       }
+
+      offset = (cmd_APDU[2] << 8) | cmd_APDU[3];
+    }
+
+  DEBUG_SHORT (len);
+  DEBUG_SHORT (offset);
+
+  r = flash_write_binary (file_selection - FILE_EF_CH_CERTIFICATE,
+                         &cmd_APDU[data_start], len, offset);
+  if (r < 0)
+    {
+      DEBUG_INFO ("memory error.\r\n");
+      GPG_MEMORY_FAILURE ();
+      return;
+    }
+
+  GPG_SUCCESS ();
+  DEBUG_INFO ("WRITE BINARY done.\r\n");
+}
+
+
 struct command
 {
   uint8_t command;
@@ -932,6 +995,7 @@ const struct command cmds[] = {
   { INS_SELECT_FILE, cmd_select_file },
   { INS_READ_BINARY, cmd_read_binary },
   { INS_GET_DATA, cmd_get_data },
+  { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */
   { INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */
   { INS_PUT_DATA, cmd_put_data },
   { INS_PUT_DATA_ODD, cmd_put_data },
diff --git a/tool/gnuk_put_binary.py b/tool/gnuk_put_binary.py
new file mode 100755 (executable)
index 0000000..f824b44
--- /dev/null
@@ -0,0 +1,301 @@
+#! /usr/bin/python
+
+"""
+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
+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 intel_hex import *
+from struct import *
+import sys, time, os, binascii
+
+# 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('>BBBBBh', cls, ins, p1, p2, 0, data_len) + data
+
+# This class only supports Gnuk (for now) 
+class gnuk_token:
+    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 = 2
+        self.__bulkin  = 0x81
+
+        self.__timeout = 10000
+        self.__seq = 0
+
+
+    def __del__(self):
+        try:
+            self.__devhandle.releaseInterface()
+            del self.__devhandle
+        except:
+            pass
+
+    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_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"
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError, "cmd_verify"
+
+    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])
+            else:
+                cmd_data = iso7816_compose(0xd0, count, 0x00, data[256*count:256*(count+1)])
+            sw = self.icc_send_cmd(cmd_data)
+            if len(sw) != 2:
+                raise ValueError, "cmd_write_binary"
+            if not (sw[0] == 0x90 and sw[1] == 0x00):
+                raise ValueError, "cmd_write_binary"
+            count += 1
+
+    def cmd_update_binary(self, fileid, data):
+        count = 0
+        data_len = len(data)
+        while count*256 < data_len:
+            if count == 0:
+                cmd_data = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[:256])
+            else:
+                cmd_data = iso7816_compose(0xd6, count, 0x00, data[256*count:256*(count+1)])
+            sw = self.icc_send_cmd(cmd_data)
+            if len(sw) != 2:
+                raise ValueError, "cmd_update_binary"
+            if not (sw[0] == 0x90 and sw[1] == 0x00):
+                raise ValueError, "cmd_update_binary"
+            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, "cmd_select_openpgp"
+        if not (sw[0] == 0x90 and sw[1] == 0x00):
+            raise ValueError, "cmd_select_openpgp"
+
+    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]
+
+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 get_device():
+    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:
+                            return dev, config, alt
+    raise ValueError, "Device not found"
+
+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)
+    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")
+    if is_update:
+        icc.cmd_update_binary(fileid, data)
+    else:
+        icc.cmd_write_binary(fileid, data)
+    icc.cmd_select_openpgp()
+    data = data[:-2]
+    data_in_device = icc.cmd_get_data(0x7f, 0x21)
+    compare(data, data_in_device)
+    icc.icc_power_off()
+    return 0
+
+if __name__ == '__main__':
+    if sys.argv[1] == '-u':
+        is_update = True
+        sys.argv.pop(1)
+    else:
+        is_update = False
+    if sys.argv[1] == '-s':
+        fileid = 2              # serial number
+        filename = sys.argv[2]
+        f = open(filename)
+        email = os.environ['MAIL']
+        serial_data_hex = None
+        for line in f.readlines():
+            field = string.split(line)
+            if field[0] == os.environ['MAIL']:
+                serial_data_hex = field[1].replace(':','')
+        f.close()
+        if not serial_data_hex:
+            print "No serial number"
+            exit 1
+        print "Writing serial number"
+        data = binascii.unhexlify(serial_data_hex)
+    elif sys.argv[1] == '-r':
+        fileid = 1              # Random number bits
+        if len(sys.argv) == 3:
+            filename = sys.argv[2]
+            f = open(filename)
+        else:
+            filename = stdin
+            f = sys.stdin
+        data = f.read()
+        f.close()
+        print "%s: %d" % (filename, len(data))
+        print "Updating random bits"
+    else:
+        fileid = 0              # Card holder certificate
+        filename = sys.argv[2]
+        f = open(filename)
+        data = f.read()
+        f.close()
+        print "%s: %d" % (filename, len(data))
+        data += "\x90\x00"
+        print "Updating card holder certificate"
+    main(fileid, is_update, data)
diff --git a/tool/gnuk_update_binary.py b/tool/gnuk_update_binary.py
deleted file mode 100755 (executable)
index f72e7c4..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-#! /usr/bin/python
-
-"""
-gnuk_update_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
-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 intel_hex import *
-from struct import *
-import sys, time, os
-
-# 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('>BBBBBh', cls, ins, p1, p2, 0, data_len) + data
-
-# This class only supports Gnuk (for now) 
-class gnuk_token:
-    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 = 2
-        self.__bulkin  = 0x81
-
-        self.__timeout = 10000
-        self.__seq = 0
-
-
-    def __del__(self):
-        try:
-            self.__devhandle.releaseInterface()
-            del self.__devhandle
-        except:
-            pass
-
-    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_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"
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError, "cmd_verify"
-
-    def cmd_update_binary(self, fileid, data):
-        count = 0
-        data_len = len(data)
-        while count*256 < data_len:
-            if count == 0:
-                cmd_data = iso7816_compose(0xd6, 0x80+fileid, 0x00, data[:256])
-            else:
-                cmd_data = iso7816_compose(0xd6, count, 0x00, data[256*count:256*(count+1)])
-            sw = self.icc_send_cmd(cmd_data)
-            if len(sw) != 2:
-                raise ValueError, "cmd_update_binary"
-            if not (sw[0] == 0x90 and sw[1] == 0x00):
-                raise ValueError, "cmd_update_binary"
-            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, "cmd_select_openpgp"
-        if not (sw[0] == 0x90 and sw[1] == 0x00):
-            raise ValueError, "cmd_select_openpgp"
-
-    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]
-
-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 get_device():
-    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:
-                            return dev, config, alt
-    raise ValueError, "Device not found"
-
-def main(fileid, filename):
-    f = open(filename)
-    data = f.read()
-    f.close()
-    print "%s: %d" % (filename, len(data))
-    data += "\x90\x00"
-    dev, config, intf = get_device()
-    print "Device: ", dev.filename
-    print "Configuration: ", config.value
-    print "Interface: ", intf.interfaceNumber
-    icc = gnuk_token(dev, config, intf)
-    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_update_binary(fileid, data)
-    icc.cmd_select_openpgp()
-    data = data[:-2]
-    data_in_device = icc.cmd_get_data(0x7f, 0x21)
-    compare(data, data_in_device)
-    icc.icc_power_off()
-    return 0
-
-if __name__ == '__main__':
-    if os.path.basename(sys.argv[1] == "random_bits"):
-        fileid = 1
-        print "Updating random bits"
-    else:
-        fileid = 0              # Card holder certificate
-        print "Updating card holder certificate"
-    main(fileid, sys.argv[1])