/* app-piv.c - The OpenPGP card application. * Copyright (C) 2019, 2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* Some notes: * - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4 * - https://developers.yubico.com/PIV/Introduction/PIV_attestation.html * * - Access control matrix: * | Action | 9B | PIN | PUK | | * |--------------+-----+-----+-----+------------------------------| * | Generate key | yes | | | | * | Change 9B | yes | | | | * | Change retry | yes | yes | | Yubikey only | * | Import key | yes | | | | * | Import cert | yes | | | | * | Change CHUID | yes | | | | * | Reset card | | | | PIN and PUK in blocked state | * | Verify PIN | | yes | | | * | Sign data | | yes | | | * | Decrypt data | | yes | | | * | Change PIN | | yes | | | * | Change PUK | | | yes | | * | Unblock PIN | | | yes | New PIN required | * |---------------------------------------------------------------| * (9B indicates the 24 byte PIV Card Application Administration Key) * * - When generating a key we store the created public key in the * corresponding data object, so that gpg and gpgsm are able to get * the public key, create a certificate and store that then in that * data object. That is not standard compliant but due to the use * of other tags, it should not harm. See do_genkey for the actual * used tag structure. */ #include #include #include #include #include #include #include #include "scdaemon.h" #include "../common/util.h" #include "../common/i18n.h" #include "iso7816.h" #include "../common/tlv.h" #include "../common/host2net.h" #include "apdu.h" /* We use apdu_send_direct. */ #define PIV_ALGORITHM_3DES_ECB_0 0x00 #define PIV_ALGORITHM_2DES_ECB 0x01 #define PIV_ALGORITHM_2DES_CBC 0x02 #define PIV_ALGORITHM_3DES_ECB 0x03 #define PIV_ALGORITHM_3DES_CBC 0x04 #define PIV_ALGORITHM_RSA 0x07 #define PIV_ALGORITHM_AES128_ECB 0x08 #define PIV_ALGORITHM_AES128_CBC 0x09 #define PIV_ALGORITHM_AES192_ECB 0x0A #define PIV_ALGORITHM_AES192_CBC 0x0B #define PIV_ALGORITHM_AES256_ECB 0x0C #define PIV_ALGORITHM_AES256_CBC 0x0D #define PIV_ALGORITHM_ECC_P256 0x11 #define PIV_ALGORITHM_ECC_P384 0x14 /* The AID for PIV. */ static char const piv_aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */ 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ }; /* A table describing the DOs of a PIV card. */ struct data_object_s { unsigned int tag; unsigned int mandatory:1; unsigned int acr_contact:2; /* 0=always, 1=VCI, 2=PIN, 3=PINorOCC */ unsigned int acr_contactless:2; /* 0=always, 1=VCI, 2=VCIandPIN, 3=VCIand(PINorOCC) */ unsigned int dont_cache:1; /* Data item will not be cached. */ unsigned int flush_on_error:1; /* Flush cached item on error. */ unsigned int keypair:1; /* Has a public key for a keypair. */ const char keyref[3]; /* The key reference. */ const char *oidsuffix; /* Suffix of the OID. */ const char *usage; /* Usage string for a keypair or NULL. */ const char *desc; /* Description of the DO. */ }; typedef struct data_object_s *data_object_t; static struct data_object_s data_objects[] = { { 0x5FC107, 1, 0,1, 0,0, 0, "", "1.219.0", NULL, "Card Capability Container"}, { 0x5FC102, 1, 0,0, 0,0, 0, "", "2.48.0", NULL, "Cardholder Unique Id" }, { 0x5FC105, 1, 0,1, 0,0, 1, "9A", "2.1.1", "a", "Cert PIV Authentication" }, { 0x5FC103, 1, 2,2, 0,0, 0, "", "2.96.16", NULL, "Cardholder Fingerprints" }, { 0x5FC106, 1, 0,1, 0,0, 0, "", "2.144.0", NULL, "Security Object" }, { 0x5FC108, 1, 2,2, 0,0, 0, "", "2.96.48", NULL, "Cardholder Facial Image" }, { 0x5FC101, 1, 0,0, 0,0, 1, "9E", "2.5.0", "a", "Cert Card Authentication"}, { 0x5FC10A, 0, 0,1, 0,0, 1, "9C", "2.1.0", "sc", "Cert Digital Signature" }, { 0x5FC10B, 0, 0,1, 0,0, 1, "9D", "2.1.2", "e", "Cert Key Management" }, { 0x5FC109, 0, 3,3, 0,0, 0, "", "2.48.1", NULL, "Printed Information" }, { 0x7E, 0, 0,0, 0,0, 0, "", "2.96.80", NULL, "Discovery Object" }, { 0x5FC10C, 0, 0,1, 0,0, 0, "", "2.96.96", NULL, "Key History Object" }, { 0x5FC10D, 0, 0,1, 0,0, 0, "82", "2.16.1", "e", "Retired Cert Key Mgm 1" }, { 0x5FC10E, 0, 0,1, 0,0, 0, "83", "2.16.2", "e", "Retired Cert Key Mgm 2" }, { 0x5FC10F, 0, 0,1, 0,0, 0, "84", "2.16.3", "e", "Retired Cert Key Mgm 3" }, { 0x5FC110, 0, 0,1, 0,0, 0, "85", "2.16.4", "e", "Retired Cert Key Mgm 4" }, { 0x5FC111, 0, 0,1, 0,0, 0, "86", "2.16.5", "e", "Retired Cert Key Mgm 5" }, { 0x5FC112, 0, 0,1, 0,0, 0, "87", "2.16.6", "e", "Retired Cert Key Mgm 6" }, { 0x5FC113, 0, 0,1, 0,0, 0, "88", "2.16.7", "e", "Retired Cert Key Mgm 7" }, { 0x5FC114, 0, 0,1, 0,0, 0, "89", "2.16.8", "e", "Retired Cert Key Mgm 8" }, { 0x5FC115, 0, 0,1, 0,0, 0, "8A", "2.16.9", "e", "Retired Cert Key Mgm 9" }, { 0x5FC116, 0, 0,1, 0,0, 0, "8B", "2.16.10", "e", "Retired Cert Key Mgm 10" }, { 0x5FC117, 0, 0,1, 0,0, 0, "8C", "2.16.11", "e", "Retired Cert Key Mgm 11" }, { 0x5FC118, 0, 0,1, 0,0, 0, "8D", "2.16.12", "e", "Retired Cert Key Mgm 12" }, { 0x5FC119, 0, 0,1, 0,0, 0, "8E", "2.16.13", "e", "Retired Cert Key Mgm 13" }, { 0x5FC11A, 0, 0,1, 0,0, 0, "8F", "2.16.14", "e", "Retired Cert Key Mgm 14" }, { 0x5FC11B, 0, 0,1, 0,0, 0, "90", "2.16.15", "e", "Retired Cert Key Mgm 15" }, { 0x5FC11C, 0, 0,1, 0,0, 0, "91", "2.16.16", "e", "Retired Cert Key Mgm 16" }, { 0x5FC11D, 0, 0,1, 0,0, 0, "92", "2.16.17", "e", "Retired Cert Key Mgm 17" }, { 0x5FC11E, 0, 0,1, 0,0, 0, "93", "2.16.18", "e", "Retired Cert Key Mgm 18" }, { 0x5FC11F, 0, 0,1, 0,0, 0, "94", "2.16.19", "e", "Retired Cert Key Mgm 19" }, { 0x5FC120, 0, 0,1, 0,0, 0, "95", "2.16.20", "e", "Retired Cert Key Mgm 20" }, { 0x5FC121, 0, 2,2, 0,0, 0, "", "2.16.21", NULL, "Cardholder Iris Images" }, { 0x7F61, 0, 0,0, 0,0, 0, "", "2.16.22", NULL, "BIT Group Template" }, { 0x5FC122, 0, 0,0, 0,0, 0, "", "2.16.23", NULL, "SM Cert Signer" }, { 0x5FC123, 0, 3,3, 0,0, 0, "", "2.16.24", NULL, "Pairing Code Ref Data" }, { 0 } /* Other key reference values without a data object: * "00" Global PIN (not cleared by application switching) * "04" PIV Secure Messaging Key * "80" PIV Application PIN * "81" PIN Unblocking Key * "96" Primary Finger OCC * "97" Secondary Finger OCC * "98" Pairing Code * "9B" PIV Card Application Administration Key * * Yubikey specific data objects: * "F9" Attestation key (preloaded can be replaced) */ }; /* One cache item for DOs. */ struct cache_s { struct cache_s *next; int tag; size_t length; unsigned char data[1]; }; /* Object with application specific data. */ struct app_local_s { /* A linked list with cached DOs. */ struct cache_s *cache; /* Various flags. */ struct { unsigned int yubikey:1; /* This is on a Yubikey. */ } flags; /* Keep track on whether we cache a certain PIN so that we get it * from the cache only if we know we cached it. This inhibits the * use of the same cache entry for a card plugged in and out without * gpg-agent having noticed that due to a bug. */ struct { unsigned int maybe_00:1; unsigned int maybe_80:1; unsigned int maybe_81:1; unsigned int maybe_96:1; unsigned int maybe_97:1; unsigned int maybe_98:1; unsigned int maybe_9B:1; } pincache; }; /***** Local prototypes *****/ static gpg_error_t get_keygrip_by_tag (app_t app, unsigned int tag, char **r_keygripstr, int *got_cert); static gpg_error_t genkey_parse_rsa (const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp); static gpg_error_t genkey_parse_ecc (const unsigned char *data, size_t datalen, int mechanism, gcry_sexp_t *r_sexp); /* Deconstructor. */ static void do_deinit (app_t app) { if (app && app->app_local) { struct cache_s *c, *c2; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } xfree (app->app_local); app->app_local = NULL; } } /* Wrapper around iso7816_get_data which first tries to get the data * from the cache. With GET_IMMEDIATE passed as true, the cache is * bypassed. The tag-53 container is also removed. */ static gpg_error_t get_cached_data (app_t app, int tag, unsigned char **result, size_t *resultlen, int get_immediate) { gpg_error_t err; int i; unsigned char *p; const unsigned char *s; size_t len, n; struct cache_s *c; *result = NULL; *resultlen = 0; if (!get_immediate) { for (c=app->app_local->cache; c; c = c->next) if (c->tag == tag) { if(c->length) { p = xtrymalloc (c->length); if (!p) return gpg_error_from_syserror (); memcpy (p, c->data, c->length); *result = p; } *resultlen = c->length; return 0; } } err = iso7816_get_data_odd (app_get_slot (app), 0, tag, &p, &len); if (err) return err; /* Unless the Discovery Object or the BIT Group Template is * requested, remove the outer container. * (SP800-73.4 Part 2, section 3.1.2) */ if (tag == 0x7E || tag == 0x7F61) ; else if (len && *p == 0x53 && (s = find_tlv (p, len, 0x53, &n))) { memmove (p, s, n); len = n; } if (len) *result = p; *resultlen = len; /* Check whether we should cache this object. */ if (get_immediate) return 0; for (i=0; data_objects[i].tag; i++) if (data_objects[i].tag == tag) { if (data_objects[i].dont_cache) return 0; break; } /* Okay, cache it. */ for (c=app->app_local->cache; c; c = c->next) log_assert (c->tag != tag); c = xtrymalloc (sizeof *c + len); if (c) { if (len) memcpy (c->data, p, len); else xfree (p); c->length = len; c->tag = tag; c->next = app->app_local->cache; app->app_local->cache = c; } return 0; } /* Remove data object described by TAG from the cache. If TAG is 0 * all cache iterms are flushed. */ static void flush_cached_data (app_t app, int tag) { struct cache_s *c, *cprev; for (c=app->app_local->cache, cprev=NULL; c; cprev=c, c = c->next) if (c->tag == tag || !tag) { if (cprev) cprev->next = c->next; else app->app_local->cache = c->next; xfree (c); for (c=app->app_local->cache; c ; c = c->next) { log_assert (c->tag != tag); /* Oops: duplicated entry. */ } return; } } /* Get the DO identified by TAG from the card in SLOT and return a * buffer with its content in RESULT and NBYTES. The return value is * NULL if not found or a pointer which must be used to release the * buffer holding value. */ static void * get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, int *r_err) { gpg_error_t err; int i; unsigned char *buffer; size_t buflen; unsigned char *value; size_t valuelen; gpg_error_t dummyerr; if (!r_err) r_err = &dummyerr; *result = NULL; *nbytes = 0; *r_err = 0; for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) ; value = NULL; err = gpg_error (GPG_ERR_ENOENT); if (!value) /* Not in a constructed DO, try simple. */ { err = get_cached_data (app, tag, &buffer, &buflen, data_objects[i].dont_cache); if (!err) { value = buffer; valuelen = buflen; } } if (!err) { *nbytes = valuelen; *result = value; return buffer; } *r_err = err; return NULL; } static void dump_all_do (int slot) { gpg_error_t err; int i; unsigned char *buffer; size_t buflen; for (i=0; data_objects[i].tag; i++) { /* We don't try extended length APDU because such large DO would be pretty useless in a log file. */ err = iso7816_get_data_odd (slot, 0, data_objects[i].tag, &buffer, &buflen); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT && !data_objects[i].mandatory) ; else log_info ("DO '%s' not available: %s\n", data_objects[i].desc, gpg_strerror (err)); } else { if (data_objects[i].tag == 0x5FC109) log_info ("DO '%s': '%.*s'\n", data_objects[i].desc, (int)buflen, buffer); else { log_info ("DO '%s': ", data_objects[i].desc); if (buflen > 16 && opt.verbose < 2) { log_printhex (buffer, 16, NULL); log_printf ("[...]\n"); } else log_printhex (buffer, buflen, ""); } } xfree (buffer); buffer = NULL; } } /* Create a TLV tag and value and store it at BUFFER. Return the * length of tag and length. A LENGTH greater than 65535 is * truncated. TAG must be less or equal to 2^16. If BUFFER is NULL, * only the required length is computed. */ static size_t add_tlv (unsigned char *buffer, unsigned int tag, size_t length) { if (length > 0xffff) length = 0xffff; if (buffer) { unsigned char *p = buffer; if (tag > 0xff) *p++ = tag >> 8; *p++ = tag; if (length < 128) *p++ = length; else if (length < 256) { *p++ = 0x81; *p++ = length; } else { *p++ = 0x82; *p++ = length >> 8; *p++ = length; } return p - buffer; } else { size_t n = 0; if (tag > 0xff) n++; n++; if (length < 128) n++; else if (length < 256) n += 2; else n += 3; return n; } } /* Function to build a list of TLV and return the result in a malloced * buffer. The varargs are tuples of (int,size_t,void) each with the * tag, the length and the actual data. A (0,0,NULL) tuple terminates * the list. Up to 10 tuples are supported. If SECMEM is true the * returned buffer is allocated in secure memory. */ static gpg_error_t concat_tlv_list (int secure, unsigned char **r_result, size_t *r_resultlen, ...) { gpg_error_t err; va_list arg_ptr; struct { int tag; unsigned int len; unsigned int contlen; const void *data; } argv[10]; int i, j, argc; unsigned char *data = NULL; size_t datalen; unsigned char *p; size_t n; *r_result = NULL; *r_resultlen = 0; /* Collect all args. Check that length is <= 2^16 to match the * behaviour of add_tlv. */ va_start (arg_ptr, r_resultlen); argc = 0; while (((argv[argc].tag = va_arg (arg_ptr, int)))) { argv[argc].len = va_arg (arg_ptr, size_t); argv[argc].contlen = 0; argv[argc].data = va_arg (arg_ptr, const void *); if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff) { va_end (arg_ptr); err = gpg_error (GPG_ERR_EINVAL); goto leave; } argc++; } va_end (arg_ptr); /* Compute the required buffer length and allocate the buffer. */ datalen = 0; for (i=0; i < argc; i++) { if (!argv[i].len && !argv[i].data) { /* Constructed tag. Compute its length. Note that we * currently allow only one constructed tag in the list. */ for (n=0, j = i + 1; j < argc; j++) { log_assert (!(!argv[j].len && !argv[j].data)); n += add_tlv (NULL, argv[j].tag, argv[j].len); n += argv[j].len; } argv[i].contlen = n; datalen += add_tlv (NULL, argv[i].tag, n); } else { datalen += add_tlv (NULL, argv[i].tag, argv[i].len); datalen += argv[i].len; } } data = secure? xtrymalloc_secure (datalen) : xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } /* Copy that data to the buffer. */ p = data; for (i=0; i < argc; i++) { if (!argv[i].len && !argv[i].data) { /* Constructed tag. */ p += add_tlv (p, argv[i].tag, argv[i].contlen); } else { p += add_tlv (p, argv[i].tag, argv[i].len); memcpy (p, argv[i].data, argv[i].len); p += argv[i].len; } } log_assert ( data + datalen == p ); *r_result = data; data = NULL; *r_resultlen = datalen; err = 0; leave: xfree (data); return err; } /* Wrapper around iso7816_put_data_odd which also sets the tag into * the '5C' data object. The varargs are tuples of (int,size_t,void) * with the tag, the length and the actual data. A (0,0,NULL) tuple * terminates the list. Up to 10 tuples are supported. */ static gpg_error_t put_data (int slot, unsigned int tag, ...) { gpg_error_t err; va_list arg_ptr; struct { int tag; size_t len; const void *data; } argv[10]; int i, argc; unsigned char data5c[5]; size_t data5clen; unsigned char *data = NULL; size_t datalen; unsigned char *p; size_t n; /* Collect all args. Check that length is <= 2^16 to match the * behaviour of add_tlv. */ va_start (arg_ptr, tag); argc = 0; while (((argv[argc].tag = va_arg (arg_ptr, int)))) { argv[argc].len = va_arg (arg_ptr, size_t); argv[argc].data = va_arg (arg_ptr, const void *); if (argc >= DIM (argv)-1 || argv[argc].len > 0xffff) { va_end (arg_ptr); return GPG_ERR_EINVAL; } argc++; } va_end (arg_ptr); /* Build the TLV with the tag to be updated. */ data5c[0] = 0x5c; /* Tag list */ if (tag <= 0xff) { data5c[1] = 1; data5c[2] = tag; data5clen = 3; } else if (tag <= 0xffff) { data5c[1] = 2; data5c[2] = (tag >> 8); data5c[3] = tag; data5clen = 4; } else { data5c[1] = 3; data5c[2] = (tag >> 16); data5c[3] = (tag >> 8); data5c[4] = tag; data5clen = 5; } /* Compute the required buffer length and allocate the buffer. */ n = 0; for (i=0; i < argc; i++) { n += add_tlv (NULL, argv[i].tag, argv[i].len); n += argv[i].len; } datalen = data5clen + add_tlv (NULL, 0x53, n) + n; data = xtrymalloc (datalen); if (!data) { err = gpg_error_from_syserror (); goto leave; } /* Copy that data to the buffer. */ p = data; memcpy (p, data5c, data5clen); p += data5clen; p += add_tlv (p, 0x53, n); for (i=0; i < argc; i++) { p += add_tlv (p, argv[i].tag, argv[i].len); memcpy (p, argv[i].data, argv[i].len); p += argv[i].len; } log_assert ( data + datalen == p ); err = iso7816_put_data_odd (slot, -1 /* use command chaining */, 0x3fff, data, datalen); leave: xfree (data); return err; } /* Parse the key reference KEYREFSTR which is expected to hold a key * reference for a CHV object. Return the one octet keyref or -1 for * an invalid reference. */ static int parse_chv_keyref (const char *keyrefstr) { if (!keyrefstr) return -1; else if (!ascii_strcasecmp (keyrefstr, "PIV.00")) return 0x00; else if (!ascii_strcasecmp (keyrefstr, "PIV.80")) return 0x80; else if (!ascii_strcasecmp (keyrefstr, "PIV.81")) return 0x81; else return -1; } /* Return an allocated string with the serial number in a format to be * show to the user. With FAILMODE is true return NULL if such an * abbreviated S/N is not available, else return the full serial * number as a hex string. May return NULL on malloc problem. */ static char * get_dispserialno (app_t app, int failmode) { char *result; if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4 && !memcmp (app->card->serialno, "\xff\x02\x00", 3)) { /* This is a 4 byte S/N of a Yubikey which seems to be printed * on the token in decimal. Maybe they will print larger S/N * also in decimal but we can't be sure, thus do it only for * these 32 bit numbers. */ unsigned long sn; sn = app->card->serialno[4] * 16777216; sn += app->card->serialno[5] * 65536; sn += app->card->serialno[6] * 256; sn += app->card->serialno[7]; result = xtryasprintf ("yk-%lu", sn); } else if (failmode) result = NULL; /* No Abbreviated S/N. */ else result = app_get_serialno (app); return result; } /* The verify command can be used to retrieve the security status of * the card. Given the PIN name (e.g. "PIV.80" for the application * pin, a ISO7817_VERIFY_* code is returned or a non-negative number * of verification attempts left. */ static int get_chv_status (app_t app, const char *keyrefstr) { int keyref; keyref = parse_chv_keyref (keyrefstr); if (!keyrefstr) return ISO7816_VERIFY_ERROR; return iso7816_verify_status (app_get_slot (app), keyref); } /* Implementation of the GETATTR command. This is similar to the * LEARN command but returns only one value via status lines. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { const char *name; int tag; int special; } table[] = { { "SERIALNO", 0x0000, -1 }, { "$AUTHKEYID", 0x0000, -2 }, /* Default ssh key. */ { "$ENCRKEYID", 0x0000, -6 }, /* Default encryption key. */ { "$SIGNKEYID", 0x0000, -7 }, /* Default signing key. */ { "$DISPSERIALNO",0x0000, -3 }, { "CHV-STATUS", 0x0000, -4 }, { "CHV-USAGE", 0x007E, -5 } }; gpg_error_t err = 0; int idx; void *relptr; unsigned char *value; size_t valuelen; const unsigned char *s; size_t n; for (idx=0; (idx < DIM (table) && ascii_strcasecmp (table[idx].name, name)); idx++) ; if (!(idx < DIM (table))) err = gpg_error (GPG_ERR_INV_NAME); else if (table[idx].special == -1) { char *serial = app_get_serialno (app); if (serial) { send_status_direct (ctrl, "SERIALNO", serial); xfree (serial); } } else if (table[idx].special == -2) { char const tmp[] = "PIV.9A"; /* Cert PIV Authenticate. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else if (table[idx].special == -3) { char *tmp = get_dispserialno (app, 1); if (tmp) { send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, (size_t)0); xfree (tmp); } else err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */ } else if (table[idx].special == -4) /* CHV-STATUS */ { int tmp[4]; tmp[0] = get_chv_status (app, "PIV.00"); tmp[1] = get_chv_status (app, "PIV.80"); tmp[2] = get_chv_status (app, "PIV.81"); err = send_status_printf (ctrl, table[idx].name, "%d %d %d", tmp[0], tmp[1], tmp[2]); } else if (table[idx].special == -5) /* CHV-USAGE (aka PIN Usage Policy) */ { /* We return 2 hex bytes or nothing in case the discovery object * is not supported. */ relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); if (relptr) { s = find_tlv (value, valuelen, 0x7E, &n); if (s && n && (s = find_tlv (s, n, 0x5F2F, &n)) && n >=2 ) err = send_status_printf (ctrl, table[idx].name, "%02X %02X", s[0], s[1]); xfree (relptr); } } else if (table[idx].special == -6) { char const tmp[] = "PIV.9D"; /* Key Management. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else if (table[idx].special == -7) { char const tmp[] = "PIV.9C"; /* Digital Signature. */ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); } else { relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err); if (relptr) { send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); xfree (relptr); } } return err; } /* Authenticate the card using the Card Application Administration * Key. (VALUE,VALUELEN) has that 24 byte key. */ static gpg_error_t auth_adm_key (app_t app, const unsigned char *value, size_t valuelen) { gpg_error_t err; unsigned char tmpl[4+24]; size_t tmpllen; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; char witness[8]; size_t n; gcry_cipher_hd_t cipher = NULL; /* Prepare decryption. */ err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0); if (err) goto leave; err = gcry_cipher_setkey (cipher, value, valuelen); if (err) goto leave; /* Request a witness. */ tmpl[0] = 0x7c; tmpl[1] = 0x02; tmpl[2] = 0x80; tmpl[3] = 0; /* (Empty witness requests a witness.) */ tmpllen = 4; err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_BAD_AUTH); if (err) goto leave; if (!(outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x80, &n)) && n == 8)) { err = gpg_error (GPG_ERR_CARD); log_error ("piv: improper witness received\n"); goto leave; } err = gcry_cipher_decrypt (cipher, witness, 8, s, 8); if (err) goto leave; /* Return decrypted witness and send our challenge. */ tmpl[0] = 0x7c; tmpl[1] = 22; tmpl[2] = 0x80; tmpl[3] = 8; memcpy (tmpl+4, witness, 8); tmpl[12] = 0x81; tmpl[13] = 8; gcry_create_nonce (tmpl+14, 8); tmpl[22] = 0x80; tmpl[23] = 0; tmpllen = 24; xfree (outdata); err = iso7816_general_authenticate (app_get_slot (app), 0, PIV_ALGORITHM_3DES_ECB_0, 0x9B, tmpl, tmpllen, 0, &outdata, &outdatalen); if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_BAD_AUTH); if (err) goto leave; if (!(outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n)) && n == 8)) { err = gpg_error (GPG_ERR_CARD); log_error ("piv: improper challenge received\n"); goto leave; } /* (We reuse the witness buffer.) */ err = gcry_cipher_decrypt (cipher, witness, 8, s, 8); if (err) goto leave; if (memcmp (witness, tmpl+14, 8)) { err = gpg_error (GPG_ERR_BAD_AUTH); goto leave; } leave: xfree (outdata); gcry_cipher_close (cipher); return err; } /* Set a new admin key. */ static gpg_error_t set_adm_key (app_t app, const unsigned char *value, size_t valuelen) { gpg_error_t err; unsigned char apdu[8+24]; unsigned int sw; /* Check whether it is a weak key and that it is of proper length. */ { gcry_cipher_hd_t cipher; err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0); if (!err) { err = gcry_cipher_setkey (cipher, value, valuelen); gcry_cipher_close (cipher); } if (err) goto leave; } if (app->app_local->flags.yubikey) { /* This is a Yubikey. */ if (valuelen != 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } /* We use a proprietary Yubikey command. */ apdu[0] = 0; apdu[1] = 0xff; apdu[2] = 0xff; apdu[3] = 0xff; /* touch policy: 0xff=never, 0xfe = always. */ apdu[4] = 3 + 24; apdu[5] = PIV_ALGORITHM_3DES_ECB; apdu[6] = 0x9b; apdu[7] = 24; memcpy (apdu+8, value, 24); err = iso7816_apdu_direct (app_get_slot (app), apdu, 8+24, 0, &sw, NULL, NULL); wipememory (apdu+8, 24); if (err) log_error ("piv: setting admin key failed; sw=%04x\n", sw); /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); } else err = gpg_error (GPG_ERR_NOT_SUPPORTED); leave: return err; } /* Handle the SETATTR operation. All arguments are already basically * checked. */ static gpg_error_t do_setattr (app_t app, ctrl_t ctrl, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t err; static struct { const char *name; unsigned short tag; unsigned short flush_tag; /* The tag which needs to be flushed or 0. */ int special; /* Special mode to use for thus NAME. */ } table[] = { /* Authenticate using the PIV Card Application Administration Key * (0x0B). Note that Yubico calls this key the "management key" * which we don't do because that term is too similar to "Cert * Management Key" (0x9D). */ { "AUTH-ADM-KEY", 0x0000, 0x0000, 1 }, { "SET-ADM-KEY", 0x0000, 0x0000, 2 } }; int idx; (void)ctrl; (void)pincb; (void)pincb_arg; for (idx=0; (idx < DIM (table) && ascii_strcasecmp (table[idx].name, name)); idx++) ; if (!(idx < DIM (table))) return gpg_error (GPG_ERR_INV_NAME); /* Flush the cache before writing it, so that the next get operation * will reread the data from the card and thus get synced in case of * errors (e.g. data truncated by the card). */ if (table[idx].tag) flush_cached_data (app, table[idx].flush_tag? table[idx].flush_tag /* */ : table[idx].tag); switch (table[idx].special) { case 1: err = auth_adm_key (app, value, valuelen); break; case 2: err = set_adm_key (app, value, valuelen); break; default: err = gpg_error (GPG_ERR_BUG); break; } return err; } /* Send the KEYPAIRINFO back. DOBJ describes the data object carrying * the key. This is used by the LEARN command. */ static gpg_error_t send_keypair_and_cert_info (app_t app, ctrl_t ctrl, data_object_t dobj, int only_keypair) { gpg_error_t err = 0; char *keygripstr = NULL; int got_cert; char idbuf[50]; const char *usage; err = get_keygrip_by_tag (app, dobj->tag, &keygripstr, &got_cert); if (err) goto leave; usage = dobj->usage? dobj->usage : ""; snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); send_status_info (ctrl, "KEYPAIRINFO", keygripstr, strlen (keygripstr), idbuf, strlen (idbuf), usage, strlen (usage), NULL, (size_t)0); if (!only_keypair && got_cert) { /* All certificates are of type 100 (Regular X.509 Cert). */ send_status_info (ctrl, "CERTINFO", "100", 3, idbuf, strlen (idbuf), NULL, (size_t)0); } leave: xfree (keygripstr); return err; } /* Handle the LEARN command. */ static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { int i; (void)flags; do_getattr (app, ctrl, "CHV-USAGE"); do_getattr (app, ctrl, "CHV-STATUS"); for (i=0; data_objects[i].tag; i++) if (data_objects[i].keypair) send_keypair_and_cert_info (app, ctrl, data_objects + i, !!(flags & APP_LEARN_FLAG_KEYPAIRINFO)); return 0; } /* Core of do_readcert which fetches the certificate based on the * given tag and returns it in a freshly allocated buffer stored at * R_CERT and the length of the certificate stored at R_CERTLEN. If * on success a non-zero value is stored at R_MECHANISM, the returned * data is not a certificate but a public key (in the format used by the * container '7f49'). */ static gpg_error_t readcert_by_tag (app_t app, unsigned int tag, unsigned char **r_cert, size_t *r_certlen, int *r_mechanism) { gpg_error_t err; unsigned char *buffer; size_t buflen; void *relptr; const unsigned char *s, *s2; size_t n, n2; *r_cert = NULL; *r_certlen = 0; *r_mechanism = 0; relptr = get_one_do (app, tag, &buffer, &buflen, NULL); if (!relptr || !buflen) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } s = find_tlv (buffer, buflen, 0x71, &n); if (!s) { /* No certificate; check whether a public key has been stored * using our own scheme. */ s = find_tlv (buffer, buflen, 0x7f49, &n); if (!s || !n) { log_error ("piv: No public key in 0x%X\n", tag); err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } s2 = find_tlv (buffer, buflen, 0x80, &n2); if (!s2 || n2 != 1 || !*s2) { log_error ("piv: No mechanism for public key in 0x%X\n", tag); err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } *r_mechanism = *s2; } else { if (n != 1) { log_error ("piv: invalid CertInfo in 0x%X\n", tag); err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } if (*s == 0x01) { log_error ("piv: gzip compression not yet supported (tag 0x%X)\n", tag); err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING); goto leave; } if (*s) { log_error ("piv: invalid CertInfo 0x%02x in 0x%X\n", *s, tag); err = gpg_error (GPG_ERR_INV_CERT_OBJ); goto leave; } /* Note: We don't check that the LRC octet has a length of zero * as required by the specs. */ /* Get the cert from the container. */ s = find_tlv (buffer, buflen, 0x70, &n); if (!s || !n) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } } /* The next is common for certificate and public key. */ if (!(*r_cert = xtrymalloc (n))) { err = gpg_error_from_syserror (); goto leave; } memcpy (*r_cert, s, n); *r_certlen = n; err = 0; leave: xfree (relptr); return err; } /* Get the keygrip in hex format of a key from the certificate stored * at TAG. Caller must free the string at R_KEYGRIPSTR. */ static gpg_error_t get_keygrip_by_tag (app_t app, unsigned int tag, char **r_keygripstr, int *r_got_cert) { gpg_error_t err; unsigned char *certbuf = NULL; size_t certbuflen; int mechanism; gcry_sexp_t s_pkey = NULL; ksba_cert_t cert = NULL; unsigned char grip[KEYGRIP_LEN]; *r_got_cert = 0; *r_keygripstr = xtrymalloc (2*KEYGRIP_LEN+1); if (!r_keygripstr) { err = gpg_error_from_syserror (); goto leave; } /* We need to get the public key from the certificate. */ err = readcert_by_tag (app, tag, &certbuf, &certbuflen, &mechanism); if (err) goto leave; if (mechanism) /* Compute keygrip from public key. */ { if (mechanism == PIV_ALGORITHM_RSA) err = genkey_parse_rsa (certbuf, certbuflen, &s_pkey); else if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) err = genkey_parse_ecc (certbuf, certbuflen, mechanism, &s_pkey); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); if (err) goto leave; if (!gcry_pk_get_keygrip (s_pkey, grip)) { log_error ("piv: error computing keygrip\n"); err = gpg_error (GPG_ERR_GENERAL); goto leave; } bin2hex (grip, sizeof grip, *r_keygripstr); } else /* Compute keygrip from certificate. */ { *r_got_cert = 0; err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, certbuf, certbuflen); if (err) goto leave; err = app_help_get_keygrip_string (cert, *r_keygripstr, NULL, NULL); } leave: gcry_sexp_release (s_pkey); ksba_cert_release (cert); xfree (certbuf); if (err) { xfree (*r_keygripstr); *r_keygripstr = NULL; } return err; } /* Locate the data object from the given KEYREF. The KEYREF may also * be the corresponding OID of the key object. Returns the data * object or NULL if not found. */ static data_object_t find_dobj_by_keyref (app_t app, const char *keyref) { int i; (void)app; if (!ascii_strncasecmp (keyref, "PIV.", 4)) /* Standard keyref */ { keyref += 4; for (i=0; data_objects[i].tag; i++) if (*data_objects[i].keyref && !ascii_strcasecmp (keyref, data_objects[i].keyref)) { return data_objects + i; } } else if (!strncmp (keyref, "2.16.840.1.101.3.7.", 19)) /* OID */ { keyref += 19; for (i=0; data_objects[i].tag; i++) if (*data_objects[i].keyref && !strcmp (keyref, data_objects[i].oidsuffix)) { return data_objects + i; } } else if (strlen (keyref) == 40) /* A keygrip */ { char *keygripstr = NULL; int tag, dummy_got_cert; for (i=0; (tag=data_objects[i].tag); i++) { if (!data_objects[i].keypair) continue; xfree (keygripstr); if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert)) continue; if (!strcmp (keygripstr, keyref)) { xfree (keygripstr); return data_objects + i; } } xfree (keygripstr); } return NULL; } /* Return the keyref from DOBJ as an integer. If it does not exist, * return -1. */ static int keyref_from_dobj (data_object_t dobj) { if (!dobj || !hexdigitp (dobj->keyref) || !hexdigitp (dobj->keyref+1)) return -1; return xtoi_2 (dobj->keyref); } /* Read a certificate from the card and returned in a freshly * allocated buffer stored at R_CERT and the length of the certificate * stored at R_CERTLEN. CERTID is either the OID of the cert's * container or of the form "PIV." */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **r_cert, size_t *r_certlen) { gpg_error_t err; data_object_t dobj; int mechanism; *r_cert = NULL; *r_certlen = 0; /* Hack to read a Yubikey attestation certificate. */ if (app->app_local->flags.yubikey && strlen (certid) == 11 && !ascii_strncasecmp (certid, "PIV.ATST.", 9) && hexdigitp (certid+9) && hexdigitp (certid+10)) { unsigned char apdu[4]; unsigned char *result; size_t resultlen; apdu[0] = 0; apdu[1] = 0xf9; /* Yubikey: Get attestation cert. */ apdu[2] = xtoi_2 (certid+9); apdu[3] = 0; err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 1, NULL, &result, &resultlen); if (!err) { *r_cert = result; *r_certlen = resultlen; } return err; } dobj = find_dobj_by_keyref (app, certid); if (!dobj) return gpg_error (GPG_ERR_INV_ID); err = readcert_by_tag (app, dobj->tag, r_cert, r_certlen, &mechanism); if (!err && mechanism) { /* Well, no certificate but a public key - we don't want it. */ xfree (*r_cert); *r_cert = NULL; *r_certlen = 0; err = gpg_error (GPG_ERR_NOT_FOUND); } return err; } /* Return a public key in a freshly allocated buffer. This will only * work for a freshly generated key as long as no reset of the * application has been performed. This is because we return a cached * result from key generation. If no cached result is available, the * error GPG_ERR_UNSUPPORTED_OPERATION is returned so that the higher * layer can then get the key by reading the matching certificate. * On success a canonical encoded s-expression with the public key is * stored at (R_PK,R_PKLEN); the caller must release that buffer. On * error R_PK and R_PKLEN are not changed and an error code is * returned. */ static gpg_error_t do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, unsigned char **r_pk, size_t *r_pklen) { gpg_error_t err; data_object_t dobj; int keyref; unsigned char *cert = NULL; size_t certlen; int mechanism; gcry_sexp_t s_pkey = NULL; unsigned char *pk = NULL; size_t pklen; dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = readcert_by_tag (app, dobj->tag, &cert, &certlen, &mechanism); if (err) goto leave; if (!mechanism) { /* We got a certificate. Extract the pubkey from it. */ err = app_help_pubkey_from_cert (cert, certlen, &pk, &pklen); if (err) { log_error ("failed to parse the certificate: %s\n", gpg_strerror (err)); goto leave; } } else { /* Convert the public key into the expected s-expression. */ if (mechanism == PIV_ALGORITHM_RSA) err = genkey_parse_rsa (cert, certlen, &s_pkey); else if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) err = genkey_parse_ecc (cert, certlen, mechanism, &s_pkey); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); if (err) goto leave; err = make_canon_sexp (s_pkey, &pk, &pklen); if (err) goto leave; } if ((flags & APP_READKEY_FLAG_INFO)) { char keygripstr[KEYGRIP_LEN*2+1]; char idbuf[50]; const char *usage; char *algostr; err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr, NULL, NULL, &algostr); if (err) { log_error ("app_help_get_keygrip_string_pk failed: %s\n", gpg_strerror (err)); goto leave; } usage = dobj->usage? dobj->usage : ""; snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref); send_status_info (ctrl, "KEYPAIRINFO", keygripstr, strlen (keygripstr), idbuf, strlen (idbuf), usage, strlen (usage), "-", (size_t)1, algostr, strlen (algostr), NULL, (size_t)0); xfree (algostr); } if (r_pk && r_pklen) { *r_pk = pk; pk = NULL; *r_pklen = pklen; } leave: gcry_sexp_release (s_pkey); xfree (pk); xfree (cert); return err; } /* Given a data object DOBJ return the corresponding PIV algorithm and * store it at R_ALGO. The algorithm is taken from the corresponding * certificate or from a cache. */ static gpg_error_t get_key_algorithm_by_dobj (app_t app, data_object_t dobj, int *r_mechanism) { gpg_error_t err; unsigned char *certbuf = NULL; size_t certbuflen; int mechanism; ksba_cert_t cert = NULL; ksba_sexp_t k_pkey = NULL; gcry_sexp_t s_pkey = NULL; gcry_sexp_t l1 = NULL; char *algoname = NULL; int algo; size_t n; const char *curve_name; *r_mechanism = 0; err = readcert_by_tag (app, dobj->tag, &certbuf, &certbuflen, &mechanism); if (err) goto leave; if (mechanism) { /* A public key was found. That makes it easy. */ switch (mechanism) { case PIV_ALGORITHM_RSA: case PIV_ALGORITHM_ECC_P256: case PIV_ALGORITHM_ECC_P384: *r_mechanism = mechanism; break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); log_error ("piv: unknown mechanism %d in public key at %s\n", mechanism, dobj->keyref); break; } goto leave; } err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, certbuf, certbuflen); if (err) { log_error ("piv: failed to parse the certificate %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } xfree (certbuf); certbuf = NULL; k_pkey = ksba_cert_get_public_key (cert); if (!k_pkey) { err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } n = gcry_sexp_canon_len (k_pkey, 0, NULL, NULL); err = gcry_sexp_new (&s_pkey, k_pkey, n, 0); if (err) goto leave; l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) { err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } { gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = l_tmp; } algoname = gcry_sexp_nth_string (l1, 0); if (!algoname) { err = gpg_error_from_syserror (); goto leave; } algo = gcry_pk_map_name (algoname); switch (algo) { case GCRY_PK_RSA: algo = PIV_ALGORITHM_RSA; break; case GCRY_PK_ECC: case GCRY_PK_ECDSA: case GCRY_PK_ECDH: curve_name = gcry_pk_get_curve (s_pkey, 0, NULL); if (curve_name && !strcmp (curve_name, "NIST P-256")) algo = PIV_ALGORITHM_ECC_P256; else if (curve_name && !strcmp (curve_name, "NIST P-384")) algo = PIV_ALGORITHM_ECC_P384; else { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("piv: certificate %s, curve '%s': %s\n", dobj->keyref, curve_name, gpg_strerror (err)); goto leave; } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); log_error ("piv: certificate %s, pubkey algo '%s': %s\n", dobj->keyref, algoname, gpg_strerror (err)); goto leave; } *r_mechanism = algo; leave: gcry_free (algoname); gcry_sexp_release (l1); gcry_sexp_release (s_pkey); ksba_free (k_pkey); xfree (certbuf); return err; } /* Helper to cache the pin PINNO. If PIN is NULL the cache is cleared. */ static void cache_pin (app_t app, ctrl_t ctrl, int pinno, const char *pin, unsigned int pinlen) { char pinref[20]; if (pinno < 0) return; switch (app->card->cardtype) { case CARDTYPE_YUBIKEY: break; default: return; } snprintf (pinref, sizeof pinref, "%02x", pinno); pincache_put (ctrl, app_get_slot (app), "piv", pinref, pin, pinlen); switch (pinno) { case 0x00: app->app_local->pincache.maybe_00 = !!pin; break; case 0x80: app->app_local->pincache.maybe_80 = !!pin; break; case 0x81: app->app_local->pincache.maybe_81 = !!pin; break; case 0x96: app->app_local->pincache.maybe_96 = !!pin; break; case 0x97: app->app_local->pincache.maybe_97 = !!pin; break; case 0x98: app->app_local->pincache.maybe_98 = !!pin; break; case 0x9B: app->app_local->pincache.maybe_9B = !!pin; break; } } /* If the PIN cache is available and really has a valid PIN return * that pin at R_PIN. Returns true if that is the case; otherwise * false. */ static int pin_from_cache (app_t app, ctrl_t ctrl, int pinno, char **r_pin) { char pinref[20]; int maybe_cached; *r_pin = NULL; if (pinno < 0) return 0; switch (app->card->cardtype) { case CARDTYPE_YUBIKEY: break; default: return 0; } switch (pinno) { case 0x00: maybe_cached = app->app_local->pincache.maybe_00; break; case 0x80: maybe_cached = app->app_local->pincache.maybe_80; break; case 0x81: maybe_cached = app->app_local->pincache.maybe_81; break; case 0x96: maybe_cached = app->app_local->pincache.maybe_96; break; case 0x97: maybe_cached = app->app_local->pincache.maybe_97; break; case 0x98: maybe_cached = app->app_local->pincache.maybe_98; break; case 0x9B: maybe_cached = app->app_local->pincache.maybe_9B; break; default: maybe_cached = 0; } if (!maybe_cached) return 0; snprintf (pinref, sizeof pinref, "%02x", pinno); if (pincache_get (ctrl, app_get_slot (app), "piv", pinref, r_pin)) return 0; return 1; } /* Return an allocated string to be used as prompt. Returns NULL on * malloc error. */ static char * make_prompt (app_t app, int remaining, const char *firstline) { char *serial, *tmpbuf, *result; serial = get_dispserialno (app, 0); if (!serial) return NULL; /* TRANSLATORS: Put a \x1f right before a colon. This can be * used by pinentry to nicely align the names and values. Keep * the %s at the start and end of the string. */ result = xtryasprintf (_("%s" "Number\x1f: %s%%0A" "Holder\x1f: %s" "%s"), "\x1e", serial, "Unknown", /* Fixme */ ""); xfree (serial); /* Append a "remaining attempts" info if needed. */ if (remaining != -1 && remaining < 3) { char *rembuf; /* TRANSLATORS: This is the number of remaining attempts to * enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */ rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining); if (rembuf) { tmpbuf = strconcat (firstline, "%0A%0A", result, "%0A%0A", rembuf, NULL); xfree (rembuf); } else tmpbuf = NULL; xfree (result); result = tmpbuf; } else { tmpbuf = strconcat (firstline, "%0A%0A", result, NULL); xfree (result); result = tmpbuf; } return result; } /* Helper for verify_chv to ask for the PIN and to prepare/pad it. On * success the result is stored at (R_PIN,R_PINLEN). */ static gpg_error_t ask_and_prepare_chv (app_t app, ctrl_t ctrl, int keyref, int ask_new, int remaining, int no_cache, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg, char **r_pin, unsigned int *r_pinlen, unsigned int *r_unpaddedpinlen) { gpg_error_t err; const char *label; char *prompt; char *pinvalue = NULL; unsigned int pinlen; char *pinbuffer = NULL; int minlen, maxlen, padding, onlydigits; *r_pin = NULL; *r_pinlen = 0; if (r_unpaddedpinlen) *r_unpaddedpinlen = 0; if (ask_new) remaining = -1; if (remaining != -1) log_debug ("piv: CHV %02X has %d attempts left\n", keyref, remaining); switch (keyref) { case 0x00: minlen = 6; maxlen = 8; padding = 1; onlydigits = 1; label = (ask_new? _("|N|Please enter the new Global-PIN") /**/ : _("||Please enter the Global-PIN of your PIV card")); break; case 0x80: minlen = 6; maxlen = 8; padding = 1; onlydigits = 1; label = (ask_new? _("|N|Please enter the new PIN") /**/ : _("||Please enter the PIN of your PIV card")); break; case 0x81: minlen = 8; maxlen = 8; padding = 0; onlydigits = 0; label = (ask_new? _("|N|Please enter the new Unblocking Key") /**/ :_("||Please enter the Unblocking Key of your PIV card")); break; case 0x96: case 0x97: case 0x98: case 0x9B: return gpg_error (GPG_ERR_NOT_IMPLEMENTED); default: return gpg_error (GPG_ERR_INV_ID); } /* Ask for the PIN. */ if (!no_cache && remaining >= 3 && pin_from_cache (app, ctrl, keyref, &pinvalue)) err = 0; else { prompt = make_prompt (app, remaining, label); err = pincb (pincb_arg, prompt, &pinvalue); xfree (prompt); prompt = NULL; } if (err) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (err)); return err; } pinlen = pinvalue? strlen (pinvalue) : 0; if (pinlen < minlen) { log_error (_("PIN for is too short; minimum length is %d\n"), minlen); if (pinvalue) wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } if (pinlen > maxlen) { log_error (_("PIN for is too long; maximum length is %d\n"), maxlen); wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } if (onlydigits && strspn (pinvalue, "0123456789") != pinlen) { log_error (_("PIN has invalid characters; only digits are allowed\n")); wipememory (pinvalue, pinlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } pinbuffer = xtrymalloc_secure (maxlen); if (!pinbuffer) { err = gpg_error_from_syserror (); wipememory (pinvalue, pinlen); xfree (pinvalue); return err; } memcpy (pinbuffer, pinvalue, pinlen); wipememory (pinvalue, pinlen); xfree (pinvalue); if (r_unpaddedpinlen) *r_unpaddedpinlen = pinlen; if (padding) { memset (pinbuffer + pinlen, 0xff, maxlen - pinlen); pinlen = maxlen; } *r_pin = pinbuffer; *r_pinlen = pinlen; return 0; } /* Verify the card holder verification identified by KEYREF. This is * either the Application PIN or the Global PIN. If FORCE is true a * verification is always done. */ static gpg_error_t verify_chv (app_t app, ctrl_t ctrl, int keyref, int force, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg) { gpg_error_t err; int remaining; char *pin = NULL; unsigned int pinlen, unpaddedpinlen; /* First check whether a verify is at all needed. */ remaining = iso7816_verify_status (app_get_slot (app), keyref); if (remaining == ISO7816_VERIFY_NOT_NEEDED) { if (!force) /* No need to verification. */ return 0; /* All fine. */ remaining = -1; } else if (remaining < 0) /* We don't care about other errors. */ remaining = -1; err = ask_and_prepare_chv (app, ctrl, keyref, 0, remaining, force, pincb, pincb_arg, &pin, &pinlen, &unpaddedpinlen); if (err) return err; err = iso7816_verify (app_get_slot (app), keyref, pin, pinlen); if (err) { log_error ("CHV %02X verification failed: %s\n", keyref, gpg_strerror (err)); cache_pin (app, ctrl, keyref, NULL, 0); } else cache_pin (app, ctrl, keyref, pin, unpaddedpinlen); wipememory (pin, pinlen); xfree (pin); return err; } /* Handle the PASSWD command. Valid values for PWIDSTR are * key references related to PINs; in particular: * PIV.00 - The Global PIN * PIV.80 - The Application PIN * PIV.81 - The PIN Unblocking key * The supported flags are: * APP_CHANGE_FLAG_CLEAR Clear the PIN verification state. * APP_CHANGE_FLAG_RESET Reset a PIN using the PUK. Only * allowed with PIV.80. */ static gpg_error_t do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; int keyref, targetkeyref; unsigned char apdu[4]; unsigned int sw; int remaining; char *oldpin = NULL; unsigned int oldpinlen; char *newpin = NULL; unsigned int newpinlen; (void)ctrl; /* Check for unknown flags. */ if ((flags & ~(APP_CHANGE_FLAG_CLEAR|APP_CHANGE_FLAG_RESET))) { err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); goto leave; } /* Parse the keyref. */ targetkeyref = keyref = parse_chv_keyref (pwidstr); if (keyref == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } cache_pin (app, ctrl, keyref, NULL, 0); /* First see whether the special --clear mode has been requested. */ if ((flags & APP_CHANGE_FLAG_CLEAR)) { apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0xff; apdu[3] = keyref; err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, NULL, NULL, NULL); goto leave; } /* Prepare reset mode. */ if ((flags & APP_CHANGE_FLAG_RESET)) { if (keyref == 0x81) { err = gpg_error (GPG_ERR_INV_ID); /* Can't reset the PUK. */ goto leave; } /* Set the keyref to the PUK and keep the TARGETKEYREF. */ keyref = 0x81; } /* Get the remaining tries count. This is done by using the check * for verified state feature. */ apdu[0] = 0x00; apdu[1] = ISO7816_VERIFY; apdu[2] = 0x00; apdu[3] = keyref; if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL)) remaining = -1; /* Already verified, thus full number of tries. */ else if ((sw & 0xfff0) == 0x63C0) remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */ else remaining = -1; /* Ask for the old pin or puk. */ err = ask_and_prepare_chv (app, ctrl, keyref, 0, remaining, 0, pincb, pincb_arg, &oldpin, &oldpinlen, NULL); if (err) return err; /* Verify the old pin so that we don't prompt for the new pin if the * old is wrong. This is not possible for the PUK, though. */ if (keyref != 0x81) { err = iso7816_verify (app_get_slot (app), keyref, oldpin, oldpinlen); if (err) { log_error ("CHV %02X verification failed: %s\n", keyref, gpg_strerror (err)); goto leave; } } /* Ask for the new pin. */ err = ask_and_prepare_chv (app, ctrl, targetkeyref, 1, -1, 0, pincb, pincb_arg, &newpin, &newpinlen, NULL); if (err) return err; if ((flags & APP_CHANGE_FLAG_RESET)) { char *buf = xtrymalloc_secure (oldpinlen + newpinlen); if (!buf) { err = gpg_error_from_syserror (); goto leave; } memcpy (buf, oldpin, oldpinlen); memcpy (buf+oldpinlen, newpin, newpinlen); err = iso7816_reset_retry_counter_with_rc (app_get_slot (app), targetkeyref, buf, oldpinlen+newpinlen); xfree (buf); if (err) log_error ("resetting CHV %02X using CHV %02X failed: %s\n", targetkeyref, keyref, gpg_strerror (err)); } else { err = iso7816_change_reference_data (app_get_slot (app), keyref, oldpin, oldpinlen, newpin, newpinlen); if (err) log_error ("CHV %02X changing PIN failed: %s\n", keyref, gpg_strerror (err)); } leave: xfree (oldpin); xfree (newpin); return err; } /* Perform a simple verify operation for the PIN specified by PWIDSTR. * For valid values see do_change_chv. */ static gpg_error_t do_check_chv (app_t app, ctrl_t ctrl, const char *pwidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int keyref; (void)ctrl; keyref = parse_chv_keyref (pwidstr); if (keyref == -1) return gpg_error (GPG_ERR_INV_ID); return verify_chv (app, ctrl, keyref, 0, pincb, pincb_arg); } /* Compute a digital signature using the GENERAL AUTHENTICATE command * on INDATA which is expected to be the raw message digest. The * KEYIDSTR has the key reference or its OID (e.g. "PIV.9A"). The * result is stored at (R_OUTDATA,R_OUTDATALEN); on error (NULL,0) is * stored there and an error code returned. For ECDSA the result is * the simple concatenation of R and S without any DER encoding. R * and S are left extended with zeroes to make sure they have an equal * length. If HASHALGO is not zero, the function prepends the hash's * OID to the indata or checks that it is consistent. */ static gpg_error_t do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata_arg, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen) { const unsigned char *indata = indata_arg; gpg_error_t err; data_object_t dobj; unsigned char oidbuf[64]; size_t oidbuflen; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; size_t n; int keyref, mechanism; unsigned char *indata_buffer = NULL; /* Malloced helper. */ unsigned char *apdudata = NULL; size_t apdudatalen; int force_verify; (void)ctrl; if (!keyidstr || !*keyidstr) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } dobj = find_dobj_by_keyref (app, keyidstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } /* According to table 4b of SP800-73-4 the signing key always * requires a verify. */ switch (keyref) { case 0x9c: force_verify = 1; break; default: force_verify = 0; break; } err = get_key_algorithm_by_dobj (app, dobj, &mechanism); if (err) goto leave; /* For ECC we need to remove the ASN.1 prefix from INDATA. For RSA * we need to add the padding and possible also the ASN.1 prefix. */ if (mechanism == PIV_ALGORITHM_ECC_P256 || mechanism == PIV_ALGORITHM_ECC_P384) { int need_algo, need_digestlen; if (mechanism == PIV_ALGORITHM_ECC_P256) { need_algo = GCRY_MD_SHA256; need_digestlen = 32; } else { need_algo = GCRY_MD_SHA384; need_digestlen = 48; } if (hashalgo && hashalgo != need_algo) { err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("piv: hash algo %d does not match mechanism %d\n", need_algo, mechanism); goto leave; } if (indatalen > need_digestlen) { oidbuflen = sizeof oidbuf; err = gcry_md_get_asnoid (need_algo, &oidbuf, &oidbuflen); if (err) { err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: no OID for hash algo %d\n", need_algo); goto leave; } if (indatalen != oidbuflen + need_digestlen || memcmp (indata, oidbuf, oidbuflen)) { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: bad input for signing with mechanism %d\n", mechanism); goto leave; } indata += oidbuflen; indatalen -= oidbuflen; } } else if (mechanism == PIV_ALGORITHM_RSA && indatalen == 2048/8 && indata[indatalen-1] == 0xBC) { /* If the provided data length matches the supported RSA * framelen and the last octet of the data is 0xBC, we assume * this is PSS formatted data and we use it verbatim; PIV cards * accept PSS as well as PKCS#1. */ } else if (mechanism == PIV_ALGORITHM_RSA) { /* PIV requires 2048 bit RSA. */ unsigned int framelen = 2048 / 8; unsigned char *frame; int i; oidbuflen = sizeof oidbuf; if (!hashalgo) { /* We assume that indata already has the required * digestinfo; thus merely prepend the padding below. */ } else if ((err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen))) { log_debug ("piv: no OID for hash algo %d\n", hashalgo); goto leave; } else { unsigned int digestlen = gcry_md_get_algo_dlen (hashalgo); if (indatalen == digestlen) { /* Plain hash in INDATA; prepend the digestinfo. */ indata_buffer = xtrymalloc (oidbuflen + indatalen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer, oidbuf, oidbuflen); memcpy (indata_buffer+oidbuflen, indata, indatalen); indata = indata_buffer; indatalen = oidbuflen + indatalen; } else if (indatalen == oidbuflen + digestlen && !memcmp (indata, oidbuf, oidbuflen)) ; /* Correct prefix. */ else { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: bad input for signing with RSA and hash %d\n", hashalgo); goto leave; } } /* Now prepend the pkcs#v1.5 padding. We require at least 8 * byte of padding and 3 extra bytes for the prefix and the * delimiting nul. */ if (!indatalen || indatalen + 8 + 4 > framelen) { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: input does not fit into a %u bit PKCS#v1.5 frame\n", 8*framelen); goto leave; } frame = xtrymalloc (framelen); if (!frame) { err = gpg_error_from_syserror (); goto leave; } n = 0; frame[n++] = 0; frame[n++] = 1; /* Block type. */ i = framelen - indatalen - 3 ; memset (frame+n, 0xff, i); n += i; frame[n++] = 0; /* Delimiter. */ memcpy (frame+n, indata, indatalen); n += indatalen; log_assert (n == framelen); /* And now put it into the indata_buffer. */ xfree (indata_buffer); indata_buffer = frame; indata = indata_buffer; indatalen = framelen; } else { err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: unknown PIV mechanism %d while signing\n", mechanism); goto leave; } /* Now verify the Application PIN. */ err = verify_chv (app, ctrl, 0x80, force_verify, pincb, pincb_arg); if (err) return err; /* Build the Dynamic Authentication Template. */ err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x7c, (size_t)0, NULL, /* Constructed. */ (int)0x82, (size_t)0, "", (int)0x81, (size_t)indatalen, indata, (int)0, (size_t)0, NULL); if (err) goto leave; /* Note: the -1 requests command chaining. */ err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); if (err) goto leave; /* Parse the response. */ if (outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n))) { if (mechanism == PIV_ALGORITHM_RSA) { memmove (outdata, outdata + (s - outdata), n); outdatalen = n; } else /* ECC */ { const unsigned char *rval, *sval; size_t rlen, rlenx, slen, slenx, resultlen; char *result; /* The result of an ECDSA signature is * SEQUENCE { r INTEGER, s INTEGER } * We re-pack that by concatenating R and S and making sure * that both have the same length. We simplify parsing by * using find_tlv and not a proper DER parser. */ s = find_tlv (s, n, 0x30, &n); if (!s) goto bad_der; rval = find_tlv (s, n, 0x02, &rlen); if (!rval) goto bad_der; log_assert (n >= (rval-s)+rlen); sval = find_tlv (rval+rlen, n-((rval-s)+rlen), 0x02, &slen); if (!rval) goto bad_der; rlenx = slenx = 0; if (rlen > slen) slenx = rlen - slen; else if (slen > rlen) rlenx = slen - rlen; resultlen = rlen + rlenx + slen + slenx; result = xtrycalloc (1, resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } memcpy (result + rlenx, rval, rlen); memcpy (result + rlenx + rlen + slenx, sval, slen); xfree (outdata); outdata = result; outdatalen = resultlen; } } else { bad_der: err = gpg_error (GPG_ERR_CARD); log_error ("piv: response does not contain a proper result\n"); goto leave; } leave: if (err) { xfree (outdata); *r_outdata = NULL; *r_outdatalen = 0; } else { *r_outdata = outdata; *r_outdatalen = outdatalen; } xfree (apdudata); xfree (indata_buffer); return err; } /* AUTH for PIV cards is actually the same as SIGN. The difference * between AUTH and SIGN is that AUTH expects that pkcs#1.5 padding * for RSA has already been done (digestInfo part w/o the padding) * whereas SIGN may accept a plain digest and does the padding if * needed. This is also the reason why SIGN takes a hashalgo. For * both it is also acceptable to receive fully prepared PSS data. */ static gpg_error_t do_auth (app_t app, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen) { return do_sign (app, ctrl, keyidstr, 0, pincb, pincb_arg, indata, indatalen, r_outdata, r_outdatalen); } /* Decrypt the data in (INDATA,INDATALEN) and on success store the * mallocated result at (R_OUTDATA,R_OUTDATALEN). */ static gpg_error_t do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata_arg, size_t indatalen, unsigned char **r_outdata, size_t *r_outdatalen, unsigned int *r_info) { const unsigned char *indata = indata_arg; gpg_error_t err; data_object_t dobj; unsigned char *outdata = NULL; size_t outdatalen; const unsigned char *s; size_t n; int keyref, mechanism; unsigned int framelen; unsigned char *indata_buffer = NULL; /* Malloced helper. */ unsigned char *apdudata = NULL; size_t apdudatalen; (void)ctrl; if (!keyidstr || !*keyidstr) { err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } dobj = find_dobj_by_keyref (app, keyidstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } if (keyref == 0x9A || keyref == 0x9C || keyref == 0x9E) { /* Signing only reference. We only allow '9D' and the retired * cert key management DOs. */ err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = get_key_algorithm_by_dobj (app, dobj, &mechanism); if (err) goto leave; switch (mechanism) { case PIV_ALGORITHM_ECC_P256: framelen = 1+32+32; break; case PIV_ALGORITHM_ECC_P384: framelen = 1+48+48; break; case PIV_ALGORITHM_RSA: framelen = 2048 / 8; break; default: err = gpg_error (GPG_ERR_INTERNAL); log_debug ("piv: unknown PIV mechanism %d while decrypting\n", mechanism); goto leave; } /* Check that the ciphertext has the right length; due to internal * convey mechanism using MPIs leading zero bytes might have been * lost. Adjust for this. Unfortunately the ciphertext might have * also been prefixed with a leading zero to make it a positive * number; that may be a too long frame and we need to adjust for * this too. Note that for ECC those fixes are not reqquired * because the first octet is always '04' to indicate an * uncompressed point. */ if (indatalen > framelen) { if (mechanism == PIV_ALGORITHM_RSA && indatalen == framelen + 1 && !*indata) { indata_buffer = xtrycalloc (1, framelen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer, indata+1, framelen); indata = indata_buffer; indatalen = framelen; } else { err = gpg_error (GPG_ERR_INV_VALUE); log_error ("piv: input of %zu octets too large for mechanism %d\n", indatalen, mechanism); goto leave; } } if (indatalen < framelen) { indata_buffer = xtrycalloc (1, framelen); if (!indata_buffer) { err = gpg_error_from_syserror (); goto leave; } memcpy (indata_buffer+(framelen-indatalen), indata, indatalen); indata = indata_buffer; indatalen = framelen; } /* Now verify the Application PIN. */ err = verify_chv (app, ctrl, 0x80, 0, pincb, pincb_arg); if (err) return err; /* Build the Dynamic Authentication Template. */ err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x7c, (size_t)0, NULL, /* Constructed. */ (int)0x82, (size_t)0, "", mechanism == PIV_ALGORITHM_RSA? (int)0x81 : (int)0x85, (size_t)indatalen, indata, (int)0, (size_t)0, NULL); if (err) goto leave; /* Note: the -1 requests command chaining. */ err = iso7816_general_authenticate (app_get_slot (app), -1, mechanism, keyref, apdudata, (int)apdudatalen, 0, &outdata, &outdatalen); if (err) goto leave; /* Parse the response. */ if (outdatalen && *outdata == 0x7c && (s = find_tlv (outdata, outdatalen, 0x82, &n))) { memmove (outdata, outdata + (s - outdata), n); outdatalen = n; } else { err = gpg_error (GPG_ERR_CARD); log_error ("piv: response does not contain a proper result\n"); goto leave; } leave: if (err) { xfree (outdata); *r_outdata = NULL; *r_outdatalen = 0; } else { *r_outdata = outdata; *r_outdatalen = outdatalen; } *r_info = 0; xfree (apdudata); xfree (indata_buffer); return err; } /* Check whether a key for DOBJ already exists. We detect this by * reading the certificate described by DOBJ. If FORCE is TRUE a * diagnositic will be printed but no error returned if the key * already exists. The flag GENERATING is used to select a * diagnositic. */ static gpg_error_t does_key_exist (app_t app, data_object_t dobj, int generating, int force) { void *relptr; unsigned char *buffer; size_t buflen; int found; relptr = get_one_do (app, dobj->tag, &buffer, &buflen, NULL); found = (relptr && buflen); xfree (relptr); if (found && !force) { log_error (_("key already exists\n")); return gpg_error (GPG_ERR_EEXIST); } if (found) log_info (_("existing key will be replaced\n")); else if (generating) log_info (_("generating new key\n")); else log_info (_("writing new key\n")); return 0; } /* Helper for do_writekey; here the RSA part. BUF, BUFLEN, and DEPTH * are the current parser state of the S-expression with the key. */ static gpg_error_t writekey_rsa (app_t app, data_object_t dobj, int keyref, const unsigned char *buf, size_t buflen, int depth) { gpg_error_t err; const unsigned char *tok; size_t toklen; int last_depth1, last_depth2; const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; const unsigned char *rsa_p = NULL; const unsigned char *rsa_q = NULL; unsigned char *rsa_dpm1 = NULL; unsigned char *rsa_dqm1 = NULL; unsigned char *rsa_qinv = NULL; size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; size_t rsa_dpm1_len, rsa_dqm1_len, rsa_qinv_len; unsigned char *apdudata = NULL; size_t apdudatalen; unsigned char tmpl[1]; last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Check that we have all parameters. */ if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } /* Fixme: Shall we check whether n == pq ? */ if (opt.verbose) log_info ("RSA private key size is %u bytes\n", (unsigned int)rsa_n_len); /* Compute the dp, dq and u components. */ { gcry_mpi_t mpi_e, mpi_p, mpi_q; gcry_mpi_t mpi_dpm1 = gcry_mpi_snew (0); gcry_mpi_t mpi_dqm1 = gcry_mpi_snew (0); gcry_mpi_t mpi_qinv = gcry_mpi_snew (0); gcry_mpi_t mpi_tmp = gcry_mpi_snew (0); gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL); gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL); gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL); gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1); gcry_mpi_invm (mpi_dpm1, mpi_e, mpi_tmp); gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1); gcry_mpi_invm (mpi_dqm1, mpi_e, mpi_tmp); gcry_mpi_invm (mpi_qinv, mpi_q, mpi_p); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dpm1, &rsa_dpm1_len, mpi_dpm1); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dqm1, &rsa_dqm1_len, mpi_dqm1); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_qinv, &rsa_qinv_len, mpi_qinv); gcry_mpi_release (mpi_e); gcry_mpi_release (mpi_p); gcry_mpi_release (mpi_q); gcry_mpi_release (mpi_dpm1); gcry_mpi_release (mpi_dqm1); gcry_mpi_release (mpi_qinv); gcry_mpi_release (mpi_tmp); } err = concat_tlv_list (1, &apdudata, &apdudatalen, (int)0x01, (size_t)rsa_p_len, rsa_p, (int)0x02, (size_t)rsa_q_len, rsa_q, (int)0x03, (size_t)rsa_dpm1_len, rsa_dpm1, (int)0x04, (size_t)rsa_dqm1_len, rsa_dqm1, (int)0x05, (size_t)rsa_qinv_len, rsa_qinv, (int)0, (size_t)0, NULL); if (err) goto leave; err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ PIV_ALGORITHM_RSA, /* P1 */ keyref, /* P2 */ apdudatalen,/* Lc */ apdudata, /* data */ NULL, NULL, NULL); if (err) goto leave; /* Write the public key to the cert object. */ xfree (apdudata); err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x81, (size_t)rsa_n_len, rsa_n, (int)0x82, (size_t)rsa_e_len, rsa_e, (int)0, (size_t)0, NULL); if (err) goto leave; tmpl[0] = PIV_ALGORITHM_RSA; err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); leave: xfree (rsa_dpm1); xfree (rsa_dqm1); xfree (rsa_qinv); xfree (apdudata); return err; } /* Helper for do_writekey; here the ECC part. BUF, BUFLEN, and DEPTH * are the current parser state of the S-expression with the key. */ static gpg_error_t writekey_ecc (app_t app, data_object_t dobj, int keyref, const unsigned char *buf, size_t buflen, int depth) { gpg_error_t err; const unsigned char *tok; size_t toklen; int last_depth1, last_depth2; int mechanism = 0; const unsigned char *ecc_q = NULL; const unsigned char *ecc_d = NULL; size_t ecc_q_len, ecc_d_len; unsigned char *apdudata = NULL; size_t apdudatalen; unsigned char tmpl[1]; last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 5 && !memcmp (tok, "curve", 5)) { char *name; const char *xname; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; name = xtrymalloc (toklen+1); if (!name) { err = gpg_error_from_syserror (); goto leave; } memcpy (name, tok, toklen); name[toklen] = 0; /* Canonicalize the curve name. We use the openpgp * functions here because Libgcrypt has no generic curve * alias lookup feature and the PIV supported curves are * also supported by OpenPGP. */ xname = openpgp_oid_to_curve (openpgp_curve_to_oid (name, NULL, NULL), 0); xfree (name); if (xname && !strcmp (xname, "nistp256")) mechanism = PIV_ALGORITHM_ECC_P256; else if (xname && !strcmp (xname, "nistp384")) mechanism = PIV_ALGORITHM_ECC_P384; else { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } } else if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break; case 'd': mpi = &ecc_d; mpi_len = &ecc_d_len; break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Check that we have all parameters. */ if (!mechanism || !ecc_q || !ecc_d) { err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if (opt.verbose) log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len); err = concat_tlv_list (1, &apdudata, &apdudatalen, (int)0x06, (size_t)ecc_d_len, ecc_d, (int)0, (size_t)0, NULL); if (err) goto leave; err = iso7816_send_apdu (app_get_slot (app), -1, /* Use command chaining. */ 0, /* Class */ 0xfe, /* Ins: Yubikey Import Asym. Key. */ mechanism, /* P1 */ keyref, /* P2 */ apdudatalen,/* Lc */ apdudata, /* data */ NULL, NULL, NULL); if (err) goto leave; /* Write the public key to the cert object. */ xfree (apdudata); err = concat_tlv_list (0, &apdudata, &apdudatalen, (int)0x86, (size_t)ecc_q_len, ecc_q, (int)0, (size_t)0, NULL); if (err) goto leave; tmpl[0] = mechanism; err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)apdudatalen, apdudata, (int)0, (size_t)0, NULL); leave: xfree (apdudata); return err; } /* Write a key to a slot. This command requires proprietary * extensions of the PIV specification and is thus only implemnted for * supported card types. The input is a canonical encoded * S-expression with the secret key in KEYDATA and its length (for * assertion) in KEYDATALEN. KEYREFSTR needs to be the usual 2 * hexdigit slot number prefixed with "PIV." PINCB and PINCB_ARG are * not used for PIV cards. * * Supported FLAGS are: * APP_WRITEKEY_FLAG_FORCE Overwrite existing key. */ static gpg_error_t do_writekey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen) { gpg_error_t err; int force = !!(flags & APP_WRITEKEY_FLAG_FORCE); data_object_t dobj; int keyref; const unsigned char *buf, *tok; size_t buflen, toklen; int depth; (void)ctrl; (void)pincb; (void)pincb_arg; if (!app->app_local->flags.yubikey) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } /* Check keyref and test whether a key already exists. */ dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = does_key_exist (app, dobj, 0, force); if (err) goto leave; /* Parse the S-expression with the key. */ buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen)) { if (!tok) ; else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen)) log_info ("protected-private-key passed to writekey\n"); else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) log_info ("shadowed-private-key passed to writekey\n"); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; /* First clear an existing key. We do this by writing an empty 7f49 * tag. This will return GPG_ERR_NO_PUBKEY on a later read. */ flush_cached_data (app, dobj->tag); err = put_data (app_get_slot (app), dobj->tag, (int)0x7f49, (size_t)0, "", (int)0, (size_t)0, NULL); if (err) { log_error ("piv: failed to clear the cert DO %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } /* Divert to the algo specific implementation. */ if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0) err = writekey_rsa (app, dobj, keyref, buf, buflen, depth); else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0) err = writekey_ecc (app, dobj, keyref, buf, buflen, depth); else err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); if (err) { /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); log_error (_("failed to store the key: %s\n"), gpg_strerror (err)); } leave: return err; } /* Parse an RSA response object, consisting of the content of tag * 0x7f49, into a gcrypt s-expression object and store that R_SEXP. * On error NULL is stored at R_SEXP. */ static gpg_error_t genkey_parse_rsa (const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp) { gpg_error_t err; const unsigned char *m, *e; unsigned char *mbuf = NULL; unsigned char *ebuf = NULL; size_t mlen, elen; *r_sexp = NULL; m = find_tlv (data, datalen, 0x0081, &mlen); if (!m) { log_error (_("response does not contain the RSA modulus\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } e = find_tlv (data, datalen, 0x0082, &elen); if (!e) { log_error (_("response does not contain the RSA public exponent\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } for (; mlen && !*m; mlen--, m++) /* Strip leading zeroes */ ; for (; elen && !*e; elen--, e++) /* Strip leading zeroes */ ; mbuf = xtrymalloc (mlen + 1); if (!mbuf) { err = gpg_error_from_syserror (); goto leave; } /* Prepend numbers with a 0 if needed. */ if (mlen && (*m & 0x80)) { *mbuf = 0; memcpy (mbuf+1, m, mlen); mlen++; } else memcpy (mbuf, m, mlen); ebuf = xtrymalloc (elen + 1); if (!ebuf) { err = gpg_error_from_syserror (); goto leave; } /* Prepend numbers with a 0 if needed. */ if (elen && (*e & 0x80)) { *ebuf = 0; memcpy (ebuf+1, e, elen); elen++; } else memcpy (ebuf, e, elen); err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))", (int)mlen, mbuf, (int)elen, ebuf); leave: xfree (mbuf); xfree (ebuf); return err; } /* Parse an ECC response object, consisting of the content of tag * 0x7f49, into a gcrypt s-expression object and store that R_SEXP. * On error NULL is stored at R_SEXP. MECHANISM specifies the * curve. */ static gpg_error_t genkey_parse_ecc (const unsigned char *data, size_t datalen, int mechanism, gcry_sexp_t *r_sexp) { gpg_error_t err; const unsigned char *ecc_q; size_t ecc_qlen; const char *curve; *r_sexp = NULL; ecc_q = find_tlv (data, datalen, 0x0086, &ecc_qlen); if (!ecc_q) { log_error (_("response does not contain the EC public key\n")); err = gpg_error (GPG_ERR_CARD); goto leave; } if (mechanism == PIV_ALGORITHM_ECC_P256) curve = "nistp256"; else if (mechanism == PIV_ALGORITHM_ECC_P384) curve = "nistp384"; else { err = gpg_error (GPG_ERR_BUG); /* Call with wrong parameters. */ goto leave; } err = gcry_sexp_build (r_sexp, NULL, "(public-key(ecc(curve%s)(q%b)))", curve, (int)ecc_qlen, ecc_q); leave: return err; } /* Create a new keypair for KEYREF. If KEYTYPE is NULL a default * keytype is selected, else it may be one of the strings: * "rsa2048", "nistp256, or "nistp384". * * Supported FLAGS are: * APP_GENKEY_FLAG_FORCE Overwrite existing key. * * Note that CREATETIME is not used for PIV cards. * * Because there seems to be no way to read the public key we need to * retrieve it from a certificate. The GnuPG system however requires * the use of app_readkey to fetch the public key from the card to * create the certificate; to support this we temporary store the * generated public key in the local context for use by app_readkey. */ static gpg_error_t do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; data_object_t dobj; unsigned char *buffer = NULL; size_t buflen; int force = !!(flags & APP_GENKEY_FLAG_FORCE); int mechanism; time_t start_at; int keyref; unsigned char tmpl[5]; size_t tmpllen; const unsigned char *keydata; size_t keydatalen; (void)ctrl; (void)createtime; (void)pincb; (void)pincb_arg; if (!keytype) keytype = "rsa2048"; if (!strcmp (keytype, "rsa2048")) mechanism = PIV_ALGORITHM_RSA; else if (!strcmp (keytype, "nistp256")) mechanism = PIV_ALGORITHM_ECC_P256; else if (!strcmp (keytype, "nistp384")) mechanism = PIV_ALGORITHM_ECC_P384; else return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* We flush the cache to increase the I/O traffic before a key * generation. This _might_ help the card to gather more entropy * and is anyway a prerequisite for does_key_exist. */ flush_cached_data (app, 0); /* Check whether a key already exists. */ dobj = find_dobj_by_keyref (app, keyrefstr); if ((keyref = keyref_from_dobj (dobj)) == -1) { err = gpg_error (GPG_ERR_INV_ID); goto leave; } err = does_key_exist (app, dobj, 1, force); if (err) goto leave; /* Create the key. */ log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); tmpl[0] = 0xac; tmpl[1] = 3; tmpl[2] = 0x80; tmpl[3] = 1; tmpl[4] = mechanism; tmpllen = 5; err = iso7816_generate_keypair (app_get_slot (app), 0, 0, keyref, tmpl, tmpllen, 0, &buffer, &buflen); if (err) { /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); log_error (_("generating key failed\n")); return err; } { int nsecs = (int)(time (NULL) - start_at); log_info (ngettext("key generation completed (%d second)\n", "key generation completed (%d seconds)\n", nsecs), nsecs); } /* Parse the result and store it as an s-expression in a dedicated * cache for later retrieval by app_readkey. */ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata || !keydatalen) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the public key data\n")); goto leave; } tmpl[0] = mechanism; flush_cached_data (app, dobj->tag); err = put_data (app_get_slot (app), dobj->tag, (int)0x80, (size_t)1, tmpl, (int)0x7f49, (size_t)keydatalen, keydata, (int)0, (size_t)0, NULL); if (err) { log_error ("piv: failed to write key to the cert DO %s: %s\n", dobj->keyref, gpg_strerror (err)); goto leave; } leave: xfree (buffer); return err; } /* Map some names to an OID. */ static const unsigned char * map_curve_name_to_oid (const unsigned char *name, size_t *namelenp) { if (*namelenp == 8 && !memcmp (name, "nistp256", 8)) { *namelenp = 19; return "1.2.840.10045.3.1.7"; } if (*namelenp == 8 && !memcmp (name, "nistp384", 8)) { *namelenp = 12; return "1.3.132.0.34"; } if (*namelenp == 8 && !memcmp (name, "nistp521", 8)) { *namelenp = 12; return "1.3.132.0.35"; } return name; } /* Communication object for my_cmp_public_key. */ struct my_cmp_public_key_parm_s { int curve_seen; }; /* Compare function used with cmp_canon_sexp. */ static int my_cmp_public_key (void *opaque, int depth, const unsigned char *aval, size_t alen, const unsigned char *bval, size_t blen) { struct my_cmp_public_key_parm_s *parm = opaque; (void)depth; if (parm->curve_seen) { /* Last token was "curve" - canonicalize its argument. */ parm->curve_seen = 0; aval = map_curve_name_to_oid (aval, &alen); bval = map_curve_name_to_oid (bval, &blen); } else if (alen == 5 && !memcmp (aval, "curve", 5)) parm->curve_seen = 1; else parm->curve_seen = 0; if (alen > blen) return 1; else if (alen < blen) return -1; else return memcmp (aval, bval, alen); } /* Write the certificate (CERT,CERTLEN) to the card at CERTREFSTR. * CERTREFSTR is either the OID of the certificate's container data * object or of the form "PIV.". */ static gpg_error_t do_writecert (app_t app, ctrl_t ctrl, const char *certrefstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *cert, size_t certlen) { gpg_error_t err; data_object_t dobj; unsigned char *pk = NULL; unsigned char *orig_pk = NULL; size_t pklen, orig_pklen; struct my_cmp_public_key_parm_s cmp_parm = { 0 }; (void)ctrl; (void)pincb; /* Not used; instead authentication is needed. */ (void)pincb_arg; if (!certlen) return gpg_error (GPG_ERR_INV_CERT_OBJ); dobj = find_dobj_by_keyref (app, certrefstr); if (!dobj || !*dobj->keyref) return gpg_error (GPG_ERR_INV_ID); flush_cached_data (app, dobj->tag); /* Check that the public key parameters from the certificate match * an already stored key. Note that we do not allow writing a * certificate if no key has yet been created (GPG_ERR_NOT_FOUND) or * if there is a problem reading the public key from the certificate * GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only * way to detect whether a key exists is by trying to use that * key. */ err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_NO_SECKEY); /* Use a better error code. */ goto leave; } /* Compare pubkeys. */ err = app_help_pubkey_from_cert (cert, certlen, &pk, &pklen); if (err) goto leave; /* No public key in new certificate. */ if (cmp_canon_sexp (orig_pk, orig_pklen, pk, pklen, my_cmp_public_key, &cmp_parm)) { err = gpg_error (GPG_ERR_CONFLICT); goto leave; } flush_cached_data (app, dobj->tag); err = put_data (app_get_slot (app), dobj->tag, (int)0x70, (size_t)certlen, cert,/* Certificate */ (int)0x71, (size_t)1, "", /* No compress */ (int)0xfe, (size_t)0, "", /* Empty LRC. */ (int)0, (size_t)0, NULL); /* A PIN is not required, thus use a better error code. */ if (gpg_err_code (err) == GPG_ERR_BAD_PIN) err = gpg_error (GPG_ERR_NO_AUTH); if (err) log_error ("piv: failed to write cert to %s: %s\n", dobj->keyref, gpg_strerror (err)); leave: xfree (pk); xfree (orig_pk); return err; } /* Process the various keygrip based info requests. */ static gpg_error_t do_with_keygrip (app_t app, ctrl_t ctrl, int action, const char *want_keygripstr, int capability) { gpg_error_t err; char *keygripstr = NULL; char *serialno = NULL; char idbuf[20]; int data = 0; int i, tag, dummy_got_cert; /* First a quick check for valid parameters. */ switch (action) { case KEYGRIP_ACTION_LOOKUP: if (!want_keygripstr) { err = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } break; case KEYGRIP_ACTION_SEND_DATA: data = 1; break; case KEYGRIP_ACTION_WRITE_STATUS: break; default: err = gpg_error (GPG_ERR_INV_ARG); goto leave; } /* Allocate the s/n string if needed. */ if (action != KEYGRIP_ACTION_LOOKUP) { serialno = app_get_serialno (app); if (!serialno) { err = gpg_error_from_syserror (); goto leave; } } for (i = 0; (tag = data_objects[i].tag); i++) { if (!data_objects[i].keypair) continue; xfree (keygripstr); if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert)) continue; if (action == KEYGRIP_ACTION_LOOKUP) { if (!strcmp (keygripstr, want_keygripstr)) { err = 0; /* Found */ goto leave; } } else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr)) { if (capability == GCRY_PK_USAGE_SIGN) { if (strcmp (data_objects[i].keyref, "9C")) continue; } if (capability == GCRY_PK_USAGE_ENCR) { if (strcmp (data_objects[i].keyref, "9D")) continue; } if (capability == GCRY_PK_USAGE_AUTH) { if (strcmp (data_objects[i].keyref, "9A")) continue; } snprintf (idbuf, sizeof idbuf, "PIV.%s", data_objects[i].keyref); send_keyinfo (ctrl, data, keygripstr, serialno, idbuf); if (want_keygripstr) { err = 0; /* Found */ goto leave; } } } /* Return an error so that the dispatcher keeps on looping over the * other applications. For clarity we use a different error code * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR * is not NULL. */ if (!want_keygripstr) err = gpg_error (GPG_ERR_TRUE); else err = gpg_error (GPG_ERR_NOT_FOUND); leave: xfree (keygripstr); xfree (serialno); return err; } /* Prepare a reselect of another application. This is used by cards * which support on-the-fly switching between applications. The * function is called to give us a chance to save state for a future * reselect of us again. */ static gpg_error_t do_prep_reselect (app_t app, ctrl_t ctrl) { gpg_error_t err; (void)app; (void)ctrl; err = 0; return err; } /* Reselect the application. This is used by cards which support * on-the-fly switching between applications. */ static gpg_error_t do_reselect (app_t app, ctrl_t ctrl) { gpg_error_t err; (void)ctrl; /* An extra check which should not be necessary because the caller * should have made sure that a re-select is only called for * appropriate cards. */ if (!app->app_local->flags.yubikey) return gpg_error (GPG_ERR_NOT_SUPPORTED); err = iso7816_select_application (app_get_slot (app), piv_aid, sizeof piv_aid, 0x0001); return err; } /* Select the PIV application on the card in SLOT. This function must * be used before any other PIV application functions. */ gpg_error_t app_select_piv (app_t app) { int slot = app_get_slot (app); gpg_error_t err; unsigned char *apt = NULL; size_t aptlen; const unsigned char *s; size_t n; /* Note that we select using the AID without the 2 octet version * number. This allows for better reporting of future specs. We * need to use the use-zero-for-P2-flag. */ err = iso7816_select_application_ext (slot, piv_aid, sizeof piv_aid, 0x0001, &apt, &aptlen); if (err) goto leave; app->apptype = APPTYPE_PIV; app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; app->app_local = NULL; /* Check the Application Property Template. */ if (opt.verbose) { /* We use a separate log_info to avoid the "DBG:" prefix. */ log_info ("piv: APT="); log_printhex (apt, aptlen, ""); } s = find_tlv (apt, aptlen, 0x4F, &n); if (!s || n != 6 || memcmp (s, piv_aid+5, 4)) { /* The PIX does not match. */ log_error ("piv: missing or invalid DO 0x4F in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } if (s[4] != 1 || s[5] != 0) { log_error ("piv: unknown PIV version %u.%u\n", s[4], s[5]); err = gpg_error (GPG_ERR_CARD); goto leave; } app->appversion = ((s[4] << 8) | s[5]); s = find_tlv (apt, aptlen, 0x79, &n); if (!s || n < 7) { log_error ("piv: missing or invalid DO 0x79 in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } s = find_tlv (s, n, 0x4F, &n); if (!s || n != 5 || memcmp (s, piv_aid, 5)) { /* The RID does not match. */ log_error ("piv: missing or invalid DO 0x79.4F in APT\n"); err = gpg_error (GPG_ERR_CARD); goto leave; } app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { err = gpg_error_from_syserror (); goto leave; } if (app->card->cardtype == CARDTYPE_YUBIKEY) app->app_local->flags.yubikey = 1; /* FIXME: Parse the optional and conditional DOs in the APT. */ if (opt.verbose) dump_all_do (slot); app->fnc.deinit = do_deinit; app->fnc.prep_reselect = do_prep_reselect; app->fnc.reselect = do_reselect; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.writecert = do_writecert; app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_chv; app->fnc.check_pin = do_check_chv; app->fnc.with_keygrip = do_with_keygrip; leave: xfree (apt); if (err) do_deinit (app); return err; }