docker: source checking container
[gnuk/gnuk.git] / src / openpgp.c
index 9543643..5b788b0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * openpgp.c -- OpenPGP card protocol support
  *
- * Copyright (C) 2010, 2011, 2012, 2013
+ * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
  *               Free Software Initiative of Japan
  * Author: NIIBE Yutaka <gniibe@fsij.org>
  *
@@ -31,7 +31,7 @@
 
 #include "gnuk.h"
 #include "sys.h"
-#include "openpgp.h"
+#include "status-code.h"
 #include "sha256.h"
 #include "random.h"
 
@@ -48,6 +48,7 @@ static struct eventflag *openpgp_comm;
 #define INS_CHANGE_REFERENCE_DATA              0x24
 #define INS_PSO                                        0x2a
 #define INS_RESET_RETRY_COUNTER                        0x2c
+#define INS_ACTIVATE_FILE                      0x44
 #define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR   0x47
 #define INS_EXTERNAL_AUTHENTICATE              0x82
 #define INS_GET_CHALLENGE                      0x84
@@ -59,6 +60,7 @@ static struct eventflag *openpgp_comm;
 #define INS_UPDATE_BINARY                      0xd6
 #define INS_PUT_DATA                           0xda
 #define INS_PUT_DATA_ODD                       0xdb    /* For key import */
+#define INS_TERMINATE_DF                       0xe6
 
 static const uint8_t *challenge; /* Random bytes */
 
@@ -96,17 +98,26 @@ set_res_sw (uint8_t sw1, uint8_t sw2)
 #define FILE_EF_UPDATE_KEY_2   7
 #define FILE_EF_UPDATE_KEY_3   8
 #define FILE_EF_CH_CERTIFICATE 9
+#define FILE_CARD_TERMINATED_OPENPGP   254
+#define FILE_CARD_TERMINATED   255
 
-static uint8_t file_selection;
+uint8_t file_selection;
 
 static void
 gpg_init (void)
 {
-  const uint8_t *flash_data_start;
+  const uint8_t *flash_do_start;
+  const uint8_t *flash_do_end;
 
-  file_selection = FILE_NONE;
-  flash_data_start = flash_init ();
-  gpg_data_scan (flash_data_start);
+  flash_init (&flash_do_start, &flash_do_end);
+
+  if (flash_do_start == NULL)
+    file_selection = FILE_CARD_TERMINATED;
+  else
+    file_selection = FILE_NONE;
+
+  gpg_data_scan (flash_do_start, flash_do_end);
+  flash_init_keys ();
 }
 
 static void
@@ -137,6 +148,7 @@ static void
 cmd_verify (void)
 {
   int len;
+  uint8_t p1 = P1 (apdu);
   uint8_t p2 = P2 (apdu);
   int r;
   const uint8_t *pw;
@@ -148,22 +160,36 @@ cmd_verify (void)
   pw = apdu.cmd_apdu_data;
 
   if (len == 0)
-    {                          /* This is to examine status.  */
-      if (p2 == 0x81)
-       r = ac_check_status (AC_PSO_CDS_AUTHORIZED);
-      else if (p2 == 0x82)
-       r = ac_check_status (AC_OTHER_AUTHORIZED);
-      else
-       r = ac_check_status (AC_ADMIN_AUTHORIZED);
+    {
+      if (p1 == 0)
+       {                       /* This is to examine status.  */
+         if (p2 == 0x81)
+           r = ac_check_status (AC_PSO_CDS_AUTHORIZED);
+         else if (p2 == 0x82)
+           r = ac_check_status (AC_OTHER_AUTHORIZED);
+         else
+           r = ac_check_status (AC_ADMIN_AUTHORIZED);
 
-      if (r)
-       GPG_SUCCESS (); /* If authentication done already, return success.  */
-      else
-       {                /* If not, return retry counter, encoded.  */
-         r = gpg_pw_get_retry_counter (p2);
-         set_res_sw (0x63, 0xc0 | (r&0x0f));
+         if (r)
+           GPG_SUCCESS ();     /* If authentication done already, return success.  */
+         else
+           {            /* If not, return retry counter, encoded.  */
+             r = gpg_pw_get_retry_counter (p2);
+             set_res_sw (0x63, 0xc0 | (r&0x0f));
+           }
        }
-
+      else if (p1 == 0xff)
+       {                       /* Reset the status.  */
+         if (p2 == 0x81)
+           ac_reset_pso_cds ();
+         else if (p2 == 0x82)
+           ac_reset_other ();
+         else
+           ac_reset_admin ();
+         GPG_SUCCESS ();
+       }
+      else
+       GPG_BAD_P1_P2 ();
       return;
     }
 
@@ -436,9 +462,12 @@ s2k (const unsigned char *salt, size_t slen,
 {
   sha256_context ctx;
   size_t count = S2KCOUNT;
+  const uint8_t *unique = unique_device_id ();
 
   sha256_start (&ctx);
 
+  sha256_update (&ctx, unique, 12);
+
   while (count > slen + ilen)
     {
       if (slen)
@@ -597,9 +626,6 @@ cmd_put_data (void)
 
   DEBUG_INFO (" - PUT DATA\r\n");
 
-  if (file_selection != FILE_DF_OPENPGP)
-    GPG_NO_RECORD();
-
   tag = ((P1 (apdu)<<8) | P2 (apdu));
   len = apdu.cmd_apdu_data_len;
   data = apdu.cmd_apdu_data;
@@ -619,12 +645,7 @@ cmd_pgp_gakp (void)
     {
       if (!ac_check_status (AC_ADMIN_AUTHORIZED))
        GPG_SECURITY_FAILURE ();
-#ifdef KEYGEN_SUPPORT
-      /* Generate key pair */
       gpg_do_keygen (apdu.cmd_apdu_data[0]);
-#else
-      GPG_FUNCTION_NOT_SUPPORTED ();
-#endif
     }
 }
 
@@ -634,7 +655,7 @@ gpg_get_firmware_update_key (uint8_t keyno)
   extern uint8_t _updatekey_store;
   const uint8_t *p;
 
-  p = &_updatekey_store + keyno * KEY_CONTENT_LEN;
+  p = &_updatekey_store + keyno * FIRMWARE_UPDATE_KEY_CONTENT_LEN;
   return p;
 }
 
@@ -693,8 +714,8 @@ cmd_read_binary (void)
       else
        {
          p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0);
-         res_APDU_size = KEY_CONTENT_LEN;
-         memcpy (res_APDU, p, KEY_CONTENT_LEN);
+         res_APDU_size = FIRMWARE_UPDATE_KEY_CONTENT_LEN;
+         memcpy (res_APDU, p, FIRMWARE_UPDATE_KEY_CONTENT_LEN);
          GPG_SUCCESS ();
        }
     }
@@ -737,19 +758,17 @@ cmd_select_file (void)
          return;
        }
 
-      file_selection = FILE_DF_OPENPGP;
-      if ((P2 (apdu) & 0x0c) == 0x0c)  /* No FCI */
-       GPG_SUCCESS ();
-      else
+      if (file_selection == FILE_CARD_TERMINATED)
        {
-         gpg_do_get_data (0x004f, 1); /* AID */
-         memmove (res_APDU+2, res_APDU, res_APDU_size);
-         res_APDU[0] = 0x6f;
-         res_APDU[1] = 0x12;
-         res_APDU[2] = 0x84;   /* overwrite: DF name */
-         res_APDU_size += 2;
-         GPG_SUCCESS ();
+         file_selection = FILE_CARD_TERMINATED_OPENPGP;
+         GPG_APPLICATION_TERMINATED();
+         return;
        }
+
+      file_selection = FILE_DF_OPENPGP;
+
+      /* Behave just like original OpenPGP card.  */
+      GPG_SUCCESS ();
     }
   else if (apdu.cmd_apdu_data_len == 2
           && apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02)
@@ -799,24 +818,38 @@ cmd_get_data (void)
 
   DEBUG_INFO (" - Get Data\r\n");
 
-  if (file_selection != FILE_DF_OPENPGP)
-    GPG_NO_RECORD ();
-
   gpg_do_get_data (tag, 0);
 }
 
+#define ECDSA_HASH_LEN 32
+#define ECDSA_SIGNATURE_LENGTH 64
+
+#define EDDSA_HASH_LEN_MAX 256
+#define EDDSA_SIGNATURE_LENGTH 64
+
+#define ECC_CIPHER_DO_HEADER_SIZE 7
+
 static void
 cmd_pso (void)
 {
   int len = apdu.cmd_apdu_data_len;
-  int r;
+  int r = -1;
+  int attr;
+  int pubkey_len;
+  unsigned int result_len = 0;
+  int cs;
 
   DEBUG_INFO (" - PSO: ");
   DEBUG_WORD ((uint32_t)&r);
   DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
+  DEBUG_SHORT (len);
 
   if (P1 (apdu) == 0x9e && P2 (apdu) == 0x9a)
     {
+      attr = gpg_get_algo_attr (GPG_KEY_FOR_SIGNING);
+      pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_SIGNING,
+                                              GPG_KEY_PUBLIC);
+
       if (!ac_check_status (AC_PSO_CDS_AUTHORIZED))
        {
          DEBUG_INFO ("security error.");
@@ -824,39 +857,89 @@ cmd_pso (void)
          return;
        }
 
-      /* Check size of digestInfo */
-      if (len != 34            /* MD5 */
-         && len != 35          /* SHA1 / RIPEMD-160 */
-         && len != 47          /* SHA224 */
-         && len != 51          /* SHA256 */
-         && len != 67          /* SHA384 */
-         && len != 83)         /* SHA512 */
+      if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
        {
-         DEBUG_INFO (" wrong length: ");
-         DEBUG_SHORT (len);
-         GPG_ERROR ();
+         /* Check size of digestInfo */
+         if (len != 34         /* MD5 */
+             && len != 35              /* SHA1 / RIPEMD-160 */
+             && len != 47              /* SHA224 */
+             && len != 51              /* SHA256 */
+             && len != 67              /* SHA384 */
+             && len != 83)             /* SHA512 */
+           {
+             DEBUG_INFO (" wrong length");
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
+           }
+
+         DEBUG_BINARY (kd[GPG_KEY_FOR_SIGNING].data, pubkey_len);
+
+         result_len = pubkey_len;
+         r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
+                       &kd[GPG_KEY_FOR_SIGNING], pubkey_len);
        }
-      else
+      else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1)
        {
-         DEBUG_SHORT (len);
-         DEBUG_BINARY (&kd[GPG_KEY_FOR_SIGNING], KEY_CONTENT_LEN);
+         /* ECDSA with p256r1/p256k1 for signature */
+         if (len != ECDSA_HASH_LEN)
+           {
+             DEBUG_INFO (" wrong length");
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
+           }
 
-         r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
-                       &kd[GPG_KEY_FOR_SIGNING]);
-         if (r < 0)
+         cs = chopstx_setcancelstate (0);
+         result_len = ECDSA_SIGNATURE_LENGTH;
+         if (attr == ALGO_NISTP256R1)
+           r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU,
+                                  kd[GPG_KEY_FOR_SIGNING].data);
+         else                  /* ALGO_SECP256K1 */
+           r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU,
+                                  kd[GPG_KEY_FOR_SIGNING].data);
+         chopstx_setcancelstate (cs);
+       }
+      else if (attr == ALGO_ED25519)
+       {
+         uint32_t output[64/4];        /* Require 4-byte alignment. */
+
+         if (len > EDDSA_HASH_LEN_MAX)
            {
-             ac_reset_pso_cds ();
-             GPG_ERROR ();
+             DEBUG_INFO ("wrong hash length.");
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
            }
-         else
-           /* Success */
-           gpg_increment_digital_signature_counter ();
+
+         cs = chopstx_setcancelstate (0);
+         result_len = EDDSA_SIGNATURE_LENGTH;
+         r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output,
+                               kd[GPG_KEY_FOR_SIGNING].data,
+                               kd[GPG_KEY_FOR_SIGNING].data+32,
+                               kd[GPG_KEY_FOR_SIGNING].pubkey);
+         chopstx_setcancelstate (cs);
+         memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH);
+       }
+      else
+       {
+         DEBUG_INFO ("unknown algo.");
+         GPG_FUNCTION_NOT_SUPPORTED ();
+         return;
+       }
+
+      if (r == 0)
+       {
+         res_APDU_size = result_len;
+         gpg_increment_digital_signature_counter ();
        }
+      else   /* Failure */
+       ac_reset_pso_cds ();
     }
   else if (P1 (apdu) == 0x80 && P2 (apdu) == 0x86)
     {
-      DEBUG_SHORT (len);
-      DEBUG_BINARY (&kd[GPG_KEY_FOR_DECRYPTION], KEY_CONTENT_LEN);
+      attr = gpg_get_algo_attr (GPG_KEY_FOR_DECRYPTION);
+      pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_DECRYPTION,
+                                              GPG_KEY_PUBLIC);
+
+      DEBUG_BINARY (kd[GPG_KEY_FOR_DECRYPTION].data, pubkey_len);
 
       if (!ac_check_status (AC_OTHER_AUTHORIZED))
        {
@@ -865,19 +948,68 @@ cmd_pso (void)
          return;
        }
 
-      /* Skip padding 0x00 */
-      len--;
-      if (len != KEY_CONTENT_LEN)
-       GPG_CONDITION_NOT_SATISFIED ();
-      else
+      if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
        {
+         /* Skip padding 0x00 */
+         len--;
+         if (len != pubkey_len)
+           {
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
+           }
          r = rsa_decrypt (apdu.cmd_apdu_data+1, res_APDU, len,
-                          &kd[GPG_KEY_FOR_DECRYPTION]);
-         if (r < 0)
-           GPG_ERROR ();
+                          &kd[GPG_KEY_FOR_DECRYPTION], &result_len);
        }
+      else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1)
+       {
+         int header = ECC_CIPHER_DO_HEADER_SIZE;
+
+         /* Format is in big endian MPI: 04 || x || y */
+         if (len != 65 + ECC_CIPHER_DO_HEADER_SIZE
+             || apdu.cmd_apdu_data[header] != 0x04)
+           {
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
+           }
+
+         cs = chopstx_setcancelstate (0);
+         result_len = 65;
+         if (attr == ALGO_NISTP256R1)
+           r = ecdh_decrypt_p256r1 (apdu.cmd_apdu_data + header, res_APDU,
+                                    kd[GPG_KEY_FOR_DECRYPTION].data);
+         else
+           r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header, res_APDU,
+                                    kd[GPG_KEY_FOR_DECRYPTION].data);
+         chopstx_setcancelstate (cs);
+       }
+      else if (attr == ALGO_CURVE25519)
+       {
+         int header = ECC_CIPHER_DO_HEADER_SIZE;
+
+         if (len != 32 + ECC_CIPHER_DO_HEADER_SIZE)
+           {
+             GPG_CONDITION_NOT_SATISFIED ();
+             return;
+           }
+
+         cs = chopstx_setcancelstate (0);
+         result_len = 32;
+         r = ecdh_decrypt_curve25519 (apdu.cmd_apdu_data + header, res_APDU,
+                                      kd[GPG_KEY_FOR_DECRYPTION].data);
+         chopstx_setcancelstate (cs);
+       }
+      else
+       {
+         DEBUG_INFO ("unknown algo.");
+         GPG_FUNCTION_NOT_SUPPORTED ();
+         return;
+       }
+
+      if (r == 0)
+       res_APDU_size = result_len;
     }
-  else
+
+  if (r < 0)
     {
       DEBUG_INFO (" - ??");
       DEBUG_BYTE (P1 (apdu));
@@ -890,98 +1022,110 @@ cmd_pso (void)
 }
 
 
-#ifdef RSA_AUTH
-#define MAX_DIGEST_INFO_LEN 102 /* 40% */
+#define MAX_RSA_DIGEST_INFO_LEN 102 /* 40% */
 static void
 cmd_internal_authenticate (void)
 {
+  int attr = gpg_get_algo_attr (GPG_KEY_FOR_AUTHENTICATION);
+  int pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_AUTHENTICATION,
+                                              GPG_KEY_PUBLIC);
   int len = apdu.cmd_apdu_data_len;
-  int r;
+  int r = -1;
+  unsigned int result_len = 0;
+  int cs;
 
   DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
 
-  if (P1 (apdu) == 0x00 && P2 (apdu) == 0x00)
+  if (P1 (apdu) != 0x00 || P2 (apdu) != 0x00)
     {
-      DEBUG_SHORT (len);
+      DEBUG_INFO (" - ??");
+      DEBUG_BYTE (P1 (apdu));
+      DEBUG_INFO (" - ??");
+      DEBUG_BYTE (P2 (apdu));
+      GPG_CONDITION_NOT_SATISFIED ();
+      return;
+    }
 
-      if (!ac_check_status (AC_OTHER_AUTHORIZED))
-       {
-         DEBUG_INFO ("security error.");
-         GPG_SECURITY_FAILURE ();
-         return;
-       }
+  DEBUG_SHORT (len);
+  if (!ac_check_status (AC_OTHER_AUTHORIZED))
+    {
+      DEBUG_INFO ("security error.");
+      GPG_SECURITY_FAILURE ();
+      return;
+    }
 
-      if (len > MAX_DIGEST_INFO_LEN)
+  if (attr == ALGO_RSA2K || attr == ALGO_RSA4K)
+    {
+      if (len > MAX_RSA_DIGEST_INFO_LEN)
        {
          DEBUG_INFO ("input is too long.");
          GPG_CONDITION_NOT_SATISFIED ();
          return;
        }
 
+      result_len = pubkey_len;
       r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len,
-                   &kd[GPG_KEY_FOR_AUTHENTICATION]);
-      if (r < 0)
-       GPG_ERROR ();
+                   &kd[GPG_KEY_FOR_AUTHENTICATION], pubkey_len);
     }
-  else
+  else if (attr == ALGO_NISTP256R1)
     {
-      DEBUG_INFO (" - ??");
-      DEBUG_BYTE (P1 (apdu));
-      DEBUG_INFO (" - ??");
-      DEBUG_BYTE (P2 (apdu));
-      GPG_ERROR ();
-    }
-
-  DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
-}
-#else
-#define ECDSA_P256_HASH_LEN 32
-#define ECDSA_SIGNATURE_LENGTH 64
-
-static void
-cmd_internal_authenticate (void)
-{
-  int len = apdu.cmd_apdu_data_len;
-  int r;
-
-  DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
+      if (len != ECDSA_HASH_LEN)
+       {
+         DEBUG_INFO ("wrong hash length.");
+         GPG_CONDITION_NOT_SATISFIED ();
+         return;
+       }
 
-  if (P1 (apdu) == 0x00 && P2 (apdu) == 0x00)
+      cs = chopstx_setcancelstate (0);
+      result_len = ECDSA_SIGNATURE_LENGTH;
+      r = ecdsa_sign_p256r1 (apdu.cmd_apdu_data, res_APDU,
+                            kd[GPG_KEY_FOR_AUTHENTICATION].data);
+      chopstx_setcancelstate (cs);
+    }
+  else if (attr == ALGO_SECP256K1)
     {
-      DEBUG_SHORT (len);
-
-      if (!ac_check_status (AC_OTHER_AUTHORIZED))
+      if (len != ECDSA_HASH_LEN)
        {
-         DEBUG_INFO ("security error.");
-         GPG_SECURITY_FAILURE ();
+         DEBUG_INFO ("wrong hash length.");
+         GPG_CONDITION_NOT_SATISFIED ();
          return;
        }
 
-      if (len != ECDSA_P256_HASH_LEN)
+      cs = chopstx_setcancelstate (0);
+      result_len = ECDSA_SIGNATURE_LENGTH;
+      r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU,
+                            kd[GPG_KEY_FOR_AUTHENTICATION].data);
+      chopstx_setcancelstate (cs);
+    }
+  else if (attr == ALGO_ED25519)
+    {
+      uint32_t output[64/4];   /* Require 4-byte alignment. */
+
+      if (len > EDDSA_HASH_LEN_MAX)
        {
          DEBUG_INFO ("wrong hash length.");
          GPG_CONDITION_NOT_SATISFIED ();
          return;
        }
 
-      res_APDU_size = ECDSA_SIGNATURE_LENGTH;
-      r = ecdsa_sign (apdu.cmd_apdu_data, res_APDU,
-                     &kd[GPG_KEY_FOR_AUTHENTICATION]);
-      if (r < 0)
-       GPG_ERROR ();
+      cs = chopstx_setcancelstate (0);
+      result_len = EDDSA_SIGNATURE_LENGTH;
+      r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output,
+                           kd[GPG_KEY_FOR_AUTHENTICATION].data,
+                           kd[GPG_KEY_FOR_AUTHENTICATION].data+32,
+                           kd[GPG_KEY_FOR_AUTHENTICATION].pubkey);
+      chopstx_setcancelstate (cs);
+      memcpy (res_APDU, output, EDDSA_SIGNATURE_LENGTH);
     }
+
+  if (r == 0)
+    res_APDU_size = result_len;
   else
-    {
-      DEBUG_INFO (" - ??");
-      DEBUG_BYTE (P1 (apdu));
-      DEBUG_INFO (" - ??");
-      DEBUG_BYTE (P2 (apdu));
-      GPG_ERROR ();
-    }
+    GPG_ERROR ();
 
   DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
 }
-#endif
+
 
 #define MBD_OPRATION_WRITE  0
 #define MBD_OPRATION_UPDATE 1
@@ -1132,7 +1276,8 @@ cmd_external_authenticate (void)
       return;
     }
 
-  r = rsa_verify (pubkey, challenge, signature);
+  r = rsa_verify (pubkey, FIRMWARE_UPDATE_KEY_CONTENT_LEN,
+                 challenge, signature);
   random_bytes_free (challenge);
   challenge = NULL;
 
@@ -1174,6 +1319,64 @@ cmd_get_challenge (void)
 }
 
 
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+static void
+cmd_activate_file (void)
+{
+  if (file_selection != FILE_CARD_TERMINATED_OPENPGP)
+    {
+      GPG_NO_RECORD();
+      return;
+    }
+
+  flash_activate ();
+  file_selection = FILE_DF_OPENPGP;
+  GPG_SUCCESS ();
+}
+
+static void
+cmd_terminate_df (void)
+{
+  uint8_t p1 = P1 (apdu);
+  uint8_t p2 = P2 (apdu);
+
+  if (file_selection != FILE_DF_OPENPGP)
+    {
+      GPG_NO_RECORD();
+      return;
+    }
+
+  if (p1 != 0 || p2 != 0)
+    {
+      GPG_BAD_P1_P2();
+      return;
+    }
+
+  if (apdu.cmd_apdu_data_len != 0)
+    {
+      GPG_WRONG_LENGTH();
+      return;
+    }
+
+
+  if (!ac_check_status (AC_ADMIN_AUTHORIZED) && !gpg_pw_locked (PW_ERR_PW3))
+    {
+      /* Only allow the case admin authorized, or, admin pass is locked.  */
+      GPG_SECURITY_FAILURE();
+      return;
+    }
+
+  ac_reset_admin ();
+  ac_reset_pso_cds ();
+  ac_reset_other ();
+  gpg_do_terminate ();
+  flash_terminate ();
+  file_selection = FILE_CARD_TERMINATED;
+  GPG_SUCCESS ();
+}
+#endif
+
+
 struct command
 {
   uint8_t command;
@@ -1185,13 +1388,16 @@ const struct command cmds[] = {
   { INS_CHANGE_REFERENCE_DATA, cmd_change_password },
   { INS_PSO, cmd_pso },
   { INS_RESET_RETRY_COUNTER, cmd_reset_user_password },
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+  { INS_ACTIVATE_FILE, cmd_activate_file },
+#endif
   { INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp },
   { INS_EXTERNAL_AUTHENTICATE,             /* Not in OpenPGP card protocol */
     cmd_external_authenticate },
   { INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */
   { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate },
   { INS_SELECT_FILE, cmd_select_file },
-  { INS_READ_BINARY, cmd_read_binary },
+  { INS_READ_BINARY, cmd_read_binary },     /* Not in OpenPGP card protocol */
   { INS_GET_DATA, cmd_get_data },
   { INS_WRITE_BINARY, cmd_write_binary},    /* Not in OpenPGP card protocol */
 #if defined(CERTDO_SUPPORT)
@@ -1199,6 +1405,9 @@ const struct command cmds[] = {
 #endif
   { INS_PUT_DATA, cmd_put_data },
   { INS_PUT_DATA_ODD, cmd_put_data },
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+  { INS_TERMINATE_DF, cmd_terminate_df},
+#endif
 };
 #define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
 
@@ -1213,7 +1422,24 @@ process_command_apdu (void)
       break;
 
   if (i < NUM_CMDS)
-    cmds[i].cmd_handler ();
+    {
+      if (file_selection == FILE_CARD_TERMINATED
+         && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
+         && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE)
+       GPG_APPLICATION_TERMINATED();
+      else if (file_selection != FILE_DF_OPENPGP
+              && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE
+              && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE
+              && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY
+              && cmd != INS_READ_BINARY)
+       GPG_NO_RECORD();
+      else
+       {
+         chopstx_setcancelstate (1);
+         cmds[i].cmd_handler ();
+         chopstx_setcancelstate (0);
+       }
+    }
   else
     {
       DEBUG_INFO (" - ??");
@@ -1222,23 +1448,10 @@ process_command_apdu (void)
     }
 }
 
-static void * card_thread (chopstx_t thd, struct eventflag *ccid_comm);
-
-void * __attribute__ ((naked))
+void *
 openpgp_card_thread (void *arg)
 {
-  chopstx_t thd;
-
-  asm ("mov    %0, sp" : "=r" (thd));
-  return card_thread (thd, (struct eventflag *)arg);
-}
-
-chopstx_t openpgp_card_thd;
-
-static void * __attribute__ ((noinline))
-card_thread (chopstx_t thd, struct eventflag *ccid_comm)
-{
-  openpgp_card_thd = thd;
+  struct eventflag *ccid_comm = (struct eventflag *)arg;
 
   openpgp_comm = ccid_comm + 1;
 
@@ -1246,10 +1459,10 @@ card_thread (chopstx_t thd, struct eventflag *ccid_comm)
 
   while (1)
     {
-      eventmask_t m = eventflag_wait (openpgp_comm);
 #if defined(PINPAD_SUPPORT)
       int len, pw_len, newpw_len;
 #endif
+      eventmask_t m = eventflag_wait (openpgp_comm);
 
       DEBUG_INFO ("GPG!: ");
 
@@ -1278,7 +1491,7 @@ card_thread (chopstx_t thd, struct eventflag *ccid_comm)
       else if (m == EV_MODIFY_CMD_AVAILABLE)
        {
 #if defined(PINPAD_SUPPORT)
-         uint8_t bConfirmPIN = apdu.cmd_apdu_data[5];
+         uint8_t bConfirmPIN = apdu.cmd_apdu_data[0];
          uint8_t *p = apdu.cmd_apdu_data;
 
          if (INS (apdu) != INS_CHANGE_REFERENCE_DATA