summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog65
-rw-r--r--scd/Makefile.am24
-rw-r--r--scd/apdu.c2
-rw-r--r--scd/apdu.h5
-rw-r--r--scd/app-common.h70
-rw-r--r--scd/app-dinsig.c129
-rw-r--r--scd/app-nks.c388
-rw-r--r--scd/app-openpgp.c139
-rw-r--r--scd/app.c131
-rw-r--r--scd/card.c6
-rw-r--r--scd/ccid-driver.c9
-rw-r--r--scd/command.c127
-rw-r--r--scd/iso7816.c192
-rw-r--r--scd/iso7816.h8
-rw-r--r--scd/sc-copykeys.c2
-rw-r--r--scd/sc-investigate.c480
-rw-r--r--scd/scdaemon.c4
-rw-r--r--scd/scdaemon.h3
-rw-r--r--scd/tlv.c219
-rw-r--r--scd/tlv.h84
20 files changed, 1818 insertions, 269 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 0862d356b..3a6a6aea4 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,68 @@
+2004-01-27 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
+
+ * app-common.h (app_ctx_s): Added readcert field.
+ * app.c (app_readcert): New.
+ * tlv.c (parse_ber_header): Added; taken from libksba.
+
+2004-01-26 Werner Koch <wk@gnupg.org>
+
+ * card.c (map_sc_err): Use SCD as the error source.
+
+ * command.c (open_card): ADD arg NAME to allow requesting a
+ specific application. Changed all callers.
+ (cmd_serialno): Allow optional argument to select the desired
+ application.
+
+ * app-nks.c: New.
+
+ * scdaemon.h (opt): Add READER_PORT.
+ * scdaemon.c (main): Set it here.
+ * app.c (app_set_default_reader_port): Removed.
+ (select_application): Add NAME arg and figure out a
+ default serial number from the GDO. Add SLOT arg and remove all
+ reader management.
+ (release_application): New.
+ (app_write_learn_status): Output an APPTYPE status line.
+ * command.c (open_card): Adapt for select_application change.
+ * app-openpgp.c (app_select_openpgp): Removed SN and SNLEN args
+ and set it directly. Changed all callers.
+
+2004-01-25 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_select_application): P1 kludge for OpenPGP
+ card.
+ * app-openpgp.c (find_tlv): Factor out this function to ..
+ * tlv.c, tlv.h: .. new.
+
+ * scdaemon.h: Introduced app_t and ctrl_t as the new types for APP
+ and CTRL.
+
+2004-01-21 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (apdu_send_le): Treat SW_EOF_REACHED as a warning.
+
+2004-01-20 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_read_binary): New.
+ (iso7816_select_file): New.
+ (iso7816_list_directory): New.
+
+ * sc-investigate.c: Add option -i.
+ (select_app, read_line, interactive_shell): New.
+
+2004-01-16 Werner Koch <wk@gnupg.org>
+
+ * apdu.h: Add SW_FILE_NOT_FOUND.
+ * iso7816.c (map_sw): Map it to GPG_ERR_ENOENT.
+ * iso7816.c (iso7816_select_file): New.
+
+ * app-dinsig.c: New file w/o any real code yet.
+ * Makefile.am (scdaemon_SOURCES,sc_investigate_SOURCES): Add file.
+
+ * sc-investigate.c: Add option --disable-ccid.
+
2003-12-19 Werner Koch <wk@gnupg.org>
* apdu.c (apdu_send_le): Send a get_response with the indicated
diff --git a/scd/Makefile.am b/scd/Makefile.am
index a2ecd3a81..c8bf3d0de 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -26,6 +26,8 @@ bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
+card_apps = app-openpgp.c app-nks.c app-dinsig.c
+
scdaemon_SOURCES = \
scdaemon.c scdaemon.h \
command.c card.c \
@@ -34,8 +36,9 @@ scdaemon_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
- app.c app-common.h \
- app-openpgp.c
+ tlv.c tlv.h \
+ app.c app-common.h $(card_apps)
+
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
@@ -46,9 +49,9 @@ sc_investigate_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
- app.c app-common.h \
- app-openpgp.c \
- atr.c atr.h
+ tlv.c tlv.h \
+ atr.c atr.h \
+ app.c app-common.h $(card_apps)
sc_investigate_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
@@ -61,17 +64,12 @@ sc_copykeys_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
- app.c app-common.h \
- app-openpgp.c \
- atr.c atr.h
+ tlv.c tlv.h \
+ atr.c atr.h \
+ app.c app-common.h $(card_apps)
sc_copykeys_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
../common/libsimple-pwquery.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \
-lgpg-error @INTLLIBS@ -ldl
-
-
-
-
-
diff --git a/scd/apdu.c b/scd/apdu.c
index 02038b65c..e5295f566 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -1168,7 +1168,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen);
}
- if (sw == SW_SUCCESS)
+ if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{
if (retbuf)
{
diff --git a/scd/apdu.h b/scd/apdu.h
index 21e2b9840..fd7634f13 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -27,13 +27,16 @@
enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/
+ SW_EOF_REACHED = 0x6282,
SW_EEPROM_FAILURE = 0x6581,
SW_WRONG_LENGTH = 0x6700,
SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985,
- SW_NOT_SUPPORTED = 0x6a81,
SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
+ SW_NOT_SUPPORTED = 0x6a81,
+ SW_FILE_NOT_FOUND = 0x6a82,
+ SW_RECORD_NOT_FOUND = 0x6a83,
SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00,
diff --git a/scd/app-common.h b/scd/app-common.h
index de1e02cac..cda17700f 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -29,43 +29,46 @@ struct app_ctx_s {
int slot; /* Used reader. */
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */
+ const char *apptype;
unsigned int card_version;
int did_chv1;
int force_chv1; /* True if the card does not cache CHV1. */
int did_chv2;
int did_chv3;
struct {
- int (*learn_status) (APP app, CTRL ctrl);
- int (*getattr) (APP app, CTRL ctrl, const char *name);
- int (*setattr) (APP app, const char *name,
+ int (*learn_status) (app_t app, ctrl_t ctrl);
+ int (*readcert) (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen);
+ int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
+ int (*setattr) (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
- int (*sign) (APP app,
+ int (*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 );
- int (*auth) (APP app, const char *keyidstr,
+ int (*auth) (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);
- int (*decipher) (APP app, const char *keyidstr,
+ int (*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);
- int (*genkey) (APP app, CTRL ctrl,
+ int (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
- int (*change_pin) (APP app, CTRL ctrl,
+ int (*change_pin) (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
- int (*check_pin) (APP app, const char *keyidstr,
+ int (*check_pin) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg);
} fnc;
@@ -74,66 +77,77 @@ struct app_ctx_s {
};
#if GNUPG_MAJOR_VERSION == 1
-int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
-int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
+int app_select_openpgp (app_t app);
+int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
#else
/*-- app.c --*/
-void app_set_default_reader_port (const char *portstr);
-APP select_application (void);
-int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
-int app_write_learn_status (APP app, CTRL ctrl);
-int app_getattr (APP app, CTRL ctrl, const char *name);
-int app_setattr (APP app, const char *name,
+app_t select_application (ctrl_t ctrl, int slot, const char *name);
+void release_application (app_t app);
+int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
+int app_write_learn_status (app_t app, ctrl_t ctrl);
+int app_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen);
+int app_getattr (app_t app, ctrl_t ctrl, const char *name);
+int app_setattr (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
-int app_sign (APP app, const char *keyidstr, int hashalgo,
+int app_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 );
-int app_auth (APP app, const char *keyidstr,
+int app_auth (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);
-int app_decipher (APP app, const char *keyidstr,
+int app_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 );
-int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+int app_genkey (app_t app, ctrl_t ctrl,
+ const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
-int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
-int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
+int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer);
+int app_change_pin (app_t app, ctrl_t ctrl,
+ const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
-int app_check_pin (APP app, const char *keyidstr,
+int app_check_pin (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
/*-- app-openpgp.c --*/
-int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
+int app_select_openpgp (app_t app);
-int app_openpgp_cardinfo (APP app,
+int app_openpgp_cardinfo (app_t app,
char **serialno,
char **disp_name,
char **pubkey_url,
unsigned char **fpr1,
unsigned char **fpr2,
unsigned char **fpr3);
-int app_openpgp_storekey (APP app, int keyno,
+int app_openpgp_storekey (app_t app, int keyno,
unsigned char *template, size_t template_len,
time_t created_at,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
-int app_openpgp_readkey (APP app, int keyno,
+int app_openpgp_readkey (app_t app, int keyno,
unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen);
+/*-- app-nks.c --*/
+int app_select_nks (app_t app);
+
+/*-- app-dinsig.c --*/
+int app_select_dinsig (app_t app);
+
+
#endif
diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c
new file mode 100644
index 000000000..4b5b517eb
--- /dev/null
+++ b/scd/app-dinsig.c
@@ -0,0 +1,129 @@
+/* app-dinsig.c - The DINSIG (DIN V 66291-1) 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
+ */
+
+
+/* The German signature law and its bylaw (SigG and SigV) is currently
+ used with an interface specification described in DIN V 66291-1.
+ The AID to be used is: 'D27600006601'.
+
+ The file IDs for certificates utilize the generic format:
+ Cxyz
+ C being the hex digit 'C' (12).
+ x being the service indicator:
+ '0' := SigG conform digital signature.
+ '1' := entity authentication.
+ '2' := key encipherment.
+ '3' := data encipherment.
+ '4' := key agreement.
+ other values are reserved for future use.
+ y being the security environment number using '0' for cards
+ not supporting a SE number.
+ z being the certificate type:
+ '0' := C.CH (base certificate of card holder) or C.ICC.
+ '1' .. '7' := C.CH (business or professional certificate
+ of card holder.
+ '8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA).
+ 'E' := C.RCA (self certified certificate of the Root-CA).
+ 'F' := reserved.
+
+ The file IDs used by default are:
+ '1F00' EF.SSD (security service descriptor). [o,o]
+ '2F02' EF.GDO (global data objects) [m,m]
+ 'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte.
+ Read and update after user authentication. [o,o]
+ 'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size
+ of keys. [m (unless a 'C00E' is present),m]
+ 'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size
+ of keys. [o,o]
+ 'C00n' EF.C.CH.DS (digital signature certificate of card holder)
+ with n := 0 .. 7. Size is 2k or size of cert. Read and
+ update allowed after user authentication. [m,m]
+ 'C00m' EF.C.CA.DS (digital signature certificate of CA)
+ with m := 8 .. E. Size is 1k or size of cert. Read always
+ allowed, update after user authentication. [o,o]
+ 'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m]
+ 'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m]
+ 'D000' EF.DM (display message) [-,m]
+
+ The letters in brackets indicate optional or mandatory files: The
+ first for card terminals under full control and the second for
+ "business" card terminals.
+
+ FIXME: Needs a lot more explanation.
+
+*/
+
+
+
+
+#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"
+
+
+
+static int
+do_learn_status (APP app, CTRL ctrl)
+{
+ return 0;
+}
+
+
+
+
+
+/* Select the DINSIG application on the card in SLOT. This function
+ must be used before any other DINSIG application functions. */
+int
+app_select_dinsig (APP app)
+{
+ static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
+ int slot = app->slot;
+ int rc;
+
+ rc = iso7816_select_application (slot, aid, sizeof aid);
+ if (!rc)
+ {
+ app->apptype = "DINSIG";
+
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.getattr = NULL;
+ app->fnc.setattr = NULL;
+ app->fnc.genkey = NULL;
+ app->fnc.sign = NULL;
+ app->fnc.auth = NULL;
+ app->fnc.decipher = NULL;
+ app->fnc.change_pin = NULL;
+ app->fnc.check_pin = NULL;
+ }
+
+ return rc;
+}
+
+
diff --git a/scd/app-nks.c b/scd/app-nks.c
new file mode 100644
index 000000000..0a04f7511
--- /dev/null
+++ b/scd/app-nks.c
@@ -0,0 +1,388 @@
+/* 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. */
+} filelist[] = {
+ { 0x4531, 0, 0xC000 },
+ { 0xC000, 101 },
+ { 0x4331, 100 },
+ { 0x4332, 100 },
+ { 0xB000, 110 },
+ { 0x45B1, 0, 0xC200 },
+ { 0xC200, 101 },
+ { 0x43B1, 100 },
+ { 0x43B2, 100 },
+ { 0, 0 }
+};
+
+
+
+/* Given the slot and the File Id FID, return the length of the
+ certificate contained in that file. Returns 0 if the file does not
+ exists or does not contain a certificate. */
+static size_t
+get_length_of_cert (int slot, int fid)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ const unsigned char *p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen;
+
+ err = iso7816_select_file (slot, fid, 0, NULL, NULL);
+ if (err)
+ {
+ log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
+ return 0;
+ }
+
+ err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen);
+ if (err)
+ {
+ log_info ("error reading certificate from FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return 0;
+ }
+
+ if (!buflen || *buffer == 0xff)
+ {
+ log_info ("no certificate contained in FID 0x%04X\n", fid);
+ xfree (buffer);
+ return 0;
+ }
+
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ {
+ log_info ("error parsing certificate in FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ xfree (buffer);
+ return 0;
+ }
+
+ /* All certificates should commence with a SEQUENCE expect fro the
+ special ROOT CA which are enclosed in a SET. */
+ if ( !(class == CLASS_UNIVERSAL && constructed
+ && (tag == TAG_SEQUENCE || tag == TAG_SET)))
+ {
+ log_info ("contents of FID 0x%04X does not look like a certificate\n",
+ fid);
+ return 0;
+ }
+
+ return objlen + hdrlen;
+}
+
+
+
+/* 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, &buffer[0], &buflen[0]);
+ if (err)
+ return err;
+ err = iso7816_read_record (slot, 2, 1, &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 = get_length_of_cert (app->slot, filelist[i].fid);
+
+ 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;
+}
+
+
+
+/* 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 = NULL;
+ app->fnc.auth = NULL;
+ app->fnc.decipher = NULL;
+ app->fnc.change_pin = NULL;
+ app->fnc.check_pin = NULL;
+ }
+
+ return rc;
+}
+
+
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 8f9a303fe..75e3e299e 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1,5 +1,5 @@
/* app-openpgp.c - The OpenPGP card application.
- * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -42,7 +42,7 @@
#include "iso7816.h"
#include "app-common.h"
-
+#include "tlv.h"
static struct {
@@ -80,94 +80,6 @@ static unsigned long convert_sig_counter_value (const unsigned char *value,
static unsigned long get_sig_counter (APP app);
-/* Locate a TLV encoded data object in BUFFER of LENGTH and
- return a pointer to value as well as its length in NBYTES. Return
- NULL if it was not found. Note, that the function does not check
- whether the value fits into the provided buffer.
-
- FIXME: Move this to an extra file, it is mostly duplicated from card.c.
-*/
-static const unsigned char *
-find_tlv (const unsigned char *buffer, size_t length,
- int tag, size_t *nbytes, int nestlevel)
-{
- const unsigned char *s = buffer;
- size_t n = length;
- size_t len;
- int this_tag;
- int composite;
-
- for (;;)
- {
- buffer = s;
- if (n < 2)
- return NULL; /* buffer definitely too short for tag and length. */
- if (!*s || *s == 0xff)
- { /* Skip optional filler between TLV objects. */
- s++;
- n--;
- continue;
- }
- composite = !!(*s & 0x20);
- if ((*s & 0x1f) == 0x1f)
- { /* more tag bytes to follow */
- s++;
- n--;
- if (n < 2)
- return NULL; /* buffer definitely too short for tag and length. */
- if ((*s & 0x1f) == 0x1f)
- return NULL; /* We support only up to 2 bytes. */
- this_tag = (s[-1] << 8) | (s[0] & 0x7f);
- }
- else
- this_tag = s[0];
- len = s[1];
- s += 2; n -= 2;
- if (len < 0x80)
- ;
- else if (len == 0x81)
- { /* One byte length follows. */
- if (!n)
- return NULL; /* we expected 1 more bytes with the length. */
- len = s[0];
- s++; n--;
- }
- else if (len == 0x82)
- { /* Two byte length follows. */
- if (n < 2)
- return NULL; /* we expected 2 more bytes with the length. */
- len = (s[0] << 8) | s[1];
- s += 2; n -= 2;
- }
- else
- return NULL; /* APDU limit is 65535, thus it does not make
- sense to assume longer length fields. */
-
- if (composite && nestlevel < 100)
- { /* Dive into this composite DO after checking for too deep
- nesting. */
- const unsigned char *tmp_s;
- size_t tmp_len;
-
- tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
- if (tmp_s)
- {
- *nbytes = tmp_len;
- return tmp_s;
- }
- }
-
- if (this_tag == tag)
- {
- *nbytes = len;
- return s;
- }
- if (len > n)
- return NULL; /* buffer too short to skip to the next tag. */
- s += len; n -= len;
- }
-}
-
/* Get the DO identified by TAG from the card in SLOT and return a
buffer with its content in RESULT and NBYTES. The return value is
@@ -197,7 +109,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
{
const unsigned char *s;
- s = find_tlv (buffer, buflen, tag, &valuelen, 0);
+ s = find_tlv (buffer, buflen, tag, &valuelen);
if (!s)
value = NULL; /* not found */
else if (valuelen > buflen - (s - buffer))
@@ -271,7 +183,7 @@ dump_all_do (int slot)
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv (buffer, buflen,
- data_objects[j].tag, &valuelen, 0);
+ data_objects[j].tag, &valuelen);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
@@ -443,7 +355,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
{
/* The serial number is very special. We could have used the
AID DO to retrieve it, but we have it already in the app
- context and the stanmp argument is required anyway which we
+ context and the stamp argument is required anyway which we
can't by other means. The AID DO is available anyway but not
hex formatted. */
char *serial;
@@ -772,7 +684,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
- fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
rc = gpg_error (GPG_ERR_GENERAL);
@@ -820,7 +732,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
}
log_info ("key generation completed (%d seconds)\n",
(int)(time (NULL) - start_at));
- keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
rc = gpg_error (GPG_ERR_CARD);
@@ -828,7 +740,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave;
}
- m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
+ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m)
{
rc = gpg_error (GPG_ERR_CARD);
@@ -838,7 +750,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
/* log_printhex ("RSA n:", m, mlen); */
send_key_data (ctrl, "n", m, mlen);
- e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
+ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e)
{
rc = gpg_error (GPG_ERR_CARD);
@@ -913,7 +825,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
- fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
xfree (buffer);
@@ -1268,7 +1180,7 @@ do_check_pin (APP app, const char *keyidstr,
/* Select the OpenPGP application on the card in SLOT. This function
must be used before any other OpenPGP application functions. */
int
-app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
+app_select_openpgp (APP app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int slot = app->slot;
@@ -1280,10 +1192,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
+ app->apptype = "OPENPGP";
+
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
+ /* The OpenPGP card returns the serial number as part of the
+ AID; because we prefer to use OpenPGP serial numbers, we
+ repalce a possibly already set one from a EF.GDO with this
+ one. Note, that for current OpenPGP cards, no EF.GDO exists
+ and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
@@ -1293,15 +1212,12 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
log_printhex ("", buffer, buflen);
}
- if (sn)
- {
- *sn = buffer;
- *snlen = buflen;
- app->card_version = buffer[6] << 8;
- app->card_version |= buffer[7];
- }
- else
- xfree (buffer);
+ app->card_version = buffer[6] << 8;
+ app->card_version |= buffer[7];
+ xfree (app->serialno);
+ app->serialno = buffer;
+ app->serialnolen = buflen;
+ buffer = NULL;
relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen);
if (!relptr)
@@ -1316,6 +1232,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
dump_all_do (slot);
app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = NULL;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
app->fnc.genkey = do_genkey;
@@ -1498,7 +1415,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
- keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
log_error ("response does not contain the public key data\n");
@@ -1506,7 +1423,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
- a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
+ a = find_tlv (keydata, keydatalen, 0x0081, &alen);
if (!a)
{
log_error ("response does not contain the RSA modulus\n");
@@ -1517,7 +1434,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
*m = xmalloc (alen);
memcpy (*m, a, alen);
- a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
+ a = find_tlv (keydata, keydatalen, 0x0082, &alen);
if (!e)
{
log_error ("response does not contain the RSA public exponent\n");
diff --git a/scd/app.c b/scd/app.c
index 1f142ea32..6ac18272b 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -29,49 +29,78 @@
#include "app-common.h"
#include "apdu.h"
#include "iso7816.h"
-#include "dynload.h"
+#include "tlv.h"
-static char *default_reader_port;
-void
-app_set_default_reader_port (const char *portstr)
-{
- xfree (default_reader_port);
- default_reader_port = portstr? xstrdup (portstr): NULL;
-}
-
-
-/* The select the best fitting application and return a context.
- Returns NULL if no application was found or no card is present. */
+/* If called with NAME as NULL, select the best fitting application
+ and return a context; otherwise select the application with NAME
+ and return a context. SLOT identifies the reader device. Returns
+ NULL if no application was found or no card is present. */
APP
-select_application (void)
+select_application (ctrl_t ctrl, int slot, const char *name)
{
- int slot;
int rc;
APP app;
-
- slot = apdu_open_reader (default_reader_port);
- if (slot == -1)
- {
- log_error ("card reader not available\n");
- return NULL;
- }
+ unsigned char *result = NULL;
+ size_t resultlen;
app = xtrycalloc (1, sizeof *app);
if (!app)
{
rc = out_of_core ();
log_info ("error allocating context: %s\n", gpg_strerror (rc));
- /*apdu_close_reader (slot);*/
return NULL;
}
-
app->slot = slot;
- rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+
+ /* Fixme: We should now first check whether a card is at all
+ present. */
+
+ /* Try to read the GDO file first to get a default serial number. */
+ rc = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
+ if (!rc)
+ rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
+ if (!rc)
+ rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!rc)
+ {
+ size_t n;
+ const unsigned char *p;
+
+ p = find_tlv (result, resultlen, 0x5A, &n);
+ if (p && n && n >= (resultlen - (p - result)))
+ {
+ /* The GDO file is pretty short, thus we simply reuse it for
+ storing the serial number. */
+ memmove (result, p, n);
+ app->serialno = result;
+ app->serialnolen = n;
+ }
+ else
+ xfree (result);
+ result = NULL;
+ }
+
+
+ rc = gpg_error (GPG_ERR_NOT_FOUND);
+
+ if (!name || !strcmp (name, "openpgp"))
+ rc = app_select_openpgp (app);
+ if (rc && (!name || !strcmp (name, "nks")))
+ rc = app_select_nks (app);
+ if (rc && (!name || !strcmp (name, "dinsig")))
+ rc = app_select_dinsig (app);
+ if (rc && name)
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
if (rc)
{
-/* apdu_close_reader (slot); */
- log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ if (name)
+ log_info ("can't select application `%s': %s\n",
+ name, gpg_strerror (rc));
+ else
+ log_info ("no supported card application found: %s\n",
+ gpg_strerror (rc));
xfree (app);
return NULL;
}
@@ -81,23 +110,36 @@ select_application (void)
}
+void
+release_application (app_t app)
+{
+ if (!app)
+ return;
+
+ xfree (app->serialno);
+ xfree (app);
+}
+
+
/* Retrieve the serial number and the time of the last update of the
card. The serial number is returned as a malloced string (hex
encoded) in SERIAL and the time of update is returned in STAMP. If
no update time is available the returned value is 0. Caller must
- free SERIAL unless the function returns an error. */
+ free SERIAL unless the function returns an error. If STAMP is not
+ of interest, NULL may be passed. */
int
-app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
+app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
{
unsigned char *buf, *p;
int i;
- if (!app || !serial || !stamp)
+ if (!app || !serial)
return gpg_error (GPG_ERR_INV_VALUE);
*serial = NULL;
- *stamp = 0; /* not available */
+ if (stamp)
+ *stamp = 0; /* not available */
buf = xtrymalloc (app->serialnolen * 2 + 1);
if (!buf)
@@ -121,10 +163,34 @@ app_write_learn_status (APP app, CTRL ctrl)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.learn_status)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ if (app->apptype)
+ send_status_info (ctrl, "APPTYPE",
+ app->apptype, strlen (app->apptype), NULL, 0);
+
return app->fnc.learn_status (app, ctrl);
}
+/* 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. */
+int
+app_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen)
+{
+ if (!app)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.readcert)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ return app->fnc.readcert (app, certid, cert, certlen);
+}
+
+
/* Perform a GETATTR operation. */
int
app_getattr (APP app, CTRL ctrl, const char *name)
@@ -317,8 +383,3 @@ app_check_pin (APP app, const char *keyidstr,
return rc;
}
-
-
-
-
-
diff --git a/scd/card.c b/scd/card.c
index 95149074d..53c89f3a4 100644
--- a/scd/card.c
+++ b/scd/card.c
@@ -53,7 +53,10 @@ map_sc_err (int rc)
#endif
default: e = GPG_ERR_CARD; break;
}
- return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e);
+ /* It does not make much sense to further distingusih the error
+ source between OpenSC and SCD. Thus we use SCD as source
+ here. */
+ return gpg_err_make (GPG_ERR_SOURCE_SCD, e);
}
/* Get the keygrip from CERT, return 0 on success */
@@ -462,6 +465,7 @@ card_enum_keypairs (CARD card, int idx,
100 := Regular X.509 cert
101 := Trusted X.509 cert
102 := Useful X.509 cert
+ 110 := Root CA cert (DINSIG)
*/
int
card_enum_certs (CARD card, int idx, char **certid, int *certtype)
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 936672cc6..f910722eb 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -98,6 +98,11 @@
# include "scdaemon.h"
# endif
+/* Disable all debgging output for now. */
+#undef DBG_CARD_IO
+#define DBG_CARD_IO 0
+
+
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t); } while (0)
# define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \
@@ -944,7 +949,9 @@ ccid_transceive (ccid_driver_t handle,
{
if (n > maxresplen)
{
- DEBUGOUT ("provided buffer too short for received data\n");
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
return -1;
}
diff --git a/scd/command.c b/scd/command.c
index bc3132a0b..9e571f228 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -1,5 +1,5 @@
/* command.c - SCdaemon command handler
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -31,6 +31,7 @@
#include "scdaemon.h"
#include <ksba.h>
#include "app-common.h"
+#include "apdu.h" /* Required for apdu_*_reader (). */
/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100
@@ -90,17 +91,34 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
function returns an Assuan error, so don't map the error a second
time */
static AssuanError
-open_card (CTRL ctrl)
+open_card (CTRL ctrl, const char *apptype)
{
+ int slot;
+
if (ctrl->app_ctx)
return 0; /* Already initialized for one specific application. */
if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */
- ctrl->app_ctx = select_application ();
+ slot = apdu_open_reader (opt.reader_port);
+ if (slot != -1)
+ {
+ ctrl->app_ctx = select_application (ctrl, slot, apptype);
+ if (!ctrl->app_ctx)
+ apdu_close_reader (slot);
+ }
if (!ctrl->app_ctx)
{ /* No application found - fall back to old mode. */
- int rc = card_open (&ctrl->card_ctx);
+ /* Note that we should rework the old code to use the
+ application paradigma too. */
+ int rc;
+
+ /* If an APPTYPE was requested and it is not pkcs#15, we return
+ an error here. */
+ if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15")))
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ rc = card_open (&ctrl->card_ctx);
if (rc)
return map_to_assuan_status (rc);
}
@@ -143,11 +161,17 @@ percent_plus_unescape (unsigned char *string)
-/* SERIALNO
+/* SERIALNO [APPTYPE]
Return the serial number of the card using a status reponse. This
functon should be used to check for the presence of a card.
+ If APPTYPE is given, an application of that type is selected and an
+ error is returned if the application is not supported or available.
+ The default is to auto-select the application using a hardwired
+ preference system. Note, that a future extension to this function
+ may allow to specify a list and order of applications to try.
+
This function is special in that it can be used to reset the card.
Most other functions will return an error when a card change has
been detected and the use of this function is therefore required.
@@ -165,7 +189,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
char *serial;
time_t stamp;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, *line? line:NULL)))
return rc;
if (ctrl->app_ctx)
@@ -223,6 +247,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
100 := Regular X.509 cert
101 := Trusted X.509 cert
102 := Useful X.509 cert
+ 110 := Root CA cert (DINSIG)
For certain cards, more information will be returned:
@@ -240,7 +265,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
S DISP-NAME <name_of_card_holder>
The name of the card holder as stored on the card; percent
- aescaping takes place, spaces are encoded as '+'
+ escaping takes place, spaces are encoded as '+'
S PUBKEY-URL <url>
@@ -254,7 +279,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
int rc = 0;
int idx;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
/* Unless the force option is used we try a shortcut by identifying
@@ -305,10 +330,15 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
free (serial_and_stamp);
}
- /* Return information about the certificates. */
- if (ctrl->app_ctx)
- rc = -1; /* This information is not yet available for applications. */
- for (idx=0; !rc; idx++)
+ /* If we are using the modern application paradigma, let the
+ application print out its collection of useful status
+ information. */
+ if (!rc && ctrl->app_ctx)
+ rc = app_write_learn_status (ctrl->app_ctx, ctrl);
+
+ /* Return information about the certificates. FIXME: Move this into
+ an app-p15.c*/
+ for (idx=0; !rc && !ctrl->app_ctx; idx++)
{
char *certid;
int certtype;
@@ -333,11 +363,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1)
rc = 0;
-
- /* Return information about the keys. */
- if (ctrl->app_ctx)
- rc = -1; /* This information is not yet available for applications. */
- for (idx=0; !rc; idx++)
+ /* Return information about the keys. FIXME: Move this into an
+ app-p15.c */
+ for (idx=0; !rc && !ctrl->app_ctx; idx++)
{
unsigned char keygrip[20];
char *keyid;
@@ -346,7 +374,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid)
{
- /* this does happen with an incomplete personalized
+ /* This does happen with an incomplete personalized
card; i.e. during the time we have stored the key on the
card but not stored the certificate; probably becuase it
has not yet been received back from the CA. Note that we
@@ -383,10 +411,6 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1)
rc = 0;
- if (!rc && ctrl->app_ctx)
- rc = app_write_learn_status (ctrl->app_ctx, ctrl);
-
-
return map_to_assuan_status (rc);
}
@@ -403,17 +427,24 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
unsigned char *cert;
size_t ncert;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
+ line = xstrdup (line); /* Need a copy of the line. */
if (ctrl->app_ctx)
- return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-
- rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
- if (rc)
{
- log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
+ rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
+ if (rc)
+ log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
}
+ xfree (line);
+ line = NULL;
if (!rc)
{
rc = assuan_send_data (ctx, cert, ncert);
@@ -440,18 +471,26 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
ksba_cert_t kc = NULL;
ksba_sexp_t p;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
+ line = xstrdup (line); /* Need a copy of the line. */
if (ctrl->app_ctx)
- return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-
- rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
- if (rc)
{
- log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
- goto leave;
+ rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
+ if (rc)
+ log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
+ if (rc)
+ log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
}
+ xfree (line);
+ line = NULL;
+ if (rc)
+ goto leave;
rc = ksba_cert_new (&kc);
if (rc)
@@ -569,7 +608,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
/* We have to use a copy of the key ID because the function may use
@@ -619,7 +658,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@@ -665,7 +704,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
keyidstr = xtrystrdup (line);
@@ -718,7 +757,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line)
int rc;
char *keyword;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
keyword = line;
@@ -757,7 +796,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line)
size_t nbytes;
char *line, *linebuf;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
/* We need to use a copy of LINE, because PIN_CB uses the same
@@ -823,7 +862,7 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
line++;
*line = 0;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@@ -854,7 +893,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line)
return set_error (Parameter_Error, "number of requested bytes missing");
nbytes = strtoul (line, NULL, 0);
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@@ -904,7 +943,7 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
line++;
*line = 0;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@@ -931,7 +970,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line)
int rc;
char *keyidstr;
- if ((rc = open_card (ctrl)))
+ if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
diff --git a/scd/iso7816.c b/scd/iso7816.c
index f4aa18c6f..9a15ce953 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -51,6 +51,8 @@
#define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84
+#define CMD_READ_BINARY 0xB0
+#define CMD_READ_RECORD 0xB2
static gpg_error_t
map_sw (int sw)
@@ -66,6 +68,8 @@ map_sw (int sw)
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
+ case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
+ case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
@@ -91,18 +95,79 @@ map_sw (int sw)
apdu_open_reader (), AID is a buffer of size AIDLEN holding the
requested application ID. The function can't be used to enumerate
AIDs and won't return the AID on success. The return value is 0
- for okay or GNUPG error code. Note that ISO error codes are
+ for okay or a GPG error code. Note that ISO error codes are
internally mapped. */
gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen)
{
+ static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int sw;
+ int p1 = 0x0C; /* No FCI to be returned. */
+
+ if (aidlen == sizeof openpgp_aid
+ && !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
+ p1 = 0; /* The current openpgp cards don't allow 0x0c. */
- sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
+ sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
return map_sw (sw);
}
+gpg_error_t
+iso7816_select_file (int slot, int tag, int is_dir,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw, p0, p1;
+ unsigned char tagbuf[2];
+
+ tagbuf[0] = (tag >> 8) & 0xff;
+ tagbuf[1] = tag & 0xff;
+
+ if (result || resultlen)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+ else
+ {
+ p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
+ p1 = 0x0c; /* No FC return. */
+ sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+ p0, p1, 2, tagbuf );
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* This is a private command currently only working for TCOS cards. */
+gpg_error_t
+iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ return map_sw (sw);
+}
+
+
+
/* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
gpg_error_t
@@ -381,3 +446,126 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
return 0;
}
+
+/* Perform a READ BINARY command requesting a maximum of NMAX bytes
+ from OFFSET. With NMAX = 0 the entire file is read. The result is
+ stored in a newly allocated buffer at the address passed by RESULT.
+ Returns the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (offset > 32767 || nmax > 254)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ do
+ {
+ buffer = NULL;
+ bufferlen = 0;
+ /* Fixme: Either the ccid driver of the TCOS cards have problems
+ with an Le of 0. */
+ sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+ nmax? nmax : 254, &buffer, &bufferlen);
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ if (*result) /* Need to extend the buffer. */
+ {
+ unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
+ if (!p)
+ {
+ gpg_error_t err = gpg_error_from_errno (errno);
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return err;
+ }
+ *result = p;
+ memcpy (*result + *resultlen, buffer, bufferlen);
+ *resultlen += bufferlen;
+ xfree (buffer);
+ buffer = NULL;
+ }
+ else /* Transfer the buffer into our result. */
+ {
+ *result = buffer;
+ *resultlen = bufferlen;
+ }
+ offset += bufferlen;
+ if (offset > 32767)
+ break; /* We simply truncate the result for too large
+ files. */
+ }
+ while (!nmax && sw != SW_EOF_REACHED);
+
+ return 0;
+}
+
+/* Perform a READ RECORD command. RECNO gives the record number to
+ read with 0 indicating the current record. RECCOUNT must be 1 (not
+ all cards support reading of more than one record). The result is
+ stored in a newly allocated buffer at the address passed by RESULT.
+ Returns the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_read_record (int slot, int recno, int reccount,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (recno < 0 || recno > 255 || reccount != 1)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ buffer = NULL;
+ bufferlen = 0;
+ /* Fixme: Either the ccid driver of the TCOS cards have problems
+ with an Le of 0. */
+ sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
+ recno,
+ 0x04,
+ -1, NULL,
+ 254, &buffer, &bufferlen);
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ *result = buffer;
+ *resultlen = bufferlen;
+
+ return 0;
+}
+
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 26b8d6aba..98e688693 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -27,6 +27,10 @@
gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen);
+gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen);
gpg_error_t iso7816_change_reference_data (int slot, int chvno,
@@ -56,5 +60,9 @@ gpg_error_t iso7816_read_public_key (int slot,
gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer);
+gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
+ unsigned char **result, size_t *resultlen);
#endif /*ISO7816_H*/
diff --git a/scd/sc-copykeys.c b/scd/sc-copykeys.c
index b56b88590..78cb2acc8 100644
--- a/scd/sc-copykeys.c
+++ b/scd/sc-copykeys.c
@@ -165,7 +165,7 @@ main (int argc, char **argv )
/* FIXME: Use select_application. */
appbuf.slot = slot;
- rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen);
+ rc = app_select_openpgp (&appbuf);
if (rc)
{
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
index ecd385690..acef86ead 100644
--- a/scd/sc-investigate.c
+++ b/scd/sc-investigate.c
@@ -24,6 +24,13 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#ifdef HAVE_READLINE_READLINE_H
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@@ -32,17 +39,25 @@
#include "apdu.h" /* for open_reader */
#include "atr.h"
#include "app-common.h"
+#include "iso7816.h"
#define _(a) (a)
+#define CONTROL_D ('D' - 'A' + 1)
+
enum cmd_and_opt_values
-{ oVerbose = 'v',
+{
+ oInteractive = 'i',
+ oVerbose = 'v',
oReaderPort = 500,
octapiDriver,
oDebug,
oDebugAll,
+ oDisableCCID,
+
+
oGenRandom,
aTest };
@@ -52,15 +67,27 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
+ { oInteractive, "interactive", 0, "start in interactive explorer mode"},
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
{ octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"},
+ { oDisableCCID, "disable-ccid", 0,
+#ifdef HAVE_LIBUSB
+ "do not use the internal CCID driver"
+#else
+ "@"
+#endif
+ },
{ oDebug, "debug" ,4|16, "set debugging flags"},
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
{ oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
{0}
};
+
+static void interactive_shell (int slot);
+
+
static const char *
my_strusage (int level)
{
@@ -111,10 +138,8 @@ main (int argc, char **argv )
ARGPARSE_ARGS pargs;
int slot, rc;
const char *reader_port = NULL;
- struct app_ctx_s appbuf;
unsigned long gen_random = 0;
-
- memset (&appbuf, 0, sizeof appbuf);
+ int interactive = 0;
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
@@ -143,7 +168,9 @@ main (int argc, char **argv )
case oDebugAll: opt.debug = ~0; break;
case oReaderPort: reader_port = pargs.r.ret_str; break;
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
+ case oDisableCCID: opt.disable_ccid = 1; break;
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
+ case oInteractive: interactive = 1; break;
default : pargs.err = 2; break;
}
}
@@ -151,7 +178,7 @@ main (int argc, char **argv )
exit(2);
if (opt.verbose < 2)
- opt.verbose = 2; /* hack to let select_openpgp print some info. */
+ opt.verbose = 2; /* Hack to let select_openpgp print some info. */
if (argc)
usage (1);
@@ -167,40 +194,61 @@ main (int argc, char **argv )
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
}
- appbuf.slot = slot;
- rc = app_select_openpgp (&appbuf, NULL, NULL);
- if (rc)
- log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ if (interactive)
+ interactive_shell (slot);
else
{
- appbuf.initialized = 1;
- log_info ("openpgp application selected\n");
+ struct app_ctx_s appbuf;
- if (gen_random)
+ /* Fixme: We better use app.c directly. */
+ memset (&appbuf, 0, sizeof appbuf);
+ appbuf.slot = slot;
+ rc = app_select_openpgp (&appbuf);
+ if (rc)
{
- size_t nbytes;
- unsigned char *buffer;
-
- buffer = xmalloc (4096);
- do
+ log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ memset (&appbuf, 0, sizeof appbuf);
+ appbuf.slot = slot;
+ rc = app_select_dinsig (&appbuf);
+ if (rc)
+ log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
+ else
{
- nbytes = gen_random > 4096? 4096 : gen_random;
- rc = app_get_challenge (&appbuf, nbytes, buffer);
- if (rc)
- log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
- else
+ appbuf.initialized = 1;
+ log_info ("dinsig application selected\n");
+ }
+ }
+ else
+ {
+ appbuf.initialized = 1;
+ log_info ("openpgp application selected\n");
+
+ if (gen_random)
+ {
+ size_t nbytes;
+ unsigned char *buffer;
+
+ buffer = xmalloc (4096);
+ do
{
- if (fwrite (buffer, nbytes, 1, stdout) != 1)
- log_error ("writing to stdout failed: %s\n",
- strerror (errno));
- gen_random -= nbytes;
+ nbytes = gen_random > 4096? 4096 : gen_random;
+ rc = app_get_challenge (&appbuf, nbytes, buffer);
+ if (rc)
+ log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
+ else
+ {
+ if (fwrite (buffer, nbytes, 1, stdout) != 1)
+ log_error ("writing to stdout failed: %s\n",
+ strerror (errno));
+ gen_random -= nbytes;
+ }
}
+ while (gen_random && !log_get_errorcount (0));
+ xfree (buffer);
}
- while (gen_random && !log_get_errorcount (0));
- xfree (buffer);
}
}
-
+
return log_get_errorcount (0)? 2:0;
}
@@ -211,3 +259,377 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
{
/* DUMMY */
}
+
+
+
+/* Dump BUFFER of length NBYTES in a nicely human readable format. */
+static void
+dump_buffer (const unsigned char *buffer, size_t nbytes)
+{
+ int i;
+
+ while (nbytes)
+ {
+ for (i=0; i < 16 && i < nbytes; i++)
+ printf ("%02X%s ", buffer[i], i==8? " ":"");
+ for (; i < 16; i++)
+ printf (" %s ", i==8? " ":"");
+ putchar (' ');
+ putchar (' ');
+ for (i=0; i < 16 && i < nbytes; i++)
+ if (isprint (buffer[i]))
+ putchar (buffer[i]);
+ else
+ putchar ('.');
+ nbytes -= i;
+ buffer += i;
+ for (; i < 16; i++)
+ putchar (' ');
+ putchar ('\n');
+ }
+}
+
+
+static void
+dump_or_store_buffer (const char *arg,
+ const unsigned char *buffer, size_t nbytes)
+{
+ const char *s = strchr (arg, '>');
+ int append;
+ FILE *fp;
+
+ if (!s)
+ {
+ dump_buffer (buffer, nbytes);
+ return;
+ }
+ if ((append = (*++s == '>')))
+ s++;
+ fp = fopen (s, append? "ab":"wb");
+ if (!fp)
+ {
+ log_error ("failed to create `%s': %s\n", s, strerror (errno));
+ return;
+ }
+ if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1)
+ log_error ("failed to write to `%s': %s\n", s, strerror (errno));
+ if (fclose (fp))
+ log_error ("failed to close `%s': %s\n", s, strerror (errno));
+}
+
+
+/* Convert STRING into a a newly allocated buffer and return the
+ length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and
+ unhexify that one. */
+static unsigned char *
+pin_to_buffer (const char *string, size_t *r_length)
+{
+ unsigned char *buffer = xmalloc (strlen (string)+1);
+ const char *s;
+ size_t n;
+
+ for (s=string, n=0; *s; s += 3)
+ {
+ if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2]))
+ {
+ buffer[n++] = xtoi_2 (s);
+ if (!s[2])
+ break;
+ }
+ else
+ {
+ memcpy (buffer, string, strlen (string));
+ *r_length = strlen (string);
+ return buffer;
+ }
+ }
+ *r_length = n;
+ return buffer;
+}
+
+
+static char *
+read_line (int use_readline, char *prompt)
+{
+ static char buf[256];
+
+#ifdef HAVE_READLINE
+ if (use_readline)
+ {
+ char *line = readline (prompt);
+ if (line)
+ trim_spaces (line);
+ if (line && strlen (line) > 2 )
+ add_history (line);
+ return line;
+ }
+#endif
+ /* Either we don't have readline or we are not running
+ interactively */
+#ifndef HAVE_READLINE
+ printf ("%s", prompt );
+#endif
+ fflush(stdout);
+ if (!fgets(buf, sizeof(buf), stdin))
+ return NULL;
+ if (!strlen(buf))
+ return NULL;
+ if (buf[strlen (buf)-1] == '\n')
+ buf[strlen (buf)-1] = 0;
+ trim_spaces (buf);
+ return buf;
+}
+
+/* Run a shell for interactive exploration of the card. */
+static void
+interactive_shell (int slot)
+{
+ enum cmdids
+ {
+ cmdNOP = 0,
+ cmdQUIT, cmdHELP,
+ cmdSELECT,
+ cmdCHDIR,
+ cmdLS,
+ cmdAPP,
+ cmdREAD,
+ cmdREADREC,
+ cmdDEBUG,
+ cmdVERIFY,
+ cmdCHANGEREF,
+
+ cmdINVCMD
+ };
+ static struct
+ {
+ const char *name;
+ enum cmdids id;
+ const char *desc;
+ } cmds[] = {
+ { "quit" , cmdQUIT , "quit this menu" },
+ { "q" , cmdQUIT , NULL },
+ { "help" , cmdHELP , "show this help" },
+ { "?" , cmdHELP , NULL },
+ { "debug" , cmdDEBUG, "set debugging flags" },
+ { "select" , cmdSELECT, "select file (EF)" },
+ { "s" , cmdSELECT, NULL },
+ { "chdir" , cmdCHDIR, "change directory (select DF)"},
+ { "cd" , cmdCHDIR, NULL },
+ { "ls" , cmdLS, "list directory (some cards only)"},
+ { "app" , cmdAPP, "select application"},
+ { "read" , cmdREAD, "read binary" },
+ { "rb" , cmdREAD, NULL },
+ { "readrec", cmdREADREC, "read record(s)" },
+ { "rr" , cmdREADREC, NULL },
+ { "verify" , cmdVERIFY, "verify CHVNO PIN" },
+ { "ver" , cmdVERIFY, NULL },
+ { "changeref", cmdCHANGEREF, "change reference data" },
+ { NULL, cmdINVCMD }
+ };
+ enum cmdids cmd = cmdNOP;
+ int use_readline = isatty (fileno(stdin));
+ char *line;
+ gpg_error_t err = 0;
+ unsigned char *result = NULL;
+ size_t resultlen;
+
+#ifdef HAVE_READLINE
+ if (use_readline)
+ using_history ();
+#endif
+
+ for (;;)
+ {
+ int arg_number;
+ const char *arg_string = "";
+ const char *arg_next = "";
+ char *p;
+ int i;
+
+ if (err)
+ printf ("command failed: %s\n", gpg_strerror (err));
+ err = 0;
+ xfree (result);
+ result = NULL;
+
+ printf ("\n");
+ do
+ {
+ line = read_line (use_readline, "cmd> ");
+ }
+ while ( line && *line == '#' );
+
+ arg_number = 0;
+ if (!line || *line == CONTROL_D)
+ cmd = cmdQUIT;
+ else if (!*line)
+ cmd = cmdNOP;
+ else {
+ if ((p=strchr (line,' ')))
+ {
+ char *endp;
+
+ *p++ = 0;
+ trim_spaces (line);
+ trim_spaces (p);
+ arg_number = strtol (p, &endp, 0);
+ arg_string = p;
+ if (endp != p)
+ {
+ arg_next = endp;
+ while ( spacep (arg_next) )
+ arg_next++;
+ }
+ }
+
+ for (i=0; cmds[i].name; i++ )
+ if (!ascii_strcasecmp (line, cmds[i].name ))
+ break;
+
+ cmd = cmds[i].id;
+ }
+
+ switch (cmd)
+ {
+ case cmdHELP:
+ for (i=0; cmds[i].name; i++ )
+ if (cmds[i].desc)
+ printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+ break;
+
+ case cmdQUIT:
+ goto leave;
+
+ case cmdNOP:
+ break;
+
+ case cmdDEBUG:
+ if (!*arg_string)
+ opt.debug = opt.debug? 0 : 2048;
+ else
+ opt.debug = arg_number;
+ break;
+
+ case cmdSELECT:
+ err = iso7816_select_file (slot, arg_number, 0, NULL, NULL);
+ break;
+
+ case cmdCHDIR:
+ err = iso7816_select_file (slot, arg_number, 1, NULL, NULL);
+ break;
+
+ case cmdLS:
+ err = iso7816_list_directory (slot, 1, &result, &resultlen);
+ if (!err || gpg_err_code (err) == GPG_ERR_ENOENT)
+ err = iso7816_list_directory (slot, 0, &result, &resultlen);
+ /* FIXME: Do something with RESULT. */
+ break;
+
+ case cmdAPP:
+ {
+ app_t app;
+
+ app = select_application (NULL, slot, *arg_string? arg_string:NULL);
+ if (app)
+ {
+ char *sn;
+
+ app_get_serial_and_stamp (app, &sn, NULL);
+ log_info ("application `%s' ready; sn=%s\n",
+ app->apptype?app->apptype:"?", sn? sn:"[none]");
+ release_application (app);
+ }
+ }
+ break;
+
+ case cmdREAD:
+ err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!err)
+ dump_or_store_buffer (arg_string, result, resultlen);
+ break;
+
+ case cmdREADREC:
+ if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' '))
+ {
+ /* Fixme: Can't write to a file yet. */
+ for (i=1, err=0; !err; i++)
+ {
+ xfree (result); result = NULL;
+ err = iso7816_read_record (slot, i, 1, &result, &resultlen);
+ if (!err)
+ dump_buffer (result, resultlen);
+ }
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ }
+ else
+ {
+ err = iso7816_read_record (slot, arg_number, 1,
+ &result, &resultlen);
+ if (!err)
+ dump_or_store_buffer (arg_string, result, resultlen);
+ }
+ break;
+
+ case cmdVERIFY:
+ if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
+ printf ("error: invalid CHVNO\n");
+ else
+ {
+ unsigned char *pin;
+ size_t pinlen;
+
+ pin = pin_to_buffer (arg_next, &pinlen);
+ err = iso7816_verify (slot, arg_number, pin, pinlen);
+ xfree (pin);
+ }
+ break;
+
+ case cmdCHANGEREF:
+ {
+ const char *newpin = arg_next;
+
+ while ( *newpin && !spacep (newpin) )
+ newpin++;
+ while ( spacep (newpin) )
+ newpin++;
+
+ if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
+ printf ("error: invalid CHVNO\n");
+ else if (!*arg_next || !*newpin || newpin == arg_next)
+ printf ("usage: changeref CHVNO OLDPIN NEWPIN\n");
+ else
+ {
+ char *oldpin = xstrdup (arg_next);
+ unsigned char *oldpin_buf, *newpin_buf;
+ size_t oldpin_len, newpin_len;
+
+ for (p=oldpin; *p && !spacep (p); p++ )
+ ;
+ *p = 0;
+ oldpin_buf = pin_to_buffer (oldpin, &oldpin_len);
+ newpin_buf = pin_to_buffer (newpin, &newpin_len);
+
+ err = iso7816_change_reference_data (slot, arg_number,
+ oldpin_buf, oldpin_len,
+ newpin_buf, newpin_len);
+
+ xfree (newpin_buf);
+ xfree (oldpin_buf);
+ xfree (oldpin);
+ }
+ }
+ break;
+
+ case cmdINVCMD:
+ default:
+ printf ("\n");
+ printf ("Invalid command (try \"help\")\n");
+ break;
+ } /* End command switch. */
+ } /* End of main menu loop. */
+
+ leave:
+ ;
+}
+
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 91ac93227..c6652c8dc 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -100,7 +100,7 @@ static ARGPARSE_OPTS opts[] = {
{ oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
{ octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")},
{ opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")},
- { oDisableCCID, "disable-ccidc", 0,
+ { oDisableCCID, "disable-ccid", 0,
#ifdef HAVE_LIBUSB
N_("do not use the internal CCID driver")
#else
@@ -397,7 +397,7 @@ main (int argc, char **argv )
case oServer: pipe_server = 1; break;
case oDaemon: is_daemon = 1; break;
- case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break;
+ case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
case oDisableCCID: opt.disable_ccid = 1; break;
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index e13377af7..2bbf271da 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -55,6 +55,7 @@ struct {
const char *homedir; /* configuration directory name */
const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */
+ const char *reader_port; /* NULL or reder port to use. */
int disable_opensc; /* Disable the use of the OpenSC framework. */
int disable_ccid; /* Disable the use of the internal CCID driver. */
int allow_admin; /* Allow the use of admin commands for certain
@@ -96,8 +97,10 @@ struct server_control_s {
};
typedef struct server_control_s *CTRL;
+typedef struct server_control_s *ctrl_t;
typedef struct card_ctx_s *CARD;
typedef struct app_ctx_s *APP;
+typedef struct app_ctx_s *app_t;
/*-- scdaemon.c --*/
void scd_exit (int rc);
diff --git a/scd/tlv.c b/scd/tlv.c
new file mode 100644
index 000000000..dbcd24546
--- /dev/null
+++ b/scd/tlv.c
@@ -0,0 +1,219 @@
+/* tlv.c - Tag-Length-Value Utilities
+ * Copyright (C) 2003, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <gpg-error.h>
+
+#include "tlv.h"
+
+static const unsigned char *
+do_find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes, int nestlevel)
+{
+ const unsigned char *s = buffer;
+ size_t n = length;
+ size_t len;
+ int this_tag;
+ int composite;
+
+ for (;;)
+ {
+ buffer = s;
+ if (n < 2)
+ return NULL; /* Buffer definitely too short for tag and length. */
+ if (!*s || *s == 0xff)
+ { /* Skip optional filler between TLV objects. */
+ s++;
+ n--;
+ continue;
+ }
+ composite = !!(*s & 0x20);
+ if ((*s & 0x1f) == 0x1f)
+ { /* more tag bytes to follow */
+ s++;
+ n--;
+ if (n < 2)
+ return NULL; /* buffer definitely too short for tag and length. */
+ if ((*s & 0x1f) == 0x1f)
+ return NULL; /* We support only up to 2 bytes. */
+ this_tag = (s[-1] << 8) | (s[0] & 0x7f);
+ }
+ else
+ this_tag = s[0];
+ len = s[1];
+ s += 2; n -= 2;
+ if (len < 0x80)
+ ;
+ else if (len == 0x81)
+ { /* One byte length follows. */
+ if (!n)
+ return NULL; /* we expected 1 more bytes with the length. */
+ len = s[0];
+ s++; n--;
+ }
+ else if (len == 0x82)
+ { /* Two byte length follows. */
+ if (n < 2)
+ return NULL; /* We expected 2 more bytes with the length. */
+ len = (s[0] << 8) | s[1];
+ s += 2; n -= 2;
+ }
+ else
+ return NULL; /* APDU limit is 65535, thus it does not make
+ sense to assume longer length fields. */
+
+ if (composite && nestlevel < 100)
+ { /* Dive into this composite DO after checking for a too deep
+ nesting. */
+ const unsigned char *tmp_s;
+ size_t tmp_len;
+
+ tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
+ if (tmp_s)
+ {
+ *nbytes = tmp_len;
+ return tmp_s;
+ }
+ }
+
+ if (this_tag == tag)
+ {
+ *nbytes = len;
+ return s;
+ }
+ if (len > n)
+ return NULL; /* Buffer too short to skip to the next tag. */
+ s += len; n -= len;
+ }
+}
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and
+ return a pointer to value as well as its length in NBYTES. Return
+ NULL if it was not found. Note, that the function does not check
+ whether the value fits into the provided buffer. */
+const unsigned char *
+find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes)
+{
+ return do_find_tlv (buffer, length, tag, nbytes, 0);
+}
+
+
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+ and the length part from the TLV triplet. Update BUFFER and SIZE
+ on success. */
+gpg_error_t
+parse_ber_header (unsigned char const **buffer, size_t *size,
+ int *r_class, int *r_tag,
+ int *r_constructed, int *r_ndef,
+ size_t *r_length, size_t *r_nhdr)
+{
+ int c;
+ unsigned long tag;
+ const unsigned char *buf = *buffer;
+ size_t length = *size;
+
+ *r_ndef = 0;
+ *r_length = 0;
+ *r_nhdr = 0;
+
+ /* Get the tag. */
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+
+ *r_class = (c & 0xc0) >> 6;
+ *r_constructed = !!(c & 0x20);
+ tag = c & 0x1f;
+
+ if (tag == 0x1f)
+ {
+ tag = 0;
+ do
+ {
+ /* Simple check against overflow. We limit our maximim tag
+ value more than needed but that should not be a problem
+ because I have nver encountered such large value. We
+ assume at least 32 bit integers. */
+ if (tag > (1 << 24))
+ return gpg_error (GPG_ERR_TOO_LARGE);
+ tag <<= 7;
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+ tag |= c & 0x7f;
+
+ }
+ while (c & 0x80);
+ }
+ *r_tag = tag;
+
+ /* Get the length. */
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+
+ if ( !(c & 0x80) )
+ *r_length = c;
+ else if (c == 0x80)
+ *r_ndef = 1;
+ else if (c == 0xff)
+ return gpg_error (GPG_ERR_BAD_BER);
+ else
+ {
+ unsigned long len = 0;
+ int count = c & 0x7f;
+
+ for (; count; count--)
+ {
+ /* Simple check against overflow. We limit our maximim
+ length more than needed but that should not be a problem
+ because I have never encountered such large value and
+ well they are managed in memory and thus we would run
+ into memory problems anyway. We assume at least 32 bit
+ integers. */
+ if (len > (1 << 24))
+ return gpg_error (GPG_ERR_TOO_LARGE);
+ len <<= 8;
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+ len |= c & 0xff;
+ }
+ *r_length = len;
+ }
+
+ /* Without this kludge some example certs can't be parsed. */
+ if (*r_class == CLASS_UNIVERSAL && !*r_tag)
+ *r_length = 0;
+
+ *buffer = buf;
+ *size = length;
+ return 0;
+}
diff --git a/scd/tlv.h b/scd/tlv.h
new file mode 100644
index 000000000..26a9905f7
--- /dev/null
+++ b/scd/tlv.h
@@ -0,0 +1,84 @@
+/* tlv.h - Tag-Length-Value Utilities
+ * 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
+ */
+
+#ifndef SCD_TLV_H
+#define SCD_TLV_H 1
+
+
+enum tlv_tag_class {
+ CLASS_UNIVERSAL = 0,
+ CLASS_APPLICATION = 1,
+ CLASS_CONTEXT = 2,
+ CLASS_PRIVATE =3
+};
+
+enum tlv_tag_type {
+ TAG_NONE = 0,
+ TAG_BOOLEAN = 1,
+ TAG_INTEGER = 2,
+ TAG_BIT_STRING = 3,
+ TAG_OCTET_STRING = 4,
+ TAG_NULL = 5,
+ TAG_OBJECT_ID = 6,
+ TAG_OBJECT_DESCRIPTOR = 7,
+ TAG_EXTERNAL = 8,
+ TAG_REAL = 9,
+ TAG_ENUMERATED = 10,
+ TAG_EMBEDDED_PDV = 11,
+ TAG_UTF8_STRING = 12,
+ TAG_REALTIVE_OID = 13,
+ TAG_SEQUENCE = 16,
+ TAG_SET = 17,
+ TAG_NUMERIC_STRING = 18,
+ TAG_PRINTABLE_STRING = 19,
+ TAG_TELETEX_STRING = 20,
+ TAG_VIDEOTEX_STRING = 21,
+ TAG_IA5_STRING = 22,
+ TAG_UTC_TIME = 23,
+ TAG_GENERALIZED_TIME = 24,
+ TAG_GRAPHIC_STRING = 25,
+ TAG_VISIBLE_STRING = 26,
+ TAG_GENERAL_STRING = 27,
+ TAG_UNIVERSAL_STRING = 28,
+ TAG_CHARACTER_STRING = 29,
+ TAG_BMP_STRING = 30
+};
+
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
+ pointer to value as well as its length in NBYTES. Return NULL if
+ it was not found. Note, that the function does not check whether
+ the value fits into the provided buffer.*/
+const unsigned char *find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes);
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+ and the length part from the TLV triplet. Update BUFFER and SIZE
+ on success. */
+gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
+ int *r_class, int *r_tag,
+ int *r_constructed,
+ int *r_ndef, size_t *r_length, size_t *r_nhdr);
+
+
+
+#endif /* SCD_TLV_H */