mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-11 21:48:50 +01:00
694 lines
21 KiB
C
694 lines
21 KiB
C
/* app-nks.c - The Telesec NKS card application.
|
||
* Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||
*
|
||
* This file is part of GnuPG.
|
||
*
|
||
* GnuPG is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* GnuPG is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <config.h>
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
#include <time.h>
|
||
|
||
#include "scdaemon.h"
|
||
#include "i18n.h"
|
||
#include "iso7816.h"
|
||
#include "app-common.h"
|
||
#include "tlv.h"
|
||
|
||
static struct
|
||
{
|
||
int fid; /* File ID. */
|
||
int nks_ver; /* 0 for NKS version 2, 3 for version 3. */
|
||
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, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
|
||
{ 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
|
||
{ 0x4331, 0, 100 },
|
||
{ 0x4332, 0, 100 },
|
||
{ 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
|
||
{ 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
|
||
{ 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
|
||
{ 0x43B1, 0, 100 },
|
||
{ 0x43B2, 0, 100 },
|
||
{ 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
|
||
{ 0xC500, 3, 101 }, /* EF_C.NKS.AUT */
|
||
{ 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
|
||
{ 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
|
||
{ 0 }
|
||
};
|
||
|
||
|
||
|
||
/* Object with application (i.e. NKS) specific data. */
|
||
struct app_local_s {
|
||
int nks_version; /* NKS version. */
|
||
|
||
};
|
||
|
||
|
||
|
||
|
||
/* Release local data. */
|
||
static void
|
||
do_deinit (app_t app)
|
||
{
|
||
if (app && app->app_local)
|
||
{
|
||
xfree (app->app_local);
|
||
app->app_local = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
/* Read the file with FID, assume it contains a public key and return
|
||
its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
|
||
static gpg_error_t
|
||
keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
|
||
{
|
||
gpg_error_t err;
|
||
unsigned char grip[20];
|
||
unsigned char *buffer[2];
|
||
size_t buflen[2];
|
||
gcry_sexp_t sexp;
|
||
int i;
|
||
|
||
err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
|
||
if (err)
|
||
return err;
|
||
err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]);
|
||
if (err)
|
||
return err;
|
||
err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]);
|
||
if (err)
|
||
{
|
||
xfree (buffer[0]);
|
||
return err;
|
||
}
|
||
|
||
if (app->app_local->nks_version < 3)
|
||
{
|
||
/* Old versions of NKS store the values in a TLV encoded format.
|
||
We need to do some checks. */
|
||
for (i=0; i < 2; i++)
|
||
{
|
||
/* Check that the value appears like an integer encoded as
|
||
Simple-TLV. We don't check the tag because the tests cards I
|
||
have use 1 for both, the modulus and the exponent - the
|
||
example in the documentation gives 2 for the exponent. */
|
||
if (buflen[i] < 3)
|
||
err = gpg_error (GPG_ERR_TOO_SHORT);
|
||
else if (buffer[i][1] != buflen[i]-2 )
|
||
err = gpg_error (GPG_ERR_INV_OBJ);
|
||
}
|
||
}
|
||
|
||
if (!err)
|
||
err = gcry_sexp_build (&sexp, NULL,
|
||
"(public-key (rsa (n %b) (e %b)))",
|
||
(int)buflen[0]-2, buffer[0]+2,
|
||
(int)buflen[1]-2, buffer[1]+2);
|
||
|
||
xfree (buffer[0]);
|
||
xfree (buffer[1]);
|
||
if (err)
|
||
return err;
|
||
|
||
if (!gcry_pk_get_keygrip (sexp, grip))
|
||
{
|
||
err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by
|
||
libgcrypt. */
|
||
}
|
||
else
|
||
{
|
||
bin2hex (grip, 20, r_gripstr);
|
||
}
|
||
gcry_sexp_release (sexp);
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
static gpg_error_t
|
||
do_learn_status (app_t app, ctrl_t ctrl)
|
||
{
|
||
gpg_error_t err;
|
||
char ct_buf[100], id_buf[100];
|
||
int i;
|
||
|
||
/* Output information about all useful objects. */
|
||
for (i=0; filelist[i].fid; i++)
|
||
{
|
||
if (filelist[i].nks_ver > app->app_local->nks_version)
|
||
continue;
|
||
|
||
if (filelist[i].certtype)
|
||
{
|
||
size_t len;
|
||
|
||
len = app_help_read_length_of_cert (app->slot,
|
||
filelist[i].fid, NULL);
|
||
if (len)
|
||
{
|
||
/* FIXME: We should store the length in the application's
|
||
context so that a following readcert does only need to
|
||
read that many bytes. */
|
||
sprintf (ct_buf, "%d", filelist[i].certtype);
|
||
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
|
||
send_status_info (ctrl, "CERTINFO",
|
||
ct_buf, strlen (ct_buf),
|
||
id_buf, strlen (id_buf),
|
||
NULL, (size_t)0);
|
||
}
|
||
}
|
||
else if (filelist[i].iskeypair)
|
||
{
|
||
char gripstr[40+1];
|
||
|
||
err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
|
||
if (err)
|
||
log_error ("can't get keygrip from FID 0x%04X: %s\n",
|
||
filelist[i].fid, gpg_strerror (err));
|
||
else
|
||
{
|
||
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
|
||
send_status_info (ctrl, "KEYPAIRINFO",
|
||
gripstr, 40,
|
||
id_buf, strlen (id_buf),
|
||
NULL, (size_t)0);
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
|
||
/* Read the certificate with id CERTID (as returned by learn_status in
|
||
the CERTINFO status lines) and return it in the freshly allocated
|
||
buffer put into CERT and the length of the certificate put into
|
||
CERTLEN. */
|
||
static gpg_error_t
|
||
do_readcert (app_t app, const char *certid,
|
||
unsigned char **cert, size_t *certlen)
|
||
{
|
||
int i, fid;
|
||
gpg_error_t err;
|
||
unsigned char *buffer;
|
||
const unsigned char *p;
|
||
size_t buflen, n;
|
||
int class, tag, constructed, ndef;
|
||
size_t totobjlen, objlen, hdrlen;
|
||
int rootca = 0;
|
||
|
||
*cert = NULL;
|
||
*certlen = 0;
|
||
if (strncmp (certid, "NKS-DF01.", 9) )
|
||
return gpg_error (GPG_ERR_INV_ID);
|
||
certid += 9;
|
||
if (!hexdigitp (certid) || !hexdigitp (certid+1)
|
||
|| !hexdigitp (certid+2) || !hexdigitp (certid+3)
|
||
|| certid[4])
|
||
return gpg_error (GPG_ERR_INV_ID);
|
||
fid = xtoi_4 (certid);
|
||
for (i=0; filelist[i].fid; i++)
|
||
if ((filelist[i].certtype || filelist[i].iskeypair)
|
||
&& filelist[i].fid == fid)
|
||
break;
|
||
if (!filelist[i].fid)
|
||
return gpg_error (GPG_ERR_NOT_FOUND);
|
||
|
||
/* If the requested objects is a plain public key, redirect it to
|
||
the corresponding certificate. The whole system is a bit messy
|
||
because we sometime use the key directly or let the caller
|
||
retrieve the key from the certificate. The rationale for
|
||
that is to support not-yet stored certificates. */
|
||
if (filelist[i].iskeypair)
|
||
fid = filelist[i].iskeypair;
|
||
|
||
|
||
/* Read the entire file. fixme: This could be optimized by first
|
||
reading the header to figure out how long the certificate
|
||
actually is. */
|
||
err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
|
||
if (err)
|
||
{
|
||
log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
|
||
return err;
|
||
}
|
||
|
||
err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
|
||
if (err)
|
||
{
|
||
log_error ("error reading certificate from FID 0x%04X: %s\n",
|
||
fid, gpg_strerror (err));
|
||
return err;
|
||
}
|
||
|
||
if (!buflen || *buffer == 0xff)
|
||
{
|
||
log_info ("no certificate contained in FID 0x%04X\n", fid);
|
||
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||
goto leave;
|
||
}
|
||
|
||
/* Now figure something out about the object. */
|
||
p = buffer;
|
||
n = buflen;
|
||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||
&ndef, &objlen, &hdrlen);
|
||
if (err)
|
||
goto leave;
|
||
if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
|
||
;
|
||
else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
|
||
rootca = 1;
|
||
else
|
||
return gpg_error (GPG_ERR_INV_OBJ);
|
||
totobjlen = objlen + hdrlen;
|
||
assert (totobjlen <= buflen);
|
||
|
||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||
&ndef, &objlen, &hdrlen);
|
||
if (err)
|
||
goto leave;
|
||
|
||
if (rootca)
|
||
;
|
||
else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
|
||
{
|
||
const unsigned char *save_p;
|
||
|
||
/* The certificate seems to be contained in a userCertificate
|
||
container. Skip this and assume the following sequence is
|
||
the certificate. */
|
||
if (n < objlen)
|
||
{
|
||
err = gpg_error (GPG_ERR_INV_OBJ);
|
||
goto leave;
|
||
}
|
||
p += objlen;
|
||
n -= objlen;
|
||
save_p = p;
|
||
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
|
||
&ndef, &objlen, &hdrlen);
|
||
if (err)
|
||
goto leave;
|
||
if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
|
||
return gpg_error (GPG_ERR_INV_OBJ);
|
||
totobjlen = objlen + hdrlen;
|
||
assert (save_p + totobjlen <= buffer + buflen);
|
||
memmove (buffer, save_p, totobjlen);
|
||
}
|
||
|
||
*cert = buffer;
|
||
buffer = NULL;
|
||
*certlen = totobjlen;
|
||
|
||
leave:
|
||
xfree (buffer);
|
||
return err;
|
||
}
|
||
|
||
|
||
/* Verify the PIN if required. */
|
||
static gpg_error_t
|
||
verify_pin (app_t app,
|
||
gpg_error_t (*pincb)(void*, const char *, char **),
|
||
void *pincb_arg)
|
||
{
|
||
iso7816_pininfo_t pininfo;
|
||
int rc;
|
||
|
||
/* Note that force_chv1 is never set but we do it here anyway so
|
||
that other applications may reuse 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 )
|
||
return 0; /* No need to verify it again. */
|
||
|
||
memset (&pininfo, 0, sizeof pininfo);
|
||
pininfo.mode = 1;
|
||
pininfo.minlen = 6;
|
||
pininfo.maxlen = 16;
|
||
|
||
if (!opt.disable_keypad
|
||
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
|
||
{
|
||
rc = pincb (pincb_arg,
|
||
_("||Please enter your PIN at the reader's keypad"),
|
||
NULL);
|
||
if (rc)
|
||
{
|
||
log_info (_("PIN callback returned error: %s\n"),
|
||
gpg_strerror (rc));
|
||
return rc;
|
||
}
|
||
|
||
/* Although it is possible to use a local PIN, we use the global
|
||
PIN for this application. */
|
||
rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo);
|
||
/* Dismiss the prompt. */
|
||
pincb (pincb_arg, NULL, NULL);
|
||
}
|
||
else
|
||
{
|
||
char *pinvalue;
|
||
|
||
rc = pincb (pincb_arg, "PIN", &pinvalue);
|
||
if (rc)
|
||
{
|
||
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
|
||
return rc;
|
||
}
|
||
|
||
/* The following limits are due to TCOS but also defined in the
|
||
NKS specs. */
|
||
if (strlen (pinvalue) < pininfo.minlen)
|
||
{
|
||
log_error ("PIN is too short; minimum length is %d\n",
|
||
pininfo.minlen);
|
||
xfree (pinvalue);
|
||
return gpg_error (GPG_ERR_BAD_PIN);
|
||
}
|
||
else if (strlen (pinvalue) > pininfo.maxlen)
|
||
{
|
||
log_error ("PIN is too large; maximum length is %d\n",
|
||
pininfo.maxlen);
|
||
xfree (pinvalue);
|
||
return gpg_error (GPG_ERR_BAD_PIN);
|
||
}
|
||
|
||
/* Although it is possible to use a local PIN, we use the global
|
||
PIN for this application. */
|
||
rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
|
||
xfree (pinvalue);
|
||
}
|
||
|
||
if (rc)
|
||
{
|
||
if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS )
|
||
log_error (_("the NullPIN has not yet been changed\n"));
|
||
else
|
||
log_error ("verify PIN failed\n");
|
||
return rc;
|
||
}
|
||
app->did_chv1 = 1;
|
||
|
||
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 gpg_error_t
|
||
do_sign (app_t app, const char *keyidstr, int hashalgo,
|
||
gpg_error_t (*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 valid. This is not really needed
|
||
but we do it 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 gpg_error_t
|
||
do_decipher (app_t app, const char *keyidstr,
|
||
gpg_error_t (*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 local secret key 1 for decryption. */
|
||
};
|
||
int rc, i;
|
||
int fid;
|
||
|
||
if (!keyidstr || !*keyidstr || !indatalen)
|
||
return gpg_error (GPG_ERR_INV_VALUE);
|
||
|
||
/* Check that the provided ID is valid. 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;
|
||
}
|
||
|
||
|
||
/* Handle the PASSWD command. CHVNOSTR is currently ignored; we
|
||
always use VHV0. RESET_MODE is not yet implemented. */
|
||
static gpg_error_t
|
||
do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
|
||
unsigned int flags,
|
||
gpg_error_t (*pincb)(void*, const char *, char **),
|
||
void *pincb_arg)
|
||
{
|
||
gpg_error_t err;
|
||
char *pinvalue;
|
||
const char *oldpin;
|
||
size_t oldpinlen;
|
||
|
||
(void)ctrl;
|
||
(void)chvnostr;
|
||
|
||
if ((flags & APP_CHANGE_FLAG_RESET))
|
||
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||
|
||
if ((flags & APP_CHANGE_FLAG_NULLPIN))
|
||
{
|
||
/* With the nullpin flag, we do not verify the PIN - it would fail
|
||
if the Nullpin is still set. */
|
||
oldpin = "\0\0\0\0\0";
|
||
oldpinlen = 6;
|
||
}
|
||
else
|
||
{
|
||
err = verify_pin (app, pincb, pincb_arg);
|
||
if (err)
|
||
return err;
|
||
oldpin = NULL;
|
||
oldpinlen = 0;
|
||
}
|
||
|
||
/* TRANSLATORS: Do not translate the "|*|" prefixes but
|
||
keep it at the start of the string. We need this elsewhere
|
||
to get some infos on the string. */
|
||
err = pincb (pincb_arg, _("|N|New PIN"), &pinvalue);
|
||
if (err)
|
||
{
|
||
log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
|
||
return err;
|
||
}
|
||
|
||
err = iso7816_change_reference_data (app->slot, 0x00,
|
||
oldpin, oldpinlen,
|
||
pinvalue, strlen (pinvalue));
|
||
xfree (pinvalue);
|
||
return err;
|
||
}
|
||
|
||
|
||
/* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
|
||
static gpg_error_t
|
||
do_check_pin (app_t app, const char *keyidstr,
|
||
gpg_error_t (*pincb)(void*, const char *, char **),
|
||
void *pincb_arg)
|
||
{
|
||
(void)keyidstr;
|
||
return verify_pin (app, pincb, pincb_arg);
|
||
}
|
||
|
||
|
||
/* Return the version of the NKS application. */
|
||
static int
|
||
get_nks_version (int slot)
|
||
{
|
||
unsigned char *result = NULL;
|
||
size_t resultlen;
|
||
int type;
|
||
|
||
if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0,
|
||
&result, &resultlen))
|
||
return 2; /* NKS 2 does not support this command. */
|
||
|
||
/* Example value: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00
|
||
vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx
|
||
vendor (Philips) -+ | | | | | | |
|
||
chip type -----------+ | | | | | |
|
||
chip id ----------------+ | | | | |
|
||
card type (3 - tcos 3) -------------------+ | | | |
|
||
OS version of card type ---------------------+ | | |
|
||
OS release of card type ------------------------+ | |
|
||
OS vendor internal version ------------------------+ |
|
||
RFU -----------------------------------------------------------+
|
||
*/
|
||
if (resultlen < 16)
|
||
type = 0; /* Invalid data returned. */
|
||
else
|
||
type = result[8];
|
||
xfree (result);
|
||
|
||
return type;
|
||
}
|
||
|
||
|
||
/* Select the NKS application. */
|
||
gpg_error_t
|
||
app_select_nks (app_t app)
|
||
{
|
||
static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
|
||
int slot = app->slot;
|
||
int rc;
|
||
|
||
rc = iso7816_select_application (slot, aid, sizeof aid, 0);
|
||
if (!rc)
|
||
{
|
||
app->apptype = "NKS";
|
||
|
||
app->app_local = xtrycalloc (1, sizeof *app->app_local);
|
||
if (!app->app_local)
|
||
{
|
||
rc = gpg_error (gpg_err_code_from_errno (errno));
|
||
goto leave;
|
||
}
|
||
|
||
app->app_local->nks_version = get_nks_version (slot);
|
||
if (opt.verbose)
|
||
log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
|
||
|
||
app->fnc.deinit = do_deinit;
|
||
app->fnc.learn_status = do_learn_status;
|
||
app->fnc.readcert = do_readcert;
|
||
app->fnc.getattr = NULL;
|
||
app->fnc.setattr = NULL;
|
||
app->fnc.genkey = NULL;
|
||
app->fnc.sign = do_sign;
|
||
app->fnc.auth = NULL;
|
||
app->fnc.decipher = do_decipher;
|
||
app->fnc.change_pin = do_change_pin;
|
||
app->fnc.check_pin = do_check_pin;
|
||
}
|
||
|
||
leave:
|
||
if (rc)
|
||
do_deinit (app);
|
||
return rc;
|
||
}
|
||
|
||
|