aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2021-04-29 13:26:36 +0000
committerWerner Koch <[email protected]>2021-04-29 13:26:36 +0000
commit7637d39fe20e3a0450095a3b893250cf0fc569e3 (patch)
treebedb838c5fabd436470ae2a8d68137176e0f8dbc
parentcommon: Extend the openpgp_curve_to_oid function. (diff)
downloadgnupg-7637d39fe20e3a0450095a3b893250cf0fc569e3.tar.gz
gnupg-7637d39fe20e3a0450095a3b893250cf0fc569e3.zip
scd:p15: Update from current GnuPG 2.3
-- This reflects the state of commit 1f846823b397d68eaa8a31422f78c99f8e9ff738 featuring these commits: 1f846823b scd:p15: Fix the name of a card. cc5aa68b6 scd:p15: Fix last commit and improve D-TRUST detection. 21e3f750b scd:p15: Shorten the displayed s/n of RSCS cards 30f90fc85 scd:p15: Support attribute KEY-FPR. ecb9265b8 scd:p15: Match private keys with certificates also by ... e17d3f866 scd:p15: New flag APP_LEARN_FLAG_REREAD. 1c16878ef scd: Replace all assert macros by the log_assert macro. 7f9126363 scd:p15: Return labels for keys and certificates. 651c07a73 scd:p15: For CardOS make use of ISO7816_VERIFY_NOT_NEEDED. de4d3c99a scd:p15: Return the creation time of the keys. 592f48011 scd:p15: Make RSA with SHA512 work with CardOS. a494b29af scd:p15: Support ECDSA and ECDH for CardOS. 964363e78 scd:p15: Make $SIGNKEY et al determination more fault ... 85082a83c scd:p15: Allow to use an auth object label with cmd CHECKPIN. ef29a960b scd:p15: New attribute CHV-LABEL. bf1d7bc36 scd:p15: Implement CHV-STATUS attribute 0f191a070 scd:p15: Fix faulty removal of a test code change. 08b5ac492 scd:p15: Support special extended usage flags for OpenPGP ... d51a5ca10 scd:p15: Read out the access flags. cfdaf2bcc scd:p15: Get the label value of all objects for better diag... 33aaa37e5 scd:p15: Make it code work again for D-Trust cards. 488eaedc9 scd:p15: Extract extended usage flagsand act upon them. 0c080ed57 scd:p15: Read PuKDF and minor refactoring. 1e197c29e scd:p15: Make file selection more robust. 5bcbc8cee scd:p15: Factor the commonKeyAttributes parser out. fb84674d6 scd:p15: Factor the commonObjectAttributes parser out. fc287c055 scd:p15: First step towards real CardOS 5 support. 60499d989 scd:p15: Show the ATR as part of the TokenInfo diagnostics. 00037f499 scd:p15: Print the internal card type. c7b9a4ee4 scd:p15: Improve support for some CardOS based cards. Signed-off-by: Werner Koch <[email protected]>
-rw-r--r--scd/app-p15.c3564
1 files changed, 2726 insertions, 838 deletions
diff --git a/scd/app-p15.c b/scd/app-p15.c
index 23d68d0a4..90f6b4c99 100644
--- a/scd/app-p15.c
+++ b/scd/app-p15.c
@@ -1,6 +1,6 @@
/* app-p15.c - The pkcs#15 card application.
* Copyright (C) 2005 Free Software Foundation, Inc.
- * Copyright (C) 2020 g10 Code GmbH
+ * Copyright (C) 2020, 2021 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -34,7 +34,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <time.h>
#include "scdaemon.h"
@@ -42,9 +41,31 @@
#include "iso7816.h"
#include "../common/i18n.h"
#include "../common/tlv.h"
+#include "../common/host2net.h"
+#include "../common/openpgpdefs.h"
#include "apdu.h" /* fixme: we should move the card detection to a
separate file */
+
+static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3";
+static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8";
+static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9";
+static const char oid_kp_ms_documentSigning[] = "1.3.6.1.4.1.311.10.3.12";
+static const char oid_kp_ms_old_documentSigning[] = "1.3.6.1.4.1.311.3.10.3.12";
+
+static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4";
+
+static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1";
+static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2";
+static const char oid_kp_ms_smartcardLogon[] = "1.3.6.1.4.1.311.20.2.2";
+
+static const char oid_kp_anyExtendedKeyUsage[] = "2.5.29.37.0";
+
+static const char oid_kp_gpgUsageCert[] = "1.3.6.1.4.1.11591.2.6.1";
+static const char oid_kp_gpgUsageSign[] = "1.3.6.1.4.1.11591.2.6.2";
+static const char oid_kp_gpgUsageEncr[] = "1.3.6.1.4.1.11591.2.6.3";
+static const char oid_kp_gpgUsageAuth[] = "1.3.6.1.4.1.11591.2.6.4";
+
/* Types of cards we know and which needs special treatment. */
typedef enum
{
@@ -52,6 +73,7 @@ typedef enum
CARD_TYPE_TCOS,
CARD_TYPE_MICARDO,
CARD_TYPE_CARDOS_50,
+ CARD_TYPE_CARDOS_53,
CARD_TYPE_BELPIC /* Belgian eID card specs. */
}
card_type_t;
@@ -62,6 +84,7 @@ card_type_t;
typedef enum
{
CARD_PRODUCT_UNKNOWN,
+ CARD_PRODUCT_RSCS, /* Rohde&Schwarz Cybersecurity */
CARD_PRODUCT_DTRUST /* D-Trust GmbH (bundesdruckerei.de) */
}
card_product_t;
@@ -95,11 +118,20 @@ static struct
CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
{ 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x01\x14"),
CARD_TYPE_CARDOS_50 }, /* CardOS 5.0 */
+ { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x03\x16"),
+ CARD_TYPE_CARDOS_53 }, /* CardOS 5.3 */
{ 0 }
};
#undef X
+/* Macro to test for CardOS 5.0 and 5.3. */
+#define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \
+ || (a)->app_local->card_type == CARD_TYPE_CARDOS_53)
+
+/* The default PKCS-15 home DF */
+#define DEFAULT_HOME_DF 0x5015
+
/* The AID of PKCS15. */
static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
@@ -139,6 +171,31 @@ struct keyusage_flags_s
typedef struct keyusage_flags_s keyusage_flags_t;
+/* A bit array with for the key access flags from the
+ commonKeyAttributes. */
+struct keyaccess_flags_s
+{
+ unsigned int any:1; /* Any access flag set. */
+ unsigned int sensitive:1;
+ unsigned int extractable:1;
+ unsigned int always_sensitive:1;
+ unsigned int never_extractable:1;
+ unsigned int local:1;
+};
+typedef struct keyaccess_flags_s keyaccess_flags_t;
+
+
+/* A bit array with for the gpg usage flags. */
+struct gpgusage_flags_s
+{
+ unsigned int any:1; /* Any of the next flags are set. */
+ unsigned int cert:1; /* 1.3.6.1.4.1.11591.2.6.1 */
+ unsigned int sign:1; /* 1.3.6.1.4.1.11591.2.6.2 */
+ unsigned int encr:1; /* 1.3.6.1.4.1.11591.2.6.3 */
+ unsigned int auth:1; /* 1.3.6.1.4.1.11591.2.6.4 */
+};
+typedef struct gpgusage_flags_s gpgusage_flags_t;
+
/* This is an object to store information about a Certificate
Directory File (CDF) in a format suitable for further processing by
@@ -159,10 +216,17 @@ struct cdf_object_s
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;
+ /* Length and allocated buffer with the authId of this object or
+ NULL if no authID is known. */
+ size_t authidlen;
+ unsigned char *authid;
+
+ /* NULL or the malloced label of this object. */
+ char *label;
+
+ /* To avoid reading and parsing a certificate more than once, we
+ * cache the ksba object. */
+ ksba_cert_t cert;
/* 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. */
@@ -191,25 +255,55 @@ struct prkdf_object_s
unsigned int keygrip_valid:1;
unsigned int key_reference_valid:1;
unsigned int have_off:1;
+ unsigned int have_keytime:1;
/* Flag indicating that the corresponding PIN has already been
- * verified. */
+ * verified. Note that for cards which are able to return the
+ * verification stus, this flag is not used. */
unsigned int pin_verified:1;
+ /* PKCS#15 info whether this is an EC key. Default is RSA. Note
+ * that there is also a KEYALGO field which is derived from the
+ * publick key via Libgcrypt. */
+ unsigned int is_ecc:1;
+
/* The key's usage flags. */
keyusage_flags_t usageflags;
+ /* The key's access flags. */
+ keyaccess_flags_t accessflags;
+
+ /* Extended key usage flags. Only used if .valid is set. This
+ * information is computed from an associated certificate15. */
+ struct {
+ unsigned int valid:1;
+ unsigned int sign:1;
+ unsigned int encr:1;
+ unsigned int auth:1;
+ } extusage;
+
+ /* OpenPGP key features for this key. This is taken from special
+ * extended key usage flags different from those tracked in EXTUSAGE
+ * above. There is also no valid flag as in EXTUSAGE. */
+ gpgusage_flags_t gpgusage;
+
/* The keygrip of the key. This is used as a cache. */
char keygrip[2*KEYGRIP_LEN+1];
+ /* A malloced algorithm string or NULL if not known. */
+ char *keyalgostr;
+
/* The Gcrypt algo identifier for the key. It is valid if the
- * keygrip is also valid. */
+ * keygrip is also valid. See also is_ecc above. */
int keyalgo;
/* The length of the key in bits (e.g. for RSA the length of the
* modulus). It is valid if the keygrip is also valid. */
unsigned int keynbits;
+ /* The creation time of the key or 0 if not known. */
+ u32 keytime;
+
/* Malloced CN from the Subject-DN of the corresponding certificate
* or NULL if not known. */
char *common_name;
@@ -218,6 +312,10 @@ struct prkdf_object_s
* certificate or NULL if not known. */
char *serial_number;
+ /* KDF/KEK parameter for OpenPGP's ECDH. First byte is zero if not
+ * availabale. .*/
+ unsigned char ecdh_kdf[4];
+
/* Length and allocated buffer with the Id of this object. */
size_t objidlen;
unsigned char *objid;
@@ -227,6 +325,9 @@ struct prkdf_object_s
size_t authidlen;
unsigned char *authid;
+ /* NULL or the malloced label of this object. */
+ char *label;
+
/* The keyReference and a flag telling whether it is valid. */
unsigned long key_reference;
@@ -240,6 +341,7 @@ struct prkdf_object_s
unsigned short path[1];
};
typedef struct prkdf_object_s *prkdf_object_t;
+typedef struct prkdf_object_s *pukdf_object_t;
/* This is an object to store information about a Authentication
@@ -264,6 +366,9 @@ struct aodf_object_s
size_t authidlen;
unsigned char *authid;
+ /* NULL or the malloced label of this object. */
+ char *label;
+
/* The file ID of this AODF. */
unsigned short fid;
@@ -334,6 +439,9 @@ struct app_local_s
/* Flag indicating whether we may use direct path selection. */
int direct_path_selection;
+ /* Flag indicating whether the card has any key with a gpgusage set. */
+ int any_gpgusage;
+
/* Structure with the EFIDs of the objects described in the ODF
file. */
struct
@@ -353,9 +461,16 @@ struct app_local_s
unsigned char *serialno;
size_t serialnolen;
- /* The manufacturerID from the TokenInfo EF. Malloced. */
+ /* The manufacturerID from the TokenInfo EF. Malloced or NULL. */
char *manufacturer_id;
+ /* The label from the TokenInfo EF. Malloced or NULL. */
+ char *token_label;
+
+ /* The tokenflags from the TokenInfo EF. Malloced or NULL. */
+ unsigned char *tokenflags;
+ unsigned int tokenflagslen;
+
/* Information on all certificates. */
cdf_object_t certificate_info;
/* Information on all trusted certificates. */
@@ -363,8 +478,11 @@ struct app_local_s
/* Information on all useful certificates. */
cdf_object_t useful_certificate_info;
+ /* Information on all public keys. */
+ prkdf_object_t public_key_info;
+
/* Information on all private keys. */
- prkdf_object_t private_key_info;
+ pukdf_object_t private_key_info;
/* Information on all authentication objects. */
aodf_object_t auth_object_info;
@@ -373,6 +491,8 @@ struct app_local_s
/*** Local prototypes. ***/
+static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path,
+ size_t pathlen);
static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf);
static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
unsigned char **r_cert, size_t *r_certlen);
@@ -380,6 +500,33 @@ static char *get_dispserialno (app_t app, prkdf_object_t prkdf);
static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name);
+
+static const char *
+cardtype2str (card_type_t cardtype)
+{
+ switch (cardtype)
+ {
+ case CARD_TYPE_UNKNOWN: return "";
+ case CARD_TYPE_TCOS: return "TCOS";
+ case CARD_TYPE_MICARDO: return "Micardo";
+ case CARD_TYPE_CARDOS_50: return "CardOS 5.0";
+ case CARD_TYPE_CARDOS_53: return "CardOS 5.3";
+ case CARD_TYPE_BELPIC: return "Belgian eID";
+ }
+ return "";
+}
+
+static const char *
+cardproduct2str (card_product_t cardproduct)
+{
+ switch (cardproduct)
+ {
+ case CARD_PRODUCT_UNKNOWN: return "";
+ case CARD_PRODUCT_RSCS: return "R&S";
+ case CARD_PRODUCT_DTRUST: return "D-Trust";
+ }
+ return "";
+}
/* Release the CDF object A */
static void
@@ -388,8 +535,10 @@ release_cdflist (cdf_object_t a)
while (a)
{
cdf_object_t tmp = a->next;
- xfree (a->image);
+ ksba_free (a->cert);
xfree (a->objid);
+ xfree (a->authid);
+ xfree (a->label);
xfree (a);
a = tmp;
}
@@ -402,15 +551,23 @@ release_prkdflist (prkdf_object_t a)
while (a)
{
prkdf_object_t tmp = a->next;
+ xfree (a->keyalgostr);
xfree (a->common_name);
xfree (a->serial_number);
xfree (a->objid);
xfree (a->authid);
+ xfree (a->label);
xfree (a);
a = tmp;
}
}
+static void
+release_pukdflist (pukdf_object_t a)
+{
+ release_prkdflist (a);
+}
+
/* Release just one aodf object. */
void
release_aodf_object (aodf_object_t a)
@@ -419,6 +576,7 @@ release_aodf_object (aodf_object_t a)
{
xfree (a->objid);
xfree (a->authid);
+ xfree (a->label);
xfree (a->path);
xfree (a);
}
@@ -437,50 +595,135 @@ release_aodflist (aodf_object_t a)
}
+static void
+release_lists (app_t app)
+{
+ release_cdflist (app->app_local->certificate_info);
+ app->app_local->certificate_info = NULL;
+ release_cdflist (app->app_local->trusted_certificate_info);
+ app->app_local->trusted_certificate_info = NULL;
+ release_cdflist (app->app_local->useful_certificate_info);
+ app->app_local->useful_certificate_info = NULL;
+ release_pukdflist (app->app_local->public_key_info);
+ app->app_local->public_key_info = NULL;
+ release_prkdflist (app->app_local->private_key_info);
+ app->app_local->private_key_info = NULL;
+ release_aodflist (app->app_local->auth_object_info);
+ app->app_local->auth_object_info = NULL;
+}
+
+
+static void
+release_tokeninfo (app_t app)
+{
+ xfree (app->app_local->manufacturer_id);
+ app->app_local->manufacturer_id = NULL;
+ xfree (app->app_local->token_label);
+ app->app_local->token_label = NULL;
+ xfree (app->app_local->tokenflags);
+ app->app_local->tokenflags = NULL;
+ xfree (app->app_local->serialno);
+ app->app_local->serialno = NULL;
+}
+
+
/* 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->manufacturer_id);
- xfree (app->app_local->serialno);
+ release_lists (app);
+ release_tokeninfo (app);
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. */
+ must free BUFFER only on success. If EFID is 0 no seelct is done. */
static gpg_error_t
-select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
+select_and_read_binary (app_t app, unsigned short efid, const char *efid_desc,
unsigned char **buffer, size_t *buflen)
{
gpg_error_t err;
+ int sw;
+
+ if (efid)
+ {
+ err = select_ef_by_path (app, &efid, 1);
+ if (err)
+ {
+ log_error ("p15: error selecting %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
+ return err;
+ }
+ }
- err = iso7816_select_file (slot, efid, 0);
+ err = iso7816_read_binary_ext (app_get_slot (app),
+ 0, 0, 0, buffer, buflen, &sw);
if (err)
+ log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
+ efid_desc, efid, gpg_strerror (err), sw);
+ return err;
+}
+
+
+/* If EFID is not 0 do a select and then read the record RECNO.
+ * 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_record (app_t app, unsigned short efid, int recno,
+ const char *efid_desc,
+ unsigned char **buffer, size_t *buflen, int *r_sw)
+{
+ gpg_error_t err;
+ int sw;
+
+ if (r_sw)
+ *r_sw = 0x9000;
+
+ if (efid)
{
- log_error ("p15: error selecting %s (0x%04X): %s\n",
- efid_desc, efid, gpg_strerror (err));
- return err;
+ err = select_ef_by_path (app, &efid, 1);
+ if (err)
+ {
+ log_error ("p15: error selecting %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
+ if (r_sw)
+ *r_sw = sw;
+ return err;
+ }
}
- err = iso7816_read_binary (slot, 0, 0, buffer, buflen);
+
+ err = iso7816_read_record_ext (app_get_slot (app),
+ recno, 1, 0, buffer, buflen, &sw);
if (err)
{
- log_error ("p15: error reading %s (0x%04X): %s\n",
- efid_desc, efid, gpg_strerror (err));
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ ;
+ else if (err && sw == SW_FILE_STRUCT)
+ ;
+ else
+ log_error ("p15: error reading %s (0x%04X) record %d: %s (sw=%04X)\n",
+ efid_desc, efid, recno, gpg_strerror (err), sw);
+ if (r_sw)
+ *r_sw = sw;
return err;
}
+ /* On CardOS with a Linear TLV file structure the records starts
+ * with some tag (often the record number) followed by the length
+ * byte for this record. Detect and remove this prefix. */
+ if (*buflen > 2 && (*buffer)[0] != 0x30 && (*buffer)[1] == *buflen - 2)
+ {
+ memmove (*buffer, *buffer + 2, *buflen - 2);
+ *buflen = *buflen - 2;
+ }
+
return 0;
}
@@ -496,43 +739,61 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
if (!pathlen)
return gpg_error (GPG_ERR_INV_VALUE);
- if (pathlen && *path != 0x3f00 )
- log_error ("p15: warning: relative path selection not yet implemented\n");
+ /* log_debug ("%s: path=", __func__); */
+ /* for (j=0; j < pathlen; j++) */
+ /* log_printf ("%s%04hX", j? "/":"", path[j]); */
+ /* log_printf ("%s\n",app->app_local->direct_path_selection?" (direct)":"");*/
if (app->app_local->direct_path_selection)
{
- err = iso7816_select_path (app->slot, path+1, pathlen-1, 0);
+ if (pathlen && *path == 0x3f00 )
+ {
+ if (pathlen == 1)
+ err = iso7816_select_mf (app_get_slot (app));
+ else
+ err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1,
+ 0);
+ }
+ else
+ err = iso7816_select_path (app_get_slot (app), path, pathlen,
+ app->app_local->home_df);
if (err)
{
log_error ("p15: error selecting path ");
- for (j=0; j < pathlen; j++)
- log_printf ("%04hX", path[j]);
- log_printf (": %s\n", gpg_strerror (err));
- return err;
+ goto err_print_path;
}
}
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. */
+ if (pathlen && *path != 0x3f00 )
+ log_error ("p15: warning: relative path select not yet implemented\n");
+
+ /* FIXME: Use home_df. */
for (i=0; i < pathlen; i++)
{
- err = iso7816_select_file (app->slot, path[i], !(i+1 == pathlen));
+ err = iso7816_select_file (app_get_slot (app),
+ path[i], !(i+1 == pathlen));
if (err)
{
log_error ("p15: 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;
+ goto err_print_path;
}
}
}
return 0;
+
+ err_print_path:
+ if (pathlen && *path != 0x3f00 )
+ log_printf ("3F00/");
+ else
+ log_printf ("%04hX/", app->app_local->home_df);
+ for (j=0; j < pathlen; j++)
+ log_printf ("%s%04hX", j? "/":"", path[j]);
+ log_printf (": %s\n", gpg_strerror (err));
+ return err;
}
+
/* 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
@@ -569,7 +830,7 @@ parse_certid (app_t app, const char *certid,
}
else /* This is a usual keyref. */
{
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
(unsigned int)(app->app_local->home_df & 0xffff));
else
@@ -605,20 +866,14 @@ parse_certid (app_t app, const char *certid,
}
-/* Find a certificate object by the certificate ID CERTID and store a
- pointer to it at R_CDF. */
+/* Find a certificate object by its object ID 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)
+cdf_object_from_objid (app_t app, size_t objidlen, const unsigned char *objid,
+ 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;
@@ -630,7 +885,6 @@ cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_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;
@@ -638,6 +892,69 @@ cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf)
}
+/* Find a certificate object by its label and store a pointer to it at
+ * R_CDF. */
+static gpg_error_t
+cdf_object_from_label (app_t app, const char *label, cdf_object_t *r_cdf)
+{
+ cdf_object_t cdf;
+
+ if (!label)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_cdf = cdf;
+ 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;
+ prkdf_object_t prkdf;
+
+ err = parse_certid (app, certid, &objid, &objidlen);
+ if (err)
+ return err;
+
+ err = cdf_object_from_objid (app, objidlen, objid, &cdf);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ {
+ /* Try again by finding the certid in the prkdf and matching by
+ * label. */
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ if (prkdf->objidlen == objidlen
+ && !memcmp (prkdf->objid, objid, objidlen))
+ break;
+ if (prkdf)
+ err = cdf_object_from_label (app, prkdf->label, &cdf);
+ }
+ xfree (objid);
+ if (err)
+ return err;
+ *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
@@ -691,7 +1008,19 @@ read_ef_odf (app_t app, unsigned short odf_fid)
size_t offset;
unsigned short home_df = 0;
- err = select_and_read_binary (app->slot, odf_fid, "ODF", &buffer, &buflen);
+
+ app->app_local->odf.private_keys = 0;
+ app->app_local->odf.public_keys = 0;
+ app->app_local->odf.trusted_public_keys = 0;
+ app->app_local->odf.secret_keys = 0;
+ app->app_local->odf.certificates = 0;
+ app->app_local->odf.trusted_certificates = 0;
+ app->app_local->odf.useful_certificates = 0;
+ app->app_local->odf.data_objects = 0;
+ app->app_local->odf.auth_objects = 0;
+
+ err = select_and_read_binary (app, odf_fid, "ODF",
+ &buffer, &buflen);
if (err)
return err;
@@ -717,6 +1046,7 @@ read_ef_odf (app_t app, unsigned short odf_fid)
&& !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
&& (!home_df || home_df == ((p[8]<<8)|p[9])) )
{
+ /* FIXME: Is this hack still required? */
/* If we do not know the home DF, we take it from the first
* ODF object. Here are sample values:
* a0 0a 30 08 0406 3f00 5015 4401
@@ -807,6 +1137,47 @@ read_ef_odf (app_t app, unsigned short odf_fid)
}
+/* Helper for the read_ef_foo functions to read the first record or
+ * the entire data. */
+static gpg_error_t
+read_first_record (app_t app, unsigned short fid, const char *fid_desc,
+ unsigned char **r_buffer, size_t *r_buflen,
+ int *r_use_read_record)
+{
+ gpg_error_t err;
+ int sw;
+
+ *r_buffer = NULL;
+ *r_buflen = 0;
+ *r_use_read_record = 0;
+
+ if (!fid)
+ return gpg_error (GPG_ERR_NO_DATA); /* No such file. */
+
+ if (IS_CARDOS_5 (app))
+ {
+ *r_use_read_record = 1;
+ err = select_and_read_record (app, fid, 1, fid_desc,
+ r_buffer, r_buflen, &sw);
+ if (err && sw == SW_FILE_STRUCT)
+ {
+ *r_use_read_record = 0;
+ err = select_and_read_binary (app, 0, fid_desc, r_buffer, r_buflen);
+ }
+ }
+ else
+ err = select_and_read_binary (app, fid, fid_desc, r_buffer, r_buflen);
+
+ /* We get a not_found state in read_record mode if the select
+ * succeeded but reading the record failed. Map that to no_data
+ * which is what the caller of the read_ef_foo functions expect. */
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = gpg_error (GPG_ERR_NO_DATA);
+
+ return err;
+}
+
+
/* Parse the BIT STRING with the keyUsageFlags from the
CommonKeyAttributes. */
static gpg_error_t
@@ -871,82 +1242,488 @@ parse_keyusage_flags (const unsigned char *der, size_t derlen,
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 [email protected]...
-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 [email protected]....
-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 [email protected]...........
-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 void
+dump_keyusage_flags (keyusage_flags_t usageflags)
+{
+ const char *s = "";
+
+ log_info ("p15: usage=");
+ if (usageflags.encrypt)
+ log_printf ("%sencrypt", s), s = ",";
+ if (usageflags.decrypt)
+ log_printf ("%sdecrypt", s), s = ",";
+ if (usageflags.sign )
+ log_printf ("%ssign", s), s = ",";
+ if (usageflags.sign_recover)
+ log_printf ("%ssign_recover", s), s = ",";
+ if (usageflags.wrap )
+ log_printf ("%swrap", s), s = ",";
+ if (usageflags.unwrap )
+ log_printf ("%sunwrap", s), s = ",";
+ if (usageflags.verify )
+ log_printf ("%sverify", s), s = ",";
+ if (usageflags.verify_recover)
+ log_printf ("%sverify_recover", s), s = ",";
+ if (usageflags.derive )
+ log_printf ("%sderive", s), s = ",";
+ if (usageflags.non_repudiation)
+ log_printf ("%snon_repudiation", s), s = ",";
+}
-*/
+
+static void
+dump_keyaccess_flags (keyaccess_flags_t accessflags)
+{
+ const char *s = "";
+
+ log_info ("p15: access=");
+ if (accessflags.sensitive)
+ log_printf ("%ssensitive", s), s = ",";
+ if (accessflags.extractable)
+ log_printf ("%sextractable", s), s = ",";
+ if (accessflags.always_sensitive)
+ log_printf ("%salways_sensitive", s), s = ",";
+ if (accessflags.never_extractable)
+ log_printf ("%snever_extractable", s), s = ",";
+ if (accessflags.local)
+ log_printf ("%slocal", s), s = ",";
+}
+
+
+static void
+dump_gpgusage_flags (gpgusage_flags_t gpgusage)
+{
+ const char *s = "";
+
+ log_info ("p15: gpgusage=");
+ if (gpgusage.cert)
+ log_printf ("%scert", s), s = ",";
+ if (gpgusage.sign)
+ log_printf ("%ssign", s), s = ",";
+ if (gpgusage.encr)
+ log_printf ("%sencr", s), s = ",";
+ if (gpgusage.auth)
+ log_printf ("%sauth", s), s = ",";
+}
+
+
+/* Parse the BIT STRING with the keyAccessFlags from the
+ CommonKeyAttributes. */
+static gpg_error_t
+parse_keyaccess_flags (const unsigned char *der, size_t derlen,
+ keyaccess_flags_t *accessflags)
+{
+ unsigned int bits, mask;
+ int i, unused, full;
+
+ memset (accessflags, 0, sizeof *accessflags);
+ 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 & 0x10)) accessflags->local = 1;
+ if ((bits & 0x08)) accessflags->never_extractable = 1;
+ if ((bits & 0x04)) accessflags->always_sensitive = 1;
+ if ((bits & 0x02)) accessflags->extractable = 1;
+ if ((bits & 0x01)) accessflags->sensitive = 1;
+
+ accessflags->any = 1;
+ return 0;
+}
+
+
+/* Parse the commonObjectAttributes and store a malloced authid at
+ * (r_authid,r_authidlen). (NULL,0) is stored on error or if no
+ * authid is found. IF R_LABEL is not NULL the label is stored there
+ * as a malloced string (spaces are replaced by underscores).
+ *
+ * Example data:
+ * 2 30 17: SEQUENCE { -- commonObjectAttributes
+ * 4 0C 8: UTF8String 'SK.CH.DS' -- label
+ * 14 03 2: BIT STRING 6 unused bits
+ * : '01'B (bit 0)
+ * 18 04 1: OCTET STRING --authid
+ * : 07
+ * : }
+ */
+static gpg_error_t
+parse_common_obj_attr (unsigned char const **buffer, size_t *size,
+ unsigned char **r_authid, size_t *r_authidlen,
+ char **r_label)
+{
+ gpg_error_t err;
+ int where;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen, nnn;
+ const unsigned char *ppp;
+ int ignore_eof = 0;
+ char *p;
+
+ *r_authid = NULL;
+ *r_authidlen = 0;
+ if (r_label)
+ *r_label = NULL;
+
+ where = __LINE__;
+ err = parse_ber_header (buffer, size, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > *size || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ ppp = *buffer;
+ nnn = objlen;
+ *buffer += objlen;
+ *size -= objlen;
+
+ /* Search the optional AuthId. */
+ ignore_eof = 1;
+ 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 (err)
+ goto leave;
+
+ if (tag == TAG_UTF8_STRING)
+ {
+ if (r_label)
+ {
+ *r_label = xtrymalloc (objlen + 1);
+ if (!*r_label)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (*r_label, ppp, objlen);
+ (*r_label)[objlen] = 0;
+ /* We don't want spaces in the labels due to the properties
+ * of CHV-LABEL. */
+ for (p = *r_label; *p; p++)
+ if (ascii_isspace (*p))
+ *p = '_';
+ }
+
+ ppp += objlen;
+ 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 (err)
+ goto leave;
+ }
+ 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 (err)
+ goto leave;
+ }
+ if (tag == TAG_OCTET_STRING && objlen)
+ {
+ *r_authid = xtrymalloc (objlen);
+ if (!*r_authid)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (*r_authid, ppp, objlen);
+ *r_authidlen = objlen;
+ }
+
+ leave:
+ if (ignore_eof && gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ else if (err)
+ log_error ("p15: error parsing commonObjectAttributes at %d: %s\n",
+ where, gpg_strerror (err));
+
+ if (err && r_label)
+ {
+ xfree (*r_label);
+ *r_label = NULL;
+ }
+
+ return err;
+}
+
+
+/* Parse the commonKeyAttributes. On success store the objid at
+ * (R_OBJID/R_OBJIDLEN), sets the key usage flags at USAGEFLAGS and
+ * the optiona key refrence at R_KEY_REFERENCE. The latter is only
+ * valid if true is also stored at R_KEY_REFERENCE_VALID.
+ *
+ * Example data:
+ *
+ * 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)
+ * : }
+ */
+static gpg_error_t
+parse_common_key_attr (unsigned char const **buffer, size_t *size,
+ unsigned char **r_objid, size_t *r_objidlen,
+ keyusage_flags_t *usageflags,
+ keyaccess_flags_t *accessflags,
+ unsigned long *r_key_reference,
+ int *r_key_reference_valid)
+{
+ gpg_error_t err;
+ int where;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen, nnn;
+ const unsigned char *ppp;
+ int ignore_eof = 0;
+ unsigned long ul;
+ const unsigned char *objid = NULL;
+ size_t objidlen;
+ unsigned long key_reference = 0;
+ int key_reference_valid = 0;
+
+ *r_objid = NULL;
+ *r_objidlen = 0;
+ memset (usageflags, 0, sizeof *usageflags);
+ memset (accessflags, 0, sizeof *accessflags);
+ *r_key_reference_valid = 0;
+
+ where = __LINE__;
+ err = parse_ber_header (buffer, size, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > *size || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ ppp = *buffer;
+ nnn = objlen;
+ *buffer += objlen;
+ *size -= 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 leave;
+
+ 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 leave;
+
+ err = parse_keyusage_flags (ppp, objlen, usageflags);
+ if (err)
+ goto leave;
+ ppp += objlen;
+ nnn -= objlen;
+
+ ignore_eof = 1; /* Remaining items are optional. */
+
+ /* Find the keyReference */
+ 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 leave;
+
+ 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 (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
+ {
+ /* These are the keyAccessFlags. */
+ err = parse_keyaccess_flags (ppp, objlen, accessflags);
+ if (err)
+ goto leave;
+ ppp += objlen;
+ nnn -= objlen;
+
+ 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 leave;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
+ {
+ /* This is the keyReference. */
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*ppp++) & 0xff;
+ nnn--;
+ }
+ key_reference = ul;
+ key_reference_valid = 1;
+ }
+
+ leave:
+ if (ignore_eof && gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+
+ if (!err)
+ {
+ if (!objid || !objidlen)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else
+ {
+ *r_objid = xtrymalloc (objidlen);
+ if (!*r_objid)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ memcpy (*r_objid, objid, objidlen);
+ *r_objidlen = objidlen;
+ }
+ }
+ }
+ if (!err && key_reference_valid)
+ {
+ *r_key_reference = key_reference;
+ *r_key_reference_valid = 1;
+ }
+
+ if (err)
+ log_error ("p15: error parsing commonKeyAttributes at %d: %s\n",
+ where, gpg_strerror (err));
+ return err;
+
+}
+
+
+/* Read and parse the Private Key Directory Files.
+ *
+ * Sample object:
+ * SEQUENCE {
+ * SEQUENCE { -- commonObjectAttributes
+ * UTF8String 'SK.CH.DS'
+ * BIT STRING 6 unused bits
+ * '01'B (bit 0) -- flags: non-modifiable,private
+ * OCTET STRING --authid
+ * 07
+ * }
+ * SEQUENCE { -- commonKeyAttributes
+ * OCTET STRING
+ * 01
+ * BIT STRING 6 unused bits
+ * '1000000000'B (bit 9) -- keyusage: non-repudiation
+ * INTEGER 80 -- keyReference (optional)
+ * }
+ * [1] { -- keyAttributes
+ * SEQUENCE { -- privateRSAKeyAttributes
+ * SEQUENCE { -- objectValue
+ * OCTET STRING --path
+ * 3F 00 40 16 00 50
+ * }
+ * INTEGER 1024 -- modulus
+ * }
+ * }
+ * }
+ *
+ * Sample part for EC objects:
+ * [1] { -- keyAttributes
+ * [1] { -- privateECkeyAttributes
+ * SEQUENCE { -- objectValue
+ * SEQUENCE { --path
+ * OCTET STRING 50 72 4B 03
+ * }
+ * INTEGER 33 -- Not in PKCS#15v1.1, need to buy 7816-15?
+ * }
+ * }
+ */
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;
+ unsigned char *buffer;
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->slot, fid, "PrKDF", &buffer, &buflen);
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ unsigned char *objid = NULL;
+ size_t objidlen = 0;
+ char *label = NULL;
+ int record_mode;
+
+ err = read_first_record (app, fid, "PrKDF", &buffer, &buflen, &record_mode);
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. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -955,25 +1732,47 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
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;
+ keyaccess_flags_t accessflags;
unsigned long key_reference = 0;
int key_reference_valid = 0;
- const char *s;
+ int is_ecc = 0;
+ where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ if (err)
+ ;
+ else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* PrivateRSAKeyAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: is_ecc = 1; break; /* PrivateECKeyAttributes */
+ 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;
+ }
+ if (errstr)
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
if (err)
{
log_error ("p15: error parsing PrKDF record: %s\n",
gpg_strerror (err));
goto leave;
}
+
pp = p;
nn = objlen;
p += objlen;
@@ -981,173 +1780,327 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
/* Parse the commonObjectAttributes. */
where = __LINE__;
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
+ if (err)
+ goto parse_error;
+
+ /* Parse the commonKeyAttributes. */
+ where = __LINE__;
+ xfree (objid);
+ err = parse_common_key_attr (&pp, &nn,
+ &objid, &objidlen,
+ &usageflags, &accessflags,
+ &key_reference, &key_reference_valid);
+ if (err)
+ goto parse_error;
+ log_assert (objid);
+
+ /* Skip commonPrivateKeyAttributes. */
+ where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ if (!err && objlen > nn)
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;
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ 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:
- ;
- }
+ 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;
- /* Parse the commonKeyAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence. */
+ else
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;
+ nn = objlen;
- /* Get the Id. */
- where = __LINE__;
- err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ /* 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 > nnn
- || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
- err = gpg_error (GPG_ERR_INV_OBJ);
- if (err)
+ 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;
- objid = ppp;
- objidlen = objlen;
- ppp += objlen;
- nnn -= objlen;
+ }
+ nn = objlen;
- /* Get the KeyUsageFlags. */
- where = __LINE__;
- err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ /* Parse the Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &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;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
- /* 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)
+ /* 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;
- 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--;
+ }
+
+ /* 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->is_ecc = is_ecc;
+
+ prkdf->objidlen = objidlen;
+ prkdf->objid = objid;
+ objid = NULL;
+ if (authid)
+ {
+ prkdf->authidlen = authidlen;
+ prkdf->authid = authid;
+ authid = NULL;
+ }
+ if (label)
+ {
+ prkdf->label = label;
+ label = NULL;
+ }
+
+ 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->accessflags = accessflags;
+ 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--;
}
- key_reference = ul;
- key_reference_valid = 1;
+ 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;
+ }
+
+ /* The info is printed later in read_p15_info because we also
+ * want to look at the certificates. */
+
+ /* Put it into the list. */
+ prkdf->next = prkdflist;
+ prkdflist = prkdf;
+ prkdf = NULL;
+ goto next_record; /* Ready with this record. */
+
+ parse_error:
+ log_error ("p15: error parsing PrKDF record at %d: %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ if (prkdf)
+ {
+ xfree (prkdf->objid);
+ xfree (prkdf->authid);
+ xfree (prkdf->label);
+ xfree (prkdf);
+ }
+ err = 0;
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "PrKDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
}
+ p = buffer;
+ n = buflen;
+ }
+ } /* End looping over all records. */
+
+ leave:
+ xfree (authid);
+ xfree (label);
+ xfree (objid);
+ xfree (buffer);
+ if (err)
+ release_prkdflist (prkdflist);
+ else
+ *result = prkdflist;
+ return err;
+}
+
+
+/* Read and parse the Public Keys Directory File. */
+static gpg_error_t
+read_ef_pukdf (app_t app, unsigned short fid, pukdf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ pukdf_object_t pukdflist = NULL;
+ int i;
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ unsigned char *objid = NULL;
+ size_t objidlen = 0;
+ char *label = NULL;
+ int record_mode;
+
+ err = read_first_record (app, fid, "PuKDF", &buffer, &buflen, &record_mode);
+ 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. Note the
+ * special handling for record mode at the end of the loop. */
+ while (n && *p && *p != 0xff)
+ {
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ pukdf_object_t pukdf = NULL;
+ unsigned long ul;
+ keyusage_flags_t usageflags;
+ keyaccess_flags_t accessflags;
+ unsigned long key_reference = 0;
+ int key_reference_valid = 0;
- leave_cki:
+ where = __LINE__;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
;
- }
+ else if (objlen > n)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* PublicRSAKeyAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: break; /* EC key object */
+ 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 publicKeyObject"; break;
+ }
+ if (errstr)
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
+ if (err)
+ {
+ log_error ("p15: error parsing PuKDF record: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
+ if (err)
+ goto parse_error;
+ /* Parse the commonKeyAttributes. */
+ where = __LINE__;
+ xfree (objid);
+ err = parse_common_key_attr (&pp, &nn,
+ &objid, &objidlen,
+ &usageflags, &accessflags,
+ &key_reference, &key_reference_valid);
+ if (err)
+ goto parse_error;
+ log_assert (objid);
- /* Skip subClassAttributes. */
+ /* Parse the subClassAttributes. */
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
@@ -1157,6 +2110,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
goto parse_error;
if (class == CLASS_CONTEXT && tag == 0)
{
+ /* Skip this CommonPublicKeyAttribute. */
pp += objlen;
nn -= objlen;
@@ -1164,39 +2118,26 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
}
- /* Parse the keyAttributes. */
+ /* We expect a typeAttribute. */
if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
- goto parse_error;
+ goto parse_error; /* No typeAttribute. */
nn = objlen;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && objlen > nn)
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence. */
+ else
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;
@@ -1232,50 +2173,44 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
errstr = "invalid path reference";
goto parse_error;
}
- /* Create a new PrKDF list item. */
- prkdf = xtrycalloc (1, (sizeof *prkdf
+
+ /* Create a new PuKDF list item. */
+ pukdf = xtrycalloc (1, (sizeof *pukdf
- sizeof(unsigned short)
+ objlen/2 * sizeof(unsigned short)));
- if (!prkdf)
+ if (!pukdf)
{
err = gpg_error_from_syserror ();
goto leave;
}
- prkdf->objidlen = objidlen;
- prkdf->objid = xtrymalloc (objidlen);
- if (!prkdf->objid)
+ pukdf->objidlen = objidlen;
+ pukdf->objid = objid;
+ objid = NULL;
+ if (authid)
{
- err = gpg_error_from_syserror ();
- xfree (prkdf);
- goto leave;
+ pukdf->authidlen = authidlen;
+ pukdf->authid = authid;
+ authid = NULL;
}
- memcpy (prkdf->objid, objid, objidlen);
- if (authid)
+ if (label)
{
- 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);
+ pukdf->label = label;
+ label = NULL;
}
- prkdf->pathlen = objlen/2;
- for (i=0; i < prkdf->pathlen; i++, pp += 2, nn -= 2)
- prkdf->path[i] = ((pp[0] << 8) | pp[1]);
+ pukdf->pathlen = objlen/2;
+ for (i=0; i < pukdf->pathlen; i++, pp += 2, nn -= 2)
+ pukdf->path[i] = ((pp[0] << 8) | pp[1]);
- prkdf->usageflags = usageflags;
- prkdf->key_reference = key_reference;
- prkdf->key_reference_valid = key_reference_valid;
+ pukdf->usageflags = usageflags;
+ pukdf->accessflags = accessflags;
+ pukdf->key_reference = key_reference;
+ pukdf->key_reference_valid = key_reference_valid;
if (nn)
{
/* An index and length follows. */
- prkdf->have_off = 1;
+ pukdf->have_off = 1;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
@@ -1291,7 +2226,7 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
ul |= (*pp++) & 0xff;
nn--;
}
- prkdf->off = ul;
+ pukdf->off = ul;
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
@@ -1308,70 +2243,82 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
ul |= (*pp++) & 0xff;
nn--;
}
- prkdf->len = ul;
+ pukdf->len = ul;
}
if (opt.verbose)
{
- log_info ("p15: 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 ("%s%04hX", i?"/":"",prkdf->path[i]);
- if (prkdf->have_off)
- log_printf ("[%lu/%lu]", prkdf->off, prkdf->len);
- if (prkdf->authid)
+ log_info ("p15: PuKDF %04hX: id=", fid);
+ for (i=0; i < pukdf->objidlen; i++)
+ log_printf ("%02X", pukdf->objid[i]);
+ if (pukdf->label)
+ log_printf (" (%s)", pukdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < pukdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"",pukdf->path[i]);
+ if (pukdf->have_off)
+ log_printf ("[%lu/%lu]", pukdf->off, pukdf->len);
+ if (pukdf->authid)
{
log_printf (" authid=");
- for (i=0; i < prkdf->authidlen; i++)
- log_printf ("%02X", prkdf->authid[i]);
+ for (i=0; i < pukdf->authidlen; i++)
+ log_printf ("%02X", pukdf->authid[i]);
}
- if (prkdf->key_reference_valid)
- log_printf (" keyref=0x%02lX", prkdf->key_reference);
- log_info ("p15: 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 = ",";
+ if (pukdf->key_reference_valid)
+ log_printf (" keyref=0x%02lX", pukdf->key_reference);
+ if (pukdf->accessflags.any)
+ dump_keyaccess_flags (pukdf->accessflags);
+ dump_keyusage_flags (pukdf->usageflags);
log_printf ("\n");
}
/* Put it into the list. */
- prkdf->next = prkdflist;
- prkdflist = prkdf;
- prkdf = NULL;
- continue; /* Ready. */
+ pukdf->next = pukdflist;
+ pukdflist = pukdf;
+ pukdf = NULL;
+ goto next_record; /* Ready with this record. */
parse_error:
- log_error ("p15: error parsing PrKDF record (%d): %s - skipped\n",
+ log_error ("p15: error parsing PuKDF record at %d: %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
- if (prkdf)
+ if (pukdf)
{
- xfree (prkdf->objid);
- xfree (prkdf->authid);
- xfree (prkdf);
+ xfree (pukdf->objid);
+ xfree (pukdf->authid);
+ xfree (pukdf->label);
+ xfree (pukdf);
}
err = 0;
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "PuKDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
} /* End looping over all records. */
leave:
+ xfree (authid);
+ xfree (label);
+ xfree (objid);
xfree (buffer);
if (err)
- release_prkdflist (prkdflist);
+ release_pukdflist (pukdflist);
else
- *result = prkdflist;
+ *result = pukdflist;
return err;
}
@@ -1381,21 +2328,23 @@ read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
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)
+read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result)
{
gpg_error_t err;
- unsigned char *buffer = NULL;
+ unsigned char *buffer;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
cdf_object_t cdflist = NULL;
int i;
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ char *label = NULL;
+ int record_mode;
- if (!fid)
- return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
-
- err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen);
+ err = read_first_record (app, fid, "CDF", &buffer, &buflen, &record_mode);
if (err)
return err;
@@ -1404,7 +2353,8 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
/* 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. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -1430,16 +2380,13 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
p += objlen;
n -= objlen;
- /* Skip the commonObjectAttributes. */
+ /* 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);
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
if (err)
goto parse_error;
- pp += objlen;
- nn -= objlen;
/* Parse the commonCertificateAttributes. */
where = __LINE__;
@@ -1530,6 +2477,18 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
err = gpg_error_from_syserror ();
goto leave;
}
+ if (authid)
+ {
+ cdf->authidlen = authidlen;
+ cdf->authid = authid;
+ authid = NULL;
+ }
+ if (label)
+ {
+ cdf->label = label;
+ label = NULL;
+ }
+
cdf->objidlen = objidlen;
cdf->objid = xtrymalloc (objidlen);
if (!cdf->objid)
@@ -1585,14 +2544,22 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
if (opt.verbose)
{
- log_info ("p15: CDF %04hX: id=", fid);
+ log_info ("p15: CDF-%c %04hX: id=", cdftype, fid);
for (i=0; i < cdf->objidlen; i++)
log_printf ("%02X", cdf->objid[i]);
- log_printf (" path=");
+ if (cdf->label)
+ log_printf (" (%s)", cdf->label);
+ log_info ("p15: path=");
for (i=0; i < cdf->pathlen; i++)
log_printf ("%s%04hX", i?"/":"", cdf->path[i]);
if (cdf->have_off)
log_printf ("[%lu/%lu]", cdf->off, cdf->len);
+ if (cdf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < cdf->authidlen; i++)
+ log_printf ("%02X", cdf->authid[i]);
+ }
log_printf ("\n");
}
@@ -1600,16 +2567,38 @@ read_ef_cdf (app_t app, unsigned short fid, cdf_object_t *result)
cdf->next = cdflist;
cdflist = cdf;
cdf = NULL;
- continue; /* Ready. */
+ goto next_record; /* Ready with this record. */
parse_error:
- log_error ("p15: error parsing CDF record (%d): %s - skipped\n",
+ log_error ("p15: error parsing CDF record at %d: %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
xfree (cdf);
err = 0;
- } /* End looping over all records. */
+
+ next_record:
+ xfree (authid);
+ xfree (label);
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "CDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End loop over all records. */
leave:
+ xfree (authid);
+ xfree (label);
xfree (buffer);
if (err)
release_cdflist (cdflist);
@@ -1660,29 +2649,27 @@ 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;
+ unsigned char *buffer;
size_t buflen;
const unsigned char *p;
size_t n, objlen, hdrlen;
int class, tag, constructed, ndef;
aodf_object_t aodflist = NULL;
int i;
+ int recno = 1;
+ int record_mode;
- if (!fid)
- return gpg_error (GPG_ERR_NO_DATA); /* No authentication objects. */
-
- err = select_and_read_binary (app->slot, fid, "AODF", &buffer, &buflen);
+ err = read_first_record (app, fid, "AODF", &buffer, &buflen, &record_mode);
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. */
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
while (n && *p && *p != 0xff)
{
const unsigned char *pp;
@@ -1693,10 +2680,33 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
unsigned long ul;
const char *s;
+ where = __LINE__;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ if (err)
+ ;
+ else if (objlen > n)
err = gpg_error (GPG_ERR_INV_OBJ);
+ else 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;
+ }
+
+
if (err)
{
log_error ("p15: error parsing AODF record: %s\n",
@@ -1716,72 +2726,10 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
/* 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);
+ err = parse_common_obj_attr (&pp, &nn, &aodf->authid, &aodf->authidlen,
+ &aodf->label);
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__;
@@ -1801,7 +2749,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
/* Get the Id. */
where = __LINE__;
err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
- &ndef, &objlen, &hdrlen);
+ &ndef, &objlen, &hdrlen);
if (!err && (objlen > nnn
|| class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
err = gpg_error (GPG_ERR_INV_OBJ);
@@ -1828,28 +2776,16 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
where = __LINE__;
err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
- if (!err && objlen > nn)
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence */
+ else
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;
@@ -2125,7 +3061,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
}
aodf->pathlen = objlen/2;
- aodf->path = xtrymalloc (aodf->pathlen);
+ aodf->path = xtrycalloc (aodf->pathlen, sizeof *aodf->path);
if (!aodf->path)
goto no_core;
for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2)
@@ -2171,23 +3107,20 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
}
}
- /* Igonore further objects which might be there due to future
+ /* Ignore further objects which might be there due to future
extensions of pkcs#15. */
ready:
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
if (opt.verbose)
{
- log_info ("p15: AODF %04hX: id=", fid);
+ log_info ("p15: 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]);
- }
- if (aodf->pin_reference_valid)
- log_printf (" pinref=0x%02lX", aodf->pin_reference);
+ if (aodf->label)
+ log_printf (" (%s)", aodf->label);
+ log_info ("p15: ");
if (aodf->pathlen)
{
log_printf (" path=");
@@ -2196,6 +3129,14 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
if (aodf->have_off)
log_printf ("[%lu/%lu]", aodf->off, aodf->len);
}
+ if (aodf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < aodf->authidlen; i++)
+ log_printf ("%02X", aodf->authid[i]);
+ }
+ if (aodf->pin_reference_valid)
+ log_printf (" pinref=0x%02lX", aodf->pin_reference);
log_printf (" min=%lu", aodf->min_length);
log_printf (" stored=%lu", aodf->stored_length);
if (aodf->max_length_valid)
@@ -2203,7 +3144,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
if (aodf->pad_char_valid)
log_printf (" pad=0x%02x", aodf->pad_char);
- log_info ("p15: flags=");
+ log_info ("p15: flags=");
s = "";
if (aodf->pinflags.case_sensitive)
log_printf ("%scase_sensitive", s), s = ",";
@@ -2231,18 +3172,20 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
log_printf ("%sexchange_ref_data", s), s = ",";
{
char numbuf[50];
+ const char *s2;
+
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;
+ case PIN_TYPE_BCD: s2 = "bcd"; break;
+ case PIN_TYPE_ASCII_NUMERIC: s2 = "ascii-numeric"; break;
+ case PIN_TYPE_UTF8: s2 = "utf8"; break;
+ case PIN_TYPE_HALF_NIBBLE_BCD: s2 = "half-nibble-bcd"; break;
+ case PIN_TYPE_ISO9564_1: s2 = "iso9564-1"; break;
default:
sprintf (numbuf, "%lu", (unsigned long)aodf->pintype);
- s = numbuf;
+ s2 = numbuf;
}
- log_printf (" type=%s", s);
+ log_printf ("%stype=%s", s, s2); s = ",";
}
log_printf ("\n");
}
@@ -2251,7 +3194,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
aodf->next = aodflist;
aodflist = aodf;
aodf = NULL;
- continue; /* Ready. */
+ goto next_record; /* Ready with this record. */
no_core:
err = gpg_error_from_syserror ();
@@ -2259,10 +3202,28 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
goto leave;
parse_error:
- log_error ("p15: error parsing AODF record (%d): %s - skipped\n",
+ log_error ("p15: error parsing AODF record at %d: %s - skipped\n",
where, errstr? errstr : gpg_strerror (err));
err = 0;
release_aodf_object (aodf);
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "AODF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
} /* End looping over all records. */
leave:
@@ -2335,53 +3296,48 @@ print_tokeninfo_tokenflags (const unsigned char *der, size_t derlen)
/* 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.
- : }
-
-
-
-
+ *
+ * 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)
+ * }
+ *
+ *
+ * Sample EF 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)
@@ -2394,12 +3350,10 @@ read_ef_tokeninfo (app_t app)
int class, tag, constructed, ndef;
unsigned long ul;
- xfree (app->app_local->manufacturer_id);
- app->app_local->manufacturer_id = NULL;
+ release_tokeninfo (app);
app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
- err = select_and_read_binary (app->slot, 0x5032, "TokenInfo",
- &buffer, &buflen);
+ err = select_and_read_binary (app, 0x5032, "TokenInfo", &buffer, &buflen);
if (err)
return err;
@@ -2439,8 +3393,6 @@ read_ef_tokeninfo (app_t app)
goto leave;
}
- if (opt.verbose)
- log_info ("p15: TokenInfo:\n");
/* serialNumber. */
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
@@ -2458,12 +3410,6 @@ read_ef_tokeninfo (app_t app)
}
memcpy (app->app_local->serialno, p, objlen);
app->app_local->serialnolen = objlen;
- if (opt.verbose)
- {
- /* (We use a separate log_info to avoid the "DBG:" prefix.) */
- log_info ("p15: serialNumber .: ");
- log_printhex (p, objlen, "");
- }
p += objlen;
n -= objlen;
@@ -2476,8 +3422,6 @@ read_ef_tokeninfo (app_t app)
goto leave;
if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING)
{
- if (opt.verbose)
- log_info ("p15: manufacturerID: %.*s\n", (int)objlen, p);
app->app_local->manufacturer_id = percent_data_escape (0, NULL,
p, objlen);
p += objlen;
@@ -2492,11 +3436,7 @@ read_ef_tokeninfo (app_t app)
}
if (class == CLASS_CONTEXT && tag == 0)
{
- if (opt.verbose)
- log_info ("p15: label ........: %.*s\n", (int)objlen, p);
- if (objlen > 15 && !memcmp (p, "D-TRUST Card V3", 15)
- && app->app_local->card_type == CARD_TYPE_CARDOS_50)
- app->app_local->card_product = CARD_PRODUCT_DTRUST;
+ app->app_local->token_label = percent_data_escape (0, NULL, p, objlen);
p += objlen;
n -= objlen;
@@ -2511,16 +3451,19 @@ read_ef_tokeninfo (app_t app)
/* The next is the mandatory tokenflags object. */
if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
{
- if (opt.verbose)
+ app->app_local->tokenflagslen = objlen;
+ app->app_local->tokenflags = xtrymalloc (objlen);
+ if (!app->app_local->tokenflags)
{
- log_info ("p15: tokenflags ...:");
- print_tokeninfo_tokenflags (p, objlen);
- log_printf ("\n");
+ err = gpg_error_from_syserror ();
+ goto leave;
}
+ memcpy (app->app_local->tokenflags, p, objlen);
p += objlen;
n -= objlen;
}
+
leave:
xfree (buffer);
return err;
@@ -2534,23 +3477,27 @@ static gpg_error_t
read_p15_info (app_t app)
{
gpg_error_t err;
+ prkdf_object_t prkdf;
+ unsigned int flag;
- if (!read_ef_tokeninfo (app))
+ err = read_ef_tokeninfo (app);
+ if (err)
+ return err;
+ /* If we don't have a serial number yet but the TokenInfo provides
+ * one, use that. */
+ if (!APP_CARD(app)->serialno && app->app_local->serialno)
{
- /* If we don't have a serial number yet but the TokenInfo provides
- one, use that. */
- if (!app->serialno && app->app_local->serialno)
- {
- app->serialno = app->app_local->serialno;
- app->serialnolen = app->app_local->serialnolen;
- app->app_local->serialno = NULL;
- app->app_local->serialnolen = 0;
- err = app_munge_serialno (app);
- if (err)
- return err;
- }
+ APP_CARD(app)->serialno = app->app_local->serialno;
+ APP_CARD(app)->serialnolen = app->app_local->serialnolen;
+ app->app_local->serialno = NULL;
+ app->app_local->serialnolen = 0;
+ err = app_munge_serialno (APP_CARD(app));
+ if (err)
+ return err;
}
+ release_lists (app);
+
/* 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. */
@@ -2559,24 +3506,36 @@ read_p15_info (app_t app)
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,
+ log_assert (!app->app_local->certificate_info);
+ log_assert (!app->app_local->trusted_certificate_info);
+ log_assert (!app->app_local->useful_certificate_info);
+ err = read_ef_cdf (app, app->app_local->odf.certificates, 'c',
&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,
+ err = read_ef_cdf (app, app->app_local->odf.trusted_certificates, 't',
&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,
+ err = read_ef_cdf (app, app->app_local->odf.useful_certificates, 'u',
&app->app_local->useful_certificate_info);
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
err = 0;
if (err)
return err;
+ /* Read information about public keys. */
+ log_assert (!app->app_local->public_key_info);
+ err = read_ef_pukdf (app, app->app_local->odf.public_keys,
+ &app->app_local->public_key_info);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = read_ef_pukdf (app, app->app_local->odf.trusted_public_keys,
+ &app->app_local->public_key_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);
+ log_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)
@@ -2585,13 +3544,269 @@ read_p15_info (app_t app)
return err;
/* Read information about authentication objects. */
- assert (!app->app_local->auth_object_info);
+ log_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;
+ /* See whether we can extend the private key information using
+ * information from certificates. We use only the first matching
+ * certificate; if we want to change this strategy we should walk
+ * over the certificates and then find the corresponsing private key
+ * objects. */
+ app->app_local->any_gpgusage = 0;
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ cdf_object_t cdf;
+ char *extusage;
+ char *p, *pend;
+ int seen, i;
+
+ if (opt.debug)
+ log_printhex (prkdf->objid, prkdf->objidlen, "p15: prkdf id=");
+ if (cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf)
+ && cdf_object_from_label (app, prkdf->label, &cdf))
+ continue; /* No matching certificate. */
+ if (!cdf->cert) /* Read and parse the certificate. */
+ readcert_by_cdf (app, cdf, NULL, NULL);
+ if (!cdf->cert)
+ continue; /* Unsupported or broken certificate. */
+
+ if (prkdf->is_ecc)
+ {
+ const char *oid;
+ const unsigned char *der;
+ size_t off, derlen, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+
+ for (i=0; !(err = ksba_cert_get_extension
+ (cdf->cert, i, &oid, NULL, &off, &derlen)); i++)
+ if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.10") )
+ break;
+ if (!err && (der = ksba_cert_get_image (cdf->cert, NULL)))
+ {
+ der += off;
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_OCTET_STRING || ndef))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (!err)
+ {
+ derlen = objlen;
+ if (opt.debug)
+ log_printhex (der, derlen, "p15: OpenPGP KDF parms:");
+ /* Store them if they match the known OpenPGP format. */
+ if (derlen == 4 && der[0] == 3 && der[1] == 1)
+ memcpy (prkdf->ecdh_kdf, der, 4);
+ }
+ }
+ err = 0;
+ }
+
+ if (ksba_cert_get_ext_key_usages (cdf->cert, &extusage))
+ continue; /* No extended key usage attribute. */
+
+ if (opt.debug)
+ log_debug ("p15: ExtKeyUsages: %s\n", extusage);
+ p = extusage;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ if ( *pend == 'C' ) /* Look only at critical usages. */
+ {
+ prkdf->extusage.valid = 1;
+ seen = 1;
+ if (!strcmp (p, oid_kp_codeSigning)
+ || !strcmp (p, oid_kp_timeStamping)
+ || !strcmp (p, oid_kp_ocspSigning)
+ || !strcmp (p, oid_kp_ms_documentSigning)
+ || !strcmp (p, oid_kp_ms_old_documentSigning))
+ prkdf->extusage.sign = 1;
+ else if (!strcmp (p, oid_kp_emailProtection))
+ prkdf->extusage.encr = 1;
+ else if (!strcmp (p, oid_kp_serverAuth)
+ || !strcmp (p, oid_kp_clientAuth)
+ || !strcmp (p, oid_kp_ms_smartcardLogon))
+ prkdf->extusage.auth = 1;
+ else if (!strcmp (p, oid_kp_anyExtendedKeyUsage))
+ {
+ prkdf->extusage.sign = 1;
+ prkdf->extusage.encr = 1;
+ prkdf->extusage.auth = 1;
+ }
+ else
+ seen = 0;
+ }
+ else
+ seen = 0;
+
+ /* Now check the gpg Usage. Here we don't care about
+ * critical or non-critical here. */
+ if (seen)
+ ; /* No more need to look for other caps. */
+ else if (!strcmp (p, oid_kp_gpgUsageCert))
+ {
+ prkdf->gpgusage.cert = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageSign))
+ {
+ prkdf->gpgusage.sign = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageEncr))
+ {
+ prkdf->gpgusage.encr = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageAuth))
+ {
+ prkdf->gpgusage.auth = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+
+ /* Skip to next item. */
+ if ((p = strchr (pend, '\n')))
+ p++;
+ }
+ xfree (extusage);
+ }
+
+ /* See whether we can figure out something about the card. */
+ if (!app->app_local->card_product
+ && app->app_local->manufacturer_id
+ && !strcmp (app->app_local->manufacturer_id, "www.atos.net/cardos")
+ && IS_CARDOS_5 (app))
+ {
+ /* This is a modern CARDOS card. */
+ flag = 0;
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ if (prkdf->label && !strcmp (prkdf->label, "IdentityKey")
+ && prkdf->key_reference_valid && prkdf->key_reference == 1
+ && !prkdf->authid)
+ flag |= 1;
+ else if (prkdf->label && !strcmp (prkdf->label, "TransportKey")
+ && prkdf->key_reference_valid && prkdf->key_reference==2
+ && prkdf->authid)
+ flag |= 2;
+ }
+ if (flag == 3)
+ app->app_local->card_product = CARD_PRODUCT_RSCS;
+
+ }
+ if (!app->app_local->card_product
+ && app->app_local->token_label
+ && !strncmp (app->app_local->token_label, "D-TRUST Card V3", 15)
+ && app->app_local->card_type == CARD_TYPE_CARDOS_50)
+ {
+ app->app_local->card_product = CARD_PRODUCT_DTRUST;
+ }
+
+
+ /* Now print the info about the PrKDF. */
+ if (opt.verbose)
+ {
+ int i;
+ unsigned char *atr;
+ size_t atrlen;
+ const char *cardstr;
+
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ log_info ("p15: PrKDF %04hX: id=", app->app_local->odf.private_keys);
+ for (i=0; i < prkdf->objidlen; i++)
+ log_printf ("%02X", prkdf->objid[i]);
+ if (prkdf->label)
+ log_printf (" (%s)", prkdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < prkdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"",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 (" type=%s", prkdf->is_ecc? "ecc":"rsa");
+ if (prkdf->accessflags.any)
+ dump_keyaccess_flags (prkdf->accessflags);
+ dump_keyusage_flags (prkdf->usageflags);
+ if (prkdf->extusage.valid)
+ log_info ("p15: extusage=%s%s%s%s%s",
+ prkdf->extusage.sign? "sign":"",
+ (prkdf->extusage.sign
+ && prkdf->extusage.encr)?",":"",
+ prkdf->extusage.encr? "encr":"",
+ ((prkdf->extusage.sign || prkdf->extusage.encr)
+ && prkdf->extusage.auth)?",":"",
+ prkdf->extusage.auth? "auth":"");
+ if (prkdf->gpgusage.any)
+ dump_gpgusage_flags (prkdf->gpgusage);
+
+ log_printf ("\n");
+ }
+
+ log_info ("p15: TokenInfo:\n");
+ if (app->app_local->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (app->app_local->serialno, app->app_local->serialnolen,
+ "");
+ }
+ else if (APP_CARD(app)->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (APP_CARD(app)->serialno, APP_CARD(app)->serialnolen,
+ "");
+ }
+
+ if (app->app_local->manufacturer_id)
+ log_info ("p15: manufacturerID: %s\n",
+ app->app_local->manufacturer_id);
+ if (app->app_local->card_product)
+ {
+ cardstr = cardproduct2str (app->app_local->card_product);
+ log_info ("p15: product ......: %d%s%s%s\n",
+ app->app_local->card_product,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
+ }
+ if (app->app_local->token_label)
+ log_info ("p15: label ........: %s\n", app->app_local->token_label);
+ if (app->app_local->tokenflags)
+ {
+ log_info ("p15: tokenflags ...:");
+ print_tokeninfo_tokenflags (app->app_local->tokenflags,
+ app->app_local->tokenflagslen);
+ log_printf ("\n");
+ }
+
+ log_info ("p15: atr ..........: ");
+ atr = apdu_get_atr (app_get_slot (app), &atrlen);
+ if (!atr)
+ log_printf ("[error]\n");
+ else
+ {
+ log_printhex (atr, atrlen, "");
+ xfree (atr);
+ }
+
+ cardstr = cardtype2str (app->app_local->card_type);
+ log_info ("p15: cardtype .....: %d%s%s%s\n",
+ app->app_local->card_type,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
+ }
+
return err;
}
@@ -2606,12 +3821,14 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
for (; certinfo; certinfo = certinfo->next)
{
char *buf, *p;
+ const char *label;
+ char *labelbuf;
buf = xtrymalloc (9 + certinfo->objidlen*2 + 1);
if (!buf)
return gpg_error_from_syserror ();
p = stpcpy (buf, "P15");
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
{
snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff));
@@ -2620,9 +3837,18 @@ send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
p = stpcpy (p, ".");
bin2hex (certinfo->objid, certinfo->objidlen, p);
+ label = (certinfo->label && *certinfo->label)? certinfo->label : "-";
+ labelbuf = percent_data_escape (0, NULL, label, strlen (label));
+ if (!labelbuf)
+ {
+ xfree (buf);
+ return gpg_error_from_syserror ();
+ }
+
send_status_info (ctrl, "CERTINFO",
certtype, strlen (certtype),
buf, strlen (buf),
+ labelbuf, strlen (labelbuf),
NULL, (size_t)0);
xfree (buf);
}
@@ -2652,33 +3878,22 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
xfree (prkdf->serial_number);
prkdf->serial_number = NULL;
- /* 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 by 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)
- {
- err = gpg_error (GPG_ERR_NOT_FOUND);
- goto leave;
- }
+ /* We could have also checked whether a public key directory file
+ * and a matching public key for PRKDF is available. This would
+ * make extraction of the key faster. However, this way we don't
+ * have a way to look at extended key attributes to check gpgusage.
+ * FIXME: Add public key lookup if no certificate was found. */
+
+ /* Look for a matching certificate. A certificate matches if the id
+ * matches the one of the private key info. If none was found we
+ * also try to match on the label. */
+ err = cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = cdf_object_from_label (app, prkdf->label, &cdf);
+ if (!err && !cdf)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (err)
+ goto leave;
err = readcert_by_cdf (app, cdf, &der, &derlen);
if (err)
@@ -2690,12 +3905,14 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
xfree (der);
if (!err)
err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey, NULL);
- if (!err)
+ if (!err && !prkdf->gpgusage.any)
{
/* Try to get the CN and the SerialNumber from the certificate;
* we use a very simple approach here which should work in many
* cases. Eventually we should add a rfc-2253 parser into
* libksba to make it easier to parse such a string.
+ * We don't do this if this is marked as gpg key and thus
+ * has only a dummy certificate.
*
* First example string:
* "CN=Otto Schily,O=Miniluv,C=DE"
@@ -2743,6 +3960,20 @@ keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
}
}
+ if (!err && !prkdf->keytime)
+ {
+ ksba_isotime_t isot;
+ time_t t;
+
+ ksba_cert_get_validity (cert, 0, isot);
+ t = isotime2epoch (isot);
+ prkdf->keytime = (t == (time_t)(-1))? 0 : (u32)t;
+ prkdf->have_keytime = 1;
+ }
+
+ if (!err && !prkdf->keyalgostr)
+ prkdf->keyalgostr = pubkey_algo_string (s_pkey, NULL);
+
ksba_cert_release (cert);
if (err)
goto leave;
@@ -2780,7 +4011,7 @@ keyref_from_prkdf (app_t app, prkdf_object_t prkdf)
if (!buf)
return NULL;
p = stpcpy (buf, "P15");
- if (app->app_local->home_df)
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
{
snprintf (p, 6, "-%04X",
(unsigned int)(app->app_local->home_df & 0xffff));
@@ -2820,27 +4051,58 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
else
{
char usage[5];
+ char keytime[20];
+ const char *algostr;
size_t usagelen = 0;
- if (prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover
- || prkdf->usageflags.non_repudiation)
- usage[usagelen++] = 's';
- if (prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover)
- usage[usagelen++] = 'c';
- if (prkdf->usageflags.decrypt
- || prkdf->usageflags.unwrap)
- usage[usagelen++] = 'e';
- if (prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover)
- usage[usagelen++] = 'a';
+ if (prkdf->gpgusage.any)
+ {
+ if (prkdf->gpgusage.sign)
+ usage[usagelen++] = 's';
+ if (prkdf->gpgusage.cert)
+ usage[usagelen++] = 'c';
+ if (prkdf->gpgusage.encr)
+ usage[usagelen++] = 'e';
+ if (prkdf->gpgusage.auth)
+ usage[usagelen++] = 'a';
+ }
+ else
+ {
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation)
+ && (!prkdf->extusage.valid
+ || prkdf->extusage.sign))
+ usage[usagelen++] = 's';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.sign))
+ usage[usagelen++] = 'c';
+ if ((prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap)
+ && (!prkdf->extusage.valid || prkdf->extusage.encr))
+ usage[usagelen++] = 'e';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.auth))
+ usage[usagelen++] = 'a';
+ }
log_assert (strlen (prkdf->keygrip) == 40);
+ if (prkdf->keytime && prkdf->have_keytime)
+ snprintf (keytime, sizeof keytime, "%lu",
+ (unsigned long)prkdf->keytime);
+ else
+ strcpy (keytime, "-");
+
+ algostr = prkdf->keyalgostr;
+
send_status_info (ctrl, "KEYPAIRINFO",
prkdf->keygrip, 2*KEYGRIP_LEN,
buf, strlen (buf),
usage, usagelen,
+ keytime, strlen (keytime),
+ algostr, strlen (algostr?algostr:""),
NULL, (size_t)0);
}
xfree (buf);
@@ -2850,13 +4112,22 @@ send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
-/* This is the handler for the LEARN command. */
+/* This is the handler for the LEARN command. Note that if
+ * APP_LEARN_FLAG_REREAD is set and this function returns an error,
+ * the caller must deinitialize this application. */
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_REREAD)
+ {
+ err = read_p15_info (app);
+ if (err)
+ return err;
+ }
+
+ if ((flags & APP_LEARN_FLAG_KEYPAIRINFO))
err = 0;
else
{
@@ -2875,13 +4146,20 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
if (!err)
err = send_keypairinfo (app, ctrl, app->app_local->private_key_info);
+ if (!err)
+ err = do_getattr (app, ctrl, "CHV-STATUS");
+ if (!err)
+ err = do_getattr (app, ctrl, "CHV-LABEL");
+
+
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. */
+/* Read a certificate using the information in CDF and return the
+ * certificate in a newly malloced buffer R_CERT and its length
+ * R_CERTLEN. Also parses the certificate. R_CERT and R_CERTLEN may
+ * be NULL to do just the caching. */
static gpg_error_t
readcert_by_cdf (app_t app, cdf_object_t cdf,
unsigned char **r_cert, size_t *r_certlen)
@@ -2895,17 +4173,31 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
int rootca;
int i;
- *r_cert = NULL;
- *r_certlen = 0;
+ if (r_cert)
+ *r_cert = NULL;
+ if (r_certlen)
+ *r_certlen = 0;
/* First check whether it has been cached. */
- if (cdf->image)
+ if (cdf->cert)
{
- *r_cert = xtrymalloc (cdf->imagelen);
+ const unsigned char *image;
+ size_t imagelen;
+
+ if (!r_cert || !r_certlen)
+ return 0; /* Caller does not actually want the result. */
+
+ image = ksba_cert_get_image (cdf->cert, &imagelen);
+ if (!image)
+ {
+ log_error ("p15: ksba_cert_get_image failed\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ *r_cert = xtrymalloc (imagelen);
if (!*r_cert)
return gpg_error_from_syserror ();
- memcpy (*r_cert, cdf->image, cdf->imagelen);
- *r_certlen = cdf->imagelen;
+ memcpy (*r_cert, image, imagelen);
+ *r_certlen = imagelen;
return 0;
}
@@ -2950,7 +4242,7 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
goto leave;
}
totobjlen = objlen + hdrlen;
- assert (totobjlen <= buflen);
+ log_assert (totobjlen <= buflen);
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
@@ -2981,21 +4273,38 @@ readcert_by_cdf (app_t app, cdf_object_t cdf,
goto leave;
}
totobjlen = objlen + hdrlen;
- assert (save_p + totobjlen <= buffer + buflen);
+ log_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)))
+ /* Try to parse and cache the certificate. */
+ err = ksba_cert_new (&cdf->cert);
+ if (!err)
{
- memcpy (cdf->image, *r_cert, *r_certlen);
- cdf->imagelen = *r_certlen;
- }
+ err = ksba_cert_init_from_mem (cdf->cert, buffer, totobjlen);
+ if (!err) /* Call us to use the just cached cert object. */
+ err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
+ if (err)
+ {
+ ksba_cert_release (cdf->cert);
+ cdf->cert = NULL;
+ }
+ }
+ if (err)
+ {
+ log_error ("p15: caching certificate failed: %s\n",
+ gpg_strerror (err));
+ /* We return the certificate anyway so that the caller has a
+ * chance to get an even unsupported or broken certificate. */
+ if (r_cert && r_certlen)
+ {
+ *r_cert = buffer;
+ buffer = NULL;
+ *r_certlen = totobjlen;
+ }
+ }
leave:
xfree (buffer);
@@ -3026,6 +4335,119 @@ do_readcert (app_t app, const char *certid,
}
+/* Sort helper for an array of authentication objects. */
+static int
+compare_aodf_objid (const void *arg_a, const void *arg_b)
+{
+ const aodf_object_t a = *(const aodf_object_t *)arg_a;
+ const aodf_object_t b = *(const aodf_object_t *)arg_b;
+ int rc;
+
+ rc = memcmp (a->objid, b->objid,
+ a->objidlen < b->objidlen? a->objidlen : b->objidlen);
+ if (!rc)
+ {
+ if (a->objidlen < b->objidlen)
+ rc = -1;
+ else if (a->objidlen > b->objidlen)
+ rc = 1;
+ }
+ return rc;
+}
+
+
+static void
+send_key_fpr_line (ctrl_t ctrl, int number, const unsigned char *fpr)
+{
+ char buf[41];
+ char numbuf[25];
+
+ bin2hex (fpr, 20, buf);
+ if (number == -1)
+ *numbuf = 0; /* Don't print the key number */
+ else
+ snprintf (numbuf, sizeof numbuf, "%d", number);
+ send_status_info (ctrl, "KEY-FPR",
+ numbuf, (size_t)strlen(numbuf),
+ buf, (size_t)strlen (buf),
+ NULL, 0);
+}
+
+
+/* If possible Emit a FPR-KEY status line for the private key object
+ * PRKDF using NUMBER as index. */
+static void
+send_key_fpr (app_t app, ctrl_t ctrl, prkdf_object_t prkdf, int number)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+ unsigned char *pk, *fixed_pk;
+ size_t pklen, fixed_pklen;
+ const unsigned char *m, *e, *q;
+ size_t mlen, elen, qlen;
+ unsigned char fpr20[20];
+
+ if (cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf)
+ && cdf_object_from_label (app, prkdf->label, &cdf))
+ return;
+ if (!cdf->cert)
+ readcert_by_cdf (app, cdf, NULL, NULL);
+ if (!cdf->cert)
+ return;
+ if (!prkdf->have_keytime)
+ return;
+ pk = ksba_cert_get_public_key (cdf->cert);
+ if (!pk)
+ return;
+ pklen = gcry_sexp_canon_len (pk, 0, NULL, &err);
+
+ if (uncompress_ecc_q_in_canon_sexp (pk, pklen, &fixed_pk, &fixed_pklen))
+ {
+ xfree (pk);
+ return;
+ }
+ if (fixed_pk)
+ {
+ xfree (pk); pk = NULL;
+ pk = fixed_pk;
+ pklen = fixed_pklen;
+ }
+
+ switch (prkdf->keyalgo)
+ {
+ case GCRY_PK_RSA:
+ if (!get_rsa_pk_from_canon_sexp (pk, pklen,
+ &m, &mlen, &e, &elen)
+ && !compute_openpgp_fpr_rsa (4,
+ prkdf->keytime,
+ m, mlen, e, elen,
+ fpr20, NULL))
+ send_key_fpr_line (ctrl, number, fpr20);
+ break;
+
+ case GCRY_PK_ECC:
+ case GCRY_PK_ECDSA:
+ case GCRY_PK_ECDH:
+ case GCRY_PK_EDDSA:
+ /* Note that NUMBER 2 indicates the encryption key. */
+ if (!get_ecc_q_from_canon_sexp (pk, pklen, &q, &qlen)
+ && !compute_openpgp_fpr_ecc (4,
+ prkdf->keytime,
+ prkdf->keyalgostr,
+ number == 2,
+ q, qlen,
+ prkdf->ecdh_kdf, 4,
+ fpr20, NULL))
+ send_key_fpr_line (ctrl, number, fpr20);
+ break;
+
+ default: /* No Fingerprint for an unknown algo. */
+ break;
+
+ }
+ xfree (pk);
+}
+
/* Implement the GETATTR command. This is similar to the LEARN
command but returns just one value via the status interface. */
@@ -3042,22 +4464,34 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
char *buf;
/* We return the ID of the first private key capable of the
- * requested action. Note that we do not yet return
+ * requested action. If any gpgusage flag has been set for the
+ * card we consult the gpgusage flags and not the regualr usage
+ * flags.
+ */
+ /* FIXME: This changed: Note that we do not yet return
* non_repudiation keys for $SIGNKEYID because our D-Trust
* testcard uses rsaPSS, which is not supported by gpgsm and not
* covered by the VS-NfD approval. */
for (prkdf = app->app_local->private_key_info; prkdf;
prkdf = prkdf->next)
{
- if (name[1] == 'A' && (prkdf->usageflags.sign
- || prkdf->usageflags.sign_recover))
- break;
- else if (name[1] == 'E' && (prkdf->usageflags.decrypt
- || prkdf->usageflags.unwrap))
- break;
- else if (name[1] == 'S' && (prkdf->usageflags.sign
+ if (app->app_local->any_gpgusage)
+ {
+ if ((name[1] == 'A' && prkdf->gpgusage.auth)
+ || (name[1] == 'E' && prkdf->gpgusage.encr)
+ || (name[1] == 'S' && prkdf->gpgusage.sign))
+ break;
+ }
+ else
+ {
+ if ((name[1] == 'A' && (prkdf->usageflags.sign
|| prkdf->usageflags.sign_recover))
- break;
+ || (name[1] == 'E' && (prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap))
+ || (name[1] == 'S' && (prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)))
+ break;
+ }
}
if (prkdf)
{
@@ -3085,7 +4519,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
err = select_ef_by_path (app, path, DIM(path) );
if (!err)
- err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
+ err = iso7816_read_binary (app_get_slot (app), 0, 0,
+ &buffer, &buflen);
if (err)
{
log_error ("p15: error accessing EF(ID): %s\n",
@@ -3111,6 +4546,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
else
{
+ char *sn;
+
/* We use the first private key object which has a serial
* number set. If none was found, we parse the first
* object and see whether this has then a serial number. */
@@ -3125,11 +4562,12 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
if (!prkdf->serial_number)
prkdf = NULL;
}
- if (prkdf)
+ sn = get_dispserialno (app, prkdf);
+ /* Unless there is a bogus S/N in the cert, or the product
+ * has a different strategy for the display-s/n, we should
+ * have a suitable one from the cert now. */
+ if (sn)
{
- char *sn = get_dispserialno (app, prkdf);
- /* Unless there is a bogus S/N in the cert we should
- * have a suitable one from the cert here now. */
err = send_status_printf (ctrl, name, "%s", sn);
xfree (sn);
return err;
@@ -3139,12 +4577,134 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
else if (!strcmp (name, "MANUFACTURER"))
{
- if (app->app_local->manufacturer_id)
+ if (app->app_local->manufacturer_id
+ && (app->app_local->card_product
+ || strchr (app->app_local->manufacturer_id, '[')))
+ return send_status_printf (ctrl, "MANUFACTURER", "0 %s [%s]",
+ app->app_local->manufacturer_id,
+ cardproduct2str (app->app_local->card_product));
+ else if (app->app_local->manufacturer_id)
return send_status_printf (ctrl, "MANUFACTURER", "0 %s",
app->app_local->manufacturer_id);
else
return 0;
}
+ else if (!strcmp (name, "CHV-STATUS") || !strcmp (name, "CHV-LABEL"))
+ {
+ int is_label = (name[4] == 'L');
+ aodf_object_t aodf;
+ aodf_object_t aodfarray[16];
+ int naodf = 0;
+ membuf_t mb;
+ char *p;
+ int i;
+
+ /* Put the AODFs into an array for easier sorting. Note that we
+ * handle onl the first 16 encountrer which should be more than
+ * enough. */
+ for (aodf = app->app_local->auth_object_info;
+ aodf && naodf < DIM(aodfarray); aodf = aodf->next)
+ if (aodf->objidlen && aodf->pin_reference_valid)
+ aodfarray[naodf++] = aodf;
+ qsort (aodfarray, naodf, sizeof *aodfarray, compare_aodf_objid);
+
+ init_membuf (&mb, 256);
+ for (i = 0; i < naodf; i++)
+ {
+ /* int j; */
+ /* log_debug ("p15: AODF[%d] pinref=%lu id=", */
+ /* i, aodfarray[i]->pin_reference); */
+ /* for (j=0; j < aodfarray[i]->objidlen; j++) */
+ /* log_printf ("%02X", aodfarray[i]->objid[j]); */
+ /* Note that there is no need to percent escape the label
+ * because all white space have been replaced by '_'. */
+ if (is_label)
+ put_membuf_printf (&mb, "%s%s", i? " ":"",
+ (aodfarray[i]->label
+ && *aodfarray[i]->label)?
+ aodfarray[i]->label:"X");
+ else
+ put_membuf_printf
+ (&mb, "%s%d", i? " ":"",
+ iso7816_verify_status (app_get_slot (app),
+ aodfarray[i]->pin_reference));
+ }
+ put_membuf( &mb, "", 1);
+ p = get_membuf (&mb, NULL);
+ if (!p)
+ return gpg_error_from_syserror ();
+ err = send_status_direct (ctrl, is_label? "CHV-LABEL":"CHV-STATUS", p);
+ xfree (p);
+ return err;
+ }
+ else if (!strcmp (name, "KEY-LABEL"))
+ {
+ /* Send KEY-LABEL lines for all private key objects. */
+ const char *label;
+ char *idbuf, *labelbuf;
+
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ idbuf = keyref_from_prkdf (app, prkdf);
+ if (!idbuf)
+ return gpg_error_from_syserror ();
+
+ label = (prkdf->label && *prkdf->label)? prkdf->label : "-";
+ labelbuf = percent_data_escape (0, NULL, label, strlen (label));
+ if (!labelbuf)
+ {
+ xfree (idbuf);
+ return gpg_error_from_syserror ();
+ }
+
+ send_status_info (ctrl, name,
+ idbuf, strlen (idbuf),
+ labelbuf, strlen(labelbuf),
+ NULL, 0);
+ xfree (idbuf);
+ xfree (labelbuf);
+ }
+ return 0;
+ }
+ else if (!strcmp (name, "KEY-FPR"))
+ {
+ /* Send KEY-FPR for the two openpgp keys. */
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ if (app->app_local->any_gpgusage)
+ {
+ if (prkdf->gpgusage.sign)
+ break;
+ }
+ else
+ {
+ if (prkdf->usageflags.sign || prkdf->usageflags.sign_recover)
+ break;
+ }
+ }
+ if (prkdf)
+ send_key_fpr (app, ctrl, prkdf, 1);
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ if (app->app_local->any_gpgusage)
+ {
+ if (prkdf->gpgusage.encr)
+ break;
+ }
+ else
+ {
+ if (prkdf->usageflags.decrypt || prkdf->usageflags.unwrap)
+ break;
+ }
+ }
+ if (prkdf)
+ send_key_fpr (app, ctrl, prkdf, 2);
+ return 0;
+ }
+
return gpg_error (GPG_ERR_INV_NAME);
}
@@ -3165,7 +4725,7 @@ micardo_mse (app_t app, unsigned short fid)
unsigned char msebuf[10];
/* Read the KeyD file containing extra information on keys. */
- err = iso7816_select_file (app->slot, 0x0013, 0);
+ err = iso7816_select_file (app_get_slot (app), 0x0013, 0);
if (err)
{
log_error ("p15: error reading EF_keyD: %s\n", gpg_strerror (err));
@@ -3179,7 +4739,8 @@ micardo_mse (app_t app, unsigned short fid)
size_t n, nn;
const unsigned char *p, *pp;
- err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen);
+ 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)
@@ -3222,7 +4783,8 @@ micardo_mse (app_t app, unsigned short fid)
/* Restore the security environment to SE_NUM if needed */
if (se_num)
{
- err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0);
+ err = iso7816_manage_security_env (app_get_slot (app),
+ 0xf3, se_num, NULL, 0);
if (err)
{
log_error ("p15: restoring SE to %d failed: %s\n",
@@ -3237,7 +4799,7 @@ micardo_mse (app_t app, unsigned short fid)
msebuf[2] = 0x80;
msebuf[3] = (refdata >> 8);
msebuf[4] = refdata;
- err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5);
+ err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xb6, msebuf, 5);
if (err)
{
log_error ("p15: setting SE to reference file %04hX failed: %s\n",
@@ -3251,7 +4813,7 @@ micardo_mse (app_t app, unsigned short fid)
/* Prepare the verification of the PIN for the key PRKDF by checking
* the AODF and selecting the key file. KEYREF is used for error
- * messages. */
+ * messages. AODF may be NULL if no verification needs to be done. */
static gpg_error_t
prepare_verify_pin (app_t app, const char *keyref,
prkdf_object_t prkdf, aodf_object_t aodf)
@@ -3259,29 +4821,32 @@ prepare_verify_pin (app_t app, const char *keyref,
gpg_error_t err;
int i;
- if (opt.verbose)
+ if (aodf)
{
- log_info ("p15: using AODF %04hX id=", aodf->fid);
- for (i=0; i < aodf->objidlen; i++)
- log_printf ("%02X", aodf->objid[i]);
- log_printf ("\n");
- }
+ if (opt.verbose)
+ {
+ log_info ("p15: using AODF %04hX id=", aodf->fid);
+ for (i=0; i < aodf->objidlen; i++)
+ log_printf ("%02X", aodf->objid[i]);
+ log_printf ("\n");
+ }
- if (aodf->authid && opt.verbose)
- log_info ("p15: PIN is controlled by another authentication token\n");
+ if (aodf->authid && opt.verbose)
+ log_info ("p15: PIN is controlled by another authentication token\n");
- if (aodf->pinflags.integrity_protected
- || aodf->pinflags.confidentiality_protected)
- {
- log_error ("p15: "
- "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 ("p15: "
- "PIN verification requires padding but no length known\n");
- return gpg_error (GPG_ERR_INV_CARD);
+ if (aodf->pinflags.integrity_protected
+ || aodf->pinflags.confidentiality_protected)
+ {
+ log_error ("p15: 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 ("p15: PIN verification requires"
+ " padding but no length known\n");
+ return gpg_error (GPG_ERR_INV_CARD);
+ }
}
@@ -3301,7 +4866,7 @@ prepare_verify_pin (app_t app, const char *keyref,
log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n",
keyref, gpg_strerror (err));
}
- else
+ else if (prkdf)
{
/* Standard case: Select the key file. Note that this may
* change the security environment thus we need to do it before
@@ -3311,6 +4876,11 @@ prepare_verify_pin (app_t app, const char *keyref,
log_error ("p15: error selecting file for key %s: %s\n",
keyref, gpg_strerror (err));
}
+ else
+ {
+ log_info ("p15: skipping EF selection for auth object '%s'\n", keyref);
+ err = 0;
+ }
return err;
}
@@ -3334,11 +4904,19 @@ static char *
get_dispserialno (app_t app, prkdf_object_t prkdf)
{
char *serial;
+ size_t n;
/* We prefer the SerialNumber RDN from the Subject-DN but we don't
* use it if it features a percent sign (special character in pin
* prompts) or has any control character. */
- if (prkdf && prkdf->serial_number && *prkdf->serial_number
+ if (app->app_local->card_product == CARD_PRODUCT_RSCS)
+ {
+ /* We use only the right 8 hex digits. */
+ serial = app_get_serialno (app);
+ if (serial && (n=strlen (serial)) > 8)
+ memmove (serial, serial + n - 8, 9);
+ }
+ else if (prkdf && prkdf->serial_number && *prkdf->serial_number
&& !strchr (prkdf->serial_number, '%')
&& !any_control_or_space (prkdf->serial_number))
{
@@ -3352,16 +4930,24 @@ get_dispserialno (app_t app, prkdf_object_t prkdf)
}
-/* Return an allocated string to be used as prompt. Returns NULL on
- * malloc error. */
+/* Return an allocated string to be used as prompt. PRKDF may be
+ * NULL. Returns NULL on malloc error. */
static char *
make_pin_prompt (app_t app, int remaining, const char *firstline,
prkdf_object_t prkdf)
{
char *serial, *tmpbuf, *result;
+ const char *holder;
serial = get_dispserialno (app, prkdf);
+ if (prkdf && prkdf->common_name)
+ holder = prkdf->common_name;
+ else if (app->app_local->token_label)
+ holder = app->app_local->token_label;
+ else
+ holder = "";
+
/* 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. */
@@ -3371,7 +4957,7 @@ make_pin_prompt (app_t app, int remaining, const char *firstline,
"%s"),
"\x1e",
serial,
- prkdf->common_name? prkdf->common_name: "",
+ holder,
"");
xfree (serial);
if (!result)
@@ -3408,7 +4994,8 @@ make_pin_prompt (app_t app, int remaining, const char *firstline,
/* Given the private key object PRKDF and its authentication object
- * AODF ask for the PIN and verify that PIN. */
+ * AODF ask for the PIN and verify that PIN. If AODF is NULL, no
+ * authentication is done. */
static gpg_error_t
verify_pin (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg,
@@ -3422,6 +5009,7 @@ verify_pin (app_t app,
const char *s;
int remaining;
int pin_reference;
+ int verified = 0;
int i;
if (!aodf)
@@ -3429,34 +5017,45 @@ verify_pin (app_t app,
pin_reference = aodf->pin_reference_valid? aodf->pin_reference : 0;
- if (app->app_local->card_type == CARD_TYPE_CARDOS_50)
+ if (IS_CARDOS_5 (app))
{
/* We know that this card supports a verify status check. Note
* that in contrast to PIV cards ISO7816_VERIFY_NOT_NEEDED is
- * not supported. */
+ * not supported. Noet that we don't use the pin_verified cache
+ * status because that is not as reliable than to ask the card
+ * about its state. */
+ if (prkdf) /* Clear the cache which we don't use. */
+ prkdf->pin_verified = 0;
+
remaining = iso7816_verify_status (app_get_slot (app), pin_reference);
- if (remaining < 0)
- remaining = -1; /* We don't care about the concrete error. */
- if (remaining < 3)
+ if (remaining == ISO7816_VERIFY_NOT_NEEDED)
{
- if (remaining >= 0)
- log_info ("p15: PIN has %d attempts left\n", remaining);
- /* On error or if less than 3 better ask. */
- prkdf->pin_verified = 0;
+ verified = 1;
+ remaining = -1;
}
+ else if (remaining < 0)
+ remaining = -1; /* We don't care about the concrete error. */
+ else if (remaining < 3)
+ log_info ("p15: PIN has %d attempts left\n", remaining);
}
else
remaining = -1; /* Unknown. */
/* Check whether we already verified it. */
- if (prkdf->pin_verified)
+ if (prkdf && (prkdf->pin_verified || verified))
return 0; /* Already done. */
- if (prkdf->usageflags.non_repudiation
+ if (prkdf
+ && prkdf->usageflags.non_repudiation
&& (app->app_local->card_type == CARD_TYPE_BELPIC
|| app->app_local->card_product == CARD_PRODUCT_DTRUST))
label = _("||Please enter the PIN for the key to create "
"qualified signatures.");
+ else if (aodf->pinflags.so_pin)
+ label = _("|A|Please enter the Admin PIN");
+ else if (aodf->pinflags.unblocking_pin)
+ label = _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the standard keys.");
else
label = _("||Please enter the PIN for the standard keys.");
@@ -3607,7 +5206,8 @@ verify_pin (app_t app,
}
if (opt.verbose)
log_info ("p15: PIN verification succeeded\n");
- prkdf->pin_verified = 1;
+ if (prkdf)
+ prkdf->pin_verified = 1;
return 0;
}
@@ -3628,43 +5228,31 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen )
{
- static unsigned char sha256_prefix[19] = /* OID: 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 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;
- unsigned char data[32+19]; /* Must be large enough for a SHA-256 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. */
- unsigned int hashlen; /* Length of the hash. */
- unsigned int datalen; /* Length of the data to sign (prefix+hash). */
- unsigned char *dataptr;
+ unsigned int digestlen; /* Length of the hash. */
int exmode, le_value;
+ unsigned char oidbuf[64];
+ size_t oidbuflen;
+ size_t n;
+ unsigned char *indata_buffer = NULL; /* Malloced helper. */
(void)ctrl;
- if (!keyidstr || !*keyidstr)
- return gpg_error (GPG_ERR_INV_VALUE);
- if (indatalen != 20 && indatalen != 16
- && indatalen != 35 && indatalen != 36
- && indatalen != (32+19))
+ 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.sign || prkdf->usageflags.sign_recover
- ||prkdf->usageflags.non_repudiation))
+ if (!(prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation
+ || prkdf->gpgusage.cert
+ || prkdf->gpgusage.sign
+ || prkdf->gpgusage.auth ))
{
log_error ("p15: key %s may not be used for signing\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
@@ -3684,10 +5272,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
&& !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
break;
if (!aodf)
- {
- log_error ("p15: authentication object for %s missing\n", keyidstr);
- return gpg_error (GPG_ERR_INV_CARD);
- }
+ log_info ("p15: no authentication for %s needed\n", keyidstr);
/* We need some more info about the key - get the keygrip to
* populate these fields. */
@@ -3698,6 +5283,150 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return err;
}
+
+ digestlen = gcry_md_get_algo_dlen (hashalgo);
+
+ /* We handle ECC separately from RSA so that we do not need to touch
+ * working code. In particular we prepare the input data before the
+ * verify and a possible MSE. */
+ if (prkdf->is_ecc)
+ {
+ if (digestlen != 32 && digestlen != 48 && digestlen != 64)
+ {
+ log_error ("p15: ECC signing not possible: dlen=%u\n", digestlen);
+ err = gpg_error (GPG_ERR_DIGEST_ALGO);
+ goto leave;
+ }
+
+ if (indatalen == digestlen)
+ ; /* Already prepared. */
+ else if (indatalen > digestlen)
+ {
+ /* Assume a PKCS#1 prefix and remove it. */
+ oidbuflen = sizeof oidbuf;
+ err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen);
+ if (err)
+ {
+ log_error ("p15: no OID for hash algo %d\n", hashalgo);
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ if (indatalen != oidbuflen + digestlen
+ || memcmp (indata, oidbuf, oidbuflen))
+ {
+ log_error ("p15: input data too long for ECC: len=%zu\n",
+ indatalen);
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ indata = (const char*)indata + oidbuflen;
+ indatalen -= oidbuflen;
+ }
+ else
+ {
+ log_error ("p15: input data too short for ECC: len=%zu\n",
+ indatalen);
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ }
+ else /* Prepare RSA input. */
+ {
+ unsigned int framelen;
+ unsigned char *frame;
+ int i;
+
+ framelen = (prkdf->keynbits+7) / 8;
+ if (!framelen)
+ {
+ log_error ("p15: key length unknown"
+ " - can't prepare PKCS#v1.5 frame\n");
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ 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 ("p15: no OID for hash algo %d\n", hashalgo);
+ goto leave;
+ }
+ else
+ {
+ 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))
+ ; /* We already got the correct prefix. */
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ log_error ("p15: 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 ("p15: 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;
+ }
+ if (app->app_local->card_type == CARD_TYPE_BELPIC)
+ {
+ /* This card wants only the plain hash w/o any prefix. */
+ /* FIXME: We may want to remove this code because it is unlikely
+ * that such cards are still in use. */
+ memcpy (frame, indata, indatalen);
+ framelen = indatalen;
+ }
+ else
+ {
+ 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;
+ }
+
/* Prepare PIN verification. This is split so that we can do
* MSE operation for some task after having selected the key file but
* before sending the verify APDU. */
@@ -3709,7 +5438,8 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
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. */
+ for the BELPIC card here.
+ FIXME: see comment above about these cards. */
if (app->app_local->card_type == CARD_TYPE_BELPIC)
{
unsigned char mse[5];
@@ -3723,10 +5453,9 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
mse[3] = 0x84; /* Private key reference tag. */
mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82;
- err = iso7816_manage_security_env (app->slot,
+ err = iso7816_manage_security_env (app_get_slot (app),
0x41, 0xB6,
mse, sizeof mse);
- no_data_padding = 1;
mse_done = 1;
}
if (err)
@@ -3741,72 +5470,6 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
if (err)
return err;
-
- /* 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);
- datalen = hashlen = 36;
- }
- 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);
- datalen = 35;
- hashlen = 20;
- }
- else if (indatalen == 32 + 19)
- {
- /* Seems to be a prepared SHA256 DER object. */
- if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
- ;
- else
- return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
- memcpy (data, indata, indatalen);
- datalen = 51;
- hashlen = 32;
- }
- else
- {
- /* Need to prepend the prefix. */
- if (hashalgo == GCRY_MD_SHA256)
- {
- memcpy (data, sha256_prefix, 19);
- memcpy (data+19, indata, indatalen);
- datalen = 51;
- hashlen = 32;
- }
- else if (hashalgo == GCRY_MD_SHA1)
- {
- memcpy (data, sha1_prefix, 15);
- memcpy (data+15, indata, indatalen);
- datalen = 35;
- hashlen = 20;
- }
- else if (hashalgo == GCRY_MD_RMD160)
- {
- memcpy (data, rmd160_prefix, 15);
- memcpy (data+15, indata, indatalen);
- datalen = 35;
- hashlen = 20;
- }
- else
- return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
- }
-
-
/* Manage security environment needs to be tweaked for certain cards. */
if (mse_done)
err = 0;
@@ -3830,7 +5493,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
mse[1] = 1;
mse[2] = prkdf->key_reference;
- err = iso7816_manage_security_env (app->slot,
+ err = iso7816_manage_security_env (app_get_slot (app),
0x41, 0xB6,
mse, sizeof mse);
}
@@ -3840,13 +5503,6 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
return err;
}
- dataptr = data;
- if (no_data_padding)
- {
- dataptr += datalen - hashlen;
- datalen = hashlen;
- }
-
if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits > 2048)
{
exmode = 1;
@@ -3859,9 +5515,11 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
}
err = iso7816_compute_ds (app_get_slot (app),
- exmode, dataptr, datalen,
+ exmode, indata, indatalen,
le_value, outdata, outdatalen);
+ leave:
+ xfree (indata_buffer);
return err;
}
@@ -3889,7 +5547,7 @@ do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
- if (!prkdf->usageflags.sign)
+ if (!(prkdf->usageflags.sign || prkdf->gpgusage.auth))
{
log_error ("p15: key %s may not be used for authentication\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
@@ -3929,9 +5587,11 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
if (err)
return err;
- if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
+ if (!(prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap
+ || prkdf->gpgusage.encr ))
{
- log_error ("p15: key %s may not be used for decruption\n", keyidstr);
+ log_error ("p15: key %s may not be used for decryption\n", keyidstr);
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
@@ -3948,10 +5608,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
&& !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
break;
if (!aodf)
- {
- log_error ("p15: authentication object for %s missing\n", keyidstr);
- return gpg_error (GPG_ERR_INV_CARD);
- }
+ log_info ("p15: no authentication for %s needed\n", keyidstr);
/* We need some more info about the key - get the keygrip to
* populate these fields. */
@@ -3969,6 +5626,18 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (err)
return err;
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+
+ err = iso7816_manage_security_env (app_get_slot (app), 0xF3, 0x01,
+ NULL, 0);
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+ }
+
/* The next is guess work for CardOS. */
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
@@ -3990,19 +5659,31 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
}
else if (prkdf->key_reference_valid)
{
- unsigned char mse[6];
+ unsigned char mse[9];
+ int i;
/* Note: This works with CardOS but the D-Trust card has the
* problem that the next created signature would be broken. */
- mse[0] = 0x80; /* Algorithm reference. */
- mse[1] = 1;
- mse[2] = 0x0a; /* RSA, no padding. */
- mse[3] = 0x84;
- mse[4] = 1;
- mse[5] = prkdf->key_reference;
+ i = 0;
+ if (!prkdf->is_ecc)
+ {
+ mse[i++] = 0x80; /* Algorithm reference. */
+ mse[i++] = 1;
+ mse[i++] = 0x0a; /* RSA, no padding. */
+ }
+ mse[i++] = 0x84; /* Key reference. */
+ mse[i++] = 1;
+ mse[i++] = prkdf->key_reference;
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ mse[i++] = 0x95; /* ???. */
+ mse[i++] = 1;
+ mse[i++] = 0x40;
+ }
+ log_assert (i <= DIM(mse));
err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
- mse, sizeof mse);
+ mse, i);
}
/* Check for MSE error. */
if (err)
@@ -4022,10 +5703,192 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
padind = 0x81;
- err = iso7816_decipher (app_get_slot (app), exmode,
- indata, indatalen,
- le_value, padind,
- outdata, outdatalen);
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ if ((indatalen & 1) && *(const char *)indata == 0x04)
+ {
+ /* Strip indicator byte. */
+ indatalen--;
+ indata = (const char *)indata + 1;
+ }
+ err = iso7816_pso_csv (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value,
+ outdata, outdatalen);
+ }
+ else
+ {
+ err = iso7816_decipher (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value, padind,
+ outdata, outdatalen);
+ }
+
+ return err;
+}
+
+
+/* Perform a simple verify operation for the PIN specified by
+ * KEYIDSTR. Note that we require a key reference which is then used
+ * to select the authentication object. Return GPG_ERR_NO_PIN if a
+ * PIN is not required for using the private key KEYIDSTR. */
+static gpg_error_t
+do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf; /* The private key object. */
+ aodf_object_t aodf; /* The associated authentication object. */
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err
+ && gpg_err_code (err) != GPG_ERR_INV_ID
+ && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ return err;
+
+ if (err) /* Not found or invalid - assume it is the label. */
+ {
+ prkdf = NULL;
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->label && !ascii_strcasecmp (aodf->label, keyidstr))
+ break;
+ if (!aodf)
+ return err; /* Re-use the original error code. */
+ }
+ else /* Find the authentication object to this private key object. */
+ {
+ if (!prkdf->authid)
+ {
+ log_error ("p15: no authentication object defined for %s\n",
+ keyidstr);
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ }
+ 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) /* None found. */
+ return gpg_error (GPG_ERR_NO_PIN);
+ }
+
+ err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
+ if (!err)
+ err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
+
+ 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, int capability)
+{
+ gpg_error_t err;
+ char *serialno = NULL;
+ int as_data = 0;
+ prkdf_object_t prkdf;
+
+ /* 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:
+ as_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 (prkdf = app->app_local->private_key_info;
+ prkdf; prkdf = prkdf->next)
+ {
+ if (keygrip_from_prkdf (app, prkdf))
+ continue;
+
+ if (action == KEYGRIP_ACTION_LOOKUP)
+ {
+ if (!strcmp (prkdf->keygrip, want_keygripstr))
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ else if (!want_keygripstr || !strcmp (prkdf->keygrip, want_keygripstr))
+ {
+ char *keyref;
+
+ if (capability == GCRY_PK_USAGE_SIGN)
+ {
+ if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation))
+ continue;
+ }
+ else if (capability == GCRY_PK_USAGE_ENCR)
+ {
+ if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
+ continue;
+ }
+ else if (capability == GCRY_PK_USAGE_AUTH)
+ {
+ if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover))
+ continue;
+ }
+
+ keyref = keyref_from_prkdf (app, prkdf);
+ if (!keyref)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ send_keyinfo (ctrl, as_data, prkdf->keygrip, serialno, keyref);
+ xfree (keyref);
+ 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 (serialno);
return err;
}
@@ -4086,14 +5949,17 @@ read_home_df (int slot, int *r_belpic)
gpg_error_t
app_select_p15 (app_t app)
{
- int slot = app->slot;
+ 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;
+ unsigned char *fci = NULL;
+ size_t fcilen;
- rc = iso7816_select_application (slot, pkcs15_aid, sizeof pkcs15_aid, 0);
+ rc = iso7816_select_application_ext (slot, pkcs15_aid, sizeof pkcs15_aid, 1,
+ &fci, &fcilen);
if (rc)
{ /* Not found: Try to locate it from 2F00. We use direct path
selection here because it seems that the Belgian eID card
@@ -4118,7 +5984,7 @@ app_select_p15 (app_t app)
}
if (rc)
{ /* Still not found: Try the default DF. */
- def_home_df = 0x5015;
+ def_home_df = DEFAULT_HOME_DF;
rc = iso7816_select_file (slot, def_home_df, 1);
}
if (!rc)
@@ -4136,7 +6002,7 @@ app_select_p15 (app_t app)
size_t atrlen;
int i;
- atr = apdu_get_atr (app->slot, &atrlen);
+ atr = apdu_get_atr (app_get_slot (app), &atrlen);
if (!atr)
rc = gpg_error (GPG_ERR_INV_CARD);
else
@@ -4163,11 +6029,18 @@ app_select_p15 (app_t app)
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. */
+ /* Set the home DF from the FCI returned by the select. */
+ if (!def_home_df && fci)
+ {
+ const unsigned char *s;
+ size_t n;
+
+ s = find_tlv (fci, fcilen, 0x83, &n);
+ if (s && n == 2)
+ def_home_df = buf16_to_ushort (s);
+ else
+ log_error ("p15: select(AID) did not return the DF\n");
+ }
app->app_local->home_df = def_home_df;
/* Store the card type. FIXME: We might want to put this into
@@ -4177,6 +6050,16 @@ app_select_p15 (app_t app)
app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
/* Store whether we may and should use direct path selection. */
+ switch (card_type)
+ {
+ case CARD_TYPE_CARDOS_50:
+ case CARD_TYPE_CARDOS_53:
+ direct = 1;
+ break;
+ default:
+ /* Use whatever has been determined above. */
+ break;
+ }
app->app_local->direct_path_selection = direct;
/* Read basic information and thus check whether this is a real
@@ -4189,8 +6072,9 @@ app_select_p15 (app_t app)
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->serialnolen == 12
- && !memcmp (app->serialno, "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12))
+ if (APP_CARD(app)->serialnolen == 12
+ && !memcmp (APP_CARD(app)->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). . */
@@ -4198,20 +6082,22 @@ app_select_p15 (app_t app)
/* FIXME: actually get it from EF(TokenInfo). */
- p = xtrymalloc (3 + app->serialnolen);
+ p = xtrymalloc (3 + APP_CARD(app)->serialnolen);
if (!p)
rc = gpg_error (gpg_err_code_from_errno (errno));
else
{
memcpy (p, "\xff\x01", 3);
- memcpy (p+3, app->serialno, app->serialnolen);
- app->serialnolen += 3;
- xfree (app->serialno);
- app->serialno = p;
+ memcpy (p+3, APP_CARD(app)->serialno, APP_CARD(app)->serialnolen);
+ APP_CARD(app)->serialnolen += 3;
+ xfree (APP_CARD(app)->serialno);
+ APP_CARD(app)->serialno = p;
}
}
app->fnc.deinit = do_deinit;
+ app->fnc.prep_reselect = NULL;
+ app->fnc.reselect = NULL;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.getattr = do_getattr;
@@ -4221,12 +6107,14 @@ app_select_p15 (app_t app)
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher;
app->fnc.change_pin = NULL;
- app->fnc.check_pin = NULL;
+ app->fnc.check_pin = do_check_pin;
+ app->fnc.with_keygrip = do_with_keygrip;
leave:
if (rc)
do_deinit (app);
}
+ xfree (fci);
return rc;
}