card holder certificate support
authorNIIBE Yutaka <gniibe@fsij.org>
Thu, 27 Jan 2011 09:17:01 +0000 (18:17 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Thu, 27 Jan 2011 09:17:01 +0000 (18:17 +0900)
ChangeLog
NEWS
src/flash.c
src/gnuk.h
src/gnuk.ld.in
src/openpgp-do.c
src/openpgp.c
src/openpgp.h
src/usb-icc.c

index c594045..309101f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,22 @@
 2011-01-27  NIIBE Yutaka  <gniibe@fsij.org>
 
-       * src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE
-       for now.
+       * src/usb-icc.c (res_APDU_pointer): New.
+       (icc_handle_data, USBthread): Handle res_APDU_pointer.
+
+       * src/openpgp.h (GPG_COMMAND_NOT_ALLOWED): New.
+
+       * src/openpgp.c (INS_UPDATE_BINARY, FILE_EF_CH_CERTIFICATE)
+       (FILE_EF_RANDOM, cmd_update_binary): New.
+       (process_command_apdu): Initialize res_APDU_pointer.
+
+       * src/openpgp-do.c (gpg_do_get_data): Handle GPG_DO_CH_CERTIFICATE.
+
+       * src/gnuk.ld.in (.gnuk_ch_certificate): New.
+
+       * src/flash.c (flash_check_blank, flash_erase_binary)
+       (flash_write_binary): New.
+
+       * src/openpgp-do.c (gpg_do_table): Exclude GPG_DO_CH_CERTIFICATE.
 
        * src/openpgp.c (cmd_reset_user_password): Add PINPAD_SUPPORT.
 
diff --git a/NEWS b/NEWS
index 30743a6..5d888fe 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ Gnuk NEWS - User visible changes
 
   Released 2011-01-XX, by NIIBE Yutaka
 
+** Card Holder Certificate is supported.
+
 ** Better interoperability to OpenSC.
 Gnuk is not yet supported by OpenSC, but it could be.  With the
 changes in Gnuk, it could be relatively easily possible to support
index 1e7d1a2..193b725 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM
  *
- * Copyright (C) 2010 Free Software Initiative of Japan
+ * Copyright (C) 2010, 2011 Free Software Initiative of Japan
  * Author: NIIBE Yutaka <gniibe@fsij.org>
  *
  * This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -25,8 +25,8 @@
  * We assume single DO size is less than 256.
  *
  * NOTE: When we will support "Card holder certificate" (which size is
- *       larger than 256), it will not be put into data pool, but will
- *       be implemented by its own flash page.
+ *       larger than 256), it is not put into data pool, but is
+ *       implemented by its own flash page(s).
  */
 
 #include "config.h"
@@ -167,7 +167,7 @@ static const uint8_t *keystore_pool;
 static uint8_t *last_p;
 static const uint8_t *keystore;
 
-/* The first halfward is generation for the data page (little endian) */
+/* The first halfword is generation for the data page (little endian) */
 const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
   0x01, 0x00, 0xff, 0xff
 };
@@ -300,7 +300,7 @@ flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len)
     flash_warning ("DO WRITE ERROR");
   addr += 2;
 
-  for (i = 0; i < len/2; i ++)
+  for (i = 0; i < len/2; i++)
     {
       hw = data[i*2] | (data[i*2+1]<<8);
       if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
@@ -581,3 +581,71 @@ flash_cnt123_clear (const uint8_t **addr_p)
   flash_program_halfword ((uint32_t)p, 0);
   *addr_p = NULL;
 }
+
+
+static int
+flash_check_blank (const uint8_t *page, int size)
+{
+  const uint8_t *p;
+
+  for (p = page; p < page + size; p++)
+    if (*p != 0xff)
+      return 0;
+
+  return 1;
+}
+
+
+#define FLASH_CH_CERTIFICATE_SIZE 2048
+int
+flash_erase_binary (uint8_t file_id)
+{
+  const uint8_t *p = &ch_certificate_start;
+
+  if (file_id == FILEID_CH_CERTIFICATE)
+    {
+      if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0)
+       {
+         flash_erase_page ((uint32_t)p);
+#if FLASH_CH_CERTIFICATE_SIZE > FLASH_PAGE_SIZE
+         flash_erase_page ((uint32_t)p + FLASH_PAGE_SIZE);
+#endif
+       }
+
+      return 0;
+    }
+  else
+    return -1;
+}
+
+
+int
+flash_write_binary (uint8_t file_id, const uint8_t *data,
+                   uint16_t len, uint16_t offset)
+{
+  if (file_id == FILEID_CH_CERTIFICATE)
+    {
+      if (offset + len > FLASH_CH_CERTIFICATE_SIZE || (offset&1) || (len&1))
+       return -1;
+      else
+       {
+         const uint8_t *p = &ch_certificate_start;
+         uint16_t hw;
+         uint32_t addr;
+         int i;
+
+         addr = (uint32_t)p + offset;
+         for (i = 0; i < len/2; i++)
+           {
+             hw = data[i*2] | (data[i*2+1]<<8);
+             if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
+               flash_warning ("DO WRITE ERROR");
+             addr += 2;
+           }
+
+         return 0;
+       }
+    }
+  else
+    return -1;
+}
index 114a3c7..c363271 100644 (file)
@@ -36,6 +36,7 @@ extern void *memmove(void *dest, const void *src, size_t n);
 extern int icc_data_size;
 #define cmd_APDU_size icc_data_size
 extern int res_APDU_size;
+extern uint8_t *res_APDU_pointer;
 
 /* USB buffer size of LL (Low-level): size of single Bulk transaction */
 #define USB_LL_BUF_SIZE 64
@@ -115,6 +116,14 @@ extern void flash_clear_halfword (uint32_t addr);
 extern void flash_increment_counter (uint8_t counter_tag_nr);
 extern void flash_reset_counter (uint8_t counter_tag_nr);
 
+#define FILEID_CH_CERTIFICATE  0
+#define FILEID_RANDOM          1
+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);
+
+/* Linker set this symbol */
+extern uint8_t ch_certificate_start;
+
 #define KEY_MAGIC_LEN 8
 #define KEY_CONTENT_LEN 256    /* p and q */
 #define GNUK_MAGIC "Gnuk KEY"
index c968c4e..a296369 100644 (file)
@@ -121,6 +121,14 @@ SECTIONS
         . = ALIGN (@FLASH_PAGE_SIZE@);
     } > flash =0xffffffff
 
+    .gnuk_ch_certificate :
+    {
+        . = ALIGN (@FLASH_PAGE_SIZE@);
+        ch_certificate_start = .;
+       . += 1920;
+        . = ALIGN (@FLASH_PAGE_SIZE@);
+    } > flash =0xffffffff
+
     .gnuk_flash :
     {
        _data_pool = .;
index 07b46fb..6f9a1b1 100644 (file)
@@ -929,8 +929,8 @@ gpg_do_table[] = {
   { GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED,
     proc_key_import },
 #if 0
-  /* Card holder certificate: Not supported yet */
-  { GPG_DO_CH_CERTIFICATE, DO_PROC_READWRITE, AC_NEVER, AC_NEVER, NULL },
+  /* Card holder certificate */
+  { GPG_DO_CH_CERTIFICATE, DO_PROC_READ, AC_ALWAYS, AC_NEVER, NULL },
 #endif
 };
 
@@ -1200,27 +1200,40 @@ copy_do (const struct do_table_entry *do_p, int with_tag)
 void
 gpg_do_get_data (uint16_t tag, int with_tag)
 {
-  const struct do_table_entry *do_p = get_do_entry (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];
+      if (res_APDU_size == 0xffff)
+       {
+         res_APDU_pointer = NULL;
+         GPG_NO_RECORD ();
+       }
+    }
+  else
+    {
+      const struct do_table_entry *do_p = get_do_entry (tag);
 
-  res_p = res_APDU;
+      res_p = res_APDU;
 
-  DEBUG_INFO ("   ");
-  DEBUG_SHORT (tag);
+      DEBUG_INFO ("   ");
+      DEBUG_SHORT (tag);
 
-  if (do_p)
-    {
-      if (copy_do (do_p, with_tag) < 0)
-       /* Overwriting partially written result  */
-       GPG_SECURITY_FAILURE ();
-      else
+      if (do_p)
        {
-         *res_p++ = 0x90;
-         *res_p++ = 0x00;
-         res_APDU_size = res_p - res_APDU;
+         if (copy_do (do_p, with_tag) < 0)
+           /* Overwriting partially written result  */
+           GPG_SECURITY_FAILURE ();
+         else
+           {
+             *res_p++ = 0x90;
+             *res_p++ = 0x00;
+             res_APDU_size = res_p - res_APDU;
+           }
        }
+      else
+       GPG_NO_RECORD ();
     }
-  else
-    GPG_NO_RECORD ();
 }
 
 void
index f90e176..5760516 100644 (file)
@@ -39,6 +39,7 @@
 #define INS_SELECT_FILE                                0xa4
 #define INS_READ_BINARY                                0xb0
 #define INS_GET_DATA                           0xca
+#define INS_UPDATE_BINARY                      0xd6
 #define INS_PUT_DATA                           0xda
 #define INS_PUT_DATA_ODD                       0xdb    /* For key import */
 
@@ -75,6 +76,8 @@ write_res_apdu (const uint8_t *p, int len, uint8_t sw1, uint8_t sw2)
 #define FILE_MF                2
 #define FILE_EF_DIR    3
 #define FILE_EF_SERIAL 4
+#define FILE_EF_CH_CERTIFICATE  5
+#define FILE_EF_RANDOM         6
 
 static uint8_t file_selection;
 
@@ -632,7 +635,7 @@ cmd_read_binary (void)
 static void
 cmd_select_file (void)
 {
-  if (cmd_APDU[2] == 4)        /* Selection by DF name */
+  if (cmd_APDU[2] == 4)        /* Selection by DF name: it must be OpenPGP card */
     {
       DEBUG_INFO (" - select DF by name\r\n");
 
@@ -654,8 +657,7 @@ cmd_select_file (void)
        }
     }
   else if (cmd_APDU[4] == 2
-          && cmd_APDU[5] == 0x2f
-          && cmd_APDU[6] == 0x02)
+          && cmd_APDU[5] == 0x2f && cmd_APDU[6] == 0x02)
     {
       DEBUG_INFO (" - select 0x2f02 EF\r\n");
       /*
@@ -665,8 +667,7 @@ cmd_select_file (void)
       file_selection = FILE_EF_SERIAL;
     }
   else if (cmd_APDU[4] == 2
-          && cmd_APDU[5] == 0x3f
-          && cmd_APDU[6] == 0x00)
+          && cmd_APDU[5] == 0x3f && cmd_APDU[6] == 0x00)
     {
       DEBUG_INFO (" - select ROOT MF\r\n");
       if (cmd_APDU[3] == 0x0c)
@@ -682,6 +683,7 @@ cmd_select_file (void)
        }
 
       file_selection = FILE_MF;
+      ac_fini ();              /* Reset authentication */
     }
   else
     {
@@ -838,6 +840,77 @@ cmd_internal_authenticate (void)
   DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
 }
 
+
+static void
+cmd_update_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 (" - UPDATE 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) <= 0x01)
+      {
+       file_selection = FILE_EF_CH_CERTIFICATE + (cmd_APDU[2] & 0x7f);
+       r = flash_erase_binary (file_selection - FILE_EF_CH_CERTIFICATE);
+       if (r < 0)
+         {
+           DEBUG_INFO ("memory error.\r\n");
+           GPG_MEMORY_FAILURE ();
+           return;
+         }
+
+       offset = 0;
+      }
+    else
+      {
+       GPG_NO_FILE ();
+       return;
+      }
+  else
+    {
+      if (file_selection != FILE_EF_CH_CERTIFICATE
+         && file_selection != FILE_EF_RANDOM)
+       {
+         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;
+    }
+
+  DEBUG_INFO ("UPDATE BINARY done.\r\n");
+}
+
+
 struct command
 {
   uint8_t command;
@@ -854,6 +927,7 @@ const struct command cmds[] = {
   { INS_SELECT_FILE, cmd_select_file },
   { INS_READ_BINARY, cmd_read_binary },
   { INS_GET_DATA, cmd_get_data },
+  { INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */
   { INS_PUT_DATA, cmd_put_data },
   { INS_PUT_DATA_ODD, cmd_put_data },
 };
@@ -869,6 +943,8 @@ process_command_apdu (void)
     if (cmds[i].command == cmd)
       break;
 
+  res_APDU_pointer = NULL; /* default */
+
   if (i < NUM_CMDS)
     cmds[i].cmd_handler ();
   else
index fd4175d..035f026 100644 (file)
@@ -1,6 +1,7 @@
 #define GPG_MEMORY_FAILURE()           write_res_apdu (NULL, 0, 0x65, 0x81)
 #define GPG_SECURITY_FAILURE()         write_res_apdu (NULL, 0, 0x69, 0x82)
 #define GPG_SECURITY_AUTH_BLOCKED()    write_res_apdu (NULL, 0, 0x69, 0x83)
+#define GPG_COMMAND_NOT_ALLOWED()      write_res_apdu (NULL, 0, 0x69, 0x86)
 #define GPG_NO_FILE()                  write_res_apdu (NULL, 0, 0x6a, 0x82)
 #define GPG_NO_RECORD()                        write_res_apdu (NULL, 0, 0x6a, 0x88)
 #define GPG_BAD_P0_P1()                        write_res_apdu (NULL, 0, 0x6b, 0x00)
index 11b03d7..189e2ec 100644 (file)
@@ -385,6 +385,7 @@ icc_power_off (void)
 }
 
 int res_APDU_size;
+uint8_t *res_APDU_pointer;
 
 static void
 icc_send_data_block (int len, uint8_t status, uint8_t chain)
@@ -608,8 +609,16 @@ icc_handle_data (void)
        {
          if (icc_header->param == 0x10)
            {
-             memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE,
-                      res_APDU_size);
+             if (res_APDU_pointer != NULL)
+               {
+                 memcpy (res_APDU, res_APDU_pointer,
+                         ICC_RESPONSE_MSG_DATA_SIZE);
+                 res_APDU_pointer += ICC_RESPONSE_MSG_DATA_SIZE;
+               }
+             else
+               memmove (res_APDU, res_APDU+ICC_RESPONSE_MSG_DATA_SIZE,
+                        res_APDU_size);
+
              if (res_APDU_size <= ICC_RESPONSE_MSG_DATA_SIZE)
                {
                  icc_send_data_block (res_APDU_size, 0, 0x02);
@@ -689,6 +698,9 @@ USBthread (void *arg)
        {
          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);
@@ -698,6 +710,7 @@ USBthread (void *arg)
                {
                  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;
                }
            }