/* * partial PKCS15 emulation for gemsafe GPK cards * * Copyright (C) 2005, Douglas E. Engert * 2004, Nils Larsch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "log.h" #include "cardctl.h" #define MANU_ID "GemSAFE on GPK16000" int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); static int (*pin_cmd_save)(struct sc_card *, struct sc_pin_cmd_data *, int *tries_left); typedef struct cdata_st { const char *label; int authority; const char *path; const char *id; int obj_flags; } cdata; typedef struct pdata_st { const char *id; const char *label; const char *path; int ref; int type; unsigned int maxlen; unsigned int minlen; unsigned int storedlen; int flags; int tries_left; const char pad_char; int obj_flags; } pindata; typedef struct prdata_st { const char *id; const char *label; unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; } prdata; typedef struct keyinfo_st { int fileid; sc_pkcs15_id_t id; unsigned int modulus_len; u8 modulus[1024/8]; } keyinfo; #define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION #define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP #define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ SC_PKCS15_PRKEY_USAGE_DECRYPT | \ SC_PKCS15_PRKEY_USAGE_WRAP | \ SC_PKCS15_PRKEY_USAGE_UNWRAP | \ SC_PKCS15_PRKEY_USAGE_SIGN static const u8 gemsafe_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0F, 0x00, 0x00, 0x01, 0x63, 0x00, 0x01}; static int my_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data * data, int *tries_left) { /* GemSAFE pin uses a null terminated string with 0xFF */ /* so we need to add the 0x00 to the pin then pad with 0xFF */ int r; const u8 *saved_data = NULL; int saved_len = 0; u8 newpin[8]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); memset(newpin, 0xff, sizeof(newpin)); if (data->pin1.data && data->pin1.len < 8 && data->pin1.len > 0) { memcpy(newpin,data->pin1.data, (size_t)data->pin1.len); newpin[data->pin1.len] = 0x00; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "pin len=%d", data->pin1.len); saved_data = data->pin1.data; saved_len = data->pin1.len; data->pin1.data = newpin; data->pin1.len = sizeof(newpin); } r = pin_cmd_save(card, data, tries_left); if (saved_data) { data->pin1.data = saved_data; data->pin1.len = saved_len; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int is_seq(unsigned char * seq, unsigned int *seq_size, unsigned int *seq_len) { int i,j,k; if (seq[0] != 0x30) return 0; /* not a sequence */ if (seq[1] & 0x80) { i = seq[1] & 0x7f; if (i > 2 || i == 0) return 0; /* cert would be bigger then 65k or zero */ if (seq[2] == 0) return 0; /* DER would not have extra zero */ k = 0; for (j = 0; j < i; j++) { k = (k << 8) + seq[j + 2]; } if (k < 128) return 0; /* DER would have used single byte for len */ } else { i = 0; k = seq[1]; } *seq_size = i + 2; *seq_len = k; return 1; } static int gemsafe_detect_card(sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (strcmp(card->name, "Gemplus GPK")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_gemsafeGPK_init(sc_pkcs15_card_t *p15card) { const cdata certs[] = { {"User certificate",0, "","1", 0}, {NULL, 0, NULL, NULL, 0} }; const pindata pins[] = { { "1", "pin", "3F000200", 0x00, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, -1, 0x00, SC_PKCS15_CO_FLAG_PRIVATE }, { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; const prdata prkeys[] = { { "1", "AUTH key", 1024, USAGE_AUT, "I0009", 0x00, "1", 0}, { NULL, NULL, 0, 0, NULL, 0, NULL, 0} }; int r, i, j; int dfpath; sc_path_t path; sc_file_t *file = NULL; sc_card_t *card = p15card->card; unsigned char *gsdata = NULL; unsigned int idxlen, idx1, idx2, seq_len1, seq_len2, seq_size1, seq_size2; sc_serial_number_t serial; u8 sysrec[7]; int num_keyinfo = 0; keyinfo kinfo[8]; /* will loook for 8 keys */ u8 modulus_buf[ 1 + 1024 / 8]; /* tag+modulus */ u8 *cp; char buf[256]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* need to limit to 248 */ card->max_send_size = 248; card->max_recv_size = 248; /* could read this off card if needed */ p15card->tokeninfo->label = strdup("GemSAFE"); p15card->tokeninfo->manufacturer_id = strdup(MANU_ID); /* get serial number */ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; p15card->tokeninfo->serial_number = strdup(buf); /* test if we have a gemsafe app df */ memset(&path, 0, sizeof(path)); memcpy(path.value, gemsafe_aid, sizeof(gemsafe_aid)); path.len = sizeof(gemsafe_aid); path.type = SC_PATH_TYPE_DF_NAME; r = sc_select_file(card, &path, &file); if (r < 0) { /* OK, then lets try for 3f000200 */ sc_format_path("3F000200",&path); path.type = SC_PATH_TYPE_PATH; r = sc_select_file(card, &path, &file); } if (r < 0) return SC_ERROR_WRONG_CARD; /* we will use dfpath in all other references */ dfpath = file->id; free(file); file = NULL; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GemSafe file found, id=%d",dfpath); /* There may be more then one key in the directory. */ /* we need to find them so we can associate them with the */ /* the certificate. The files are 0007 to 000f */ for (i = 7; i < 16; i++) { path.value[0] = 0x00; path.value[1] = i; path.len = 2; path.type = SC_PATH_TYPE_FILE_ID; r = sc_select_file(card, &path, NULL); if (r < 0) continue; r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); if (r != 7 || sysrec[0] != 0) { continue; } if (sysrec[5] != 0x00) { continue; } switch (sysrec[1]) { case 0x00: kinfo[num_keyinfo].modulus_len = 512 / 8; break; case 0x10: kinfo[num_keyinfo].modulus_len = 768 / 8; break; case 0x11: kinfo[num_keyinfo].modulus_len = 1024 / 8; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported modulus length"); continue; } kinfo[num_keyinfo].fileid = i; sc_pkcs15_format_id("NONE", &kinfo[num_keyinfo].id); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"reading modulus"); r = sc_read_record(card, 2, modulus_buf, kinfo[num_keyinfo].modulus_len+1, SC_RECORD_BY_REC_NR); if (r < 0) continue; /* need to reverse the modulus skiping the tag */ j = kinfo[num_keyinfo].modulus_len; cp = kinfo[num_keyinfo].modulus; while (j--) *cp++ = modulus_buf[j + 1]; num_keyinfo++; } /* Get the gemsafe data with the cert */ sc_format_path("3F000200004", &path); /* file.id has the real DF of the GemSAFE file from above*/ path.value[2] = dfpath >> 8; path.value[3] = dfpath & 0xff; if (sc_select_file(card, &path, &file) < 0) { return SC_ERROR_WRONG_CARD; } /* the GemSAFE file has our cert, but we do not know the format */ /* of the file. But we do know a cert has SEQ SEQ SEQOF INT 2 */ /* so we will look for that. We assume cert is larger then 127 bytes */ /* and less then 65K, and must be fit in the file->size */ /* There is a chance that we might find something that is not */ /* a cert, but the chances are low. If GemPlus ever publishes */ /* the format of the file, we can used that instead. */ /* For performance reasons we will only */ /* read part of the file , as it is about 6100 bytes */ gsdata = malloc(file->size); if (!gsdata) return SC_ERROR_OUT_OF_MEMORY; /* set indcies of data in gsdata */ idx1 = 0; /* start point */ idx2 = 0; /* index of last data read so far */ /* set certs We only have one we are interested in */ /* but the read loop is set up to allow for more in future */ for (i = 0; certs[i].label; i++) { struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; sc_pkcs15_cert_t *cert_out; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); sc_pkcs15_format_id(certs[i].id, &cert_info.id); cert_info.authority = certs[i].authority; strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); cert_obj.flags = certs[i].obj_flags; while (idx1 < file->size - 16) { /* actually 13 for all these tests */ if (idx1 + 16 > idx2 ) { /* need more data in buff */ idxlen = 248; /* read in next 248 bytes */ if (idxlen > file->size - idx2) idxlen = file->size - idx2; r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); if (r < 0) break; idx2 = idx2 + idxlen; } if ( gsdata[idx1] == 0x30 && is_seq(gsdata + idx1, &seq_size1, &seq_len1) && is_seq(gsdata + idx1 + seq_size1, &seq_size2, &seq_len2) && gsdata[idx1 + seq_size1 + seq_size2 + 0] == 0xa0 && gsdata[idx1 + seq_size1 + seq_size2 + 1] == 0x03 && gsdata[idx1 + seq_size1 + seq_size2 + 2] == 0x02 && gsdata[idx1 + seq_size1 + seq_size2 + 3] == 0x01 && gsdata[idx1 + seq_size1 + seq_size2 + 4] == 0x02 && idx1 + 4 + seq_len1 < file->size) { /* we have a cert (I hope) */ /* read in rest if needed */ idxlen = idx1 + seq_len1 + 4 - idx2; if (idxlen > 0) { idxlen = (idxlen + 3) & 0xfffffffc; r = sc_read_binary(card, idx2, gsdata + idx2, idxlen, 0); if (r < 0) break; /* can not read cert */ idx2 = idx2 + idxlen; } cert_info.value.len = seq_len1 + 4; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Found cert at offset %d", idx1); cert_info.value.value = (unsigned char *) malloc(cert_info.value.len); if (!cert_info.value.value) return SC_ERROR_OUT_OF_MEMORY; memcpy(cert_info.value.value, gsdata + idx1, cert_info.value.len); idx1 = idx1 + cert_info.value.len; break; } idx1++; } if (cert_info.value.value == NULL) break; /* cert not found, no more certs */ r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { free(gsdata); return SC_ERROR_INTERNAL; } /* now lets see if we have a matching key for this cert */ r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out); if (r < 0) { free(gsdata); return SC_ERROR_INTERNAL; } for (j = 0; j < num_keyinfo; j++) { if (cert_out->key->u.rsa.modulus.len == kinfo[j].modulus_len && memcmp(cert_out->key->u.rsa.modulus.data, &kinfo[j].modulus, cert_out->key->u.rsa.modulus.len) == 0) { memcpy(&kinfo[j].id, &cert_info.id, sizeof(sc_pkcs15_id_t)); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found match"); } } sc_pkcs15_free_certificate(cert_out); } if (gsdata) free(gsdata); /* set pins */ /* GemSAFE uses different padding, so need to trap */ /* the pin_cmd and reset the padding */ pin_cmd_save = card->ops->pin_cmd; card->ops->pin_cmd = my_pin_cmd; for (i = 0; pins[i].label; i++) { struct sc_pkcs15_pin_info pin_info; struct sc_pkcs15_object pin_obj; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id); pin_info.reference = pins[i].ref; pin_info.flags = pins[i].flags; pin_info.type = pins[i].type; pin_info.min_length = pins[i].minlen; pin_info.stored_length = pins[i].storedlen; pin_info.max_length = pins[i].maxlen; pin_info.pad_char = pins[i].pad_char; sc_format_path(pins[i].path, &pin_info.path); pin_info.path.value[2] = dfpath >> 8; pin_info.path.value[3] = dfpath & 0xff; pin_info.tries_left = -1; strlcpy(pin_obj.label, pins[i].label, sizeof(pin_obj.label)); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if (r < 0) return SC_ERROR_INTERNAL; } /* needs work, as we may want to add more then one key */ /* but not sure what the other keys do */ /* set private keys */ for (i = 0; prkeys[i].label; i++) { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id); prkey_info.usage = prkeys[i].usage; prkey_info.native = 1; prkey_info.key_reference = prkeys[i].ref; prkey_info.modulus_length= prkeys[i].modulus_len; sc_format_path(prkeys[i].path, &prkey_info.path); /*DEE need to look for them by reading and checking mudulus vs cert */ /* will use the default path, unless we found a key with */ /* the same modulus as the cert(s) we already added */ /* This allows us to have a card with a key but no cert */ for (j = 0; j < num_keyinfo; j++) { if (sc_pkcs15_compare_id(&kinfo[j].id, &prkey_info.id)) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found key in file %d for id %d", kinfo[j].fileid, prkey_info.id); prkey_info.path.value[0] = kinfo[j].fileid >> 8; prkey_info.path.value[1] = kinfo[j].fileid & 0xff; break; } } strlcpy(prkey_obj.label, prkeys[i].label, sizeof(prkey_obj.label)); prkey_obj.flags = prkeys[i].obj_flags; if (prkeys[i].auth_id) sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id); r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); if (r < 0) return SC_ERROR_INTERNAL; } return SC_SUCCESS; } int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Entering %s", __FUNCTION__); if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) return sc_pkcs15emu_gemsafeGPK_init(p15card); else { int r = gemsafe_detect_card(p15card); if (r) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_gemsafeGPK_init(p15card); } }