(iso7816_manage_security_env): New.
authorWerner Koch <wk@gnupg.org>
Wed, 28 Jan 2004 16:21:57 +0000 (16:21 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 28 Jan 2004 16:21:57 +0000 (16:21 +0000)
(iso7816_decipher): Add PADIND argument.

** app-nks.c is now functional **

scd/ChangeLog
scd/app-nks.c
scd/app-openpgp.c
scd/iso7816.c
scd/iso7816.h

index 3a6a6ae..66f48d4 100644 (file)
@@ -1,3 +1,8 @@
+2004-01-28  Werner Koch  <wk@gnupg.org>
+
+       * iso7816.c (iso7816_manage_security_env): New.
+       (iso7816_decipher): Add PADIND argument.
+
 2004-01-27  Werner Koch  <wk@gnupg.org>
 
        * command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
index 0a04f75..2ceb537 100644 (file)
 #include "tlv.h"
 
 static struct {
-  int fid;      /* File ID. */
-  int certtype; /* Type of certificate or 0 if it is not a certificate. */
+  int fid;       /* File ID. */
+  int certtype;  /* Type of certificate or 0 if it is not a certificate. */
   int iskeypair; /* If true has the FID of the correspoding certificate. */
+  int issignkey; /* True if file is a key usable for signing. */
+  int isenckey;  /* True if file is a key usable for decryption. */
 } filelist[] = {
-  { 0x4531, 0,  0xC000 }, 
+  { 0x4531, 0,  0xC000, 1, 0 }, 
   { 0xC000, 101 },
   { 0x4331, 100 },
   { 0x4332, 100 },
   { 0xB000, 110 },
-  { 0x45B1, 0,  0xC200 },
+  { 0x45B1, 0,  0xC200, 0, 1 },
   { 0xC200, 101 },
   { 0x43B1, 100 },
   { 0x43B2, 100 },
@@ -356,6 +358,191 @@ do_readcert (app_t app, const char *certid,
 }
 
 
+/* Verify the PIN if required.  */
+static int
+verify_pin (app_t app,
+            int (pincb)(void*, const char *, char **),
+            void *pincb_arg)
+{
+  /* Note that force_chv1 is never set but we do it here anyway so
+     that other applications may euse this function.  For example it
+     makes sense to set force_chv1 for German signature law cards.
+     NKS is very similar to the DINSIG draft standard. */
+  if (!app->did_chv1 || app->force_chv1 ) 
+    {
+      char *pinvalue;
+      int rc;
+
+      rc = pincb (pincb_arg, "PIN", &pinvalue); 
+      if (rc)
+        {
+          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+          return rc;
+        }
+
+      /* The follwoing limits are due to TCOS but also defined in the
+         NKS specs. */
+      if (strlen (pinvalue) < 6)
+        {
+          log_error ("PIN is too short; minimum length is 6\n");
+          xfree (pinvalue);
+          return gpg_error (GPG_ERR_BAD_PIN);
+        }
+      else if (strlen (pinvalue) > 16)
+        {
+          log_error ("PIN is too large; maximum length is 16\n");
+          xfree (pinvalue);
+          return gpg_error (GPG_ERR_BAD_PIN);
+        }
+
+      /* Also it is possible to use a local PIN, we use the gloabl
+         PIN for this application.  */
+      rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
+      if (rc)
+        {
+          log_error ("verify PIN failed\n");
+          xfree (pinvalue);
+          return rc;
+        }
+      app->did_chv1 = 1;
+      xfree (pinvalue);
+    }
+
+  return 0;
+}
+
+
+
+/* Create the signature and return the allocated result in OUTDATA.
+   If a PIN is required the PINCB will be used to ask for the PIN;
+   that callback should return the PIN in an allocated buffer and
+   store that in the 3rd argument.  */
+static int 
+do_sign (app_t app, const char *keyidstr, int hashalgo,
+         int (pincb)(void*, const char *, char **),
+           void *pincb_arg,
+           const void *indata, size_t indatalen,
+           unsigned char **outdata, size_t *outdatalen )
+{
+  static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+      0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+  static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+      0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+  int rc, i;
+  int fid;
+  unsigned char data[35];   /* Must be large enough for a SHA-1 digest
+                               + the largest OID _prefix above. */
+
+  if (!keyidstr || !*keyidstr)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (indatalen != 20 && indatalen != 16 && indatalen != 35)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Check that the provided ID is vaid.  This is not really needed
+     but we do it to to enforce correct usage by the caller. */
+  if (strncmp (keyidstr, "NKS-DF01.", 9) ) 
+    return gpg_error (GPG_ERR_INV_ID);
+  keyidstr += 9;
+  if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
+      || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
+      || keyidstr[4])
+    return gpg_error (GPG_ERR_INV_ID);
+  fid = xtoi_4 (keyidstr);
+  for (i=0; filelist[i].fid; i++)
+    if (filelist[i].iskeypair && filelist[i].fid == fid)
+      break;
+  if (!filelist[i].fid)
+    return gpg_error (GPG_ERR_NOT_FOUND);
+  if (!filelist[i].issignkey)
+    return gpg_error (GPG_ERR_INV_ID);
+
+  /* Prepare the DER object from INDATA. */
+  if (indatalen == 35)
+    {
+      /* Alright, the caller was so kind to send us an already
+         prepared DER object.  Check that it is waht we want and that
+         it matches the hash algorithm. */
+      if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
+        ;
+      else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
+        ;
+      else 
+        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+      memcpy (data, indata, indatalen);
+    }
+  else
+    {
+      if (hashalgo == GCRY_MD_SHA1)
+        memcpy (data, sha1_prefix, 15);
+      else if (hashalgo == GCRY_MD_RMD160)
+        memcpy (data, rmd160_prefix, 15);
+      else 
+        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+      memcpy (data+15, indata, indatalen);
+    }
+
+  rc = verify_pin (app, pincb, pincb_arg);
+  if (!rc)
+    rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+  return rc;
+}
+
+
+
+
+/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
+   If a PIN is required the PINCB will be used to ask for the PIN; it
+   should return the PIN in an allocated buffer and put it into PIN.  */
+static int 
+do_decipher (app_t app, const char *keyidstr,
+             int (pincb)(void*, const char *, char **),
+             void *pincb_arg,
+             const void *indata, size_t indatalen,
+             unsigned char **outdata, size_t *outdatalen )
+{
+  static const unsigned char mse_parm[] = {
+    0x80, 1, 0x10, /* Select algorithm RSA. */
+    0x84, 1, 0x81  /* Select locak secret key 1 for descryption. */
+  };
+  int rc, i;
+  int fid;
+
+  if (!keyidstr || !*keyidstr || !indatalen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  /* Check that the provided ID is vaid.  This is not really needed
+     but we do it to to enforce correct usage by the caller. */
+  if (strncmp (keyidstr, "NKS-DF01.", 9) ) 
+    return gpg_error (GPG_ERR_INV_ID);
+  keyidstr += 9;
+  if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
+      || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
+      || keyidstr[4])
+    return gpg_error (GPG_ERR_INV_ID);
+  fid = xtoi_4 (keyidstr);
+  for (i=0; filelist[i].fid; i++)
+    if (filelist[i].iskeypair && filelist[i].fid == fid)
+      break;
+  if (!filelist[i].fid)
+    return gpg_error (GPG_ERR_NOT_FOUND);
+  if (!filelist[i].isenckey)
+    return gpg_error (GPG_ERR_INV_ID);
+
+  /* Do the TCOS specific MSE. */
+  rc = iso7816_manage_security_env (app->slot, 
+                                    0xC1, 0xB8,
+                                    mse_parm, sizeof mse_parm);
+  if (!rc)
+    rc = verify_pin (app, pincb, pincb_arg);
+  if (!rc)
+    rc = iso7816_decipher (app->slot, indata, indatalen, 0x81,
+                           outdata, outdatalen);
+  return rc;
+}
+
+
 
 /* Select the NKS 2.0 application on the card in SLOT.  */
 int
@@ -375,9 +562,9 @@ app_select_nks (APP app)
       app->fnc.getattr = NULL;
       app->fnc.setattr = NULL;
       app->fnc.genkey = NULL;
-      app->fnc.sign = NULL;
+      app->fnc.sign = do_sign;
       app->fnc.auth = NULL;
-      app->fnc.decipher = NULL;
+      app->fnc.decipher = do_decipher;
       app->fnc.change_pin = NULL;
       app->fnc.check_pin = NULL;
    }
index 75e3e29..021d6a5 100644 (file)
@@ -1121,7 +1121,8 @@ do_decipher (APP app, const char *keyidstr,
 
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen);
+    rc = iso7816_decipher (app->slot, indata, indatalen, 0,
+                           outdata, outdatalen);
   return rc;
 }
 
index 9a15ce9..1ee9b55 100644 (file)
@@ -1,5 +1,5 @@
 /* iso7816.c - ISO 7816 commands
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -47,6 +47,7 @@
 #define CMD_RESET_RETRY_COUNTER   0x2C
 #define CMD_GET_DATA    0xCA
 #define CMD_PUT_DATA    0xDA
+#define CMD_MSE         0x22
 #define CMD_PSO         0x2A
 #define CMD_INTERNAL_AUTHENTICATE 0x88
 #define CMD_GENERATE_KEYPAIR      0x47
@@ -270,6 +271,23 @@ iso7816_put_data (int slot, int tag,
   return map_sw (sw);
 }
 
+/* Manage Security Environment.  This is a weird operation and there
+   is no easy abstraction for it.  Furthermore, some card seem to have
+   a different interpreation of 7816-8 and thus we resort to let the
+   caller decide what to do. */
+gpg_error_t
+iso7816_manage_security_env (int slot, int p1, int p2,
+                             const unsigned char *data, size_t datalen)
+{
+  int sw;
+
+  if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data);
+  return map_sw (sw);
+}
+
 
 /* Perform the security operation COMPUTE DIGITAL SIGANTURE.  On
    success 0 is returned and the data is availavle in a newly
@@ -301,13 +319,14 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
 }
 
 
-/* Perform the security operation DECIPHER.  On
-   success 0 is returned and the plaintext is available in a newly
-   allocated buffer stored at RESULT with its length stored at
-   RESULTLEN. */
+/* Perform the security operation DECIPHER.  PADIND is the padding
+   indicator to be used.  It should be 0 if no padding is required, a
+   value of -1 suppresses the padding byte.  On success 0 is returned
+   and the plaintext is available in a newly allocated buffer stored
+   at RESULT with its length stored at RESULTLEN. */
 gpg_error_t
 iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
-                  unsigned char **result, size_t *resultlen)
+                  int padind, unsigned char **result, size_t *resultlen)
 {
   int sw;
   unsigned char *buf;
@@ -317,15 +336,23 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
   *result = NULL;
   *resultlen = 0;
 
-  /* We need to prepend the padding indicator. */
-  buf = xtrymalloc (datalen + 1);
-  if (!buf)
-    return out_of_core ();
-  *buf = 0; /* Padding indicator. */
-  memcpy (buf+1, data, datalen);
-  sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
-                  result, resultlen);
-  xfree (buf);
+  if (padind >= 0)
+    {
+      /* We need to prepend the padding indicator. */
+      buf = xtrymalloc (datalen + 1);
+      if (!buf)
+        return out_of_core ();
+      *buf = padind; /* Padding indicator. */
+      memcpy (buf+1, data, datalen);
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
+                      result, resultlen);
+      xfree (buf);
+    }
+  else
+    {
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
+                      result, resultlen);
+    }
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
index 98e6886..937326b 100644 (file)
@@ -42,11 +42,15 @@ gpg_error_t iso7816_get_data (int slot, int tag,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_put_data (int slot, int tag,
                               const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
+                                         const unsigned char *data,
+                                         size_t datalen);
 gpg_error_t iso7816_compute_ds (int slot,
                                 const unsigned char *data, size_t datalen,
                                 unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_decipher (int slot,
                               const unsigned char *data, size_t datalen,
+                              int padind,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_internal_authenticate (int slot,
                                    const unsigned char *data, size_t datalen,