add support of card holder certificate
authorNIIBE Yutaka <gniibe@fsij.org>
Fri, 28 Jan 2011 08:38:52 +0000 (17:38 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Fri, 28 Jan 2011 11:54:05 +0000 (20:54 +0900)
ChangeLog
doc/HACKING
doc/NOTES
src/openpgp-do.c
src/openpgp.c
src/usb-icc.c
tool/dfuse.py
tool/dump_mem.py
tool/gnuk_update_binary.py [new file with mode: 0755]

index 309101f..2932902 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2011-01-28  NIIBE Yutaka  <gniibe@fsij.org>
+
+       * tool/gnuk_update_binary.py: New.
+
+       * src/openpgp-do.c (gpg_do_get_data): Fix length adding two for
+       status word at the end and adding four for the tag and the length.
+
+       * src/usb-icc.c (icc_handle_data): Fix decrementing res_APDU_size.
+       (icc_power_off): Status should be the one *after* power off.
+
+       * src/openpgp.c (cmd_update_binary): Fix return code.
+
 2011-01-27  NIIBE Yutaka  <gniibe@fsij.org>
 
        * src/usb-icc.c (res_APDU_pointer): New.
index c4cb1b1..6246e76 100644 (file)
@@ -29,11 +29,10 @@ It would be good not to use malloc.
 Get it from FSFE.
 
 
-* [Partially DONE] Serial number
+* [Mostly DONE] Serial number
 
-Currently, aid[] in openpgp-do.c has serial number 00000001.
-It would be good to generate (random) number at compile time.
-Use same serial number for OpenPGPcard and USB serial number.
+The AID of the card contains serial number.  It should be unique.  USB
+serial number should be unique to identify different tokens, too.
 
 
 * Flash ROM recover from shutdown
index 86626db..457ee66 100644 (file)
--- a/doc/NOTES
+++ b/doc/NOTES
@@ -4,15 +4,15 @@ USB communication
   * No command chaining, but extended APDU and extended Lc and Le.
     I think that this keep the code simple.
 
-  * Once, the value of dwMaxCCIDMessageLength was 64 and we supported
-    ICC block chaining, so that we could not handle multple Bulk
-    transactions.
+  * Once in the past (version <= 0.4), the value of
+    dwMaxCCIDMessageLength was 64 and we supported ICC block chaining,
+    so that we could not handle multple Bulk transactions.
 
   * Now, the value of dwMaxCCIDMessageLength is 320, that's the size
     of header of ICC block plus size of maximum APDU (by 64
-    granularity).  Still, some ccid implementation sends ICC block
-    using chaining (unfortunately), so we keep the code of ICC block
-    chaining.
+    granularity).  Still, some ccid implementation (ccid 1.3.11, for
+    example) sends ICC block using chaining unfortunately, so we keep
+    the code of ICC block chaining.
 
 
 OpenPGP card protocol implementation
index 6f9a1b1..e5ef24e 100644 (file)
@@ -1203,12 +1203,15 @@ gpg_do_get_data (uint16_t tag, int with_tag)
   if (tag == GPG_DO_CH_CERTIFICATE)
     {
       res_APDU_pointer = &ch_certificate_start;
-      res_APDU_size = (res_APDU_pointer[2] << 8) | res_APDU_pointer[3];
+      res_APDU_size = ((res_APDU_pointer[2] << 8) | res_APDU_pointer[3]);
       if (res_APDU_size == 0xffff)
        {
          res_APDU_pointer = NULL;
          GPG_NO_RECORD ();
        }
+      else
+       /* Add length of (tag+len) and status word (0x9000) at the end */
+       res_APDU_size += 4 + 2;
     }
   else
     {
index 5760516..a412e8f 100644 (file)
@@ -29,7 +29,6 @@
 #include "polarssl/config.h"
 #include "polarssl/sha1.h"
 
-#define INS_NOP                                        0x00
 #define INS_VERIFY                             0x20
 #define INS_CHANGE_REFERENCE_DATA              0x24
 #define INS_PSO                                        0x2a
@@ -907,6 +906,7 @@ cmd_update_binary (void)
       return;
     }
 
+  GPG_SUCCESS ();
   DEBUG_INFO ("UPDATE BINARY done.\r\n");
 }
 
@@ -943,8 +943,6 @@ process_command_apdu (void)
     if (cmds[i].command == cmd)
       break;
 
-  res_APDU_pointer = NULL; /* default */
-
   if (i < NUM_CMDS)
     cmds[i].cmd_handler ();
   else
@@ -973,6 +971,8 @@ GPGthread (void *arg)
       DEBUG_INFO ("GPG!: ");
       DEBUG_WORD ((uint32_t)&m);
 
+      res_APDU_pointer = NULL; /* default */
+
       if (icc_data_size != 0)
        {
          process_command_apdu ();
index 189e2ec..a4ceeb0 100644 (file)
@@ -379,6 +379,7 @@ icc_power_off (void)
       gpg_thread = NULL;
     }
 
+  icc_state = ICC_STATE_START; /* This status change should be here */
   icc_send_status ();
   DEBUG_INFO ("OFF\r\n");
   return ICC_STATE_START;
@@ -627,7 +628,7 @@ icc_handle_data (void)
              else
                {
                  icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x03);
-                 res_APDU_size -= ICC_MAX_MSG_DATA_SIZE;
+                 res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
                }
            }
          else
@@ -695,30 +696,30 @@ USBthread (void *arg)
       if (m == EV_RX_DATA_READY)
        icc_state = icc_handle_data ();
       else if (m == EV_EXEC_FINISHED)
-       {
-         if (icc_state == ICC_STATE_EXECUTE)
-           {
-             if (res_APDU_pointer != NULL)
+       if (icc_state == ICC_STATE_EXECUTE)
+         {
+           if (res_APDU_pointer != NULL)
+             {
                memcpy (res_APDU, res_APDU_pointer, ICC_RESPONSE_MSG_DATA_SIZE);
-
-             if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
-               {
-                 icc_send_data_block (res_APDU_size, 0, 0);
-                 icc_state = ICC_STATE_WAIT;
-               }
-             else
-               {
-                 icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01);
-                 res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
-                 res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
-                 icc_state = ICC_STATE_SEND;
-               }
-           }
-         else
-           {                   /* XXX: error */
-             DEBUG_INFO ("ERR07\r\n");
-           }
-       }
+               res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
+             }
+
+           if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
+             {
+               icc_send_data_block (res_APDU_size, 0, 0);
+               icc_state = ICC_STATE_WAIT;
+             }
+           else
+             {
+               icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01);
+               res_APDU_size -= ICC_RESPONSE_MSG_DATA_SIZE;
+               icc_state = ICC_STATE_SEND;
+             }
+         }
+       else
+         {                     /* XXX: error */
+           DEBUG_INFO ("ERR07\r\n");
+         }
       else if (m == EV_TX_FINISHED)
        {
          if (icc_state == ICC_STATE_START || icc_state == ICC_STATE_WAIT
index 3b6e52f..2f9c180 100755 (executable)
@@ -121,7 +121,7 @@ class DFU_STM32:
 
     def __del__(self):
         try:
-            self.__devhandle.releaseInterface(self.__intf)
+            self.__devhandle.releaseInterface()
             del self.__devhandle
         except:
             pass
index 88ebd5c..806bafe 100755 (executable)
@@ -1,5 +1,27 @@
 #! /usr/bin/python
 
+"""
+dump_mem.py - dump memory with DfuSe for STM32 Processor.
+
+Copyright (C) 2010 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/>.
+"""
+
 import sys
 from dfuse import *
 
diff --git a/tool/gnuk_update_binary.py b/tool/gnuk_update_binary.py
new file mode 100755 (executable)
index 0000000..a634720
--- /dev/null
@@ -0,0 +1,246 @@
+#! /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, struct
+
+# 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(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:
+        print icc.icc_power_on()
+    icc.cmd_verify(3, "12345678")
+    icc.cmd_update_binary(0, 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__':
+    main(sys.argv[1])