mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-03 12:11:33 +01:00
(iso7816_manage_security_env): New.
(iso7816_decipher): Add PADIND argument. ** app-nks.c is now functional **
This commit is contained in:
parent
a0cb56fe7f
commit
97958029f6
@ -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>
|
2004-01-27 Werner Koch <wk@gnupg.org>
|
||||||
|
|
||||||
* command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
|
* command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
|
||||||
|
199
scd/app-nks.c
199
scd/app-nks.c
@ -33,16 +33,18 @@
|
|||||||
#include "tlv.h"
|
#include "tlv.h"
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
int fid; /* File ID. */
|
int fid; /* File ID. */
|
||||||
int certtype; /* Type of certificate or 0 if it is not a certificate. */
|
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 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[] = {
|
} filelist[] = {
|
||||||
{ 0x4531, 0, 0xC000 },
|
{ 0x4531, 0, 0xC000, 1, 0 },
|
||||||
{ 0xC000, 101 },
|
{ 0xC000, 101 },
|
||||||
{ 0x4331, 100 },
|
{ 0x4331, 100 },
|
||||||
{ 0x4332, 100 },
|
{ 0x4332, 100 },
|
||||||
{ 0xB000, 110 },
|
{ 0xB000, 110 },
|
||||||
{ 0x45B1, 0, 0xC200 },
|
{ 0x45B1, 0, 0xC200, 0, 1 },
|
||||||
{ 0xC200, 101 },
|
{ 0xC200, 101 },
|
||||||
{ 0x43B1, 100 },
|
{ 0x43B1, 100 },
|
||||||
{ 0x43B2, 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. */
|
/* Select the NKS 2.0 application on the card in SLOT. */
|
||||||
int
|
int
|
||||||
@ -375,9 +562,9 @@ app_select_nks (APP app)
|
|||||||
app->fnc.getattr = NULL;
|
app->fnc.getattr = NULL;
|
||||||
app->fnc.setattr = NULL;
|
app->fnc.setattr = NULL;
|
||||||
app->fnc.genkey = NULL;
|
app->fnc.genkey = NULL;
|
||||||
app->fnc.sign = NULL;
|
app->fnc.sign = do_sign;
|
||||||
app->fnc.auth = NULL;
|
app->fnc.auth = NULL;
|
||||||
app->fnc.decipher = NULL;
|
app->fnc.decipher = do_decipher;
|
||||||
app->fnc.change_pin = NULL;
|
app->fnc.change_pin = NULL;
|
||||||
app->fnc.check_pin = NULL;
|
app->fnc.check_pin = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1121,7 +1121,8 @@ do_decipher (APP app, const char *keyidstr,
|
|||||||
|
|
||||||
rc = verify_chv2 (app, pincb, pincb_arg);
|
rc = verify_chv2 (app, pincb, pincb_arg);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen);
|
rc = iso7816_decipher (app->slot, indata, indatalen, 0,
|
||||||
|
outdata, outdatalen);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* iso7816.c - ISO 7816 commands
|
/* 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.
|
* This file is part of GnuPG.
|
||||||
*
|
*
|
||||||
@ -47,6 +47,7 @@
|
|||||||
#define CMD_RESET_RETRY_COUNTER 0x2C
|
#define CMD_RESET_RETRY_COUNTER 0x2C
|
||||||
#define CMD_GET_DATA 0xCA
|
#define CMD_GET_DATA 0xCA
|
||||||
#define CMD_PUT_DATA 0xDA
|
#define CMD_PUT_DATA 0xDA
|
||||||
|
#define CMD_MSE 0x22
|
||||||
#define CMD_PSO 0x2A
|
#define CMD_PSO 0x2A
|
||||||
#define CMD_INTERNAL_AUTHENTICATE 0x88
|
#define CMD_INTERNAL_AUTHENTICATE 0x88
|
||||||
#define CMD_GENERATE_KEYPAIR 0x47
|
#define CMD_GENERATE_KEYPAIR 0x47
|
||||||
@ -270,6 +271,23 @@ iso7816_put_data (int slot, int tag,
|
|||||||
return map_sw (sw);
|
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
|
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
|
||||||
success 0 is returned and the data is availavle in a newly
|
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
|
/* Perform the security operation DECIPHER. PADIND is the padding
|
||||||
success 0 is returned and the plaintext is available in a newly
|
indicator to be used. It should be 0 if no padding is required, a
|
||||||
allocated buffer stored at RESULT with its length stored at
|
value of -1 suppresses the padding byte. On success 0 is returned
|
||||||
RESULTLEN. */
|
and the plaintext is available in a newly allocated buffer stored
|
||||||
|
at RESULT with its length stored at RESULTLEN. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
|
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;
|
int sw;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
@ -317,15 +336,23 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
|
|||||||
*result = NULL;
|
*result = NULL;
|
||||||
*resultlen = 0;
|
*resultlen = 0;
|
||||||
|
|
||||||
/* We need to prepend the padding indicator. */
|
if (padind >= 0)
|
||||||
buf = xtrymalloc (datalen + 1);
|
{
|
||||||
if (!buf)
|
/* We need to prepend the padding indicator. */
|
||||||
return out_of_core ();
|
buf = xtrymalloc (datalen + 1);
|
||||||
*buf = 0; /* Padding indicator. */
|
if (!buf)
|
||||||
memcpy (buf+1, data, datalen);
|
return out_of_core ();
|
||||||
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
|
*buf = padind; /* Padding indicator. */
|
||||||
result, resultlen);
|
memcpy (buf+1, data, datalen);
|
||||||
xfree (buf);
|
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)
|
if (sw != SW_SUCCESS)
|
||||||
{
|
{
|
||||||
/* Make sure that pending buffers are released. */
|
/* Make sure that pending buffers are released. */
|
||||||
|
@ -42,11 +42,15 @@ gpg_error_t iso7816_get_data (int slot, int tag,
|
|||||||
unsigned char **result, size_t *resultlen);
|
unsigned char **result, size_t *resultlen);
|
||||||
gpg_error_t iso7816_put_data (int slot, int tag,
|
gpg_error_t iso7816_put_data (int slot, int tag,
|
||||||
const unsigned char *data, size_t datalen);
|
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,
|
gpg_error_t iso7816_compute_ds (int slot,
|
||||||
const unsigned char *data, size_t datalen,
|
const unsigned char *data, size_t datalen,
|
||||||
unsigned char **result, size_t *resultlen);
|
unsigned char **result, size_t *resultlen);
|
||||||
gpg_error_t iso7816_decipher (int slot,
|
gpg_error_t iso7816_decipher (int slot,
|
||||||
const unsigned char *data, size_t datalen,
|
const unsigned char *data, size_t datalen,
|
||||||
|
int padind,
|
||||||
unsigned char **result, size_t *resultlen);
|
unsigned char **result, size_t *resultlen);
|
||||||
gpg_error_t iso7816_internal_authenticate (int slot,
|
gpg_error_t iso7816_internal_authenticate (int slot,
|
||||||
const unsigned char *data, size_t datalen,
|
const unsigned char *data, size_t datalen,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user