aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2004-01-28 16:21:57 +0000
committerWerner Koch <[email protected]>2004-01-28 16:21:57 +0000
commit97958029f67eee3f87ad6da48afc3ac73cbbe27f (patch)
treeaea4dcb820019a89833b2bae65378bb549658f07
parent(gpgsm_validate_chain): Changed the message printed (diff)
downloadgnupg-97958029f67eee3f87ad6da48afc3ac73cbbe27f.tar.gz
gnupg-97958029f67eee3f87ad6da48afc3ac73cbbe27f.zip
(iso7816_manage_security_env): New.
(iso7816_decipher): Add PADIND argument. ** app-nks.c is now functional **
-rw-r--r--scd/ChangeLog5
-rw-r--r--scd/app-nks.c199
-rw-r--r--scd/app-openpgp.c3
-rw-r--r--scd/iso7816.c57
-rw-r--r--scd/iso7816.h4
5 files changed, 246 insertions, 22 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 3a6a6aea4..66f48d467 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,8 @@
+2004-01-28 Werner Koch <[email protected]>
+
+ * iso7816.c (iso7816_manage_security_env): New.
+ (iso7816_decipher): Add PADIND argument.
+
2004-01-27 Werner Koch <[email protected]>
* command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 0a04f7511..2ceb537a2 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -33,16 +33,18 @@
#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;
}
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 75e3e299e..021d6a52c 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -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;
}
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 9a15ce953..1ee9b55e7 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -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. */
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 98e688693..937326b6d 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -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,