diff --git a/scd/app-common.h b/scd/app-common.h index 5866c9b32..9fadafd6a 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -1,325 +1,327 @@ /* app-common.h - Common declarations for all card applications * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * $Id$ */ #ifndef GNUPG_SCD_APP_COMMON_H #define GNUPG_SCD_APP_COMMON_H #include #include /* Flags used with app_change_pin. */ #define APP_CHANGE_FLAG_RESET 1 /* PIN Reset mode. */ #define APP_CHANGE_FLAG_NULLPIN 2 /* NULL PIN mode. */ #define APP_CHANGE_FLAG_CLEAR 4 /* Clear the given PIN. */ /* Flags used with app_genkey. */ #define APP_GENKEY_FLAG_FORCE 1 /* Force overwriting existing key. */ /* Flags used with app_writekey. */ #define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */ /* Flags used with app_readkey. */ #define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */ -/* Bit flags set by the decipher function into R_INFO. */ +/* Flags set by the decipher function into R_INFO. */ #define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */ +/* Flags used by the app_write_learn_status. */ +#define APP_LEARN_FLAG_KEYPAIRINFO 1 /* Return only keypair infos. */ /* List of supported card types. Generic is the usual ISO7817-4 * compliant card. More specific card or token versions can be given * here. Use strcardtype() to map them to a string. */ typedef enum { CARDTYPE_GENERIC = 0, CARDTYPE_YUBIKEY } cardtype_t; /* List of supported card applications. The source code for each * application can usually be found in an app-NAME.c file. Use * strapptype() to map them to a string. */ typedef enum { APPTYPE_NONE = 0, APPTYPE_UNDEFINED, APPTYPE_OPENPGP, APPTYPE_PIV, APPTYPE_NKS, APPTYPE_P15, APPTYPE_GELDKARTE, APPTYPE_DINSIG, APPTYPE_SC_HSM } apptype_t; /* Forward declarations. */ struct card_ctx_s; struct app_ctx_s; struct app_local_s; /* Defined by all app-*.c. */ typedef struct card_ctx_s *card_t; typedef struct app_ctx_s *app_t; /* The object describing a card. */ struct card_ctx_s { card_t next; npth_mutex_t lock; /* Number of connections currently using this application context. */ unsigned int ref_count; /* Used reader slot. */ int slot; cardtype_t cardtype; /* The token's type. */ unsigned int cardversion;/* Firmware version of the token or 0. */ unsigned int card_status; /* The serial number is associated with the card and not with a * specific app. If a card uses different serial numbers for its * applications, our code picks the serial number of a specific * application and uses that. */ unsigned char *serialno; /* Serialnumber in raw form, allocated. */ size_t serialnolen; /* Length in octets of serialnumber. */ /* A linked list of applications used on this card. The app at the * head of the list is the currently active app; To work with the * other apps, switching to that app might be needed. Switching will * put the active app at the head of the list. */ app_t app; /* Various flags. */ unsigned int reset_requested:1; unsigned int periodical_check_needed:1; }; /* The object describing a card's applications. A card may have * several applications and it is usuallay required to explicity * switch between applications. */ struct app_ctx_s { app_t next; card_t card; /* Link back to the card. */ apptype_t apptype; /* The type of the application. */ unsigned int appversion; /* Version of the application or 0. */ unsigned int did_chv1:1; unsigned int force_chv1:1; /* True if the card does not cache CHV1. */ unsigned int did_chv2:1; unsigned int did_chv3:1; struct app_local_s *app_local; /* Local to the application. */ struct { void (*deinit) (app_t app); gpg_error_t (*reselect) (app_t app, ctrl_t ctrl); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags); gpg_error_t (*readcert) (app_t app, const char *certid, unsigned char **cert, size_t *certlen); gpg_error_t (*readkey) (app_t app, ctrl_t ctrl, const char *certid, unsigned int flags, unsigned char **pk, size_t *pklen); gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name); gpg_error_t (*setattr) (app_t app, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); gpg_error_t (*sign) (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); gpg_error_t (*auth) (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); gpg_error_t (*decipher) (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info); gpg_error_t (*writecert) (app_t app, ctrl_t ctrl, const char *certid, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg, const unsigned char *data, size_t datalen); gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg, const unsigned char *pk, size_t pklen); gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, const char *keyref, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); gpg_error_t (*check_pin) (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action, const char *keygrip_str); } fnc; }; /* Action values for app_do_with_keygrip. */ enum { KEYGRIP_ACTION_SEND_DATA, KEYGRIP_ACTION_WRITE_STATUS, KEYGRIP_ACTION_LOOKUP }; /* Helper to get the slot from an APP object. */ static inline int app_get_slot (app_t app) { if (app && app->card) return app->card->slot; return -1; } /*-- app-help.c --*/ unsigned int app_help_count_bits (const unsigned char *a, size_t len); gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen, unsigned char **r_pk, size_t *r_pklen); size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); /*-- app.c --*/ const char *strcardtype (cardtype_t t); const char *strapptype (apptype_t t); void app_update_priority_list (const char *arg); gpg_error_t app_send_card_list (ctrl_t ctrl); char *card_get_serialno (card_t card); char *app_get_serialno (app_t app); void app_dump_state (void); void application_notify_card_reset (int slot); gpg_error_t check_application_conflict (card_t card, const char *name, const unsigned char *serialno_bin, size_t serialno_bin_len); gpg_error_t card_reset (card_t card, ctrl_t ctrl, int send_reset); gpg_error_t select_application (ctrl_t ctrl, const char *name, card_t *r_app, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len); gpg_error_t select_additional_application (ctrl_t ctrl, const char *name); char *get_supported_applications (void); card_t card_ref (card_t card); void card_unref (card_t card); void card_unref_locked (card_t card); gpg_error_t app_munge_serialno (card_t card); gpg_error_t app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags); gpg_error_t app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen); gpg_error_t app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags, unsigned char **pk, size_t *pklen); gpg_error_t app_getattr (card_t card, ctrl_t ctrl, const char *name); gpg_error_t app_setattr (card_t card, ctrl_t ctrl, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen); gpg_error_t app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); gpg_error_t app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); gpg_error_t app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info); gpg_error_t app_writecert (card_t card, ctrl_t ctrl, const char *certidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); gpg_error_t app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen); gpg_error_t app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); gpg_error_t app_get_challenge (card_t card, ctrl_t ctrl, size_t nbytes, unsigned char *buffer); gpg_error_t app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); gpg_error_t app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg); card_t app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str); /*-- app-openpgp.c --*/ gpg_error_t app_select_openpgp (app_t app); /*-- app-nks.c --*/ gpg_error_t app_select_nks (app_t app); /*-- app-dinsig.c --*/ gpg_error_t app_select_dinsig (app_t app); /*-- app-p15.c --*/ gpg_error_t app_select_p15 (app_t app); /*-- app-geldkarte.c --*/ gpg_error_t app_select_geldkarte (app_t app); /*-- app-sc-hsm.c --*/ gpg_error_t app_select_sc_hsm (app_t app); /*-- app-piv.c --*/ gpg_error_t app_select_piv (app_t app); #endif /*GNUPG_SCD_APP_COMMON_H*/ diff --git a/scd/app-nks.c b/scd/app-nks.c index 898a838c3..bb5329bfe 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -1,1473 +1,1473 @@ /* app-nks.c - The Telesec NKS card application. * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* 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. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "../common/i18n.h" #include "iso7816.h" #include "../common/tlv.h" #include "apdu.h" #include "../common/host2net.h" static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 }; static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 }; static struct { int is_sigg; /* Valid for SigG application. */ int fid; /* File ID. */ int nks_ver; /* 0 for NKS version 2, 3 for version 3. */ int certtype; /* Type of certificate or 0 if it is not a certificate. */ int iskeypair; /* If true has the FID of the corresponding certificate. */ int issignkey; /* True if file is a key usable for signing. */ int isenckey; /* 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, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ { 0, 0x4331, 0, 100 }, { 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, 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, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ { 1, 0x4531, 3, 0, 0xC000, 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 */ { 0, 0 } }; /* Object with application (i.e. NKS) specific data. */ struct app_local_s { int nks_version; /* NKS version. */ 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 need_app_select; /* Need to re-select the application. */ }; static gpg_error_t switch_application (app_t app, int enable_sigg); /* Release local data. */ static void do_deinit (app_t app) { if (app && app->app_local) { xfree (app->app_local); app->app_local = NULL; } } static int all_zero_p (void *buffer, size_t length) { char *p; for (p=buffer; length; length--, p++) if (*p) return 0; return 1; } /* Read the file with FID, assume it contains a public key and return its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ static gpg_error_t keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr) { gpg_error_t err; unsigned char grip[20]; unsigned char *buffer[2]; size_t buflen[2]; gcry_sexp_t sexp; int i; int offset[2] = { 0, 0 }; err = iso7816_select_file (app_get_slot (app), fid, 0); if (err) return err; err = iso7816_read_record (app_get_slot (app), 1, 1, 0, &buffer[0], &buflen[0]); if (err) return err; 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) { /* Old versions of NKS store the values in a TLV encoded format. We need to do some checks. */ for (i=0; i < 2; i++) { /* Check that the value appears like an integer encoded as Simple-TLV. We don't check the tag because the tests cards I have use 1 for both, the modulus and the exponent - the example in the documentation gives 2 for the exponent. */ if (buflen[i] < 3) err = gpg_error (GPG_ERR_TOO_SHORT); else if (buffer[i][1] != buflen[i]-2 ) err = gpg_error (GPG_ERR_INV_OBJ); else offset[i] = 2; } } else { /* Remove leading zeroes to get a correct keygrip. Take care of negative numbers. We should also fix it the same way in libgcrypt but we can't yet rely on it yet. */ for (i=0; i < 2; i++) { while (buflen[i]-offset[i] > 1 && !buffer[i][offset[i]] && !(buffer[i][offset[i]+1] & 0x80)) offset[i]++; } } /* Check whether negative values are not prefixed with a zero and fix that. */ for (i=0; i < 2; i++) { if ((buflen[i]-offset[i]) && (buffer[i][offset[i]] & 0x80)) { unsigned char *newbuf; size_t newlen; newlen = 1 + buflen[i] - offset[i]; newbuf = xtrymalloc (newlen); if (!newlen) { xfree (buffer[0]); xfree (buffer[1]); return gpg_error_from_syserror (); } newbuf[0] = 0; memcpy (newbuf+1, buffer[i]+offset[i], buflen[i] - offset[i]); xfree (buffer[i]); buffer[i] = newbuf; buflen[i] = newlen; offset[i] = 0; } } 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]); xfree (buffer[0]); xfree (buffer[1]); if (err) return err; if (!gcry_pk_get_keygrip (sexp, grip)) { err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by libgcrypt. */ } else { bin2hex (grip, 20, r_gripstr); } gcry_sexp_release (sexp); return err; } /* 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: -1 = Error retrieving the data, -2 = No such PIN, -3 = PIN blocked, -4 = NullPIN activ, n >= 0 = Number of verification attempts left. */ static int get_chv_status (app_t app, int sigg, int pwid) { unsigned char *result = NULL; size_t resultlen; char command[4]; int rc; if (switch_application (app, sigg)) return sigg? -2 : -1; /* No such PIN / General error. */ command[0] = 0x00; command[1] = 0x20; command[2] = 0x00; command[3] = pwid; if (apdu_send_direct (app_get_slot (app), 0, (unsigned char *)command, 4, 0, NULL, &result, &resultlen)) rc = -1; /* Error. */ else if (resultlen < 2) rc = -1; /* Error. */ else { unsigned int sw = buf16_to_uint (result+resultlen-2); if (sw == 0x6a88) rc = -2; /* No such PIN. */ else if (sw == 0x6983) rc = -3; /* PIN is blocked. */ else if (sw == 0x6985) rc = -4; /* NullPIN is activ. */ else if ((sw & 0xfff0) == 0x63C0) rc = (sw & 0x000f); /* PIN has N tries left. */ else rc = -1; /* Other error. */ } xfree (result); return rc; } /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { const char *name; int special; } table[] = { { "$AUTHKEYID", 1 }, { "$ENCRKEYID", 2 }, { "$SIGNKEYID", 3 }, { "NKS-VERSION", 4 }, { "CHV-STATUS", 5 }, { NULL, 0 } }; gpg_error_t err = 0; int idx; char buffer[100]; err = switch_application (app, 0); if (err) return err; for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); switch (table[idx].special) { 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"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } break; case 2: /* $ENCRKEYID */ { char const tmp[] = "NKS-NKS3.45B1"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } break; case 3: /* $SIGNKEYID */ { char const tmp[] = "NKS-NKS3.4531"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } break; case 4: /* NKS-VERSION */ snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version); 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. */ 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]); send_status_info (ctrl, table[idx].name, buffer, strlen (buffer), NULL, 0); } break; default: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; } return err; } static void do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg) { gpg_error_t err; char ct_buf[100], id_buf[100]; int i; const char *tag; const char *usage; if (is_sigg) tag = "SIGG"; else if (app->app_local->nks_version < 3) tag = "DF01"; else tag = "NKS3"; /* 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) continue; if (!!filelist[i].is_sigg != !!is_sigg) 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_get_slot (app), filelist[i].fid, NULL); if (len) { /* FIXME: We should store the length in the application's context so that a following readcert does only need to read that many bytes. */ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype); snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", tag, filelist[i].fid); send_status_info (ctrl, "CERTINFO", ct_buf, strlen (ct_buf), id_buf, strlen (id_buf), NULL, (size_t)0); } } else if (filelist[i].iskeypair) { char gripstr[40+1]; err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr); if (err) log_error ("can't get keygrip from FID 0x%04X: %s\n", filelist[i].fid, gpg_strerror (err)); else { 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 = ""; send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, id_buf, strlen (id_buf), usage, strlen (usage), NULL, (size_t)0); } } } } static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; err = switch_application (app, 0); if (err) return err; do_learn_status_core (app, ctrl, flags, 0); err = switch_application (app, 1); if (err) return 0; /* Silently ignore if we can't switch to SigG. */ do_learn_status_core (app, ctrl, flags, 1); return 0; } /* Read the certificate with id CERTID (as returned by learn_status in the CERTINFO status lines) and return it in the freshly allocated buffer put into CERT and the length of the certificate put into CERTLEN. */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { int i, fid; gpg_error_t err; unsigned char *buffer; const unsigned char *p; size_t buflen, n; int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca = 0; 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_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_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error reading certificate from FID 0x%04X: %s\n", fid, gpg_strerror (err)); return err; } if (!buflen || *buffer == 0xff) { log_info ("no certificate contained in FID 0x%04X\n", fid); err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } /* Now figure something out about the object. */ p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed ) ; else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) rootca = 1; else return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; assert (totobjlen <= buflen); err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (rootca) ; else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) { const unsigned char *save_p; /* The certificate seems to be contained in a userCertificate container. Skip this and assume the following sequence is the certificate. */ if (n < objlen) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } p += objlen; n -= objlen; save_p = p; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) return gpg_error (GPG_ERR_INV_OBJ); totobjlen = objlen + hdrlen; assert (save_p + totobjlen <= buffer + buflen); memmove (buffer, save_p, totobjlen); } *cert = buffer; buffer = NULL; *certlen = totobjlen; leave: xfree (buffer); return err; } /* 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 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 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 }; /* We use a generic name to retrieve PK.AUT.IFD-SPK. */ if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) ; else /* Return the error code expected by cmd_readkey. */ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* Access the KEYD file which is always in the master directory. */ err = iso7816_select_path (app_get_slot (app), path, DIM (path)); 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_get_slot (app), 5, 1, 0, &buffer[0], &buflen[0]); 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_get_slot (app), 6, 1, 0, &buffer[1], &buflen[1]); if (err) { xfree (buffer[0]); return err; } if ((flags & APP_READKEY_FLAG_INFO)) { /* Not yet implemented but we won't get here for any regular * keyrefs anyway, thus the top layer will provide the * keypairinfo from the certificate. */ (void)ctrl; } if (pk && pklen) { *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]); return err; } /* Handle the WRITEKEY command for NKS. This function expects a canonical encoded S-expression with the public key in KEYDATA and its length in KEYDATALEN. The only supported KEYID is "$IFDAUTHKEY" to store the terminal key on the card. Bit 0 of FLAGS indicates whether an existing key shall get overwritten. PINCB and PINCB_ARG are the usual arguments for the pinentry callback. */ static gpg_error_t do_writekey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen) { gpg_error_t err; int force = (flags & 1); const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; size_t rsa_n_len, rsa_e_len; unsigned int nbits; (void)ctrl; (void)pincb; (void)pincb_arg; if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3) ; else return gpg_error (GPG_ERR_INV_ID); if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL)) return gpg_error (GPG_ERR_EEXIST); /* Parse the S-expression. */ err = get_rsa_pk_from_canon_sexp (keydata, keydatalen, &rsa_n, &rsa_n_len, &rsa_e, &rsa_e_len); if (err) goto leave; /* Check that the parameters match the requirements. */ nbits = app_help_count_bits (rsa_n, rsa_n_len); if (nbits != 1024) { log_error (_("RSA modulus missing or not of size %d bits\n"), 1024); err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } nbits = app_help_count_bits (rsa_e, rsa_e_len); if (nbits < 2 || nbits > 32) { log_error (_("RSA public exponent missing or larger than %d bits\n"), 32); err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } /* /\* Store them. *\/ */ /* err = verify_pin (app, 0, NULL, pincb, pincb_arg); */ /* if (err) */ /* goto leave; */ /* Send the MSE:Store_Public_Key. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* mse = xtrymalloc (1000); */ /* mse[0] = 0x80; /\* Algorithm reference. *\/ */ /* mse[1] = 1; */ /* mse[2] = 0x17; */ /* mse[3] = 0x84; /\* Private key reference. *\/ */ /* mse[4] = 1; */ /* mse[5] = 0x77; */ /* mse[6] = 0x7F; /\* Public key parameter. *\/ */ /* mse[7] = 0x49; */ /* mse[8] = 0x81; */ /* mse[9] = 3 + 0x80 + 2 + rsa_e_len; */ /* mse[10] = 0x81; /\* RSA modulus of 128 byte. *\/ */ /* mse[11] = 0x81; */ /* mse[12] = rsa_n_len; */ /* memcpy (mse+12, rsa_n, rsa_n_len); */ /* 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_get_slot (app), 0x81, 0xB6, */ /* mse, sizeof mse); */ leave: return err; } static gpg_error_t basic_pin_checks (const char *pinvalue, int minlen, int maxlen) { if (strlen (pinvalue) < minlen) { log_error ("PIN is too short; minimum length is %d\n", minlen); return gpg_error (GPG_ERR_BAD_PIN); } if (strlen (pinvalue) > maxlen) { log_error ("PIN is too large; maximum length is %d\n", maxlen); return gpg_error (GPG_ERR_BAD_PIN); } return 0; } /* Verify the PIN if required. */ 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) { pininfo_t pininfo; int rc; if (!desc) desc = "PIN"; memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; pininfo.minlen = 6; pininfo.maxlen = 16; if (!opt.disable_pinpad && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { rc = pincb (pincb_arg, desc, NULL); if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } 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); if (rc) { log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); return rc; } rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen); if (rc) { xfree (pinvalue); return rc; } rc = iso7816_verify (app_get_slot (app), pwid, pinvalue, strlen (pinvalue)); xfree (pinvalue); } if (rc) { if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS ) log_error (_("the NullPIN has not yet been changed\n")); else log_error ("verify PIN failed\n"); return rc; } return 0; } /* Create the signature and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that in the 3rd argument. */ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; int rc, i; int is_sigg = 0; int fid; unsigned char kid; unsigned char data[83]; /* Must be large enough for a SHA-1 digest + the largest OID prefix. */ size_t datalen; 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); } /* 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; if (is_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); memcpy (data, indata, indatalen); datalen = indatalen; } else if (indatalen == 35) { /* Alright, the caller was so kind to send us an already prepared DER object. This is for TCOS 2. */ if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) ; else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata,rmd160_prefix,15)) ; else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); memcpy (data, indata, indatalen); 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; } else return gpg_error (GPG_ERR_INV_VALUE); /* Send an MSE for PSO:Computer_Signature. */ if (app->app_local->nks_version > 2) { unsigned char mse[6]; 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_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } /* Verify using PW1.CH. */ if (!rc) rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Compute the signature. */ if (!rc) rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, outdata, outdatalen); return rc; } /* Decrypt the data in INDATA and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ static gpg_error_t do_decipher (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { int rc, i; int is_sigg = 0; int fid; int kid; (void)r_info; if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); /* Check that the provided ID is valid. This is not really needed but we do it to 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; if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || keyidstr[4]) return gpg_error (GPG_ERR_INV_ID); fid = xtoi_4 (keyidstr); for (i=0; filelist[i].fid; i++) if (filelist[i].iskeypair && filelist[i].fid == fid) break; if (!filelist[i].fid) return gpg_error (GPG_ERR_NOT_FOUND); if (!filelist[i].isenckey) return gpg_error (GPG_ERR_INV_ID); kid = filelist[i].kid; if (app->app_local->nks_version > 2) { unsigned char mse[6]; mse[0] = 0x80; /* Algorithm reference. */ mse[1] = 1; mse[2] = 0x0a; /* RSA no padding. (0x1A is pkcs#1.5 padding.) */ mse[3] = 0x84; /* Private key reference. */ mse[4] = 1; mse[5] = kid; rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, mse, sizeof mse); } else { 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_get_slot (app), 0xC1, 0xB8, mse, sizeof mse); } if (!rc) rc = verify_pin (app, 0, NULL, pincb, pincb_arg); /* Note that we need to use extended length APDUs for TCOS 3 cards. Command chaining does not work. */ if (!rc) rc = iso7816_decipher (app_get_slot (app), app->app_local->nks_version > 2? 1:0, indata, indatalen, 0, 0x81, outdata, outdatalen); return rc; } /* 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 */ static const char * parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid) { const char *desc; if (!pwidstr) desc = NULL; else if (!strcmp (pwidstr, "PW1.CH")) { *r_sigg = 0; *r_pwid = 0x00; /* TRANSLATORS: Do not translate the "|*|" prefixes but keep them verbatim at the start of the string. */ desc = (new_mode ? _("|N|Please enter a new PIN for the standard keys.") : _("||Please enter the PIN for the standard keys.")); } else if (!strcmp (pwidstr, "PW2.CH")) { *r_pwid = 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")) { *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")) { *r_pwid = 0x83; /* Yes, that is 83 and not 82. */ *r_sigg = 1; 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 { *r_pwid = 0; /* Only to avoid gcc warning in calling function. */ desc = NULL; /* Error. */ } return desc; } /* Handle the PASSWD command. See parse_pwidstr() for allowed values for CHVNOSTR. */ static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; char *newpin = NULL; char *oldpin = NULL; size_t newpinlen; size_t oldpinlen; int is_sigg; const char *newdesc; int pwid; pininfo_t pininfo; (void)ctrl; /* The minimum length is enforced by TCOS, the maximum length is just a reasonable value. */ memset (&pininfo, 0, sizeof pininfo); pininfo.minlen = 6; pininfo.maxlen = 16; newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &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); if (err) return err; if ((flags & APP_CHANGE_FLAG_NULLPIN)) { /* With the nullpin flag, we do not verify the PIN - it would fail if the Nullpin is still set. */ oldpin = xtrycalloc (1, 6); if (!oldpin) { err = gpg_error_from_syserror (); goto leave; } oldpinlen = 6; } else { const char *desc; int dummy1, dummy2; if ((flags & APP_CHANGE_FLAG_RESET)) { /* Reset mode: Ask for the alternate PIN. */ const char *altpwidstr; if (!strcmp (pwidstr, "PW1.CH")) altpwidstr = "PW2.CH"; else if (!strcmp (pwidstr, "PW2.CH")) altpwidstr = "PW1.CH"; else if (!strcmp (pwidstr, "PW1.CH.SIG")) altpwidstr = "PW2.CH.SIG"; else if (!strcmp (pwidstr, "PW2.CH.SIG")) altpwidstr = "PW1.CH.SIG"; else { err = gpg_error (GPG_ERR_BUG); goto leave; } desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2); } else { /* Regular change mode: Ask for the old PIN. */ desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2); } err = pincb (pincb_arg, desc, &oldpin); if (err) { log_error ("error getting old PIN: %s\n", gpg_strerror (err)); goto leave; } oldpinlen = strlen (oldpin); err = basic_pin_checks (oldpin, pininfo.minlen, pininfo.maxlen); if (err) goto leave; } err = pincb (pincb_arg, newdesc, &newpin); if (err) { log_error (_("error getting new PIN: %s\n"), gpg_strerror (err)); goto leave; } newpinlen = strlen (newpin); err = basic_pin_checks (newpin, pininfo.minlen, pininfo.maxlen); if (err) goto leave; if ((flags & APP_CHANGE_FLAG_RESET)) { char *data; size_t datalen = oldpinlen + newpinlen; data = xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } memcpy (data, oldpin, oldpinlen); memcpy (data+oldpinlen, newpin, newpinlen); err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), pwid, data, datalen); wipememory (data, datalen); xfree (data); } else err = iso7816_change_reference_data (app_get_slot (app), pwid, oldpin, oldpinlen, newpin, newpinlen); leave: xfree (oldpin); xfree (newpin); return err; } /* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */ static gpg_error_t do_check_pin (app_t app, const char *pwidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; int pwid; int is_sigg; const char *desc; desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid); if (!desc) return gpg_error (GPG_ERR_INV_ID); err = switch_application (app, is_sigg); if (err) return err; return verify_pin (app, pwid, desc, pincb, pincb_arg); } /* Return the version of the NKS application. */ static int get_nks_version (int slot) { unsigned char *result = NULL; size_t resultlen; int type; if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0, 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 -----------------------------------------------------------+ */ 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. */ static gpg_error_t switch_application (app_t app, int enable_sigg) { gpg_error_t err; if (((app->app_local->sigg_active && enable_sigg) || (!app->app_local->sigg_active && !enable_sigg)) && !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_get_slot (app), aid_sigg, sizeof aid_sigg, 0); else err = iso7816_select_application (app_get_slot (app), aid_nks, sizeof aid_nks, 0); if (!err && enable_sigg && app->app_local->nks_version >= 3 && !app->app_local->sigg_msig_checked) { /* Check whether this card is a mass signature card. */ unsigned char *buffer; size_t buflen; const unsigned char *tmpl; size_t tmpllen; app->app_local->sigg_msig_checked = 1; app->app_local->sigg_is_msig = 1; err = iso7816_select_file (app_get_slot (app), 0x5349, 0); if (!err) err = iso7816_read_record (app_get_slot (app), 1, 1, 0, &buffer, &buflen); if (!err) { tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen); if (tmpl && tmpllen == 12 && !memcmp (tmpl, "\x93\x02\x00\x01\xA4\x06\x83\x01\x81\x83\x01\x83", 12)) app->app_local->sigg_is_msig = 0; xfree (buffer); } if (app->app_local->sigg_is_msig) log_info ("This is a mass signature card\n"); } if (!err) { app->app_local->need_app_select = 0; app->app_local->sigg_active = enable_sigg; } else log_error ("app-nks: error switching to %s: %s\n", enable_sigg? "SigG":"NKS", gpg_strerror (err)); return err; } /* Select the NKS application. */ gpg_error_t app_select_nks (app_t app) { int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0); if (!rc) { app->apptype = APPTYPE_NKS; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } app->app_local->nks_version = get_nks_version (slot); if (opt.verbose) log_info ("Detected NKS version: %d\n", app->app_local->nks_version); app->fnc.deinit = do_deinit; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = NULL; app->fnc.writekey = do_writekey; app->fnc.genkey = NULL; app->fnc.sign = do_sign; app->fnc.auth = NULL; app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; app->fnc.check_pin = do_check_pin; } leave: if (rc) do_deinit (app); return rc; } diff --git a/scd/app-p15.c b/scd/app-p15.c index ce82b34a9..348242f4f 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -1,3436 +1,3436 @@ /* app-p15.c - The pkcs#15 card application. * Copyright (C) 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* Information pertaining to the BELPIC developer card samples: Unblock PUK: "222222111111" Reset PIN: "333333111111") e.g. the APDUs 00:20:00:02:08:2C:33:33:33:11:11:11:FF and 00:24:01:01:08:24:12:34:FF:FF:FF:FF:FF should change the PIN into 1234. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "iso7816.h" #include "../common/tlv.h" #include "apdu.h" /* fixme: we should move the card detection to a separate file */ /* Types of cards we know and which needs special treatment. */ typedef enum { CARD_TYPE_UNKNOWN, CARD_TYPE_TCOS, CARD_TYPE_MICARDO, CARD_TYPE_BELPIC /* Belgian eID card specs. */ } card_type_t; /* A list card types with ATRs noticed with these cards. */ #define X(a) ((unsigned char const *)(a)) static struct { size_t atrlen; unsigned char const *atr; card_type_t type; } card_atr_list[] = { { 19, X("\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80" "\x90\x00\x8B"), CARD_TYPE_TCOS }, /* SLE44 */ { 19, X("\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80" "\x90\x00\x91"), CARD_TYPE_TCOS }, /* SLE66S */ { 19, X("\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80" "\x90\x00\x66"), CARD_TYPE_TCOS }, /* SLE66P */ { 27, X("\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00" "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23"), CARD_TYPE_MICARDO }, /* German BMI card */ { 19, X("\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80" "\x00\x90\x00"), CARD_TYPE_MICARDO }, /* German BMI card (ATR due to reader problem) */ { 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49" "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"), CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */ { 0 } }; #undef X /* The AID of PKCS15. */ static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; /* The Belgian eID variant - they didn't understood why a shared AID is useful for a standard. Oh well. */ static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; /* The PIN types as defined in pkcs#15 v1.1 */ typedef enum { PIN_TYPE_BCD = 0, PIN_TYPE_ASCII_NUMERIC = 1, PIN_TYPE_UTF8 = 2, PIN_TYPE_HALF_NIBBLE_BCD = 3, PIN_TYPE_ISO9564_1 = 4 } pin_type_t; /* A bit array with for the key usage flags from the commonKeyAttributes. */ struct keyusage_flags_s { unsigned int encrypt: 1; unsigned int decrypt: 1; unsigned int sign: 1; unsigned int sign_recover: 1; unsigned int wrap: 1; unsigned int unwrap: 1; unsigned int verify: 1; unsigned int verify_recover: 1; unsigned int derive: 1; unsigned int non_repudiation: 1; }; typedef struct keyusage_flags_s keyusage_flags_t; /* This is an object to store information about a Certificate Directory File (CDF) in a format suitable for further processing by us. To keep memory management, simple we use a linked list of items; i.e. one such object represents one certificate and the list the entire CDF. */ struct cdf_object_s { /* Link to next item when used in a linked list. */ struct cdf_object_s *next; /* Length and allocated buffer with the Id of this object. */ size_t objidlen; unsigned char *objid; /* To avoid reading a certificate more than once, we cache it in an allocated memory IMAGE of IMAGELEN. */ size_t imagelen; unsigned char *image; /* Set to true if a length and offset is available. */ int have_off; /* The offset and length of the object. They are only valid if HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ unsigned long off, len; /* The length of the path as given in the CDF and the path itself. path[0] is the top DF (usually 0x3f00). The path will never be empty. */ size_t pathlen; unsigned short path[1]; }; typedef struct cdf_object_s *cdf_object_t; /* This is an object to store information about a Private Key Directory File (PrKDF) in a format suitable for further processing by us. To keep memory management, simple we use a linked list of items; i.e. one such object represents one certificate and the list the entire PrKDF. */ struct prkdf_object_s { /* Link to next item when used in a linked list. */ struct prkdf_object_s *next; /* Length and allocated buffer with the Id of this object. */ size_t objidlen; unsigned char *objid; /* Length and allocated buffer with the authId of this object or NULL if no authID is known. */ size_t authidlen; unsigned char *authid; /* The key's usage flags. */ keyusage_flags_t usageflags; /* The keyReference and a flag telling whether it is valid. */ unsigned long key_reference; int key_reference_valid; /* Set to true if a length and offset is available. */ int have_off; /* The offset and length of the object. They are only valid if HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ unsigned long off, len; /* The length of the path as given in the PrKDF and the path itself. path[0] is the top DF (usually 0x3f00). */ size_t pathlen; unsigned short path[1]; }; typedef struct prkdf_object_s *prkdf_object_t; /* This is an object to store information about a Authentication Object Directory File (AODF) in a format suitable for further processing by us. To keep memory management, simple we use a linked list of items; i.e. one such object represents one authentication object and the list the entire AOKDF. */ struct aodf_object_s { /* Link to next item when used in a linked list. */ struct aodf_object_s *next; /* Length and allocated buffer with the Id of this object. */ size_t objidlen; unsigned char *objid; /* Length and allocated buffer with the authId of this object or NULL if no authID is known. */ size_t authidlen; unsigned char *authid; /* The PIN Flags. */ struct { unsigned int case_sensitive: 1; unsigned int local: 1; unsigned int change_disabled: 1; unsigned int unblock_disabled: 1; unsigned int initialized: 1; unsigned int needs_padding: 1; unsigned int unblocking_pin: 1; unsigned int so_pin: 1; unsigned int disable_allowed: 1; unsigned int integrity_protected: 1; unsigned int confidentiality_protected: 1; unsigned int exchange_ref_data: 1; } pinflags; /* The PIN Type. */ pin_type_t pintype; /* The minimum length of a PIN. */ unsigned long min_length; /* The stored length of a PIN. */ unsigned long stored_length; /* The maximum length of a PIN and a flag telling whether it is valid. */ unsigned long max_length; int max_length_valid; /* The pinReference and a flag telling whether it is valid. */ unsigned long pin_reference; int pin_reference_valid; /* The padChar and a flag telling whether it is valid. */ char pad_char; int pad_char_valid; /* Set to true if a length and offset is available. */ int have_off; /* The offset and length of the object. They are only valid if HAVE_OFF is true and set to 0 if HAVE_OFF is false. */ unsigned long off, len; /* The length of the path as given in the Aodf and the path itself. path[0] is the top DF (usually 0x3f00). PATH is optional and thus may be NULL. Malloced.*/ size_t pathlen; unsigned short *path; }; typedef struct aodf_object_s *aodf_object_t; /* Context local to this application. */ struct app_local_s { /* The home DF. Note, that we don't yet support a multilevel hierarchy. Thus we assume this is directly below the MF. */ unsigned short home_df; /* The type of the card. */ card_type_t card_type; /* Flag indicating whether we may use direct path selection. */ int direct_path_selection; /* Structure with the EFIDs of the objects described in the ODF file. */ struct { unsigned short private_keys; unsigned short public_keys; unsigned short trusted_public_keys; unsigned short secret_keys; unsigned short certificates; unsigned short trusted_certificates; unsigned short useful_certificates; unsigned short data_objects; unsigned short auth_objects; } odf; /* The PKCS#15 serialnumber from EF(TokeiNFo) or NULL. Malloced. */ unsigned char *serialno; size_t serialnolen; /* Information on all certificates. */ cdf_object_t certificate_info; /* Information on all trusted certificates. */ cdf_object_t trusted_certificate_info; /* Information on all useful certificates. */ cdf_object_t useful_certificate_info; /* Information on all private keys. */ prkdf_object_t private_key_info; /* Information on all authentication objects. */ aodf_object_t auth_object_info; }; /*** Local prototypes. ***/ static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, unsigned char **r_cert, size_t *r_certlen); /* Release the CDF object A */ static void release_cdflist (cdf_object_t a) { while (a) { cdf_object_t tmp = a->next; xfree (a->image); xfree (a->objid); xfree (a); a = tmp; } } /* Release the PrKDF object A. */ static void release_prkdflist (prkdf_object_t a) { while (a) { prkdf_object_t tmp = a->next; xfree (a->objid); xfree (a->authid); xfree (a); a = tmp; } } /* Release just one aodf object. */ void release_aodf_object (aodf_object_t a) { if (a) { xfree (a->objid); xfree (a->authid); xfree (a->path); xfree (a); } } /* Release the AODF list A. */ static void release_aodflist (aodf_object_t a) { while (a) { aodf_object_t tmp = a->next; release_aodf_object (a); a = tmp; } } /* Release all local resources. */ static void do_deinit (app_t app) { if (app && app->app_local) { release_cdflist (app->app_local->certificate_info); release_cdflist (app->app_local->trusted_certificate_info); release_cdflist (app->app_local->useful_certificate_info); release_prkdflist (app->app_local->private_key_info); release_aodflist (app->app_local->auth_object_info); xfree (app->app_local->serialno); xfree (app->app_local); app->app_local = NULL; } } /* Do a select and a read for the file with EFID. EFID_DESC is a desctription of the EF to be used with error messages. On success BUFFER and BUFLEN contain the entire content of the EF. The caller must free BUFFER only on success. */ static gpg_error_t select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, unsigned char **buffer, size_t *buflen) { gpg_error_t err; err = iso7816_select_file (slot, efid, 0); if (err) { log_error ("error selecting %s (0x%04X): %s\n", efid_desc, efid, gpg_strerror (err)); return err; } err = iso7816_read_binary (slot, 0, 0, buffer, buflen); if (err) { log_error ("error reading %s (0x%04X): %s\n", efid_desc, efid, gpg_strerror (err)); return err; } return 0; } /* This function calls select file to read a file using a complete path which may or may not start at the master file (MF). */ static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) { gpg_error_t err; int i, j; if (!pathlen) return gpg_error (GPG_ERR_INV_VALUE); if (pathlen && *path != 0x3f00 ) log_debug ("WARNING: relative path selection not yet implemented\n"); if (app->app_local->direct_path_selection) { err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1); if (err) { log_error ("error selecting path "); for (j=0; j < pathlen; j++) log_printf ("%04hX", path[j]); log_printf (": %s\n", gpg_strerror (err)); return err; } } else { /* FIXME: Need code to remember the last PATH so that we can decide what select commands to send in case the path does not start off with 3F00. We might also want to use direct path selection if supported by the card. */ for (i=0; i < pathlen; i++) { err = iso7816_select_file (app_get_slot (app), path[i], !(i+1 == pathlen)); if (err) { log_error ("error selecting part %d from path ", i); for (j=0; j < pathlen; j++) log_printf ("%04hX", path[j]); log_printf (": %s\n", gpg_strerror (err)); return err; } } } return 0; } /* Parse a cert Id string (or a key Id string) and return the binary object Id string in a newly allocated buffer stored at R_OBJID and R_OBJIDLEN. On Error NULL will be stored there and an error code returned. On success caller needs to free the buffer at R_OBJID. */ static gpg_error_t parse_certid (app_t app, const char *certid, unsigned char **r_objid, size_t *r_objidlen) { char tmpbuf[10]; const char *s; size_t objidlen; unsigned char *objid; int i; *r_objid = NULL; *r_objidlen = 0; if (app->app_local->home_df) snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.", (unsigned int)(app->app_local->home_df & 0xffff)); else strcpy (tmpbuf, "P15."); if (strncmp (certid, tmpbuf, strlen (tmpbuf)) ) { if (!strncmp (certid, "P15.", 4) || (!strncmp (certid, "P15-", 4) && hexdigitp (certid+4) && hexdigitp (certid+5) && hexdigitp (certid+6) && hexdigitp (certid+7) && certid[8] == '.')) return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_INV_ID); } certid += strlen (tmpbuf); for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++) ; if (*s || !objidlen || (objidlen%2)) return gpg_error (GPG_ERR_INV_ID); objidlen /= 2; objid = xtrymalloc (objidlen); if (!objid) return gpg_error_from_syserror (); for (s=certid, i=0; i < objidlen; i++, s+=2) objid[i] = xtoi_2 (s); *r_objid = objid; *r_objidlen = objidlen; return 0; } /* Find a certificate object by the certificate ID CERTID and store a pointer to it at R_CDF. */ static gpg_error_t cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf) { gpg_error_t err; size_t objidlen; unsigned char *objid; cdf_object_t cdf; err = parse_certid (app, certid, &objid, &objidlen); if (err) return err; for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) break; if (!cdf) for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) break; if (!cdf) for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) break; xfree (objid); if (!cdf) return gpg_error (GPG_ERR_NOT_FOUND); *r_cdf = cdf; return 0; } /* Find a private key object by the key Id string KEYIDSTR and store a pointer to it at R_PRKDF. */ static gpg_error_t prkdf_object_from_keyidstr (app_t app, const char *keyidstr, prkdf_object_t *r_prkdf) { gpg_error_t err; size_t objidlen; unsigned char *objid; prkdf_object_t prkdf; err = parse_certid (app, keyidstr, &objid, &objidlen); if (err) return err; for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen)) break; xfree (objid); if (!prkdf) return gpg_error (GPG_ERR_NOT_FOUND); *r_prkdf = prkdf; return 0; } /* Read and parse the Object Directory File and store away the pointers. ODF_FID shall contain the FID of the ODF. Example of such a file: A0 06 30 04 04 02 60 34 = Private Keys A4 06 30 04 04 02 60 35 = Certificates A5 06 30 04 04 02 60 36 = TrustedCertificates A7 06 30 04 04 02 60 37 = DataObjects A8 06 30 04 04 02 60 38 = AuthObjects These are all PathOrObjects using the path CHOICE element. The paths are octet strings of length 2. Using this Path CHOICE element is recommended, so we only implement that for now. */ static gpg_error_t read_ef_odf (app_t app, unsigned short odf_fid) { gpg_error_t err; unsigned char *buffer, *p; size_t buflen; unsigned short value; size_t offset; err = select_and_read_binary (app_get_slot (app), odf_fid, "ODF", &buffer, &buflen); if (err) return err; if (buflen < 8) { log_error ("error: ODF too short\n"); xfree (buffer); return gpg_error (GPG_ERR_INV_OBJ); } p = buffer; while (buflen && *p && *p != 0xff) { if ( buflen >= 8 && (p[0] & 0xf0) == 0xA0 && !memcmp (p+1, "\x06\x30\x04\x04\x02", 5) ) { offset = 6; } else if ( buflen >= 12 && (p[0] & 0xf0) == 0xA0 && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7) && app->app_local->home_df == ((p[8]<<8)|p[9]) ) { /* We only allow a full path if all files are at the same level and below the home directory. The extend this we would need to make use of new data type capable of keeping a full path. */ offset = 10; } else { log_error ("ODF format is not supported by us\n"); xfree (buffer); return gpg_error (GPG_ERR_INV_OBJ); } switch ((p[0] & 0x0f)) { case 0: value = app->app_local->odf.private_keys; break; case 1: value = app->app_local->odf.public_keys; break; case 2: value = app->app_local->odf.trusted_public_keys; break; case 3: value = app->app_local->odf.secret_keys; break; case 4: value = app->app_local->odf.certificates; break; case 5: value = app->app_local->odf.trusted_certificates; break; case 6: value = app->app_local->odf.useful_certificates; break; case 7: value = app->app_local->odf.data_objects; break; case 8: value = app->app_local->odf.auth_objects; break; default: value = 0; break; } if (value) { log_error ("duplicate object type %d in ODF ignored\n",(p[0]&0x0f)); continue; } value = ((p[offset] << 8) | p[offset+1]); switch ((p[0] & 0x0f)) { case 0: app->app_local->odf.private_keys = value; break; case 1: app->app_local->odf.public_keys = value; break; case 2: app->app_local->odf.trusted_public_keys = value; break; case 3: app->app_local->odf.secret_keys = value; break; case 4: app->app_local->odf.certificates = value; break; case 5: app->app_local->odf.trusted_certificates = value; break; case 6: app->app_local->odf.useful_certificates = value; break; case 7: app->app_local->odf.data_objects = value; break; case 8: app->app_local->odf.auth_objects = value; break; default: log_error ("unknown object type %d in ODF ignored\n", (p[0]&0x0f)); } offset += 2; if (buflen < offset) break; p += offset; buflen -= offset; } if (buflen) log_info ("warning: %u bytes of garbage detected at end of ODF\n", (unsigned int)buflen); xfree (buffer); return 0; } /* Parse the BIT STRING with the keyUsageFlags from the CommonKeyAttributes. */ static gpg_error_t parse_keyusage_flags (const unsigned char *der, size_t derlen, keyusage_flags_t *usageflags) { unsigned int bits, mask; int i, unused, full; memset (usageflags, 0, sizeof *usageflags); if (!derlen) return gpg_error (GPG_ERR_INV_OBJ); unused = *der++; derlen--; if ((!derlen && unused) || unused/8 > derlen) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = derlen - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* First octet */ if (derlen) { bits = *der++; derlen--; if (full) full--; else { bits &= ~mask; mask = 0; } } else bits = 0; if ((bits & 0x80)) usageflags->encrypt = 1; if ((bits & 0x40)) usageflags->decrypt = 1; if ((bits & 0x20)) usageflags->sign = 1; if ((bits & 0x10)) usageflags->sign_recover = 1; if ((bits & 0x08)) usageflags->wrap = 1; if ((bits & 0x04)) usageflags->unwrap = 1; if ((bits & 0x02)) usageflags->verify = 1; if ((bits & 0x01)) usageflags->verify_recover = 1; /* Second octet. */ if (derlen) { bits = *der++; derlen--; if (full) full--; else { bits &= ~mask; } } else bits = 0; if ((bits & 0x80)) usageflags->derive = 1; if ((bits & 0x40)) usageflags->non_repudiation = 1; return 0; } /* Read and parse the Private Key Directory Files. */ /* 6034 (privatekeys) 30 33 30 11 0C 08 53 4B 2E 43 48 2E 44 53 03 02 030...SK.CH.DS.. 06 80 04 01 07 30 0C 04 01 01 03 03 06 00 40 02 .....0........@. 02 00 50 A1 10 30 0E 30 08 04 06 3F 00 40 16 00 ..P..0.0...?.@.. 50 02 02 04 00 30 33 30 11 0C 08 53 4B 2E 43 48 P....030...SK.CH 2E 4B 45 03 02 06 80 04 01 0A 30 0C 04 01 0C 03 .KE.......0..... 03 06 44 00 02 02 00 52 A1 10 30 0E 30 08 04 06 ..D....R..0.0... 3F 00 40 16 00 52 02 02 04 00 30 34 30 12 0C 09 ?.@..R....040... 53 4B 2E 43 48 2E 41 55 54 03 02 06 80 04 01 0A SK.CH.AUT....... 30 0C 04 01 0D 03 03 06 20 00 02 02 00 51 A1 10 0....... ....Q.. 30 0E 30 08 04 06 3F 00 40 16 00 51 02 02 04 00 0.0...?.@..Q.... 30 37 30 15 0C 0C 53 4B 2E 43 48 2E 44 53 2D 53 070...SK.CH.DS-S 50 58 03 02 06 80 04 01 0A 30 0C 04 01 02 03 03 PX.......0...... 06 20 00 02 02 00 53 A1 10 30 0E 30 08 04 06 3F . ....S..0.0...? 00 40 16 00 53 02 02 04 00 00 00 00 00 00 00 00 .@..S........... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0 30 51: SEQUENCE { 2 30 17: SEQUENCE { -- commonObjectAttributes 4 0C 8: UTF8String 'SK.CH.DS' 14 03 2: BIT STRING 6 unused bits : '01'B (bit 0) 18 04 1: OCTET STRING --authid : 07 : } 21 30 12: SEQUENCE { -- commonKeyAttributes 23 04 1: OCTET STRING : 01 26 03 3: BIT STRING 6 unused bits : '1000000000'B (bit 9) 31 02 2: INTEGER 80 -- keyReference (optional) : } 35 A1 16: [1] { -- keyAttributes 37 30 14: SEQUENCE { -- privateRSAKeyAttributes 39 30 8: SEQUENCE { -- objectValue 41 04 6: OCTET STRING --path : 3F 00 40 16 00 50 : } 49 02 2: INTEGER 1024 -- modulus : } : } : } */ static gpg_error_t read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; prkdf_object_t prkdflist = NULL; int i; if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ err = select_and_read_binary (app_get_slot (app), fid, "PrKDF", &buffer, &buflen); if (err) return err; p = buffer; n = buflen; /* FIXME: This shares a LOT of code with read_ef_cdf! */ /* Loop over the records. We stop as soon as we detect a new record starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. */ while (n && *p && *p != 0xff) { const unsigned char *pp; size_t nn; int where; const char *errstr = NULL; prkdf_object_t prkdf = NULL; unsigned long ul; const unsigned char *objid; size_t objidlen; const unsigned char *authid = NULL; size_t authidlen = 0; keyusage_flags_t usageflags; unsigned long key_reference = 0; int key_reference_valid = 0; const char *s; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing PrKDF record: %s\n", gpg_strerror (err)); goto leave; } pp = p; nn = objlen; p += objlen; n -= objlen; /* Parse the commonObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Search the optional AuthId. We need to skip the optional Label (UTF8STRING) and the optional CommonObjectFlags (BITSTRING). */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; if (tag == TAG_UTF8_STRING) { ppp += objlen; /* Skip the Label. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_BIT_STRING) { ppp += objlen; /* Skip the CommonObjectFlags. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_OCTET_STRING && objlen) { authid = ppp; authidlen = objlen; } no_authid: ; } /* Parse the commonKeyAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Get the Id. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; objid = ppp; objidlen = objlen; ppp += objlen; nnn -= objlen; /* Get the KeyUsageFlags. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; err = parse_keyusage_flags (ppp, objlen, &usageflags); if (err) goto parse_error; ppp += objlen; nnn -= objlen; /* Find the keyReference */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN) { /* Skip the native element. */ ppp += objlen; nnn -= objlen; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING) { /* Skip the accessFlags. */ ppp += objlen; nnn -= objlen; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER) { /* Yep, this is the keyReference. */ for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*ppp++) & 0xff; nnn--; } key_reference = ul; key_reference_valid = 1; } leave_cki: ; } /* Skip subClassAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_CONTEXT && tag == 0) { pp += objlen; nn -= objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); } /* Parse the keyAttributes. */ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE) ; /* RSA */ else if (class == CLASS_CONTEXT) { switch (tag) { case 0: errstr = "EC key objects are not supported"; break; case 1: errstr = "DH key objects are not supported"; break; case 2: errstr = "DSA key objects are not supported"; break; case 3: errstr = "KEA key objects are not supported"; break; default: errstr = "unknown privateKeyObject"; break; } goto parse_error; } else { err = gpg_error (GPG_ERR_INV_OBJ); goto parse_error; } nn = objlen; /* Check that the reference is a Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) { errstr = "unsupported reference type"; goto parse_error; } nn = objlen; /* Parse the Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); 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). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING || !objlen || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; } /* Create a new PrKDF list item. */ prkdf = xtrycalloc (1, (sizeof *prkdf - sizeof(unsigned short) + objlen/2 * sizeof(unsigned short))); if (!prkdf) { err = gpg_error_from_syserror (); goto leave; } prkdf->objidlen = objidlen; prkdf->objid = xtrymalloc (objidlen); if (!prkdf->objid) { err = gpg_error_from_syserror (); xfree (prkdf); goto leave; } memcpy (prkdf->objid, objid, objidlen); if (authid) { prkdf->authidlen = authidlen; prkdf->authid = xtrymalloc (authidlen); if (!prkdf->authid) { err = gpg_error_from_syserror (); xfree (prkdf->objid); xfree (prkdf); goto leave; } memcpy (prkdf->authid, authid, authidlen); } prkdf->pathlen = objlen/2; for (i=0; i < prkdf->pathlen; i++, pp += 2, nn -= 2) prkdf->path[i] = ((pp[0] << 8) | pp[1]); prkdf->usageflags = usageflags; prkdf->key_reference = key_reference; prkdf->key_reference_valid = key_reference_valid; if (nn) { /* An index and length follows. */ prkdf->have_off = 1; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } prkdf->off = ul; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 0)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } prkdf->len = ul; } log_debug ("PrKDF %04hX: id=", fid); for (i=0; i < prkdf->objidlen; i++) log_printf ("%02X", prkdf->objid[i]); log_printf (" path="); for (i=0; i < prkdf->pathlen; i++) log_printf ("%04hX", prkdf->path[i]); if (prkdf->have_off) log_printf ("[%lu/%lu]", prkdf->off, prkdf->len); if (prkdf->authid) { log_printf (" authid="); for (i=0; i < prkdf->authidlen; i++) log_printf ("%02X", prkdf->authid[i]); } if (prkdf->key_reference_valid) log_printf (" keyref=0x%02lX", prkdf->key_reference); log_printf (" usage="); s = ""; if (prkdf->usageflags.encrypt) log_printf ("%sencrypt", s), s = ","; if (prkdf->usageflags.decrypt) log_printf ("%sdecrypt", s), s = ","; if (prkdf->usageflags.sign ) log_printf ("%ssign", s), s = ","; if (prkdf->usageflags.sign_recover) log_printf ("%ssign_recover", s), s = ","; if (prkdf->usageflags.wrap ) log_printf ("%swrap", s), s = ","; if (prkdf->usageflags.unwrap ) log_printf ("%sunwrap", s), s = ","; if (prkdf->usageflags.verify ) log_printf ("%sverify", s), s = ","; if (prkdf->usageflags.verify_recover) log_printf ("%sverify_recover", s), s = ","; if (prkdf->usageflags.derive ) log_printf ("%sderive", s), s = ","; if (prkdf->usageflags.non_repudiation) log_printf ("%snon_repudiation", s), s = ","; log_printf ("\n"); /* Put it into the list. */ prkdf->next = prkdflist; prkdflist = prkdf; prkdf = NULL; continue; /* Ready. */ parse_error: log_error ("error parsing PrKDF record (%d): %s - skipped\n", where, errstr? errstr : gpg_strerror (err)); if (prkdf) { xfree (prkdf->objid); xfree (prkdf->authid); xfree (prkdf); } err = 0; } /* End looping over all records. */ leave: xfree (buffer); if (err) release_prkdflist (prkdflist); else *result = prkdflist; return err; } /* Read and parse the Certificate Directory Files identified by FID. On success a newlist of CDF object gets stored at RESULT and the caller is then responsible of releasing this list. On error a error code is returned and RESULT won't get changed. */ static gpg_error_t read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; cdf_object_t cdflist = NULL; int i; if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ err = select_and_read_binary (app_get_slot (app), fid, "CDF", &buffer, &buflen); if (err) return err; p = buffer; n = buflen; /* Loop over the records. We stop as soon as we detect a new record starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. */ while (n && *p && *p != 0xff) { const unsigned char *pp; size_t nn; int where; const char *errstr = NULL; cdf_object_t cdf = NULL; unsigned long ul; const unsigned char *objid; size_t objidlen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing CDF record: %s\n", gpg_strerror (err)); goto leave; } pp = p; nn = objlen; p += objlen; n -= objlen; /* Skip the commonObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; pp += objlen; nn -= objlen; /* Parse the commonCertificateAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Get the Id. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; objid = ppp; objidlen = objlen; } /* Parse the certAttribute. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; /* Check that the reference is a Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) { errstr = "unsupported reference type"; goto parse_error; } nn = objlen; /* Parse the Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); 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). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING || !objlen || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; } /* Create a new CDF list item. */ cdf = xtrycalloc (1, (sizeof *cdf - sizeof(unsigned short) + objlen/2 * sizeof(unsigned short))); if (!cdf) { err = gpg_error_from_syserror (); goto leave; } cdf->objidlen = objidlen; cdf->objid = xtrymalloc (objidlen); if (!cdf->objid) { err = gpg_error_from_syserror (); xfree (cdf); goto leave; } memcpy (cdf->objid, objid, objidlen); cdf->pathlen = objlen/2; for (i=0; i < cdf->pathlen; i++, pp += 2, nn -= 2) cdf->path[i] = ((pp[0] << 8) | pp[1]); if (nn) { /* An index and length follows. */ cdf->have_off = 1; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } cdf->off = ul; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 0)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } cdf->len = ul; } log_debug ("CDF %04hX: id=", fid); for (i=0; i < cdf->objidlen; i++) log_printf ("%02X", cdf->objid[i]); log_printf (" path="); for (i=0; i < cdf->pathlen; i++) log_printf ("%04hX", cdf->path[i]); if (cdf->have_off) log_printf ("[%lu/%lu]", cdf->off, cdf->len); log_printf ("\n"); /* Put it into the list. */ cdf->next = cdflist; cdflist = cdf; cdf = NULL; continue; /* Ready. */ parse_error: log_error ("error parsing CDF record (%d): %s - skipped\n", where, errstr? errstr : gpg_strerror (err)); xfree (cdf); err = 0; } /* End looping over all records. */ leave: xfree (buffer); if (err) release_cdflist (cdflist); else *result = cdflist; return err; } /* SEQUENCE { SEQUENCE { -- CommonObjectAttributes UTF8String 'specific PIN for DS' BIT STRING 0 unused bits '00000011'B } SEQUENCE { -- CommonAuthenticationObjectAttributes OCTET STRING 07 -- iD } [1] { -- typeAttributes SEQUENCE { -- PinAttributes BIT STRING 0 unused bits '0000100000110010'B -- local,initialized,needs-padding -- exchangeRefData ENUMERATED 1 -- ascii-numeric INTEGER 6 -- minLength INTEGER 6 -- storedLength INTEGER 8 -- maxLength [0] 02 -- pinReference GeneralizedTime 19/04/2002 12:12 GMT -- lastPinChange SEQUENCE { OCTET STRING 3F 00 40 16 -- path to DF of PIN } } } } */ /* Read and parse an Authentication Object Directory File identified by FID. On success a newlist of AODF objects gets stored at RESULT and the caller is responsible of releasing this list. On error a error code is returned and RESULT won't get changed. */ static gpg_error_t read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; aodf_object_t aodflist = NULL; int i; if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */ err = select_and_read_binary (app_get_slot (app), fid, "AODF", &buffer, &buflen); if (err) return err; p = buffer; n = buflen; /* FIXME: This shares a LOT of code with read_ef_prkdf! */ /* Loop over the records. We stop as soon as we detect a new record starting with 0x00 or 0xff as these values are commonly used to pad data blocks and are no valid ASN.1 encoding. */ while (n && *p && *p != 0xff) { const unsigned char *pp; size_t nn; int where; const char *errstr = NULL; aodf_object_t aodf = NULL; unsigned long ul; const char *s; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing AODF record: %s\n", gpg_strerror (err)); goto leave; } pp = p; nn = objlen; p += objlen; n -= objlen; /* Allocate memory for a new AODF list item. */ aodf = xtrycalloc (1, sizeof *aodf); if (!aodf) goto no_core; /* Parse the commonObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Search the optional AuthId. We need to skip the optional Label (UTF8STRING) and the optional CommonObjectFlags (BITSTRING). */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; if (tag == TAG_UTF8_STRING) { ppp += objlen; /* Skip the Label. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_BIT_STRING) { ppp += objlen; /* Skip the CommonObjectFlags. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_OCTET_STRING && objlen) { aodf->authidlen = objlen; aodf->authid = xtrymalloc (objlen); if (!aodf->authid) goto no_core; memcpy (aodf->authid, ppp, objlen); } no_authid: ; } /* Parse the CommonAuthenticationObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Get the Id. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; aodf->objidlen = objlen; aodf->objid = xtrymalloc (objlen); if (!aodf->objid) goto no_core; memcpy (aodf->objid, ppp, objlen); } /* Parse the typeAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE) ; /* PinAttributes */ else if (class == CLASS_CONTEXT) { switch (tag) { case 0: errstr = "biometric auth types are not supported"; break; case 1: errstr = "authKey auth types are not supported"; break; case 2: errstr = "external auth type are not supported"; break; default: errstr = "unknown privateKeyObject"; break; } goto parse_error; } else { err = gpg_error (GPG_ERR_INV_OBJ); goto parse_error; } nn = objlen; /* PinFlags */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || !objlen || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { unsigned int bits, mask; int unused, full; unused = *pp++; nn--; objlen--; if ((!objlen && unused) || unused/8 > objlen) { err = gpg_error (GPG_ERR_ENCODING_PROBLEM); goto parse_error; } full = objlen - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* The first octet */ bits = 0; if (objlen) { bits = *pp++; nn--; objlen--; if (full) full--; else { bits &= ~mask; mask = 0; } } if ((bits & 0x80)) /* ASN.1 bit 0. */ aodf->pinflags.case_sensitive = 1; if ((bits & 0x40)) /* ASN.1 bit 1. */ aodf->pinflags.local = 1; if ((bits & 0x20)) aodf->pinflags.change_disabled = 1; if ((bits & 0x10)) aodf->pinflags.unblock_disabled = 1; if ((bits & 0x08)) aodf->pinflags.initialized = 1; if ((bits & 0x04)) aodf->pinflags.needs_padding = 1; if ((bits & 0x02)) aodf->pinflags.unblocking_pin = 1; if ((bits & 0x01)) aodf->pinflags.so_pin = 1; /* The second octet. */ bits = 0; if (objlen) { bits = *pp++; nn--; objlen--; if (full) full--; else { bits &= ~mask; } } if ((bits & 0x80)) aodf->pinflags.disable_allowed = 1; if ((bits & 0x40)) aodf->pinflags.integrity_protected = 1; if ((bits & 0x20)) aodf->pinflags.confidentiality_protected = 1; if ((bits & 0x10)) aodf->pinflags.exchange_ref_data = 1; /* Skip remaining bits. */ pp += objlen; nn -= objlen; } /* PinType */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_ENUMERATED)) err = gpg_error (GPG_ERR_INV_OBJ); if (!err && objlen > sizeof (ul)) err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } aodf->pintype = ul; /* minLength */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (!err && objlen > sizeof (ul)) err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } aodf->min_length = ul; /* storedLength */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (!err && objlen > sizeof (ul)) err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } aodf->stored_length = ul; /* optional maxLength */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto ready; if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER) { if (objlen > sizeof (ul)) { err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); goto parse_error; } for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } aodf->max_length = ul; aodf->max_length_valid = 1; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto ready; if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } /* Optional pinReference. */ if (class == CLASS_CONTEXT && tag == 0) { if (objlen > sizeof (ul)) { err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); goto parse_error; } for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*pp++) & 0xff; nn--; } aodf->pin_reference = ul; aodf->pin_reference_valid = 1; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto ready; if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } /* Optional padChar. */ if (class == CLASS_UNIVERSAL && tag == TAG_OCTET_STRING) { if (objlen != 1) { errstr = "padChar is not of size(1)"; goto parse_error; } aodf->pad_char = *pp++; nn--; aodf->pad_char_valid = 1; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto ready; if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } /* Skip optional lastPinChange. */ if (class == CLASS_UNIVERSAL && tag == TAG_GENERALIZED_TIME) { pp += objlen; nn -= objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto ready; if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } /* Optional Path object. */ if (class == CLASS_UNIVERSAL || tag == TAG_SEQUENCE) { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; /* Make sure that the next element is a non zero FID and of even length (FID are two bytes each). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING || !objlen || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; } aodf->pathlen = objlen/2; aodf->path = xtrymalloc (aodf->pathlen); if (!aodf->path) goto no_core; for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2) aodf->path[i] = ((ppp[0] << 8) | ppp[1]); if (nnn) { /* An index and length follows. */ aodf->have_off = 1; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*ppp++) & 0xff; nnn--; } aodf->off = ul; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_CONTEXT || tag != 0)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*ppp++) & 0xff; nnn--; } aodf->len = ul; } } /* Igonore further objects which might be there due to future extensions of pkcs#15. */ ready: log_debug ("AODF %04hX: id=", fid); for (i=0; i < aodf->objidlen; i++) log_printf ("%02X", aodf->objid[i]); if (aodf->authid) { log_printf (" authid="); for (i=0; i < aodf->authidlen; i++) log_printf ("%02X", aodf->authid[i]); } log_printf (" flags="); s = ""; if (aodf->pinflags.case_sensitive) log_printf ("%scase_sensitive", s), s = ","; if (aodf->pinflags.local) log_printf ("%slocal", s), s = ","; if (aodf->pinflags.change_disabled) log_printf ("%schange_disabled", s), s = ","; if (aodf->pinflags.unblock_disabled) log_printf ("%sunblock_disabled", s), s = ","; if (aodf->pinflags.initialized) log_printf ("%sinitialized", s), s = ","; if (aodf->pinflags.needs_padding) log_printf ("%sneeds_padding", s), s = ","; if (aodf->pinflags.unblocking_pin) log_printf ("%sunblocking_pin", s), s = ","; if (aodf->pinflags.so_pin) log_printf ("%sso_pin", s), s = ","; if (aodf->pinflags.disable_allowed) log_printf ("%sdisable_allowed", s), s = ","; if (aodf->pinflags.integrity_protected) log_printf ("%sintegrity_protected", s), s = ","; if (aodf->pinflags.confidentiality_protected) log_printf ("%sconfidentiality_protected", s), s = ","; if (aodf->pinflags.exchange_ref_data) log_printf ("%sexchange_ref_data", s), s = ","; { char numbuf[50]; switch (aodf->pintype) { case PIN_TYPE_BCD: s = "bcd"; break; case PIN_TYPE_ASCII_NUMERIC: s = "ascii-numeric"; break; case PIN_TYPE_UTF8: s = "utf8"; break; case PIN_TYPE_HALF_NIBBLE_BCD: s = "half-nibble-bcd"; break; case PIN_TYPE_ISO9564_1: s = "iso9564-1"; break; default: sprintf (numbuf, "%lu", (unsigned long)aodf->pintype); s = numbuf; } log_printf (" type=%s", s); } log_printf (" min=%lu", aodf->min_length); log_printf (" stored=%lu", aodf->stored_length); if (aodf->max_length_valid) log_printf (" max=%lu", aodf->max_length); if (aodf->pad_char_valid) log_printf (" pad=0x%02x", aodf->pad_char); if (aodf->pin_reference_valid) log_printf (" pinref=0x%02lX", aodf->pin_reference); if (aodf->pathlen) { log_printf (" path="); for (i=0; i < aodf->pathlen; i++) log_printf ("%04hX", aodf->path[i]); if (aodf->have_off) log_printf ("[%lu/%lu]", aodf->off, aodf->len); } log_printf ("\n"); /* Put it into the list. */ aodf->next = aodflist; aodflist = aodf; aodf = NULL; continue; /* Ready. */ no_core: err = gpg_error_from_syserror (); release_aodf_object (aodf); goto leave; parse_error: log_error ("error parsing AODF record (%d): %s - skipped\n", where, errstr? errstr : gpg_strerror (err)); err = 0; release_aodf_object (aodf); } /* End looping over all records. */ leave: xfree (buffer); if (err) release_aodflist (aodflist); else *result = aodflist; return err; } /* Read and parse the EF(TokenInfo). TokenInfo ::= SEQUENCE { version INTEGER {v1(0)} (v1,...), serialNumber OCTET STRING, manufacturerID Label OPTIONAL, label [0] Label OPTIONAL, tokenflags TokenFlags, seInfo SEQUENCE OF SecurityEnvironmentInfo OPTIONAL, recordInfo [1] RecordInfo OPTIONAL, supportedAlgorithms [2] SEQUENCE OF AlgorithmInfo OPTIONAL, ..., issuerId [3] Label OPTIONAL, holderId [4] Label OPTIONAL, lastUpdate [5] LastUpdate OPTIONAL, preferredLanguage PrintableString OPTIONAL -- In accordance with -- IETF RFC 1766 } (CONSTRAINED BY { -- Each AlgorithmInfo.reference value must be unique --}) TokenFlags ::= BIT STRING { readOnly (0), loginRequired (1), prnGeneration (2), eidCompliant (3) } 5032: 30 31 02 01 00 04 04 05 45 36 9F 0C 0C 44 2D 54 01......E6...D-T 72 75 73 74 20 47 6D 62 48 80 14 4F 66 66 69 63 rust GmbH..Offic 65 20 69 64 65 6E 74 69 74 79 20 63 61 72 64 03 e identity card. 02 00 40 20 63 61 72 64 03 02 00 40 00 00 00 00 ..@ card...@.... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0 49: SEQUENCE { 2 1: INTEGER 0 5 4: OCTET STRING 05 45 36 9F 11 12: UTF8String 'D-Trust GmbH' 25 20: [0] 'Office identity card' 47 2: BIT STRING : '00000010'B (bit 1) : Error: Spurious zero bits in bitstring. : } */ static gpg_error_t read_ef_tokeninfo (app_t app) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; unsigned long ul; err = select_and_read_binary (app_get_slot (app), 0x5032, "TokenInfo", &buffer, &buflen); if (err) return err; p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing TokenInfo: %s\n", gpg_strerror (err)); goto leave; } n = objlen; /* Version. */ err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_INTEGER)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto leave; for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*p++) & 0xff; n--; } if (ul) { log_error ("invalid version %lu in TokenInfo\n", ul); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } /* serialNumber. */ err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto leave; xfree (app->app_local->serialno); app->app_local->serialno = xtrymalloc (objlen); if (!app->app_local->serialno) { err = gpg_error_from_syserror (); goto leave; } memcpy (app->app_local->serialno, p, objlen); app->app_local->serialnolen = objlen; log_printhex (p, objlen, "Serialnumber from EF(TokenInfo) is:"); leave: xfree (buffer); return err; } /* Get all the basic information from the pkcs#15 card, check the structure and initialize our local context. This is used once at application initialization. */ static gpg_error_t read_p15_info (app_t app) { gpg_error_t err; if (!read_ef_tokeninfo (app)) { /* If we don't have a serial number yet but the TokenInfo provides one, use that. */ if (!app->card->serialno && app->app_local->serialno) { app->card->serialno = app->app_local->serialno; app->card->serialnolen = app->app_local->serialnolen; app->app_local->serialno = NULL; app->app_local->serialnolen = 0; err = app_munge_serialno (app->card); if (err) return err; } } /* Read the ODF so that we know the location of all directory files. */ /* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */ err = read_ef_odf (app, 0x5031); if (err) return err; /* Read certificate information. */ assert (!app->app_local->certificate_info); assert (!app->app_local->trusted_certificate_info); assert (!app->app_local->useful_certificate_info); err = read_ef_cdf (app, app->app_local->odf.certificates, &app->app_local->certificate_info); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) err = read_ef_cdf (app, app->app_local->odf.trusted_certificates, &app->app_local->trusted_certificate_info); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) err = read_ef_cdf (app, app->app_local->odf.useful_certificates, &app->app_local->useful_certificate_info); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; if (err) return err; /* Read information about private keys. */ assert (!app->app_local->private_key_info); err = read_ef_prkdf (app, app->app_local->odf.private_keys, &app->app_local->private_key_info); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; if (err) return err; /* Read information about authentication objects. */ assert (!app->app_local->auth_object_info); err = read_ef_aodf (app, app->app_local->odf.auth_objects, &app->app_local->auth_object_info); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; return err; } /* Helper to do_learn_status: Send information about all certificates listed in CERTINFO back. Use CERTTYPE as type of the certificate. */ static gpg_error_t send_certinfo (app_t app, ctrl_t ctrl, const char *certtype, cdf_object_t certinfo) { for (; certinfo; certinfo = certinfo->next) { char *buf, *p; buf = xtrymalloc (9 + certinfo->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "P15"); if (app->app_local->home_df) { snprintf (p, 6, "-%04X", (unsigned int)(app->app_local->home_df & 0xffff)); p += 5; } p = stpcpy (p, "."); bin2hex (certinfo->objid, certinfo->objidlen, p); send_status_info (ctrl, "CERTINFO", certtype, strlen (certtype), buf, strlen (buf), NULL, (size_t)0); xfree (buf); } return 0; } /* Get the keygrip of the private key object PRKDF. On success the keygrip gets returned in the caller provided 41 byte buffer R_GRIPSTR. */ static gpg_error_t keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr) { gpg_error_t err; cdf_object_t cdf; unsigned char *der; size_t derlen; ksba_cert_t cert; /* FIXME: We should check whether a public key directory file and a matching public key for PRKDF is available. This should make extraction of the key much easier. My current test card doesn't have one, so we can only use the fallback solution bu looking for a matching certificate and extract the key from there. */ /* Look for a matching certificate. A certificate matches if the Id matches the one of the private key info. */ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == prkdf->objidlen && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) break; if (!cdf) for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == prkdf->objidlen && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) break; if (!cdf) for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == prkdf->objidlen && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) break; if (!cdf) return gpg_error (GPG_ERR_NOT_FOUND); err = readcert_by_cdf (app, cdf, &der, &derlen); if (err) return err; err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, der, derlen); xfree (der); if (!err) err = app_help_get_keygrip_string (cert, r_gripstr); ksba_cert_release (cert); return err; } /* Helper to do_learn_status: Send information about all known keypairs back. FIXME: much code duplication from send_certinfo(). */ static gpg_error_t send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo) { gpg_error_t err; for (; keyinfo; keyinfo = keyinfo->next) { char gripstr[40+1]; char *buf, *p; int j; buf = xtrymalloc (9 + keyinfo->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "P15"); if (app->app_local->home_df) { snprintf (p, 6, "-%04X", (unsigned int)(app->app_local->home_df & 0xffff)); p += 5; } p = stpcpy (p, "."); bin2hex (keyinfo->objid, keyinfo->objidlen, p); err = keygripstr_from_prkdf (app, keyinfo, gripstr); if (err) { log_error ("can't get keygrip from "); for (j=0; j < keyinfo->pathlen; j++) log_printf ("%04hX", keyinfo->path[j]); log_printf (": %s\n", gpg_strerror (err)); } else { assert (strlen (gripstr) == 40); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, buf, strlen (buf), NULL, (size_t)0); } xfree (buf); } return 0; } /* This is the handler for the LEARN command. */ static gpg_error_t 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 { err = send_certinfo (app, ctrl, "100", app->app_local->certificate_info); if (!err) err = send_certinfo (app, ctrl, "101", app->app_local->trusted_certificate_info); if (!err) err = send_certinfo (app, ctrl, "102", app->app_local->useful_certificate_info); } if (!err) err = send_keypairinfo (app, ctrl, app->app_local->private_key_info); return err; } /* Read a certifciate using the information in CDF and return the certificate in a newly llocated buffer R_CERT and its length R_CERTLEN. */ static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; unsigned char *buffer = NULL; const unsigned char *p, *save_p; size_t buflen, n; int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca; int i; *r_cert = NULL; *r_certlen = 0; /* First check whether it has been cached. */ if (cdf->image) { *r_cert = xtrymalloc (cdf->imagelen); if (!*r_cert) return gpg_error_from_syserror (); memcpy (*r_cert, cdf->image, cdf->imagelen); *r_certlen = cdf->imagelen; return 0; } /* Read the entire file. fixme: This could be optimized by first reading the header to figure out how long the certificate actually is. */ err = select_ef_by_path (app, cdf->path, cdf->pathlen); if (err) goto leave; err = iso7816_read_binary (app_get_slot (app), cdf->off, cdf->len, &buffer, &buflen); if (!err && (!buflen || *buffer == 0xff)) err = gpg_error (GPG_ERR_NOT_FOUND); if (err) { log_error ("error reading certificate with Id "); for (i=0; i < cdf->objidlen; i++) log_printf ("%02X", cdf->objid[i]); log_printf (": %s\n", gpg_strerror (err)); goto leave; } /* Check whether this is really a certificate. */ p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) rootca = 0; else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) rootca = 1; else { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } totobjlen = objlen + hdrlen; assert (totobjlen <= buflen); err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (!rootca && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) { /* The certificate seems to be contained in a userCertificate container. Skip this and assume the following sequence is the certificate. */ if (n < objlen) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } p += objlen; n -= objlen; save_p = p; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } totobjlen = objlen + hdrlen; assert (save_p + totobjlen <= buffer + buflen); memmove (buffer, save_p, totobjlen); } *r_cert = buffer; buffer = NULL; *r_certlen = totobjlen; /* Try to cache it. */ if (!cdf->image && (cdf->image = xtrymalloc (*r_certlen))) { memcpy (cdf->image, *r_cert, *r_certlen); cdf->imagelen = *r_certlen; } leave: xfree (buffer); return err; } /* Handler for the READCERT command. Read the certificate with id CERTID (as returned by learn_status in the CERTINFO status lines) and return it in the freshly allocated buffer to be stored at R_CERT and its length at R_CERTLEN. A error code will be returned on failure and R_CERT and R_CERTLEN will be set to (NULL,0). */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; cdf_object_t cdf; *r_cert = NULL; *r_certlen = 0; err = cdf_object_from_certid (app, certid, &cdf); if (!err) err = readcert_by_cdf (app, cdf, r_cert, r_certlen); return err; } /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { gpg_error_t err; if (!strcmp (name, "$AUTHKEYID")) { char *buf, *p; prkdf_object_t prkdf; /* We return the ID of the first private keycapable of signing. */ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) if (prkdf->usageflags.sign) break; if (prkdf) { buf = xtrymalloc (9 + prkdf->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "P15"); if (app->app_local->home_df) { snprintf (p, 6, "-%04X", (unsigned int)(app->app_local->home_df & 0xffff)); p += 5; } p = stpcpy (p, "."); bin2hex (prkdf->objid, prkdf->objidlen, p); send_status_info (ctrl, name, buf, strlen (buf), NULL, 0); xfree (buf); return 0; } } else if (!strcmp (name, "$DISPSERIALNO")) { /* For certain cards we return special IDs. There is no general rule for it so we need to decide case by case. */ if (app->app_local->card_type == CARD_TYPE_BELPIC) { /* The eID card has a card number printed on the front matter which seems to be a good indication. */ unsigned char *buffer; const unsigned char *p; size_t buflen, n; unsigned short path[] = { 0x3F00, 0xDF01, 0x4031 }; err = select_ef_by_path (app, path, DIM(path) ); if (!err) err = iso7816_read_binary (app_get_slot (app), 0, 0, &buffer, &buflen); if (err) { log_error ("error accessing EF(ID): %s\n", gpg_strerror (err)); return err; } p = find_tlv (buffer, buflen, 1, &n); if (p && n == 12) { char tmp[12+2+1]; memcpy (tmp, p, 3); tmp[3] = '-'; memcpy (tmp+4, p+3, 7); tmp[11] = '-'; memcpy (tmp+12, p+10, 2); tmp[14] = 0; send_status_info (ctrl, name, tmp, strlen (tmp), NULL, 0); xfree (buffer); return 0; } xfree (buffer); } } return gpg_error (GPG_ERR_INV_NAME); } /* Micardo cards require special treatment. This is a helper for the crypto functions to manage the security environment. We expect that the key file has already been selected. FID is the one of the selected key. */ static gpg_error_t micardo_mse (app_t app, unsigned short fid) { gpg_error_t err; int recno; unsigned short refdata = 0; int se_num; unsigned char msebuf[10]; /* Read the KeyD file containing extra information on keys. */ err = iso7816_select_file (app_get_slot (app), 0x0013, 0); if (err) { log_error ("error reading EF_keyD: %s\n", gpg_strerror (err)); return err; } for (recno = 1, se_num = -1; ; recno++) { unsigned char *buffer; size_t buflen; size_t n, nn; const unsigned char *p, *pp; err = iso7816_read_record (app_get_slot (app), recno, 1, 0, &buffer, &buflen); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) break; /* ready */ if (err) { log_error ("error reading EF_keyD record: %s\n", gpg_strerror (err)); return err; } log_printhex (buffer, buflen, "keyD record:"); p = find_tlv (buffer, buflen, 0x83, &n); if (p && n == 4 && ((p[2]<<8)|p[3]) == fid) { refdata = ((p[0]<<8)|p[1]); /* Locate the SE DO and the there included sec env number. */ p = find_tlv (buffer, buflen, 0x7b, &n); if (p && n) { pp = find_tlv (p, n, 0x80, &nn); if (pp && nn == 1) { se_num = *pp; xfree (buffer); break; /* found. */ } } } xfree (buffer); } if (se_num == -1) { log_error ("CRT for keyfile %04hX not found\n", fid); return gpg_error (GPG_ERR_NOT_FOUND); } /* Restore the security environment to SE_NUM if needed */ if (se_num) { err = iso7816_manage_security_env (app_get_slot (app), 0xf3, se_num, NULL, 0); if (err) { log_error ("restoring SE to %d failed: %s\n", se_num, gpg_strerror (err)); return err; } } /* Set the DST reference data. */ msebuf[0] = 0x83; msebuf[1] = 0x03; msebuf[2] = 0x80; msebuf[3] = (refdata >> 8); msebuf[4] = refdata; err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xb6, msebuf, 5); if (err) { log_error ("setting SE to reference file %04hX failed: %s\n", refdata, gpg_strerror (err)); return err; } return 0; } /* Handler for the PKSIGN command. Create the signature and return the allocated result in OUTDATA. If a PIN is required, the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that as the 3rd argument. */ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; gpg_error_t err; int i; unsigned char data[36]; /* Must be large enough for a SHA-1 digest + the largest OID prefix above and also fit the 36 bytes of md5sha1. */ prkdf_object_t prkdf; /* The private key object. */ aodf_object_t aodf; /* The associated authentication object. */ int no_data_padding = 0; /* True if the card want the data without padding.*/ int mse_done = 0; /* Set to true if the MSE has been done. */ if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); if (indatalen != 20 && indatalen != 16 && indatalen != 35 && indatalen != 36) return gpg_error (GPG_ERR_INV_VALUE); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover ||prkdf->usageflags.non_repudiation)) { log_error ("key %s may not be used for signing\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (!prkdf->authid) { log_error ("no authentication object defined for %s\n", keyidstr); /* fixme: we might want to go ahead and do without PIN verification. */ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); } /* Find the authentication object to this private key object. */ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next) if (aodf->objidlen == prkdf->authidlen && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen)) break; if (!aodf) { log_error ("authentication object for %s missing\n", keyidstr); return gpg_error (GPG_ERR_INV_CARD); } if (aodf->authid) { log_error ("PIN verification is protected by an " "additional authentication token\n"); return gpg_error (GPG_ERR_BAD_PIN_METHOD); } if (aodf->pinflags.integrity_protected || aodf->pinflags.confidentiality_protected) { log_error ("PIN verification requires unsupported protection method\n"); return gpg_error (GPG_ERR_BAD_PIN_METHOD); } if (!aodf->stored_length && aodf->pinflags.needs_padding) { log_error ("PIN verification requires padding but no length known\n"); return gpg_error (GPG_ERR_INV_CARD); } /* Select the key file. Note that this may change the security environment thus we do it before PIN verification. */ err = select_ef_by_path (app, prkdf->path, prkdf->pathlen); if (err) { log_error ("error selecting file for key %s: %s\n", keyidstr, gpg_strerror (errno)); return err; } /* Due to the fact that the non-repudiation signature on a BELPIC card requires a verify immediately before the DSO we set the MSE before we do the verification. Other cards might also allow this but I don't want to break anything, thus we do it only for the BELPIC card here. */ if (app->app_local->card_type == CARD_TYPE_BELPIC) { unsigned char mse[5]; mse[0] = 4; /* Length of the template. */ mse[1] = 0x80; /* Algorithm reference tag. */ if (hashalgo == MD_USER_TLS_MD5SHA1) mse[2] = 0x01; /* Let card do pkcs#1 0xFF padding. */ else mse[2] = 0x02; /* RSASSA-PKCS1-v1.5 using SHA1. */ mse[3] = 0x84; /* Private key reference tag. */ mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82; err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); no_data_padding = 1; mse_done = 1; } if (err) { log_error ("MSE failed: %s\n", gpg_strerror (err)); return err; } /* Now that we have all the information available, prepare and run the PIN verification.*/ if (1) { char *pinvalue; size_t pinvaluelen; const char *errstr; const char *s; if (prkdf->usageflags.non_repudiation && app->app_local->card_type == CARD_TYPE_BELPIC) err = pincb (pincb_arg, "PIN (qualified signature!)", &pinvalue); else err = pincb (pincb_arg, "PIN", &pinvalue); if (err) { log_info ("PIN callback returned error: %s\n", gpg_strerror (err)); return err; } /* 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) { log_error ("PIN is too short; minimum length is %lu\n", aodf->min_length); err = gpg_error (GPG_ERR_BAD_PIN); } else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length) { /* This would otherwise truncate the PIN silently. */ log_error ("PIN is too large; maximum length is %lu\n", aodf->stored_length); err = gpg_error (GPG_ERR_BAD_PIN); } else if (aodf->max_length_valid && strlen (pinvalue) > aodf->max_length) { log_error ("PIN is too large; maximum length is %lu\n", aodf->max_length); err = gpg_error (GPG_ERR_BAD_PIN); } if (err) { xfree (pinvalue); return err; } errstr = NULL; err = 0; switch (aodf->pintype) { case PIN_TYPE_BCD: case PIN_TYPE_ASCII_NUMERIC: for (s=pinvalue; digitp (s); s++) ; if (*s) { errstr = "Non-numeric digits found in PIN"; err = gpg_error (GPG_ERR_BAD_PIN); } break; case PIN_TYPE_UTF8: break; case PIN_TYPE_HALF_NIBBLE_BCD: errstr = "PIN type Half-Nibble-BCD is not supported"; break; case PIN_TYPE_ISO9564_1: errstr = "PIN type ISO9564-1 is not supported"; break; default: errstr = "Unknown PIN type"; break; } if (errstr) { log_error ("can't verify PIN: %s\n", errstr); xfree (pinvalue); return err? err : gpg_error (GPG_ERR_BAD_PIN_METHOD); } if (aodf->pintype == PIN_TYPE_BCD ) { char *paddedpin; int ndigits; for (ndigits=0, s=pinvalue; *s; ndigits++, s++) ; paddedpin = xtrymalloc (aodf->stored_length+1); if (!paddedpin) { err = gpg_error_from_syserror (); xfree (pinvalue); return err; } i = 0; paddedpin[i++] = 0x20 | (ndigits & 0x0f); for (s=pinvalue; i < aodf->stored_length && *s && s[1]; s = s+2 ) paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f)); if (i < aodf->stored_length && *s) paddedpin[i++] = (((*s - '0') << 4) |((aodf->pad_char_valid?aodf->pad_char:0)&0x0f)); if (aodf->pinflags.needs_padding) while (i < aodf->stored_length) paddedpin[i++] = aodf->pad_char_valid? aodf->pad_char : 0; xfree (pinvalue); pinvalue = paddedpin; pinvaluelen = i; } else if (aodf->pinflags.needs_padding) { char *paddedpin; paddedpin = xtrymalloc (aodf->stored_length+1); if (!paddedpin) { err = gpg_error_from_syserror (); xfree (pinvalue); return err; } for (i=0, s=pinvalue; i < aodf->stored_length && *s; i++, s++) paddedpin[i] = *s; /* Not sure what padding char to use if none has been set. For now we use 0x00; maybe a space would be better. */ for (; i < aodf->stored_length; i++) paddedpin[i] = aodf->pad_char_valid? aodf->pad_char : 0; paddedpin[i] = 0; pinvaluelen = i; xfree (pinvalue); pinvalue = paddedpin; } else pinvaluelen = strlen (pinvalue); err = iso7816_verify (app_get_slot (app), aodf->pin_reference_valid? aodf->pin_reference : 0, pinvalue, pinvaluelen); xfree (pinvalue); if (err) { log_error ("PIN verification failed: %s\n", gpg_strerror (err)); return err; } log_debug ("PIN verification succeeded\n"); } /* Prepare the DER object from INDATA. */ if (indatalen == 36) { /* No ASN.1 container used. */ if (hashalgo != MD_USER_TLS_MD5SHA1) return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); memcpy (data, indata, indatalen); } else if (indatalen == 35) { /* Alright, the caller was so kind to send us an already prepared DER object. Check that it is what we want and that it matches the hash algorithm. */ if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) ; else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix, 15)) ; else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); memcpy (data, indata, indatalen); } else { /* Need to prepend the prefix. */ 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); } /* Manage security environment needs to be weaked for certain cards. */ if (mse_done) err = 0; else if (app->app_local->card_type == CARD_TYPE_TCOS) { /* TCOS creates signatures always using the local key 0. MSE may not be used. */ } else if (app->app_local->card_type == CARD_TYPE_MICARDO) { if (!prkdf->pathlen) err = gpg_error (GPG_ERR_BUG); else err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]); } else if (prkdf->key_reference_valid) { unsigned char mse[3]; mse[0] = 0x84; /* Select asym. key. */ mse[1] = 1; mse[2] = prkdf->key_reference; err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } if (err) { log_error ("MSE failed: %s\n", gpg_strerror (err)); return err; } if (hashalgo == MD_USER_TLS_MD5SHA1) err = iso7816_compute_ds (app_get_slot (app), 0, data, 36, 0, outdata, outdatalen); else if (no_data_padding) err = iso7816_compute_ds (app_get_slot (app), 0, data+15, 20, 0,outdata,outdatalen); else err = iso7816_compute_ds (app_get_slot (app), 0, data, 35, 0, outdata, outdatalen); return err; } /* Handler for the PKAUTH command. This is basically the same as the PKSIGN command but we first check that the requested key is suitable for authentication; that is, it must match the criteria used for the attribute $AUTHKEYID. See do_sign for calling conventions; there is no HASHALGO, though. */ static gpg_error_t do_auth (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { gpg_error_t err; prkdf_object_t prkdf; int algo; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; if (!prkdf->usageflags.sign) { log_error ("key %s may not be used for authentication\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1; return do_sign (app, keyidstr, algo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); } /* Assume that EF(DIR) has been selected. Read its content and figure out the home EF of pkcs#15. Return that home DF or 0 if not found and the value at the address of BELPIC indicates whether it was found by the belpic aid. */ static unsigned short read_home_df (int slot, int *r_belpic) { gpg_error_t err; unsigned char *buffer; const unsigned char *p, *pp; size_t buflen, n, nn; unsigned short result = 0; *r_belpic = 0; err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen); if (err) { log_error ("error reading EF{DIR}: %s\n", gpg_strerror (err)); return 0; } /* FIXME: We need to scan all records. */ p = find_tlv (buffer, buflen, 0x61, &n); if (p && n) { pp = find_tlv (p, n, 0x4f, &nn); if (pp && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn)) || (*r_belpic = (nn == sizeof pkcs15be_aid && !memcmp (pp, pkcs15be_aid, nn))))) { pp = find_tlv (p, n, 0x50, &nn); if (pp) /* fixme: Filter log value? */ log_info ("pkcs#15 application label from EF(DIR) is '%.*s'\n", (int)nn, pp); pp = find_tlv (p, n, 0x51, &nn); if (pp && nn == 4 && *pp == 0x3f && !pp[1]) { result = ((pp[2] << 8) | pp[3]); log_info ("pkcs#15 application directory is 0x%04hX\n", result); } } } xfree (buffer); return result; } /* Select the PKCS#15 application on the card in SLOT. */ gpg_error_t app_select_p15 (app_t app) { int slot = app_get_slot (app); int rc; unsigned short def_home_df = 0; card_type_t card_type = CARD_TYPE_UNKNOWN; int direct = 0; int is_belpic = 0; rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0); if (rc) { /* Not found: Try to locate it from 2F00. We use direct path selection here because it seems that the Belgian eID card does only allow for that. Many other cards supports this selection method too. Note, that we don't use select_application above for the Belgian card - the call works but it seems that it did not switch to the correct DF. Using the 2f02 just works. */ unsigned short path[1] = { 0x2f00 }; rc = iso7816_select_path (app_get_slot (app), path, 1); if (!rc) { direct = 1; def_home_df = read_home_df (slot, &is_belpic); if (def_home_df) { path[0] = def_home_df; rc = iso7816_select_path (app_get_slot (app), path, 1); } } } if (rc) { /* Still not found: Try the default DF. */ def_home_df = 0x5015; rc = iso7816_select_file (slot, def_home_df, 1); } if (!rc) { /* Determine the type of the card. The general case is to look it up from the ATR table. For the Belgian eID card we know it instantly from the AID. */ if (is_belpic) { card_type = CARD_TYPE_BELPIC; } else { unsigned char *atr; size_t atrlen; int i; atr = apdu_get_atr (app_get_slot (app), &atrlen); if (!atr) rc = gpg_error (GPG_ERR_INV_CARD); else { for (i=0; card_atr_list[i].atrlen; i++) if (card_atr_list[i].atrlen == atrlen && !memcmp (card_atr_list[i].atr, atr, atrlen)) { card_type = card_atr_list[i].type; break; } xfree (atr); } } } if (!rc) { app->apptype = APPTYPE_P15; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { rc = gpg_error_from_syserror (); goto leave; } /* Set the home DF. Note that we currently can't do that if the selection via application ID worked. This will store 0 there instead. FIXME: We either need to figure the home_df via the DIR file or using the return values from the select file APDU. */ app->app_local->home_df = def_home_df; /* Store the card type. FIXME: We might want to put this into the common APP structure. */ app->app_local->card_type = card_type; /* Store whether we may and should use direct path selection. */ app->app_local->direct_path_selection = direct; /* Read basic information and thus check whether this is a real card. */ rc = read_p15_info (app); if (rc) goto leave; /* Special serial number munging. We need to check for a German prototype card right here because we need to access to EF(TokenInfo). We mark such a serial number by the using a prefix of FF0100. */ if (app->card->serialnolen == 12 && !memcmp (app->card->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12)) { /* This is a German card with a silly serial number. Try to get the serial number from the EF(TokenInfo). . */ unsigned char *p; /* FIXME: actually get it from EF(TokenInfo). */ p = xtrymalloc (3 + app->card->serialnolen); if (!p) rc = gpg_error (gpg_err_code_from_errno (errno)); else { memcpy (p, "\xff\x01", 3); memcpy (p+3, app->card->serialno, app->card->serialnolen); app->card->serialnolen += 3; xfree (app->card->serialno); app->card->serialno = p; } } app->fnc.deinit = do_deinit; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; app->fnc.setattr = NULL; app->fnc.genkey = NULL; app->fnc.sign = do_sign; app->fnc.auth = do_auth; app->fnc.decipher = NULL; app->fnc.change_pin = NULL; app->fnc.check_pin = NULL; leave: if (rc) do_deinit (app); } return rc; } diff --git a/scd/app-piv.c b/scd/app-piv.c index 3b94a28e4..82d52aabc 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -1,3523 +1,3524 @@ /* app-piv.c - The OpenPGP card application. * Copyright (C) 2019 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* Some notes: * - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4 * - https://developers.yubico.com/PIV/Introduction/PIV_attestation.html * * - Access control matrix: * | Action | 9B | PIN | PUK | | * |--------------+-----+-----+-----+------------------------------| * | Generate key | yes | | | | * | Change 9B | yes | | | | * | Change retry | yes | yes | | Yubikey only | * | Import key | yes | | | | * | Import cert | yes | | | | * | Change CHUID | yes | | | | * | Reset card | | | | PIN and PUK in blocked state | * | Verify PIN | | yes | | | * | Sign data | | yes | | | * | Decrypt data | | yes | | | * | Change PIN | | yes | | | * | Change PUK | | | yes | | * | Unblock PIN | | | yes | New PIN required | * |---------------------------------------------------------------| * (9B indicates the 24 byte PIV Card Application Administration Key) * * - When generating a key we store the created public key in the * corresponding data object, so that gpg and gpgsm are able to get * the public key, create a certificate and store that then in that * data object. That is not standard compliant but due to the use * of other tags, it should not harm. See do_genkey for the actual * used tag structure. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "apdu.h" /* We use apdu_send_direct. */ #define PIV_ALGORITHM_3DES_ECB_0 0x00 #define PIV_ALGORITHM_2DES_ECB 0x01 #define PIV_ALGORITHM_2DES_CBC 0x02 #define PIV_ALGORITHM_3DES_ECB 0x03 #define PIV_ALGORITHM_3DES_CBC 0x04 #define PIV_ALGORITHM_RSA 0x07 #define PIV_ALGORITHM_AES128_ECB 0x08 #define PIV_ALGORITHM_AES128_CBC 0x09 #define PIV_ALGORITHM_AES192_ECB 0x0A #define PIV_ALGORITHM_AES192_CBC 0x0B #define PIV_ALGORITHM_AES256_ECB 0x0C #define PIV_ALGORITHM_AES256_CBC 0x0D #define PIV_ALGORITHM_ECC_P256 0x11 #define PIV_ALGORITHM_ECC_P384 0x14 /* The AID for PIV. */ static char const piv_aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; /* A table describing the DOs of a PIV card. */ struct data_object_s { unsigned int tag; unsigned int mandatory:1; unsigned int acr_contact:2; /* 0=always, 1=VCI, 2=PIN, 3=PINorOCC */ unsigned int acr_contactless:2; /* 0=always, 1=VCI, 2=VCIandPIN, 3=VCIand(PINorOCC) */ unsigned int dont_cache:1; /* Data item will not be cached. */ unsigned int flush_on_error:1; /* Flush cached item on error. */ unsigned int keypair:1; /* Has a public key for a keypair. */ const char keyref[3]; /* The key reference. */ const char *oidsuffix; /* Suffix of the OID. */ const char *usage; /* Usage string for a keypair or NULL. */ const char *desc; /* Description of the DO. */ }; typedef struct data_object_s *data_object_t; static struct data_object_s data_objects[] = { { 0x5FC107, 1, 0,1, 0,0, 0, "", "1.219.0", NULL, "Card Capability Container"}, { 0x5FC102, 1, 0,0, 0,0, 0, "", "2.48.0", NULL, "Cardholder Unique Id" }, { 0x5FC105, 1, 0,1, 0,0, 1, "9A", "2.1.1", "a", "Cert PIV Authentication" }, { 0x5FC103, 1, 2,2, 0,0, 0, "", "2.96.16", NULL, "Cardholder Fingerprints" }, { 0x5FC106, 1, 0,1, 0,0, 0, "", "2.144.0", NULL, "Security Object" }, { 0x5FC108, 1, 2,2, 0,0, 0, "", "2.96.48", NULL, "Cardholder Facial Image" }, { 0x5FC101, 1, 0,0, 0,0, 1, "9E", "2.5.0", "a", "Cert Card Authentication"}, { 0x5FC10A, 0, 0,1, 0,0, 1, "9C", "2.1.0", "sc", "Cert Digital Signature" }, { 0x5FC10B, 0, 0,1, 0,0, 1, "9D", "2.1.2", "e", "Cert Key Management" }, { 0x5FC109, 0, 3,3, 0,0, 0, "", "2.48.1", NULL, "Printed Information" }, { 0x7E, 0, 0,0, 0,0, 0, "", "2.96.80", NULL, "Discovery Object" }, { 0x5FC10C, 0, 0,1, 0,0, 0, "", "2.96.96", NULL, "Key History Object" }, { 0x5FC10D, 0, 0,1, 0,0, 0, "82", "2.16.1", "e", "Retired Cert Key Mgm 1" }, { 0x5FC10E, 0, 0,1, 0,0, 0, "83", "2.16.2", "e", "Retired Cert Key Mgm 2" }, { 0x5FC10F, 0, 0,1, 0,0, 0, "84", "2.16.3", "e", "Retired Cert Key Mgm 3" }, { 0x5FC110, 0, 0,1, 0,0, 0, "85", "2.16.4", "e", "Retired Cert Key Mgm 4" }, { 0x5FC111, 0, 0,1, 0,0, 0, "86", "2.16.5", "e", "Retired Cert Key Mgm 5" }, { 0x5FC112, 0, 0,1, 0,0, 0, "87", "2.16.6", "e", "Retired Cert Key Mgm 6" }, { 0x5FC113, 0, 0,1, 0,0, 0, "88", "2.16.7", "e", "Retired Cert Key Mgm 7" }, { 0x5FC114, 0, 0,1, 0,0, 0, "89", "2.16.8", "e", "Retired Cert Key Mgm 8" }, { 0x5FC115, 0, 0,1, 0,0, 0, "8A", "2.16.9", "e", "Retired Cert Key Mgm 9" }, { 0x5FC116, 0, 0,1, 0,0, 0, "8B", "2.16.10", "e", "Retired Cert Key Mgm 10" }, { 0x5FC117, 0, 0,1, 0,0, 0, "8C", "2.16.11", "e", "Retired Cert Key Mgm 11" }, { 0x5FC118, 0, 0,1, 0,0, 0, "8D", "2.16.12", "e", "Retired Cert Key Mgm 12" }, { 0x5FC119, 0, 0,1, 0,0, 0, "8E", "2.16.13", "e", "Retired Cert Key Mgm 13" }, { 0x5FC11A, 0, 0,1, 0,0, 0, "8F", "2.16.14", "e", "Retired Cert Key Mgm 14" }, { 0x5FC11B, 0, 0,1, 0,0, 0, "90", "2.16.15", "e", "Retired Cert Key Mgm 15" }, { 0x5FC11C, 0, 0,1, 0,0, 0, "91", "2.16.16", "e", "Retired Cert Key Mgm 16" }, { 0x5FC11D, 0, 0,1, 0,0, 0, "92", "2.16.17", "e", "Retired Cert Key Mgm 17" }, { 0x5FC11E, 0, 0,1, 0,0, 0, "93", "2.16.18", "e", "Retired Cert Key Mgm 18" }, { 0x5FC11F, 0, 0,1, 0,0, 0, "94", "2.16.19", "e", "Retired Cert Key Mgm 19" }, { 0x5FC120, 0, 0,1, 0,0, 0, "95", "2.16.20", "e", "Retired Cert Key Mgm 20" }, { 0x5FC121, 0, 2,2, 0,0, 0, "", "2.16.21", NULL, "Cardholder Iris Images" }, { 0x7F61, 0, 0,0, 0,0, 0, "", "2.16.22", NULL, "BIT Group Template" }, { 0x5FC122, 0, 0,0, 0,0, 0, "", "2.16.23", NULL, "SM Cert Signer" }, { 0x5FC123, 0, 3,3, 0,0, 0, "", "2.16.24", NULL, "Pairing Code Ref Data" }, { 0 } /* Other key reference values without a data object: * "00" Global PIN (not cleared by application switching) * "04" PIV Secure Messaging Key * "80" PIV Application PIN * "81" PIN Unblocking Key * "96" Primary Finger OCC * "97" Secondary Finger OCC * "98" Pairing Code * "9B" PIV Card Application Administration Key * * Yubikey specific data objects: * "F9" Attestation key (preloaded can be replaced) */ }; /* One cache item for DOs. */ struct cache_s { struct cache_s *next; int tag; size_t length; unsigned char data[1]; }; /* Object with application specific data. */ struct app_local_s { /* A linked list with cached DOs. */ struct cache_s *cache; /* Various flags. */ struct { unsigned int yubikey:1; /* This is on a Yubikey. */ } flags; }; /***** Local prototypes *****/ static gpg_error_t get_keygrip_by_tag (app_t app, unsigned int tag, char **r_keygripstr, int *got_cert); static gpg_error_t genkey_parse_rsa (const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp); static gpg_error_t genkey_parse_ecc (const unsigned char *data, size_t datalen, int mechanism, gcry_sexp_t *r_sexp); /* Deconstructor. */ static void do_deinit (app_t app) { if (app && app->app_local) { struct cache_s *c, *c2; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } xfree (app->app_local); app->app_local = NULL; } } /* 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. The tag-53 container is also removed. */ static gpg_error_t get_cached_data (app_t app, int tag, unsigned char **result, size_t *resultlen, int get_immediate) { gpg_error_t err; int i; unsigned char *p; const unsigned char *s; size_t len, n; struct cache_s *c; *result = NULL; *resultlen = 0; if (!get_immediate) { for (c=app->app_local->cache; c; c = c->next) if (c->tag == tag) { if(c->length) { p = xtrymalloc (c->length); if (!p) return gpg_error_from_syserror (); memcpy (p, c->data, c->length); *result = p; } *resultlen = c->length; return 0; } } err = iso7816_get_data_odd (app_get_slot (app), 0, tag, &p, &len); if (err) return err; /* Unless the Discovery Object or the BIT Group Template is * requested, remove the outer container. * (SP800-73.4 Part 2, section 3.1.2) */ if (tag == 0x7E || tag == 0x7F61) ; else if (len && *p == 0x53 && (s = find_tlv (p, len, 0x53, &n))) { memmove (p, s, n); len = n; } if (len) *result = p; *resultlen = len; /* Check whether we should cache this object. */ if (get_immediate) return 0; for (i=0; data_objects[i].tag; i++) if (data_objects[i].tag == tag) { if (data_objects[i].dont_cache) return 0; break; } /* Okay, cache it. */ for (c=app->app_local->cache; c; c = c->next) log_assert (c->tag != tag); c = xtrymalloc (sizeof *c + len); if (c) { if (len) memcpy (c->data, p, len); else xfree (p); c->length = len; c->tag = tag; c->next = app->app_local->cache; app->app_local->cache = c; } return 0; } /* Remove data object described by TAG from the cache. If TAG is 0 * all cache iterms are flushed. */ static void flush_cached_data (app_t app, int tag) { struct cache_s *c, *cprev; for (c=app->app_local->cache, cprev=NULL; c; cprev=c, c = c->next) if (c->tag == tag || !tag) { if (cprev) cprev->next = c->next; else app->app_local->cache = c->next; xfree (c); for (c=app->app_local->cache; c ; c = c->next) { log_assert (c->tag != tag); /* Oops: duplicated entry. */ } return; } } /* Get the DO identified by TAG from the card in SLOT and return a * buffer with its content in RESULT and NBYTES. The return value is * NULL if not found or a pointer which must be used to release the * buffer holding value. */ static void * get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, int *r_err) { gpg_error_t err; int i; unsigned char *buffer; size_t buflen; unsigned char *value; size_t valuelen; gpg_error_t dummyerr; if (!r_err) r_err = &dummyerr; *result = NULL; *nbytes = 0; *r_err = 0; for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) ; value = NULL; err = gpg_error (GPG_ERR_ENOENT); if (!value) /* Not in a constructed DO, try simple. */ { err = get_cached_data (app, tag, &buffer, &buflen, data_objects[i].dont_cache); if (!err) { value = buffer; valuelen = buflen; } } if (!err) { *nbytes = valuelen; *result = value; return buffer; } *r_err = err; return NULL; } static void dump_all_do (int slot) { gpg_error_t err; int i; unsigned char *buffer; size_t buflen; for (i=0; data_objects[i].tag; i++) { /* We don't try extended length APDU because such large DO would be pretty useless in a log file. */ err = iso7816_get_data_odd (slot, 0, data_objects[i].tag, &buffer, &buflen); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT && !data_objects[i].mandatory) ; else log_info ("DO '%s' not available: %s\n", data_objects[i].desc, gpg_strerror (err)); } else { if (data_objects[i].tag == 0x5FC109) log_info ("DO '%s': '%.*s'\n", data_objects[i].desc, (int)buflen, buffer); else { log_info ("DO '%s': ", data_objects[i].desc); if (buflen > 16 && opt.verbose < 2) { log_printhex (buffer, 16, NULL); log_printf ("[...]\n"); } else log_printhex (buffer, buflen, ""); } } xfree (buffer); buffer = NULL; } } /* Create a TLV tag and value and store it at BUFFER. Return the * length of tag and length. A LENGTH greater than 65535 is * truncated. TAG must be less or equal to 2^16. If BUFFER is NULL, * only the required length is computed. */ static size_t add_tlv (unsigned char *buffer, unsigned int tag, size_t length) { if (length > 0xffff) length = 0xffff; if (buffer) { unsigned char *p = buffer; if (tag > 0xff) *p++ = tag >> 8; *p++ = tag; if (length < 128) *p++ = length; else if (length < 256) { *p++ = 0x81; *p++ = length; } else { *p++ = 0x82; *p++ = length >> 8; *p++ = length; } return p - buffer; } else { size_t n = 0; if (tag > 0xff) n++; n++; if (length < 128) n++; else if (length < 256) n += 2; else n += 3; return n; } } /* Function to build a list of TLV and return the result in a mallcoed * buffer. The varargs are tuples of (int,size_t,void) each with the * tag, the length and the actual data. A (0,0,NULL) tuple terminates * the list. Up to 10 tuples are supported. If SECMEM is true the * returned buffer is allocated in secure memory. */ static gpg_error_t concat_tlv_list (int secure, unsigned char **r_result, size_t *r_resultlen, ...) { gpg_error_t err; va_list arg_ptr; struct { int tag; unsigned int len; unsigned int contlen; const void *data; } argv[10]; int i, j, argc; unsigned char *data = NULL; size_t datalen; unsigned char *p; size_t n; *r_result = NULL; *r_resultlen = 0; /* Collect all args. Check that length is <= 2^16 to match the * behaviour of add_tlv. */ va_start (arg_ptr, r_resultlen); argc = 0; while (((argv[argc].tag = va_arg (arg_ptr, int)))) { argv[argc].len = va_arg (arg_ptr, size_t); argv[argc].contlen = 0; argv[argc].data = va_arg (arg_ptr, const void *); if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff) { va_end (arg_ptr); err = gpg_error (GPG_ERR_EINVAL); goto leave; } argc++; } va_end (arg_ptr); /* Compute the required buffer length and allocate the buffer. */ datalen = 0; for (i=0; i < argc; i++) { if (!argv[i].len && !argv[i].data) { /* Constructed tag. Compute its length. Note that we * currently allow only one constructed tag in the list. */ for (n=0, j = i + 1; j < argc; j++) { log_assert (!(!argv[j].len && !argv[j].data)); n += add_tlv (NULL, argv[j].tag, argv[j].len); n += argv[j].len; } argv[i].contlen = n; datalen += add_tlv (NULL, argv[i].tag, n); } else { datalen += add_tlv (NULL, argv[i].tag, argv[i].len); datalen += argv[i].len; } } data = secure? xtrymalloc_secure (datalen) : xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } /* Copy that data to the buffer. */ p = data; for (i=0; i < argc; i++) { if (!argv[i].len && !argv[i].data) { /* Constructed tag. */ p += add_tlv (p, argv[i].tag, argv[i].contlen); } else { p += add_tlv (p, argv[i].tag, argv[i].len); memcpy (p, argv[i].data, argv[i].len); p += argv[i].len; } } log_assert ( data + datalen == p ); *r_result = data; data = NULL; *r_resultlen = datalen; err = 0; leave: xfree (data); return err; } /* Wrapper around iso7816_put_data_odd which also sets the tag into * the '5C' data object. The varargs are tuples of (int,size_t,void) * with the tag, the length and the actual data. A (0,0,NULL) tuple * terminates the list. Up to 10 tuples are supported. */ static gpg_error_t put_data (int slot, unsigned int tag, ...) { gpg_error_t err; va_list arg_ptr; struct { int tag; size_t len; const void *data; } argv[10]; int i, argc; unsigned char data5c[5]; size_t data5clen; unsigned char *data = NULL; size_t datalen; unsigned char *p; size_t n; /* Collect all args. Check that length is <= 2^16 to match the * behaviour of add_tlv. */ va_start (arg_ptr, tag); argc = 0; while (((argv[argc].tag = va_arg (arg_ptr, int)))) { argv[argc].len = va_arg (arg_ptr, size_t); argv[argc].data = va_arg (arg_ptr, const void *); if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff) { va_end (arg_ptr); return GPG_ERR_EINVAL; } argc++; } va_end (arg_ptr); /* Build the TLV with the tag to be updated. */ data5c[0] = 0x5c; /* Tag list */ if (tag <= 0xff) { data5c[1] = 1; data5c[2] = tag; data5clen = 3; } else if (tag <= 0xffff) { data5c[1] = 2; data5c[2] = (tag >> 8); data5c[3] = tag; data5clen = 4; } else { data5c[1] = 3; data5c[2] = (tag >> 16); data5c[3] = (tag >> 8); data5c[4] = tag; data5clen = 5; } /* Compute the required buffer length and allocate the buffer. */ n = 0; for (i=0; i < argc; i++) { n += add_tlv (NULL, argv[i].tag, argv[i].len); n += argv[i].len; } datalen = data5clen + add_tlv (NULL, 0x53, n) + n; data = xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } /* Copy that data to the buffer. */ p = data; memcpy (p, data5c, data5clen); p += data5clen; p += add_tlv (p, 0x53, n); for (i=0; i < argc; i++) { p += add_tlv (p, argv[i].tag, argv[i].len); memcpy (p, argv[i].data, argv[i].len); p += argv[i].len; } log_assert ( data + datalen == p ); err = iso7816_put_data_odd (slot, -1 /* use command chaining */, 0x3fff, data, datalen); leave: xfree (data); return err; } /* Parse the key reference KEYREFSTR which is expected to hold a key * reference for a CHV object. Return the one octet keyref or -1 for * an invalid reference. */ static int parse_chv_keyref (const char *keyrefstr) { if (!keyrefstr) return -1; else if (!ascii_strcasecmp (keyrefstr, "PIV.00")) return 0x00; else if (!ascii_strcasecmp (keyrefstr, "PIV.80")) return 0x80; else if (!ascii_strcasecmp (keyrefstr, "PIV.81")) return 0x81; else return -1; } /* Return an allocated string with the serial number in a format to be * show to the user. With FAILMODE is true return NULL if such an * abbreviated S/N is not available, else return the full serial * number as a hex string. May return NULL on malloc problem. */ static char * get_dispserialno (app_t app, int failmode) { char *result; if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4 && !memcmp (app->card->serialno, "\xff\x02\x00", 3)) { /* This is a 4 byte S/N of a Yubikey which seems to be printed * on the token in decimal. Maybe they will print larger S/N * also in decimal but we can't be sure, thus do it only for * these 32 bit numbers. */ unsigned long sn; sn = app->card->serialno[4] * 16777216; sn += app->card->serialno[5] * 65536; sn += app->card->serialno[6] * 256; sn += app->card->serialno[7]; result = xtryasprintf ("yk-%lu", sn); } else if (failmode) result = NULL; /* No Abbreviated S/N. */ else result = app_get_serialno (app); return result; } /* The verify command can be used to retrieve the security status of * the card. Given the PIN name (e.g. "PIV.80" for thge application * pin, a status is returned: * * -1 = Error retrieving the data, * -2 = No such PIN, * -3 = PIN blocked, * -5 = Verify still valid, * n >= 0 = Number of verification attempts left. */ static int get_chv_status (app_t app, const char *keyrefstr) { unsigned char apdu[4]; unsigned int sw; int result; int keyref; keyref = parse_chv_keyref (keyrefstr); if (!keyrefstr) return -1; apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) result = -5; /* No need to verification. */ else if (sw == 0x6a88 || sw == 0x6a80) result = -2; /* No such PIN. */ else if (sw == 0x6983) result = -3; /* PIN is blocked. */ else if ((sw & 0xfff0) == 0x63C0) result = (sw & 0x000f); else result = -1; /* Error. */ return result; } /* Implementation of the GETATTR command. This is similar to the * LEARN command but returns only one value via status lines. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { const char *name; int tag; int special; } table[] = { { "SERIALNO", 0x0000, -1 }, { "$AUTHKEYID", 0x0000, -2 }, /* Default ssh key. */ { "$ENCRKEYID", 0x0000, -6 }, /* Default encryption key. */ { "$SIGNKEYID", 0x0000, -7 }, /* Default signing key. */ { "$DISPSERIALNO",0x0000, -3 }, { "CHV-STATUS", 0x0000, -4 }, { "CHV-USAGE", 0x007E, -5 } }; gpg_error_t err = 0; int idx; void *relptr; unsigned char *value; size_t valuelen; const unsigned char *s; size_t n; for (idx=0; (idx < DIM (table) && ascii_strcasecmp (table[idx].name, name)); idx++) ; if (!(idx < DIM (table))) err = gpg_error (GPG_ERR_INV_NAME); else if (table[idx].special == -1) { char *serial = app_get_serialno (app); if (serial) { send_status_direct (ctrl, "SERIALNO", serial); xfree (serial); } } else if (table[idx].special == -2) { char const tmp[] = "PIV.9A"; /* Cert PIV Authenticate. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else if (table[idx].special == -3) { char *tmp = get_dispserialno (app, 1); if (tmp) { send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, (size_t)0); xfree (tmp); } else err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */ } else if (table[idx].special == -4) /* CHV-STATUS */ { int tmp[4]; tmp[0] = get_chv_status (app, "PIV.00"); tmp[1] = get_chv_status (app, "PIV.80"); tmp[2] = get_chv_status (app, "PIV.81"); err = send_status_printf (ctrl, table[idx].name, "%d %d %d", tmp[0], tmp[1], tmp[2]); } else if (table[idx].special == -5) /* CHV-USAGE (aka PIN Usage Policy) */ { /* We return 2 hex bytes or nothing in case the discovery object * is not supported. */ relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); if (relptr) { s = find_tlv (value, valuelen, 0x7E, &n); if (s && n && (s = find_tlv (s, n, 0x5F2F, &n)) && n >=2 ) err = send_status_printf (ctrl, table[idx].name, "%02X %02X", s[0], s[1]); xfree (relptr); } } else if (table[idx].special == -6) { char const tmp[] = "PIV.9D"; /* Key Management. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else if (table[idx].special == -7) { char const tmp[] = "PIV.9C"; /* Digital Signature. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else { relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); if (relptr) { send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); xfree (relptr); } } return err; } /* Authenticate the card using the Card Application Administration * Key. (VALUE,VALUELEN) has that 24 byte key. */ static gpg_error_t auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) { gpg_error_t err; unsigned char tmpl[4+24]; size_t tmpllen; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; char witness[8]; size_t n; gcry_cipher_hd_t cipher = NULL; /* Prepare decryption. */ err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0); if (err) goto leave; err = gcry_cipher_setkey (cipher, value, valuelen); if (err) goto leave; /* Request a witness. */ tmpl[0] = 0x7c; tmpl[1] = 0x02; tmpl[2] = 0x80; tmpl[3] = 0; /* (Empty witness requests a witness.) */ tmpllen = 4; err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_BAD_AUTH); if (err) goto leave; if (!(outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x80, &n)) && n == 8)) { err = gpg_error (GPG_ERR_CARD); log_error ("piv: improper witness received\n"); goto leave; } err = gcry_cipher_decrypt (cipher, witness, 8, s, 8); if (err) goto leave; /* Return decrypted witness and send our challenge. */ tmpl[0] = 0x7c; tmpl[1] = 22; tmpl[2] = 0x80; tmpl[3] = 8; memcpy (tmpl+4, witness, 8); tmpl[12] = 0x81; tmpl[13] = 8; gcry_create_nonce (tmpl+14, 8); tmpl[22] = 0x80; tmpl[23] = 0; tmpllen = 24; xfree (outdata); err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_BAD_AUTH); if (err) goto leave; if (!(outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n)) && n == 8)) { err = gpg_error (GPG_ERR_CARD); log_error ("piv: improper challenge received\n"); goto leave; } /* (We reuse the witness buffer.) */ err = gcry_cipher_decrypt (cipher, witness, 8, s, 8); if (err) goto leave; if (memcmp (witness, tmpl+14, 8)) { err = gpg_error (GPG_ERR_BAD_AUTH); goto leave; } leave: xfree (outdata); gcry_cipher_close (cipher); return err; } /* Set a new admin key. */ static gpg_error_t set_adm_key (app_t app, const unsigned char *value, size_t valuelen) { gpg_error_t err; unsigned char apdu[8+24]; unsigned int sw; /* Check whether it is a weak key and that it is of proper length. */ { gcry_cipher_hd_t cipher; err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0); if (!err) { err = gcry_cipher_setkey (cipher, value, valuelen); gcry_cipher_close (cipher); } if (err) goto leave; } if (app->app_local->flags.yubikey) { /* This is a Yubikey. */ if (valuelen != 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } /* We use a proprietary Yubikey command. */ apdu[0] = 0; apdu[1] = 0xff; apdu[2] = 0xff; apdu[3] = 0xff; /* touch policy: 0xff=never, 0xfe = always. */ apdu[4] = 3 + 24; apdu[5] = PIV_ALGORITHM_3DES_ECB; apdu[6] = 0x9b; apdu[7] = 24; memcpy (apdu+8, value, 24); err = iso7816_apdu_direct (app_get_slot (app), apdu, 8+24, 0, &sw, NULL, NULL); wipememory (apdu+8, 24); if (err) log_error ("piv: setting admin key failed; sw=%04x\n", sw); /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); } else err = gpg_error (GPG_ERR_NOT_SUPPORTED); leave: return err; } /* Handle the SETATTR operation. All arguments are already basically * checked. */ static gpg_error_t do_setattr (app_t app, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t err; static struct { const char *name; unsigned short tag; unsigned short flush_tag; /* The tag which needs to be flushed or 0. */ int special; /* Special mode to use for thus NAME. */ } table[] = { /* Authenticate using the PIV Card Application Administration Key * (0x0B). Note that Yubico calls this key the "management key" * which we don't do because that term is too similar to "Cert * Management Key" (0x9D). */ { "AUTH-ADM-KEY", 0x0000, 0x0000, 1 }, { "SET-ADM-KEY", 0x0000, 0x0000, 2 } }; int idx; (void)pincb; (void)pincb_arg; for (idx=0; (idx < DIM (table) && ascii_strcasecmp (table[idx].name, name)); idx++) ; if (!(idx < DIM (table))) return gpg_error (GPG_ERR_INV_NAME); /* Flush the cache before writing it, so that the next get operation * will reread the data from the card and thus get synced in case of * errors (e.g. data truncated by the card). */ if (table[idx].tag) flush_cached_data (app, table[idx].flush_tag? table[idx].flush_tag /* */ : table[idx].tag); switch (table[idx].special) { case 1: err = auth_adm_key (app, value, valuelen); break; case 2: err = set_adm_key (app, value, valuelen); break; default: err = gpg_error (GPG_ERR_BUG); break; } return err; } /* Send the KEYPAIRINFO back. DOBJ describes the data object carrying * the key. This is used by the LEARN command. */ static gpg_error_t send_keypair_and_cert_info (app_t app, ctrl_t ctrl, data_object_t dobj, int only_keypair) { gpg_error_t err = 0; char *keygripstr = NULL; int got_cert; char idbuf[50]; const char *usage; err = get_keygrip_by_tag (app, dobj->tag, &keygripstr, &got_cert); if (err) goto leave; usage = dobj->usage? dobj->usage : ""; snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); send_status_info (ctrl, "KEYPAIRINFO", keygripstr, strlen (keygripstr), idbuf, strlen (idbuf), usage, strlen (usage), NULL, (size_t)0); if (!only_keypair && got_cert) { /* All certificates are of type 100 (Regular X.509 Cert). */ send_status_info (ctrl, "CERTINFO", "100", 3, idbuf, strlen (idbuf), NULL, (size_t)0); } leave: xfree (keygripstr); return err; } /* Handle the LEARN command. */ static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { int i; (void)flags; do_getattr (app, ctrl, "CHV-USAGE"); do_getattr (app, ctrl, "CHV-STATUS"); for (i=0; data_objects[i].tag; i++) if (data_objects[i].keypair) - send_keypair_and_cert_info (app, ctrl, data_objects + i, !!(flags & 1)); + send_keypair_and_cert_info (app, ctrl, data_objects + i, + !!(flags & APP_LEARN_FLAG_KEYPAIRINFO)); return 0; } /* Core of do_readcert which fetches the certificate based on the * given tag and returns it in a freshly allocated buffer stored at * R_CERT and the length of the certificate stored at R_CERTLEN. If * on success a non-zero value is stored at R_MECHANISM, the returned * data is not a certificate but a public key (in the format used by the * container '7f49'. */ static gpg_error_t readcert_by_tag (app_t app, unsigned int tag, unsigned char **r_cert, size_t *r_certlen, int *r_mechanism) { gpg_error_t err; unsigned char *buffer; size_t buflen; void *relptr; const unsigned char *s, *s2; size_t n, n2; *r_cert = NULL; *r_certlen = 0; *r_mechanism = 0; relptr = get_one_do (app, tag, &buffer, &buflen, NULL); if (!relptr || !buflen) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } s = find_tlv (buffer, buflen, 0x71, &n); if (!s) { /* No certificate; check whether a public key has been stored * using our own scheme. */ s = find_tlv (buffer, buflen, 0x7f49, &n); if (!s || !n) { log_error ("piv: No public key in 0x%X\n", tag); err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } s2 = find_tlv (buffer, buflen, 0x80, &n2); if (!s2 || n2 != 1 || !*s2) { log_error ("piv: No mechanism for public key in 0x%X\n", tag); err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } *r_mechanism = *s2; } else { if (n != 1) { log_error ("piv: invalid CertInfo in 0x%X\n", tag); err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (*s == 0x01) { log_error ("piv: gzip compression not yet supported (tag 0x%X)\n", tag); err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); goto leave; } if (*s) { log_error ("piv: invalid CertInfo 0x%02x in 0x%X\n", *s, tag); err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } /* Note: We don't check that the LRC octet has a length of zero * as required by the specs. */ /* Get the cert from the container. */ s = find_tlv (buffer, buflen, 0x70, &n); if (!s || !n) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } } /* The next is common for certificate and public key. */ if (!(*r_cert = xtrymalloc (n))) { err = gpg_error_from_syserror (); goto leave; } memcpy (*r_cert, s, n); *r_certlen = n; err = 0; leave: xfree (relptr); return err; } /* Get the keygrip in hex format of a key from the certificate stored * at TAG. Caller must free the string at R_KEYGRIPSTR. */ static gpg_error_t get_keygrip_by_tag (app_t app, unsigned int tag, char **r_keygripstr, int *r_got_cert) { gpg_error_t err; unsigned char *certbuf = NULL; size_t certbuflen; int mechanism; gcry_sexp_t s_pkey = NULL; ksba_cert_t cert = NULL; unsigned char grip[KEYGRIP_LEN]; *r_got_cert = 0; *r_keygripstr = xtrymalloc (2*KEYGRIP_LEN+1); if (!r_keygripstr) { err = gpg_error_from_syserror (); goto leave; } /* We need to get the public key from the certificate. */ err = readcert_by_tag (app, tag, &certbuf, &certbuflen, &mechanism); if (err) goto leave; if (mechanism) /* Compute keygrip from public key. */ { if (mechanism == PIV_ALGORITHM_RSA) err = genkey_parse_rsa (certbuf, certbuflen, &s_pkey); else if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) err = genkey_parse_ecc (certbuf, certbuflen, mechanism, &s_pkey); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); if (err) goto leave; if (!gcry_pk_get_keygrip (s_pkey, grip)) { log_error ("piv: error computing keygrip\n"); err = gpg_error (GPG_ERR_GENERAL); goto leave; } bin2hex (grip, sizeof grip, *r_keygripstr); } else /* Compute keygrip from certificate. */ { *r_got_cert = 0; err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, certbuf, certbuflen); if (err) goto leave; err = app_help_get_keygrip_string (cert, *r_keygripstr); } leave: gcry_sexp_release (s_pkey); ksba_cert_release (cert); xfree (certbuf); if (err) { xfree (*r_keygripstr); *r_keygripstr = NULL; } return err; } /* Locate the data object from the given KEYREF. The KEYREF may also * be the corresponding OID of the key object. Returns the data * object or NULL if not found. */ static data_object_t find_dobj_by_keyref (app_t app, const char *keyref) { int i; (void)app; if (!ascii_strncasecmp (keyref, "PIV.", 4)) { keyref += 4; for (i=0; data_objects[i].tag; i++) if (*data_objects[i].keyref && !ascii_strcasecmp (keyref, data_objects[i].keyref)) { return data_objects + i; } } else if (!strncmp (keyref, "2.16.840.1.101.3.7.", 19)) { keyref += 19; for (i=0; data_objects[i].tag; i++) if (*data_objects[i].keyref && !strcmp (keyref, data_objects[i].oidsuffix)) { return data_objects + i; } } return NULL; } /* Return the keyref from DOBJ as an integer. If it does not exist, * return -1. */ static int keyref_from_dobj (data_object_t dobj) { if (!dobj || !hexdigitp (dobj->keyref) || !hexdigitp (dobj->keyref+1)) return -1; return xtoi_2 (dobj->keyref); } /* Read a certificate from the card and returned in a freshly * allocated buffer stored at R_CERT and the length of the certificate * stored at R_CERTLEN. CERTID is either the OID of the cert's * container or of the form "PIV." */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; data_object_t dobj; int mechanism; *r_cert = NULL; *r_certlen = 0; /* Hack to read a Yubikey attestation certificate. */ if (app->app_local->flags.yubikey && strlen (certid) == 11 && !ascii_strncasecmp (certid, "PIV.ATST.", 9) && hexdigitp (certid+9) && hexdigitp (certid+10)) { unsigned char apdu[4]; unsigned char *result; size_t resultlen; apdu[0] = 0; apdu[1] = 0xf9; /* Yubikey: Get attestation cert. */ apdu[2] = xtoi_2 (certid+9); apdu[3] = 0; err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 1, NULL, &result, &resultlen); if (!err) { *r_cert = result; *r_certlen = resultlen; } return err; } dobj = find_dobj_by_keyref (app, certid); if (!dobj) return gpg_error (GPG_ERR_INV_ID); err = readcert_by_tag (app, dobj->tag, r_cert, r_certlen, &mechanism); if (!err && mechanism) { /* Well, no certificate but a public key - we don't want it. */ xfree (*r_cert); *r_cert = NULL; *r_certlen = 0; err = gpg_error (GPG_ERR_NOT_FOUND); } return err; } /* Return a public key in a freshly allocated buffer. This will only * work for a freshly generated key as long as no reset of the * application has been performed. This is because we return a cached * result from key generation. If no cached result is available, the * error GPG_ERR_UNSUPPORTED_OPERATION is returned so that the higher * layer can then get the key by reading the matching certificate. * On success a canonical encoded s-expression with the public key is * stored at (R_PK,R_PKLEN); the caller must release that buffer. On * error R_PK and R_PKLEN are not changed and an error code is * returned. */ static gpg_error_t do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, unsigned char **r_pk, size_t *r_pklen) { gpg_error_t err; data_object_t dobj; int keyref; unsigned char *cert = NULL; size_t certlen; int mechanism; gcry_sexp_t s_pkey = NULL; unsigned char *pk = NULL; size_t pklen; dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = readcert_by_tag (app, dobj->tag, &cert, &certlen, &mechanism); if (err) goto leave; if (!mechanism) { /* We got a certificate. Extract the pubkey from it. */ err = app_help_pubkey_from_cert (cert, certlen, &pk, &pklen); if (err) { log_error ("failed to parse the certificate: %s\n", gpg_strerror (err)); goto leave; } } else { /* Convert the public key into the expected s-expression. */ if (mechanism == PIV_ALGORITHM_RSA) err = genkey_parse_rsa (cert, certlen, &s_pkey); else if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) err = genkey_parse_ecc (cert, certlen, mechanism, &s_pkey); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); if (err) goto leave; err = make_canon_sexp (s_pkey, &pk, &pklen); if (err) goto leave; } if ((flags & APP_READKEY_FLAG_INFO)) { char keygripstr[KEYGRIP_LEN*2+1]; char idbuf[50]; const char *usage; err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); if (err) { log_error ("app_help_get_keygrip_string_pk failed: %s\n", gpg_strerror (err)); goto leave; } usage = dobj->usage? dobj->usage : ""; snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); send_status_info (ctrl, "KEYPAIRINFO", keygripstr, strlen (keygripstr), idbuf, strlen (idbuf), usage, strlen (usage), NULL, (size_t)0); } if (r_pk && r_pklen) { *r_pk = pk; pk = NULL; *r_pklen = pklen; } leave: gcry_sexp_release (s_pkey); xfree (pk); xfree (cert); return err; } /* Given a data object DOBJ return the corresponding PIV algorithm and * store it at R_ALGO. The algorithm is taken from the corresponding * certificate or from a cache. */ static gpg_error_t get_key_algorithm_by_dobj (app_t app, data_object_t dobj, int *r_mechanism) { gpg_error_t err; unsigned char *certbuf = NULL; size_t certbuflen; int mechanism; ksba_cert_t cert = NULL; ksba_sexp_t k_pkey = NULL; gcry_sexp_t s_pkey = NULL; gcry_sexp_t l1 = NULL; char *algoname = NULL; int algo; size_t n; const char *curve_name; *r_mechanism = 0; err = readcert_by_tag (app, dobj->tag, &certbuf, &certbuflen, &mechanism); if (err) goto leave; if (mechanism) { /* A public key was found. That makes it easy. */ switch (mechanism) { case PIV_ALGORITHM_RSA: case PIV_ALGORITHM_ECC_P256: case PIV_ALGORITHM_ECC_P384: *r_mechanism = mechanism; break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); log_error ("piv: unknown mechanism %d in public key at %s\n", mechanism, dobj->keyref); break; } goto leave; } err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, certbuf, certbuflen); if (err) { log_error ("piv: failed to parse the certificate %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } xfree (certbuf); certbuf = NULL; k_pkey = ksba_cert_get_public_key (cert); if (!k_pkey) { err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } n = gcry_sexp_canon_len (k_pkey, 0, NULL, NULL); err = gcry_sexp_new (&s_pkey, k_pkey, n, 0); if (err) goto leave; l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) { err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } { gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = l_tmp; } algoname = gcry_sexp_nth_string (l1, 0); if (!algoname) { err = gpg_error_from_syserror (); goto leave; } algo = gcry_pk_map_name (algoname); switch (algo) { case GCRY_PK_RSA: algo = PIV_ALGORITHM_RSA; break; case GCRY_PK_ECC: case GCRY_PK_ECDSA: case GCRY_PK_ECDH: curve_name = gcry_pk_get_curve (s_pkey, 0, NULL); if (curve_name && !strcmp (curve_name, "NIST P-256")) algo = PIV_ALGORITHM_ECC_P256; else if (curve_name && !strcmp (curve_name, "NIST P-384")) algo = PIV_ALGORITHM_ECC_P384; else { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("piv: certificate %s, curve '%s': %s\n", dobj->keyref, curve_name, gpg_strerror (err)); goto leave; } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); log_error ("piv: certificate %s, pubkey algo '%s': %s\n", dobj->keyref, algoname, gpg_strerror (err)); goto leave; } *r_mechanism = algo; leave: gcry_free (algoname); gcry_sexp_release (l1); gcry_sexp_release (s_pkey); ksba_free (k_pkey); xfree (certbuf); return err; } /* 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) { char *serial, *tmpbuf, *result; serial = get_dispserialno (app, 0); if (!serial) return NULL; /* 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, "Unknown", /* Fixme */ ""); xfree (serial); /* 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, NULL); xfree (result); result = tmpbuf; } return result; } /* Helper for verify_chv to ask for the PIN and to prepare/pad it. On * success the result is stored at (R_PIN,R_PINLEN). */ static gpg_error_t ask_and_prepare_chv (app_t app, int keyref, int ask_new, int remaining, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg, char **r_pin, unsigned int *r_pinlen) { gpg_error_t err; const char *label; char *prompt; char *pinvalue = NULL; unsigned int pinlen; char *pinbuffer = NULL; int minlen, maxlen, padding, onlydigits; *r_pin = NULL; *r_pinlen = 0; if (ask_new) remaining = -1; if (remaining != -1) log_debug ("piv: CHV %02X has %d attempts left\n", keyref, remaining); switch (keyref) { case 0x00: minlen = 6; maxlen = 8; padding = 1; onlydigits = 1; label = (ask_new? _("|N|Please enter the new Global-PIN") /**/ : _("||Please enter the Global-PIN of your PIV card")); break; case 0x80: minlen = 6; maxlen = 8; padding = 1; onlydigits = 1; label = (ask_new? _("|N|Please enter the new PIN") /**/ : _("||Please enter the PIN of your PIV card")); break; case 0x81: minlen = 8; maxlen = 8; padding = 0; onlydigits = 0; label = (ask_new? _("|N|Please enter the new Unblocking Key") /**/ :_("||Please enter the Unblocking Key of your PIV card")); break; case 0x96: case 0x97: case 0x98: case 0x9B: return gpg_error (GPG_ERR_NOT_IMPLEMENTED); default: return gpg_error (GPG_ERR_INV_ID); } /* Ask for the PIN. */ prompt = make_prompt (app, remaining, label); err = pincb (pincb_arg, prompt, &pinvalue); xfree (prompt); prompt = NULL; if (err) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (err)); return err; } pinlen = pinvalue? strlen (pinvalue) : 0; if (pinlen < minlen) { log_error (_("PIN for is too short; minimum length is %d\n"), minlen); if (pinvalue) wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } if (pinlen > maxlen) { log_error (_("PIN for is too long; maximum length is %d\n"), maxlen); wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } if (onlydigits && strspn (pinvalue, "0123456789") != pinlen) { log_error (_("PIN has invalid characters; only digits are allowed\n")); wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } pinbuffer = xtrymalloc_secure (maxlen); if (!pinbuffer) { err = gpg_error_from_syserror (); wipememory (pinvalue, pinlen); xfree (pinvalue); return err; } memcpy (pinbuffer, pinvalue, pinlen); wipememory (pinvalue, pinlen); xfree (pinvalue); if (padding) { memset (pinbuffer + pinlen, 0xff, maxlen - pinlen); pinlen = maxlen; } *r_pin = pinbuffer; *r_pinlen = pinlen; return 0; } /* Verify the card holder verification identified by KEYREF. This is * either the Appication PIN or the Global PIN. If FORCE is true a * verification is always done. */ static gpg_error_t verify_chv (app_t app, int keyref, int force, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg) { gpg_error_t err; unsigned char apdu[4]; unsigned int sw; int remaining; char *pin = NULL; unsigned int pinlen; /* First check whether a verify is at all needed. This is done with * P1 being 0 and no Lc and command data send. */ apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) { if (!force) /* No need to verification. */ return 0; /* All fine. */ remaining = -1; } else if ((sw & 0xfff0) == 0x63C0) remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */ else remaining = -1; err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg, &pin, &pinlen); if (err) return err; err = iso7816_verify (app_get_slot (app), keyref, pin, pinlen); wipememory (pin, pinlen); xfree (pin); if (err) log_error ("CHV %02X verification failed: %s\n", keyref, gpg_strerror (err)); return err; } /* Handle the PASSWD command. Valid values for PWIDSTR are * key references related to PINs; in particular: * PIV.00 - The Global PIN * PIV.80 - The Application PIN * PIV.81 - The PIN Unblocking key * The supported flags are: * APP_CHANGE_FLAG_CLEAR Clear the PIN verification state. * APP_CHANGE_FLAG_RESET Reset a PIN using the PUK. Only * allowed with PIV.80. */ static gpg_error_t do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; int keyref, targetkeyref; unsigned char apdu[4]; unsigned int sw; int remaining; char *oldpin = NULL; unsigned int oldpinlen; char *newpin = NULL; unsigned int newpinlen; (void)ctrl; /* Check for unknown flags. */ if ((flags & ~(APP_CHANGE_FLAG_CLEAR|APP_CHANGE_FLAG_RESET))) { err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); goto leave; } /* Parse the keyref. */ targetkeyref = keyref = parse_chv_keyref (pwidstr); if (keyref == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } /* First see whether the special --clear mode has been requested. */ if ((flags & APP_CHANGE_FLAG_CLEAR)) { apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0xff; apdu[3] = keyref; err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, NULL, NULL, NULL); goto leave; } /* Prepare reset mode. */ if ((flags & APP_CHANGE_FLAG_RESET)) { if (keyref == 0x81) { err = gpg_error (GPG_ERR_INV_ID); /* Can't reset the PUK. */ goto leave; } /* Set the keyref to the PUK and keep the TARGETKEYREF. */ keyref = 0x81; } /* Get the remaining tries count. This is done by using the check * for verified state feature. */ apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) remaining = -1; /* Already verified, thus full number of tries. */ else if ((sw & 0xfff0) == 0x63C0) remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */ else remaining = -1; /* Ask for the old pin or puk. */ err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg, &oldpin, &oldpinlen); if (err) return err; /* Verify the old pin so that we don't prompt for the new pin if the * old is wrong. This is not possible for the PUK, though. */ if (keyref != 0x81) { err = iso7816_verify (app_get_slot (app), keyref, oldpin, oldpinlen); if (err) { log_error ("CHV %02X verification failed: %s\n", keyref, gpg_strerror (err)); goto leave; } } /* Ask for the new pin. */ err = ask_and_prepare_chv (app, targetkeyref, 1, -1, pincb, pincb_arg, &newpin, &newpinlen); if (err) return err; if ((flags & APP_CHANGE_FLAG_RESET)) { char *buf = xtrymalloc_secure (oldpinlen + newpinlen); if (!buf) { err = gpg_error_from_syserror (); goto leave; } memcpy (buf, oldpin, oldpinlen); memcpy (buf+oldpinlen, newpin, newpinlen); err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), targetkeyref, buf, oldpinlen+newpinlen); xfree (buf); if (err) log_error ("resetting CHV %02X using CHV %02X failed: %s\n", targetkeyref, keyref, gpg_strerror (err)); } else { err = iso7816_change_reference_data (app_get_slot (app), keyref, oldpin, oldpinlen, newpin, newpinlen); if (err) log_error ("CHV %02X changing PIN failed: %s\n", keyref, gpg_strerror (err)); } leave: xfree (oldpin); xfree (newpin); return err; } /* Perform a simple verify operation for the PIN specified by PWIDSTR. * For valid values see do_change_chv. */ static gpg_error_t do_check_chv (app_t app, const char *pwidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int keyref; keyref = parse_chv_keyref (pwidstr); if (keyref == -1) return gpg_error (GPG_ERR_INV_ID); return verify_chv (app, keyref, 0, pincb, pincb_arg); } /* Compute a digital signature using the GENERAL AUTHENTICATE command * on INDATA which is expected to be the raw message digest. The * KEYIDSTR has the key reference or its OID (e.g. "PIV.9A"). The * result is stored at (R_OUTDATA,R_OUTDATALEN); on error (NULL,0) is * stored there and an error code returned. For ECDSA the result is * the simple concatenation of R and S without any DER encoding. R * and S are left extended with zeroes to make sure they have an equal * length. If HASHALGO is not zero, the function prepends the hash's * OID to the indata or checks that it is consistent. */ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata_arg, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen) { const unsigned char *indata = indata_arg; gpg_error_t err; data_object_t dobj; unsigned char oidbuf[64]; size_t oidbuflen; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; size_t n; int keyref, mechanism; unsigned char *indata_buffer = NULL; /* Malloced helper. */ unsigned char *apdudata = NULL; size_t apdudatalen; int force_verify; if (!keyidstr || !*keyidstr) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } dobj = find_dobj_by_keyref (app, keyidstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } /* According to table 4b of SP800-73-4 the signing key always * requires a verify. */ switch (keyref) { case 0x9c: force_verify = 1; break; default: force_verify = 0; break; } err = get_key_algorithm_by_dobj (app, dobj, &mechanism); if (err) goto leave; /* For ECC we need to remove the ASN.1 prefix from INDATA. For RSA * we need to add the padding and possible also the ASN.1 prefix. */ if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) { int need_algo, need_digestlen; if (mechanism == PIV_ALGORITHM_ECC_P256) { need_algo = GCRY_MD_SHA256; need_digestlen = 32; } else { need_algo = GCRY_MD_SHA384; need_digestlen = 48; } if (hashalgo && hashalgo != need_algo) { err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("piv: hash algo %d does not match mechanism %d\n", need_algo, mechanism); goto leave; } if (indatalen > need_digestlen) { oidbuflen = sizeof oidbuf; err = gcry_md_get_asnoid (need_algo, &oidbuf, &oidbuflen); if (err) { err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: no OID for hash algo %d\n", need_algo); goto leave; } if (indatalen != oidbuflen + need_digestlen || memcmp (indata, oidbuf, oidbuflen)) { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: bad input for signing with mechanism %d\n", mechanism); goto leave; } indata += oidbuflen; indatalen -= oidbuflen; } } else if (mechanism == PIV_ALGORITHM_RSA) { /* PIV requires 2048 bit RSA. */ unsigned int framelen = 2048 / 8; unsigned char *frame; int i; oidbuflen = sizeof oidbuf; if (!hashalgo) { /* We assume that indata already has the required * digestinfo; thus merely prepend the padding below. */ } else if ((err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen))) { log_debug ("piv: no OID for hash algo %d\n", hashalgo); goto leave; } else { unsigned int digestlen = gcry_md_get_algo_dlen (hashalgo); if (indatalen == digestlen) { /* Plain hash in INDATA; prepend the digestinfo. */ indata_buffer = xtrymalloc (oidbuflen + indatalen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer, oidbuf, oidbuflen); memcpy (indata_buffer+oidbuflen, indata, indatalen); indata = indata_buffer; indatalen = oidbuflen + indatalen; } else if (indatalen == oidbuflen + digestlen && !memcmp (indata, oidbuf, oidbuflen)) ; /* Correct prefix. */ else { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: bad input for signing with RSA and hash %d\n", hashalgo); goto leave; } } /* Now prepend the pkcs#v1.5 padding. We require at least 8 * byte of padding and 3 extra bytes for the prefix and the * delimiting nul. */ if (!indatalen || indatalen + 8 + 4 > framelen) { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: input does not fit into a %u bit PKCS#v1.5 frame\n", 8*framelen); goto leave; } frame = xtrymalloc (framelen); if (!frame) { err = gpg_error_from_syserror (); goto leave; } n = 0; frame[n++] = 0; frame[n++] = 1; /* Block type. */ i = framelen - indatalen - 3 ; memset (frame+n, 0xff, i); n += i; frame[n++] = 0; /* Delimiter. */ memcpy (frame+n, indata, indatalen); n += indatalen; log_assert (n == framelen); /* And now put it into the indata_buffer. */ xfree (indata_buffer); indata_buffer = frame; indata = indata_buffer; indatalen = framelen; } else { err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: unknown PIV mechanism %d while signing\n", mechanism); goto leave; } /* Now verify the Application PIN. */ err = verify_chv (app, 0x80, force_verify, pincb, pincb_arg); if (err) return err; /* Build the Dynamic Authentication Template. */ err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x7c, (size_t)0, NULL, /* Constructed. */ (int)0x82, (size_t)0, "", (int)0x81, (size_t)indatalen, indata, (int)0, (size_t)0, NULL); if (err) goto leave; /* Note: the -1 requests command chaining. */ err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); if (err) goto leave; /* Parse the response. */ if (outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n))) { if (mechanism == PIV_ALGORITHM_RSA) { memmove (outdata, outdata + (s - outdata), n); outdatalen = n; } else /* ECC */ { const unsigned char *rval, *sval; size_t rlen, rlenx, slen, slenx, resultlen; char *result; /* The result of an ECDSA signature is * SEQUENCE { r INTEGER, s INTEGER } * We re-pack that by concatenating R and S and making sure * that both have the same length. We simplify parsing by * using find_tlv and not a proper DER parser. */ s = find_tlv (s, n, 0x30, &n); if (!s) goto bad_der; rval = find_tlv (s, n, 0x02, &rlen); if (!rval) goto bad_der; log_assert (n >= (rval-s)+rlen); sval = find_tlv (rval+rlen, n-((rval-s)+rlen), 0x02, &slen); if (!rval) goto bad_der; rlenx = slenx = 0; if (rlen > slen) slenx = rlen - slen; else if (slen > rlen) rlenx = slen - rlen; resultlen = rlen + rlenx + slen + slenx; result = xtrycalloc (1, resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } memcpy (result + rlenx, rval, rlen); memcpy (result + rlenx + rlen + slenx, sval, slen); xfree (outdata); outdata = result; outdatalen = resultlen; } } else { bad_der: err = gpg_error (GPG_ERR_CARD); log_error ("piv: response does not contain a proper result\n"); goto leave; } leave: if (err) { xfree (outdata); *r_outdata = NULL; *r_outdatalen = 0; } else { *r_outdata = outdata; *r_outdatalen = outdatalen; } xfree (apdudata); xfree (indata_buffer); return err; } /* AUTH for PIV cards is actually the same as SIGN. The difference * between AUTH and SIGN is that AUTH expects that pkcs#1.5 padding * for RSA has already been done (digestInfo part w/o the padding) * whereas SIGN may accept a plain digest and does the padding if * needed. This is also the reason why SIGN takes a hashalgo. */ static gpg_error_t do_auth (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen) { return do_sign (app, keyidstr, 0, pincb, pincb_arg, indata, indatalen, r_outdata, r_outdatalen); } /* Decrypt the data in (INDATA,INDATALEN) and on success store the * mallocated result at (R_OUTDATA,R_OUTDATALEN). */ static gpg_error_t do_decipher (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata_arg, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen, unsigned int *r_info) { const unsigned char *indata = indata_arg; gpg_error_t err; data_object_t dobj; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; size_t n; int keyref, mechanism; unsigned int framelen; unsigned char *indata_buffer = NULL; /* Malloced helper. */ unsigned char *apdudata = NULL; size_t apdudatalen; if (!keyidstr || !*keyidstr) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } dobj = find_dobj_by_keyref (app, keyidstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } if (keyref == 0x9A || keyref == 0x9C || keyref == 0x9E) { /* Signing only reference. We only allow '9D' and the retired * cert key management DOs. */ err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = get_key_algorithm_by_dobj (app, dobj, &mechanism); if (err) goto leave; switch (mechanism) { case PIV_ALGORITHM_ECC_P256: framelen = 1+32+32; break; case PIV_ALGORITHM_ECC_P384: framelen = 1+48+48; break; case PIV_ALGORITHM_RSA: framelen = 2048 / 8; break; default: err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: unknown PIV mechanism %d while decrypting\n", mechanism); goto leave; } /* Check that the ciphertext has the right length; due to internal * convey mechanism using MPIs leading zero bytes might have been * lost. Adjust for this. Unfortunately the ciphertext might have * also been prefixed with a leading zero to make it a positive * number; that may be a too long frame and we need to adjust for * this too. Note that for ECC thoses fixes are not reqquired * because the first octet is always '04' to indicate an * uncompressed point. */ if (indatalen > framelen) { if (mechanism == PIV_ALGORITHM_RSA && indatalen == framelen + 1 && !*indata) { indata_buffer = xtrycalloc (1, framelen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer, indata+1, framelen); indata = indata_buffer; indatalen = framelen; } else { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: input of %zu octets too large for mechanism %d\n", indatalen, mechanism); goto leave; } } if (indatalen < framelen) { indata_buffer = xtrycalloc (1, framelen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer+(framelen-indatalen), indata, indatalen); indata = indata_buffer; indatalen = framelen; } /* Now verify the Application PIN. */ err = verify_chv (app, 0x80, 0, pincb, pincb_arg); if (err) return err; /* Build the Dynamic Authentication Template. */ err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x7c, (size_t)0, NULL, /* Constructed. */ (int)0x82, (size_t)0, "", mechanism == PIV_ALGORITHM_RSA? (int)0x81 : (int)0x85, (size_t)indatalen, indata, (int)0, (size_t)0, NULL); if (err) goto leave; /* Note: the -1 requests command chaining. */ err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); if (err) goto leave; /* Parse the response. */ if (outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n))) { memmove (outdata, outdata + (s - outdata), n); outdatalen = n; } else { err = gpg_error (GPG_ERR_CARD); log_error ("piv: response does not contain a proper result\n"); goto leave; } leave: if (err) { xfree (outdata); *r_outdata = NULL; *r_outdatalen = 0; } else { *r_outdata = outdata; *r_outdatalen = outdatalen; } *r_info = 0; xfree (apdudata); xfree (indata_buffer); return err; } /* Check whether a key for DOBJ already exists. We detect this by * reading the certificate described by DOBJ. If FORCE is TRUE a * diagnositic will be printed but no error returned if the key * already exists. The flag GENERATING is used to select a * diagnositic. */ static gpg_error_t does_key_exist (app_t app, data_object_t dobj, int generating, int force) { void *relptr; unsigned char *buffer; size_t buflen; int found; relptr = get_one_do (app, dobj->tag, &buffer, &buflen, NULL); found = (relptr && buflen); xfree (relptr); if (found && !force) { log_error (_("key already exists\n")); return gpg_error (GPG_ERR_EEXIST); } if (found) log_info (_("existing key will be replaced\n")); else if (generating) log_info (_("generating new key\n")); else log_info (_("writing new key\n")); return 0; } /* Helper for do_writekey; here the RSA part. BUF, BUFLEN, and DEPTH * are the current parser state of the S-expression with the key. */ static gpg_error_t writekey_rsa (app_t app, data_object_t dobj, int keyref, const unsigned char *buf, size_t buflen, int depth) { gpg_error_t err; const unsigned char *tok; size_t toklen; int last_depth1, last_depth2; const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; const unsigned char *rsa_p = NULL; const unsigned char *rsa_q = NULL; unsigned char *rsa_dpm1 = NULL; unsigned char *rsa_dqm1 = NULL; unsigned char *rsa_qinv = NULL; size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; size_t rsa_dpm1_len, rsa_dqm1_len, rsa_qinv_len; unsigned char *apdudata = NULL; size_t apdudatalen; unsigned char tmpl[1]; last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Check that we have all parameters. */ if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } /* Fixme: Shall we check whether n == pq ? */ if (opt.verbose) log_info ("RSA private key size is %u bytes\n", (unsigned int)rsa_n_len); /* Compute the dp, dq and u components. */ { gcry_mpi_t mpi_e, mpi_p, mpi_q; gcry_mpi_t mpi_dpm1 = gcry_mpi_snew (0); gcry_mpi_t mpi_dqm1 = gcry_mpi_snew (0); gcry_mpi_t mpi_qinv = gcry_mpi_snew (0); gcry_mpi_t mpi_tmp = gcry_mpi_snew (0); gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL); gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL); gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL); gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1); gcry_mpi_invm (mpi_dpm1, mpi_e, mpi_tmp); gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1); gcry_mpi_invm (mpi_dqm1, mpi_e, mpi_tmp); gcry_mpi_invm (mpi_qinv, mpi_q, mpi_p); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dpm1, &rsa_dpm1_len, mpi_dpm1); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dqm1, &rsa_dqm1_len, mpi_dqm1); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_qinv, &rsa_qinv_len, mpi_qinv); gcry_mpi_release (mpi_e); gcry_mpi_release (mpi_p); gcry_mpi_release (mpi_q); gcry_mpi_release (mpi_dpm1); gcry_mpi_release (mpi_dqm1); gcry_mpi_release (mpi_qinv); gcry_mpi_release (mpi_tmp); } err = concat_tlv_list (1, &apdudata, &apdudatalen, (int)0x01, (size_t)rsa_p_len, rsa_p, (int)0x02, (size_t)rsa_q_len, rsa_q, (int)0x03, (size_t)rsa_dpm1_len, rsa_dpm1, (int)0x04, (size_t)rsa_dqm1_len, rsa_dqm1, (int)0x05, (size_t)rsa_qinv_len, rsa_qinv, (int)0, (size_t)0, NULL); if (err) goto leave; err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ PIV_ALGORITHM_RSA, /* P1 */ keyref, /* P2 */ apdudatalen,/* Lc */ apdudata, /* data */ NULL, NULL, NULL); if (err) goto leave; /* Write the public key to the cert object. */ xfree (apdudata); err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x81, (size_t)rsa_n_len, rsa_n, (int)0x82, (size_t)rsa_e_len, rsa_e, (int)0, (size_t)0, NULL); if (err) goto leave; tmpl[0] = PIV_ALGORITHM_RSA; err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); leave: xfree (rsa_dpm1); xfree (rsa_dqm1); xfree (rsa_qinv); xfree (apdudata); return err; } /* Helper for do_writekey; here the ECC part. BUF, BUFLEN, and DEPTH * are the current parser state of the S-expression with the key. */ static gpg_error_t writekey_ecc (app_t app, data_object_t dobj, int keyref, const unsigned char *buf, size_t buflen, int depth) { gpg_error_t err; const unsigned char *tok; size_t toklen; int last_depth1, last_depth2; int mechanism = 0; const unsigned char *ecc_q = NULL; const unsigned char *ecc_d = NULL; size_t ecc_q_len, ecc_d_len; unsigned char *apdudata = NULL; size_t apdudatalen; unsigned char tmpl[1]; last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 5 && !memcmp (tok, "curve", 5)) { char *name; const char *xname; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; name = xtrymalloc (toklen+1); if (!name) { err = gpg_error_from_syserror (); goto leave; } memcpy (name, tok, toklen); name[toklen] = 0; /* Canonicalize the curve name. We use the openpgp * functions here because Libgcrypt has no generic curve * alias lookup feature and the PIV suppotred curves alre * also supported by OpenPGP. */ xname = openpgp_oid_to_curve (openpgp_curve_to_oid (name, NULL), 0); xfree (name); if (xname && !strcmp (xname, "nistp256")) mechanism = PIV_ALGORITHM_ECC_P256; else if (xname && !strcmp (xname, "nistp384")) mechanism = PIV_ALGORITHM_ECC_P384; else { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } } else if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break; case 'd': mpi = &ecc_d; mpi_len = &ecc_d_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Check that we have all parameters. */ if (!mechanism || !ecc_q || !ecc_d) { err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if (opt.verbose) log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len); err = concat_tlv_list (1, &apdudata, &apdudatalen, (int)0x06, (size_t)ecc_d_len, ecc_d, (int)0, (size_t)0, NULL); if (err) goto leave; err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ mechanism, /* P1 */ keyref, /* P2 */ apdudatalen,/* Lc */ apdudata, /* data */ NULL, NULL, NULL); if (err) goto leave; /* Write the public key to the cert object. */ xfree (apdudata); err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x86, (size_t)ecc_q_len, ecc_q, (int)0, (size_t)0, NULL); if (err) goto leave; tmpl[0] = mechanism; err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); leave: xfree (apdudata); return err; } /* Write a key to a slot. This command requires proprietary * extensions of the PIV specification and is thus only implemnted for * supported card types. The input is a canonical encoded * S-expression with the secret key in KEYDATA and its length (for * assertion) in KEYDATALEN. KEYREFSTR needs to be the usual 2 * hexdigit slot number prefixed with "PIV." PINCB and PINCB_ARG are * not used for PIV cards. * * Supported FLAGS are: * APP_WRITEKEY_FLAG_FORCE Overwrite existing key. */ static gpg_error_t do_writekey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen) { gpg_error_t err; int force = !!(flags & APP_WRITEKEY_FLAG_FORCE); data_object_t dobj; int keyref; const unsigned char *buf, *tok; size_t buflen, toklen; int depth; (void)ctrl; (void)pincb; (void)pincb_arg; if (!app->app_local->flags.yubikey) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } /* Check keyref and test whether a key already exists. */ dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = does_key_exist (app, dobj, 0, force); if (err) goto leave; /* Parse the S-expression with the key. */ buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen)) { if (!tok) ; else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen)) log_info ("protected-private-key passed to writekey\n"); else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) log_info ("shadowed-private-key passed to writekey\n"); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; /* First clear an existing key. We do this by writing an empty 7f49 * tag. This will return GPG_ERR_NO_PUBKEY on a later read. */ flush_cached_data (app, dobj->tag); err = put_data (app_get_slot (app), dobj->tag, (int)0x7f49, (size_t)0, "", (int)0, (size_t)0, NULL); if (err) { log_error ("piv: failed to clear the cert DO %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } /* Divert to the algo specific implementation. */ if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0) err = writekey_rsa (app, dobj, keyref, buf, buflen, depth); else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0) err = writekey_ecc (app, dobj, keyref, buf, buflen, depth); else err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); if (err) { /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); log_error (_("failed to store the key: %s\n"), gpg_strerror (err)); } leave: return err; } /* Parse an RSA response object, consisting of the content of tag * 0x7f49, into a gcrypt s-expression object and store that R_SEXP. * On error NULL is stored at R_SEXP. */ static gpg_error_t genkey_parse_rsa (const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp) { gpg_error_t err; const unsigned char *m, *e; unsigned char *mbuf = NULL; unsigned char *ebuf = NULL; size_t mlen, elen; *r_sexp = NULL; m = find_tlv (data, datalen, 0x0081, &mlen); if (!m) { log_error (_("response does not contain the RSA modulus\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } e = find_tlv (data, datalen, 0x0082, &elen); if (!e) { log_error (_("response does not contain the RSA public exponent\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } for (; mlen && !*m; mlen--, m++) /* Strip leading zeroes */ ; for (; elen && !*e; elen--, e++) /* Strip leading zeroes */ ; mbuf = xtrymalloc (mlen + 1); if (!mbuf) { err = gpg_error_from_syserror (); goto leave; } /* Prepend numbers with a 0 if needed. */ if (mlen && (*m & 0x80)) { *mbuf = 0; memcpy (mbuf+1, m, mlen); mlen++; } else memcpy (mbuf, m, mlen); ebuf = xtrymalloc (elen + 1); if (!ebuf) { err = gpg_error_from_syserror (); goto leave; } /* Prepend numbers with a 0 if needed. */ if (elen && (*e & 0x80)) { *ebuf = 0; memcpy (ebuf+1, e, elen); elen++; } else memcpy (ebuf, e, elen); err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))", (int)mlen, mbuf, (int)elen, ebuf); leave: xfree (mbuf); xfree (ebuf); return err; } /* Parse an ECC response object, consisting of the content of tag * 0x7f49, into a gcrypt s-expression object and store that R_SEXP. * On error NULL is stored at R_SEXP. MECHANISM specifies the * curve. */ static gpg_error_t genkey_parse_ecc (const unsigned char *data, size_t datalen, int mechanism, gcry_sexp_t *r_sexp) { gpg_error_t err; const unsigned char *ecc_q; size_t ecc_qlen; const char *curve; *r_sexp = NULL; ecc_q = find_tlv (data, datalen, 0x0086, &ecc_qlen); if (!ecc_q) { log_error (_("response does not contain the EC public key\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } if (mechanism == PIV_ALGORITHM_ECC_P256) curve = "nistp256"; else if (mechanism == PIV_ALGORITHM_ECC_P384) curve = "nistp384"; else { err = gpg_error (GPG_ERR_BUG); /* Call with wrong parameters. */ goto leave; } err = gcry_sexp_build (r_sexp, NULL, "(public-key(ecc(curve%s)(q%b)))", curve, (int)ecc_qlen, ecc_q); leave: return err; } /* Create a new keypair for KEYREF. If KEYTYPE is NULL a default * keytype is selected, else it may be one of the strings: * "rsa2048", "nistp256, or "nistp384". * * Supported FLAGS are: * APP_GENKEY_FLAG_FORCE Overwrite existing key. * * Note that CREATETIME is not used for PIV cards. * * Because there seems to be no way to read the public key we need to * retrieve it from a certificate. The GnuPG system however requires * the use of app_readkey to fetch the public key from the card to * create the certificate; to support this we temporary store the * generated public key in the local context for use by app_readkey. */ static gpg_error_t do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; data_object_t dobj; unsigned char *buffer = NULL; size_t buflen; int force = !!(flags & APP_GENKEY_FLAG_FORCE); int mechanism; time_t start_at; int keyref; unsigned char tmpl[5]; size_t tmpllen; const unsigned char *keydata; size_t keydatalen; (void)ctrl; (void)createtime; (void)pincb; (void)pincb_arg; if (!keytype) keytype = "rsa2048"; if (!strcmp (keytype, "rsa2048")) mechanism = PIV_ALGORITHM_RSA; else if (!strcmp (keytype, "nistp256")) mechanism = PIV_ALGORITHM_ECC_P256; else if (!strcmp (keytype, "nistp384")) mechanism = PIV_ALGORITHM_ECC_P384; else return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* We flush the cache to increase the I/O traffic before a key * generation. This _might_ help the card to gather more entropy * and is anyway a prerequisite for does_key_exist. */ flush_cached_data (app, 0); /* Check whether a key already exists. */ dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = does_key_exist (app, dobj, 1, force); if (err) goto leave; /* Create the key. */ log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); tmpl[0] = 0xac; tmpl[1] = 3; tmpl[2] = 0x80; tmpl[3] = 1; tmpl[4] = mechanism; tmpllen = 5; err = iso7816_generate_keypair (app_get_slot (app), 0, 0, keyref, tmpl, tmpllen, 0, &buffer, &buflen); if (err) { /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); log_error (_("generating key failed\n")); return err; } { int nsecs = (int)(time (NULL) - start_at); log_info (ngettext("key generation completed (%d second)\n", "key generation completed (%d seconds)\n", nsecs), nsecs); } /* Parse the result and store it as an s-expression in a dedicated * cache for later retrieval by app_readkey. */ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata || !keydatalen) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the public key data\n")); goto leave; } tmpl[0] = mechanism; flush_cached_data (app, dobj->tag); err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)keydatalen, keydata, (int)0, (size_t)0, NULL); if (err) { log_error ("piv: failed to write key to the cert DO %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } leave: xfree (buffer); return err; } /* Write the certificate (CERT,CERTLEN) to the card at CERTREFSTR. * CERTREFSTR is either the OID of the certificate's container data * object or of the form "PIV.". */ static gpg_error_t do_writecert (app_t app, ctrl_t ctrl, const char *certrefstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *cert, size_t certlen) { gpg_error_t err; data_object_t dobj; unsigned char *pk = NULL; unsigned char *orig_pk = NULL; size_t pklen, orig_pklen; (void)ctrl; (void)pincb; /* Not used; instead authentication is needed. */ (void)pincb_arg; if (!certlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); dobj = find_dobj_by_keyref (app, certrefstr); if (!dobj || !*dobj->keyref) return gpg_error (GPG_ERR_INV_ID); flush_cached_data (app, dobj->tag); /* Check that the public key parameters from the certificate match * an already stored key. Note that we do not allow writing a * certificate if no key has yet been created (GPG_ERR_NOT_FOUND) or * if there is a problem reading the public key from the certificate * GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only * way to detect whether a key exists is by trying to use that * key. */ err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_NO_SECKEY); /* Use a better error code. */ goto leave; } /* Compare pubkeys. */ err = app_help_pubkey_from_cert (cert, certlen, &pk, &pklen); if (err) goto leave; /* No public key in new certificate. */ if (orig_pklen != pklen || memcmp (orig_pk, pk, pklen)) { err = gpg_error (GPG_ERR_CONFLICT); goto leave; } err = put_data (app_get_slot (app), dobj->tag, (int)0x70, (size_t)certlen, cert,/* Certificate */ (int)0x71, (size_t)1, "", /* No compress */ (int)0xfe, (size_t)0, "", /* Empty LRC. */ (int)0, (size_t)0, NULL); /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); if (err) log_error ("piv: failed to write cert to %s: %s\n", dobj->keyref, gpg_strerror (err)); leave: xfree (pk); xfree (orig_pk); return err; } /* 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) { gpg_error_t err; char *keygripstr = NULL; char *serialno = NULL; char idbuf[20]; int data = 0; int i, tag, dummy_got_cert; /* First a quick check for valid parameters. */ switch (action) { case KEYGRIP_ACTION_LOOKUP: if (!want_keygripstr) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } break; case KEYGRIP_ACTION_SEND_DATA: data = 1; break; case KEYGRIP_ACTION_WRITE_STATUS: break; default: err = gpg_error (GPG_ERR_INV_ARG); goto leave; } /* Allocate the s/n string if needed. */ if (action != KEYGRIP_ACTION_LOOKUP) { serialno = app_get_serialno (app); if (!serialno) { err = gpg_error_from_syserror (); goto leave; } } for (i = 0; (tag = data_objects[i].tag); i++) { if (!data_objects[i].keypair) continue; xfree (keygripstr); if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert)) continue; if (action == KEYGRIP_ACTION_LOOKUP) { if (!strcmp (keygripstr, want_keygripstr)) { err = 0; /* Found */ goto leave; } } else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr)) { snprintf (idbuf, sizeof idbuf, "PIV.%s", data_objects[i].keyref); send_keyinfo (ctrl, data, keygripstr, serialno, idbuf); if (want_keygripstr) { err = 0; /* Found */ goto leave; } } } /* Return an error so that the dispatcher keeps on looping over the * other applications. For clarity we use a different error code * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR * is not NULL. */ if (!want_keygripstr) err = gpg_error (GPG_ERR_TRUE); else err = gpg_error (GPG_ERR_NOT_FOUND); leave: xfree (keygripstr); xfree (serialno); return err; } /* Reselect the application. This is used by cards which support * on-the-fly switching between applications. */ static gpg_error_t do_reselect (app_t app, ctrl_t ctrl) { gpg_error_t err; (void)ctrl; /* An extra check which should not be necessary because the caller * should have made sure that a re-select is only called for * approriate cards. */ if (!app->app_local->flags.yubikey) return gpg_error (GPG_ERR_NOT_SUPPORTED); err = iso7816_select_application (app_get_slot (app), piv_aid, sizeof piv_aid, 0x0001); return err; } /* Select the PIV application on the card in SLOT. This function must * be used before any other PIV application functions. */ gpg_error_t app_select_piv (app_t app) { int slot = app_get_slot (app); gpg_error_t err; unsigned char *apt = NULL; size_t aptlen; const unsigned char *s; size_t n; /* Note that we select using the AID without the 2 octet version * number. This allows for better reporting of future specs. We * need to use the use-zero-for-P2-flag. */ err = iso7816_select_application_ext (slot, piv_aid, sizeof piv_aid, 0x0001, &apt, &aptlen); if (err) goto leave; app->apptype = APPTYPE_PIV; app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; app->app_local = NULL; /* Check the Application Property Template. */ if (opt.verbose) { /* We use a separate log_info to avoid the "DBG:" prefix. */ log_info ("piv: APT="); log_printhex (apt, aptlen, ""); } s = find_tlv (apt, aptlen, 0x4F, &n); if (!s || n != 6 || memcmp (s, piv_aid+5, 4)) { /* The PIX does not match. */ log_error ("piv: missing or invalid DO 0x4F in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } if (s[4] != 1 || s[5] != 0) { log_error ("piv: unknown PIV version %u.%u\n", s[4], s[5]); err = gpg_error (GPG_ERR_CARD); goto leave; } app->appversion = ((s[4] << 8) | s[5]); s = find_tlv (apt, aptlen, 0x79, &n); if (!s || n < 7) { log_error ("piv: missing or invalid DO 0x79 in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } s = find_tlv (s, n, 0x4F, &n); if (!s || n != 5 || memcmp (s, piv_aid, 5)) { /* The RID does not match. */ log_error ("piv: missing or invalid DO 0x79.4F in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { err = gpg_error_from_syserror (); goto leave; } if (app->card->cardtype == CARDTYPE_YUBIKEY) app->app_local->flags.yubikey = 1; /* FIXME: Parse the optional and conditional DOs in the APT. */ if (opt.verbose) dump_all_do (slot); app->fnc.deinit = do_deinit; app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.writecert = do_writecert; app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_chv; app->fnc.check_pin = do_check_chv; app->fnc.with_keygrip = do_with_keygrip; leave: xfree (apt); if (err) do_deinit (app); return err; } diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c index 16d25b581..2f1ab2074 100644 --- a/scd/app-sc-hsm.c +++ b/scd/app-sc-hsm.c @@ -1,2090 +1,2090 @@ /* app-sc-hsm.c - The SmartCard-HSM card application (www.smartcard-hsm.com). * Copyright (C) 2005 Free Software Foundation, Inc. * Copyright (C) 2014 Andreas Schwier * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* Code in this driver is based on app-p15.c with modifications. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "iso7816.h" #include "../common/tlv.h" #include "apdu.h" /* The AID of the SmartCard-HSM applet. */ static char const sc_hsm_aid[] = { 0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xC3, 0x1F, 0x02, 0x01 }; /* Special file identifier for SmartCard-HSM */ typedef enum { SC_HSM_PRKD_PREFIX = 0xC4, SC_HSM_CD_PREFIX = 0xC8, SC_HSM_DCOD_PREFIX = 0xC9, SC_HSM_CA_PREFIX = 0xCA, SC_HSM_KEY_PREFIX = 0xCC, SC_HSM_EE_PREFIX = 0xCE } fid_prefix_type_t; /* The key types supported by the SmartCard-HSM */ typedef enum { KEY_TYPE_RSA, KEY_TYPE_ECC } key_type_t; /* A bit array with for the key usage flags from the commonKeyAttributes. */ struct keyusage_flags_s { unsigned int encrypt: 1; unsigned int decrypt: 1; unsigned int sign: 1; unsigned int sign_recover: 1; unsigned int wrap: 1; unsigned int unwrap: 1; unsigned int verify: 1; unsigned int verify_recover: 1; unsigned int derive: 1; unsigned int non_repudiation: 1; }; typedef struct keyusage_flags_s keyusage_flags_t; /* This is an object to store information about a Certificate Directory File (CDF) in a format suitable for further processing by us. To keep memory management, simple we use a linked list of items; i.e. one such object represents one certificate and the list the entire CDF. */ struct cdf_object_s { /* Link to next item when used in a linked list. */ struct cdf_object_s *next; /* Length and allocated buffer with the Id of this object. */ size_t objidlen; unsigned char *objid; /* To avoid reading a certificate more than once, we cache it in an allocated memory IMAGE of IMAGELEN. */ size_t imagelen; unsigned char *image; /* EF containing certificate */ unsigned short fid; }; typedef struct cdf_object_s *cdf_object_t; /* This is an object to store information about a Private Key Directory File (PrKDF) in a format suitable for further processing by us. To keep memory management, simple we use a linked list of items; i.e. one such object represents one certificate and the list the entire PrKDF. */ struct prkdf_object_s { /* Link to next item when used in a linked list. */ struct prkdf_object_s *next; /* Key type */ key_type_t keytype; /* Key size in bits or 0 if unknown */ size_t keysize; /* Length and allocated buffer with the Id of this object. */ size_t objidlen; unsigned char *objid; /* The key's usage flags. */ keyusage_flags_t usageflags; /* The keyReference */ unsigned char key_reference; }; typedef struct prkdf_object_s *prkdf_object_t; /* Context local to this application. */ struct app_local_s { /* Information on all certificates. */ cdf_object_t certificate_info; /* Information on all trusted certificates. */ cdf_object_t trusted_certificate_info; /* Information on all private keys. */ prkdf_object_t private_key_info; }; /*** Local prototypes. ***/ static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, unsigned char **r_cert, size_t *r_certlen); /* Release the CDF object A */ static void release_cdflist (cdf_object_t a) { while (a) { cdf_object_t tmp = a->next; xfree (a->image); xfree (a->objid); xfree (a); a = tmp; } } /* Release the PrKDF object A. */ static void release_prkdflist (prkdf_object_t a) { while (a) { prkdf_object_t tmp = a->next; xfree (a->objid); xfree (a); a = tmp; } } /* Release all local resources. */ static void do_deinit (app_t app) { if (app && app->app_local) { release_cdflist (app->app_local->certificate_info); release_cdflist (app->app_local->trusted_certificate_info); release_prkdflist (app->app_local->private_key_info); xfree (app->app_local); app->app_local = NULL; } } /* Get the list of EFs from the SmartCard-HSM. * On success a dynamically buffer containing the EF list is returned. * The caller is responsible for freeing the buffer. */ static gpg_error_t list_ef (int slot, unsigned char **result, size_t *resultlen) { int sw; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; sw = apdu_send_le (slot, 1, 0x80, 0x58, 0x00, 0x00, -1, NULL, 65536, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; } return iso7816_map_sw (sw); } /* Do a select and a read for the file with EFID. EFID_DESC is a description of the EF to be used with error messages. On success BUFFER and BUFLEN contain the entire content of the EF. The caller must free BUFFER only on success. */ static gpg_error_t select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, unsigned char **buffer, size_t *buflen, int maxread) { gpg_error_t err; unsigned char cdata[4]; int sw; cdata[0] = 0x54; /* Create ISO 7861-4 odd ins READ BINARY */ cdata[1] = 0x02; cdata[2] = 0x00; cdata[3] = 0x00; sw = apdu_send_le(slot, 1, 0x00, 0xB1, efid >> 8, efid & 0xFF, 4, cdata, maxread, buffer, buflen); if (sw == SW_EOF_REACHED) sw = SW_SUCCESS; err = iso7816_map_sw (sw); if (err) { log_error ("error reading %s (0x%04X): %s\n", efid_desc, efid, gpg_strerror (err)); return err; } return 0; } /* Parse a cert Id string (or a key Id string) and return the binary object Id string in a newly allocated buffer stored at R_OBJID and R_OBJIDLEN. On Error NULL will be stored there and an error code returned. On success caller needs to free the buffer at R_OBJID. */ static gpg_error_t parse_certid (const char *certid, unsigned char **r_objid, size_t *r_objidlen) { const char *s; size_t objidlen; unsigned char *objid; int i; *r_objid = NULL; *r_objidlen = 0; if (strncmp (certid, "HSM.", 4)) return gpg_error (GPG_ERR_INV_ID); certid += 4; for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++) ; if (*s || !objidlen || (objidlen%2)) return gpg_error (GPG_ERR_INV_ID); objidlen /= 2; objid = xtrymalloc (objidlen); if (!objid) return gpg_error_from_syserror (); for (s=certid, i=0; i < objidlen; i++, s+=2) objid[i] = xtoi_2 (s); *r_objid = objid; *r_objidlen = objidlen; return 0; } /* Find a certificate object by the certificate ID CERTID and store a pointer to it at R_CDF. */ static gpg_error_t cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf) { gpg_error_t err; size_t objidlen; unsigned char *objid; cdf_object_t cdf; err = parse_certid (certid, &objid, &objidlen); if (err) return err; for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) break; if (!cdf) for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen)) break; xfree (objid); if (!cdf) return gpg_error (GPG_ERR_NOT_FOUND); *r_cdf = cdf; return 0; } /* Find a private key object by the key Id string KEYIDSTR and store a pointer to it at R_PRKDF. */ static gpg_error_t prkdf_object_from_keyidstr (app_t app, const char *keyidstr, prkdf_object_t *r_prkdf) { gpg_error_t err; size_t objidlen; unsigned char *objid; prkdf_object_t prkdf; err = parse_certid (keyidstr, &objid, &objidlen); if (err) return err; for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen)) break; xfree (objid); if (!prkdf) return gpg_error (GPG_ERR_NOT_FOUND); *r_prkdf = prkdf; return 0; } /* Parse the BIT STRING with the keyUsageFlags from the CommonKeyAttributes. */ static gpg_error_t parse_keyusage_flags (const unsigned char *der, size_t derlen, keyusage_flags_t *usageflags) { unsigned int bits, mask; int i, unused, full; memset (usageflags, 0, sizeof *usageflags); if (!derlen) return gpg_error (GPG_ERR_INV_OBJ); unused = *der++; derlen--; if ((!derlen && unused) || unused/8 > derlen) return gpg_error (GPG_ERR_ENCODING_PROBLEM); full = derlen - (unused+7)/8; unused %= 8; mask = 0; for (i=1; unused; i <<= 1, unused--) mask |= i; /* First octet */ if (derlen) { bits = *der++; derlen--; if (full) full--; else { bits &= ~mask; mask = 0; } } else bits = 0; if ((bits & 0x80)) usageflags->encrypt = 1; if ((bits & 0x40)) usageflags->decrypt = 1; if ((bits & 0x20)) usageflags->sign = 1; if ((bits & 0x10)) usageflags->sign_recover = 1; if ((bits & 0x08)) usageflags->wrap = 1; if ((bits & 0x04)) usageflags->unwrap = 1; if ((bits & 0x02)) usageflags->verify = 1; if ((bits & 0x01)) usageflags->verify_recover = 1; /* Second octet. */ if (derlen) { bits = *der++; derlen--; if (full) full--; else { bits &= ~mask; } } else bits = 0; if ((bits & 0x80)) usageflags->derive = 1; if ((bits & 0x40)) usageflags->non_repudiation = 1; return 0; } /* Read and parse a Private Key Directory File containing a single key description in PKCS#15 format. For each private key a matching certificate description is created, if the certificate EF exists and contains a X.509 certificate. Example data: 0000 30 2A 30 13 0C 11 4A 6F 65 20 44 6F 65 20 28 52 0*0...Joe Doe (R 0010 53 41 32 30 34 38 29 30 07 04 01 01 03 02 02 74 SA2048)0.......t 0020 A1 0A 30 08 30 02 04 00 02 02 08 00 ..0.0....... Decoded example: SEQUENCE SIZE( 42 ) SEQUENCE SIZE( 19 ) UTF8-STRING SIZE( 17 ) -- label 0000 4A 6F 65 20 44 6F 65 20 28 52 53 41 32 30 34 38 Joe Doe (RSA2048 0010 29 ) SEQUENCE SIZE( 7 ) OCTET-STRING SIZE( 1 ) -- id 0000 01 BIT-STRING SIZE( 2 ) -- key usage 0000 02 74 A1 [ CONTEXT 1 ] IMPLICIT SEQUENCE SIZE( 10 ) SEQUENCE SIZE( 8 ) SEQUENCE SIZE( 2 ) OCTET-STRING SIZE( 0 ) -- empty path, req object in PKCS#15 INTEGER SIZE( 2 ) -- modulus size in bits 0000 08 00 */ static gpg_error_t read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult, cdf_object_t *cdresult) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; int i; const unsigned char *pp; size_t nn; int where; const char *errstr = NULL; prkdf_object_t prkdf = NULL; cdf_object_t cdf = NULL; unsigned long ul; const unsigned char *objid; size_t objidlen; keyusage_flags_t usageflags; const char *s; key_type_t keytype; size_t keysize; if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */ err = select_and_read_binary (app_get_slot (app), fid, "PrKDF", &buffer, &buflen, 255); if (err) return err; p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || (tag != TAG_SEQUENCE && tag != 0x00))) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing PrKDF record: %s\n", gpg_strerror (err)); goto leave; } keytype = tag == 0x00 ? KEY_TYPE_ECC : KEY_TYPE_RSA; pp = p; nn = objlen; p += objlen; n -= objlen; /* Parse the commonObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Search the optional AuthId. We need to skip the optional Label (UTF8STRING) and the optional CommonObjectFlags (BITSTRING). */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; if (tag == TAG_UTF8_STRING) { ppp += objlen; /* Skip the Label. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_BIT_STRING) { ppp += objlen; /* Skip the CommonObjectFlags. */ nnn -= objlen; where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL)) err = gpg_error (GPG_ERR_INV_OBJ); if (gpg_err_code (err) == GPG_ERR_EOF) goto no_authid; if (err) goto parse_error; } if (tag == TAG_OCTET_STRING && objlen) { /* AuthId ignored */ } no_authid: ; } /* Parse the commonKeyAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Get the Id. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; objid = ppp; objidlen = objlen; ppp += objlen; nnn -= objlen; /* Get the KeyUsageFlags. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; err = parse_keyusage_flags (ppp, objlen, &usageflags); if (err) goto parse_error; ppp += objlen; nnn -= objlen; /* Find the keyReference */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN) { /* Skip the native element. */ ppp += objlen; nnn -= objlen; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING) { /* Skip the accessFlags. */ ppp += objlen; nnn -= objlen; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (gpg_err_code (err) == GPG_ERR_EOF) goto leave_cki; if (!err && objlen > nnn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; } if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER) { /* Yep, this is the keyReference. Note: UL is currently not used. */ for (ul=0; objlen; objlen--) { ul <<= 8; ul |= (*ppp++) & 0xff; nnn--; } } leave_cki: ; } /* Skip subClassAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class == CLASS_CONTEXT && tag == 0) { pp += objlen; nn -= objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); } /* Parse the keyAttributes. */ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; /* Check that the reference is a Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) { errstr = "unsupported reference type"; goto parse_error; } pp += objlen; nn -= objlen; /* Parse the key size object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; keysize = 0; if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER && objlen == 2) { keysize = *pp++ << 8; keysize += *pp++; } /* Create a new PrKDF list item. */ prkdf = xtrycalloc (1, sizeof *prkdf); if (!prkdf) { err = gpg_error_from_syserror (); goto leave; } prkdf->keytype = keytype; prkdf->keysize = keysize; prkdf->objidlen = objidlen; prkdf->objid = xtrymalloc (objidlen); if (!prkdf->objid) { err = gpg_error_from_syserror (); xfree (prkdf); prkdf = NULL; goto leave; } memcpy (prkdf->objid, objid, objidlen); prkdf->usageflags = usageflags; prkdf->key_reference = fid & 0xFF; log_debug ("PrKDF %04hX: id=", fid); for (i=0; i < prkdf->objidlen; i++) log_printf ("%02X", prkdf->objid[i]); log_printf (" keyref=0x%02X", prkdf->key_reference); log_printf (" keysize=%zu", prkdf->keysize); log_printf (" usage="); s = ""; if (prkdf->usageflags.encrypt) { log_printf ("%sencrypt", s); s = ","; } if (prkdf->usageflags.decrypt) { log_printf ("%sdecrypt", s); s = ","; } if (prkdf->usageflags.sign) { log_printf ("%ssign", s); s = ","; } if (prkdf->usageflags.sign_recover) { log_printf ("%ssign_recover", s); s = ","; } if (prkdf->usageflags.wrap ) { log_printf ("%swrap", s); s = ","; } if (prkdf->usageflags.unwrap ) { log_printf ("%sunwrap", s); s = ","; } if (prkdf->usageflags.verify ) { log_printf ("%sverify", s); s = ","; } if (prkdf->usageflags.verify_recover) { log_printf ("%sverify_recover", s); s = ","; } if (prkdf->usageflags.derive ) { log_printf ("%sderive", s); s = ","; } if (prkdf->usageflags.non_repudiation) { log_printf ("%snon_repudiation", s); } log_printf ("\n"); xfree (buffer); buffer = NULL; buflen = 0; 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) { /* Create a matching CDF list item. */ cdf = xtrycalloc (1, sizeof *cdf); if (!cdf) { err = gpg_error_from_syserror (); goto leave; } cdf->objidlen = prkdf->objidlen; cdf->objid = xtrymalloc (cdf->objidlen); if (!cdf->objid) { err = gpg_error_from_syserror (); xfree (cdf); cdf = NULL; goto leave; } memcpy (cdf->objid, prkdf->objid, objidlen); cdf->fid = (SC_HSM_EE_PREFIX << 8) | (fid & 0xFF); log_debug ("CDF %04hX: id=", fid); for (i=0; i < cdf->objidlen; i++) log_printf ("%02X", cdf->objid[i]); log_printf (" fid=%04X\n", cdf->fid); } goto leave; /* Ready. */ parse_error: log_error ("error parsing PrKDF record (%d): %s - skipped\n", where, errstr? errstr : gpg_strerror (err)); err = 0; leave: xfree (buffer); if (err) { if (prkdf) { if (prkdf->objid) xfree (prkdf->objid); xfree (prkdf); } if (cdf) { if (cdf->objid) xfree (cdf->objid); xfree (cdf); } } else { if (prkdf) prkdf->next = *prkdresult; *prkdresult = prkdf; if (cdf) { cdf->next = *cdresult; *cdresult = cdf; } } return err; } /* Read and parse the Certificate Description File identified by FID. On success a the CDF list gets stored at RESULT and the caller is then responsible of releasing the object. Example data: 0000 30 35 30 11 0C 0B 43 65 72 74 69 66 69 63 61 74 050...Certificat 0010 65 03 02 06 40 30 16 04 14 C2 01 7C 2F BA A4 4A e...@0.....|/..J 0020 4A BB B8 49 11 DB 4A CA AA 7E 6A 2D 1B A1 08 30 J..I..J..~j-...0 0030 06 30 04 04 02 CA 00 .0..... Decoded example: SEQUENCE SIZE( 53 ) SEQUENCE SIZE( 17 ) UTF8-STRING SIZE( 11 ) -- label 0000 43 65 72 74 69 66 69 63 61 74 65 Certificate BIT-STRING SIZE( 2 ) -- common object attributes 0000 06 40 SEQUENCE SIZE( 22 ) OCTET-STRING SIZE( 20 ) -- id 0000 C2 01 7C 2F BA A4 4A 4A BB B8 49 11 DB 4A CA AA 0010 7E 6A 2D 1B A1 [ CONTEXT 1 ] IMPLICIT SEQUENCE SIZE( 8 ) SEQUENCE SIZE( 6 ) SEQUENCE SIZE( 4 ) OCTET-STRING SIZE( 2 ) -- path 0000 CA 00 .. */ static gpg_error_t read_ef_cd (app_t app, unsigned short fid, cdf_object_t *result) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p; size_t n, objlen, hdrlen; int class, tag, constructed, ndef; int i; const unsigned char *pp; size_t nn; int where; const char *errstr = NULL; cdf_object_t cdf = NULL; const unsigned char *objid; size_t objidlen; if (!fid) return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */ err = select_and_read_binary (app_get_slot (app), fid, "CDF", &buffer, &buflen, 255); if (err) return err; p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing CDF record: %s\n", gpg_strerror (err)); goto leave; } pp = p; nn = objlen; p += objlen; n -= objlen; /* Skip the commonObjectAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; pp += objlen; nn -= objlen; /* Parse the commonCertificateAttributes. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; { const unsigned char *ppp = pp; size_t nnn = objlen; pp += objlen; nn -= objlen; /* Get the Id. */ where = __LINE__; err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nnn || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; objid = ppp; objidlen = objlen; } /* Parse the certAttribute. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; nn = objlen; /* Check that the reference is a Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto parse_error; if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE) { err = gpg_error (GPG_ERR_INV_OBJ); goto parse_error; } nn = objlen; /* Parse the Path object. */ where = __LINE__; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && objlen > nn) err = gpg_error (GPG_ERR_INV_OBJ); 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). */ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING || (objlen & 1) ) { errstr = "invalid path reference"; goto parse_error; } /* Create a new CDF list item. */ cdf = xtrycalloc (1, sizeof *cdf); if (!cdf) { err = gpg_error_from_syserror (); goto leave; } cdf->objidlen = objidlen; cdf->objid = xtrymalloc (objidlen); if (!cdf->objid) { err = gpg_error_from_syserror (); xfree (cdf); cdf = NULL; goto leave; } memcpy (cdf->objid, objid, objidlen); cdf->fid = (SC_HSM_CA_PREFIX << 8) | (fid & 0xFF); log_debug ("CDF %04hX: id=", fid); for (i=0; i < cdf->objidlen; i++) log_printf ("%02X", cdf->objid[i]); goto leave; parse_error: log_error ("error parsing CDF record (%d): %s - skipped\n", where, errstr? errstr : gpg_strerror (err)); err = 0; leave: xfree (buffer); if (err) { if (cdf) { if (cdf->objid) xfree (cdf->objid); xfree (cdf); } } else { if (cdf) cdf->next = *result; *result = cdf; } return err; } /* Read the device certificate and extract the serial number. EF.C_DevAut (2F02) contains two CVCs, the first is the device certificate, the second is the issuer certificate. Example data: 0000 7F 21 81 E2 7F 4E 81 9B 5F 29 01 00 42 0B 55 54 .!...N.._)..B.UT 0010 43 43 30 32 30 30 30 30 32 7F 49 4F 06 0A 04 00 CC0200002.IO.... 0020 7F 00 07 02 02 02 02 03 86 41 04 6D FF D6 85 57 .........A.m...W 0030 40 FB 10 5D 94 71 8A 94 D2 5E 50 33 E7 1E C0 6C @..].q...^P3...l 0040 63 D5 C8 FC BA F3 02 1D 70 23 F6 47 E8 35 48 EF c.......p#.G.5H. 0050 B5 94 72 3C 6F BE C0 EB 9A C7 FB 06 59 26 CF 65 ..r...<. 0150 6B AC 06 EA 5F 20 0B 55 54 43 43 30 32 30 30 30 k..._ .UTCC02000 0160 30 32 7F 4C 10 06 0B 2B 06 01 04 01 81 C3 1F 03 02.L...+........ 0170 01 01 53 01 80 5F 25 06 01 03 00 03 02 08 5F 24 ..S.._%......._$ 0180 06 02 01 00 03 02 07 5F 37 40 93 C1 42 8B B3 8E ......._7@..B... 0190 42 61 6F 2C 19 E6 98 41 BD AA 60 BD E0 DD 4E F0 Bao,...A..`...N. 01A0 15 D5 4F 71 B7 BB C3 3A F2 AD 27 5E DD EE 6D 12 ..Oq...:..'^..m. 01B0 76 E6 2B A0 4C 01 CA C1 26 0C 45 6D C6 CB EC 92 v.+.L...&.Em.... 01C0 BF 38 18 AD 8F B2 29 40 A9 51 .8....)@.Q The certificate format is defined in BSI TR-03110: 7F21 [ APPLICATION 33 ] IMPLICIT SEQUENCE SIZE( 226 ) 7F4E [ APPLICATION 78 ] IMPLICIT SEQUENCE SIZE( 155 ) 5F29 [ APPLICATION 41 ] SIZE( 1 ) -- profile id 0000 00 42 [ APPLICATION 2 ] SIZE( 11 ) -- CAR 0000 55 54 43 43 30 32 30 30 30 30 32 UTCC0200002 7F49 [ APPLICATION 73 ] IMPLICIT SEQUENCE SIZE( 79 ) -- public key OBJECT IDENTIFIER = { id-TA-ECDSA-SHA-256 } 86 [ CONTEXT 6 ] SIZE( 65 ) 0000 04 6D FF D6 85 57 40 FB 10 5D 94 71 8A 94 D2 5E 0010 50 33 E7 1E C0 6C 63 D5 C8 FC BA F3 02 1D 70 23 0020 F6 47 E8 35 48 EF B5 94 72 3C 6F BE C0 EB 9A C7 0030 FB 06 59 26 CF 65 EF A1 72 E0 98 F3 F0 44 1B B7 0040 71 5F20 [ APPLICATION 32 ] SIZE( 16 ) -- CHR 0000 55 54 43 43 30 32 30 30 30 31 33 30 30 30 30 30 UTCC020001300000 7F4C [ APPLICATION 76 ] IMPLICIT SEQUENCE SIZE( 16 ) -- CHAT OBJECT IDENTIFIER = { 1 3 6 1 4 1 24991 3 1 1 } 53 [ APPLICATION 19 ] SIZE( 1 ) 0000 00 5F25 [ APPLICATION 37 ] SIZE( 6 ) -- Valid from 0000 01 04 00 07 01 01 5F24 [ APPLICATION 36 ] SIZE( 6 ) -- Valid to 0000 02 01 00 03 02 07 5F37 [ APPLICATION 55 ] SIZE( 64 ) -- Signature 0000 7F 73 04 3B 06 63 79 41 BE 1A 9F FC F6 77 67 2B 0010 8A 41 D1 11 F6 9B 54 44 AD 19 FB B8 0C C6 2F 34 0020 71 8E 4F F6 92 59 34 61 D9 4F 4A 86 36 A8 D8 9A 0030 C6 3C 17 7E 71 CE A8 26 D0 C5 25 61 78 9D 01 F8 The serial number is contained in tag 5F20, while the last 5 digits are truncated. */ static gpg_error_t read_serialno(app_t app) { gpg_error_t err; unsigned char *buffer = NULL; size_t buflen; const unsigned char *p,*chr; size_t n, objlen, hdrlen, chrlen; int class, tag, constructed, ndef; err = select_and_read_binary (app_get_slot (app), 0x2F02, "EF.C_DevAut", &buffer, &buflen, 512); if (err) return err; p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != 0x21)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { log_error ("error parsing C_DevAut: %s\n", gpg_strerror (err)); goto leave; } chr = find_tlv (p, objlen, 0x5F20, &chrlen); if (!chr || chrlen <= 5) { err = gpg_error (GPG_ERR_INV_OBJ); log_error ("CHR not found in CVC\n"); goto leave; } chrlen -= 5; app->card->serialno = xtrymalloc (chrlen); if (!app->card->serialno) { err = gpg_error_from_syserror (); goto leave; } app->card->serialnolen = chrlen; memcpy (app->card->serialno, chr, chrlen); leave: xfree (buffer); return err; } /* Get all the basic information from the SmartCard-HSM, check the structure and initialize our local context. This is used once at application initialization. */ static gpg_error_t read_meta (app_t app) { gpg_error_t err; unsigned char *eflist = NULL; size_t eflistlen = 0; int i; err = read_serialno(app); if (err) return err; err = list_ef (app_get_slot (app), &eflist, &eflistlen); if (err) return err; for (i = 0; i < eflistlen; i += 2) { switch(eflist[i]) { case SC_HSM_KEY_PREFIX: if (eflist[i + 1] == 0) /* No key with ID=0 */ break; err = read_ef_prkd (app, ((SC_HSM_PRKD_PREFIX << 8) | eflist[i + 1]), &app->app_local->private_key_info, &app->app_local->certificate_info); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; if (err) return err; break; case SC_HSM_CD_PREFIX: err = read_ef_cd (app, ((eflist[i] << 8) | eflist[i + 1]), &app->app_local->trusted_certificate_info); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; if (err) return err; break; } } xfree (eflist); return err; } /* Helper to do_learn_status: Send information about all certificates listed in CERTINFO back. Use CERTTYPE as type of the certificate. */ static gpg_error_t send_certinfo (ctrl_t ctrl, const char *certtype, cdf_object_t certinfo) { for (; certinfo; certinfo = certinfo->next) { char *buf, *p; buf = xtrymalloc (4 + certinfo->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "HSM."); bin2hex (certinfo->objid, certinfo->objidlen, p); send_status_info (ctrl, "CERTINFO", certtype, strlen (certtype), buf, strlen (buf), NULL, (size_t)0); xfree (buf); } return 0; } /* Get the keygrip of the private key object PRKDF. On success the keygrip gets returned in the caller provided 41 byte buffer R_GRIPSTR. */ static gpg_error_t keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr) { gpg_error_t err; cdf_object_t cdf; unsigned char *der; size_t derlen; ksba_cert_t cert; /* Look for a matching certificate. A certificate matches if the Id matches the one of the private key info. */ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next) if (cdf->objidlen == prkdf->objidlen && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen)) break; if (!cdf) return gpg_error (GPG_ERR_NOT_FOUND); err = readcert_by_cdf (app, cdf, &der, &derlen); if (err) return err; err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, der, derlen); xfree (der); if (!err) err = app_help_get_keygrip_string (cert, r_gripstr); ksba_cert_release (cert); return err; } /* Helper to do_learn_status: Send information about all known keypairs back. */ static gpg_error_t send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo) { gpg_error_t err; for (; keyinfo; keyinfo = keyinfo->next) { char gripstr[40+1]; char *buf, *p; buf = xtrymalloc (4 + keyinfo->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "HSM."); bin2hex (keyinfo->objid, keyinfo->objidlen, p); err = keygripstr_from_prkdf (app, keyinfo, gripstr); if (err) { log_error ("can't get keygrip from %04X\n", keyinfo->key_reference); } else { assert (strlen (gripstr) == 40); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, buf, strlen (buf), NULL, (size_t)0); } xfree (buf); } return 0; } /* This is the handler for the LEARN command. */ static gpg_error_t 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 { err = send_certinfo (ctrl, "100", app->app_local->certificate_info); if (!err) err = send_certinfo (ctrl, "101", app->app_local->trusted_certificate_info); } if (!err) err = send_keypairinfo (app, ctrl, app->app_local->private_key_info); return err; } /* Read a certificate using the information in CDF and return the certificate in a newly allocated buffer R_CERT and its length R_CERTLEN. */ static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; unsigned char *buffer = NULL; const unsigned char *p, *save_p; size_t buflen, n; int class, tag, constructed, ndef; size_t totobjlen, objlen, hdrlen; int rootca; int i; *r_cert = NULL; *r_certlen = 0; /* First check whether it has been cached. */ if (cdf->image) { *r_cert = xtrymalloc (cdf->imagelen); if (!*r_cert) return gpg_error_from_syserror (); memcpy (*r_cert, cdf->image, cdf->imagelen); *r_certlen = cdf->imagelen; return 0; } err = select_and_read_binary (app_get_slot (app), cdf->fid, "CD", &buffer, &buflen, 4096); if (err) { log_error ("error reading certificate with Id "); for (i=0; i < cdf->objidlen; i++) log_printf ("%02X", cdf->objid[i]); log_printf (": %s\n", gpg_strerror (err)); goto leave; } /* Check whether this is really a certificate. */ p = buffer; n = buflen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) rootca = 0; else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed ) rootca = 1; else { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } totobjlen = objlen + hdrlen; assert (totobjlen <= buflen); err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if (!rootca && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed) { /* The certificate seems to be contained in a userCertificate container. Skip this and assume the following sequence is the certificate. */ if (n < objlen) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } p += objlen; n -= objlen; save_p = p; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (err) goto leave; if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) ) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } totobjlen = objlen + hdrlen; assert (save_p + totobjlen <= buffer + buflen); memmove (buffer, save_p, totobjlen); } *r_cert = buffer; buffer = NULL; *r_certlen = totobjlen; /* Try to cache it. */ if (!cdf->image && (cdf->image = xtrymalloc (*r_certlen))) { memcpy (cdf->image, *r_cert, *r_certlen); cdf->imagelen = *r_certlen; } leave: xfree (buffer); return err; } /* Handler for the READCERT command. Read the certificate with id CERTID (as returned by learn_status in the CERTINFO status lines) and return it in the freshly allocated buffer to be stored at R_CERT and its length at R_CERTLEN. A error code will be returned on failure and R_CERT and R_CERTLEN will be set to (NULL,0). */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; cdf_object_t cdf; *r_cert = NULL; *r_certlen = 0; err = cdf_object_from_certid (app, certid, &cdf); if (!err) err = readcert_by_cdf (app, cdf, r_cert, r_certlen); return err; } /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { if (!strcmp (name, "$AUTHKEYID")) { char *buf, *p; prkdf_object_t prkdf; /* We return the ID of the first private key capable of signing. */ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next) if (prkdf->usageflags.sign) break; if (prkdf) { buf = xtrymalloc (4 + prkdf->objidlen*2 + 1); if (!buf) return gpg_error_from_syserror (); p = stpcpy (buf, "HSM."); bin2hex (prkdf->objid, prkdf->objidlen, p); send_status_info (ctrl, name, buf, strlen (buf), NULL, 0); xfree (buf); return 0; } } else if (!strcmp (name, "$DISPSERIALNO")) { send_status_info (ctrl, name, app->card->serialno, app->card->serialnolen, NULL, 0); return 0; } return gpg_error (GPG_ERR_INV_NAME); } /* Apply PKCS#1 V1.5 padding for signature operation. The function * combines padding, digest info and the hash value. The buffer must * be allocated by the caller matching the key size. */ static void apply_PKCS_padding(const unsigned char *dig, int diglen, const unsigned char *prefix, int prefixlen, unsigned char *buff, int bufflen) { int i, n_ff; /* Caller must ensure a sufficient buffer. */ if (diglen + prefixlen + 4 > bufflen) return; n_ff = bufflen - diglen - prefixlen - 3; *buff++ = 0x00; *buff++ = 0x01; for (i=0; i < n_ff; i++) *buff++ = 0xFF; *buff++ = 0x00; if (prefix) memcpy (buff, prefix, prefixlen); buff += prefixlen; memcpy (buff, dig, diglen); } /* Decode a digest info structure (DI,DILEN) to extract the hash * value. The buffer HASH to receive the digest must be provided by * the caller with HASHLEN pointing to the inbound length. HASHLEN is * updated to the outbound length. */ static int hash_from_digestinfo (const unsigned char *di, size_t dilen, unsigned char *hash, size_t *hashlen) { const unsigned char *p,*pp; size_t n, nn, objlen, hdrlen; int class, tag, constructed, ndef; gpg_error_t err; p = di; n = dilen; err = parse_ber_header (&p, &n, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > n || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if ( err ) return err; pp = p; nn = objlen; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if ( err ) return err; pp += objlen; nn -= objlen; err = parse_ber_header (&pp, &nn, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > nn || tag != TAG_OCTET_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if ( err ) return err; if (*hashlen < objlen) return gpg_error (GPG_ERR_TOO_SHORT); memcpy (hash, pp, objlen); *hashlen = objlen; return 0; } /* Perform PIN verification */ static gpg_error_t verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; pininfo_t pininfo; char *pinvalue; char *prompt; int sw; 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 */ if (sw == SW_REF_DATA_INV) { log_error ("SmartCard-HSM not initialized. Run sc-hsm-tool first\n"); return gpg_error (GPG_ERR_NO_PIN); } if (sw == SW_CHV_BLOCKED) { log_error ("PIN Blocked\n"); return gpg_error (GPG_ERR_PIN_BLOCKED); } memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = 0; pininfo.minlen = 6; pininfo.maxlen = 15; prompt = "||Please enter the PIN"; if (!opt.disable_pinpad && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) { err = pincb (pincb_arg, prompt, NULL); if (err) { log_info ("PIN callback returned error: %s\n", gpg_strerror (err)); return err; } err = iso7816_verify_kp (app_get_slot (app), 0x81, &pininfo); pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */ } else { err = pincb (pincb_arg, prompt, &pinvalue); if (err) { log_info ("PIN callback returned error: %s\n", gpg_strerror (err)); return err; } err = iso7816_verify (app_get_slot (app), 0x81, pinvalue, strlen(pinvalue)); xfree (pinvalue); } if (err) { log_error ("PIN verification failed: %s\n", gpg_strerror (err)); return err; } log_debug ("PIN verification succeeded\n"); return err; } /* Handler for the PKSIGN command. Create the signature and return the allocated result in OUTDATA. If a PIN is required, the PINCB will be used to ask for the PIN; that callback should return the PIN in an allocated buffer and store that as the 3rd argument. The API is somewhat inconsistent: The caller can either supply a plain hash and the algorithm in hashalgo or a complete DigestInfo structure. The former is detect by characteristic length of the provided data (20,28,32,48 or 64 byte). The function returns the RSA block in the size of the modulus or the ECDSA signature in X9.62 format (SEQ/INT(r)/INT(s)) */ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { static unsigned char 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 }; static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; 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; unsigned char cdsblk[256]; /* Raw PKCS#1 V1.5 block with padding (RSA) or hash. */ prkdf_object_t prkdf; /* The private key object. */ size_t cdsblklen; unsigned char algoid; int sw; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); if (indatalen > 124) /* Limit for 1024 bit key */ return gpg_error (GPG_ERR_INV_VALUE); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover ||prkdf->usageflags.non_repudiation)) { log_error ("key %s may not be used for signing\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (prkdf->keytype == KEY_TYPE_RSA) { algoid = 0x20; cdsblklen = prkdf->keysize >> 3; if (!cdsblklen) cdsblklen = 256; if (hashalgo == GCRY_MD_SHA1 && indatalen == 20) apply_PKCS_padding (indata, indatalen, sha1_prefix, sizeof(sha1_prefix), cdsblk, cdsblklen); else if (hashalgo == GCRY_MD_MD5 && indatalen == 20) apply_PKCS_padding (indata, indatalen, rmd160_prefix, sizeof(rmd160_prefix), cdsblk, cdsblklen); else if (hashalgo == GCRY_MD_SHA224 && indatalen == 28) apply_PKCS_padding (indata, indatalen, sha224_prefix, sizeof(sha224_prefix), cdsblk, cdsblklen); else if (hashalgo == GCRY_MD_SHA256 && indatalen == 32) apply_PKCS_padding (indata, indatalen, sha256_prefix, sizeof(sha256_prefix), cdsblk, cdsblklen); else if (hashalgo == GCRY_MD_SHA384 && indatalen == 48) apply_PKCS_padding (indata, indatalen, sha384_prefix, sizeof(sha384_prefix), cdsblk, cdsblklen); else if (hashalgo == GCRY_MD_SHA512 && indatalen == 64) apply_PKCS_padding (indata, indatalen, sha512_prefix, sizeof(sha512_prefix), cdsblk, cdsblklen); else /* Assume it's already a digest info or TLS_MD5SHA1 */ apply_PKCS_padding (indata, indatalen, NULL, 0, cdsblk, cdsblklen); } else { algoid = 0x70; if (indatalen != 20 && indatalen != 28 && indatalen != 32 && indatalen != 48 && indatalen != 64) { cdsblklen = sizeof(cdsblk); err = hash_from_digestinfo (indata, indatalen, cdsblk, &cdsblklen); if (err) { log_error ("DigestInfo invalid: %s\n", gpg_strerror (err)); return err; } } else { memcpy (cdsblk, indata, indatalen); cdsblklen = indatalen; } } err = verify_pin (app, pincb, pincb_arg); if (err) return err; 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); } /* Handler for the PKAUTH command. This is basically the same as the PKSIGN command but we first check that the requested key is suitable for authentication; that is, it must match the criteria used for the attribute $AUTHKEYID. See do_sign for calling conventions; there is no HASHALGO, though. */ static gpg_error_t do_auth (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { gpg_error_t err; prkdf_object_t prkdf; int algo; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; if (!prkdf->usageflags.sign) { log_error ("key %s may not be used for authentication\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1; return do_sign (app, keyidstr, algo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); } /* Check PKCS#1 V1.5 padding and extract plain text. The function * allocates a buffer for the plain text. The caller must release the * buffer. */ static gpg_error_t strip_PKCS15_padding(unsigned char *src, int srclen, unsigned char **dst, size_t *dstlen) { unsigned char *p; if (srclen < 2) return gpg_error (GPG_ERR_DECRYPT_FAILED); if (*src++ != 0x00) return gpg_error (GPG_ERR_DECRYPT_FAILED); if (*src++ != 0x02) return gpg_error (GPG_ERR_DECRYPT_FAILED); srclen -= 2; while ((srclen > 0) && *src) { src++; srclen--; } if (srclen < 2) return gpg_error (GPG_ERR_DECRYPT_FAILED); src++; srclen--; p = xtrymalloc (srclen); if (!p) return gpg_error_from_syserror (); memcpy (p, src, srclen); *dst = p; *dstlen = srclen; return 0; } /* Decrypt a PKCS#1 V1.5 formatted cryptogram using the referenced key. */ static gpg_error_t do_decipher (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { gpg_error_t err; unsigned char p1blk[256]; /* Enciphered P1 block */ prkdf_object_t prkdf; /* The private key object. */ unsigned char *rspdata; size_t rspdatalen; size_t p1blklen; int sw; if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap)) { log_error ("key %s may not be used for deciphering\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (prkdf->keytype != KEY_TYPE_RSA) return gpg_error (GPG_ERR_NOT_SUPPORTED); p1blklen = prkdf->keysize >> 3; if (!p1blklen) p1blklen = 256; /* The input may be shorter (due to MPIs not storing leading zeroes) or longer than the block size. We put INDATA right aligned into the buffer. If INDATA is longer than the block size we truncate it on the left. */ memset (p1blk, 0, sizeof(p1blk)); if (indatalen > p1blklen) memcpy (p1blk, (unsigned char *)indata + (indatalen - p1blklen), p1blklen); else memcpy (p1blk + (p1blklen - indatalen), indata, indatalen); err = verify_pin(app, pincb, pincb_arg); if (err) return err; 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) { log_error ("Decrypt failed: %s\n", gpg_strerror (err)); return err; } err = strip_PKCS15_padding (rspdata, rspdatalen, outdata, outdatalen); xfree (rspdata); if (!err) *r_info |= APP_DECIPHER_INFO_NOPAD; return err; } /* * Select the SmartCard-HSM application on the card in SLOT. */ gpg_error_t app_select_sc_hsm (app_t app) { int slot = app_get_slot (app); int rc; rc = iso7816_select_application (slot, sc_hsm_aid, sizeof sc_hsm_aid, 0); if (!rc) { app->apptype = APPTYPE_SC_HSM; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { rc = gpg_error_from_syserror (); goto leave; } rc = read_meta (app); if (rc) goto leave; app->fnc.deinit = do_deinit; app->fnc.reselect = NULL; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.getattr = do_getattr; app->fnc.setattr = NULL; app->fnc.genkey = NULL; app->fnc.sign = do_sign; app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = NULL; app->fnc.check_pin = NULL; leave: if (rc) do_deinit (app); } return rc; } diff --git a/scd/app.c b/scd/app.c index 6455f7287..e8152933b 100644 --- a/scd/app.c +++ b/scd/app.c @@ -1,1760 +1,1760 @@ /* app.c - Application selection. * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "scdaemon.h" #include "../common/exechelp.h" #include "iso7816.h" #include "apdu.h" #include "../common/tlv.h" /* Forward declaration of internal function. */ static gpg_error_t select_additional_application_internal (card_t card, apptype_t req_apptype); /* Lock to protect the list of cards and its associated * applications. */ static npth_mutex_t card_list_lock; /* A list of card contexts. A card is a collection of applications * (described by app_t) on the same physical token. */ static card_t card_top; /* The list of application names and their select function. If no * specific application is selected the first available application on * a card is selected. */ struct app_priority_list_s { apptype_t apptype; char const *name; gpg_error_t (*select_func)(app_t); }; static struct app_priority_list_s app_priority_list[] = {{ APPTYPE_OPENPGP , "openpgp", app_select_openpgp }, { APPTYPE_PIV , "piv", app_select_piv }, { APPTYPE_NKS , "nks", app_select_nks }, { APPTYPE_P15 , "p15", app_select_p15 }, { APPTYPE_GELDKARTE, "geldkarte", app_select_geldkarte }, { APPTYPE_DINSIG , "dinsig", app_select_dinsig }, { APPTYPE_SC_HSM , "sc-hsm", app_select_sc_hsm }, { APPTYPE_NONE , NULL, NULL } /* APPTYPE_UNDEFINED is special and not listed here. */ }; /* Map a cardtype to a string. Never returns NULL. */ const char * strcardtype (cardtype_t t) { switch (t) { case CARDTYPE_GENERIC: return "generic"; case CARDTYPE_YUBIKEY: return "yubikey"; } return "?"; } /* Map an application type to a string. Never returns NULL. */ const char * strapptype (apptype_t t) { int i; for (i=0; app_priority_list[i].apptype; i++) if (app_priority_list[i].apptype == t) return app_priority_list[i].name; return t == APPTYPE_UNDEFINED? "undefined" : t? "?" : "none"; } /* Return the apptype for NAME. */ static apptype_t apptype_from_name (const char *name) { int i; if (!name) return APPTYPE_NONE; for (i=0; app_priority_list[i].apptype; i++) if (!ascii_strcasecmp (app_priority_list[i].name, name)) return app_priority_list[i].apptype; if (!ascii_strcasecmp ("undefined", name)) return APPTYPE_UNDEFINED; return APPTYPE_NONE; } /* Initialization function to change the default app_priority_list. * LIST is a list of comma or space separated strings with application * names. Unknown names will only result in warning message. * Application not mentioned in LIST are used in their original order * after the given once. */ void app_update_priority_list (const char *arg) { struct app_priority_list_s save; char **names; int i, j, idx; names = strtokenize (arg, ", "); if (!names) log_fatal ("strtokenize failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); idx = 0; for (i=0; names[i]; i++) { ascii_strlwr (names[i]); for (j=0; j < i; j++) if (!strcmp (names[j], names[i])) break; if (j < i) { log_info ("warning: duplicate application '%s' in priority list\n", names[i]); continue; } for (j=idx; app_priority_list[j].name; j++) if (!strcmp (names[i], app_priority_list[j].name)) break; if (!app_priority_list[j].name) { log_info ("warning: unknown application '%s' in priority list\n", names[i]); continue; } save = app_priority_list[idx]; app_priority_list[idx] = app_priority_list[j]; app_priority_list[j] = save; idx++; } log_assert (idx < DIM (app_priority_list)); xfree (names); for (i=0; app_priority_list[i].name; i++) log_info ("app priority %d: %s\n", i, app_priority_list[i].name); } static void print_progress_line (void *opaque, const char *what, int pc, int cur, int tot) { ctrl_t ctrl = opaque; char line[100]; if (ctrl) { snprintf (line, sizeof line, "%s %c %d %d", what, pc, cur, tot); send_status_direct (ctrl, "PROGRESS", line); } } /* Lock the CARD. This function shall be used right before calling * any of the actual application functions to serialize access to the * reader. We do this always even if the card is not actually used. * This allows an actual connection to assume that it never shares a * card (while performing one command). Returns 0 on success; only * then the unlock_reader function must be called after returning from * the handler. Right now we assume a that a reader has just one * card; this may eventually need refinement. */ static gpg_error_t lock_card (card_t card, ctrl_t ctrl) { if (npth_mutex_lock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); log_error ("failed to acquire CARD lock for %p: %s\n", card, gpg_strerror (err)); return err; } apdu_set_progress_cb (card->slot, print_progress_line, ctrl); apdu_set_prompt_cb (card->slot, popup_prompt, ctrl); return 0; } /* Release a lock on a card. See lock_reader(). */ static void unlock_card (card_t card) { apdu_set_progress_cb (card->slot, NULL, NULL); apdu_set_prompt_cb (card->slot, NULL, NULL); if (npth_mutex_unlock (&card->lock)) { gpg_error_t err = gpg_error_from_syserror (); log_error ("failed to release CARD lock for %p: %s\n", card, gpg_strerror (err)); } } /* This function may be called to print information pertaining to the * current state of this module to the log. */ void app_dump_state (void) { card_t c; app_t a; npth_mutex_lock (&card_list_lock); for (c = card_top; c; c = c->next) { log_info ("app_dump_state: card=%p slot=%d type=%s\n", c, c->slot, strcardtype (c->cardtype)); for (a=c->app; a; a = a->next) log_info ("app_dump_state: app=%p type='%s'\n", a, strapptype (a->apptype)); } npth_mutex_unlock (&card_list_lock); } /* Check whether the application NAME is allowed. This does not mean we have support for it though. */ static int is_app_allowed (const char *name) { strlist_t l; for (l=opt.disabled_applications; l; l = l->next) if (!strcmp (l->d, name)) return 0; /* no */ return 1; /* yes */ } /* This function is mainly used by the serialno command to check for * an application conflict which may appear if the serialno command is * used to request a specific application and the connection has * already done a select_application. Return values are: * 0 - No conflict * GPG_ERR_FALSE - Another application is in use but it is possible * to switch to the requested application. * Other code - Switching is not possible. * * If SERIALNO_BIN is not NULL a coflict is onl asserted if the * serialno of the card matches. */ gpg_error_t check_application_conflict (card_t card, const char *name, const unsigned char *serialno_bin, size_t serialno_bin_len) { apptype_t apptype; if (!card || !name) return 0; if (!card->app) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); /* Should not happen. */ if (serialno_bin && card->serialno) { if (serialno_bin_len != card->serialnolen || memcmp (serialno_bin, card->serialno, card->serialnolen)) return 0; /* The card does not match the requested S/N. */ } apptype = apptype_from_name (name); if (card->app->apptype == apptype) return 0; if (card->app->apptype == APPTYPE_UNDEFINED) return 0; if (card->cardtype == CARDTYPE_YUBIKEY) { if (card->app->apptype == APPTYPE_OPENPGP) { /* Current app is OpenPGP. */ if (!ascii_strcasecmp (name, "piv")) return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ } else if (card->app->apptype == APPTYPE_PIV) { /* Current app is PIV. */ if (!ascii_strcasecmp (name, "openpgp")) return gpg_error (GPG_ERR_FALSE); /* Switching allowed. */ } } log_info ("application '%s' in use - can't switch\n", strapptype (card->app->apptype)); return gpg_error (GPG_ERR_CONFLICT); } gpg_error_t card_reset (card_t card, ctrl_t ctrl, int send_reset) { gpg_error_t err = 0; if (send_reset) { int sw; lock_card (card, ctrl); sw = apdu_reset (card->slot); if (sw) err = gpg_error (GPG_ERR_CARD_RESET); card->reset_requested = 1; unlock_card (card); scd_kick_the_loop (); gnupg_sleep (1); } else { ctrl->card_ctx = NULL; ctrl->current_apptype = APPTYPE_NONE; card_unref (card); } return err; } static gpg_error_t app_new_register (int slot, ctrl_t ctrl, const char *name, int periodical_check_needed) { gpg_error_t err = 0; card_t card = NULL; app_t app = NULL; unsigned char *result = NULL; size_t resultlen; int want_undefined; int i; /* Need to allocate a new card object */ card = xtrycalloc (1, sizeof *card); if (!card) { err = gpg_error_from_syserror (); log_info ("error allocating context: %s\n", gpg_strerror (err)); return err; } card->slot = slot; card->card_status = (unsigned int)-1; if (npth_mutex_init (&card->lock, NULL)) { err = gpg_error_from_syserror (); log_error ("error initializing mutex: %s\n", gpg_strerror (err)); xfree (card); return err; } err = lock_card (card, ctrl); if (err) { xfree (card); return err; } want_undefined = (name && !strcmp (name, "undefined")); /* Try to read the GDO file first to get a default serial number. We skip this if the undefined application has been requested. */ if (!want_undefined) { err = iso7816_select_file (slot, 0x3F00, 1); if (gpg_err_code (err) == GPG_ERR_CARD) { /* Might be SW==0x7D00. Let's test whether it is a Yubikey * by selecting its manager application and then reading the * config. */ static char const yk_aid[] = { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; /*MGR*/ static char const otp_aid[] = { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; /*OTP*/ unsigned char *buf; size_t buflen; const unsigned char *s0; unsigned char formfactor; size_t n; if (!iso7816_select_application (slot, yk_aid, sizeof yk_aid, 0x0001) && !iso7816_apdu_direct (slot, "\x00\x1d\x00\x00\x00", 5, 0, NULL, &buf, &buflen)) { card->cardtype = CARDTYPE_YUBIKEY; if (opt.verbose) { log_info ("Yubico: config="); log_printhex (buf, buflen, ""); } /* We skip the first byte which seems to be the total * length of the config data. */ if (buflen > 1) { s0 = find_tlv (buf+1, buflen-1, 0x04, &n); /* Form factor */ formfactor = (s0 && n == 1)? *s0 : 0; s0 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */ if (s0 && n >= 4) { card->serialno = xtrymalloc (3 + 1 + n); if (card->serialno) { card->serialnolen = 3 + 1 + n; card->serialno[0] = 0xff; card->serialno[1] = 0x02; card->serialno[2] = 0x0; card->serialno[3] = formfactor; memcpy (card->serialno + 4, s0, n); /* Note that we do not clear the error * so that no further serial number * testing is done. After all we just * set the serial number. */ } } s0 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */ if (s0 && n == 3) card->cardversion = ((s0[0]<<16)|(s0[1]<<8)|s0[2]); else if (!s0) { /* No version - this is not a Yubikey 5. We now * switch to the OTP app and take the first * three bytes of the reponse as version * number. */ xfree (buf); buf = NULL; if (!iso7816_select_application_ext (slot, otp_aid, sizeof otp_aid, 1, &buf, &buflen) && buflen > 3) card->cardversion = ((buf[0]<<16)|(buf[1]<<8)|buf[2]); } } xfree (buf); } } if (!err) err = iso7816_select_file (slot, 0x2F02, 0); if (!err) err = iso7816_read_binary (slot, 0, 0, &result, &resultlen); if (!err) { size_t n; const unsigned char *p; p = find_tlv_unchecked (result, resultlen, 0x5A, &n); if (p) resultlen -= (p-result); if (p && n > resultlen && n == 0x0d && resultlen+1 == n) { /* The object does not fit into the buffer. This is an invalid encoding (or the buffer is too short. However, I have some test cards with such an invalid encoding and therefore I use this ugly workaround to return something I can further experiment with. */ log_info ("enabling BMI testcard workaround\n"); n--; } if (p && n <= resultlen) { /* The GDO file is pretty short, thus we simply reuse it for storing the serial number. */ memmove (result, p, n); card->serialno = result; card->serialnolen = n; err = app_munge_serialno (card); if (err) goto leave; } else xfree (result); result = NULL; } } /* Allocate a new app object. */ app = xtrycalloc (1, sizeof *app); if (!app) { err = gpg_error_from_syserror (); log_info ("error allocating app context: %s\n", gpg_strerror (err)); goto leave; } card->app = app; app->card = card; /* Figure out the application to use. */ if (want_undefined) { /* We switch to the "undefined" application only if explicitly requested. */ app->apptype = APPTYPE_UNDEFINED; /* Clear the error so that we don't run through the application * selection chain. */ err = 0; } else { /* For certain error codes, there is no need to try more. */ if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_ENODEV) goto leave; /* Set a default error so that we run through the application * selection chain. */ err = gpg_error (GPG_ERR_NOT_FOUND); } /* Find the first available app if NAME is NULL or the matching * NAME but only if that application is also enabled. */ for (i=0; err && app_priority_list[i].name; i++) { if (is_app_allowed (app_priority_list[i].name) && (!name || !strcmp (name, app_priority_list[i].name))) err = app_priority_list[i].select_func (app); } if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE) err = gpg_error (GPG_ERR_NOT_SUPPORTED); leave: if (err) { if (name) log_info ("can't select application '%s': %s\n", name, gpg_strerror (err)); else log_info ("no supported card application found: %s\n", gpg_strerror (err)); unlock_card (card); xfree (app); xfree (card); return err; } card->periodical_check_needed = periodical_check_needed; card->next = card_top; card_top = card; unlock_card (card); return 0; } /* If called with NAME as NULL, select the best fitting application * and return its card context; otherwise select the application with * NAME and return its card context. Returns an error code and stores * NULL at R_CARD if no application was found or no card is present. */ gpg_error_t select_application (ctrl_t ctrl, const char *name, card_t *r_card, int scan, const unsigned char *serialno_bin, size_t serialno_bin_len) { gpg_error_t err = 0; card_t card, card_prev = NULL; *r_card = NULL; npth_mutex_lock (&card_list_lock); if (scan || !card_top) { struct dev_list *l; int new_card = 0; /* Scan the devices to find new device(s). */ err = apdu_dev_list_start (opt.reader_port, &l); if (err) { npth_mutex_unlock (&card_list_lock); return err; } while (1) { int slot; int periodical_check_needed_this; slot = apdu_open_reader (l, !card_top); if (slot < 0) break; periodical_check_needed_this = apdu_connect (slot); if (periodical_check_needed_this < 0) { /* We close a reader with no card. */ err = gpg_error (GPG_ERR_ENODEV); } else { err = app_new_register (slot, ctrl, name, periodical_check_needed_this); new_card++; } if (err) apdu_close_reader (slot); } apdu_dev_list_finish (l); /* If new device(s), kick the scdaemon loop. */ if (new_card) scd_kick_the_loop (); } for (card = card_top; card; card = card->next) { lock_card (card, ctrl); if (serialno_bin == NULL) break; if (card->serialnolen == serialno_bin_len && !memcmp (card->serialno, serialno_bin, card->serialnolen)) break; unlock_card (card); card_prev = card; } if (card) { err = check_application_conflict (card, name, NULL, 0); if (!err) ctrl->current_apptype = card->app ? card->app->apptype : APPTYPE_NONE; else if (gpg_err_code (err) == GPG_ERR_FALSE) { apptype_t req_apptype = apptype_from_name (name); if (!req_apptype) err = gpg_error (GPG_ERR_NOT_FOUND); else { err = select_additional_application_internal (card, req_apptype); if (!err) ctrl->current_apptype = req_apptype; } } if (!err) { /* Note: We do not use card_ref as we are already locked. */ card->ref_count++; *r_card = card; if (card_prev) { card_prev->next = card->next; card->next = card_top; card_top = card; } } unlock_card (card); } else err = gpg_error (GPG_ERR_ENODEV); npth_mutex_unlock (&card_list_lock); return err; } static gpg_error_t select_additional_application_internal (card_t card, apptype_t req_apptype) { gpg_error_t err = 0; app_t app; int i; /* Check that the requested app has not yet been put onto the list. */ for (app = card->app; app; app = app->next) if (app->apptype == req_apptype) { /* We already got this one. Note that in this case we don't * make it the current one but it doesn't matter because * maybe_switch_app will do that anyway. */ err = 0; app = NULL; goto leave; } /* Allocate a new app object. */ app = xtrycalloc (1, sizeof *app); if (!app) { err = gpg_error_from_syserror (); log_info ("error allocating app context: %s\n", gpg_strerror (err)); goto leave; } app->card = card; /* Find the app and run the select. */ for (i=0; app_priority_list[i].apptype; i++) { if (app_priority_list[i].apptype == req_apptype && is_app_allowed (app_priority_list[i].name)) { err = app_priority_list[i].select_func (app); break; } } if (!app_priority_list[i].apptype || (err && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)) err = gpg_error (GPG_ERR_NOT_SUPPORTED); if (err) goto leave; /* Add this app. We make it the current one to avoid an extra * reselect by maybe_switch_app after the select we just did. */ app->next = card->app; card->app = app; log_info ("added app '%s' to the card context\n", strapptype (app->apptype)); leave: if (err) xfree (app); return err; } /* This function needs to be called with the NAME of the new * application to be selected on CARD. On success the application is * added to the list of the card's active applications as currently * active application. On error no new application is allocated. * Selecting an already selected application has no effect. */ gpg_error_t select_additional_application (ctrl_t ctrl, const char *name) { gpg_error_t err = 0; apptype_t req_apptype; card_t card; req_apptype = apptype_from_name (name); if (!req_apptype) err = gpg_error (GPG_ERR_NOT_FOUND); card = ctrl->card_ctx; if (!card) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); err = lock_card (card, ctrl); if (err) return err; err = select_additional_application_internal (card, req_apptype); if (!err) { ctrl->current_apptype = req_apptype; log_debug ("current_apptype is set to %s\n", name); } unlock_card (card); return err; } char * get_supported_applications (void) { int idx; size_t nbytes; char *buffer, *p; const char *s; for (nbytes=1, idx=0; (s=app_priority_list[idx].name); idx++) nbytes += strlen (s) + 1 + 1; buffer = xtrymalloc (nbytes); if (!buffer) return NULL; for (p=buffer, idx=0; (s=app_priority_list[idx].name); idx++) if (is_app_allowed (s)) p = stpcpy (stpcpy (p, s), ":\n"); *p = 0; return buffer; } /* Deallocate the application. */ static void deallocate_card (card_t card) { card_t c, c_prev = NULL; app_t a, anext; for (c = card_top; c; c = c->next) if (c == card) { if (c_prev == NULL) card_top = c->next; else c_prev->next = c->next; break; } else c_prev = c; if (card->ref_count) log_error ("releasing still used card context (%d)\n", card->ref_count); for (a = card->app; a; a = anext) { if (a->fnc.deinit) { a->fnc.deinit (a); a->fnc.deinit = NULL; } anext = a->next; xfree (a); } xfree (card->serialno); unlock_card (card); xfree (card); } /* Increment the reference counter of CARD. Returns CARD. */ card_t card_ref (card_t card) { lock_card (card, NULL); ++card->ref_count; unlock_card (card); return card; } /* Decrement the reference counter for CARD. Note that we are using * reference counting to track the users of the card's application and * are deferring the actual deallocation to allow for a later reuse by * a new connection. Using NULL for CARD is a no-op. */ void card_unref (card_t card) { if (!card) return; /* We don't deallocate CARD here. Instead, we keep it. This is useful so that a card does not get reset even if only one session is using the card - this way the PIN cache and other cached data are preserved. */ lock_card (card, NULL); card_unref_locked (card); unlock_card (card); } /* This is the same as card_unref but assumes that CARD is already * locked. */ void card_unref_locked (card_t card) { if (!card) return; if (!card->ref_count) log_bug ("tried to release an already released card context\n"); --card->ref_count; } /* The serial number may need some cosmetics. Do it here. This function shall only be called once after a new serial number has been put into APP->serialno. Prefixes we use: FF 00 00 = For serial numbers starting with an FF FF 01 00 = Some german p15 cards return an empty serial number so the serial number from the EF(TokenInfo) is used instead. FF 02 00 = Serial number from Yubikey config FF 7F 00 = No serialno. All other serial number not starting with FF are used as they are. */ gpg_error_t app_munge_serialno (card_t card) { if (card->serialnolen && card->serialno[0] == 0xff) { /* The serial number starts with our special prefix. This requires that we put our default prefix "FF0000" in front. */ unsigned char *p = xtrymalloc (card->serialnolen + 3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\0", 3); memcpy (p+3, card->serialno, card->serialnolen); card->serialnolen += 3; xfree (card->serialno); card->serialno = p; } else if (!card->serialnolen) { unsigned char *p = xtrymalloc (3); if (!p) return gpg_error_from_syserror (); memcpy (p, "\xff\x7f", 3); card->serialnolen = 3; xfree (card->serialno); card->serialno = p; } return 0; } /* Retrieve the serial number of the card. The serial number is returned as a malloced string (hex encoded) in SERIAL. Caller must free SERIAL unless the function returns an error. */ char * card_get_serialno (card_t card) { char *serial; if (!card) return NULL; if (!card->serialnolen) serial = xtrystrdup ("FF7F00"); else serial = bin2hex (card->serialno, card->serialnolen, NULL); return serial; } /* Same as card_get_serialno but takes an APP object. */ char * app_get_serialno (app_t app) { if (!app || !app->card) return NULL; return card_get_serialno (app->card); } /* Check that the card has been initialized and whether we need to * switch to another application on the same card. Switching means * that the new active app will be moved to the head of the list at * CARD->app. Thus function must be called with the card lock held. */ static gpg_error_t maybe_switch_app (ctrl_t ctrl, card_t card) { gpg_error_t err; app_t app, app_prev; if (!card->ref_count || !card->app) return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); if (!ctrl->current_apptype) { /* For whatever reasons the current apptype has not been set - * fix that and use the current app. */ ctrl->current_apptype = card->app->apptype; return 0; } log_debug ("card=%p want=%s card->app=%s\n", card, strapptype (ctrl->current_apptype), strapptype (card->app->apptype)); app_dump_state (); if (ctrl->current_apptype == card->app->apptype) return 0; /* No need to switch. */ app_prev = card->app; for (app = app_prev->next; app; app_prev = app, app = app->next) if (app->apptype == ctrl->current_apptype) break; if (!app) return gpg_error (GPG_ERR_WRONG_CARD); if (!app->fnc.reselect) { log_error ("oops: reselect function missing for '%s'\n", strapptype(app->apptype)); return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); } err = app->fnc.reselect (app, ctrl); if (err) { log_error ("error re-selecting '%s': %s\n", strapptype(app->apptype), gpg_strerror (err)); return err; } /* Swap APP with the head of the app list. Note that APP is not the * head of the list. */ app_prev->next = app->next; app->next = card->app; card->app = app; ctrl->current_apptype = app->apptype; log_info ("switched to '%s'\n", strapptype (app->apptype)); return 0; } /* Write out the application specific status lines for the LEARN command. */ gpg_error_t app_write_learn_status (card_t card, ctrl_t ctrl, unsigned int flags) { gpg_error_t err; app_t app; if (!card) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.learn_status) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else { app = card->app; /* We do not send CARD and APPTYPE if only keypairinfo is requested. */ - if (!(flags &1)) + if (!(flags & APP_LEARN_FLAG_KEYPAIRINFO)) { if (card->cardtype) send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); if (card->cardversion) send_status_printf (ctrl, "CARDVERSION", "%X", card->cardversion); if (app->apptype) send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype)); if (app->appversion) send_status_printf (ctrl, "APPVERSION", "%X", app->appversion); /* FIXME: Send info for the other active apps of the card? */ } err = app->fnc.learn_status (app, ctrl, flags); } unlock_card (card); 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. */ gpg_error_t app_readcert (card_t card, ctrl_t ctrl, const char *certid, unsigned char **cert, size_t *certlen) { gpg_error_t err; if (!card) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.readcert) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.readcert (card->app, certid, cert, certlen); unlock_card (card); return err; } /* Read the key with ID KEYID. On success a canonical encoded * S-expression with the public key will get stored at PK and its * length (for assertions) at PKLEN; the caller must release that * buffer. On error NULL will be stored at PK and PKLEN and an error * code returned. If the key is not required NULL may be passed for * PK; this makse send if the APP_READKEY_FLAG_INFO has also been set. * * This function might not be supported by all applications. */ gpg_error_t app_readkey (card_t card, ctrl_t ctrl, const char *keyid, unsigned int flags, unsigned char **pk, size_t *pklen) { gpg_error_t err; if (pk) *pk = NULL; if (pklen) *pklen = 0; if (!card || !keyid) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.readkey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.readkey (card->app, ctrl, keyid, flags, pk, pklen); unlock_card (card); return err; } /* Perform a GETATTR operation. */ gpg_error_t app_getattr (card_t card, ctrl_t ctrl, const char *name) { gpg_error_t err; if (!card || !name || !*name) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (name && !strcmp (name, "CARDTYPE")) { send_status_direct (ctrl, "CARDTYPE", strcardtype (card->cardtype)); } else if (name && !strcmp (name, "APPTYPE")) { send_status_direct (ctrl, "APPTYPE", strapptype (card->app->apptype)); } else if (name && !strcmp (name, "SERIALNO")) { char *serial; serial = card_get_serialno (card); if (!serial) err = gpg_error (GPG_ERR_INV_VALUE); else { send_status_direct (ctrl, "SERIALNO", serial); xfree (serial); } } else if (!card->app->fnc.getattr) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.getattr (card->app, ctrl, name); unlock_card (card); return err; } /* Perform a SETATTR operation. */ gpg_error_t app_setattr (card_t card, ctrl_t ctrl, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t err; if (!card || !name || !*name || !value) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.setattr) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.setattr (card->app, name, pincb, pincb_arg, value, valuelen); unlock_card (card); return err; } /* Create the signature and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t app_sign (card_t card, ctrl_t ctrl, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { gpg_error_t err; if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.sign) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.sign (card->app, keyidstr, hashalgo, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); unlock_card (card); if (opt.verbose) log_info ("operation sign result: %s\n", gpg_strerror (err)); return err; } /* Create the signature using the INTERNAL AUTHENTICATE command and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t app_auth (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { gpg_error_t err; if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.auth) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.auth (card->app, keyidstr, pincb, pincb_arg, indata, indatalen, outdata, outdatalen); unlock_card (card); if (opt.verbose) log_info ("operation auth result: %s\n", gpg_strerror (err)); return err; } /* Decrypt the data in INDATA and return the allocated result in OUTDATA. If a PIN is required the PINCB will be used to ask for the PIN; it should return the PIN in an allocated buffer and put it into PIN. */ gpg_error_t app_decipher (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen, unsigned int *r_info) { gpg_error_t err; *r_info = 0; if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.decipher) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.decipher (card->app, keyidstr, pincb, pincb_arg, indata, indatalen, outdata, outdatalen, r_info); unlock_card (card); if (opt.verbose) log_info ("operation decipher result: %s\n", gpg_strerror (err)); return err; } /* Perform the WRITECERT operation. */ gpg_error_t app_writecert (card_t card, ctrl_t ctrl, const char *certidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *data, size_t datalen) { gpg_error_t err; if (!card || !certidstr || !*certidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.writecert) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.writecert (card->app, ctrl, certidstr, pincb, pincb_arg, data, datalen); unlock_card (card); if (opt.verbose) log_info ("operation writecert result: %s\n", gpg_strerror (err)); return err; } /* Perform the WRITEKEY operation. */ gpg_error_t app_writekey (card_t card, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen) { gpg_error_t err; if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.writekey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.writekey (card->app, ctrl, keyidstr, flags, pincb, pincb_arg, keydata, keydatalen); unlock_card (card); if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; } /* Perform a GENKEY operation. */ gpg_error_t app_genkey (card_t card, ctrl_t ctrl, const char *keynostr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; if (!card || !keynostr || !*keynostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.genkey) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.genkey (card->app, ctrl, keynostr, keytype, flags, createtime, pincb, pincb_arg); unlock_card (card); if (opt.verbose) log_info ("operation genkey result: %s\n", gpg_strerror (err)); return err; } /* Perform a GET CHALLENGE operation. This function is special as it directly accesses the card without any application specific wrapper. */ gpg_error_t app_get_challenge (card_t card, ctrl_t ctrl, size_t nbytes, unsigned char *buffer) { gpg_error_t err; if (!card || !nbytes || !buffer) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if (!card->ref_count) err = gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); else err = iso7816_get_challenge (card->slot, nbytes, buffer); unlock_card (card); return err; } /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */ gpg_error_t app_change_pin (card_t card, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; if (!card || !chvnostr || !*chvnostr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.change_pin) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.change_pin (card->app, ctrl, chvnostr, flags, pincb, pincb_arg); unlock_card (card); if (opt.verbose) log_info ("operation change_pin result: %s\n", gpg_strerror (err)); return err; } /* Perform a VERIFY operation without doing anything else. This may be used to initialize a the PIN cache for long lasting other operations. Its use is highly application dependent. */ gpg_error_t app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; if (!card || !keyidstr || !*keyidstr || !pincb) return gpg_error (GPG_ERR_INV_VALUE); err = lock_card (card, ctrl); if (err) return err; if ((err = maybe_switch_app (ctrl, card))) ; else if (!card->app->fnc.check_pin) err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); else err = card->app->fnc.check_pin (card->app, keyidstr, pincb, pincb_arg); unlock_card (card); if (opt.verbose) log_info ("operation check_pin result: %s\n", gpg_strerror (err)); return err; } static void report_change (int slot, int old_status, int cur_status) { char *homestr, *envstr; char *fname; char templ[50]; FILE *fp; snprintf (templ, sizeof templ, "reader_%d.status", slot); fname = make_filename (gnupg_homedir (), templ, NULL ); fp = fopen (fname, "w"); if (fp) { fprintf (fp, "%s\n", (cur_status & 1)? "USABLE": (cur_status & 4)? "ACTIVE": (cur_status & 2)? "PRESENT": "NOCARD"); fclose (fp); } xfree (fname); homestr = make_filename (gnupg_homedir (), NULL); if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0) log_error ("out of core while building environment\n"); else { gpg_error_t err; const char *args[9], *envs[2]; char numbuf1[30], numbuf2[30], numbuf3[30]; envs[0] = envstr; envs[1] = NULL; sprintf (numbuf1, "%d", slot); sprintf (numbuf2, "0x%04X", old_status); sprintf (numbuf3, "0x%04X", cur_status); args[0] = "--reader-port"; args[1] = numbuf1; args[2] = "--old-code"; args[3] = numbuf2; args[4] = "--new-code"; args[5] = numbuf3; args[6] = "--status"; args[7] = ((cur_status & 1)? "USABLE": (cur_status & 4)? "ACTIVE": (cur_status & 2)? "PRESENT": "NOCARD"); args[8] = NULL; fname = make_filename (gnupg_homedir (), "scd-event", NULL); err = gnupg_spawn_process_detached (fname, args, envs); if (err && gpg_err_code (err) != GPG_ERR_ENOENT) log_error ("failed to run event handler '%s': %s\n", fname, gpg_strerror (err)); xfree (fname); xfree (envstr); } xfree (homestr); } int scd_update_reader_status_file (void) { card_t card, card_next; int periodical_check_needed = 0; npth_mutex_lock (&card_list_lock); for (card = card_top; card; card = card_next) { int sw; unsigned int status; lock_card (card, NULL); card_next = card->next; if (card->reset_requested) status = 0; else { sw = apdu_get_status (card->slot, 0, &status); if (sw == SW_HOST_NO_READER) { /* Most likely the _reader_ has been unplugged. */ status = 0; } else if (sw) { /* Get status failed. Ignore that. */ if (card->periodical_check_needed) periodical_check_needed = 1; unlock_card (card); continue; } } if (card->card_status != status) { report_change (card->slot, card->card_status, status); send_client_notifications (card, status == 0); if (status == 0) { log_debug ("Removal of a card: %d\n", card->slot); apdu_close_reader (card->slot); deallocate_card (card); } else { card->card_status = status; if (card->periodical_check_needed) periodical_check_needed = 1; unlock_card (card); } } else { if (card->periodical_check_needed) periodical_check_needed = 1; unlock_card (card); } } npth_mutex_unlock (&card_list_lock); return periodical_check_needed; } /* This function must be called once to initialize this module. This has to be done before a second thread is spawned. We can't do the static initialization because Pth emulation code might not be able to do a static init; in particular, it is not possible for W32. */ gpg_error_t initialize_module_command (void) { gpg_error_t err; if (npth_mutex_init (&card_list_lock, NULL)) { err = gpg_error_from_syserror (); log_error ("app: error initializing mutex: %s\n", gpg_strerror (err)); return err; } return apdu_init (); } /* Sort helper for app_send_card_list. */ static int compare_card_list_items (const void *arg_a, const void *arg_b) { const card_t a = *(const card_t *)arg_a; const card_t b = *(const card_t *)arg_b; return a->slot - b->slot; } /* Send status lines with the serialno of all inserted cards. */ gpg_error_t app_send_card_list (ctrl_t ctrl) { gpg_error_t err; card_t c; char buf[65]; card_t *cardlist = NULL; int n, ncardlist; npth_mutex_lock (&card_list_lock); for (n=0, c = card_top; c; c = c->next) n++; cardlist = xtrycalloc (n, sizeof *cardlist); if (!cardlist) { err = gpg_error_from_syserror (); goto leave; } for (ncardlist=0, c = card_top; c; c = c->next) cardlist[ncardlist++] = c; qsort (cardlist, ncardlist, sizeof *cardlist, compare_card_list_items); for (n=0; n < ncardlist; n++) { if (DIM (buf) < 2 * cardlist[n]->serialnolen + 1) continue; bin2hex (cardlist[n]->serialno, cardlist[n]->serialnolen, buf); send_status_direct (ctrl, "SERIALNO", buf); } err = 0; leave: npth_mutex_unlock (&card_list_lock); xfree (cardlist); return err; } /* Execute an action for each app. ACTION can be one of: * * - KEYGRIP_ACTION_SEND_DATA * * If KEYGRIP_STR matches a public key of any active application * send information as LF terminated data lines about the public * key. The format of these lines is * T * If a match was found a pointer to the matching application is * returned. With the KEYGRIP_STR given as NULL, lines for all * keys will be send and the return value is NULL. * * - KEYGRIP_ACTION_WRITE_STATUS * * Same as KEYGRIP_ACTION_SEND_DATA but uses status lines instead * of data lines. * * - KEYGRIP_ACTION_LOOKUP * * Returns a pointer to the application matching KEYGRIP_STR but * does not emit any status or data lines. If no key with that * keygrip is available or KEYGRIP_STR is NULL, NULL is returned. */ card_t app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str) { gpg_error_t err; card_t c; app_t a; npth_mutex_lock (&card_list_lock); for (c = card_top; c; c = c->next) for (a = c->app; a; a = a->next) if (a->fnc.with_keygrip) { if (!lock_card (c, ctrl)) { err = a->fnc.with_keygrip (a, ctrl, action, keygrip_str); unlock_card (c); if (!err) goto leave_the_loop; } } leave_the_loop: /* FIXME: Add app switching logic. The above code assumes that the * actions can be performend without switching. This needs to be * checked. */ /* Force switching of the app if the selected one is not the current * one. Changing the current apptype is sufficient to do this. */ if (c && c->app && c->app->apptype != a->apptype) ctrl->current_apptype = a->apptype; npth_mutex_unlock (&card_list_lock); return c; } diff --git a/scd/command.c b/scd/command.c index d6962a946..1baa9670d 100644 --- a/scd/command.c +++ b/scd/command.c @@ -1,2285 +1,2287 @@ /* command.c - SCdaemon command handler * Copyright (C) 2001, 2002, 2003, 2004, 2005, * 2007, 2008, 2009, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #ifdef USE_NPTH # include #endif #include "scdaemon.h" #include #include #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" #ifdef HAVE_LIBUSB #include "ccid-driver.h" #endif #include "../common/asshelp.h" #include "../common/server-help.h" /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ #define MAXLEN_PIN 100 /* Maximum allowed size of key data as used in inquiries. */ #define MAXLEN_KEYDATA 4096 /* Maximum allowed total data size for SETDATA. */ #define MAXLEN_SETDATA 4096 /* Maximum allowed size of certificate data as used in inquiries. */ #define MAXLEN_CERTDATA 16384 /* Maximum allowed size for "SETATTR --inquire". */ #define MAXLEN_SETATTRDATA 16384 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) #define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local) /* Data used to associate an Assuan context with local server data. This object describes the local properties of one session. */ struct server_local_s { /* We keep a list of all active sessions with the anchor at SESSION_LIST (see below). This field is used for linking. */ struct server_local_s *next_session; /* This object is usually assigned to a CTRL object (which is globally visible). While enumerating all sessions we sometimes need to access data of the CTRL object; thus we keep a backpointer here. */ ctrl_t ctrl_backlink; /* The Assuan context used by this session/server. */ assuan_context_t assuan_ctx; #ifdef HAVE_W32_SYSTEM void *event_signal; /* Or NULL if not used. */ #else int event_signal; /* Or 0 if not used. */ #endif /* True if the card has been removed and a reset is required to continue operation. */ int card_removed; /* If set to true we will be terminate ourself at the end of the this session. */ int stopme; }; /* To keep track of all running sessions, we link all active server contexts and the anchor in this variable. */ static struct server_local_s *session_list; /* If a session has been locked we store a link to its server object in this variable. */ static struct server_local_s *locked_session; /* Convert the STRING into a newly allocated buffer while translating the hex numbers. Stops at the first invalid character. Blanks and colons are allowed to separate the hex digits. Returns NULL on error or a newly malloced buffer and its length in LENGTH. */ static unsigned char * hex_to_buffer (const char *string, size_t *r_length) { unsigned char *buffer; const char *s; size_t n; buffer = xtrymalloc (strlen (string)+1); if (!buffer) return NULL; for (s=string, n=0; *s; s++) { if (spacep (s) || *s == ':') continue; if (hexdigitp (s) && hexdigitp (s+1)) { buffer[n++] = xtoi_2 (s); s++; } else break; } *r_length = n; return buffer; } /* Reset the card and free the application context. With SEND_RESET set to true actually send a RESET to the reader; this is the normal way of calling the function. */ static void do_reset (ctrl_t ctrl, int send_reset) { card_t card = ctrl->card_ctx; if (card) card_reset (card, ctrl, IS_LOCKED (ctrl)? 0: send_reset); /* If we hold a lock, unlock now. */ if (locked_session && ctrl->server_local == locked_session) { locked_session = NULL; log_info ("implicitly unlocking due to RESET\n"); } } static gpg_error_t reset_notify (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); (void) line; do_reset (ctrl, 1); return 0; } static gpg_error_t option_handler (assuan_context_t ctx, const char *key, const char *value) { ctrl_t ctrl = assuan_get_pointer (ctx); if (!strcmp (key, "event-signal")) { /* A value of 0 is allowed to reset the event signal. */ #ifdef HAVE_W32_SYSTEM if (!*value) return gpg_error (GPG_ERR_ASS_PARAMETER); #ifdef _WIN64 ctrl->server_local->event_signal = (void *)strtoull (value, NULL, 16); #else ctrl->server_local->event_signal = (void *)strtoul (value, NULL, 16); #endif #else int i = *value? atoi (value) : -1; if (i < 0) return gpg_error (GPG_ERR_ASS_PARAMETER); ctrl->server_local->event_signal = i; #endif } return 0; } /* If the card has not yet been opened, do it. */ static gpg_error_t open_card (ctrl_t ctrl) { /* If we ever got a card not present error code, return that. Only the SERIALNO command and a reset are able to clear from that state. */ if (ctrl->server_local->card_removed) return gpg_error (GPG_ERR_CARD_REMOVED); if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); if (ctrl->card_ctx) return 0; return select_application (ctrl, NULL, &ctrl->card_ctx, 0, NULL, 0); } /* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */ static gpg_error_t open_card_with_request (ctrl_t ctrl, const char *apptypestr, const char *serialno) { gpg_error_t err; unsigned char *serialno_bin = NULL; size_t serialno_bin_len = 0; card_t card = ctrl->card_ctx; if (serialno) serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); /* If we are already initialized for one specific application we need to check that the client didn't requested a specific application different from the one in use before we continue. */ if (apptypestr && ctrl->card_ctx) { err = check_application_conflict (ctrl->card_ctx, apptypestr, serialno_bin, serialno_bin_len); if (gpg_err_code (err) == GPG_ERR_FALSE) { /* Different application but switching is supported. */ err = select_additional_application (ctrl, apptypestr); } goto leave; } /* Re-scan USB devices. Release CARD, before the scan. */ /* FIXME: Is a card_unref sufficient or do we need to deallocate? */ ctrl->card_ctx = NULL; ctrl->current_apptype = APPTYPE_NONE; card_unref (card); err = select_application (ctrl, apptypestr, &ctrl->card_ctx, 1, serialno_bin, serialno_bin_len); leave: xfree (serialno_bin); return err; } static const char hlp_serialno[] = "SERIALNO [--demand=] []\n" "\n" "Return the serial number of the card using a status response. This\n" "function should be used to check for the presence of a card.\n" "\n" "If --demand is given, an application on the card with SERIALNO is\n" "selected and an error is returned if no such card available.\n" "\n" "If APPTYPE is given, an application of that type is selected and an\n" "error is returned if the application is not supported or available.\n" "The default is to auto-select the application using a hardwired\n" "preference system. Note, that a future extension to this function\n" "may enable specifying a list and order of applications to try.\n" "\n" "This function is special in that it can be used to reset the card.\n" "Most other functions will return an error when a card change has\n" "been detected and the use of this function is therefore required.\n" "\n" "Background: We want to keep the client clear of handling card\n" "changes between operations; i.e. the client can assume that all\n" "operations are done on the same card unless he calls this function."; static gpg_error_t cmd_serialno (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); struct server_local_s *sl; int rc = 0; char *serial; const char *demand; if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); if ((demand = has_option_name (line, "--demand"))) { if (*demand != '=') return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option"); line = (char *)++demand; for (; *line && !spacep (line); line++) ; if (*line) *line++ = 0; } else demand = NULL; /* Clear the remove flag so that the open_card is able to reread it. */ if (ctrl->server_local->card_removed) ctrl->server_local->card_removed = 0; if ((rc = open_card_with_request (ctrl, *line? line:NULL, demand))) { ctrl->server_local->card_removed = 1; return rc; } /* Success, clear the card_removed flag for all sessions. */ for (sl=session_list; sl; sl = sl->next_session) { ctrl_t c = sl->ctrl_backlink; if (c != ctrl) c->server_local->card_removed = 0; } serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_write_status (ctx, "SERIALNO", serial); xfree (serial); return rc; } static const char hlp_learn[] = "LEARN [--force] [--keypairinfo]\n" "\n" "Learn all useful information of the currently inserted card. When\n" "used without the force options, the command might do an INQUIRE\n" "like this:\n" "\n" " INQUIRE KNOWNCARDP \n" "\n" "The client should just send an \"END\" if the processing should go on\n" "or a \"CANCEL\" to force the function to terminate with a Cancel\n" "error message.\n" "\n" "With the option --keypairinfo only KEYPARIINFO status lines are\n" "returned.\n" "\n" "The response of this command is a list of status lines formatted as\n" "this:\n" "\n" " S APPTYPE \n" "\n" "This returns the type of the application, currently the strings:\n" "\n" " P15 = PKCS-15 structure used\n" " DINSIG = DIN SIG\n" " OPENPGP = OpenPGP card\n" " PIV = PIV card\n" " NKS = NetKey card\n" "\n" "are implemented. These strings are aliases for the AID\n" "\n" " S KEYPAIRINFO []\n" "\n" "If there is no certificate yet stored on the card a single 'X' is\n" "returned as the keygrip. In addition to the keypair info, information\n" "about all certificates stored on the card is also returned:\n" "\n" " S CERTINFO \n" "\n" "Where CERTTYPE is a number indicating the type of certificate:\n" " 0 := Unknown\n" " 100 := Regular X.509 cert\n" " 101 := Trusted X.509 cert\n" " 102 := Useful X.509 cert\n" " 110 := Root CA cert in a special format (e.g. DINSIG)\n" " 111 := Root CA cert as standard X509 cert.\n" "\n" "For certain cards, more information will be returned:\n" "\n" " S KEY-FPR \n" "\n" "For OpenPGP cards this returns the stored fingerprints of the\n" "keys. This can be used check whether a key is available on the\n" "card. NO may be 1, 2 or 3.\n" "\n" " S CA-FPR \n" "\n" "Similar to above, these are the fingerprints of keys assumed to be\n" "ultimately trusted.\n" "\n" " S DISP-NAME \n" "\n" "The name of the card holder as stored on the card; percent\n" "escaping takes place, spaces are encoded as '+'\n" "\n" " S PUBKEY-URL \n" "\n" "The URL to be used for locating the entire public key.\n" " \n" "Note, that this function may even be used on a locked card."; static gpg_error_t cmd_learn (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc = 0; int only_keypairinfo = has_option (line, "--keypairinfo"); if ((rc = open_card (ctrl))) return rc; /* Unless the force option is used we try a shortcut by identifying the card using a serial number and inquiring the client with that. The client may choose to cancel the operation if he already knows about this card */ if (!only_keypairinfo) { const char *reader; char *serial; card_t card = ctrl->card_ctx; if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); reader = apdu_get_reader_name (card->slot); if (!reader) return out_of_core (); send_status_direct (ctrl, "READER", reader); /* No need to free the string of READER. */ serial = card_get_serialno (ctrl->card_ctx); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_write_status (ctx, "SERIALNO", serial); if (rc < 0) { xfree (serial); return out_of_core (); } if (!has_option (line, "--force")) { char *command; rc = gpgrt_asprintf (&command, "KNOWNCARDP %s", serial); if (rc < 0) { xfree (serial); return out_of_core (); } rc = assuan_inquire (ctx, command, NULL, NULL, 0); xfree (command); if (rc) { if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED) log_error ("inquire KNOWNCARDP failed: %s\n", gpg_strerror (rc)); xfree (serial); return rc; } /* Not canceled, so we have to proceed. */ } xfree (serial); } /* Let the application print out its collection of useful status information. */ if (!rc) - rc = app_write_learn_status (ctrl->card_ctx, ctrl, only_keypairinfo); + rc = app_write_learn_status + (ctrl->card_ctx, ctrl, + (only_keypairinfo? APP_LEARN_FLAG_KEYPAIRINFO : 0)); return rc; } static const char hlp_readcert[] = "READCERT ||\n" "\n" "Note, that this function may even be used on a locked card."; static gpg_error_t cmd_readcert (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; unsigned char *cert; size_t ncert; if ((rc = open_card (ctrl))) return rc; line = xstrdup (line); /* Need a copy of the line. */ rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); xfree (line); line = NULL; if (!rc) { rc = assuan_send_data (ctx, cert, ncert); xfree (cert); if (rc) return rc; } return rc; } static const char hlp_readkey[] = "READKEY [--advanced] [--info[-only]] |\n" "\n" "Return the public key for the given cert or key ID as a standard\n" "S-expression. With --advanced the S-expression is returned in\n" "advanced format. With --info a KEYPAIRINFO status line is also\n" "emitted; with --info-only the regular output is suppressed."; static gpg_error_t cmd_readkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; int advanced = 0; int opt_info = 0; int opt_nokey = 0; unsigned char *cert = NULL; unsigned char *pk = NULL; size_t ncert, pklen; if ((rc = open_card (ctrl))) return rc; if (has_option (line, "--advanced")) advanced = 1; if (has_option (line, "--info")) opt_info = 1; if (has_option (line, "--info-only")) opt_info = opt_nokey = 1; line = skip_options (line); line = xstrdup (line); /* Need a copy of the line. */ /* If the application supports the READKEY function we use that. Otherwise we use the old way by extracting it from the certificate. */ rc = app_readkey (ctrl->card_ctx, ctrl, line, opt_info? APP_READKEY_FLAG_INFO : 0, opt_nokey? NULL : &pk, &pklen); if (!rc) ; /* Okay, got that key. */ else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { /* Fall back to certificate reading. */ rc = app_readcert (ctrl->card_ctx, ctrl, line, &cert, &ncert); if (rc) { log_error ("app_readcert failed: %s\n", gpg_strerror (rc)); goto leave; } rc = app_help_pubkey_from_cert (cert, ncert, &pk, &pklen); if (rc) { log_error ("failed to parse the certificate: %s\n", gpg_strerror (rc)); goto leave; } if (opt_info) { char keygripstr[KEYGRIP_LEN*2+1]; rc = app_help_get_keygrip_string_pk (pk, pklen, keygripstr); if (rc) { log_error ("app_help_get_keygrip_string failed: %s\n", gpg_strerror (rc)); goto leave; } /* FIXME: Using LINE is not correct because it might be an * OID and has not been canonicalized (i.e. uppercased). */ send_status_info (ctrl, "KEYPAIRINFO", keygripstr, strlen (keygripstr), line, strlen (line), NULL, (size_t)0); } } else { log_error ("app_readkey failed: %s\n", gpg_strerror (rc)); goto leave; } if (opt_nokey) ; else if (advanced) { gcry_sexp_t s_key; unsigned char *pkadv; size_t pkadvlen; rc = gcry_sexp_new (&s_key, pk, pklen, 0); if (rc) goto leave; pkadvlen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, NULL, 0); pkadv = xtrymalloc (pkadvlen); if (!pkadv) { rc = gpg_error_from_syserror (); goto leave; } log_assert (pkadvlen); gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, pkadv, pkadvlen); gcry_sexp_release (s_key); /* (One less to adjust for the trailing '\0') */ rc = assuan_send_data (ctx, pkadv, pkadvlen-1); xfree (pkadv); } else rc = assuan_send_data (ctx, pk, pklen); leave: xfree (pk); xfree (cert); return rc; } static const char hlp_setdata[] = "SETDATA [--append] \n" "\n" "The client should use this command to tell us the data he want to sign.\n" "With the option --append, the data is appended to the data set by a\n" "previous SETDATA command."; static gpg_error_t cmd_setdata (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int append; int n, i, off; char *p; unsigned char *buf; append = (ctrl->in_data.value && has_option (line, "--append")); line = skip_options (line); if (locked_session && locked_session != ctrl->server_local) return gpg_error (GPG_ERR_LOCKED); /* Parse the hexstring. */ for (p=line,n=0; hexdigitp (p); p++, n++) ; if (*p) return set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring"); if (!n) return set_error (GPG_ERR_ASS_PARAMETER, "no data given"); if ((n&1)) return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits"); n /= 2; if (append) { if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA) return set_error (GPG_ERR_TOO_LARGE, "limit on total size of data reached"); buf = xtrymalloc (ctrl->in_data.valuelen + n); } else buf = xtrymalloc (n); if (!buf) return out_of_core (); if (append) { memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen); off = ctrl->in_data.valuelen; } else off = 0; for (p=line, i=0; i < n; p += 2, i++) buf[off+i] = xtoi_2 (p); xfree (ctrl->in_data.value); ctrl->in_data.value = buf; ctrl->in_data.valuelen = off+n; return 0; } static gpg_error_t pin_cb (void *opaque, const char *info, char **retstr) { assuan_context_t ctx = opaque; char *command; int rc; unsigned char *value; size_t valuelen; if (!retstr) { /* We prompt for pinpad entry. To make sure that the popup has been show we use an inquire and not just a status message. We ignore any value returned. */ if (info) { log_debug ("prompting for pinpad entry '%s'\n", info); rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info); if (rc < 0) return gpg_error (gpg_err_code_from_errno (errno)); rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); xfree (command); } else { log_debug ("dismiss pinpad entry prompt\n"); rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT", &value, &valuelen, MAXLEN_PIN); } if (!rc) xfree (value); return rc; } *retstr = NULL; log_debug ("asking for PIN '%s'\n", info); rc = gpgrt_asprintf (&command, "NEEDPIN %s", info); if (rc < 0) return gpg_error (gpg_err_code_from_errno (errno)); /* Fixme: Write an inquire function which returns the result in secure memory and check all further handling of the PIN. */ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); xfree (command); if (rc) return rc; if (!valuelen || value[valuelen-1]) { /* We require that the returned value is an UTF-8 string */ xfree (value); return gpg_error (GPG_ERR_INV_RESPONSE); } *retstr = (char*)value; return 0; } static const char hlp_pksign[] = "PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] \n" "\n" "The --hash option is optional; the default is SHA1."; static gpg_error_t cmd_pksign (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; unsigned char *outdata; size_t outdatalen; char *keyidstr; int hash_algo; card_t card; int direct = 0; if (has_option (line, "--hash=rmd160")) hash_algo = GCRY_MD_RMD160; else if (has_option (line, "--hash=sha1")) hash_algo = GCRY_MD_SHA1; else if (has_option (line, "--hash=sha224")) hash_algo = GCRY_MD_SHA224; else if (has_option (line, "--hash=sha256")) hash_algo = GCRY_MD_SHA256; else if (has_option (line, "--hash=sha384")) hash_algo = GCRY_MD_SHA384; else if (has_option (line, "--hash=sha512")) hash_algo = GCRY_MD_SHA512; else if (has_option (line, "--hash=md5")) hash_algo = GCRY_MD_MD5; else if (!strstr (line, "--")) hash_algo = GCRY_MD_SHA1; else return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); line = skip_options (line); if ((rc = open_card (ctrl))) return rc; /* We have to use a copy of the key ID because the function may use the pin_cb which in turn uses the assuan line buffer and thus overwriting the original line with the keyid */ keyidstr = xtrystrdup (line); if (!keyidstr) return out_of_core (); /* When it's a keygrip, we directly use the card, with no change of ctrl->card_ctx. */ if (strlen (keyidstr) == 40) { card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); direct = 1; } else card = ctrl->card_ctx; if (card) { if (direct) card_ref (card); rc = app_sign (card, ctrl, keyidstr, hash_algo, pin_cb, ctx, ctrl->in_data.value, ctrl->in_data.valuelen, &outdata, &outdatalen); if (direct) card_unref (card); } else rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) { log_error ("app_sign failed: %s\n", gpg_strerror (rc)); } else { rc = assuan_send_data (ctx, outdata, outdatalen); xfree (outdata); if (rc) return rc; /* that is already an assuan error code */ } return rc; } static const char hlp_pkauth[] = "PKAUTH "; static gpg_error_t cmd_pkauth (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; unsigned char *outdata; size_t outdatalen; char *keyidstr; card_t card; int direct = 0; if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use the pin_cb which in turn uses the assuan line buffer and thus overwriting the original line with the keyid */ keyidstr = xtrystrdup (line); if (!keyidstr) return out_of_core (); /* When it's a keygrip, we directly use CARD, with no change of ctrl->card_ctx. */ if (strlen (keyidstr) == 40) { card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); direct = 1; } else card = ctrl->card_ctx; if (card) { if (direct) card_ref (card); rc = app_auth (card, ctrl, keyidstr, pin_cb, ctx, ctrl->in_data.value, ctrl->in_data.valuelen, &outdata, &outdatalen); if (direct) card_unref (card); } else rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) { log_error ("app_auth failed: %s\n", gpg_strerror (rc)); } else { rc = assuan_send_data (ctx, outdata, outdatalen); xfree (outdata); if (rc) return rc; /* that is already an assuan error code */ } return rc; } static const char hlp_pkdecrypt[] = "PKDECRYPT "; static gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; unsigned char *outdata; size_t outdatalen; char *keyidstr; unsigned int infoflags; card_t card; int direct = 0; if ((rc = open_card (ctrl))) return rc; keyidstr = xtrystrdup (line); if (!keyidstr) return out_of_core (); /* When it's a keygrip, we directly use CARD, with no change of ctrl->card_ctx. */ if (strlen (keyidstr) == 40) { card = app_do_with_keygrip (ctrl, KEYGRIP_ACTION_LOOKUP, keyidstr); direct = 1; } else card = ctrl->card_ctx; if (card) { if (direct) card_ref (card); rc = app_decipher (card, ctrl, keyidstr, pin_cb, ctx, ctrl->in_data.value, ctrl->in_data.valuelen, &outdata, &outdatalen, &infoflags); if (direct) card_unref (card); } else rc = gpg_error (GPG_ERR_NO_SECKEY); xfree (keyidstr); if (rc) { log_error ("app_decipher failed: %s\n", gpg_strerror (rc)); } else { /* If the card driver told us that there is no padding, send a status line. If there is a padding it is assumed that the caller knows what padding is used. It would have been better to always send that information but for backward compatibility we can't do that. */ if ((infoflags & APP_DECIPHER_INFO_NOPAD)) send_status_direct (ctrl, "PADDING", "0"); rc = assuan_send_data (ctx, outdata, outdatalen); xfree (outdata); if (rc) return rc; /* that is already an assuan error code */ } return rc; } static const char hlp_getattr[] = "GETATTR \n" "\n" "This command is used to retrieve data from a smartcard. The\n" "allowed names depend on the currently selected smartcard\n" "application. NAME must be percent and '+' escaped. The value is\n" "returned through status message, see the LEARN command for details.\n" "\n" "However, the current implementation assumes that Name is not escaped;\n" "this works as long as no one uses arbitrary escaping. \n" "\n" "Note, that this function may even be used on a locked card."; static gpg_error_t cmd_getattr (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; const char *keyword; if ((rc = open_card (ctrl))) return rc; keyword = line; for (; *line && !spacep (line); line++) ; if (*line) *line++ = 0; /* (We ignore any garbage for now.) */ /* FIXME: Applications should not return sensitive data if the card is locked. */ rc = app_getattr (ctrl->card_ctx, ctrl, keyword); return rc; } static const char hlp_setattr[] = "SETATTR [--inquire] \n" "\n" "This command is used to store data on a smartcard. The allowed\n" "names and values are depend on the currently selected smartcard\n" "application. NAME and VALUE must be percent and '+' escaped.\n" "\n" "However, the current implementation assumes that NAME is not\n" "escaped; this works as long as no one uses arbitrary escaping.\n" "\n" "If the option --inquire is used, VALUE shall not be given; instead\n" "an inquiry using the keyword \"VALUE\" is used to retrieve it. The\n" "value is in this case considered to be confidential and not logged.\n" "\n" "A PIN will be requested for most NAMEs. See the corresponding\n" "setattr function of the actually used application (app-*.c) for\n" "details."; static gpg_error_t cmd_setattr (assuan_context_t ctx, char *orig_line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; char *keyword; int keywordlen; size_t nbytes; char *line, *linebuf; int opt_inquire; opt_inquire = has_option (orig_line, "--inquire"); orig_line = skip_options (orig_line); if ((err = open_card (ctrl))) return err; /* We need to use a copy of LINE, because PIN_CB uses the same context and thus reuses the Assuan provided LINE. */ line = linebuf = xtrystrdup (orig_line); if (!line) return out_of_core (); keyword = line; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; if (*line) *line++ = 0; while (spacep (line)) line++; if (opt_inquire) { unsigned char *value; assuan_begin_confidential (ctx); err = assuan_inquire (ctx, "VALUE", &value, &nbytes, MAXLEN_SETATTRDATA); assuan_end_confidential (ctx); if (!err) { err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, value, nbytes); wipememory (value, nbytes); xfree (value); } } else { nbytes = percent_plus_unescape_inplace (line, 0); err = app_setattr (ctrl->card_ctx, ctrl, keyword, pin_cb, ctx, (const unsigned char*)line, nbytes); } xfree (linebuf); return err; } static const char hlp_writecert[] = "WRITECERT \n" "\n" "This command is used to store a certificate on a smartcard. The\n" "allowed certids depend on the currently selected smartcard\n" "application. The actual certifciate is requested using the inquiry\n" "\"CERTDATA\" and needs to be provided in its raw (e.g. DER) form.\n" "\n" "In almost all cases a PIN will be requested. See the related\n" "writecert function of the actually used application (app-*.c) for\n" "details."; static gpg_error_t cmd_writecert (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; char *certid; unsigned char *certdata; size_t certdatalen; line = skip_options (line); if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no certid given"); certid = line; while (*line && !spacep (line)) line++; *line = 0; if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); certid = xtrystrdup (certid); if (!certid) return out_of_core (); /* Now get the actual keydata. */ rc = assuan_inquire (ctx, "CERTDATA", &certdata, &certdatalen, MAXLEN_CERTDATA); if (rc) { xfree (certid); return rc; } /* Write the certificate to the card. */ rc = app_writecert (ctrl->card_ctx, ctrl, certid, pin_cb, ctx, certdata, certdatalen); xfree (certid); xfree (certdata); return rc; } static const char hlp_writekey[] = "WRITEKEY [--force] \n" "\n" "This command is used to store a secret key on a smartcard. The\n" "allowed keyids depend on the currently selected smartcard\n" "application. The actual keydata is requested using the inquiry\n" "\"KEYDATA\" and need to be provided without any protection. With\n" "--force set an existing key under this KEYID will get overwritten.\n" "The keydata is expected to be the usual canonical encoded\n" "S-expression.\n" "\n" "A PIN will be requested for most NAMEs. See the corresponding\n" "writekey function of the actually used application (app-*.c) for\n" "details."; static gpg_error_t cmd_writekey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; char *keyid; int force = has_option (line, "--force"); unsigned char *keydata; size_t keydatalen; line = skip_options (line); if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given"); keyid = line; while (*line && !spacep (line)) line++; *line = 0; if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyid = xtrystrdup (keyid); if (!keyid) return out_of_core (); /* Now get the actual keydata. */ assuan_begin_confidential (ctx); rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA); assuan_end_confidential (ctx); if (rc) { xfree (keyid); return rc; } /* Write the key to the card. */ rc = app_writekey (ctrl->card_ctx, ctrl, keyid, force? 1:0, pin_cb, ctx, keydata, keydatalen); xfree (keyid); xfree (keydata); return rc; } static const char hlp_genkey[] = "GENKEY [--force] [--timestamp=] \n" "\n" "Generate a key on-card identified by , 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 \n" " S KEY-CREATED-AT \n" " S KEY-DATA [-|p|n] \n" "\n" " 'p' and 'n' are the names of the RSA parameters; '-' is used to\n" " indicate that HEXDATA is the first chunk of a parameter given\n" " by the next KEY-DATA. Only used by GnuPG version < 2.1.\n" "\n" "--force is required to overwrite an already existing key. The\n" "KEY-CREATED-AT is required for further processing because it is\n" "part of the hashed key material for the fingerprint.\n" "\n" "If --timestamp is given an OpenPGP key will be created using this\n" "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 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); 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"); if ((s=has_option_name (line, "--timestamp"))) { if (*s != '=') return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option"); timestamp = isotime2epoch (s+1); if (timestamp < 1) return set_error (GPG_ERR_ASS_PARAMETER, "invalid time value"); } else timestamp = 0; err = get_option_value (line, "--algo", &opt_algo); if (err) goto leave; line = skip_options (line); if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no key number given"); keyref = line; while (*line && !spacep (line)) line++; *line = 0; if ((err = open_card (ctrl))) goto leave; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); keyref = keyref_buffer = xtrystrdup (keyref); if (!keyref) { err = gpg_error_from_syserror (); goto leave; } err = app_genkey (ctrl->card_ctx, ctrl, keyref, opt_algo, force? APP_GENKEY_FLAG_FORCE : 0, timestamp, pin_cb, ctx); leave: xfree (keyref_buffer); xfree (opt_algo); return err; } static const char hlp_random[] = "RANDOM \n" "\n" "Get NBYTES of random from the card and send them back as data.\n" "This usually involves EEPROM write on the card and thus excessive\n" "use of this command may destroy the card.\n" "\n" "Note, that this function may be even be used on a locked card."; static gpg_error_t cmd_random (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; size_t nbytes; unsigned char *buffer; if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "number of requested bytes missing"); nbytes = strtoul (line, NULL, 0); if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); buffer = xtrymalloc (nbytes); if (!buffer) return out_of_core (); rc = app_get_challenge (ctrl->card_ctx, ctrl, nbytes, buffer); if (!rc) { rc = assuan_send_data (ctx, buffer, nbytes); xfree (buffer); return rc; /* that is already an assuan error code */ } xfree (buffer); return rc; } static const char hlp_passwd[] = "PASSWD [--reset] [--nullpin] [--clear] \n" "\n" "Change the PIN or, if --reset is given, reset the retry counter of\n" "the card holder verification vector CHVNO. The option --nullpin is\n" "used for TCOS cards to set the initial PIN. The option --clear clears\n" "the security status associated with the PIN so that the PIN needs to\n" "be presented again. The format of CHVNO depends on the card application."; static gpg_error_t cmd_passwd (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; char *chvnostr; unsigned int flags = 0; if (has_option (line, "--reset")) flags |= APP_CHANGE_FLAG_RESET; if (has_option (line, "--nullpin")) flags |= APP_CHANGE_FLAG_NULLPIN; if (has_option (line, "--clear")) flags |= APP_CHANGE_FLAG_CLEAR; line = skip_options (line); if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given"); chvnostr = line; while (*line && !spacep (line)) line++; *line = 0; /* Do not allow other flags aside of --clear. */ if ((flags & APP_CHANGE_FLAG_CLEAR) && (flags & ~APP_CHANGE_FLAG_CLEAR)) return set_error (GPG_ERR_UNSUPPORTED_OPERATION, "--clear used with other options"); if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); chvnostr = xtrystrdup (chvnostr); if (!chvnostr) return out_of_core (); rc = app_change_pin (ctrl->card_ctx, ctrl, chvnostr, flags, pin_cb, ctx); if (rc) log_error ("command passwd failed: %s\n", gpg_strerror (rc)); xfree (chvnostr); return rc; } static const char hlp_checkpin[] = "CHECKPIN \n" "\n" "Perform a VERIFY operation without doing anything else. This may\n" "be used to initialize a the PIN cache earlier to long lasting\n" "operations. Its use is highly application dependent.\n" "\n" "For OpenPGP:\n" "\n" " Perform a simple verify operation for CHV1 and CHV2, so that\n" " further operations won't ask for CHV2 and it is possible to do a\n" " cheap check on the PIN: If there is something wrong with the PIN\n" " entry system, only the regular CHV will get blocked and not the\n" " dangerous CHV3. IDSTR is the usual card's serial number in hex\n" " notation; an optional fingerprint part will get ignored. There\n" " is however a special mode if the IDSTR is sffixed with the\n" " literal string \"[CHV3]\": In this case the Admin PIN is checked\n" " if and only if the retry counter is still at 3.\n" "\n" "For Netkey:\n" "\n" " Any of the valid PIN Ids may be used. These are the strings:\n" "\n" " PW1.CH - Global password 1\n" " PW2.CH - Global password 2\n" " PW1.CH.SIG - SigG password 1\n" " PW2.CH.SIG - SigG password 2\n" "\n" " For a definitive list, see the implementation in app-nks.c.\n" " Note that we call a PW2.* PIN a \"PUK\" despite that since TCOS\n" " 3.0 they are technically alternative PINs used to mutally\n" " unblock each other."; static gpg_error_t cmd_checkpin (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; char *idstr; if ((rc = open_card (ctrl))) return rc; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); /* We have to use a copy of the key ID because the function may use the pin_cb which in turn uses the assuan line buffer and thus overwriting the original line with the keyid. */ idstr = xtrystrdup (line); if (!idstr) return out_of_core (); rc = app_check_pin (ctrl->card_ctx, ctrl, idstr, pin_cb, ctx); xfree (idstr); if (rc) log_error ("app_check_pin failed: %s\n", gpg_strerror (rc)); return rc; } static const char hlp_lock[] = "LOCK [--wait]\n" "\n" "Grant exclusive card access to this session. Note that there is\n" "no lock counter used and a second lock from the same session will\n" "be ignored. A single unlock (or RESET) unlocks the session.\n" "Return GPG_ERR_LOCKED if another session has locked the reader.\n" "\n" "If the option --wait is given the command will wait until a\n" "lock has been released."; static gpg_error_t cmd_lock (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc = 0; retry: if (locked_session) { if (locked_session != ctrl->server_local) rc = gpg_error (GPG_ERR_LOCKED); } else locked_session = ctrl->server_local; #ifdef USE_NPTH if (rc && has_option (line, "--wait")) { rc = 0; npth_sleep (1); /* Better implement an event mechanism. However, for card operations this should be sufficient. */ /* FIXME: Need to check that the connection is still alive. This can be done by issuing status messages. */ goto retry; } #endif /*USE_NPTH*/ if (rc) log_error ("cmd_lock failed: %s\n", gpg_strerror (rc)); return rc; } static const char hlp_unlock[] = "UNLOCK\n" "\n" "Release exclusive card access."; static gpg_error_t cmd_unlock (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc = 0; (void)line; if (locked_session) { if (locked_session != ctrl->server_local) rc = gpg_error (GPG_ERR_LOCKED); else locked_session = NULL; } else rc = gpg_error (GPG_ERR_NOT_LOCKED); if (rc) log_error ("cmd_unlock failed: %s\n", gpg_strerror (rc)); return rc; } static const char hlp_getinfo[] = "GETINFO \n" "\n" "Multi purpose command to return certain information. \n" "Supported values of WHAT are:\n" "\n" " version - Return the version of the program.\n" " pid - Return the process id of the server.\n" " socket_name - Return the name of the socket.\n" " connections - Return number of active connections.\n" " status - Return the status of the current reader (in the future,\n" " may also return the status of all readers). The status\n" " is a list of one-character flags. The following flags\n" " are currently defined:\n" " 'u' Usable card present.\n" " 'r' Card removed. A reset is necessary.\n" " These flags are exclusive.\n" " reader_list - Return a list of detected card readers. Does\n" " currently only work with the internal CCID driver.\n" " deny_admin - Returns OK if admin commands are not allowed or\n" " GPG_ERR_GENERAL if admin commands are allowed.\n" " app_list - Return a list of supported applications. One\n" " application per line, fields delimited by colons,\n" " first field is the name.\n" " card_list - Return a list of serial numbers of active cards,\n" " using a status response."; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { int rc = 0; if (!strcmp (line, "version")) { const char *s = VERSION; rc = assuan_send_data (ctx, s, strlen (s)); } else if (!strcmp (line, "pid")) { char numbuf[50]; snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); } else if (!strcmp (line, "socket_name")) { const char *s = scd_get_socket_name (); if (s) rc = assuan_send_data (ctx, s, strlen (s)); else rc = gpg_error (GPG_ERR_NO_DATA); } else if (!strcmp (line, "connections")) { char numbuf[20]; snprintf (numbuf, sizeof numbuf, "%d", get_active_connection_count ()); rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); } else if (!strcmp (line, "status")) { ctrl_t ctrl = assuan_get_pointer (ctx); char flag; if (open_card (ctrl)) flag = 'r'; else flag = 'u'; rc = assuan_send_data (ctx, &flag, 1); } else if (!strcmp (line, "reader_list")) { #ifdef HAVE_LIBUSB char *s = ccid_get_reader_list (); #else char *s = NULL; #endif if (s) rc = assuan_send_data (ctx, s, strlen (s)); else rc = gpg_error (GPG_ERR_NO_DATA); xfree (s); } else if (!strcmp (line, "deny_admin")) rc = opt.allow_admin? gpg_error (GPG_ERR_GENERAL) : 0; else if (!strcmp (line, "app_list")) { char *s = get_supported_applications (); if (s) rc = assuan_send_data (ctx, s, strlen (s)); else rc = 0; xfree (s); } else if (!strcmp (line, "card_list")) { ctrl_t ctrl = assuan_get_pointer (ctx); rc = app_send_card_list (ctrl); } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); return rc; } static const char hlp_restart[] = "RESTART\n" "\n" "Restart the current connection; this is a kind of warm reset. It\n" "deletes the context used by this connection but does not send a\n" "RESET to the card. Thus the card itself won't get reset. \n" "\n" "This is used by gpg-agent to reuse a primary pipe connection and\n" "may be used by clients to backup from a conflict in the serial\n" "command; i.e. to select another application."; static gpg_error_t cmd_restart (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); card_t card = ctrl->card_ctx; (void)line; if (card) { ctrl->card_ctx = NULL; ctrl->current_apptype = APPTYPE_NONE; card_unref (card); } if (locked_session && ctrl->server_local == locked_session) { locked_session = NULL; log_info ("implicitly unlocking due to RESTART\n"); } return 0; } static const char hlp_disconnect[] = "DISCONNECT\n" "\n" "Disconnect the card if the backend supports a disconnect operation."; static gpg_error_t cmd_disconnect (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); (void)line; if (!ctrl->card_ctx) return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); apdu_disconnect (ctrl->card_ctx->slot); return 0; } static const char hlp_apdu[] = "APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]\n" "\n" "Send an APDU to the current reader. This command bypasses the high\n" "level functions and sends the data directly to the card. HEXSTRING\n" "is expected to be a proper APDU. If HEXSTRING is not given no\n" "commands are set to the card but the command will implictly check\n" "whether the card is ready for use. \n" "\n" "Using the option \"--atr\" returns the ATR of the card as a status\n" "message before any data like this:\n" " S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1\n" "\n" "Using the option --more handles the card status word MORE_DATA\n" "(61xx) and concatenates all responses to one block.\n" "\n" "Using the option \"--exlen\" the returned APDU may use extended\n" "length up to N bytes. If N is not given a default value is used\n" "(currently 4096)."; static gpg_error_t cmd_apdu (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); card_t card; int rc; unsigned char *apdu; size_t apdulen; int with_atr; int handle_more; const char *s; size_t exlen; if (has_option (line, "--dump-atr")) with_atr = 2; else with_atr = has_option (line, "--atr"); handle_more = has_option (line, "--more"); if ((s=has_option_name (line, "--exlen"))) { if (*s == '=') exlen = strtoul (s+1, NULL, 0); else exlen = 4096; } else exlen = 0; line = skip_options (line); if ((rc = open_card (ctrl))) return rc; card = ctrl->card_ctx; if (!card) return gpg_error (GPG_ERR_CARD_NOT_PRESENT); if (with_atr) { unsigned char *atr; size_t atrlen; char hexbuf[400]; atr = apdu_get_atr (card->slot, &atrlen); if (!atr || atrlen > sizeof hexbuf - 2 ) { rc = gpg_error (GPG_ERR_INV_CARD); goto leave; } if (with_atr == 2) { char *string, *p, *pend; string = atr_dump (atr, atrlen); if (string) { for (rc=0, p=string; !rc && (pend = strchr (p, '\n')); p = pend+1) { rc = assuan_send_data (ctx, p, pend - p + 1); if (!rc) rc = assuan_send_data (ctx, NULL, 0); } if (!rc && *p) rc = assuan_send_data (ctx, p, strlen (p)); es_free (string); if (rc) goto leave; } } else { bin2hex (atr, atrlen, hexbuf); send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0); } xfree (atr); } apdu = hex_to_buffer (line, &apdulen); if (!apdu) { rc = gpg_error_from_syserror (); goto leave; } if (apdulen) { unsigned char *result = NULL; size_t resultlen; rc = apdu_send_direct (card->slot, exlen, apdu, apdulen, handle_more, NULL, &result, &resultlen); if (rc) log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc)); else { rc = assuan_send_data (ctx, result, resultlen); xfree (result); } } xfree (apdu); leave: return rc; } static const char hlp_killscd[] = "KILLSCD\n" "\n" "Commit suicide."; static gpg_error_t cmd_killscd (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); (void)line; ctrl->server_local->stopme = 1; assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1); return 0; } static const char hlp_keyinfo[] = "KEYINFO [--list] [--data] \n" "\n" "Return information about the key specified by the KEYGRIP. If the\n" "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" "--list is given the keygrip is ignored and information about all\n" "available keys are returned. Unless --data is given, the\n" "information is returned as a status line using the format:\n" "\n" " KEYINFO T \n" "\n" "KEYGRIP is the keygrip.\n" "\n" "SERIALNO is an ASCII string with the serial number of the\n" " smartcard. If the serial number is not known a single\n" " dash '-' is used instead.\n" "\n" "IDSTR is the IDSTR used to distinguish keys on a smartcard. If it\n" " is not known a dash is used instead.\n" "\n" "More information may be added in the future."; static gpg_error_t cmd_keyinfo (assuan_context_t ctx, char *line) { int list_mode; int opt_data; int action; char *keygrip_str; ctrl_t ctrl = assuan_get_pointer (ctx); card_t card; list_mode = has_option (line, "--list"); opt_data = has_option (line, "--data"); line = skip_options (line); if (list_mode) keygrip_str = NULL; else keygrip_str = line; if (opt_data) action = KEYGRIP_ACTION_SEND_DATA; else action = KEYGRIP_ACTION_WRITE_STATUS; card = app_do_with_keygrip (ctrl, action, keygrip_str); if (!list_mode && !card) return gpg_error (GPG_ERR_NOT_FOUND); return 0; } /* Send a keyinfo string as used by the KEYGRIP_ACTION_SEND_DATA. If * DATA is true the string is emitted as a 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) { char *string; assuan_context_t ctx = ctrl->server_local->assuan_ctx; string = xtryasprintf ("%s T %s %s%s", keygrip_str, serialno? serialno : "-", idstr? idstr : "-", data? "\n" : ""); if (!string) return; if (!data) assuan_write_status (ctx, "KEYINFO", string); else assuan_send_data (ctx, string, strlen (string)); xfree (string); return; } /* Tell the assuan library about our commands */ static int register_commands (assuan_context_t ctx) { static struct { const char *name; assuan_handler_t handler; const char * const help; } table[] = { { "SERIALNO", cmd_serialno, hlp_serialno }, { "LEARN", cmd_learn, hlp_learn }, { "READCERT", cmd_readcert, hlp_readcert }, { "READKEY", cmd_readkey, hlp_readkey }, { "SETDATA", cmd_setdata, hlp_setdata }, { "PKSIGN", cmd_pksign, hlp_pksign }, { "PKAUTH", cmd_pkauth, hlp_pkauth }, { "PKDECRYPT", cmd_pkdecrypt,hlp_pkdecrypt }, { "INPUT", NULL }, { "OUTPUT", NULL }, { "GETATTR", cmd_getattr, hlp_getattr }, { "SETATTR", cmd_setattr, hlp_setattr }, { "WRITECERT", cmd_writecert,hlp_writecert }, { "WRITEKEY", cmd_writekey, hlp_writekey }, { "GENKEY", cmd_genkey, hlp_genkey }, { "RANDOM", cmd_random, hlp_random }, { "PASSWD", cmd_passwd, hlp_passwd }, { "CHECKPIN", cmd_checkpin, hlp_checkpin }, { "LOCK", cmd_lock, hlp_lock }, { "UNLOCK", cmd_unlock, hlp_unlock }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "RESTART", cmd_restart, hlp_restart }, { "DISCONNECT", cmd_disconnect,hlp_disconnect }, { "APDU", cmd_apdu, hlp_apdu }, { "KILLSCD", cmd_killscd, hlp_killscd }, { "KEYINFO", cmd_keyinfo, hlp_keyinfo }, { NULL } }; int i, rc; for (i=0; table[i].name; i++) { rc = assuan_register_command (ctx, table[i].name, table[i].handler, table[i].help); if (rc) return rc; } assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready"); assuan_register_reset_notify (ctx, reset_notify); assuan_register_option_handler (ctx, option_handler); return 0; } /* Startup the server. If FD is given as -1 this is simple pipe server, otherwise it is a regular server. Returns true if there are no more active asessions. */ int scd_command_handler (ctrl_t ctrl, int fd) { int rc; assuan_context_t ctx = NULL; int stopme; rc = assuan_new (&ctx); if (rc) { log_error ("failed to allocate assuan context: %s\n", gpg_strerror (rc)); scd_exit (2); } if (fd == -1) { assuan_fd_t filedes[2]; filedes[0] = assuan_fdopen (0); filedes[1] = assuan_fdopen (1); rc = assuan_init_pipe_server (ctx, filedes); } else { rc = assuan_init_socket_server (ctx, INT2FD(fd), ASSUAN_SOCKET_SERVER_ACCEPTED); } if (rc) { log_error ("failed to initialize the server: %s\n", gpg_strerror(rc)); scd_exit (2); } rc = register_commands (ctx); if (rc) { log_error ("failed to register commands with Assuan: %s\n", gpg_strerror(rc)); scd_exit (2); } assuan_set_pointer (ctx, ctrl); /* Allocate and initialize the server object. Put it into the list of active sessions. */ ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local); ctrl->server_local->next_session = session_list; session_list = ctrl->server_local; ctrl->server_local->ctrl_backlink = ctrl; ctrl->server_local->assuan_ctx = ctx; /* Command processing loop. */ for (;;) { rc = assuan_accept (ctx); if (rc == -1) { break; } else if (rc) { log_info ("Assuan accept problem: %s\n", gpg_strerror (rc)); break; } rc = assuan_process (ctx); if (rc) { log_info ("Assuan processing failed: %s\n", gpg_strerror (rc)); continue; } } /* Cleanup. We don't send an explicit reset to the card. */ do_reset (ctrl, 0); /* Release the server object. */ if (session_list == ctrl->server_local) session_list = ctrl->server_local->next_session; else { struct server_local_s *sl; for (sl=session_list; sl->next_session; sl = sl->next_session) if (sl->next_session == ctrl->server_local) break; if (!sl->next_session) BUG (); sl->next_session = ctrl->server_local->next_session; } stopme = ctrl->server_local->stopme; xfree (ctrl->server_local); ctrl->server_local = NULL; /* Release the Assuan context. */ assuan_release (ctx); if (stopme) scd_exit (0); /* If there are no more sessions return true. */ return !session_list; } /* Send a line with status information via assuan and escape all given buffers. The variable elements are pairs of (char *, size_t), terminated with a (NULL, 0). */ void send_status_info (ctrl_t ctrl, const char *keyword, ...) { va_list arg_ptr; const unsigned char *value; size_t valuelen; char buf[950], *p; size_t n; assuan_context_t ctx = ctrl->server_local->assuan_ctx; va_start (arg_ptr, keyword); p = buf; n = 0; while ( (value = va_arg (arg_ptr, const unsigned char *)) && n < DIM (buf)-2 ) { valuelen = va_arg (arg_ptr, size_t); if (!valuelen) continue; /* empty buffer */ if (n) { *p++ = ' '; n++; } for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++) { if (*value == '+' || *value == '\"' || *value == '%' || *value < ' ') { sprintf (p, "%%%02X", *value); p += 3; n += 2; } else if (*value == ' ') *p++ = '+'; else *p++ = *value; } } *p = 0; assuan_write_status (ctx, keyword, buf); va_end (arg_ptr); } /* Send a ready formatted status line via assuan. */ void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args) { assuan_context_t ctx = ctrl->server_local->assuan_ctx; if (strchr (args, '\n')) log_error ("error: LF detected in status line - not sending\n"); else assuan_write_status (ctx, keyword, args); } /* This status functions expects a printf style format string. No * filtering of the data is done instead the orintf formatted data is * send using assuan_send_status. */ gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...) { gpg_error_t err; va_list arg_ptr; assuan_context_t ctx; if (!ctrl || !ctrl->server_local || !(ctx = ctrl->server_local->assuan_ctx)) return 0; va_start (arg_ptr, format); err = vprint_assuan_status (ctx, keyword, format, arg_ptr); va_end (arg_ptr); return err; } void popup_prompt (void *opaque, int on) { ctrl_t ctrl = opaque; if (ctrl) { assuan_context_t ctx = ctrl->server_local->assuan_ctx; if (ctx) { const char *cmd; gpg_error_t err; unsigned char *value; size_t valuelen; if (on) cmd = "POPUPPINPADPROMPT --ack"; else cmd = "DISMISSPINPADPROMPT"; err = assuan_inquire (ctx, cmd, &value, &valuelen, 100); if (!err) xfree (value); } } } /* Helper to send the clients a status change notification. Note that * this function assumes that APP is already locked. */ void send_client_notifications (card_t card, int removal) { struct { pid_t pid; #ifdef HAVE_W32_SYSTEM HANDLE handle; #else int signo; #endif } killed[50]; int killidx = 0; int kidx; struct server_local_s *sl; for (sl=session_list; sl; sl = sl->next_session) if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card) { pid_t pid; #ifdef HAVE_W32_SYSTEM HANDLE handle; #else int signo; #endif if (removal) { sl->ctrl_backlink->card_ctx = NULL; sl->ctrl_backlink->current_apptype = APPTYPE_NONE; sl->card_removed = 1; card_unref_locked (card); } if (!sl->event_signal || !sl->assuan_ctx) continue; pid = assuan_get_pid (sl->assuan_ctx); #ifdef HAVE_W32_SYSTEM handle = sl->event_signal; for (kidx=0; kidx < killidx; kidx++) if (killed[kidx].pid == pid && killed[kidx].handle == handle) break; if (kidx < killidx) log_info ("event %p (%p) already triggered for client %d\n", sl->event_signal, handle, (int)pid); else { log_info ("triggering event %p (%p) for client %d\n", sl->event_signal, handle, (int)pid); if (!SetEvent (handle)) log_error ("SetEvent(%p) failed: %s\n", sl->event_signal, w32_strerror (-1)); if (killidx < DIM (killed)) { killed[killidx].pid = pid; killed[killidx].handle = handle; killidx++; } } #else /*!HAVE_W32_SYSTEM*/ signo = sl->event_signal; if (pid != (pid_t)(-1) && pid && signo > 0) { for (kidx=0; kidx < killidx; kidx++) if (killed[kidx].pid == pid && killed[kidx].signo == signo) break; if (kidx < killidx) log_info ("signal %d already sent to client %d\n", signo, (int)pid); else { log_info ("sending signal %d to client %d\n", signo, (int)pid); kill (pid, signo); if (killidx < DIM (killed)) { killed[killidx].pid = pid; killed[killidx].signo = signo; killidx++; } } } #endif /*!HAVE_W32_SYSTEM*/ } }