aboutsummaryrefslogtreecommitdiffstats
path: root/scd/app-nks.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/app-nks.c')
-rw-r--r--scd/app-nks.c515
1 files changed, 0 insertions, 515 deletions
diff --git a/scd/app-nks.c b/scd/app-nks.c
deleted file mode 100644
index e69b59879..000000000
--- a/scd/app-nks.c
+++ /dev/null
@@ -1,515 +0,0 @@
-/* app-nks.c - The Telesec NKS 2.0 card application.
- * Copyright (C) 2004 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 2 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#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 "iso7816.h"
-#include "app-common.h"
-#include "tlv.h"
-
-static struct {
- 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, 1, 0 },
- { 0xC000, 101 },
- { 0x4331, 100 },
- { 0x4332, 100 },
- { 0xB000, 110 },
- { 0x45B1, 0, 0xC200, 0, 1 },
- { 0xC200, 101 },
- { 0x43B1, 100 },
- { 0x43B2, 100 },
- { 0, 0 }
-};
-
-
-
-/* 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 (int slot, 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 (slot, fid, 0, NULL, NULL);
- if (err)
- return err;
- err = iso7816_read_record (slot, 1, 1, 0, &buffer[0], &buflen[0]);
- if (err)
- return err;
- err = iso7816_read_record (slot, 2, 1, 0, &buffer[1], &buflen[1]);
- if (err)
- {
- xfree (buffer[0]);
- return err;
- }
-
- 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
- {
- for (i=0; i < 20; i++)
- sprintf (r_gripstr+i*2, "%02X", grip[i]);
- }
- gcry_sexp_release (sexp);
- return err;
-}
-
-
-
-static int
-do_learn_status (APP app, CTRL 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].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->slot, 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 int
-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
- becuase we sometime use the key directly or let the caller
- retrieve the key from the certificate. The valid point behind
- 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 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
-app_select_nks (APP 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);
- if (!rc)
- {
- app->apptype = "NKS";
-
- 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 = NULL;
- app->fnc.check_pin = NULL;
- }
-
- return rc;
-}
-
-