📄 card-gpk.c
字号:
fbuf[1] = fid & 0xff; log_errs = card->ctx->log_errors; card->ctx->log_errors = 0; r = gpk_select(card, kind, fbuf, 2, file); card->ctx->log_errors = log_errs; /* Fix up the path cache. * NB we never cache the ID of an EF, just the DF path */ if (r == 0) { unsigned short int *path; switch (kind) { case GPK_SEL_MF: cp->len = 0; /* fallthru */ case GPK_SEL_DF: assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2); path = (unsigned short int *) cp->value; path[cp->len++] = fid; } } else { cp->len = 0; } return r;}static intgpk_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file){ unsigned short int pathtmp[SC_MAX_PATH_SIZE/2]; unsigned short int *pathptr; size_t pathlen, n; int locked = 0, r = 0, use_relative = 0, retry = 1; u8 leaf_type; SC_FUNC_CALLED(card->ctx, 1); /* Handle the AID case first */ if (path->type == SC_PATH_TYPE_DF_NAME) { if (path->len > 16) return SC_ERROR_INVALID_ARGUMENTS; r = gpk_select(card, GPK_SEL_AID, path->value, path->len, file); goto done; } /* Now we know we're dealing with 16bit FIDs, either as * an absolute path name (SC_PATH_TYPE_PATH) or a relative * FID (SC_PATH_TYPE_FILE_ID) * * The API should really tell us whether this is a DF or EF * we're selecting. All we can do is read tea leaves... */ leaf_type = GPK_SEL_EF;try_again: if ((path->len & 1) || path->len > sizeof(pathtmp)) return SC_ERROR_INVALID_ARGUMENTS; pathptr = pathtmp; for (n = 0; n < path->len; n += 2) pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1]; pathlen = path->len >> 1; /* See whether we can skip an initial portion of the * (absolute) path */ if (path->type == SC_PATH_TYPE_PATH) { /* Do not retry selecting if this cannot be a DF */ if ((pathptr[0] == GPK_FID_MF && pathlen > 2) || (pathptr[0] != GPK_FID_MF && pathlen > 1)) retry = 0; use_relative = match_path(card, &pathptr, &pathlen, file != 0); if (pathlen == 0) goto done; } else { /* SC_PATH_TYPE_FILEID */ if (pathlen > 1) return SC_ERROR_INVALID_ARGUMENTS; use_relative = 1; } if (pathlen == 1 && pathptr[0] == GPK_FID_MF) { /* Select just the MF */ leaf_type = GPK_SEL_MF; } else { if (!locked++) { r = sc_lock(card); SC_TEST_RET(card->ctx, r, "sc_lock() failed"); } /* Do we need to select the MF first? */ if (!use_relative) { r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, r, "Unable to select MF"); /* Consume the MF FID if it's there */ if (pathptr[0] == GPK_FID_MF) { pathptr++; pathlen--; } if (pathlen == 0) goto done; } /* Next comes a DF, if at all. * This loop can deal with nesting levels > 1 even * though the GPK4000 doesn't support it. */ while (pathlen > 1) { r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL); if (r) sc_unlock(card); SC_TEST_RET(card->ctx, r, "Unable to select DF"); pathptr++; pathlen--; } } /* Remaining component will be a DF or EF. How do we find out? * All we can do is try */ r = gpk_select_id(card, leaf_type, pathptr[0], file); if (r) { /* Did we guess EF, and were wrong? If so, invalidate * path cache and try again; this time aiming for a DF */ if (leaf_type == GPK_SEL_EF && retry) { card->cache.current_path.len = 0; leaf_type = GPK_SEL_DF; goto try_again; } }done: if (locked) sc_unlock(card); return r;}/* * GPK versions of {read,write,update}_binary functions. * Required because by default the GPKs do word offsets */static intgpk_read_binary(struct sc_card *card, unsigned int offset, u8 *buf, size_t count, unsigned long flags){ struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_error(card->ctx, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->read_binary(card, offset >> priv->offset_shift, buf, count, flags);}static intgpk_write_binary(struct sc_card *card, unsigned int offset, const u8 *buf, size_t count, unsigned long flags){ struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_error(card->ctx, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->write_binary(card, offset >> priv->offset_shift, buf, count, flags);}static intgpk_update_binary(struct sc_card *card, unsigned int offset, const u8 *buf, size_t count, unsigned long flags){ struct gpk_private_data *priv = DRVDATA(card); if (offset & priv->offset_mask) { sc_error(card->ctx, "Invalid file offset (not a multiple of %d)", priv->offset_mask + 1); return SC_ERROR_INVALID_ARGUMENTS; } return iso_ops->update_binary(card, offset >> priv->offset_shift, buf, count, flags);}/* * Secure messaging */static intgpk_compute_crycks(struct sc_card *card, struct sc_apdu *apdu, u8 *crycks1){ struct gpk_private_data *priv = DRVDATA(card); des_key_schedule k1, k2; u8 in[8], out[8], block[64]; unsigned int len = 0, i, j; /* Set the key schedule */ DES_set_key_unchecked((des_cblock *) priv->key, &k1); DES_set_key_unchecked((des_cblock *) (priv->key+8), &k2); /* Fill block with 0x00 and then with the data. */ memset(block, 0x00, sizeof(block)); block[len++] = apdu->cla; block[len++] = apdu->ins; block[len++] = apdu->p1; block[len++] = apdu->p2; block[len++] = apdu->lc + 3; if ((i = apdu->datalen) + len > sizeof(block)) i = sizeof(block) - len; memcpy(block+len, apdu->data, i); len += i; /* Set IV */ memset(in, 0x00, 8); for (j = 0; j < len; ) { for (i = 0; i < 8; i++, j++) in[i] ^= block[j]; DES_ecb3_encrypt((des_cblock *)in, (des_cblock *)out, &k1, &k2, &k1, DES_ENCRYPT); memcpy(in, out, 8); } memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3); apdu->datalen += 3; apdu->lc += 3; apdu->le += 3; if (crycks1) memcpy(crycks1, out, 3); des_cleanse(k1); des_cleanse(k2); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); memset(block, 0, sizeof(block)); return 0;}/* * Verify secure messaging response */static intgpk_verify_crycks(struct sc_card *card, struct sc_apdu *apdu, u8 *crycks){ if (apdu->resplen < 3 || memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) { if (card->ctx->debug) sc_debug(card->ctx, "Invalid secure messaging reply\n"); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } apdu->resplen -= 3; return 0;}/* * Create a file or directory. * This is a bit tricky because we abuse the ef_structure * field to transport file types that are non-standard * (the GPK4000 has lots of bizarre file types). */static intgpk_create_file(struct sc_card *card, struct sc_file *file){ struct gpk_private_data *priv = DRVDATA(card); struct sc_apdu apdu; u8 data[28+3], crycks[3], resp[3]; size_t datalen, namelen; int r; if (card->ctx->debug) sc_debug(card->ctx, "gpk_create_file(0x%04X)\n", file->id); /* Prepare APDU */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; /* assume no secure messaging */ apdu.cse = SC_APDU_CASE_3_SHORT; apdu.ins = 0xE0; apdu.p2 = 0x00; /* clear data */ memset(data, 0, sizeof(data)); datalen = 12; /* FID */ data[0] = file->id >> 8; data[1] = file->id & 0xFF; /* encode ACLs */ if (file->type == SC_FILE_TYPE_DF) { /* The GPK4000 has separate AC bits for * creating sensitive files and creating * data files. Since OpenSC has just the notion * of "file" we use the same ACL for both AC words */ apdu.p1 = 0x01; /* create DF */ data[2] = 0x38; acl_to_ac(file, SC_AC_OP_CREATE, data + 6); acl_to_ac(file, SC_AC_OP_CREATE, data + 8); if ((namelen = file->namelen) != 0) { if (namelen > 16) return SC_ERROR_INVALID_ARGUMENTS; memcpy(data+datalen, file->name, namelen); data[5] = namelen; datalen += namelen; } } else { apdu.p1 = 0x02; /* create EF */ data[2] = file->ef_structure; data[3] = file->record_length; data[4] = file->size >> 8; data[5] = file->size & 0xff; acl_to_ac(file, SC_AC_OP_UPDATE, data + 6); acl_to_ac(file, SC_AC_OP_WRITE, data + 8); acl_to_ac(file, SC_AC_OP_READ, data + 10); } apdu.data = data; apdu.datalen = datalen; apdu.lc = datalen; if (priv->key_set) { apdu.cla = 0x84; apdu.cse = SC_APDU_CASE_4_SHORT; r = gpk_compute_crycks(card, &apdu, crycks); if (r) return r; apdu.resp = resp; apdu.resplen = sizeof(resp); /* XXX? */ } 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"); /* verify secure messaging response */ if (priv->key_set) r = gpk_verify_crycks(card, &apdu, crycks); return r;}/* * Set the secure messaging key following a Select FileKey */static intgpk_set_filekey(const u8 *key, const u8 *challenge, const u8 *r_rn, u8 *kats){ des_key_schedule k1, k2; des_cblock out; int r = 0; DES_set_key_unchecked((des_cblock *) key, &k1); DES_set_key_unchecked((des_cblock *) (key+8), &k2); DES_ecb3_encrypt((des_cblock *)(r_rn+4), (des_cblock *) kats, &k1, &k2, &k1, DES_ENCRYPT); DES_ecb3_encrypt((des_cblock *)(r_rn+4), (des_cblock *) (kats+8), &k2, &k1, &k2, DES_ENCRYPT); /* Verify Cryptogram presented by the card terminal * XXX: what is the appropriate error code to return * here? INVALID_ARGS doesn't seem quite right */ DES_set_key_unchecked((des_cblock *) kats, &k1); DES_set_key_unchecked((des_cblock *) (kats+8), &k2); DES_ecb3_encrypt((des_cblock *) challenge, &out, &k1, &k2, &k1, DES_ENCRYPT ); if (memcmp(r_rn, out+4, 4) != 0) r = SC_ERROR_INVALID_ARGUMENTS; des_cleanse(k1); des_cleanse(k2); memset(out, 0, sizeof(out)); return r;}/* * Verify a key presented by the user for secure messaging */static intgpk_select_key(struct sc_card *card, int key_sfi, const u8 *buf, size_t buflen){ struct gpk_private_data *priv = DRVDATA(card); struct sc_apdu apdu; u8 random[8], resp[258]; int r; SC_FUNC_CALLED(card->ctx, 1); if (buflen != 16) return SC_ERROR_INVALID_ARGUMENTS; /* now do the SelFk */ RAND_pseudo_bytes(random, sizeof(random)); memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; apdu.cse = SC_APDU_CASE_4_SHORT; apdu.ins = 0x28; apdu.p1 = 0; apdu.p2 = key_sfi; apdu.data = random; apdu.datalen = sizeof(random); apdu.lc = apdu.datalen; apdu.resp = resp; apdu.resplen = sizeof(resp); apdu.le = 12; apdu.sensitive = 1; 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"); if (apdu.resplen != 12) { r = SC_ERROR_UNKNOWN_DATA_RECEIVED; } else if ((r = gpk_set_filekey(buf, random, resp, priv->key)) == 0) { priv->key_set = 1; priv->key_reference = key_sfi; } memset(resp, 0, sizeof(resp)); return r;}/* * Select a security environment (Set Crypto Context in GPK docs). * When we get here, the PK file has already been selected. * * Issue: the GPK distinguishes between "signing" and * "card internal authentication". I don't know whether this * makes any difference in practice... * * Issue: it seems that sc_compute_signature() does not hash * the data for the caller. So what is the purpose of HASH_SHA * and other flags? */static intgpk_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num){ struct gpk_private_data *priv = DRVDATA(card); struct sc_apdu apdu; unsigned int context, algorithm; unsigned int file_id; u8 sysrec[7]; int r; /* According to several sources from GemPlus, they don't * have off the shelf cards that do DSA. So I won't bother * with implementing this stuff here. */ algorithm = SC_ALGORITHM_RSA; if (env->flags & SC_SEC_ENV_ALG_PRESENT) algorithm = env->algorithm; if (algorithm != SC_ALGORITHM_RSA) { sc_error(card->ctx, "Algorithm not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } priv->sec_algorithm = algorithm; /* If there's a key reference, it must be 0 */ if ((env->flags & SC_SEC_ENV_KEY_REF_PRESENT) && (env->key_ref_len != 1 || env->key_ref[0] != 0)) { sc_error(card->ctx, "Unknown key referenced.\n"); return SC_ERROR_NOT_SUPPORTED; } /* Right now, the OpenSC flags do not support any padding * other than PKCS#1. */ if (env->flags & SC_ALGORITHM_RSA_PAD_PKCS1) priv->sec_padding = 0; else if (env->flags & SC_ALGORITHM_RSA_PAD_ANSI) priv->sec_padding = 1; else if (env->flags & SC_ALGORITHM_RSA_PAD_ISO9796) priv->sec_padding = 2; else { sc_error(card->ctx, "Padding algorithm not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } switch (env->operation) { case SC_SEC_OPERATION_SIGN: /* Again, the following may not make any difference * because we don't do any hashing on-card. But * what the hell, we have all those nice macros, * so why not use them :) */ if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { context = GPK_SIGN_RSA_SHA; priv->sec_hash_len = 20; } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5_SHA1) { context = GPK_SIGN_RSA_SSL; priv->sec_hash_len = 36; } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) { context = GPK_SIGN_RSA_MD5; priv->sec_hash_len = 16; } else { sc_error(card->ctx, "Unsupported signature algorithm"); return SC_ERROR_NOT_SUPPORTED; } break; case SC_SEC_OPERATION_DECIPHER: context = GPK_UNWRAP_RSA; break; default: sc_error(card->ctx, "Crypto operation not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } /* Get the file ID */ if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { if (env->file_ref.len != 2) { sc_error(card->ctx, "File reference: invalid length.\n"); return SC_ERROR_INVALID_ARGUMENTS; } file_id = (env->file_ref.value[0] << 8) | env->file_ref.value[1]; } else { sc_error(card->ctx, "File reference missing.\n"); return SC_ERROR_INVALID_ARGUMENTS; } /* Select the PK file. The caller has already selected * the DF. */ r = gpk_select_id(card, GPK_SEL_EF, file_id, NULL); SC_TEST_RET(card->ctx, r, "Failed to select PK file"); /* Read the sys record of the PK file to find out the key length */ r = sc_read_record(card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); SC_TEST_RET(card->ctx, r, "Failed to read PK sysrec"); if (r != 7 || sysrec[0] != 0) { sc_error(card->ctx, "First record of file is not the sysrec"); return SC_ERROR_OBJECT_NOT_VALID; } if (sysrec[5] != 0x00) { sc_error(card->ctx, "Public key is not an RSA key"); return SC_ERROR_OBJECT_NOT_VALID; } switch (sysrec[1]) { case 0x00: priv->sec_mod_len = 512 / 8; break; case 0x10: priv->sec_mod_len = 768 / 8; break; case 0x11: priv->sec_mod_len = 1024 / 8; break; default: sc_error(card->ctx, "Unsupported modulus length"); return SC_ERROR_OBJECT_NOT_VALID; } /* Now do SelectCryptoContext */ memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_1; apdu.cla = 0x80; apdu.ins = 0xA6; apdu.p1 = file_id & 0x1f; apdu.p2 = context; 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"); return r;}/* * Restore security environment * Not sure what this is supposed to do. */static intgpk_restore_security_env(struct sc_card *card, int se_num){ return 0;}/* * Revert buffer (needed for all GPK crypto operations because * they want LSB byte order internally */static intreverse(u8 *out, size_t outlen, const u8 *in, size_t inlen){ if (inlen > outlen) return SC_ERROR_BUFFER_TOO_SMALL; outlen = inlen; while (inlen--) *out++ = in[inlen]; return outlen;}/* * Use the card's on-board hashing functions to hash some data */#ifdef dontusestatic intgpk_hash(struct sc_card *card, const u8 *data, size_t datalen){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -