📄 card-gpk.c
字号:
/* * card-gpk: Driver for GPK 4000 cards * * Copyright (C) 2002 Olaf Kirch <okir@lst.de> * * 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 "internal.h"#include "cardctl.h"#include "pkcs15.h"#ifdef HAVE_OPENSSL#include <stdlib.h>#include <string.h>#include <openssl/des.h>#include <openssl/rand.h>#if OPENSSL_VERSION_NUMBER >= 0x00907000L#define des_cleanse(k) OPENSSL_cleanse(k.ks, sizeof(k.ks))#else#define des_cleanse(k) memset(&k, 0, sizeof(k))#define DES_set_key_unchecked(a,b) des_set_key_unchecked(a,*b)#define DES_ecb3_encrypt(a,b,c,d,e,f) des_ecb3_encrypt(a,b,*c,*d,*e,f)#endif/* Gemplus card variants */enum { GPK4000_su256 = 4000, GPK4000_s, GPK4000_sp, GPK4000_sdo, GPK8000 = 8000, GPK8000_8K, GPK8000_16K, GPK16000 = 16000};#define GPK_SEL_MF 0x00#define GPK_SEL_DF 0x01#define GPK_SEL_EF 0x02#define GPK_SEL_AID 0x04#define GPK_FID_MF 0x3F00#define GPK_FTYPE_SC 0x21#define GPK_SIGN_RSA_MD5 0x11#define GPK_SIGN_RSA_SHA 0x12#define GPK_SIGN_RSA_SSL 0x18#define GPK_VERIFY_RSA_MD5 0x21#define GPK_VERIFY_RSA_SHA 0x22#define GPK_AUTH_RSA_MD5 0x31#define GPK_AUTH_RSA_SHA 0x32#define GPK_AUTH_RSA_SSL 0x38#define GPK_UNWRAP_RSA 0x77#define GPK_MAX_PINS 8#define GPK_HASH_CHUNK 62/* * GPK4000 private data */struct gpk_private_data { int variant; /* The GPK usually do file offsets in multiples of * 4 bytes. This can be customized however. We * should really query for this during gpk_init */ unsigned int offset_shift; unsigned int offset_mask; unsigned int locked : 1, sample_card : 1; /* access control bits of file most recently selected */ unsigned short int ac[3]; /* is non-zero if we should use secure messaging */ unsigned key_set : 1; unsigned int key_reference; u8 key[16]; /* crypto related data from set_security_env */ unsigned int sec_algorithm; unsigned int sec_hash_len; unsigned int sec_mod_len; unsigned int sec_padding;};#define DRVDATA(card) ((struct gpk_private_data *) ((card)->drv_data))static int gpk_get_info(struct sc_card *, u8, u8, u8 *, size_t);/* * ATRs of GPK4000 cards courtesy of libscez */static struct atrinfo { unsigned char atr[SC_MAX_ATR_SIZE]; unsigned int atr_len; int variant; const char * name;} atrlist[] = { { "\x3B\x27\x00\x80\x65\xA2\x04\x01\x01\x37", 10, GPK4000_s, "GPK 4K" }, { "\x3B\x27\x00\x80\x65\xA2\x05\x01\x01\x37", 10, GPK4000_sp, "GPK 4K" }, { "\x3B\x27\x00\x80\x65\xA2\x0C\x01\x01\x37", 10, GPK4000_su256, "GPK 4K" }, { "\x3B\xA7\x00\x40\x14\x80\x65\xA2\x14\x01\x01\x37", 12, GPK4000_sdo, "GPK 4K" }, { "\x3B\xA7\x00\x40\x18\x80\x65\xA2\x08\x01\x01\x52", 12, GPK8000_8K, "GPK 8K" }, { "\x3B\xA7\x00\x40\x18\x80\x65\xA2\x09\x01\x01\x52", 12, GPK8000_16K, "GPK 8K" }, { "\x3B\xA7\x00\x40\x18\x80\x65\xA2\x09\x01\x02\x52", 12, GPK16000, "GPK 16K" }, { "", 0, -1 }};/* * Driver and card ops structures */static struct sc_card_operations gpk_ops, *iso_ops;static struct sc_card_driver gpk_drv = { "Gemplus GPK driver", "gpk", &gpk_ops};/* * Identify the card variant based on the ATR * returns the variant number or 0 on error */static intgpk_identify(struct sc_card *card){ struct atrinfo *ai; /* Gemplus GPK docs say we can use just the * FMN and PRN fields of the historical bytes * to recognize a GPK card * See Table 43, pp. 188 * We'll use the first 2 bytes as well */ if ( (card->slot->atr_info.hist_bytes_len >= 7) && (card->slot->atr_info.hist_bytes[0] == 0x80) && (card->slot->atr_info.hist_bytes[1] == 0x65) && (card->slot->atr_info.hist_bytes[2] == 0xa2)) /* FMN */ { if (card->slot->atr_info.hist_bytes[3] == 0x08){ /* PRN? */ return GPK8000; } if (card->slot->atr_info.hist_bytes[3] == 0x09){ /* PRN? */ return GPK16000; } } /* if the above ATR-analysis fails, check the known ATR list */ for (ai = atrlist; ai->atr_len; ai++) { if (card->atr_len >= ai->atr_len && !memcmp(card->atr, ai->atr, ai->atr_len)) return ai->variant; } return 0;}/* * return 1 iff this driver can handle the card */static intgpk_match(struct sc_card *card){ return gpk_identify(card)? 1 : 0;}/* * Initialize the card struct */static intgpk_init(struct sc_card *card){ struct gpk_private_data *priv; unsigned long exponent, flags, kg; unsigned char info[13]; int variant; if (!(variant = gpk_identify(card))) return SC_ERROR_INVALID_CARD; card->drv_data = priv = (struct gpk_private_data *) malloc(sizeof(*priv)); if (card->drv_data == NULL) return SC_ERROR_OUT_OF_MEMORY; memset(priv, 0, sizeof(*priv)); priv->variant = variant; /* read/write/update binary expect offset to be the * number of 32 bit words. * offset_shift is the shift value. * offset_mask is the corresponding mask. */ priv->offset_shift = 2; priv->offset_mask = 3; card->cla = 0; /* Set up algorithm info. GPK 16000 will do any RSA * exponent, earlier ones are restricted to 0x10001 */ flags = SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_MD5_SHA1; flags |= SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ANSI | SC_ALGORITHM_RSA_PAD_ISO9796; exponent = (variant < 16000)? 0x10001 : 0; kg = (variant >= 8000)? SC_ALGORITHM_ONBOARD_KEY_GEN : 0; _sc_card_add_rsa_alg(card, 512, flags|kg, exponent); _sc_card_add_rsa_alg(card, 768, flags, exponent); _sc_card_add_rsa_alg(card, 1024, flags|kg, exponent); /* Inspect the LOCK byte */ if (gpk_get_info(card, 0x02, 0xA4, info, sizeof(info)) >= 0) { if (info[12] & 0x40) { priv->offset_shift = 0; priv->offset_mask = 0; } if (info[12] & 0x10) { /* DSA supported - add algo information. * It's highly unlikely we'll ever see this. */ } if (info[12] & 0x08) { priv->locked = 1; } /* Sample cards use a transport key of "TEST KEYTEST KEY" */ if (!memcmp(info+5, "\x00\xff\x00", 3)) { priv->sample_card = 1; } } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; return 0;}/* * Card is being closed; discard any private data etc */static intgpk_finish(struct sc_card *card){ if (card->drv_data) free(card->drv_data); card->drv_data = NULL; return 0;}/* * Error code handling for the GPK4000. * sc_check_sw doesn't seem to handle all of the * status words the GPK is capable of returning */#if 0static intgpk_check_sw(struct sc_card *card, u8 sw1, u8 sw2){ unsigned short int sw = (sw1 << 8) | sw2; if ((sw & 0xFFF0) == 0x63C0) { sc_error(card->ctx, "wrong PIN, %u tries left\n", sw&0xf); return SC_ERROR_PIN_CODE_INCORRECT; } switch (sw) { case 0x6400: sc_error(card->ctx, "wrong crypto context\n"); return SC_ERROR_OBJECT_NOT_VALID; /* XXX ??? */ /* The following are handled by iso7816_check_sw * but all return SC_ERROR_UNKNOWN_DATA_RECEIVED * XXX: fix in the iso driver? */ case 0x6983: sc_error(card->ctx, "PIN is blocked\n"); return SC_ERROR_PIN_CODE_INCORRECT; case 0x6581: sc_error(card->ctx, "out of space on card or file\n"); return SC_ERROR_OUT_OF_MEMORY; case 0x6981: return SC_ERROR_FILE_NOT_FOUND; case 0x6A80: case 0x6b00: return SC_ERROR_INVALID_ARGUMENTS; } return iso7816_check_sw(card, sw1, sw2);}#endif/* * Select a DF/EF */static intmatch_path(struct sc_card *card, unsigned short int **pathptr, size_t *pathlen, int need_info){ unsigned short int *curptr, *ptr; size_t curlen, len; size_t i; curptr = (unsigned short int *) card->cache.current_path.value; curlen = card->cache.current_path.len; ptr = *pathptr; len = *pathlen; if (curlen < 1 || len < 1) return 0; /* Make sure path starts with MF. * Note the cached path should always begin with MF. */ if (ptr[0] != GPK_FID_MF || curptr[0] != GPK_FID_MF) return 0; for (i = 1; i < len && i < curlen; i++) { if (ptr[i] != curptr[i]) break; } if (len < curlen) { /* Caller asked us to select the DF, but the * current file is some EF within the DF we're * interested in. Say ACK */ if (len == 2) goto okay; /* Anything else won't work */ return 0; } /* In the case of an exact match: * If the caller needs info on the file to be selected, * make sure we at least select the file itself. * If the DF matches the current DF, just return the * FID */ if (i == len && need_info) { if (i > 1) { *pathptr = ptr + len - 1; *pathlen = len - 1; return 1; } /* bummer */ return 0; }okay: *pathptr = ptr + i; *pathlen = len - i; return 1;}static voidac_to_acl(unsigned short int ac, struct sc_file *file, unsigned int op){ unsigned int npins, pin; npins = (ac >> 14) & 3; if (npins == 3) { sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE); return; } sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); pin = ac & 0xFF; if (npins >= 1) sc_file_add_acl_entry(file, op, SC_AC_CHV, (pin >> 4) & 0xF); if (npins == 2) sc_file_add_acl_entry(file, op, SC_AC_CHV, pin & 0xF); /* Check whether secure messaging key is specified */ if (ac & 0x3F00) sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8);}/* * Convert ACLs requested by the application to access condition * bits supported by the GPK. Since these do not map 1:1 there's * some fuzz involved. */static voidacl_to_ac(struct sc_file *file, unsigned int op, u8 *ac){ const struct sc_acl_entry *acl; unsigned int npins = 0; ac[0] = ac[1] = 0; if ((acl = sc_file_get_acl_entry(file, op)) == NULL) return; assert(acl->method != SC_AC_UNKNOWN); switch (acl->method) { case SC_AC_NEVER: ac[0] = 0xC0; return; case SC_AC_NONE: return; } while (acl) { if (acl->method == SC_AC_CHV) { /* Support up to 2 PINS only */ if (++npins >= 2) continue; ac[1] >>= 4; ac[1] |= acl->key_ref << 4; ac[0] += 0x40; } if (acl->method == SC_AC_PRO) { ac[0] |= acl->key_ref & 0x1f; } acl = acl->next; }}static intgpk_parse_fci(struct sc_card *card, const u8 *buf, size_t buflen, struct sc_file *file){ const u8 *end, *next; unsigned int tag, len; end = buf + buflen; for (; buf + 2 < end; buf = next) { next = buf + 2 + buf[1]; if (next > end) break; tag = *buf++; len = *buf++; if (tag == 0x84) { /* unknown purpose - usually the name, but * the content looks weird, such as * 84 0D A0 00 00 00 18 0F 00 00 01 63 00 01 04 */ } else if (tag == 0xC1 && len >= 2) { /* Seems to be the file id, followed by something * C1 04 02 00 00 00 */ file->id = (buf[0] << 8) | buf[1]; } else if (tag == 0xC2) { /* unknown purpose * C2 01 01 */ } } return 0;}static intgpk_parse_fileinfo(struct sc_card *card, const u8 *buf, size_t buflen, struct sc_file *file){ const u8 *sp, *end, *next; int i, rc; memset(file, 0, sizeof(*file)); for (i = 0; i < SC_MAX_AC_OPS; i++) sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); end = buf + buflen; for (sp = buf; sp + 2 < end; sp = next) { next = sp + 2 + sp[1]; if (next > end) break; if (sp[0] == 0x84) { /* ignore if name is longer than what it should be */ if (sp[1] > sizeof(file->name)) continue; memset(file->name, 0, sizeof(file->name)); memcpy(file->name, sp+2, sp[1]); } else if (sp[0] == 0x85) { unsigned int ac[3], n; file->id = (sp[4] << 8) | sp[5]; file->size = (sp[8] << 8) | sp[9]; file->record_length = sp[7]; /* Map ACLs. Note the third AC byte is * valid of EFs only */ for (n = 0; n < 3; n++) ac[n] = (sp[10+2*n] << 8) | sp[11+2*n]; /* Examine file type */ switch (sp[6] & 7) { case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = sp[6] & 7; ac_to_acl(ac[0], file, SC_AC_OP_UPDATE); ac_to_acl(ac[1], file, SC_AC_OP_WRITE); ac_to_acl(ac[2], file, SC_AC_OP_READ); break; case 0x00: /* 0x38 is DF */ file->type = SC_FILE_TYPE_DF; /* Icky: the GPK uses different ACLs * for creating data files and * 'sensitive' i.e. key files */ ac_to_acl(ac[0], file, SC_AC_OP_LOCK); ac_to_acl(ac[1], file, SC_AC_OP_CREATE); sc_file_add_acl_entry(file, SC_AC_OP_SELECT, SC_AC_NONE, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; } } else if (sp[0] == 0x6f) { /* oops - this is a directory with an IADF. * This happens with the personalized GemSafe cards * for instance. */ file->type = SC_FILE_TYPE_DF; rc = gpk_parse_fci(card, sp + 2, sp[1], file); if (rc < 0) return rc; } } if (file->record_length) file->record_count = file->size / file->record_length; file->magic = SC_FILE_MAGIC; return 0;}static intgpk_select(struct sc_card *card, u8 kind, const u8 *buf, size_t buflen, struct sc_file **file){ struct gpk_private_data *priv = DRVDATA(card); struct sc_apdu apdu; u8 resbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; /* If we're about to select a DF, invalidate secure messaging keys */ if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) { memset(priv->key, 0, sizeof(priv->key)); priv->key_set = 0; } /* do the apdu thing */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.cse = SC_APDU_CASE_3_SHORT; apdu.ins = 0xA4; apdu.p1 = kind; apdu.p2 = 0; apdu.data = buf; apdu.datalen = buflen; apdu.lc = apdu.datalen; apdu.resp = resbuf; apdu.resplen = file? sizeof(resbuf) : 0; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, r, "Card returned error"); /* Nothing we can say about it... invalidate * path cache */ if (kind == GPK_SEL_AID) { card->cache.current_path.len = 0; } if (file == NULL) return 0; *file = sc_file_new(); r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file); if (r < 0) { sc_file_free(*file); *file = NULL; } return r;}static intgpk_select_id(struct sc_card *card, u8 kind, unsigned short int fid, struct sc_file **file){ struct sc_path *cp = &card->cache.current_path; u8 fbuf[2]; int r, log_errs; if (card->ctx->debug) sc_debug(card->ctx, "gpk_select_id(0x%04X, kind=%u)\n", fid, kind); fbuf[0] = fid >> 8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -