diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 21:21:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 21:21:03 +0000 |
commit | 3675f65888fde5fddde20ff811638a338bf08ca6 (patch) | |
tree | 065688bbb6355a8a4784ec9c8e112cec342eac5e /scd | |
parent | Adding upstream version 2.2.40. (diff) | |
download | gnupg2-3675f65888fde5fddde20ff811638a338bf08ca6.tar.xz gnupg2-3675f65888fde5fddde20ff811638a338bf08ca6.zip |
Adding upstream version 2.2.43.upstream/2.2.43upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scd')
-rw-r--r-- | scd/Makefile.am | 9 | ||||
-rw-r--r-- | scd/Makefile.in | 36 | ||||
-rw-r--r-- | scd/apdu.c | 31 | ||||
-rw-r--r-- | scd/app-dinsig.c | 25 | ||||
-rw-r--r-- | scd/app-nks.c | 1976 | ||||
-rw-r--r-- | scd/app-openpgp.c | 1389 | ||||
-rw-r--r-- | scd/app-p15.c | 108 | ||||
-rw-r--r-- | scd/app-sc-hsm.c | 35 | ||||
-rw-r--r-- | scd/app.c | 33 | ||||
-rw-r--r-- | scd/command.c | 61 | ||||
-rw-r--r-- | scd/iso7816.c | 63 | ||||
-rw-r--r-- | scd/iso7816.h | 3 | ||||
-rw-r--r-- | scd/scdaemon.h | 3 | ||||
-rw-r--r-- | scd/scdaemon.w32-manifest.in | 9 |
14 files changed, 2893 insertions, 888 deletions
diff --git a/scd/Makefile.am b/scd/Makefile.am index 32be6ab..4539731 100644 --- a/scd/Makefile.am +++ b/scd/Makefile.am @@ -1,3 +1,4 @@ +# Makefile.am - scd # Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. # # This file is part of GnuPG. @@ -26,7 +27,10 @@ AM_CPPFLAGS = $(LIBUSB_CPPFLAGS) include $(top_srcdir)/am/cmacros.am if HAVE_W32_SYSTEM -scdaemon_robjs = $(resource_objs) scdaemon-w32info.o +scdaemon_rc_objs = $(resource_objs) scdaemon-w32info.o +scdaemon-w32info.o : scdaemon.w32-manifest ../common/w32info-rc.h +else +scdaemon_rc_objs = $(resource_objs) endif AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \ @@ -48,4 +52,5 @@ scdaemon_SOURCES = \ scdaemon_LDADD = $(libcommonpth) \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_robjs) + $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_rc_objs) +scdaemon_DEPENDENCIES = $(scdaemon_rc_objs) diff --git a/scd/Makefile.in b/scd/Makefile.in index d278ee5..78e7448 100644 --- a/scd/Makefile.in +++ b/scd/Makefile.in @@ -14,6 +14,7 @@ @SET_MAKE@ +# Makefile.am - scd # Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. # # This file is part of GnuPG. @@ -146,17 +147,16 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ - $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ - $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ - $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ksba.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/ldap.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libassuan.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/npth.m4 \ + $(top_srcdir)/m4/ntbtls.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/socklen.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 $(top_srcdir)/m4/tar-ustar.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -175,14 +175,9 @@ am_scdaemon_OBJECTS = scdaemon.$(OBJEXT) command.$(OBJEXT) \ $(am__objects_1) scdaemon_OBJECTS = $(am_scdaemon_OBJECTS) am__DEPENDENCIES_1 = +@HAVE_W32_SYSTEM_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ @HAVE_W32_SYSTEM_TRUE@ scdaemon-w32info.o -scdaemon_DEPENDENCIES = $(libcommonpth) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -459,7 +454,8 @@ libcommon = ../common/libcommon.a libcommonpth = ../common/libcommonpth.a libcommontls = ../common/libcommontls.a libcommontlsnpth = ../common/libcommontlsnpth.a -@HAVE_W32_SYSTEM_TRUE@scdaemon_robjs = $(resource_objs) scdaemon-w32info.o +@HAVE_W32_SYSTEM_FALSE@scdaemon_rc_objs = $(resource_objs) +@HAVE_W32_SYSTEM_TRUE@scdaemon_rc_objs = $(resource_objs) scdaemon-w32info.o AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) @@ -476,8 +472,9 @@ scdaemon_SOURCES = \ scdaemon_LDADD = $(libcommonpth) \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_robjs) + $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_rc_objs) +scdaemon_DEPENDENCIES = $(scdaemon_rc_objs) all: all-am .SUFFIXES: @@ -840,6 +837,7 @@ uninstall-am: uninstall-libexecPROGRAMS @HAVE_W32_SYSTEM_TRUE@.rc.o: @HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" +@HAVE_W32_SYSTEM_TRUE@scdaemon-w32info.o : scdaemon.w32-manifest ../common/w32info-rc.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. @@ -772,7 +772,14 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, return err; if (DBG_CARD_IO) - log_printhex (apdu, apdulen, " PCSC_data:"); + { + /* Do not dump the PIN in a VERIFY command. */ + if (apdulen > 5 && apdu[1] == 0x20) + log_debug ("PCSC_data: %02X %02X %02X %02X %02X [redacted]\n", + apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); + else + log_printhex (apdu, apdulen, "PCSC_data:"); + } if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) send_pci.protocol = PCSC_PROTOCOL_T1; @@ -1697,7 +1704,14 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, return err; if (DBG_CARD_IO) - log_printhex (apdu, apdulen, " raw apdu:"); + { + /* Do not dump the PIN in a VERIFY command. */ + if (apdulen > 5 && apdu[1] == 0x20) + log_debug (" raw apdu: %02x%02x%02x%02x%02x [redacted]\n", + apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); + else + log_printhex (apdu, apdulen, " raw apdu:"); + } maxbuflen = *buflen; if (pininfo) @@ -2601,19 +2615,16 @@ apdu_get_atr (int slot, size_t *atrlen) { unsigned char *buf; - if (DBG_READER) - log_debug ("enter: apdu_get_atr: slot=%d\n", slot); - if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) { if (DBG_READER) - log_debug ("leave: apdu_get_atr => NULL (bad slot)\n"); + log_debug ("apdu_get_atr => NULL (bad slot)\n"); return NULL; } if (!reader_table[slot].atrlen) { if (DBG_READER) - log_debug ("leave: apdu_get_atr => NULL (no ATR)\n"); + log_debug ("apdu_get_atr => NULL (no ATR)\n"); return NULL; } @@ -2621,13 +2632,11 @@ apdu_get_atr (int slot, size_t *atrlen) if (!buf) { if (DBG_READER) - log_debug ("leave: apdu_get_atr => NULL (out of core)\n"); + log_debug ("apdu_get_atr => NULL (out of core)\n"); return NULL; } memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen); *atrlen = reader_table[slot].atrlen; - if (DBG_READER) - log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen); return buf; } @@ -3235,7 +3244,7 @@ apdu_send_simple (int slot, int extended_mode, * Out of historical reasons the function returns 0 on success and * outs the status word at the end of the result to be able to get the * status word in the case of a not provided RETBUF, R_SW can be used - * to store the SW. But note that R_SW qill only be set if the + * to store the SW. But note that R_SW will only be set if the * function returns 0. */ int apdu_send_direct (int slot, size_t extended_length, diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c index 5a2713e..378ec42 100644 --- a/scd/app-dinsig.c +++ b/scd/app-dinsig.c @@ -100,7 +100,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Return the certificate of the card holder. */ fid = 0xC000; - len = app_help_read_length_of_cert (app->slot, fid, &certoff); + len = app_help_read_length_of_cert (app_get_slot (app), fid, &certoff); if (!len) return 0; /* Card has not been personalized. */ @@ -113,7 +113,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) /* Now we need to read the certificate, so that we can get the public key out of it. */ - err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen); + err = iso7816_read_binary (app_get_slot (app), certoff, len-certoff, + &der, &derlen); if (err) { log_info ("error reading entire certificate from FID 0x%04X: %s\n", @@ -192,14 +193,14 @@ do_readcert (app_t app, const char *certid, /* 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); + err = iso7816_select_file (app_get_slot (app), fid, 0); 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); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", @@ -292,7 +293,7 @@ verify_pin (app_t app, pininfo.maxlen = 8; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, _("||Please enter your PIN at the reader's pinpad"), @@ -303,7 +304,7 @@ verify_pin (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x81, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } @@ -344,7 +345,8 @@ verify_pin (app_t app, return gpg_error (GPG_ERR_BAD_PIN); } - rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), 0x81, + pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_INV_VALUE) { /* We assume that ISO 9564-1 encoding is used and we failed @@ -365,7 +367,8 @@ verify_pin (app_t app, paddedpin[i++] = (((*s - '0') << 4) | 0x0f); while (i < sizeof paddedpin) paddedpin[i++] = 0xff; - rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin); + rc = iso7816_verify (app_get_slot (app), 0x81, + paddedpin, sizeof paddedpin); } xfree (pinvalue); } @@ -483,7 +486,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, rc = verify_pin (app, pincb, pincb_arg); if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, + rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } @@ -533,7 +536,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, return err; } - err = iso7816_change_reference_data (app->slot, 0x81, + err = iso7816_change_reference_data (app_get_slot (app), 0x81, oldpin, oldpinlen, pinvalue, strlen (pinvalue)); xfree (pinvalue); @@ -548,7 +551,7 @@ gpg_error_t app_select_dinsig (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid, sizeof aid, 0); diff --git a/scd/app-nks.c b/scd/app-nks.c index 1f59321..b872e6e 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -1,5 +1,6 @@ /* app-nks.c - The Telesec NKS card application. - * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc. + * Copyright (C) 2004, 2007-2009 Free Software Foundation, Inc. + * Copyright (C) 2004, 2007-2009, 2013-2015, 2020, 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -18,28 +19,55 @@ */ /* Notes: - - - We are now targeting TCOS 3 cards and it may happen that there is - a regression towards TCOS 2 cards. Please report. - - - The TKS3 AUT key is not used. It seems that it is only useful for - the internal authentication command and not accessible by other - applications. The key itself is in the encryption class but the - corresponding certificate has only the digitalSignature - capability. - - - If required, we automagically switch between the NKS application - and the SigG application. This avoids to use the DINSIG - application which is somewhat limited, has no support for Secure - Messaging as required by TCOS 3 and has no way to change the PIN - or even set the NullPIN. - - - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer - cards. This is because the NKS application has moved to DF02 with - TCOS 3 and thus we better use a DF independent tag. - - - We use only the global PINs for the NKS application. - + * + * - We are now targeting TCOS 3 cards and it may happen that there is + * a regression towards TCOS 2 cards. Please report. + * + * - The NKS3 AUT key is not used. It seems that it is only useful for + * the internal authentication command and not accessible by other + * applications. The key itself is in the encryption class but the + * corresponding certificate has only the digitalSignature + * capability. + * Update: This changed for the Signature Card V2 (nks version 15) + * + * - If required, we automagically switch between the NKS application + * and the SigG or eSign application. This avoids to use the DINSIG + * application which is somewhat limited, has no support for Secure + * Messaging as required by TCOS 3 and has no way to change the PIN + * or even set the NullPIN. With the Signature Card v2 (nks version + * 15) the Esign application is used instead of the SigG. + * + * - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer + * cards. This is because the NKS application has moved to DF02 with + * TCOS 3 and thus we better use a DF independent tag. + * + * - We use only the global PINs for the NKS application. + * + * + * + * Here is a table with PIN stati collected from 3 cards. + * + * | app | pwid | NKS3 | SIG_B | SIG_N | + * |-----+------+-----------+-----------+-----------| + * | NKS | 0x00 | null - | - - | - - | + * | | 0x01 | 0 3 | - - | - - | + * | | 0x02 | 3 null | 15 3 | 15 null | + * | | 0x03 | - 3 | null - | 3 - | + * | | 0x04 | | null 0 | 3 3 | + * | SIG | 0x00 | null - | - - | - - | + * | | 0x01 | 0 null | - null | - null | + * | | 0x02 | 3 null | 15 0 | 15 0 | + * | | 0x03 | - 0 | null null | null null | + * - SIG is either SIGG or ESIGN. + * - "-" indicates reference not found (SW 6A88). + * - "null" indicates a NULLPIN (SW 6985). + * - The first value in each cell is the global PIN; + * the second is the local PIN (high bit of pwid set). + * - The NKS3 card is some older test card. + * - The SIG_B is a Signature Card V2.0 with Brainpool curves. + * Here the PIN 0x82 has been changed from the NULLPIN. + * - The SIG_N is a Signature Card V2.0 with NIST curves. + * The PIN was enabled using the TCOS Windows tool. */ #include <config.h> @@ -47,7 +75,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> #include <time.h> #include "scdaemon.h" @@ -59,66 +86,157 @@ static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; +static char const aid_esign[] = + { 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E }; +static char const aid_idlm[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x0c, 0x01 }; + + +/* The ids of the different apps on our TCOS cards. */ +#define NKS_APP_NKS 0 +#define NKS_APP_SIGG 1 +#define NKS_APP_ESIGN 2 +#define NKS_APP_IDLM 3 static struct { - int is_sigg; /* Valid for SigG application. */ + int nks_app_id;/* One of NKS_APP_*. Keep them sorted so that no + * unnecessary application switching is needed. */ int fid; /* File ID. */ - int nks_ver; /* 0 for NKS version 2, 3 for version 3. */ + int nks_ver; /* 0 for NKS version 2, 3 for version 3, etc. */ int certtype; /* Type of certificate or 0 if it is not a certificate. */ - int iskeypair; /* If true has the FID of the corresponding certificate. */ + int iskeypair; /* If true has the FID of the corresponding certificate. + * If no certificate is known a value of -1 is used. */ + int isauthkey; /* True if file is a key usable for authentication. */ int issignkey; /* True if file is a key usable for signing. */ - int isenckey; /* True if file is a key usable for decryption. */ + int isencrkey; /* True if file is a key usable for decryption. */ unsigned char kid; /* Corresponding key references. */ } filelist[] = { - { 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */ + { 0, 0x4531, 0, 0, 0xC000, 1,1,0, 0x80}, /* EF_PK.NKS.SIG */ + /* */ /* nks15: EF.PK.NKS.ADS */ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ - { 0, 0x4331, 0, 100 }, + /* */ /* nks15: EF.C.ICC.ADS (sign key) */ + + { 0, 0x4331, 0, 100 }, /* Unnamed. */ + /* */ /* nks15: EF.C.ICC.RFU1 */ + /* */ /* (second cert for sign key) */ + { 0, 0x4332, 0, 100 }, - { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ - { 0, 0x45B1, 0, 0, 0xC200, 0, 1, 0x81 }, /* EF_PK.NKS.ENC */ - { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ - { 0, 0x43B1, 0, 100 }, + { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ + + { 0, 0x45B1, 0, 0, 0xC200, 0,0,1, 0x81}, /* EF_PK.NKS.ENC */ + /* */ /* nks15: EF.PK.ICC.ENC1 */ + { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ + /* nks15: EF.C.ICC.ENC1 (Cert-encr) */ + + { 0, 0x43B1, 0, 100 }, /* Unnamed */ + /* */ /* nks15: EF.C.ICC.RFU2 */ + /* */ /* (second cert for enc1 key) */ + { 0, 0x43B2, 0, 100 }, -/* The authentication key is not used. */ -/* { 0, 0x4571, 3, 0, 0xC500, 0, 0, 0x82 }, /\* EF_PK.NKS.AUT *\/ */ -/* { 0, 0xC500, 3, 101 }, /\* EF_C.NKS.AUT *\/ */ - { 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */ + { 0, 0x4371,15, 100 }, /* EF.C.ICC.RFU3 */ + /* */ /* (second cert for auth key) */ + + { 0, 0x45B2, 3, 0, 0xC201, 0,0,1, 0x83}, /* EF_PK.NKS.ENC1024 */ + /* */ /* nks15: EF.PK.ICC.ENC2 */ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ - { 1, 0x4531, 3, 0, 0xC000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */ + + { 0, 0xC20E,15, 111 }, /* EF.C.CSP.RCA1 (RootCA 1) */ + { 0, 0xC208,15, 101 }, /* EF.C.CSP.SCA1 (SubCA 1) */ + { 0, 0xC10E,15, 111 }, /* EF.C.CSP.RCA2 (RootCA 2) */ + { 0, 0xC108,15, 101 }, /* EF.C.CSP.SCA2 (SubCA 2) */ + + { 0, 0x4571,15, 0, 0xC500, 1,0,0, 0x82}, /* EF.PK.ICC.AUT */ + { 0, 0xC500,15, 101 }, /* EF.C.ICC.AUT (Cert-auth) */ + + { 0, 0xC201,15, 101 }, /* EF.C.ICC.ENC2 (Cert-encr) */ + /* (empty on delivery) */ + + { 1, 0x4531, 3, 0, 0xC000, 0,1,1, 0x84}, /* EF_PK.CH.SIG */ { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */ + { 1, 0xC008, 3, 101 }, /* EF_C.CA.SIG */ { 1, 0xC00E, 3, 111 }, /* EF_C.RCA.SIG */ + + { 2, 0x4531, 15, 0, 0xC001, 0,1,0, 0x84}, /* EF_PK.CH.SIG */ + { 2, 0xC000, 15,101 }, /* EF.C.SCA.QES (SubCA) */ + { 2, 0xC001, 15,100 }, /* EF.C.ICC.QES (Cert) */ + { 2, 0xC00E, 15,111 }, /* EF.C.RCA.QES (RootCA */ + + { 3, 0x4E03, 3, 0, -1 }, /* EK_PK_03 */ + { 3, 0x4E04, 3, 0, -1 }, /* EK_PK_04 */ + { 3, 0x4E05, 3, 0, -1 }, /* EK_PK_05 */ + { 3, 0x4E06, 3, 0, -1 }, /* EK_PK_06 */ + { 3, 0x4E07, 3, 0, -1 }, /* EK_PK_07 */ + { 3, 0x4E08, 3, 0, -1 }, /* EK_PK_08 */ + { 0, 0 } }; +/* Object to cache information gathered from FIDs. */ +struct fid_cache_s { + struct fid_cache_s *next; + int nks_app_id; + int fid; /* Zero for an unused slot. */ + unsigned int got_keygrip:1; /* The keygrip and algo are valid. */ + int algo; + char *algostr; /* malloced. */ + char keygripstr[2*KEYGRIP_LEN+1]; +}; + /* Object with application (i.e. NKS) specific data. */ struct app_local_s { - int nks_version; /* NKS version. */ + int active_nks_app; /* One of the NKS_APP_ constants. */ + + int only_idlm; /* The application is fixed to IDLM (IDKey card). */ + int qes_app_id; /* Either NKS_APP_SIGG or NKS_APP_ESIGN. */ - int sigg_active; /* True if switched to the SigG application. */ - int sigg_msig_checked;/* True if we checked for a mass signature card. */ - int sigg_is_msig; /* True if this is a mass signature card. */ + int sigg_msig_checked;/* True if we checked for a mass signature card. */ + int sigg_is_msig; /* True if this is a mass signature card. */ - int need_app_select; /* Need to re-select the application. */ + int need_app_select; /* Need to re-select the application. */ + struct fid_cache_s *fid_cache; /* Linked list with cached infos. */ }; -static gpg_error_t switch_application (app_t app, int enable_sigg); +static gpg_error_t readcert_from_ef (app_t app, int fid, + unsigned char **cert, size_t *certlen); +static gpg_error_t switch_application (app_t app, int nks_app_id); +static const char *parse_pwidstr (app_t app, const char *pwidstr, int new_mode, + int *r_nks_app_id, int *r_pwid); +static gpg_error_t verify_pin (app_t app, int pwid, const char *desc, + gpg_error_t (*pincb)(void*, const char *, + char **), + void *pincb_arg); +static gpg_error_t parse_keyref (app_t app, const char *keyref, + int want_keypair, int *r_fididx); +static void +flush_fid_cache (app_t app) +{ + while (app->app_local->fid_cache) + { + struct fid_cache_s *next = app->app_local->fid_cache->next; + if (app->app_local->fid_cache) + xfree (app->app_local->fid_cache->algostr); + xfree (app->app_local->fid_cache); + app->app_local->fid_cache = next; + } +} + /* Release local data. */ static void do_deinit (app_t app) { if (app && app->app_local) { + flush_fid_cache (app); xfree (app->app_local); app->app_local = NULL; } @@ -137,33 +255,78 @@ all_zero_p (void *buffer, size_t length) } -/* Read the file with FID, assume it contains a public key and return - its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ +/* Return an allocated string with the serial number in a format to be + * show to the user. May return NULL on malloc problem. */ +static char * +get_dispserialno (app_t app) +{ + char *result; + + /* We only need to strip the last zero which is not printed on the + * card. */ + result = app_get_serialno (app); + if (result && *result && result[strlen(result)-1] == '0') + result[strlen(result)-1] = 0; + return result; +} + + static gpg_error_t -keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) +pubkey_from_pk_file (app_t app, int pkfid, int cfid, + unsigned char **r_pk, size_t *r_pklen) { gpg_error_t err; - unsigned char grip[20]; unsigned char *buffer[2]; size_t buflen[2]; - gcry_sexp_t sexp; int i; int offset[2] = { 0, 0 }; - err = iso7816_select_file (app->slot, fid, 0); + *r_pk = NULL; + *r_pklen = 0; + + if (app->appversion == 15) + { + /* Signature Card v2 - get keygrip from the certificate. */ + unsigned char *cert; + size_t certlen; + + if (cfid == -1) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* Fall back to certificate reading. */ + err = readcert_from_ef (app, cfid, &cert, &certlen); + if (err) + { + log_error ("nks: error reading certificate %04X: %s\n", + cfid, gpg_strerror (err)); + return err; + } + + err = app_help_pubkey_from_cert (cert, certlen, r_pk, r_pklen); + xfree (cert); + if (err) + log_error ("nks: error parsing certificate %04X: %s\n", + cfid, gpg_strerror (err)); + + return err; + } + + err = iso7816_select_file (app_get_slot (app), pkfid, 0); if (err) return err; - err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]); + err = iso7816_read_record (app_get_slot (app), 1, 1, 0, + &buffer[0], &buflen[0]); if (err) return err; - err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]); + err = iso7816_read_record (app_get_slot (app), 2, 1, 0, + &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } - if (app->app_local->nks_version < 3) + if (app->appversion < 3) { /* Old versions of NKS store the values in a TLV encoded format. We need to do some checks. */ @@ -179,6 +342,12 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) err = gpg_error (GPG_ERR_INV_OBJ); else offset[i] = 2; + if (err) + { + xfree (buffer[0]); + xfree (buffer[1]); + return err; + } } } else @@ -208,9 +377,10 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) newbuf = xtrymalloc (newlen); if (!newbuf) { + err = gpg_error_from_syserror (); xfree (buffer[0]); xfree (buffer[1]); - return gpg_error_from_syserror (); + return err; } newbuf[0] = 0; memcpy (newbuf+1, buffer[i]+offset[i], buflen[i] - offset[i]); @@ -221,41 +391,238 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) } } - if (!err) - err = gcry_sexp_build (&sexp, NULL, - "(public-key (rsa (n %b) (e %b)))", - (int)buflen[0]-offset[0], buffer[0]+offset[0], - (int)buflen[1]-offset[1], buffer[1]+offset[1]); + *r_pk = make_canon_sexp_from_rsa_pk (buffer[0]+offset[0], buflen[0]-offset[0], + buffer[1]+offset[1], buflen[1]-offset[1], + r_pklen); xfree (buffer[0]); xfree (buffer[1]); - if (err) - return err; + return err; +} + +/* Read the file with PKFID, assume it contains a public key and + * return its keygrip in the caller provided 41 byte buffer R_GRIPSTR. + * This works only for RSA card. For the Signature Card v2 ECC is + * used and Read Record needs to be replaced by read binary. Given + * all the ECC parameters required, we don't do that but rely that the + * corresponding certificate at CFID is already available and get the + * public key from there. Note that a CFID of 1 is indicates that a + * certificate is not known. If R_ALGO is not NULL the public key + * algorithm for the returned KEYGRIP is stored there. If R_ALGOSTR + * is not NULL the public key algo string (e.g. "rsa2048") is stored + * there. */ +static gpg_error_t +keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr, + int *r_algo, char **r_algostr) +{ + gpg_error_t err; + int algo = 0; /* Public key algo. */ + char *algostr = NULL; /* Public key algo string. */ + struct fid_cache_s *ci; + unsigned char *pk; + size_t pklen; + + for (ci = app->app_local->fid_cache; ci; ci = ci->next) + if (ci->fid && ci->nks_app_id == app->app_local->active_nks_app + && ci->fid == pkfid) + { + if (!ci->got_keygrip) + return gpg_error (GPG_ERR_NOT_FOUND); + if (r_algostr && !ci->algostr) + break; /* Not in the cache - try w/o cache. */ + memcpy (r_gripstr, ci->keygripstr, 2*KEYGRIP_LEN+1); + if (r_algo) + *r_algo = ci->algo; + if (r_algostr) + { + *r_algostr = xtrystrdup (ci->algostr); + if (!*r_algostr) + return gpg_error_from_syserror (); + } + return 0; /* Found in cache. */ + } - if (!gcry_pk_get_keygrip (sexp, grip)) + err = pubkey_from_pk_file (app, pkfid, cfid, &pk, &pklen); + if (!err) + err = app_help_get_keygrip_string_pk (pk, pklen, r_gripstr, NULL, + &algo, &algostr); + xfree (pk); + + if (!err) { - err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by - libgcrypt. */ + if (r_algo) + *r_algo = algo; + if (r_algostr) + { + *r_algostr = algostr; + algostr = NULL; + } + + /* FIXME: We need to implement not_found caching. */ + for (ci = app->app_local->fid_cache; ci; ci = ci->next) + if (ci->fid + && ci->nks_app_id == app->app_local->active_nks_app + && ci->fid == pkfid) + { + /* Update the keygrip. */ + memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1); + ci->algo = algo; + xfree (ci->algostr); + ci->algostr = algostr? xtrystrdup (algostr) : NULL; + ci->got_keygrip = 1; + break; + } + if (!ci) + { + for (ci = app->app_local->fid_cache; ci; ci = ci->next) + if (!ci->fid) + break; + if (!ci) + ci = xtrycalloc (1, sizeof *ci); + if (!ci) + ; /* Out of memory - it is a cache, so we ignore it. */ + else + { + ci->nks_app_id = app->app_local->active_nks_app; + ci->fid = pkfid; + memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1); + ci->algo = algo; + ci->got_keygrip = 1; + ci->next = app->app_local->fid_cache; + app->app_local->fid_cache = ci; + } + } } - else + xfree (algostr); + return err; +} + + +/* Parse KEYREF and return the index into the FILELIST at R_IDX. + * Returns 0 on success and switches to the requested application. + * The public key algo is stored at R_ALGO unless it is NULL. */ +static gpg_error_t +find_fid_by_keyref (app_t app, const char *keyref, int *r_idx, int *r_algo) +{ + gpg_error_t err; + int idx; + char keygripstr[2*KEYGRIP_LEN+1]; + + if (!keyref || !keyref[0]) + err = gpg_error (GPG_ERR_INV_ID); + else if (keyref[0] != 'N' && strlen (keyref) == 40) /* This is a keygrip. */ + { + struct fid_cache_s *ci; + + /* FIXME: Our cache structure needs to be revised. It doesn't + * take the app_id into account and we don't have a way to + * directly access the FID item if there are several of them + * with different app_ids. We disable the cache for now. */ + for (ci = app->app_local->fid_cache ; ci; ci = ci->next) + if (ci->fid && ci->got_keygrip && !strcmp (ci->keygripstr, keyref)) + break; + if (ci && 0 ) /* Cached (disabled) */ + { + for (idx=0; filelist[idx].fid; idx++) + if (filelist[idx].fid == ci->fid) + break; + if (!filelist[idx].fid) + { + log_debug ("nks: Ooops: Unkown FID cached!\n"); + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + err = switch_application (app, filelist[idx].nks_app_id); + if (err) + goto leave; + if (r_algo) + *r_algo = ci->algo; + } + else /* Not cached. */ + { + for (idx=0; filelist[idx].fid; idx++) + { + if (!filelist[idx].iskeypair) + continue; + + if (app->app_local->only_idlm) + { + if (filelist[idx].nks_app_id != NKS_APP_IDLM) + continue; + } + else + { + if (filelist[idx].nks_app_id != NKS_APP_NKS + && filelist[idx].nks_app_id != app->app_local->qes_app_id) + continue; + + err = switch_application (app, filelist[idx].nks_app_id); + if (err) + goto leave; + } + + err = keygripstr_from_pk_file (app, filelist[idx].fid, + filelist[idx].iskeypair, + keygripstr, r_algo, NULL); + if (err) + { + log_info ("nks: no keygrip for FID 0x%04X: %s - ignored\n", + filelist[idx].fid, gpg_strerror (err)); + continue; + } + if (!strcmp (keygripstr, keyref)) + break; /* Found */ + } + if (!filelist[idx].fid) + { + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + /* (No need to switch the app as that has already been done + * in the loop.) */ + } + *r_idx = idx; + err = 0; + } + else /* This is a usual keyref. */ { - bin2hex (grip, 20, r_gripstr); + err = parse_keyref (app, keyref, 1, &idx); + if (err) + goto leave; + *r_idx = idx; + + err = switch_application (app, filelist[idx].nks_app_id); + if (err) + goto leave; + if (r_algo) + { + /* We need to get the public key algo. */ + err = keygripstr_from_pk_file (app, filelist[idx].fid, + filelist[idx].iskeypair, + keygripstr, r_algo, NULL); + if (err) + log_error ("nks: no keygrip for FID 0x%04X: %s\n", + filelist[idx].fid, gpg_strerror (err)); + } } - gcry_sexp_release (sexp); + + leave: return err; } /* TCOS responds to a verify with empty data (i.e. without the Lc - * byte) with the status of the PIN. PWID is the PIN ID, If SIGG is - * true, the application is switched into SigG mode. Returns: + * byte) with the status of the PIN. PWID is the PIN ID. NKS_APP_ID + * gives the application to first switch to. Returns: * ISO7816_VERIFY_* codes or non-negative number of verification * attempts left. */ static int -get_chv_status (app_t app, int sigg, int pwid) +get_chv_status (app_t app, int nks_app_id, int pwid) { - if (switch_application (app, sigg)) - return sigg? -2 : -1; /* No such PIN / General error. */ + if (switch_application (app, nks_app_id)) + return (nks_app_id == NKS_APP_NKS + ? ISO7816_VERIFY_ERROR + : ISO7816_VERIFY_NO_PIN); return iso7816_verify_status (app_get_slot (app), pwid); } @@ -273,33 +640,49 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "$AUTHKEYID", 1 }, { "$ENCRKEYID", 2 }, { "$SIGNKEYID", 3 }, - { "NKS-VERSION", 4 }, + { "NKS-VERSION", 4 }, /* Legacy (printed decimal) */ { "CHV-STATUS", 5 }, - { NULL, 0 } + { "$DISPSERIALNO",6 }, + { "SERIALNO", 0 } }; gpg_error_t err = 0; int idx; + char *p, *p2; char buffer[100]; + int nksver = app->appversion; - err = switch_application (app, 0); + err = switch_application (app, NKS_APP_NKS); if (err) return err; - for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) + for (idx=0; (idx < DIM(table) + && ascii_strcasecmp (table[idx].name, name)); idx++) ; - if (!table[idx].name) + if (!(idx < DIM (table))) return gpg_error (GPG_ERR_INV_NAME); switch (table[idx].special) { + case 0: /* SERIALNO */ + { + p = app_get_serialno (app); + if (p) + { + send_status_direct (ctrl, "SERIALNO", p); + xfree (p); + } + } + break; + case 1: /* $AUTHKEYID */ { /* NetKey 3.0 cards define an authentication key but according to the specs this key is only usable for encryption and not signing. it might work anyway but it has not yet been tested - fixme. Thus for now we use the NKS signature key - for authentication. */ - char const tmp[] = "NKS-NKS3.4531"; + for authentication for netkey 3. For the Signature Card + V2.0 the auth key is defined and thus we use it. */ + const char *tmp = nksver == 15? "NKS-NKS3.4571" : "NKS-NKS3.4531"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } break; @@ -319,33 +702,65 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) break; case 4: /* NKS-VERSION */ - snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version); + snprintf (buffer, sizeof buffer, "%d", app->appversion); send_status_info (ctrl, table[idx].name, buffer, strlen (buffer), NULL, 0); break; case 5: /* CHV-STATUS */ { - /* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the - two global passwords followed by the two SigG passwords. - For the values, see the function get_chv_status. */ + /* Return the status for the the PINs as described in the + * table below. See the macros ISO7816_VERIFY_* for a list + * for each slot. The order is + * + * | idx | name | + * |-----+------------| + * | 0 | PW1.CH | + * | 1 | PW2.CH | + * | 2 | PW1.CH.SIG | + * | 3 | PW2.CH.SIG | + * + * See parse_pwidstr for details of the mapping. + */ int tmp[4]; /* We use a helper array so that we can control that there is - no superfluous application switch. Note that PW2.CH.SIG - really has the identifier 0x83 and not 0x82 as one would - expect. */ - tmp[0] = get_chv_status (app, 0, 0x00); - tmp[1] = get_chv_status (app, 0, 0x01); - tmp[2] = get_chv_status (app, 1, 0x81); - tmp[3] = get_chv_status (app, 1, 0x83); - snprintf (buffer, sizeof buffer, - "%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]); + * no superfluous application switches. */ + if (app->appversion == 15) + { + tmp[0] = get_chv_status (app, 0, 0x03); + tmp[1] = get_chv_status (app, 0, 0x04); + } + else + { + tmp[0] = get_chv_status (app, 0, 0x00); + tmp[1] = get_chv_status (app, 0, 0x01); + } + tmp[2] = get_chv_status (app, app->app_local->qes_app_id, 0x81); + if (app->appversion == 15) + tmp[3] = get_chv_status (app, app->app_local->qes_app_id, 0x82); + else + tmp[3] = get_chv_status (app, app->app_local->qes_app_id, 0x83); + snprintf (buffer, sizeof buffer, "%d %d %d %d", + tmp[0], tmp[1], tmp[2], tmp[3]); send_status_info (ctrl, table[idx].name, buffer, strlen (buffer), NULL, 0); } break; + case 6: /* $DISPSERIALNO */ + { + p = app_get_serialno (app); + p2 = get_dispserialno (app); + if (p && p2 && strcmp (p, p2)) + send_status_info (ctrl, table[idx].name, p2, strlen (p2), + NULL, (size_t)0); + else /* No abbreviated S/N or identical to the full full S/N. */ + err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */ + xfree (p); + xfree (p2); + } + break; default: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); @@ -356,37 +771,108 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } +/* Parse a keyref (NKS_*.*) and return the corresponding EF as an + * index into the filetable. With WANT_KEYPAIR set a keypair EF is + * requested; otherwise also cert EFs are returned. */ +static gpg_error_t +parse_keyref (app_t app, const char *keyref, int want_keypair, int *r_fididx) +{ + int nks_app_id, fid, idx; + + if (!ascii_strncasecmp (keyref, "NKS-NKS3.", 9)) + nks_app_id = NKS_APP_NKS; + else if (!ascii_strncasecmp (keyref, "NKS-ESIGN.", 10) + && (!want_keypair || app->app_local->qes_app_id == NKS_APP_ESIGN)) + nks_app_id = NKS_APP_ESIGN; + else if (!ascii_strncasecmp (keyref, "NKS-SIGG.", 9) + && (!want_keypair || app->app_local->qes_app_id == NKS_APP_SIGG)) + nks_app_id = NKS_APP_SIGG; + else if (!ascii_strncasecmp (keyref, "NKS-IDLM.", 9)) + nks_app_id = NKS_APP_IDLM; + else if (!ascii_strncasecmp (keyref, "NKS-DF01.", 9)) + nks_app_id = NKS_APP_NKS; + else + return gpg_error (GPG_ERR_INV_ID); -static void -do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) + keyref += nks_app_id == NKS_APP_ESIGN? 10 : 9; + + if (!hexdigitp (keyref) || !hexdigitp (keyref+1) + || !hexdigitp (keyref+2) || !hexdigitp (keyref+3) + || keyref[4]) + return gpg_error (GPG_ERR_INV_ID); + fid = xtoi_4 (keyref); + for (idx=0; filelist[idx].fid; idx++) + if (filelist[idx].fid == fid + && filelist[idx].nks_app_id == nks_app_id + && ((want_keypair && filelist[idx].iskeypair) + || (!want_keypair + && (filelist[idx].certtype || filelist[idx].iskeypair > 0)))) + break; + if (!filelist[idx].fid) + return gpg_error (GPG_ERR_NOT_FOUND); + + *r_fididx = idx; + return 0; +} + + +const char * +get_nks_tag (app_t app, int nks_app_id) { - gpg_error_t err; - char ct_buf[100], id_buf[100]; - int i; const char *tag; - const char *usage; - if (is_sigg) + if (nks_app_id == NKS_APP_ESIGN) + tag = "ESIGN"; + else if (nks_app_id == NKS_APP_SIGG) tag = "SIGG"; - else if (app->app_local->nks_version < 3) + else if (nks_app_id == NKS_APP_IDLM) + tag = "IDLM"; + else if (app->appversion < 3) tag = "DF01"; else tag = "NKS3"; + return tag; +} + +static void +set_usage_string (char usagebuf[5], int i) +{ + int usageidx = 0; + if (filelist[i].issignkey) + usagebuf[usageidx++] = 's'; + if (filelist[i].isauthkey) + usagebuf[usageidx++] = 'a'; + if (filelist[i].isencrkey) + usagebuf[usageidx++] = 'e'; + if (!usageidx) + usagebuf[usageidx++] = '-'; + usagebuf[usageidx] = 0; +} + +static void +do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, + int nks_app_id) +{ + gpg_error_t err; + char ct_buf[100], id_buf[100]; + int i; + const char *tag = get_nks_tag (app, nks_app_id); + /* Output information about all useful objects in the NKS application. */ for (i=0; filelist[i].fid; i++) { - if (filelist[i].nks_ver > app->app_local->nks_version) + if (filelist[i].nks_ver > app->appversion) continue; - if (!!filelist[i].is_sigg != !!is_sigg) + if (filelist[i].nks_app_id != nks_app_id) continue; - if (filelist[i].certtype && !(flags &1)) + if (filelist[i].certtype && !(flags & APP_LEARN_FLAG_KEYPAIRINFO)) { size_t len; - len = app_help_read_length_of_cert (app->slot, + len = app_help_read_length_of_cert (app_get_slot (app), filelist[i].fid, NULL); if (len) { @@ -405,8 +891,12 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) else if (filelist[i].iskeypair) { char gripstr[40+1]; + char usagebuf[5]; + char *algostr = NULL; - err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr); + err = keygripstr_from_pk_file (app, filelist[i].fid, + filelist[i].iskeypair, gripstr, + NULL, &algostr); if (err) log_error ("can't get keygrip from FID 0x%04X: %s\n", filelist[i].fid, gpg_strerror (err)); @@ -414,25 +904,18 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) { snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", tag, filelist[i].fid); - if (filelist[i].issignkey && filelist[i].isenckey) - usage = "sae"; - else if (filelist[i].issignkey) - usage = "sa"; - else if (filelist[i].isenckey) - usage = "e"; - else - usage = ""; - + set_usage_string (usagebuf, i); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, id_buf, strlen (id_buf), - usage, strlen (usage), + usagebuf, strlen (usagebuf), + "-", (size_t)1, + algostr, strlen (algostr), NULL, (size_t)0); } + xfree (algostr); } } - - } @@ -441,33 +924,32 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; - err = switch_application (app, 0); + do_getattr (app, ctrl, "CHV-STATUS"); + + err = switch_application (app, NKS_APP_NKS); if (err) return err; - do_learn_status_core (app, ctrl, flags, 0); + do_learn_status_core (app, ctrl, flags, app->app_local->active_nks_app); + + if (app->app_local->only_idlm) + return 0; /* ready. */ - err = switch_application (app, 1); + err = switch_application (app, app->app_local->qes_app_id); if (err) return 0; /* Silently ignore if we can't switch to SigG. */ - do_learn_status_core (app, ctrl, flags, 1); + do_learn_status_core (app, ctrl, flags, app->app_local->qes_app_id); 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. */ +/* Helper to read a certificate from the file FID. The function + * assumes that the application has already been selected. */ static gpg_error_t -do_readcert (app_t app, const char *certid, - unsigned char **cert, size_t *certlen) +readcert_from_ef (app_t app, int fid, unsigned char **cert, size_t *certlen) { - int i, fid; gpg_error_t err; unsigned char *buffer; const unsigned char *p; @@ -475,67 +957,32 @@ do_readcert (app_t app, const char *certid, int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca = 0; - int is_sigg = 0; *cert = NULL; *certlen = 0; - if (!strncmp (certid, "NKS-NKS3.", 9)) - ; - else if (!strncmp (certid, "NKS-DF01.", 9)) - ; - else if (!strncmp (certid, "NKS-SIGG.", 9)) - is_sigg = 1; - else - return gpg_error (GPG_ERR_INV_ID); - - err = switch_application (app, is_sigg); - if (err) - return err; - - certid += 9; - if (!hexdigitp (certid) || !hexdigitp (certid+1) - || !hexdigitp (certid+2) || !hexdigitp (certid+3) - || certid[4]) - return gpg_error (GPG_ERR_INV_ID); - fid = xtoi_4 (certid); - for (i=0; filelist[i].fid; i++) - if ((filelist[i].certtype || filelist[i].iskeypair) - && filelist[i].fid == fid) - break; - if (!filelist[i].fid) - return gpg_error (GPG_ERR_NOT_FOUND); - - /* If the requested objects is a plain public key, redirect it to - the corresponding certificate. The whole system is a bit messy - because we sometime use the key directly or let the caller - retrieve the key from the certificate. The rationale for - that is to support not-yet stored certificates. */ - if (filelist[i].iskeypair) - fid = filelist[i].iskeypair; - - /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ - err = iso7816_select_file (app->slot, fid, 0); + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { - log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err)); + log_error ("nks: error selecting FID 0x%04X: %s\n", + fid, gpg_strerror (err)); return err; } - err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen); + err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { - log_error ("error reading certificate from FID 0x%04X: %s\n", + log_error ("nks: error reading certificate from FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } - if (!buflen || *buffer == 0xff) + if (!buflen || *buffer == 0xff || all_zero_p (buffer, buflen)) { - log_info ("no certificate contained in FID 0x%04X\n", fid); + log_info ("nks: no certificate contained in FID 0x%04X\n", fid); err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } @@ -554,7 +1001,7 @@ do_readcert (app_t app, const char *certid, else return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; - assert (totobjlen <= buflen); + log_assert (totobjlen <= buflen); err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); @@ -585,7 +1032,7 @@ do_readcert (app_t app, const char *certid, if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; - assert (save_p + totobjlen <= buffer + buflen); + log_assert (save_p + totobjlen <= buffer + buflen); memmove (buffer, save_p, totobjlen); } @@ -599,12 +1046,178 @@ do_readcert (app_t app, const char *certid, } +/* + * Iterate over FILELIST, supporting two use cases: + * + * (1) With WANT_KEYGRIPSTR=<GRIP>, finding matching entry. + * (2) With WANT_KEYGRIPSTR=NULL, listing entries + * by CAPABILITY (possibly == 0, for all entries). + * + * Caller supplies an array KEYGRIPSTR. + * Caller should start *IDX_P == -1, and keep the index value in IDX_P. + * + * Returns 0 on success, otherwise returns error value. + * + * When all entries are tried, returns GPG_ERR_NOT_FOUND for the use + * case of (1). Returns GPG_ERR_TRUE for the use case of (2). + */ +static gpg_error_t +iterate_over_filelist (app_t app, const char *want_keygripstr, int capability, + char keygripstr[2*KEYGRIP_LEN+1], int *idx_p) +{ + gpg_error_t err; + int idx = *idx_p; + + for (idx++; filelist[idx].fid; idx++) + { + if (filelist[idx].nks_ver > app->appversion) + continue; /* EF not support by this card version. */ + + if (!filelist[idx].iskeypair) + continue; /* Skip - We are only interested in keypairs. */ + + if (app->app_local->only_idlm) + { + /* IDLM cards have no other applications we want to switch + * to. We skip all EFs which are not known for IDLM. */ + if (filelist[idx].nks_app_id != NKS_APP_IDLM) + continue; + } + else + { + /* Skip all EFs which are not for NKS or the card's + * implementation for a qualified electoric signature (QES) + * which is either the old SIGG or the newer ESIGN. */ + if (filelist[idx].nks_app_id != NKS_APP_NKS + && filelist[idx].nks_app_id != app->app_local->qes_app_id) + continue; + + /* Switch if needed. Note that the filelist should be + * sorted to avoid unnecessary switches. */ + err = switch_application (app, filelist[idx].nks_app_id); + if (err) + { + *idx_p = idx; + return err; + } + } + + /* Get the keygrip from the EF. Note that this functions + * consults the cache to avoid computing the keygrip again. */ + err = keygripstr_from_pk_file (app, filelist[idx].fid, + filelist[idx].iskeypair, keygripstr, + NULL, NULL); + if (err) + { + log_error ("can't get keygrip from FID 0x%04X: %s\n", + filelist[idx].fid, gpg_strerror (err)); + continue; + } + + if (want_keygripstr) + { + /* If the keygrip matches the requested one we are ready. */ + if (!strcmp (keygripstr, want_keygripstr)) + { + /* Found */ + *idx_p = idx; + return 0; + } + } + else /* No keygrip requested - list all . */ + { + /* If a capability has been requested return only keys with + * that capability. */ + if (capability == GCRY_PK_USAGE_SIGN) + { + if (!filelist[idx].issignkey) + continue; + } + if (capability == GCRY_PK_USAGE_ENCR) + { + if (!filelist[idx].isencrkey) + continue; + } + if (capability == GCRY_PK_USAGE_AUTH) + { + if (!filelist[idx].isauthkey) + continue; + } + + /* Found. Return but save the last idenx of the loop. */ + *idx_p = idx; + return 0; + } + } + + if (!want_keygripstr) + err = gpg_error (GPG_ERR_TRUE); + else + err = gpg_error (GPG_ERR_NOT_FOUND); + + return err; +} + + +/* Read the certificate with id CERTID (as returned by learn_status in + the CERTINFO status lines) and return it in the freshly allocated + buffer put into CERT and the length of the certificate put into + CERTLEN. */ +static gpg_error_t +do_readcert (app_t app, const char *certid, + unsigned char **cert, size_t *certlen) +{ + int idx, fid; + gpg_error_t err; + + *cert = NULL; + *certlen = 0; + + /* Handle the case with KEYGRIP. We got a keygrip if the string has + * a length of 40 and does not start with an N as in NKS-* */ + if (certid[0] != 'N' && strlen (certid) == 40) + { + char keygripstr[2*KEYGRIP_LEN+1]; + + idx = -1; + err = iterate_over_filelist (app, certid, 0, keygripstr, &idx); + if (err) + return err; + + /* Switching is not required here because iterate_over_filelist + * has already done that. */ + } + else /* This is not a keygrip. */ + { + err = parse_keyref (app, certid, 0, &idx); + if (err) + return err; + + err = switch_application (app, filelist[idx].nks_app_id); + if (err) + return err; + } + + /* If the requested objects is a plain public key, redirect it to + the corresponding certificate. The whole system is a bit messy + because we sometime use the key directly or let the caller + retrieve the key from the certificate. The rationale for + that is to support not-yet stored certificates. */ + if (filelist[idx].iskeypair > 0) + fid = filelist[idx].iskeypair; + else + fid = filelist[idx].fid; + + return readcert_from_ef (app, fid, cert, certlen); +} + + /* Handle the READKEY command. On success a canonical encoded S-expression with the public key will get stored at PK and its length at PKLEN; the caller must release that buffer. On error PK and PKLEN are not changed and an error code is returned. As of now this function is only useful for the internal authentication key. - Other keys are automagically retrieved via by means of the + Other keys are automagically retrieved by means of the certificate parsing code in commands.c:cmd_readkey. For internal use PK and PKLEN may be NULL to just check for an existing key. */ static gpg_error_t @@ -612,9 +1225,13 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, unsigned char **pk, size_t *pklen) { gpg_error_t err; - unsigned char *buffer[2]; - size_t buflen[2]; - unsigned short path[1] = { 0x4500 }; + unsigned char *dummy_pk = NULL; + size_t dummy_pklen = 0; + + if (!pk) + pk = &dummy_pk; + if (!pklen) + pklen = &dummy_pklen; (void)ctrl; @@ -622,45 +1239,198 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, return GPG_ERR_NOT_SUPPORTED; /* We use a generic name to retrieve PK.AUT.IFD-SPK. */ - if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) - ; + if (!strcmp (keyid, "$IFDAUTHKEY") && app->appversion >= 3) + { + unsigned short path[1] = { 0x4500 }; + unsigned char *buffer[2]; + size_t buflen[2]; + + /* Access the KEYD file which is always in the master directory. */ + err = iso7816_select_path (app_get_slot (app), path, DIM (path), 0); + if (err) + goto leave; + /* Due to the above select we need to re-select our application. */ + app->app_local->need_app_select = 1; + /* Get the two records. */ + err = iso7816_read_record (app_get_slot (app), 5, 1, 0, + &buffer[0], &buflen[0]); + if (err) + goto leave; + if (all_zero_p (buffer[0], buflen[0])) + { + xfree (buffer[0]); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + err = iso7816_read_record (app_get_slot (app), 6, 1, 0, + &buffer[1], &buflen[1]); + if (err) + { + xfree (buffer[0]); + goto leave; + } + + if ((flags & APP_READKEY_FLAG_INFO)) + { + /* FIXME */ + } + + if (pk && pklen && pk != &dummy_pk) + { + *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], + buffer[1], buflen[1], + pklen); + if (!*pk) + err = gpg_error_from_syserror (); + } + + xfree (buffer[0]); + xfree (buffer[1]); + } + else if (keyid[0] != 'N' && strlen (keyid) == 40) + { + char keygripstr[2*KEYGRIP_LEN+1]; + int i = -1; + + err = iterate_over_filelist (app, keyid, 0, keygripstr, &i); + if (err) + goto leave; + + err = pubkey_from_pk_file (app, filelist[i].fid, filelist[i].iskeypair, + pk, pklen); + if (!err && (flags & APP_READKEY_FLAG_INFO)) + { + char *algostr; + char usagebuf[5]; + char id_buf[100]; + + if (app_help_get_keygrip_string_pk (*pk, *pklen, NULL, NULL, NULL, + &algostr)) + algostr = NULL; /* Ooops. */ + + snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", + get_nks_tag (app, filelist[i].nks_app_id), + filelist[i].fid); + set_usage_string (usagebuf, i); + send_status_info (ctrl, "KEYPAIRINFO", + keygripstr, strlen (keygripstr), + id_buf, strlen (id_buf), + usagebuf, strlen (usagebuf), + "-", (size_t)1, + algostr, strlen (algostr?algostr:""), + NULL, (size_t)0); + xfree (algostr); + } + } + else if (!strncmp (keyid, "NKS-IDLM.", 9)) + { + keyid += 9; + if (!hexdigitp (keyid) || !hexdigitp (keyid+1) + || !hexdigitp (keyid+2) || !hexdigitp (keyid+3) + || keyid[4]) + { + err = gpg_error (GPG_ERR_INV_ID); + goto leave; + } + + err = pubkey_from_pk_file (app, xtoi_4 (keyid), -1, pk, pklen); + /* FIXME: Implement KEYPAIRINFO. */ + } else /* Return the error code expected by cmd_readkey. */ - return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + leave: + xfree (dummy_pk); + return err; +} + + +/* Write the certificate (CERT,CERTLEN) to the card at CERTREFSTR. + * CERTREFSTR is of the form "NKS_<yyy>.<four_hexdigit_keyref>". */ +static gpg_error_t +do_writecert (app_t app, ctrl_t ctrl, + const char *certid, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *cert, size_t certlen) +{ + gpg_error_t err; + int i, fid, pwid; + int nks_app_id, tmp_app_id; + const char *desc; + + (void)ctrl; - /* Access the KEYD file which is always in the master directory. */ - err = iso7816_select_path (app_get_slot (app), path, DIM (path), 0); + if (!strncmp (certid, "NKS-NKS3.", 9)) + nks_app_id = NKS_APP_NKS; + else if (!strncmp (certid, "NKS-ESIGN.", 10)) + nks_app_id = NKS_APP_ESIGN; + else if (!strncmp (certid, "NKS-SIGG.", 9)) + nks_app_id = NKS_APP_SIGG; + else if (!strncmp (certid, "NKS-DF01.", 9)) + nks_app_id = NKS_APP_NKS; + else if (!strncmp (certid, "NKS-IDLM.", 9)) + nks_app_id = NKS_APP_IDLM; + else + return gpg_error (GPG_ERR_INV_ID); + certid += nks_app_id == NKS_APP_ESIGN? 10 : 9; + + err = switch_application (app, nks_app_id); if (err) return err; - /* Due to the above select we need to re-select our application. */ - app->app_local->need_app_select = 1; - /* Get the two records. */ - err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]); + + 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 > 0) + && filelist[i].nks_app_id == nks_app_id + && 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. This makes it easier for the user + * to figure out which CERTID to use. For example gpg-card shows + * the id of the key and not of the certificate. */ + if (filelist[i].iskeypair > 0) + fid = filelist[i].iskeypair; + + /* We have no selective flush mechanism and given the rare use of + * writecert it won't harm to flush the entire cache. */ + flush_fid_cache (app); + + /* The certificates we support all require PW1.CH. Note that we + * check that the nks_app_id matches which sorts out CERTID values + * which are subkeys to a different nks_app_id. */ + desc = parse_pwidstr (app, "PW1.CH", 0, &tmp_app_id, &pwid); + if (!desc || tmp_app_id != nks_app_id) + return gpg_error (GPG_ERR_INV_ID); + err = verify_pin (app, pwid, desc, pincb, pincb_arg); if (err) return err; - if (all_zero_p (buffer[0], buflen[0])) - { - xfree (buffer[0]); - return gpg_error (GPG_ERR_NOT_FOUND); - } - err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]); + + /* Select the file and write the certificate. */ + err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) { - xfree (buffer[0]); + log_error ("nks: error selecting FID 0x%04X: %s\n", + fid, gpg_strerror (err)); return err; } - if (pk && pklen) + err = iso7816_update_binary (app_get_slot (app), 1, 0, cert, certlen); + if (err) { - *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0], - buffer[1], buflen[1], - pklen); - if (!*pk) - err = gpg_error_from_syserror (); + log_error ("nks: error updating certificate at FID 0x%04X: %s\n", + fid, gpg_strerror (err)); + return err; } - xfree (buffer[0]); - xfree (buffer[1]); - return err; + return 0; } @@ -688,7 +1458,7 @@ do_writekey (app_t app, ctrl_t ctrl, (void)pincb; (void)pincb_arg; - if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) + if (!strcmp (keyid, "$IFDAUTHKEY") && app->appversion >= 3) ; else return gpg_error (GPG_ERR_INV_ID); @@ -726,6 +1496,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* goto leave; */ /* Send the MSE:Store_Public_Key. */ + /* We will need to clear the cache here. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* mse = xtrymalloc (1000); */ @@ -746,7 +1517,7 @@ do_writekey (app_t app, ctrl_t ctrl, /* mse[10] = 0x82; /\* RSA public exponent of up to 4 bytes. *\/ */ /* mse[12] = rsa_e_len; */ /* memcpy (mse+12, rsa_e, rsa_e_len); */ -/* err = iso7816_manage_security_env (app->slot, 0x81, 0xB6, */ +/* err = iso7816_manage_security_env (app_get_slot (app), 0x81, 0xB6, */ /* mse, sizeof mse); */ leave: @@ -754,6 +1525,63 @@ do_writekey (app_t app, ctrl_t ctrl, } +/* Return an allocated string to be used as prompt. Returns NULL on + * malloc error. */ +static char * +make_prompt (app_t app, int remaining, const char *firstline, + const char *extraline) +{ + char *serial, *tmpbuf, *result; + + serial = get_dispserialno (app); + + /* TRANSLATORS: Put a \x1f right before a colon. This can be + * used by pinentry to nicely align the names and values. Keep + * the %s at the start and end of the string. */ + result = xtryasprintf (_("%s" + "Number\x1f: %s%%0A" + "Holder\x1f: %s" + "%s"), + "\x1e", + serial, + "", + ""); + xfree (serial); + if (!result) + return NULL; /* Out of core. */ + + /* Append a "remaining attempts" info if needed. */ + if (remaining != -1 && remaining < 3) + { + char *rembuf; + + /* TRANSLATORS: This is the number of remaining attempts to + * enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */ + rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining); + if (rembuf) + { + tmpbuf = strconcat (firstline, "%0A%0A", result, + "%0A%0A", rembuf, NULL); + xfree (rembuf); + } + else + tmpbuf = NULL; + xfree (result); + result = tmpbuf; + } + else + { + tmpbuf = strconcat (firstline, "%0A%0A", result, + extraline? "%0A%0A":"", extraline, + NULL); + xfree (result); + result = tmpbuf; + } + + return result; +} + + static gpg_error_t basic_pin_checks (const char *pinvalue, int minlen, int maxlen) { @@ -777,21 +1605,67 @@ verify_pin (app_t app, int pwid, const char *desc, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { - pininfo_t pininfo; int rc; + pininfo_t pininfo; + char *prompt; + const char *extrapromptline = NULL; + int remaining, nullpin; if (!desc) - desc = "PIN"; + desc = "||PIN"; memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; - pininfo.minlen = 6; - pininfo.maxlen = 16; + + /* FIXME: TCOS allows to read the min. and max. values - do this. */ + if (app->appversion == 15) + { + if (app->app_local->active_nks_app == NKS_APP_NKS && pwid == 0x03) + pininfo.minlen = 6; + else if (app->app_local->active_nks_app == NKS_APP_ESIGN && pwid == 0x81) + pininfo.minlen = 6; + else + pininfo.minlen = 8; + pininfo.maxlen = 24; + } + else if (app->app_local->active_nks_app == NKS_APP_IDLM) + { + if (pwid == 0x00) + pininfo.minlen = 6; + else + pininfo.minlen = 8; + pininfo.maxlen = 24; + } + else + { + /* For NKS3 we used these fixed values; let's keep this. */ + pininfo.minlen = 6; + pininfo.maxlen = 16; + } + + remaining = iso7816_verify_status (app_get_slot (app), pwid); + nullpin = (remaining == ISO7816_VERIFY_NULLPIN); + if (remaining < 0) + remaining = -1; /* We don't care about the concrete error. */ + if (remaining < 3) + { + if (remaining >= 0) + log_info ("nks: PIN has %d attempts left\n", remaining); + } + + if (nullpin) + { + log_info ("nks: The NullPIN for PIN 0x%02x has not yet been changed\n", + pwid); + extrapromptline = _("Note: PIN has not yet been enabled."); + } if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { - rc = pincb (pincb_arg, desc, NULL); + prompt = make_prompt (app, remaining, desc, extrapromptline); + rc = pincb (pincb_arg, prompt, NULL); + xfree (prompt); if (rc) { log_info (_("PIN callback returned error: %s\n"), @@ -799,14 +1673,16 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify_kp (app->slot, pwid, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), pwid, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else { char *pinvalue; - rc = pincb (pincb_arg, desc, &pinvalue); + prompt = make_prompt (app, remaining, desc, extrapromptline); + rc = pincb (pincb_arg, prompt, &pinvalue); + xfree (prompt); if (rc) { log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); @@ -820,7 +1696,8 @@ verify_pin (app_t app, int pwid, const char *desc, return rc; } - rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue)); + rc = iso7816_verify (app_get_slot (app), pwid, + pinvalue, strlen (pinvalue)); xfree (pinvalue); } @@ -854,70 +1731,143 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, 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 is_sigg = 0; - int fid; + static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */ + { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, + 0x1C }; + static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */ + { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20 }; + static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */ + { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, + 0x00, 0x04, 0x30 }; + static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */ + { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, + 0x00, 0x04, 0x40 }; + gpg_error_t err; + int idx; + int pwid; unsigned char kid; unsigned char data[83]; /* Must be large enough for a SHA-1 digest + the largest OID prefix. */ size_t datalen; + int algo; + unsigned int digestlen; /* Length of the hash. */ + unsigned char oidbuf[64]; + size_t oidbuflen; (void)ctrl; - if (!keyidstr || !*keyidstr) - return gpg_error (GPG_ERR_INV_VALUE); switch (indatalen) { - case 16: case 20: case 35: case 47: case 51: case 67: case 83: break; - default: return gpg_error (GPG_ERR_INV_VALUE); + case 20: /* plain SHA-1 or RMD160 digest */ + case 28: /* plain SHA-224 digest */ + case 32: /* plain SHA-256 digest */ + case 48: /* plain SHA-384 digest */ + case 64: /* plain SHA-512 digest */ + case 35: /* ASN.1 encoded SHA-1 or RMD160 digest */ + case 47: /* ASN.1 encoded SHA-224 digest */ + case 51: /* ASN.1 encoded SHA-256 digest */ + case 67: /* ASN.1 encoded SHA-384 digest */ + case 83: /* ASN.1 encoded SHA-512 digest */ + break; + default: + log_info ("nks: invalid length of input data: %zu\n", indatalen); + return gpg_error (GPG_ERR_INV_VALUE); } - /* Check that the provided ID is valid. This is not really needed - but we do it to enforce correct usage by the caller. */ - if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) - ; - else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) - ; - else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) - is_sigg = 1; - else - return gpg_error (GPG_ERR_INV_ID); - keyidstr += 9; - - rc = switch_application (app, is_sigg); - if (rc) - return rc; + err = find_fid_by_keyref (app, keyidstr, &idx, &algo); + if (err) + return err; - if (is_sigg && app->app_local->sigg_is_msig) + if (app->app_local->active_nks_app == NKS_APP_SIGG + && app->app_local->sigg_is_msig) { log_info ("mass signature cards are not allowed\n"); return gpg_error (GPG_ERR_NOT_SUPPORTED); } - 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); - kid = filelist[i].kid; - - /* Prepare the DER object from INDATA. */ - if (app->app_local->nks_version > 2 && (indatalen == 35 - || indatalen == 47 - || indatalen == 51 - || indatalen == 67 - || indatalen == 83)) - { - /* The caller send data matching the length of the ASN.1 encoded - hash for SHA-{1,224,256,384,512}. Assume that is okay. */ - assert (indatalen <= sizeof data); + if (!filelist[idx].issignkey) + { + log_debug ("key %s is not a signing key\n", keyidstr); + return gpg_error (GPG_ERR_INV_ID); + } + + kid = filelist[idx].kid; + + digestlen = gcry_md_get_algo_dlen (hashalgo); + + /* Prepare the input object from INDATA. */ + if (algo == GCRY_PK_ECC) + { + if (digestlen != 32 && digestlen != 48 && digestlen != 64) + { + log_error ("nks: ECC signing not possible: dlen=%u\n", digestlen); + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + + if (indatalen == digestlen) + { + /* Already prepared. */ + datalen = indatalen; + log_assert (datalen <= sizeof data); + memcpy (data, indata, datalen); + } + else if (indatalen > digestlen) + { + /* Assume a PKCS#1 prefix and remove it. */ + oidbuflen = sizeof oidbuf; + err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen); + if (err) + { + log_error ("nks: no OID for hash algo %d\n", hashalgo); + return gpg_error (GPG_ERR_INTERNAL); + } + if (indatalen != oidbuflen + digestlen + || memcmp (indata, oidbuf, oidbuflen)) + { + log_error ("nks: input data too long for ECC: len=%zu\n", + indatalen); + return gpg_error (GPG_ERR_INV_VALUE); + } + datalen = indatalen - oidbuflen; + log_assert (datalen <= sizeof data); + memcpy (data, (const char*)indata + oidbuflen, datalen); + } + else + { + log_error ("nks: input data too short for ECC: len=%zu\n", + indatalen); + return gpg_error (GPG_ERR_INV_VALUE); + } + } + else if (app->appversion > 2 && (indatalen == 35 + || indatalen == 47 + || indatalen == 51 + || indatalen == 67 + || indatalen == 83)) + { + /* Verify that the caller has sent a proper ASN.1 encoded hash + for RMD160 or SHA-{1,224,256,384,512}. */ +#define X(algo,prefix,plaindigestlen) \ + if (hashalgo == (algo) \ + && indatalen == sizeof prefix + (plaindigestlen) \ + && !memcmp (indata, prefix, sizeof prefix)) \ + ; + X(GCRY_MD_RMD160, rmd160_prefix, 20) + else X(GCRY_MD_SHA1, sha1_prefix, 20) + else X(GCRY_MD_SHA224, sha224_prefix, 28) + else X(GCRY_MD_SHA256, sha256_prefix, 32) + else X(GCRY_MD_SHA384, sha384_prefix, 48) + else X(GCRY_MD_SHA512, sha512_prefix, 64) + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); +#undef X + + log_assert (indatalen <= sizeof data); memcpy (data, indata, indatalen); datalen = indatalen; } @@ -934,43 +1884,67 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, memcpy (data, indata, indatalen); datalen = 35; } - else if (indatalen == 20) - { - 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); - datalen = 35; + /* Concatenate prefix and digest. + * Note that the X macro creates an "else if". Ugly - I know. */ +#define X(algo,prefix,plaindigestlen) \ + if ((hashalgo == (algo)) && (indatalen == (plaindigestlen))) \ + { \ + datalen = sizeof prefix + indatalen; \ + log_assert (datalen <= sizeof data); \ + memcpy (data, prefix, sizeof prefix); \ + memcpy (data + sizeof prefix, indata, indatalen); \ } + else X(GCRY_MD_RMD160, rmd160_prefix, 20) + else X(GCRY_MD_SHA1, sha1_prefix, 20) + else X(GCRY_MD_SHA224, sha224_prefix, 28) + else X(GCRY_MD_SHA256, sha256_prefix, 32) + else X(GCRY_MD_SHA384, sha384_prefix, 48) + else X(GCRY_MD_SHA512, sha512_prefix, 64) else return gpg_error (GPG_ERR_INV_VALUE); +#undef X - - /* Send an MSE for PSO:Computer_Signature. */ - if (app->app_local->nks_version > 2) + /* Send an MSE for PSO:Compute_Signature. */ + if (app->appversion > 2 && app->app_local->active_nks_app != NKS_APP_ESIGN) { unsigned char mse[6]; + unsigned int mselen; - mse[0] = 0x80; /* Algorithm reference. */ - mse[1] = 1; - mse[2] = 2; /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check. */ - mse[3] = 0x84; /* Private key reference. */ - mse[4] = 1; - mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6, - mse, sizeof mse); + if (algo == GCRY_PK_ECC) + { + mse[0] = 0x84; /* Private key reference. */ + mse[1] = 1; + mse[2] = kid; + mselen = 3; + } + else /* RSA */ + { + mse[0] = 0x80; /* Algorithm reference. */ + mse[1] = 1; + mse[2] = 2; /* Card does pkcs#1 v1.5 padding, no ASN.1 check. */ + mse[3] = 0x84; /* Private key reference. */ + mse[4] = 1; + mse[5] = kid; + mselen = 6; + } + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, + mse, mselen); } - /* Verify using PW1.CH. */ - if (!rc) - rc = verify_pin (app, 0, NULL, pincb, pincb_arg); + + if (app->app_local->active_nks_app == NKS_APP_ESIGN) + pwid = 0x81; + else if (app->appversion == 15) + pwid = 0x03; + else + pwid = 0x00; + + if (!err) + err = verify_pin (app, pwid, NULL, pincb, pincb_arg); /* Compute the signature. */ - if (!rc) - rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0, - outdata, outdatalen); - return rc; + if (!err) + err = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, + outdata, outdatalen); + return err; } @@ -986,48 +1960,53 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { - int rc, i; - int is_sigg = 0; - int fid; + gpg_error_t err; + int idx; int kid; + int algo; + int pwid; + int padind; + int extended_mode; (void)ctrl; (void)r_info; - if (!keyidstr || !*keyidstr || !indatalen) + if (!indatalen) return gpg_error (GPG_ERR_INV_VALUE); - /* Check that the provided ID is valid. This is not really needed - but we do it to enforce correct usage by the caller. */ - if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) - ; - else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) - ; - else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) - is_sigg = 1; - else - return gpg_error (GPG_ERR_INV_ID); - keyidstr += 9; - - rc = switch_application (app, is_sigg); - if (rc) - return rc; + err = find_fid_by_keyref (app, keyidstr, &idx, &algo); + if (err) + return err; - if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) - || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) - || keyidstr[4]) + if (!filelist[idx].isencrkey) 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); - kid = filelist[i].kid; - if (app->app_local->nks_version > 2) + kid = filelist[idx].kid; + + if (app->appversion <= 2) + { + static const unsigned char mse[] = + { + 0x80, 1, 0x10, /* Select algorithm RSA. */ + 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ + }; + err = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8, + mse, sizeof mse); + extended_mode = 0; + padind = 0x81; + } + else if (algo == GCRY_PK_ECC) + { + unsigned char mse[3]; + mse[0] = 0x84; /* Private key reference. */ + mse[1] = 1; + mse[2] = kid; + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, + mse, sizeof mse); + extended_mode = 0; + padind = 0x00; + } + else { unsigned char mse[6]; mse[0] = 0x80; /* Algorithm reference. */ @@ -1036,58 +2015,77 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; - rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8, - mse, sizeof mse); + err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, + mse, sizeof mse); + extended_mode = 1; + padind = 0x81; } - else + if (err) { - static const unsigned char mse[] = - { - 0x80, 1, 0x10, /* Select algorithm RSA. */ - 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ - }; - rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8, - mse, sizeof mse); - + log_error ("nks: MSE failed: %s\n", gpg_strerror (err)); + goto leave; } - if (!rc) - rc = verify_pin (app, 0, NULL, pincb, pincb_arg); + /* We use the Global PIN 1 */ + if (app->appversion == 15) + pwid = 0x03; + else + pwid = 0x00; - /* Note that we need to use extended length APDUs for TCOS 3 cards. - Command chaining does not work. */ - if (!rc) - rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0, - indata, indatalen, 0, 0x81, - outdata, outdatalen); - return rc; + err = verify_pin (app, pwid, NULL, pincb, pincb_arg); + if (err) + goto leave; + + err = iso7816_decipher (app_get_slot (app), extended_mode, + indata, indatalen, 0, padind, + outdata, outdatalen); + + leave: + return err; } /* Parse a password ID string. Returns NULL on error or a string - suitable as passphrase prompt on success. On success stores the - reference value for the password at R_PWID and a flag indicating - that the SigG application is to be used at R_SIGG. If NEW_MODE is - true, the returned description is suitable for a new Password. - Supported values for PWIDSTR are: - - PW1.CH - Global password 1 - PW2.CH - Global password 2 - PW1.CH.SIG - SigG password 1 - PW2.CH.SIG - SigG password 2 + * suitable as passphrase prompt on success. On success stores the + * reference value for the password at R_PWID and a flag indicating + * which app is to be used at R_NKS_APP_ID. If NEW_MODE is true, the + * returned description is suitable for a new password. Here is a + * take mapping the PWIDSTR to the used PWIDs: + * + * | pwidstr | | NKS3 | NKS15 | IDKEY1 | + * |------------+--------------+------+-------+--------| + * | PW1.CH | Global PIN 1 | 0x00 | 0x03 | 0x00 | + * | PW2.CH | Global PIN 2 | 0x01 | 0x04 | 0x01 | + * | PW1.CH.SIG | SigG PIN 1 | 0x81 | 0x81 | - | + * | PW2.CH.SIG | SigG PIN 2 | 0x83 | 0x82 | - | + * + * The names for PWIDSTR are taken from the NKS3 specs; the specs of + * other cards use different names but we keep using the. PIN1 can be + * used to unlock PIN2 and vice versa; for consistence with other + * cards we name PIN2 a "PUK". The IDKEY card also features a Card + * Reset Key (CR Key 0x01) which can also be used to reset PIN1. + * + * For testing it is possible to specify the PWID directly; the + * prompts are then not very descriptive: + * + * NKS.0xnn - Switch to NKS and select id 0xnn + * SIGG.0xnn - Switch to SigG and select id 0xnn + * ESIGN.0xnn - Switch to ESIGN and select id 0xnn */ static const char * -parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid) +parse_pwidstr (app_t app, const char *pwidstr, int new_mode, + int *r_nks_app_id, int *r_pwid) { const char *desc; + int nks15 = app->appversion == 15; if (!pwidstr) desc = NULL; else if (!strcmp (pwidstr, "PW1.CH")) { - *r_sigg = 0; - *r_pwid = 0x00; + *r_nks_app_id = NKS_APP_NKS; + *r_pwid = nks15? 0x03 : 0x00; /* TRANSLATORS: Do not translate the "|*|" prefixes but keep them verbatim at the start of the string. */ desc = (new_mode @@ -1096,33 +2094,74 @@ parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid) } else if (!strcmp (pwidstr, "PW2.CH")) { - *r_pwid = 0x01; + *r_nks_app_id = NKS_APP_NKS; + *r_pwid = nks15? 0x04 : 0x01; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the standard keys.") : _("|P|Please enter the PIN Unblocking Code (PUK) " "for the standard keys.")); } - else if (!strcmp (pwidstr, "PW1.CH.SIG")) + else if (!strcmp (pwidstr, "PW1.CH.SIG") && !app->app_local->only_idlm) { + *r_nks_app_id = app->app_local->qes_app_id; *r_pwid = 0x81; - *r_sigg = 1; desc = (new_mode ? _("|N|Please enter a new PIN for the key to create " "qualified signatures.") : _("||Please enter the PIN for the key to create " "qualified signatures.")); } - else if (!strcmp (pwidstr, "PW2.CH.SIG")) + else if (!strcmp (pwidstr, "PW2.CH.SIG") && !app->app_local->only_idlm) { - *r_pwid = 0x83; /* Yes, that is 83 and not 82. */ - *r_sigg = 1; + *r_nks_app_id = app->app_local->qes_app_id; + *r_pwid = nks15? 0x82 : 0x83; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the key to create qualified signatures.") : _("|P|Please enter the PIN Unblocking Code (PUK) " "for the key to create qualified signatures.")); } + else if (!strncmp (pwidstr, "NKS.0x", 6) + && hexdigitp (pwidstr+6) && hexdigitp (pwidstr+7) && !pwidstr[8]) + { + /* Hack to help debugging. */ + *r_nks_app_id = NKS_APP_NKS; + *r_pwid = xtoi_2 (pwidstr+6); + desc = (new_mode + ? "|N|Please enter a new PIN for the given NKS pwid" + : "||Please enter the PIN for the given NKS pwid" ); + } + else if (!strncmp (pwidstr, "SIGG.0x", 7) + && hexdigitp (pwidstr+7) && hexdigitp (pwidstr+8) && !pwidstr[9]) + { + /* Hack to help debugging. */ + *r_nks_app_id = NKS_APP_SIGG; + *r_pwid = xtoi_2 (pwidstr+7); + desc = (new_mode + ? "|N|Please enter a new PIN for the given SIGG pwid" + : "||Please enter the PIN for the given SIGG pwid" ); + } + else if (!strncmp (pwidstr, "ESIGN.0x", 8) + && hexdigitp (pwidstr+8) && hexdigitp (pwidstr+9) && !pwidstr[10]) + { + /* Hack to help debugging. */ + *r_nks_app_id = NKS_APP_ESIGN; + *r_pwid = xtoi_2 (pwidstr+8); + desc = (new_mode + ? "|N|Please enter a new PIN for the given ESIGN pwid" + : "||Please enter the PIN for the given ESIGN pwid" ); + } + else if (!strncmp (pwidstr, "IDLM.0x", 7) + && hexdigitp (pwidstr+7) && hexdigitp (pwidstr+8) && !pwidstr[9]) + { + /* Hack to help debugging. */ + *r_nks_app_id = NKS_APP_IDLM; + *r_pwid = xtoi_2 (pwidstr+7); + desc = (new_mode + ? "|N|Please enter a new PIN for the given IDLM pwid" + : "||Please enter the PIN for the given IDLM pwid" ); + } else { *r_pwid = 0; /* Only to avoid gcc warning in calling function. */ @@ -1146,10 +2185,12 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, char *oldpin = NULL; size_t newpinlen; size_t oldpinlen; - int is_sigg; + int nks_app_id; const char *newdesc; int pwid; pininfo_t pininfo; + int remaining; + char *prompt; (void)ctrl; @@ -1159,14 +2200,14 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, pininfo.minlen = 6; pininfo.maxlen = 16; - newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &pwid); + newdesc = parse_pwidstr (app, pwidstr, 1, &nks_app_id, &pwid); if (!newdesc) return gpg_error (GPG_ERR_INV_ID); if ((flags & APP_CHANGE_FLAG_CLEAR)) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); - err = switch_application (app, is_sigg); + err = switch_application (app, nks_app_id); if (err) return err; @@ -1180,7 +2221,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, err = gpg_error_from_syserror (); goto leave; } - oldpinlen = 6; + if (app->appversion == 15) + { + memset (oldpin, '0', 5); + oldpinlen = 5; /* 5 ascii zeroes. */ + } + else + { + oldpinlen = 6; /* 6 binary Nuls. */ + } } else { @@ -1205,14 +2254,27 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, err = gpg_error (GPG_ERR_BUG); goto leave; } - desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2); + desc = parse_pwidstr (app, altpwidstr, 0, &dummy1, &dummy2); + remaining = iso7816_verify_status (app_get_slot (app), dummy2); } else { /* Regular change mode: Ask for the old PIN. */ - desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2); + desc = parse_pwidstr (app, pwidstr, 0, &dummy1, &dummy2); + remaining = iso7816_verify_status (app_get_slot (app), pwid); } - err = pincb (pincb_arg, desc, &oldpin); + + if (remaining < 0) + remaining = -1; /* We don't care about the concrete error. */ + if (remaining < 3) + { + if (remaining >= 0) + log_info ("nks: PIN has %d attempts left\n", remaining); + } + + prompt = make_prompt (app, remaining, desc, NULL); + err = pincb (pincb_arg, prompt, &oldpin); + xfree (prompt); if (err) { log_error ("error getting old PIN: %s\n", gpg_strerror (err)); @@ -1224,7 +2286,10 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, goto leave; } - err = pincb (pincb_arg, newdesc, &newpin); + + prompt = make_prompt (app, -1, newdesc, NULL); + err = pincb (pincb_arg, prompt, &newpin); + xfree (prompt); if (err) { log_error (_("error getting new PIN: %s\n"), gpg_strerror (err)); @@ -1273,16 +2338,16 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr, { gpg_error_t err; int pwid; - int is_sigg; + int nks_app_id; const char *desc; (void)ctrl; - desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid); + desc = parse_pwidstr (app, pwidstr, 0, &nks_app_id, &pwid); if (!desc) return gpg_error (GPG_ERR_INV_ID); - err = switch_application (app, is_sigg); + err = switch_application (app, nks_app_id); if (err) return err; @@ -1290,6 +2355,73 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr, } +/* Process the various keygrip based info requests. */ +static gpg_error_t +do_with_keygrip (app_t app, ctrl_t ctrl, int action, + const char *want_keygripstr, int capability) +{ + gpg_error_t err; + char keygripstr[2*KEYGRIP_LEN+1]; + char *serialno = NULL; + int data = 0; + int idx = -1; + + /* First a quick check for valid parameters. */ + switch (action) + { + case KEYGRIP_ACTION_LOOKUP: + if (!want_keygripstr) + { + return gpg_error (GPG_ERR_NOT_FOUND); + } + break; + case KEYGRIP_ACTION_SEND_DATA: + data = 1; + break; + case KEYGRIP_ACTION_WRITE_STATUS: + break; + default: + return gpg_error (GPG_ERR_INV_ARG); + } + + /* Allocate the S/N string if needed. */ + if (action != KEYGRIP_ACTION_LOOKUP) + { + serialno = app_get_serialno (app); + if (!serialno) + return gpg_error_from_syserror (); + } + + while (1) + { + err = iterate_over_filelist (app, want_keygripstr, capability, + keygripstr, &idx); + if (err) + break; + + if (want_keygripstr) + { + if (!err) + break; + } + else + { + char idbuf[20]; + char usagebuf[5]; + + snprintf (idbuf, sizeof idbuf, "NKS-%s.%04X", + get_nks_tag (app, app->app_local->active_nks_app), + filelist[idx].fid); + set_usage_string (usagebuf, idx); + send_keyinfo (ctrl, data, keygripstr, serialno, idbuf, usagebuf); + } + } + + xfree (serialno); + return err; +} + + /* Return the version of the NKS application. */ static int get_nks_version (int slot) @@ -1301,48 +2433,67 @@ get_nks_version (int slot) if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0, NULL, &result, &resultlen)) return 2; /* NKS 2 does not support this command. */ - - /* Example value: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00 - vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx - vendor (Philips) -+ | | | | | | | - chip type -----------+ | | | | | | - chip id ----------------+ | | | | | - card type (3 - tcos 3) -------------------+ | | | | - OS version of card type ---------------------+ | | | - OS release of card type ------------------------+ | | - OS vendor internal version ------------------------+ | - RFU -----------------------------------------------------------+ - */ + /* Example values: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00 + * 05 a0 22 3e c8 0c 04 20 0f 01 b6 01 01 00 00 02 + * vv tt ccccccccccccccccc aa bb cc vv ff rr rr xx + * vendor -----------+ | | | | | | | | | | + * chip type -----------+ | | | | | | | | | + * chip id ----------------+ | | | | | | | | + * card type --------------------------------+ | | | | | | | + * OS version of card type ---------------------+ | | | | | | + * OS release of card type ------------------------+ | | | | | + * Completion code version number --------------------+ | | | | + * File system version ----------------------------------+ | | | + * RFU (00) ------------------------------------------------+ | | + * RFU (00) ---------------------------------------------------+ | + * Authentication key identifier ---------------------------------+ + * + * vendor 4 := Philips + * 5 := Infinion + * card type 3 := TCOS 3 + * 15 := TCOS Signature Card (bb,cc is the ROM mask version) + * Completion code version number Bit 7..5 := pre-completion code version + * Bit 4..0 := completion code version + * (pre-completion by chip vendor) + * (completion by OS developer) + */ if (resultlen < 16) type = 0; /* Invalid data returned. */ else type = result[8]; xfree (result); - return type; } -/* If ENABLE_SIGG is true switch to the SigG application if not yet - active. If false switch to the NKS application if not yet active. - Returns 0 on success. */ +/* Switch to the NKS app identified by NKS_APP_ID if not yet done. + * Returns 0 on success. */ static gpg_error_t -switch_application (app_t app, int enable_sigg) +switch_application (app_t app, int nks_app_id) { gpg_error_t err; - if (((app->app_local->sigg_active && enable_sigg) - || (!app->app_local->sigg_active && !enable_sigg)) + if (app->app_local->only_idlm) + return 0; /* No switching at all */ + if (app->app_local->active_nks_app == nks_app_id && !app->app_local->need_app_select) return 0; /* Already switched. */ - log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS"); - if (enable_sigg) - err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0); + log_info ("nks: switching to %s\n", + nks_app_id == NKS_APP_ESIGN? "eSign" : + nks_app_id == NKS_APP_SIGG? "SigG" : "NKS"); + + if (nks_app_id == NKS_APP_ESIGN) + err = iso7816_select_application (app_get_slot (app), + aid_esign, sizeof aid_esign, 0); + else if (nks_app_id == NKS_APP_SIGG) + err = iso7816_select_application (app_get_slot (app), + aid_sigg, sizeof aid_sigg, 0); else err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0); - if (!err && enable_sigg && app->app_local->nks_version >= 3 + if (!err && nks_app_id == NKS_APP_SIGG + && app->appversion >= 3 && !app->app_local->sigg_msig_checked) { /* Check whether this card is a mass signature card. */ @@ -1367,17 +2518,19 @@ switch_application (app_t app, int enable_sigg) xfree (buffer); } if (app->app_local->sigg_is_msig) - log_info ("This is a mass signature card\n"); + log_info ("nks: This is a mass signature card\n"); } if (!err) { app->app_local->need_app_select = 0; - app->app_local->sigg_active = enable_sigg; + app->app_local->active_nks_app = nks_app_id; } else - log_error ("app-nks: error switching to %s: %s\n", - enable_sigg? "SigG":"NKS", gpg_strerror (err)); + log_error ("nks: error switching to %s: %s\n", + nks_app_id == NKS_APP_ESIGN? "eSign" : + nks_app_id == NKS_APP_SIGG? "SigG" : "NKS", + gpg_strerror (err)); return err; } @@ -1389,8 +2542,14 @@ app_select_nks (app_t app) { int slot = app->slot; int rc; + int is_idlm = 0; rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0); + if (rc) + { + is_idlm = 1; + rc = iso7816_select_application (slot, aid_idlm, sizeof aid_idlm, 0); + } if (!rc) { app->apptype = APPTYPE_NKS; @@ -1402,9 +2561,22 @@ app_select_nks (app_t app) goto leave; } - app->app_local->nks_version = get_nks_version (slot); + app->appversion = get_nks_version (slot); + app->app_local->only_idlm = is_idlm; + if (is_idlm) /* Set it once, there won't be any switching. */ + app->app_local->active_nks_app = NKS_APP_IDLM; + if (opt.verbose) - log_info ("Detected NKS version: %d\n", app->app_local->nks_version); + { + log_info ("Detected NKS version: %d\n", app->appversion); + if (is_idlm) + log_info ("Using only the IDLM application\n"); + } + + if (app->appversion == 15) + app->app_local->qes_app_id = NKS_APP_ESIGN; + else + app->app_local->qes_app_id = NKS_APP_SIGG; app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; @@ -1412,6 +2584,7 @@ app_select_nks (app_t app) app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = NULL; + app->fnc.writecert = do_writecert; app->fnc.writekey = do_writekey; app->fnc.genkey = NULL; app->fnc.sign = do_sign; @@ -1419,6 +2592,7 @@ app_select_nks (app_t app) app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; app->fnc.check_pin = do_check_pin; + app->fnc.with_keygrip = do_with_keygrip; } leave: diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index da6dc7a..0ce09e1 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -48,11 +48,9 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <assert.h> #include <time.h> #include "scdaemon.h" - #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" @@ -64,6 +62,10 @@ #define KDF_DATA_LENGTH_MIN 90 #define KDF_DATA_LENGTH_MAX 110 +/* The AID of this application. */ +static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; + + /* A table describing the DOs of the card. */ static struct { int tag; @@ -80,7 +82,7 @@ static struct { status bytes. */ unsigned int try_extlen:2; /* Large object; try to use an extended length APDU when !=0. The size is - determined by extcap.max_certlen_3 + determined by extcap.max_certlen when == 1, and by extcap.max_special_do when == 2. */ char *desc; @@ -112,9 +114,13 @@ static struct { { 0x0104, 0, 0, 0, 0, 0, 0, 2, "Private DO 4"}, { 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"}, /* V3.0 */ - { 0x7F74, 0, 0, 1, 0, 0, 0, 0, "General Feature Management"}, + { 0x7F74, 0, 0x6E, 1, 0, 0, 0, 0, "General Feature Management"}, { 0x00D5, 0, 0, 1, 0, 0, 0, 0, "AES key data"}, + { 0x00D6, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Signature"}, + { 0x00D7, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Decryption"}, + { 0x00D8, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Authentication"}, { 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"}, + { 0x00FA, 0, 0, 1, 0, 0, 0, 2, "Algorithm Information"}, { 0 } }; @@ -184,7 +190,7 @@ struct app_local_s { struct { unsigned int is_v2:1; /* Compatible to v2 or later. */ - unsigned int extcap_v3:1; /* Extcap is in v3 format. */ + unsigned int is_v3:1; /* Comatible to v3 or later. */ unsigned int has_button:1; /* Has confirmation button or not. */ unsigned int sm_supported:1; /* Secure Messaging is supported. */ @@ -199,7 +205,7 @@ struct app_local_s { unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */ unsigned int pin_blk2:1; /* PIN block 2 format supported. */ unsigned int mse:1; /* MSE command supported. */ - unsigned int max_certlen_3:16; + unsigned int max_certlen:16; /* Maximum size of DO 7F21. */ unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */ unsigned int max_special_do:16; /* Maximum size for special DOs. */ } extcap; @@ -211,6 +217,12 @@ struct app_local_s { unsigned int def_chv2:1; /* Use 123456 for CHV2. */ } flags; + /* Flags used to override certain behavior. */ + struct + { + unsigned int cache_6e:1; + } override; + /* Pinpad request specified on card. */ struct { @@ -220,9 +232,10 @@ struct app_local_s { int fixedlen_admin; } pinpad; - struct - { + struct + { key_type_t key_type; + const char *keyalgo; /* Algorithm in standard string format. */ union { struct { unsigned int n_bits; /* Size of the modulus in bits. The rest @@ -232,11 +245,13 @@ struct app_local_s { rsa_key_format_t format; } rsa; struct { - const char *curve; - int flags; + const char *curve; /* Canonical name defined in openpgp-oid.c */ + int algo; + unsigned int flags; } ecc; }; } keyattr[3]; + }; #define ECC_FLAG_DJB_TWEAK (1 << 0) @@ -252,11 +267,14 @@ static gpg_error_t do_auth (app_t app, ctrl_t ctrl, const char *keyidstr, void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); +static const char *get_algorithm_attribute_string (const unsigned char *buffer, + size_t buflen); static gpg_error_t parse_algorithm_attribute (app_t app, int keyno); static gpg_error_t change_keyattr_from_string - (app_t app, + (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, + const char *keyref, const char *keyalgo, const void *value, size_t valuelen); @@ -333,6 +351,35 @@ do_deinit (app_t app) } +/* This is a helper to do a wipememory followed by a free. In general + * we do not need this if the buffer has been allocated in secure + * memory. However at some places we can't make that sure and thus we + * better to an extra wipe here. */ +static void +wipe_and_free (void *p, size_t len) +{ + if (p) + { + if (len) + wipememory (p, len); + xfree (p); + } +} + + +/* Similar to wipe_and_free but assumes P is eitehr NULL or a proper + * string. */ +static void +wipe_and_free_string (char *p) +{ + if (p) + { + wipememory (p, strlen (p)); + xfree (p); + } +} + + /* Wrapper around iso7816_get_data which first tries to get the data from the cache. With GET_IMMEDIATE passed as true, the cache is bypassed. With TRY_EXTLEN extended lengths APDUs are use if @@ -352,6 +399,9 @@ get_cached_data (app_t app, int tag, *result = NULL; *resultlen = 0; + if (tag == 0x6E && app->app_local->override.cache_6e) + get_immediate = 0; + if (!get_immediate) { for (c=app->app_local->cache; c; c = c->next) @@ -375,8 +425,8 @@ get_cached_data (app_t app, int tag, if (try_extlen && app->app_local->cardcap.ext_lc_le) { if (try_extlen == 1) - exmode = app->app_local->extcap.max_certlen_3; - else if (try_extlen == 2 && app->app_local->extcap.extcap_v3) + exmode = app->app_local->extcap.max_certlen; + else if (try_extlen == 2 && app->app_local->extcap.is_v3) exmode = app->app_local->extcap.max_special_do; else exmode = 0; @@ -384,7 +434,7 @@ get_cached_data (app_t app, int tag, else exmode = 0; - err = iso7816_get_data (app->slot, exmode, tag, &p, &len); + err = iso7816_get_data (app_get_slot (app), exmode, tag, &p, &len); if (err) return err; if (len) @@ -405,7 +455,7 @@ get_cached_data (app_t app, int tag, /* Okay, cache it. */ for (c=app->app_local->cache; c; c = c->next) - assert (c->tag != tag); + log_assert (c->tag != tag); c = xtrymalloc (sizeof *c + len); if (c) @@ -444,7 +494,7 @@ flush_cache_item (app_t app, int tag) for (c=app->app_local->cache; c ; c = c->next) { - assert (c->tag != tag); /* Oops: duplicated entry. */ + log_assert (c->tag != tag); /* Oops: duplicated entry. */ } return; } @@ -515,7 +565,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, if (app->appversion > 0x0100 && data_objects[i].get_immediate_in_v11) { exmode = 0; - rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen); + rc = iso7816_get_data (app_get_slot (app), exmode, tag, &buffer, &buflen); if (rc) { *r_rc = rc; @@ -665,6 +715,21 @@ count_bits (const unsigned char *a, size_t len) return n; } +static unsigned int +count_sos_bits (const unsigned char *a, size_t len) +{ + unsigned int n = len * 8; + int i; + + if (len == 0 || *a == 0) + return n; + + for (i=7; i && !(*a & (1<<i)); i--) + n--; + + return n; +} + /* GnuPG makes special use of the login-data DO, this function parses the login data to store the flags for later use. It may be called at any time and should be called after changing the login-data DO. @@ -790,10 +855,12 @@ parse_login_data (app_t app) #define MAX_ARGS_STORE_FPR 3 -/* Note, that FPR must be at least 20 bytes. */ +/* Note, that FPR must be at least 20 bytes. If UPDATE is not set, + * the fingerprint and the creation date is not actually stored but + * the fingerprint is only returned in FPR. */ static gpg_error_t -store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, - int algo, ...) +store_fpr (app_t app, int update, int keynumber, u32 timestamp, + unsigned char *fpr, int algo, ...) { unsigned int n, nbits; unsigned char *buffer, *p; @@ -838,12 +905,18 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, for (i = 0; i < argc; i++) { - if (algo == PUBKEY_ALGO_RSA || i == 1) + if (algo == PUBKEY_ALGO_RSA) { nbits = count_bits (m[i], mlen[i]); *p++ = nbits >> 8; *p++ = nbits; } + else if (i == 1) + { + nbits = count_sos_bits (m[i], mlen[i]); + *p++ = nbits >> 8; + *p++ = nbits; + } memcpy (p, m[i], mlen[i]); p += mlen[i]; } @@ -852,12 +925,15 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, xfree (buffer); + if (!update) + return 0; + tag = (app->appversion > 0x0007? 0xC7 : 0xC6) + keynumber; flush_cache_item (app, 0xC5); tag2 = 0xCE + keynumber; flush_cache_item (app, 0xCD); - rc = iso7816_put_data (app->slot, 0, tag, fpr, 20); + rc = iso7816_put_data (app_get_slot (app), 0, tag, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); @@ -870,7 +946,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr, buf[2] = timestamp >> 8; buf[3] = timestamp; - rc = iso7816_put_data (app->slot, 0, tag2, buf, 4); + rc = iso7816_put_data (app_get_slot (app), 0, tag2, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); @@ -954,8 +1030,12 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno) { char buffer[200]; - assert (keyno >=0 && keyno < DIM(app->app_local->keyattr)); + log_assert (keyno >=0 && keyno < DIM(app->app_local->keyattr)); + /* Note that the code in gpg-card supports prefixing the key number + * with "OPENPGP." but older code does not yet support this. There + * is also a discrepancy with the algorithm numbers: We should use + * the gcrypt numbers but the current code assumes OpenPGP numbers. */ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) snprintf (buffer, sizeof buffer, "%d 1 rsa%u %u %d", keyno+1, @@ -966,9 +1046,7 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno) { snprintf (buffer, sizeof buffer, "%d %d %s", keyno+1, - keyno==1? PUBKEY_ALGO_ECDH : - (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)? - PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA, + app->app_local->keyattr[keyno].ecc.algo, app->app_local->keyattr[keyno].ecc.curve); } else @@ -1026,8 +1104,14 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "$ENCRKEYID", 0x0000, -6 }, { "$SIGNKEYID", 0x0000, -7 }, { "$DISPSERIALNO",0x0000, -4 }, + { "UIF-1", 0x00D6, 0 }, + { "UIF-2", 0x00D7, 0 }, + { "UIF-3", 0x00D8, 0 }, { "KDF", 0x00F9, 5 }, { "MANUFACTURER", 0x0000, -8 }, + { "UIF", 0x0000, -9 }, /* Shortcut for all UIF */ + { "KEY-STATUS", 0x00DE, 6 }, + { "KEY-ATTR-INFO", 0x00FA, 7 }, { NULL, 0 } }; int idx, i, rc; @@ -1042,9 +1126,10 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) if (table[idx].special == -1) { - /* The serial number is very special. We could have used the - AID DO to retrieve it. The AID DO is available anyway but - not hex formatted. */ + /* The serial number is very special. We can't use the AID + DO (0x4f) because this is the serialno per specs with the + correct appversion. We might however use a serialno with the + version set to 0.0 and that is what we need to return. */ char *serial = app_get_serialno (app); if (serial) @@ -1058,6 +1143,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { char tmp[110]; + /* Noet that with v3 cards mcl3 is used for all certificates. */ snprintf (tmp, sizeof tmp, "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d " "sm=%d si=%u dec=%d bt=%d kdf=%d", @@ -1065,7 +1151,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) app->app_local->extcap.key_import, app->app_local->extcap.change_force_chv, app->app_local->extcap.private_dos, - app->app_local->extcap.max_certlen_3, + app->app_local->extcap.max_certlen, app->app_local->extcap.algo_attr_change, (app->app_local->extcap.sm_supported ? (app->app_local->extcap.sm_algo == 0? CIPHER_ALGO_3DES : @@ -1087,9 +1173,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) } if (table[idx].special == -4) { - char *serial = app_get_dispserialno (app, 0); + char *serial; - if (serial) + if ((serial = app_get_dispserialno (app, 0))) { send_status_info (ctrl, table[idx].name, serial, strlen (serial), NULL, 0); @@ -1123,6 +1209,15 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) app->app_local->manufacturer, get_manufacturer (app->app_local->manufacturer)); } + if (table[idx].special == -9) + { + rc = do_getattr (app, ctrl, "UIF-1"); + if (!rc) + rc = do_getattr (app, ctrl, "UIF-2"); + if (!rc) + rc = do_getattr (app, ctrl, "UIF-3"); + return rc; + } relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) @@ -1167,6 +1262,86 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); } + else if (table[idx].special == 6) + { + for (i=0,rc=0; !rc && i+1 < valuelen; i += 2) + rc = send_status_printf (ctrl, table[idx].name, "OPENPGP.%u %u", + value[i], value[i+1]); + if (gpg_err_code (rc) == GPG_ERR_NO_OBJ) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + } + else if (table[idx].special == 7) + { + const unsigned char *p = value; + int tag; + size_t len; + + if (valuelen < 2) + return gpg_error (GPG_ERR_INV_OBJ); + + tag = p[0]; + len = p[1]; + + /* Does it comes tag+len at the head? */ + if (tag == 0x00FA) + { + p += 2; + + if (len == 0x81) + { + if (valuelen < 3) + return gpg_error (GPG_ERR_INV_OBJ); + len = *p++; + } + else if (len == 0x82) + { + if (valuelen < 4) + return gpg_error (GPG_ERR_INV_OBJ); + len = *p++; + len = (len << 8) | *p++; + } + + valuelen -= (p - value); + value = (unsigned char *)p; + + if (valuelen != len) + { + if (opt.verbose) + log_info ("Yubikey bug: length %zu != %zu", valuelen, len); + + if (APP_CARD(app)->cardtype != CARDTYPE_YUBIKEY) + return gpg_error (GPG_ERR_INV_OBJ); + } + } + + for (; p < value + valuelen; p += len) + { + const char *key_algo_str; + int keyrefno; + + if (p + 2 > value + valuelen) + break; + + tag = *p++; + len = *p++; + + if (tag < 0xc1) + continue; + + if (tag == 0xda) + keyrefno = 0x81; + else + keyrefno = tag - 0xc1 + 1; + + if (p + len > value + valuelen) + break; + + key_algo_str = get_algorithm_attribute_string (p, len); + + send_status_printf (ctrl, table[idx].name, "OPENPGP.%u %s", + keyrefno, key_algo_str); + } + } else send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); @@ -1231,30 +1406,6 @@ get_disp_name (app_t app) } -/* Return the pretty formatted serialnumber. On error NULL is - * returned. */ -static char * -get_disp_serialno (app_t app) -{ - char *serial = app_get_serialno (app); - - /* For our OpenPGP cards we do not want to show the entire serial - * number but a nicely reformatted actual serial number. */ - if (serial && strlen (serial) > 16+12) - { - memmove (serial, serial+16, 4); - serial[4] = ' '; - /* memmove (serial+5, serial+20, 4); */ - /* serial[9] = ' '; */ - /* memmove (serial+10, serial+24, 4); */ - /* serial[14] = 0; */ - memmove (serial+5, serial+20, 8); - serial[13] = 0; - } - return serial; -} - - /* Return the number of remaining tries for the standard or the admin * pw. Returns -1 on card error. */ static int @@ -1290,7 +1441,7 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) unsigned char *value; size_t valuelen; - assert (keyno >=0 && keyno <= 2); + log_assert (keyno >=0 && keyno <= 2); relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL); if (relptr && valuelen >= 60) @@ -1302,6 +1453,36 @@ retrieve_fpr_from_card (app_t app, int keyno, char *fpr) } +/* Retrieve the creation time of the fingerprint for key KEYNO from + * the card inserted in the slot of APP and store it at R_FPRTIME. + * Returns 0 on success or an error code. */ +static gpg_error_t +retrieve_fprtime_from_card (app_t app, int keyno, u32 *r_fprtime) +{ + gpg_error_t err = 0; + void *relptr; + unsigned char *value; + size_t valuelen; + u32 fprtime; + + log_assert (keyno >=0 && keyno <= 2); + + relptr = get_one_do (app, 0x00CD, &value, &valuelen, NULL); + if (relptr && valuelen >= 4*(keyno+1)) + { + fprtime = buf32_to_u32 (value + 4*keyno); + if (!fprtime) + err = gpg_error (GPG_ERR_NOT_FOUND); + else + *r_fprtime = fprtime; + } + else + err = gpg_error (GPG_ERR_NOT_FOUND); + xfree (relptr); + return err; +} + + /* Retrieve the public key material for the RSA key, whose fingerprint is FPR, from gpg output, which can be read through the stream FP. The RSA modulus will be stored at the address of M and MLEN, the @@ -1415,7 +1596,8 @@ retrieve_key_material (FILE *fp, const char *hexkeyid, static gpg_error_t -rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, +rsa_read_pubkey (app_t app, ctrl_t ctrl, int meta_update, + u32 created_at, int keyno, const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp) { gpg_error_t err; @@ -1452,7 +1634,11 @@ rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, { unsigned char fprbuf[20]; - err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA, + /* If META_UPDATE is not set we only compute but not store the + * fingerprint. This might return a wrong fingerprint if + * CREATED_AT is not set. */ + err = store_fpr (app, meta_update, keyno, + created_at, fprbuf, PUBKEY_ALGO_RSA, m, mlen, e, elen); if (err) return err; @@ -1512,19 +1698,23 @@ ecdh_params (const char *curve) /* See RFC-6637 for those constants. 0x03: Number of bytes 0x01: Version for this parameter format - KDF hash algo - KEK symmetric cipher algo + KEK digest algorithm + KEK cipher algorithm + + Take care: They should match the parameters as used in g10/ecdh.c + as long as the ecdh-param is not fully support (as in gnupg 2.2). */ if (nbits <= 256) return (const unsigned char*)"\x03\x01\x08\x07"; else if (nbits <= 384) - return (const unsigned char*)"\x03\x01\x09\x08"; + return (const unsigned char*)"\x03\x01\x09\x08"; /* gnupg 2.2 only */ else return (const unsigned char*)"\x03\x01\x0a\x09"; } static gpg_error_t -ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, +ecc_read_pubkey (app_t app, ctrl_t ctrl, int meta_update, + u32 created_at, int keyno, const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp) { gpg_error_t err; @@ -1582,25 +1772,23 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, send_key_data (ctrl, "curve", oidbuf, oid_len); } + algo = app->app_local->keyattr[keyno].ecc.algo; if (keyno == 1) { if (ctrl) send_key_data (ctrl, "kdf/kek", ecdh_params (curve), (size_t)4); - algo = PUBKEY_ALGO_ECDH; - } - else - { - if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)) - algo = PUBKEY_ALGO_EDDSA; - else - algo = PUBKEY_ALGO_ECDSA; } if (ctrl) { unsigned char fprbuf[20]; - err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len, + /* If META_UPDATE is not set we only compute but not store the + * fingerprint. This might return a wrong fingerprint if + * CREATED_AT is not set or the ECDH params do not match the + * current defaults. */ + err = store_fpr (app, meta_update, keyno, + created_at, fprbuf, algo, oidbuf, oid_len, qbuf, ecc_q_len, ecdh_params (curve), (size_t)4); if (err) goto leave; @@ -1625,7 +1813,6 @@ ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno, } -/* Compute the keygrip form the local info and store it there. */ static gpg_error_t store_keygrip (app_t app, int keyno) { @@ -1644,13 +1831,15 @@ store_keygrip (app_t app, int keyno) /* Parse tag-length-value data for public key in BUFFER of BUFLEN - length. Key of KEYNO in APP is updated with an S-expression of - public key. When CTRL is not NULL, fingerprint is computed with - CREATED_AT, and fingerprint is written to the card, and key data - and fingerprint are send back to the client side. + * length. Key of KEYNO in APP is updated with an S-expression of + * public key. If CTRL is not NULL, the fingerprint is computed with + * CREATED_AT and key data and fingerprint are send back to the client + * side. If also META_UPDATE is true the fingerprint and the creation + * date are also written to the card. */ static gpg_error_t -read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, +read_public_key (app_t app, ctrl_t ctrl, int meta_update, + u32 created_at, int keyno, const unsigned char *buffer, size_t buflen) { gpg_error_t err; @@ -1666,10 +1855,10 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, } if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) - err = rsa_read_pubkey (app, ctrl, created_at, keyno, + err = rsa_read_pubkey (app, ctrl, meta_update, created_at, keyno, data, datalen, &s_pkey); else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC) - err = ecc_read_pubkey (app, ctrl, created_at, keyno, + err = ecc_read_pubkey (app, ctrl, meta_update, created_at, keyno, data, datalen, &s_pkey); else err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); @@ -1681,7 +1870,7 @@ read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno, len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0); keybuf = xtrymalloc (len); - if (!data) + if (!keybuf) { err = gpg_error_from_syserror (); gcry_sexp_release (s_pkey); @@ -1756,23 +1945,28 @@ get_public_key (app_t app, int keyno) le_value = 256; /* Use legacy value. */ } - err = iso7816_read_public_key (app->slot, exmode, + err = iso7816_read_public_key (app_get_slot (app), exmode, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); if (err) { /* Yubikey returns wrong code. Fix it up. */ - /* - * NOTE: It's not correct to blindly change the error code, - * however, for our experiences, it is only Yubikey... - */ - err = gpg_error (GPG_ERR_NO_OBJ); + if (APP_CARD(app)->cardtype == CARDTYPE_YUBIKEY) + err = gpg_error (GPG_ERR_NO_OBJ); + /* Yubikey NEO (!CARDTYPE_YUBIKEY) also returns wrong code. + * Fix it up. */ + else if (gpg_err_code (err) == GPG_ERR_CARD) + err = gpg_error (GPG_ERR_NO_OBJ); log_error (_("reading public key failed: %s\n"), gpg_strerror (err)); goto leave; } - err = read_public_key (app, NULL, 0U, keyno, buffer, buflen); + /* Note that we use 0 for the creation date and thus the - via + * status lines - returned fingerprint will only be valid if the + * key has also been created with that date. A similar problem + * occurs with the ECDH params which are fixed in the code. */ + err = read_public_key (app, NULL, 0, 0U, keyno, buffer, buflen); } else { @@ -1802,7 +1996,8 @@ get_public_key (app_t app, int keyno) hexkeyid = fpr + 24; ret = gpgrt_asprintf - (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr); + (&command, "%s --list-keys --with-colons --with-key-data '%s'", + gnupg_module_name (GNUPG_MODULE_NAME_GPG), fpr); if (ret < 0) { err = gpg_error_from_syserror (); @@ -1848,7 +2043,6 @@ get_public_key (app_t app, int keyno) app->app_local->pk[keyno].key = (unsigned char*)keybuf; /* Decrement for trailing '\0' */ app->app_local->pk[keyno].keylen = len - 1; - err = store_keygrip (app, keyno); } @@ -1862,6 +2056,21 @@ get_public_key (app_t app, int keyno) } +static const char * +get_usage_string (int keyno) +{ + const char *usage; + switch (keyno) + { + case 0: usage = "sc"; break; + case 1: usage = "e"; break; + case 2: usage = "sa"; break; + default: usage = "-"; break; + } + return usage; +} + + /* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3]. This is used by the LEARN command. */ static gpg_error_t @@ -1869,33 +2078,46 @@ send_keypair_info (app_t app, ctrl_t ctrl, int key) { int keyno = key - 1; gpg_error_t err = 0; - char idbuf[50]; const char *usage; + u32 fprtime; + char *algostr = NULL; err = get_public_key (app, keyno); if (err) goto leave; - assert (keyno >= 0 && keyno <= 2); + log_assert (keyno >= 0 && keyno <= 2); if (!app->app_local->pk[keyno].key) goto leave; /* No such key - ignore. */ - switch (keyno) + usage = get_usage_string (keyno); + + if (retrieve_fprtime_from_card (app, keyno, &fprtime)) + fprtime = 0; + + { + gcry_sexp_t s_pkey; + if (gcry_sexp_new (&s_pkey, app->app_local->pk[keyno].key, + app->app_local->pk[keyno].keylen, 0)) + algostr = xtrystrdup ("?"); + else + { + algostr = pubkey_algo_string (s_pkey, NULL); + gcry_sexp_release (s_pkey); + } + } + if (!algostr) { - case 0: usage = "sc"; break; - case 1: usage = "e"; break; - case 2: usage = "sa"; break; - default: usage = ""; break; + err = gpg_error_from_syserror (); + goto leave; } - sprintf (idbuf, "OPENPGP.%d", keyno+1); - send_status_info (ctrl, "KEYPAIRINFO", - app->app_local->pk[keyno].keygrip_str, 40, - idbuf, strlen (idbuf), - usage, strlen (usage), - NULL, (size_t)0); + err = send_status_printf (ctrl, "KEYPAIRINFO", "%s OPENPGP.%d %s %lu %s", + app->app_local->pk[keyno].keygrip_str, + keyno+1, usage, (unsigned long)fprtime, algostr); leave: + xfree (algostr); return err; } @@ -1937,6 +2159,10 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) if (gpg_err_code (err) == GPG_ERR_NO_OBJ) err = 0; } + if (!err && app->app_local->extcap.has_button) + err = do_getattr (app, ctrl, "UIF"); + if (gpg_err_code (err) == GPG_ERR_NO_OBJ) + err = 0; if (!err && app->app_local->extcap.private_dos) { if (!err) @@ -1989,11 +2215,25 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, (void)ctrl; - if (!strcmp (keyid, "OPENPGP.1")) + if (strlen (keyid) == 40) + { + const unsigned char *keygrip_str; + + for (keyno = 0; keyno < 3; keyno++) + { + keygrip_str = app->app_local->pk[keyno].keygrip_str; + if (!strncmp (keygrip_str, keyid, 40)) + break; + } + + if (keyno >= 3) + return gpg_error (GPG_ERR_INV_ID); + } + else if (!ascii_strcasecmp (keyid, "OPENPGP.1")) keyno = 0; - else if (!strcmp (keyid, "OPENPGP.2")) + else if (!ascii_strcasecmp (keyid, "OPENPGP.2")) keyno = 1; - else if (!strcmp (keyid, "OPENPGP.3")) + else if (!ascii_strcasecmp (keyid, "OPENPGP.3")) keyno = 2; else return gpg_error (GPG_ERR_INV_ID); @@ -2046,39 +2286,94 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, /* Read the standard certificate of an OpenPGP v2 card. It is returned in a freshly allocated buffer with that address stored at - CERT and the length of the certificate stored at CERTLEN. CERTID - needs to be set to "OPENPGP.3". */ + CERT and the length of the certificate stored at CERTLEN. */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { gpg_error_t err; - unsigned char *buffer; - size_t buflen; - void *relptr; + int occurrence = 0; *cert = NULL; *certlen = 0; - if (strcmp (certid, "OPENPGP.3")) + if (strlen (certid) == 40) + { + int keyno; + const unsigned char *keygrip_str; + + for (keyno = 0; keyno < 3; keyno++) + { + keygrip_str = app->app_local->pk[keyno].keygrip_str; + if (!strncmp (keygrip_str, certid, 40)) + break; + } + + if (keyno == 2) + ; + else if (keyno == 1) + occurrence = 1; + else if (keyno == 0) + occurrence = 2; + else + return gpg_error (GPG_ERR_INV_ID); + } + else if (!ascii_strcasecmp (certid, "OPENPGP.3")) + ; + else if (!ascii_strcasecmp (certid, "OPENPGP.2")) + occurrence = 1; + else if (!ascii_strcasecmp (certid, "OPENPGP.1")) + occurrence = 2; + else return gpg_error (GPG_ERR_INV_ID); + if (!app->app_local->extcap.is_v3 && occurrence) + return gpg_error (GPG_ERR_NOT_SUPPORTED); if (!app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_FOUND); - relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL); - if (!relptr) - return gpg_error (GPG_ERR_NOT_FOUND); + if (occurrence) + { + int exmode; - if (!buflen) - err = gpg_error (GPG_ERR_NOT_FOUND); - else if (!(*cert = xtrymalloc (buflen))) - err = gpg_error_from_syserror (); + err = iso7816_select_data (app_get_slot (app), occurrence, 0x7F21); + if (!err) + { + if (app->app_local->cardcap.ext_lc_le) + exmode = app->app_local->extcap.max_certlen; + else + exmode = 0; + + err = iso7816_get_data (app_get_slot (app), exmode, 0x7F21, + cert, certlen); + /* We reset the curDO even for an error. */ + iso7816_select_data (app_get_slot (app), 0, 0x7F21); + } + + if (err) + err = gpg_error (GPG_ERR_NOT_FOUND); + } else { - memcpy (*cert, buffer, buflen); - *certlen = buflen; - err = 0; + unsigned char *buffer; + size_t buflen; + void *relptr; + + relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL); + if (!relptr) + return gpg_error (GPG_ERR_NOT_FOUND); + + if (!buflen) + err = gpg_error (GPG_ERR_NOT_FOUND); + else if (!(*cert = xtrymalloc (buflen))) + err = gpg_error_from_syserror (); + else + { + memcpy (*cert, buffer, buflen); + *certlen = buflen; + err = 0; + } + xfree (relptr); } - xfree (relptr); + return err; } @@ -2128,7 +2423,7 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining) { char *serial, *disp_name, *rembuf, *tmpbuf, *result; - serial = get_disp_serialno (app); + serial = app_get_dispserialno (app, 0); if (!serial) return NULL; @@ -2187,26 +2482,41 @@ get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining) return result; } + /* Compute hash if KDF-DO is available. CHVNO must be 0 for reset - code, 1 or 2 for user pin and 3 for admin pin. - */ + * code, 1 or 2 for user pin and 3 for admin pin. PIN is the original + * PIN as entered by the user. R_PINVALUE and r_PINLEN will receive a + * newly allocated buffer with a possible modified pin. */ static gpg_error_t -pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) +pin2hash_if_kdf (app_t app, int chvno, const char *pin, + char **r_pinvalue, size_t *r_pinlen) { gpg_error_t err = 0; void *relptr = NULL; unsigned char *buffer; - size_t buflen; + size_t pinlen, buflen; + char *dek = NULL; + size_t deklen = 32; + *r_pinvalue = NULL; + *r_pinlen = 0; + + pinlen = strlen (pin); if (app->app_local->extcap.kdf_do && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL)) && buflen >= KDF_DATA_LENGTH_MIN && (buffer[2] == 0x03)) { const char *salt; unsigned long s2k_count; - char dek[32]; int salt_index; + dek = xtrymalloc (deklen); + if (!dek) + { + err = gpg_error_from_syserror (); + goto leave; + } + s2k_count = (((unsigned int)buffer[8] << 24) | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]); @@ -2221,20 +2531,29 @@ pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) } salt = &buffer[salt_index]; - err = gcry_kdf_derive (pinvalue, strlen (pinvalue), + err = gcry_kdf_derive (pin, pinlen, GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256, salt, 8, s2k_count, sizeof (dek), dek); if (!err) { - /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */ - *r_pinlen = 32; - memcpy (pinvalue, dek, *r_pinlen); - wipememory (dek, *r_pinlen); + *r_pinlen = deklen; + *r_pinvalue = dek; + dek = NULL; } } else - *r_pinlen = strlen (pinvalue); + { + /* Just copy the PIN to a malloced buffer. */ + *r_pinvalue = xtrymalloc_secure (pinlen + 1); + if (!*r_pinvalue) + { + err = gpg_error_from_syserror (); + goto leave; + } + strcpy (*r_pinvalue, pin); + *r_pinlen = pinlen; + } leave: xfree (relptr); @@ -2253,10 +2572,10 @@ pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen) as an indication that the pinpad has been used. */ static gpg_error_t -verify_a_chv (app_t app, +verify_a_chv (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, int chvno, unsigned long sigcount, - char **pinvalue, int *pinlen) + char **r_pinvalue, size_t *r_pinlen) { int rc = 0; char *prompt_buffer = NULL; @@ -2264,11 +2583,14 @@ verify_a_chv (app_t app, pininfo_t pininfo; int minlen = 6; int remaining; + char *pin = NULL; + + (void)ctrl; /* Reserved for use by a PIN cache. */ log_assert (chvno == 1 || chvno == 2); - *pinvalue = NULL; - *pinlen = 0; + *r_pinvalue = NULL; + *r_pinlen = 0; remaining = get_remaining_tries (app, 0); if (remaining == -1) @@ -2279,7 +2601,7 @@ verify_a_chv (app_t app, /* Special case for def_chv2 mechanism. */ if (opt.verbose) log_info (_("using default PIN as %s\n"), "CHV2"); - rc = iso7816_verify (app->slot, 0x82, "123456", 6); + rc = iso7816_verify (app_get_slot (app), 0x82, "123456", 6); if (rc) { /* Verification of CHV2 with the default PIN failed, @@ -2312,7 +2634,7 @@ verify_a_chv (app_t app, } if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 0)) { /* The reader supports the verify command through the pinpad. @@ -2328,16 +2650,14 @@ verify_a_chv (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x80+chvno, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); - - log_assert (!*pinvalue); } else { /* The reader has no pinpad or we don't want to use it. */ - rc = pincb (pincb_arg, prompt, pinvalue); + rc = pincb (pincb_arg, prompt, &pin); prompt = NULL; xfree (prompt_buffer); prompt_buffer = NULL; @@ -2348,25 +2668,27 @@ verify_a_chv (app_t app, return rc; } - if (strlen (*pinvalue) < minlen) + if (strlen (pin) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), chvno, minlen); - xfree (*pinvalue); - *pinvalue = NULL; + wipe_and_free_string (pin); return gpg_error (GPG_ERR_BAD_PIN); } - rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen); + rc = pin2hash_if_kdf (app, chvno, pin, r_pinvalue, r_pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen); + rc = iso7816_verify (app_get_slot (app), + 0x80 + chvno, *r_pinvalue, *r_pinlen); } + wipe_and_free_string (pin); if (rc) { log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc)); - xfree (*pinvalue); - *pinvalue = NULL; + xfree (*r_pinvalue); + *r_pinvalue = NULL; + *r_pinlen = 0; flush_cache_after_error (app); } @@ -2377,13 +2699,13 @@ verify_a_chv (app_t app, /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ static gpg_error_t -verify_chv2 (app_t app, +verify_chv2 (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; char *pinvalue; - int pinlen; + size_t pinlen; int i; if (app->did_chv2) @@ -2395,7 +2717,7 @@ verify_chv2 (app_t app, if (app->app_local->pk[1].key || app->app_local->pk[2].key) { - rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen); + rc = verify_a_chv (app, ctrl, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen); if (rc) return rc; app->did_chv2 = 1; @@ -2415,24 +2737,29 @@ verify_chv2 (app_t app, flush_cache_after_error (app); } else - app->did_chv1 = 1; + { + app->did_chv1 = 1; + /* Note that we are not able to cache the CHV 1 here because + * it is possible that due to the use of a KDF-DO PINVALUE + * has the hashed binary PIN of length PINLEN. */ + } } } else { - rc = verify_a_chv (app, pincb, pincb_arg, 1, 0, &pinvalue, &pinlen); + rc = verify_a_chv (app, ctrl, pincb, pincb_arg, 1, 0, &pinvalue, &pinlen); if (rc) return rc; } - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); return rc; } /* Build the prompt to enter the Admin PIN. The prompt depends on the - current sdtate of the card. */ + current state of the card. */ static gpg_error_t build_enter_admin_pin_prompt (app_t app, char **r_prompt) { @@ -2474,12 +2801,14 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt) /* Verify CHV3 if required. */ static gpg_error_t -verify_chv3 (app_t app, +verify_chv3 (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; + (void)ctrl; /* Reserved for use by a PIN cache. */ + if (!opt.allow_admin) { log_info (_("access to admin commands is not configured\n")); @@ -2501,7 +2830,8 @@ verify_chv3 (app_t app, return rc; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) + && !iso7816_check_pinpad (app_get_slot (app), + ISO7816_VERIFY, &pininfo) && !check_pinpad_request (app, &pininfo, 1)) { /* The reader supports the verify command through the pinpad. */ @@ -2514,16 +2844,17 @@ verify_chv3 (app_t app, gpg_strerror (rc)); return rc; } - rc = iso7816_verify_kp (app->slot, 0x83, &pininfo); + rc = iso7816_verify_kp (app_get_slot (app), 0x83, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } else { + char *pin; char *pinvalue; - int pinlen; + size_t pinlen; - rc = pincb (pincb_arg, prompt, &pinvalue); + rc = pincb (pincb_arg, prompt, &pin); xfree (prompt); prompt = NULL; if (rc) @@ -2533,18 +2864,19 @@ verify_chv3 (app_t app, return rc; } - if (strlen (pinvalue) < minlen) + if (strlen (pin) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), 3, minlen); - xfree (pinvalue); + wipe_and_free_string (pin); return gpg_error (GPG_ERR_BAD_PIN); } - rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen); + rc = pin2hash_if_kdf (app, 3, pin, &pinvalue, &pinlen); if (!rc) - rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen); - xfree (pinvalue); + rc = iso7816_verify (app_get_slot (app), 0x83, pinvalue, pinlen); + wipe_and_free_string (pin); + wipe_and_free (pinvalue, pinlen); } if (rc) @@ -2576,6 +2908,7 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name, int need_chv; int special; unsigned int need_v2:1; + unsigned int need_v3:1; } table[] = { { "DISP-NAME", 0x005B, 0, 3 }, { "LOGIN-DATA", 0x005E, 0, 3, 2 }, @@ -2590,35 +2923,44 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name, { "PRIVATE-DO-2", 0x0102, 0, 3 }, { "PRIVATE-DO-3", 0x0103, 0, 2 }, { "PRIVATE-DO-4", 0x0104, 0, 3 }, + { "CERT-1", 0x7F21, 0, 3,11, 1, 1 }, + { "CERT-2", 0x7F21, 0, 3,12, 1, 1 }, { "CERT-3", 0x7F21, 0, 3, 0, 1 }, { "SM-KEY-ENC", 0x00D1, 0, 3, 0, 1 }, { "SM-KEY-MAC", 0x00D2, 0, 3, 0, 1 }, { "KEY-ATTR", 0, 0, 0, 3, 1 }, { "AESKEY", 0x00D5, 0, 3, 0, 1 }, + { "UIF-1", 0x00D6, 0, 3, 5, 1 }, + { "UIF-2", 0x00D7, 0, 3, 5, 1 }, + { "UIF-3", 0x00D8, 0, 3, 5, 1 }, { "KDF", 0x00F9, 0, 3, 4, 1 }, { NULL, 0 } }; int exmode; - (void)ctrl; - for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); if (table[idx].need_v2 && !app->app_local->extcap.is_v2) - return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */ + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (table[idx].need_v3 && !app->app_local->extcap.is_v3) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + if (table[idx].special == 5 && app->app_local->extcap.has_button == 0) + return gpg_error (GPG_ERR_INV_OBJ); if (table[idx].special == 3) - return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen); + return change_keyattr_from_string (app, ctrl, pincb, pincb_arg, + NULL, NULL, value, valuelen); switch (table[idx].need_chv) { case 2: - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); break; case 3: - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); break; default: rc = 0; @@ -2638,7 +2980,24 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name, exmode = -254; /* Command chaining with max. 254 bytes. */ else exmode = 0; - rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen); + + if (table[idx].special == 11 || table[idx].special == 12) /* CERT-1 or -2 */ + { + rc = iso7816_select_data (app_get_slot (app), + table[idx].special == 11? 2 : 1, + table[idx].tag); + if (!rc) + { + rc = iso7816_put_data (app_get_slot (app), + exmode, table[idx].tag, value, valuelen); + /* We better reset the curDO. */ + iso7816_select_data (app_get_slot (app), 0, table[idx].tag); + } + } + else /* Standard. */ + rc = iso7816_put_data (app_get_slot (app), + exmode, table[idx].tag, value, valuelen); + if (rc) log_error ("failed to set '%s': %s\n", table[idx].name, gpg_strerror (rc)); @@ -2663,10 +3022,10 @@ do_setattr (app_t app, ctrl_t ctrl, const char *name, } -/* Handle the WRITECERT command for OpenPGP. This rites the standard - certifciate to the card; CERTID needs to be set to "OPENPGP.3". - PINCB and PINCB_ARG are the usual arguments for the pinentry - callback. */ +/* Handle the WRITECERT command for OpenPGP. This writes the standard + * certificate to the card; CERTID needs to be set to "OPENPGP.3". + * PINCB and PINCB_ARG are the usual arguments for the pinentry + * callback. */ static gpg_error_t do_writecert (app_t app, ctrl_t ctrl, const char *certidstr, @@ -2674,15 +3033,25 @@ do_writecert (app_t app, ctrl_t ctrl, void *pincb_arg, const unsigned char *certdata, size_t certdatalen) { - if (strcmp (certidstr, "OPENPGP.3")) + const char *name; + if (!ascii_strcasecmp (certidstr, "OPENPGP.3")) + name = "CERT-3"; + else if (!ascii_strcasecmp (certidstr, "OPENPGP.2")) + name = "CERT-2"; + else if (!ascii_strcasecmp (certidstr, "OPENPGP.1")) + name = "CERT-1"; + else return gpg_error (GPG_ERR_INV_ID); + if (!certdata || !certdatalen) return gpg_error (GPG_ERR_INV_ARG); if (!app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_SUPPORTED); - if (certdatalen > app->app_local->extcap.max_certlen_3) + /* do_setattr checks that CERT-2 and CERT-1 requires a v3 card. */ + + if (certdatalen > app->app_local->extcap.max_certlen) return gpg_error (GPG_ERR_TOO_LARGE); - return do_setattr (app, ctrl, "CERT-3", pincb, pincb_arg, + return do_setattr (app, ctrl, name, pincb, pincb_arg, certdata, certdatalen); } @@ -2718,13 +3087,21 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, char *pinvalue = NULL; int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET); int set_resetcode = 0; + int use_resetcode = 0; pininfo_t pininfo; int use_pinpad = 0; int minlen = 6; - int pinlen0 = 0; - int pinlen = 0; - (void)ctrl; + if (digitp (chvnostr)) + chvno = atoi (chvnostr); + else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1")) + chvno = 1; + else if (!ascii_strcasecmp (chvnostr, "OPENPGP.2")) + chvno = 2; + else if (!ascii_strcasecmp (chvnostr, "OPENPGP.3")) + chvno = 3; + else + return gpg_error (GPG_ERR_INV_ID); if (digitp (chvnostr)) chvno = atoi (chvnostr); @@ -2758,7 +3135,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, { /* We always require that the PIN is entered. */ app->did_chv3 = 0; - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); if (rc) goto leave; } @@ -2771,7 +3148,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, app->force_chv1 = 0; app->did_chv1 = 0; app->did_chv2 = 0; - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); app->force_chv1 = save_force; if (rc) goto leave; @@ -2787,7 +3164,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, /* Version 2 cards. */ if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_CHANGE_REFERENCE_DATA, &pininfo) && !check_pinpad_request (app, &pininfo, chvno == 3)) use_pinpad = 1; @@ -2797,7 +3174,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, /* To reset a PIN the Admin PIN is required. */ use_pinpad = 0; app->did_chv3 = 0; - rc = verify_chv3 (app, pincb, pincb_arg); + rc = verify_chv3 (app, ctrl, pincb, pincb_arg); if (rc) goto leave; @@ -2869,7 +3246,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, } rc = pincb (pincb_arg, - _("||Please enter the Reset Code for the card"), + _("|R|Please enter the Reset Code for the card"), &resetcode); if (rc) { @@ -2884,13 +3261,14 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, rc = gpg_error (GPG_ERR_BAD_PIN); goto leave; } + use_resetcode = 1; } else { rc = gpg_error (GPG_ERR_INV_ID); goto leave; } - } + } /* End version 2 cards. */ if (chvno == 3) app->did_chv3 = 0; @@ -2910,75 +3288,124 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc)); goto leave; } + + if (set_resetcode) + { + size_t bufferlen = strlen (pinvalue); + + if (bufferlen != 0 && bufferlen < 8) + { + log_error (_("Reset Code is too short; minimum length is %d\n"), 8); + rc = gpg_error (GPG_ERR_BAD_RESET_CODE); + goto leave; + } + } + else if (use_resetcode) + { + minlen = 6; /* Reset from the RC value to the PIN value. */ + if (strlen (pinvalue) < minlen) + { + log_info (_("PIN for CHV%d is too short;" + " minimum length is %d\n"), 1, minlen); + rc = gpg_error (GPG_ERR_BAD_PIN); + goto leave; + } + } + else + { + if (chvno == 3) + minlen = 8; + + if (strlen (pinvalue) < minlen) + { + log_info (_("PIN for CHV%d is too short;" + " minimum length is %d\n"), chvno, minlen); + rc = gpg_error (GPG_ERR_BAD_PIN); + goto leave; + } + } } if (resetcode) { - char *buffer; + char *result1 = NULL; + char *result2 = NULL; + char *buffer = NULL; + size_t resultlen1=0, resultlen2=0, bufferlen=0; - buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1); - if (!buffer) - rc = gpg_error_from_syserror (); - else + rc = pin2hash_if_kdf (app, 0, resetcode, &result1, &resultlen1); + if (!rc) + rc = pin2hash_if_kdf (app, 1, pinvalue, &result2, &resultlen2); + if (!rc) { - strcpy (buffer, resetcode); - rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0); - if (!rc) + bufferlen = resultlen1 + resultlen2; + buffer = xtrymalloc (bufferlen); + if (!buffer) + rc = gpg_error_from_syserror (); + else { - strcpy (buffer+pinlen0, pinvalue); - rc = pin2hash_if_kdf (app, 1, buffer+pinlen0, &pinlen); + memcpy (buffer, result1, resultlen1); + memcpy (buffer+resultlen1, result2, resultlen2); } - if (!rc) - rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, - buffer, pinlen0+pinlen); - wipememory (buffer, pinlen0 + pinlen); - xfree (buffer); } + if (!rc) + rc = iso7816_reset_retry_counter_with_rc (app_get_slot (app), 0x81, + buffer, bufferlen); + wipe_and_free (result1, resultlen1); + wipe_and_free (result2, resultlen2); + wipe_and_free (buffer, bufferlen); } else if (set_resetcode) { - if (strlen (pinvalue) < 8) - { - log_error (_("Reset Code is too short; minimum length is %d\n"), 8); - rc = gpg_error (GPG_ERR_BAD_PIN); - } - else - { - rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen); - if (!rc) - rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen); - } + size_t bufferlen; + char *buffer = NULL; + + rc = pin2hash_if_kdf (app, 0, pinvalue, &buffer, &bufferlen); + if (!rc) + rc = iso7816_put_data (app_get_slot (app), + 0, 0xD3, buffer, bufferlen); + + wipe_and_free (buffer, bufferlen); } else if (reset_mode) { - rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen); + char *buffer = NULL; + size_t bufferlen; + + rc = pin2hash_if_kdf (app, 1, pinvalue, &buffer, &bufferlen); if (!rc) - rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x81, buffer, bufferlen); if (!rc && !app->app_local->extcap.is_v2) - rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_reset_retry_counter (app_get_slot (app), + 0x82, buffer, bufferlen); + wipe_and_free (buffer, bufferlen); } else if (!app->app_local->extcap.is_v2) { /* Version 1 cards. */ if (chvno == 1 || chvno == 2) { - rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x81, NULL, 0, pinvalue, strlen (pinvalue)); if (!rc) - rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x82, NULL, 0, pinvalue, strlen (pinvalue)); } else /* CHVNO == 3 */ { - rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0, + rc = iso7816_change_reference_data (app_get_slot (app), + 0x80 + chvno, NULL, 0, pinvalue, strlen (pinvalue)); } } else { /* Version 2 cards. */ - assert (chvno == 1 || chvno == 3); + log_assert (chvno == 1 || chvno == 3); if (use_pinpad) { @@ -2997,36 +3424,30 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else - { - rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0); + { + char *buffer1 = NULL; + char *buffer2 = NULL; + size_t bufferlen1, bufferlen2 = 0; + + rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &buffer1, &bufferlen1); if (!rc) - rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen); + rc = pin2hash_if_kdf (app, chvno, pinvalue, &buffer2, &bufferlen2); if (!rc) rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, - oldpinvalue, pinlen0, - pinvalue, pinlen); + buffer1, bufferlen1, + buffer2, bufferlen2); + wipe_and_free (buffer1, bufferlen1); + wipe_and_free (buffer2, bufferlen2); } } - if (pinvalue) - { - wipememory (pinvalue, pinlen); - xfree (pinvalue); - } + wipe_and_free_string (pinvalue); if (rc) flush_cache_after_error (app); leave: - if (resetcode) - { - wipememory (resetcode, strlen (resetcode)); - xfree (resetcode); - } - if (oldpinvalue) - { - wipememory (oldpinvalue, pinlen0); - xfree (oldpinvalue); - } + wipe_and_free_string (resetcode); + wipe_and_free_string (oldpinvalue); return rc; } @@ -3043,9 +3464,9 @@ does_key_exist (app_t app, int keyidx, int generating, int force) size_t buflen, n; int i; - assert (keyidx >=0 && keyidx <= 2); + log_assert (keyidx >=0 && keyidx <= 2); - if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen)) + if (iso7816_get_data (app_get_slot (app), 0, 0x006E, &buffer, &buflen)) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); @@ -3083,7 +3504,7 @@ add_tlv (unsigned char *buffer, unsigned int tag, size_t length) { unsigned char *p = buffer; - assert (tag <= 0xffff); + log_assert (tag <= 0xffff); if ( tag > 0xff ) *p++ = tag >> 8; *p++ = tag; @@ -3147,7 +3568,7 @@ build_privkey_template (app_t app, int keyno, /* Get the required length for E. Rounded up to the nearest byte */ rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8; - assert (rsa_e_len <= rsa_e_reqlen); + log_assert (rsa_e_len <= rsa_e_reqlen); /* Build the 7f48 cardholder private key template. */ datalen = 0; @@ -3246,13 +3667,14 @@ build_privkey_template (app_t app, int keyno, /* Sanity check. We don't know the exact length because we allocated 3 bytes for the first length header. */ - assert (tp - template <= template_size); + log_assert (tp - template <= template_size); *result = template; *resultlen = tp - template; return 0; } + static gpg_error_t build_ecc_privkey_template (app_t app, int keyno, const unsigned char *ecc_d, size_t ecc_d_len, @@ -3353,7 +3775,7 @@ build_ecc_privkey_template (app_t app, int keyno, tp += ecc_q_len; } - assert (tp - template == template_size); + log_assert (tp - template == template_size); *result = template; *resultlen = tp - template; @@ -3364,25 +3786,27 @@ build_ecc_privkey_template (app_t app, int keyno, /* Helper for do_writekey to change the size of a key. Note that this deletes the entire key without asking. */ static gpg_error_t -change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen, +change_keyattr (app_t app, ctrl_t ctrl, + int keyno, const unsigned char *buf, size_t buflen, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; - assert (keyno >=0 && keyno <= 2); + log_assert (keyno >=0 && keyno <= 2); /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) return err; /* Change the attribute. */ - err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen); + err = iso7816_put_data (app_get_slot (app), 0, 0xC1+keyno, buf, buflen); if (err) - log_error ("error changing key attribute (key=%d)\n", keyno+1); + log_error ("error changing key attribute of OPENPGP.%d\n", + keyno+1); else - log_info ("key attribute changed (key=%d)\n", keyno+1); + log_info ("key attribute of OPENPGP.%d changed\n", keyno+1); flush_cache (app); err = parse_algorithm_attribute (app, keyno); app->did_chv1 = 0; @@ -3393,7 +3817,7 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen, static gpg_error_t -change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, +change_rsa_keyattr (app_t app, ctrl_t ctrl, int keyno, unsigned int nbits, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { @@ -3433,7 +3857,7 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, buflen = 6; } - err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg); + err = change_keyattr (app, ctrl, keyno, buf, buflen, pincb, pincb_arg); xfree (relptr); } @@ -3442,72 +3866,164 @@ change_rsa_keyattr (app_t app, int keyno, unsigned int nbits, /* Helper to process an setattr command for name KEY-ATTR. - In (VALUE,VALUELEN), it expects following string: - RSA: "--force <key> <algo> rsa<nbits>" - ECC: "--force <key> <algo> <curvename>" - */ + * + * If KEYREF and KEYALGO are NULL (VALUE,VALUELEN) are expected to + * contain one of the following strings: + * RSA: "--force <key> <algo> rsa<nbits>" + * ECC: "--force <key> <algo> <curvename>" + * + * If KEYREF and KEYALGO is given the key attribute for KEYREF are + * changed to what is described by KEYALGO (e.g. "rsa3072", "rsa2048", + * or "ed25519"). + */ static gpg_error_t -change_keyattr_from_string (app_t app, +change_keyattr_from_string (app_t app, ctrl_t ctrl, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, + const char *keyref, const char *keyalgo, const void *value, size_t valuelen) { gpg_error_t err = 0; - char *string; + char *string = NULL; int key, keyno, algo; - int n = 0; + unsigned int nbits = 0; + const char *oidstr = NULL; /* OID of the curve. */ + char *endp; + + if (keyref && keyalgo && *keyref && *keyalgo) + { + if (!ascii_strcasecmp (keyref, "OPENPGP.1")) + keyno = 0; + else if (!ascii_strcasecmp (keyref, "OPENPGP.2")) + keyno = 1; + else if (!ascii_strcasecmp (keyref, "OPENPGP.3")) + keyno = 2; + else + { + err = gpg_error (GPG_ERR_INV_ID); + goto leave; + } - /* VALUE is expected to be a string but not guaranteed to be - terminated. Thus copy it to an allocated buffer first. */ - string = xtrymalloc (valuelen+1); - if (!string) - return gpg_error_from_syserror (); - memcpy (string, value, valuelen); - string[valuelen] = 0; + if (!strncmp (keyalgo, "rsa", 3) && digitp (keyalgo+3)) + { + errno = 0; + nbits = strtoul (keyalgo+3, &endp, 10); + if (errno || *endp) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + algo = PUBKEY_ALGO_RSA; + } + else if ((!strncmp (keyalgo, "dsa", 3) || !strncmp (keyalgo, "elg", 3)) + && digitp (keyalgo+3)) + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + else + { + nbits = 0; + oidstr = openpgp_curve_to_oid (keyalgo, NULL, &algo); + if (!oidstr) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (!algo) + algo = keyno == 1? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; + } - /* Because this function deletes the key we require the string - "--force" in the data to make clear that something serious might - happen. */ - sscanf (string, "--force %d %d %n", &key, &algo, &n); - if (n < 12) + } + else if (!keyref && !keyalgo && value) { - err = gpg_error (GPG_ERR_INV_DATA); + int n; + + /* VALUE is expected to be a string but not guaranteed to be + * terminated. Thus copy it to an allocated buffer first. */ + string = xtrymalloc (valuelen+1); + if (!string) + { + err = gpg_error_from_syserror (); + goto leave; + } + memcpy (string, value, valuelen); + string[valuelen] = 0; + + /* Because this function deletes the key we require the string + * "--force" in the data to make clear that something serious + * might happen. */ + n = 0; + sscanf (string, "--force %d %d %n", &key, &algo, &n); + if (n < 12) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + keyno = key - 1; + if (algo == PUBKEY_ALGO_RSA) + { + errno = 0; + nbits = strtoul (string+n+3, NULL, 10); + if (errno) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + } + else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA) + { + oidstr = openpgp_curve_to_oid (string+n, NULL, NULL); + if (!oidstr) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + } + else + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + } + else + { + err = gpg_error (GPG_ERR_INV_ARG); goto leave; } - keyno = key - 1; if (keyno < 0 || keyno > 2) err = gpg_error (GPG_ERR_INV_ID); else if (algo == PUBKEY_ALGO_RSA) { - unsigned int nbits; - - errno = 0; - nbits = strtoul (string+n+3, NULL, 10); - if (errno) - err = gpg_error (GPG_ERR_INV_DATA); - else if (nbits < 1024) + if (nbits < 1024) err = gpg_error (GPG_ERR_TOO_SHORT); else if (nbits > 4096) err = gpg_error (GPG_ERR_TOO_LARGE); else - err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg); + err = change_rsa_keyattr (app, ctrl, keyno, nbits, pincb, pincb_arg); } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) { - const char *oidstr; gcry_mpi_t oid; const unsigned char *oidbuf; size_t oid_len; + unsigned int n; + + /* Check that the requested algo matches the properties of the + * key slot. */ + if (keyno == 1 && algo != PUBKEY_ALGO_ECDH) + err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + else if (keyno != 1 && algo == PUBKEY_ALGO_ECDH) + err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + else + err = 0; + if (err) + goto leave; - oidstr = openpgp_curve_to_oid (string+n, NULL, NULL); - if (!oidstr) - { - err = gpg_error (GPG_ERR_INV_DATA); - goto leave; - } - + /* Convert the OID string to an OpenPGP formatted OID. */ err = openpgp_oid_from_str (oidstr, &oid); if (err) goto leave; @@ -3515,10 +4031,17 @@ change_keyattr_from_string (app_t app, oidbuf = gcry_mpi_get_opaque (oid, &n); oid_len = (n+7)/8; - /* We have enough room at STRING. */ + /* Create the template. */ + xfree (string); + string = xtrymalloc (1 + oid_len); + if (!string) + { + err = gpg_error_from_syserror (); + goto leave; + } string[0] = algo; memcpy (string+1, oidbuf+1, oid_len-1); - err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg); + err = change_keyattr (app, ctrl,keyno, string, oid_len, pincb, pincb_arg); gcry_mpi_release (oid); } else @@ -3531,7 +4054,8 @@ change_keyattr_from_string (app_t app, static gpg_error_t -rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), +rsa_writekey (app_t app, ctrl_t ctrl, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, int keyno, const unsigned char *buf, size_t buflen, int depth) { @@ -3657,7 +4181,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), && app->app_local->extcap.algo_attr_change) { /* Try to switch the key to a new length. */ - err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg); + err = change_rsa_keyattr (app, ctrl, keyno, nbits, pincb, pincb_arg); if (!err) maxbits = app->app_local->keyattr[keyno].rsa.n_bits; } @@ -3759,7 +4283,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) goto leave; @@ -3770,7 +4294,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); } else @@ -3781,7 +4305,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), 0xC1 <length> prime p 0xC2 <length> prime q */ - assert (rsa_e_len <= 4); + log_assert (rsa_e_len <= 4); template_len = (1 + 1 + 4 + 1 + 1 + rsa_p_len + 1 + 1 + rsa_q_len); @@ -3812,15 +4336,15 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), memcpy (tp, rsa_q, rsa_q_len); tp += rsa_q_len; - assert (tp - template == template_len); + log_assert (tp - template == template_len); /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) goto leave; /* Store the key. */ - err = iso7816_put_data (app->slot, 0, + err = iso7816_put_data (app_get_slot (app), 0, (app->appversion > 0x0007? 0xE0:0xE9)+keyno, template, template_len); } @@ -3830,7 +4354,7 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; } - err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA, + err = store_fpr (app, 1, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA, rsa_n, rsa_n_len, rsa_e, rsa_e_len); if (err) goto leave; @@ -3843,7 +4367,8 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), static gpg_error_t -ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), +ecc_writekey (app_t app, ctrl_t ctrl, + gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, int keyno, const unsigned char *buf, size_t buflen, int depth) { @@ -3854,6 +4379,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), const unsigned char *ecc_q = NULL; const unsigned char *ecc_d = NULL; size_t ecc_q_len, ecc_d_len; + const unsigned char *ecdh_param = NULL; + size_t ecdh_param_len = 0; const char *curve = NULL; u32 created_at = 0; const char *oidstr; @@ -3866,12 +4393,14 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), unsigned char fprbuf[20]; size_t ecc_d_fixed_len; - /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)): + /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)(ecdh-params%s)): curve = "NIST P-256" */ /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)): curve = "secp256k1" */ /* (private-key(ecc(curve%s)(flags eddsa)(q%m)(d%m))(created-at%d)): curve = "Ed25519" */ + /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)): + curve = "Ed448" */ last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) @@ -3965,6 +4494,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; + if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen)) { if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen))) @@ -3976,6 +4506,17 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), created_at = created_at*10 + (*tok - '0'); } } + else if (tok && toklen == 11 && !memcmp ("ecdh-params", tok, toklen)) + { + if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen))) + goto leave; + if (tok) + { + ecdh_param = tok; + ecdh_param_len = toklen; + } + } + /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) @@ -4004,9 +4545,20 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), algo = PUBKEY_ALGO_EDDSA; else if (keyno == 1) algo = PUBKEY_ALGO_ECDH; + else if (!strcmp (curve, "Ed448")) + algo = PUBKEY_ALGO_EDDSA; else algo = PUBKEY_ALGO_ECDSA; + if (algo == PUBKEY_ALGO_ECDH && !ecdh_param) + { + /* In case this is used by older clients we fallback to our + * default ecc parameters. */ + log_info ("opgp: using default ecdh parameters\n"); + ecdh_param = ecdh_params (curve); + ecdh_param_len = 4; + } + oidstr = openpgp_curve_to_oid (curve, &n, NULL); ecc_d_fixed_len = (n+7)/8; err = openpgp_oid_from_str (oidstr, &oid); @@ -4042,7 +4594,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), } keyattr[0] = algo; memcpy (keyattr+1, oidbuf+1, oid_len-1); - err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg); + err = change_keyattr (app, ctrl, keyno, keyattr, + oid_len, pincb, pincb_arg); xfree (keyattr); if (err) goto leave; @@ -4080,7 +4633,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; /* Prepare for storing the key. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) { xfree (template); @@ -4094,7 +4647,7 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), exmode = -254; else exmode = 0; - err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, + err = iso7816_put_data_odd (app_get_slot (app), exmode, 0x3fff, template, template_len); xfree (template); } @@ -4107,8 +4660,8 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), goto leave; } - err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len, - ecc_q, ecc_q_len, ecdh_params (curve), (size_t)4); + err = store_fpr (app, 1, keyno, created_at, fprbuf, algo, oidbuf, oid_len, + ecc_q, ecc_q_len, ecdh_param, ecdh_param_len); leave: gcry_mpi_release (oid); @@ -4135,8 +4688,7 @@ do_writekey (app_t app, ctrl_t ctrl, const unsigned char *buf, *tok; size_t buflen, toklen; int depth; - - (void)ctrl; + char *algostr = NULL; if (!strcmp (keyid, "OPENPGP.1")) keyno = 0; @@ -4177,17 +4729,41 @@ do_writekey (app_t app, ctrl_t ctrl, goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; - if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0) - err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth); - else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0) - err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth); + + if (tok && toklen == 3 && (!memcmp ("rsa", tok, toklen) + || !memcmp ("ecc", tok, toklen))) + { + gcry_sexp_t stmp; + if (!gcry_sexp_new (&stmp, keydata, keydatalen, 0)) + algostr = pubkey_algo_string (stmp, NULL); + else + algostr = NULL; + gcry_sexp_release (stmp); + if (app->app_local->keyattr[keyno].keyalgo && algostr + && strcmp (app->app_local->keyattr[keyno].keyalgo, algostr)) + { + log_info ("openpgp: changing key attribute from %s to %s\n", + app->app_local->keyattr[keyno].keyalgo, algostr); + err = change_keyattr_from_string (app, ctrl, pincb, pincb_arg, + keyid, algostr, NULL, 0); + if (err) + return err; + } + + if (*tok == 'r') + err = rsa_writekey (app, ctrl, pincb, pincb_arg, keyno, + buf, buflen, depth); + else + err = ecc_writekey (app, ctrl, pincb, pincb_arg, keyno, + buf, buflen, depth); + } else { err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); - goto leave; } leave: + xfree (algostr); return err; } @@ -4195,26 +4771,31 @@ do_writekey (app_t app, ctrl_t ctrl, /* Handle the GENKEY command. */ static gpg_error_t -do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, +do_genkey (app_t app, ctrl_t ctrl, const char *keyref, const char *keyalgo, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; char numbuf[30]; + const char *keynostr; unsigned char *buffer = NULL; const unsigned char *keydata; size_t buflen, keydatalen; u32 created_at; - int keyno = atoi (keynostr) - 1; - int force = (flags & 1); + int keyno; + int force = !!(flags & APP_GENKEY_FLAG_FORCE); time_t start_at; int exmode = 0; int le_value = 256; /* Use legacy value. */ - (void)keytype; /* Ignored for OpenPGP cards. */ + /* Strip the OpenPGP prefix which is for historical reasons optional. */ + keynostr = keyref; + if (!ascii_strncasecmp (keynostr, "OPENPGP.", 8)) + keynostr += 8; - if (keyno < 0 || keyno > 2) + keyno = atoi (keynostr) - 1; + if (!digitp (keynostr) || keyno < 0 || keyno > 2) return gpg_error (GPG_ERR_INV_ID); /* We flush the cache to increase the traffic before a key @@ -4232,6 +4813,19 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, if (err) return err; + if (keyalgo && app->app_local->keyattr[keyno].keyalgo + && strcmp (keyalgo, app->app_local->keyattr[keyno].keyalgo)) + { + /* Specific algorithm requested which is not the currently + * configured algorithm. Change it. */ + log_info ("openpgp: changing key attribute from %s to %s\n", + app->app_local->keyattr[keyno].keyalgo, keyalgo); + err = change_keyattr_from_string (app, ctrl, pincb, pincb_arg, + keyref, keyalgo, NULL, 0); + if (err) + return err; + } + if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA) { unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits; @@ -4255,14 +4849,14 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, } /* Prepare for key generation by verifying the Admin PIN. */ - err = verify_chv3 (app, pincb, pincb_arg); + err = verify_chv3 (app, ctrl, pincb, pincb_arg); if (err) return err; log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); - err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0, + err = iso7816_generate_keypair (app_get_slot (app), exmode, 0x80, 0, (keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); @@ -4292,7 +4886,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype, send_status_info (ctrl, "KEY-CREATED-AT", numbuf, (size_t)strlen(numbuf), NULL, 0); - err = read_public_key (app, ctrl, created_at, keyno, buffer, buflen); + err = read_public_key (app, ctrl, 1, created_at, keyno, buffer, buflen); leave: xfree (buffer); return err; @@ -4338,7 +4932,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) size_t buflen, n; int rc, i; - assert (keyno >= 0 && keyno <= 2); + log_assert (keyno >= 0 && keyno <= 2); rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0); if (rc) @@ -4452,7 +5046,10 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno, int *r_use_auth) return gpg_error (GPG_ERR_INV_ID); } - if (n != 32 || strncmp (keyidstr, "D27600012401", 12)) + /* For a description of the serialno compare function see + * is_same_serialno. We don't use that function because here we + * are working on a hex string. */ + if (n != 32 || ascii_strncasecmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* no fingerprint given: we allow this for now. */ @@ -4460,7 +5057,9 @@ check_keyidstr (app_t app, const char *keyidstr, int keyno, int *r_use_auth) fpr = s + 1; serial = app_get_serialno (app); - if (strncmp (serial, keyidstr, 32)) + if (!serial || strlen (serial) != 32 + || ascii_memcasecmp (serial, "D27600012401", 12) + || ascii_memcasecmp (serial+16, keyidstr+16, 16)) { xfree (serial); return gpg_error (GPG_ERR_WRONG_CARD); @@ -4581,7 +5180,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (hashalgo == GCRY_MD_ ## a && (d) ) \ { \ datalen = sizeof b ## _prefix + indatalen; \ - assert (datalen <= sizeof data); \ + log_assert (datalen <= sizeof data); \ memcpy (data, b ## _prefix, sizeof b ## _prefix); \ memcpy (data + sizeof b ## _prefix, indata, indatalen); \ } @@ -4621,9 +5220,10 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (!app->did_chv1 || app->force_chv1) { char *pinvalue; - int pinlen; + size_t pinlen; - rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen); + rc = verify_a_chv (app, ctrl, pincb, pincb_arg, 1, sigcount, + &pinvalue, &pinlen); if (rc) return rc; @@ -4636,19 +5236,19 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, pinpad has been used. */ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2) { - rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen); + rc = iso7816_verify (app_get_slot (app), 0x82, pinvalue, pinlen); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) { log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc)); - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); flush_cache_after_error (app); return rc; } app->did_chv2 = 1; } - xfree (pinvalue); + wipe_and_free (pinvalue, pinlen); } @@ -4664,7 +5264,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, exmode = 0; le_value = 0; } - rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value, + rc = iso7816_compute_ds (app_get_slot (app), exmode, data, datalen, le_value, outdata, outdatalen); if (!rc && app->force_chv1) app->did_chv1 = 0; @@ -4691,8 +5291,6 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr, { int rc; - (void)ctrl; - if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA @@ -4726,7 +5324,7 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr, return rc; } - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); if (!rc) { int exmode, le_value; @@ -4756,7 +5354,7 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr, exmode = 0; le_value = 0; } - rc = iso7816_internal_authenticate (app->slot, exmode, + rc = iso7816_internal_authenticate (app_get_slot (app), exmode, indata, indatalen, le_value, outdata, outdatalen); } @@ -4779,8 +5377,6 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, int padind = 0; int fixuplen = 0; - (void)ctrl; - if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); @@ -4794,7 +5390,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, return rc; } - rc = verify_chv2 (app, pincb, pincb_arg); + rc = verify_chv2 (app, ctrl, pincb, pincb_arg); if (rc) return rc; @@ -4951,7 +5547,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, else exmode = le_value = 0; - rc = iso7816_decipher (app->slot, exmode, + rc = iso7816_decipher (app_get_slot (app), exmode, indata, indatalen, le_value, padind, outdata, outdatalen); xfree (fixbuf); @@ -5010,8 +5606,6 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, int rc; int admin_pin = 0; - (void)ctrl; - if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); @@ -5058,10 +5652,10 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, } app->did_chv3 = 0; /* Force verification. */ - return verify_chv3 (app, pincb, pincb_arg); + return verify_chv3 (app, ctrl, pincb, pincb_arg); } else - return verify_chv2 (app, pincb, pincb_arg); + return verify_chv2 (app, ctrl, pincb, pincb_arg); } @@ -5070,7 +5664,7 @@ static void show_caps (struct app_local_s *s) { log_info ("Version-2+ .....: %s\n", s->extcap.is_v2? "yes":"no"); - log_info ("Extcap-v3 ......: %s\n", s->extcap.extcap_v3? "yes":"no"); + log_info ("Version-3+ .....: %s\n", s->extcap.is_v3? "yes":"no"); log_info ("Button .........: %s\n", s->extcap.has_button? "yes":"no"); log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no"); @@ -5086,8 +5680,8 @@ show_caps (struct app_local_s *s) log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no"); log_info ("Symmetric Crypto: %s\n", s->extcap.has_decrypt? "yes":"no"); log_info ("KDF-Support ....: %s\n", s->extcap.kdf_do? "yes":"no"); - log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3); - if (s->extcap.extcap_v3) + log_info ("Max-Cert-Len ...: %u\n", s->extcap.max_certlen); + if (s->extcap.is_v3) { log_info ("PIN-Block-2 ....: %s\n", s->extcap.pin_blk2? "yes":"no"); log_info ("MSE-Support ....: %s\n", s->extcap.mse? "yes":"no"); @@ -5154,11 +5748,11 @@ parse_historical (struct app_local_s *apploc, /* * Check if the OID in an DER encoding is available by GnuPG/libgcrypt, - * and return the curve name. Return NULL if not available. + * and return the canonical curve name. Return NULL if not available. * The constant string is not allocated dynamically, never free it. */ static const char * -ecc_curve (unsigned char *buf, size_t buflen) +ecc_curve (const unsigned char *buf, size_t buflen) { gcry_mpi_t oid; char *oidstr; @@ -5189,6 +5783,39 @@ ecc_curve (unsigned char *buf, size_t buflen) } +static const char * +get_algorithm_attribute_string (const unsigned char *buffer, + size_t buflen) +{ + enum gcry_pk_algos galgo; + const char *curve; + unsigned int nbits = 0; + + galgo = map_openpgp_pk_to_gcry (*buffer); + nbits = 0; + curve = NULL; + + if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6)) + nbits = (buffer[1]<<8 | buffer[2]); + else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA + || *buffer == PUBKEY_ALGO_EDDSA) + { + int oidlen = buflen - 1; + + if (buffer[buflen-1] == 0x00 || buffer[buflen-1] == 0xff) + { /* Found "pubkey required"-byte for private key template. */ + oidlen--; + } + + curve = ecc_curve (buffer + 1, oidlen); + } + else if (opt.verbose) + log_printhex (buffer, buflen, ""); + + return get_keyalgo_string (galgo, nbits, curve); +} + + /* Parse and optionally show the algorithm attributes for KEYNO. KEYNO must be in the range 0..2. */ static gpg_error_t @@ -5198,9 +5825,12 @@ parse_algorithm_attribute (app_t app, int keyno) size_t buflen; void *relptr; const char desc[3][5] = {"sign", "encr", "auth"}; + enum gcry_pk_algos galgo; + unsigned int nbits; + const char *curve; gpg_error_t err = 0; - assert (keyno >=0 && keyno <= 2); + log_assert (keyno >=0 && keyno <= 2); app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA; app->app_local->keyattr[keyno].rsa.n_bits = 0; @@ -5220,6 +5850,11 @@ parse_algorithm_attribute (app_t app, int keyno) if (opt.verbose) log_info ("Key-Attr-%s ..: ", desc[keyno]); + + galgo = map_openpgp_pk_to_gcry (*buffer); + nbits = 0; + curve = NULL; + if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6)) { app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]); @@ -5234,6 +5869,7 @@ parse_algorithm_attribute (app_t app, int keyno) buffer[5] == 3? RSA_CRT_N : RSA_UNKNOWN_FMT); + nbits = app->app_local->keyattr[keyno].rsa.n_bits; if (opt.verbose) log_printf ("RSA, n=%u, e=%u, fmt=%s\n", @@ -5247,9 +5883,9 @@ parse_algorithm_attribute (app_t app, int keyno) else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA || *buffer == PUBKEY_ALGO_EDDSA) { - const char *curve; int oidlen = buflen - 1; + app->app_local->keyattr[keyno].ecc.algo = *buffer; app->app_local->keyattr[keyno].ecc.flags = 0; if (APP_CARD(app)->cardtype == CARDTYPE_YUBIKEY) @@ -5284,7 +5920,9 @@ parse_algorithm_attribute (app_t app, int keyno) { app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC; app->app_local->keyattr[keyno].ecc.curve = curve; - if (*buffer == PUBKEY_ALGO_EDDSA + if ((*buffer == PUBKEY_ALGO_EDDSA + && !strcmp (app->app_local->keyattr[keyno].ecc.curve, + "Ed25519")) || (*buffer == PUBKEY_ALGO_ECDH && !strcmp (app->app_local->keyattr[keyno].ecc.curve, "Curve25519"))) @@ -5299,6 +5937,13 @@ parse_algorithm_attribute (app_t app, int keyno) else if (opt.verbose) log_printhex (buffer, buflen, ""); + app->app_local->keyattr[keyno].keyalgo + = get_keyalgo_string (galgo, nbits, curve); + + if (opt.verbose) + log_info ("Key-Algo-%s ..: %s\n", + desc[keyno], app->app_local->keyattr[keyno].keyalgo); + xfree (relptr); return err; } @@ -5308,8 +5953,7 @@ parse_algorithm_attribute (app_t app, int keyno) gpg_error_t app_select_openpgp (app_t app) { - static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; - int slot = app->slot; + int slot = app_get_slot (app); gpg_error_t err; unsigned char *buffer; size_t buflen; @@ -5317,7 +5961,8 @@ app_select_openpgp (app_t app) /* Note that the card can't cope with P2=0xCO, thus we need to pass a special flag value. */ - err = iso7816_select_application (slot, aid, sizeof aid, 0x0001); + err = iso7816_select_application (slot, + openpgp_aid, sizeof openpgp_aid, 0x0001); if (!err) { unsigned int manufacturer; @@ -5358,13 +6003,16 @@ app_select_openpgp (app_t app) goto leave; } + /* We want to temporary cache the DO 6E. */ + app->app_local->override.cache_6e = 1; + app->app_local->manufacturer = manufacturer; if (app->appversion >= 0x0200) app->app_local->extcap.is_v2 = 1; if (app->appversion >= 0x0300) - app->app_local->extcap.extcap_v3 = 1; + app->app_local->extcap.is_v3 = 1; /* Read the historical bytes. */ relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL); @@ -5417,10 +6065,10 @@ app_select_openpgp (app_t app) app->app_local->extcap.sm_algo = buffer[1]; app->app_local->extcap.max_get_challenge = (buffer[2] << 8 | buffer[3]); - app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]); + app->app_local->extcap.max_certlen = (buffer[4] << 8 | buffer[5]); /* Interpretation is different between v2 and v3, unfortunately. */ - if (app->app_local->extcap.extcap_v3) + if (app->app_local->extcap.is_v3) { app->app_local->extcap.max_special_do = (buffer[6] << 8 | buffer[7]); @@ -5438,8 +6086,10 @@ app_select_openpgp (app_t app) /* Check optional DO of "General Feature Management" for button. */ relptr = get_one_do (app, 0x7f74, &buffer, &buflen, NULL); if (relptr) - /* It must be: 03 81 01 20 */ - app->app_local->extcap.has_button = 1; + { + /* It must be: 03 81 01 20 */ + app->app_local->extcap.has_button = 1; + } parse_login_data (app); @@ -5457,6 +6107,9 @@ app_select_openpgp (app_t app) if (opt.verbose > 1) dump_all_do (slot); + app->app_local->override.cache_6e = 0; + flush_cache_item (app, 0x6E); + app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; diff --git a/scd/app-p15.c b/scd/app-p15.c index 2884e0d..951ca37 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -326,7 +326,7 @@ struct prkdf_object_s char *serial_number; /* KDF/KEK parameter for OpenPGP's ECDH. First byte is zero if not - * availabale. .*/ + * available. */ unsigned char ecdh_kdf[4]; /* Length and allocated buffer with the Id of this object. */ @@ -744,7 +744,15 @@ select_and_read_record (app_t app, unsigned short efid, int recno, /* On CardOS with a Linear TLV file structure the records starts * with some tag (often the record number) followed by the length * byte for this record. Detect and remove this prefix. */ - if (*buflen > 2 && (*buffer)[0] != 0x30 && (*buffer)[1] == *buflen - 2) + if (*buflen == 2 && !(*buffer)[0] && !(*buffer)[1]) + ; /* deleted record. */ + else if (*buflen > 3 && (*buffer)[0] == 0xff + && buf16_to_uint ((*buffer)+1) == *buflen - 3) + { + memmove (*buffer, *buffer + 3, *buflen - 3); + *buflen = *buflen - 3; + } + else if (*buflen > 2 && (*buffer)[0] != 0x30 && (*buffer)[1] == *buflen - 2) { memmove (*buffer, *buffer + 2, *buflen - 2); *buflen = *buflen - 2; @@ -1771,6 +1779,9 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. Note the special handling for record mode at the end of the loop. */ + if (record_mode && buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ + while (n && *p && *p != 0xff) { const unsigned char *pp; @@ -1908,10 +1919,12 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) if (err) goto parse_error; - /* Make sure that the next element is a non zero path and of - even length (FID are two bytes each). */ + /* Make sure that the next element has a path of even length + * (FIDs are two bytes each). We should check that the path + * length is non-zero but some cards return a zero length path + * nevertheless (e.g. A.E.T. Europe Java applets). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING - || !objlen || (objlen & 1) ) + || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; @@ -2026,6 +2039,8 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) err = 0; goto leave; } + if (buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ p = buffer; n = buflen; } @@ -2075,6 +2090,9 @@ read_ef_pukdf (app_t app, unsigned short fid, pukdf_object_t *result) * starting with 0x00 or 0xff as these values are commonly used to * pad data blocks and are no valid ASN.1 encoding. Note the * special handling for record mode at the end of the loop. */ + if (record_mode && buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ + while (n && *p && *p != 0xff) { const unsigned char *pp; @@ -2212,10 +2230,10 @@ read_ef_pukdf (app_t app, unsigned short fid, pukdf_object_t *result) if (err) goto parse_error; - /* Make sure that the next element is a non zero path and of - even length (FID are two bytes each). */ + /* Make sure that the next element has a path of even length + * (FIDs are two bytes each). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING - || !objlen || (objlen & 1) ) + || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; @@ -2352,6 +2370,8 @@ read_ef_pukdf (app_t app, unsigned short fid, pukdf_object_t *result) err = 0; goto leave; } + if (buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ p = buffer; n = buflen; } @@ -2402,6 +2422,9 @@ read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result) starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. Note the special handling for record mode at the end of the loop. */ + if (record_mode && buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ + while (n && *p && *p != 0xff) { const unsigned char *pp; @@ -2507,10 +2530,10 @@ read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result) if (err) goto parse_error; - /* Make sure that the next element is a non zero path and of - even length (FID are two bytes each). */ + /* Make sure that the next element has a path of even length + * (FIDs are two bytes each). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING - || !objlen || (objlen & 1) ) + || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; @@ -2623,8 +2646,8 @@ read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result) err = 0; next_record: - xfree (authid); - xfree (label); + xfree (authid); authid = NULL; + xfree (label); label = NULL; /* If the card uses a record oriented file structure, read the * next record. Otherwise we keep on parsing the current buffer. */ recno++; @@ -2633,11 +2656,14 @@ read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result) xfree (buffer); buffer = NULL; err = select_and_read_record (app, 0, recno, "CDF", &buffer, &buflen, NULL); - if (err) { - if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) - err = 0; - goto leave; - } + if (err) + { + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + err = 0; + goto leave; + } + if (buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ p = buffer; n = buflen; } @@ -2726,6 +2752,9 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. Note the special handling for record mode at the end of the loop. */ + if (record_mode && buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ + while (n && *p && *p != 0xff) { const unsigned char *pp; @@ -2756,6 +2785,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) case 2: errstr = "external auth type are not supported"; break; default: errstr = "unknown privateKeyObject"; break; } + p += objlen; + n -= objlen; goto parse_error; } else @@ -3295,6 +3326,8 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) err = 0; goto leave; } + if (buflen == 2 && !buffer[0] && !buffer[1]) + goto next_record; /* Deleted record - continue with next */ p = buffer; n = buflen; } @@ -3873,8 +3906,8 @@ read_p15_info (app_t app) log_printf ("\n"); } - log_info ("p15: atr ..........: "); atr = apdu_get_atr (app_get_slot (app), &atrlen); + log_info ("p15: atr ..........: "); if (!atr) log_printf ("[error]\n"); else @@ -4482,7 +4515,7 @@ send_key_fpr_line (ctrl_t ctrl, int number, const unsigned char *fpr) } -/* If possible Emit a FPR-KEY status line for the private key object +/* If possible emit a FPR-KEY status line for the private key object * PRKDF using NUMBER as index. */ static void send_key_fpr (app_t app, ctrl_t ctrl, prkdf_object_t prkdf, int number) @@ -5041,15 +5074,15 @@ get_dispserialno (app_t app, prkdf_object_t prkdf) else if (IS_CARDOS_5 (app) && app->app_local->manufacturer_id && !ascii_strcasecmp (app->app_local->manufacturer_id, "Technology Nexus") - && app->serialno && app->serialnolen == 4+9 - && !memcmp (app->serialno, "\xff\x00\x00\xff", 4) - && !any_control_or_space_mem (app->serialno + 4, 9)) + && APP_CARD(app)->serialno && APP_CARD(app)->serialnolen == 4+9 + && !memcmp (APP_CARD(app)->serialno, "\xff\x00\x00\xff", 4) + && !any_control_or_space_mem (APP_CARD(app)->serialno + 4, 9)) { /* Sample: ff0000ff354830313232363537 -> "5H01 2265 7" */ serial = xtrymalloc (9+2+1); if (serial) { - s = app->serialno + 4; + s = APP_CARD(app)->serialno + 4; for (i=0; i < 4; i++) serial[i] = *s++; serial[i++] = ' '; @@ -5170,6 +5203,7 @@ verify_pin (app_t app, const char *errstr; const char *s; int remaining; + unsigned int min_length; int pin_reference; int verified = 0; int i; @@ -5236,12 +5270,16 @@ verify_pin (app_t app, } /* We might need to cope with UTF8 things here. Not sure how - min_length etc. are exactly defined, for now we take them as - a plain octet count. */ - if (strlen (pinvalue) < aodf->min_length) + min_length etc. are exactly defined, for now we take them as a + plain octet count. For RSCS we enforce 6 despite that some cards + give 4 has min. length. */ + min_length = aodf->min_length; + if (app->app_local->card_product == CARD_PRODUCT_RSCS && min_length < 6) + min_length = 6; + + if (strlen (pinvalue) < min_length) { - log_error ("p15: PIN is too short; minimum length is %lu\n", - aodf->min_length); + log_error ("p15: PIN is too short; minimum length is %u\n", min_length); err = gpg_error (GPG_ERR_BAD_PIN); } else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length) @@ -5269,7 +5307,6 @@ verify_pin (app_t app, switch (aodf->pintype) { case PIN_TYPE_BCD: - case PIN_TYPE_ASCII_NUMERIC: for (s=pinvalue; digitp (s); s++) ; if (*s) @@ -5278,6 +5315,15 @@ verify_pin (app_t app, err = gpg_error (GPG_ERR_BAD_PIN); } break; + case PIN_TYPE_ASCII_NUMERIC: + for (s=pinvalue; *s && !(*s & 0x80); s++) + ; + if (*s) + { + errstr = "Non-ascii characters found in PIN"; + err = gpg_error (GPG_ERR_BAD_PIN); + } + break; case PIN_TYPE_UTF8: break; case PIN_TYPE_HALF_NIBBLE_BCD: @@ -6030,7 +6076,7 @@ do_with_keygrip (app_t app, ctrl_t ctrl, int action, goto leave; } - send_keyinfo (ctrl, as_data, prkdf->keygrip, serialno, keyref); + send_keyinfo (ctrl, as_data, prkdf->keygrip, serialno, keyref, NULL); xfree (keyref); if (want_keygripstr) { diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 1425b43..4b9f7fc 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -483,7 +483,8 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ - err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), + fid, "PrKDF", &buffer, &buflen, 255); if (err) return err; @@ -831,7 +832,7 @@ read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, xfree (buffer); buffer = NULL; buflen = 0; - err = select_and_read_binary (app->slot, + err = select_and_read_binary (app_get_slot (app), ((SC_HSM_EE_PREFIX << 8) | (fid & 0xFF)), "CertEF", &buffer, &buflen, 1); if (!err && buffer[0] == 0x30) @@ -952,7 +953,8 @@ read_ef_cd (app_t app, unsigned short fid, cdf_object_t *result) if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ - err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen, 255); + err = select_and_read_binary (app_get_slot (app), fid, "CDF", + &buffer, &buflen, 255); if (err) return err; @@ -1201,7 +1203,7 @@ read_serialno(app_t app) size_t n, objlen, hdrlen, chrlen; int class, tag, constructed, ndef; - err = select_and_read_binary (app->slot, 0x2F02, "EF.C_DevAut", + err = select_and_read_binary (app_get_slot (app), 0x2F02, "EF.C_DevAut", &buffer, &buflen, 512); if (err) return err; @@ -1259,7 +1261,7 @@ read_meta (app_t app) if (err) return err; - err = list_ef (app->slot, &eflist, &eflistlen); + err = list_ef (app_get_slot (app), &eflist, &eflistlen); if (err) return err; @@ -1405,7 +1407,7 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; - if ((flags & 1)) + if ((flags & APP_LEARN_FLAG_KEYPAIRINFO)) err = 0; else { @@ -1453,7 +1455,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf, return 0; } - err = select_and_read_binary (app->slot, cdf->fid, "CD", + err = select_and_read_binary (app_get_slot (app), cdf->fid, "CD", &buffer, &buflen, 4096); if (err) { @@ -1692,8 +1694,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), char *prompt; int sw; - sw = apdu_send_simple (app->slot, 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, - -1, NULL); + sw = apdu_send_simple (app_get_slot (app), + 0, 0x00, ISO7816_VERIFY, 0x00, 0x81, -1, NULL); if (sw == SW_SUCCESS) return 0; /* PIN already verified */ @@ -1718,7 +1720,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), prompt = "||Please enter the PIN"; if (!opt.disable_pinpad - && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { err = pincb (pincb_arg, prompt, NULL); if (err) @@ -1727,7 +1729,7 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify_kp (app->slot, 0x81, &pininfo); + err = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else @@ -1739,7 +1741,8 @@ verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), return err; } - err = iso7816_verify (app->slot, 0x81, pinvalue, strlen(pinvalue)); + err = iso7816_verify (app_get_slot (app), + 0x81, pinvalue, strlen(pinvalue)); xfree (pinvalue); } if (err) @@ -1884,7 +1887,8 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x68, prkdf->key_reference, algoid, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x68, prkdf->key_reference, algoid, cdsblklen, cdsblk, 0, outdata, outdatalen); return iso7816_map_sw (sw); } @@ -2021,7 +2025,8 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, if (err) return err; - sw = apdu_send_le (app->slot, 1, 0x80, 0x62, prkdf->key_reference, 0x21, + sw = apdu_send_le (app_get_slot (app), + 1, 0x80, 0x62, prkdf->key_reference, 0x21, p1blklen, p1blk, 0, &rspdata, &rspdatalen); err = iso7816_map_sw (sw); if (err) @@ -2047,7 +2052,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, gpg_error_t app_select_sc_hsm (app_t app) { - int slot = app->slot; + int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, sc_hsm_aid, sizeof sc_hsm_aid, 0); @@ -66,6 +66,20 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) } +/* Map a cardtype to a string. Never returns NULL. */ +const char * +strcardtype (cardtype_t t) +{ + switch (t) + { + case CARDTYPE_GENERIC: return "generic"; + case CARDTYPE_GNUK: return "gnuk"; + case CARDTYPE_YUBIKEY: return "yubikey"; + case CARDTYPE_ZEITCONTROL: return "zeitcontrol"; + } + return "?"; +} + /* Map an application type to a string. Never returns NULL. */ const char * strapptype (apptype_t t) @@ -803,8 +817,18 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We do not send APPTYPE if only keypairinfo is requested. */ - if (app->apptype && !(flags & 1)) - send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); + if (!(flags & APP_LEARN_FLAG_KEYPAIRINFO)) + { + if (app->cardtype) + send_status_direct (ctrl, "CARDTYPE", strcardtype (app->cardtype)); + if (app->cardversion) + send_status_printf (ctrl, "CARDVERSION", "%X", app->cardversion); + if (app->apptype) + send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); + if (app->appversion) + send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); + } + err = lock_app (app, ctrl); if (err) return err; @@ -885,6 +909,11 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name) if (!app->ref_count) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (name && !strcmp (name, "CARDTYPE")) + { + send_status_direct (ctrl, "CARDTYPE", strcardtype (app->cardtype)); + return 0; + } if (app->apptype && name && !strcmp (name, "APPTYPE")) { send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); diff --git a/scd/command.c b/scd/command.c index 925fd75..363e2f0 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1083,10 +1083,10 @@ cmd_writekey (assuan_context_t ctx, char *line) static const char hlp_genkey[] = - "GENKEY [--force] [--timestamp=<isodate>] <no>\n" + "GENKEY [--force] [--timestamp=<isodate>] [--algo=ALGO] <keyref>\n" "\n" - "Generate a key on-card identified by NO, which is application\n" - "specific. Return values are application specific. For OpenPGP\n" + "Generate a key on-card identified by <keyref>, which is application\n" + "specific. Return values are also application specific. For OpenPGP\n" "cards 3 status lines are returned:\n" "\n" " S KEY-FPR <hexstring>\n" @@ -1105,16 +1105,21 @@ static const char hlp_genkey[] = "value. The value needs to be in ISO Format; e.g.\n" "\"--timestamp=20030316T120000\" and after 1970-01-01 00:00:00.\n" "\n" + "The option --algo can be used to request creation using a specific\n" + "algorithm. The possible algorithms are card dependent.\n" + "\n" "The public part of the key can also later be retrieved using the\n" "READKEY command."; static gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int rc; - char *save_line; + gpg_error_t err; + char *keyref_buffer = NULL; + char *keyref; int force; const char *s; + char *opt_algo = NULL; time_t timestamp; force = has_option (line, "--force"); @@ -1130,39 +1135,41 @@ cmd_genkey (assuan_context_t ctx, char *line) else timestamp = 0; + err = get_option_value (line, "--algo", &opt_algo); + if (err) + goto leave; line = skip_options (line); if (!*line) - { - rc = set_error (GPG_ERR_ASS_PARAMETER, "no key number given"); - goto leave; - } - save_line = line; + return set_error (GPG_ERR_ASS_PARAMETER, "no key number given"); + keyref = line; while (*line && !spacep (line)) line++; *line = 0; - if ((rc = open_card (ctrl))) + if ((err = open_card (ctrl))) goto leave; if (!ctrl->app_ctx) { - rc = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); goto leave; } - { - char *tmp = xtrystrdup (save_line); - if (!tmp) - return gpg_error_from_syserror (); - rc = app_genkey (ctrl->app_ctx, ctrl, tmp, NULL, - force? APP_GENKEY_FLAG_FORCE : 0, - timestamp, pin_cb, ctx); - xfree (tmp); - } + keyref = keyref_buffer = xtrystrdup (keyref); + if (!keyref) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = app_genkey (ctrl->app_ctx, ctrl, keyref, opt_algo, + force? APP_GENKEY_FLAG_FORCE : 0, + timestamp, pin_cb, ctx); leave: - return rc; + xfree (keyref_buffer); + xfree (opt_algo); + return err; } @@ -1705,7 +1712,10 @@ cmd_apdu (assuan_context_t ctx, char *line) apdu, apdulen, handle_more, NULL, &result, &resultlen); if (rc) - log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc)); + { + log_error ("apdu_send_direct failed: %s\n", apdu_strerror (rc)); + rc = iso7816_map_sw (rc); + } else { rc = assuan_send_data (ctx, result, resultlen); @@ -1903,14 +1913,15 @@ scd_command_handler (ctrl_t ctrl, int fd) * data line, else as a status line. */ void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, - const char *serialno, const char *idstr) + const char *serialno, const char *idstr, const char *usage) { char *string; assuan_context_t ctx = ctrl->server_local->assuan_ctx; - string = xtryasprintf ("%s T %s %s%s", keygrip_str, + string = xtryasprintf ("%s T %s %s %s%s", keygrip_str, serialno? serialno : "-", idstr? idstr : "-", + usage? usage : "-", data? "\n" : ""); if (!string) diff --git a/scd/iso7816.c b/scd/iso7816.c index c878a03..35bf282 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -43,6 +43,7 @@ #define CMD_SELECT_FILE 0xA4 +#define CMD_SELECT_DATA 0xA5 #define CMD_VERIFY ISO7816_VERIFY #define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA #define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER @@ -56,6 +57,7 @@ #define CMD_GET_CHALLENGE 0x84 #define CMD_READ_BINARY 0xB0 #define CMD_READ_RECORD 0xB2 +#define CMD_UPDATE_BINARY 0xD6 static gpg_error_t map_sw (int sw) @@ -444,6 +446,44 @@ iso7816_reset_retry_counter (int slot, int chvno, } +/* Perform a SELECT DATA command to OCCURANCE of TAG. */ +gpg_error_t +iso7816_select_data (int slot, int occurrence, int tag) +{ + int sw; + int datalen; + unsigned char data[7]; + + data[0] = 0x60; + data[2] = 0x5c; + if (tag <= 0xff) + { + data[3] = 1; + data[4] = tag; + datalen = 5; + } + else if (tag <= 0xffff) + { + data[3] = 2; + data[4] = (tag >> 8); + data[5] = tag; + datalen = 6; + } + else + { + data[3] = 3; + data[4] = (tag >> 16); + data[5] = (tag >> 8); + data[6] = tag; + datalen = 7; + } + data[1] = datalen - 2; + + sw = apdu_send_le (slot, 0, 0x00, CMD_SELECT_DATA, + occurrence, 0x04, datalen, data, 0, NULL, NULL); + return map_sw (sw); +} + /* Perform a GET DATA command requesting TAG and storing the result in a newly allocated buffer at the address passed by RESULT. Return @@ -905,7 +945,7 @@ iso7816_read_binary_ext (int slot, int extended_mode, if (r_sw) *r_sw = sw; - if (*result && sw == SW_BAD_P0_P1) + if (*result && (sw == SW_BAD_P0_P1 || sw == SW_INCORRECT_P0_P1)) { /* Bad Parameter means that the offset is outside of the EF. When reading all data we take this as an indication @@ -1025,6 +1065,7 @@ iso7816_read_record_ext (int slot, int recno, int reccount, int short_ef, return 0; } + gpg_error_t iso7816_read_record (int slot, int recno, int reccount, int short_ef, unsigned char **result, size_t *resultlen) @@ -1032,3 +1073,23 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, return iso7816_read_record_ext (slot, recno, reccount, short_ef, result, resultlen, NULL); } + + +/* Perform an UPDATE BINARY command on card in SLOT. Write DATA of + * length DATALEN to a transparent file at OFFSET. */ +gpg_error_t +iso7816_update_binary (int slot, int extended_mode, size_t offset, + const void *data, size_t datalen) +{ + int sw; + + /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus + * we check for this limit. */ + if (offset > 32767) + return gpg_error (GPG_ERR_INV_VALUE); + + sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_UPDATE_BINARY, + ((offset>>8) & 0xff), (offset & 0xff), + datalen, (const char*)data); + return map_sw (sw); +} diff --git a/scd/iso7816.h b/scd/iso7816.h index d22f0be..1e8a776 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -93,6 +93,7 @@ gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno, const char *data, size_t datalen); +gpg_error_t iso7816_select_data (int slot, int occurrence, int tag); gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag, @@ -150,5 +151,7 @@ gpg_error_t iso7816_read_record_ext (int slot, int recno, int reccount, gpg_error_t iso7816_read_record (int slot, int recno, int reccount, int short_ef, unsigned char **result, size_t *resultlen); +gpg_error_t iso7816_update_binary (int slot, int extended_mode, size_t offset, + const void *data, size_t datalen); #endif /*ISO7816_H*/ diff --git a/scd/scdaemon.h b/scd/scdaemon.h index bb7c1b0..bb7e04c 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -126,7 +126,8 @@ const char *scd_get_socket_name (void); gpg_error_t initialize_module_command (void); int scd_command_handler (ctrl_t, int); void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str, - const char *serialno, const char *idstr); + const char *serialno, const char *idstr, + const char *usage); void send_status_info (ctrl_t ctrl, const char *keyword, ...) GPGRT_ATTR_SENTINEL(1); gpg_error_t send_status_direct (ctrl_t ctrl, const char *keyword, diff --git a/scd/scdaemon.w32-manifest.in b/scd/scdaemon.w32-manifest.in index 22dabb6..7a0c12e 100644 --- a/scd/scdaemon.w32-manifest.in +++ b/scd/scdaemon.w32-manifest.in @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> -<description>GNU Privacy Guard (Scmartcard daemon)</description> +<description>GNU Privacy Guard (Smartcard Daemon)</description> <assemblyIdentity type="win32" name="GnuPG.scdaemon" @@ -15,4 +15,11 @@ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista --> </application> </compatibility> +<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> +</trustInfo> </assembly> |