From d8f63eb6fcc1441c12a44850da2fa22a6fe81634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Thu, 11 Apr 2013 11:47:51 +0700 Subject: [PATCH 12/18] OpenPGP: Support write certificate for Gnuk. --- src/libopensc/card-openpgp.c | 158 +++++++++++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 35 deletions(-) diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c index 8a1a270..d9db948 100644 --- a/src/libopensc/card-openpgp.c +++ b/src/libopensc/card-openpgp.c @@ -725,6 +725,8 @@ pgp_iterate_blobs(struct blob *blob, int level, void (*func)()) static int pgp_read_blob(sc_card_t *card, struct blob *blob) { + struct pgp_priv_data *priv = DRVDATA (card); + if (blob->data != NULL) return SC_SUCCESS; if (blob->info == NULL) @@ -735,6 +737,11 @@ pgp_read_blob(sc_card_t *card, struct blob *blob) size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT) ? sizeof(buffer) : 256; + /* Buffer length for certificate */ + if (blob->id == DO_CERT && priv->max_cert_size > 0) { + buf_len = MIN(priv->max_cert_size, sizeof(buffer)); + } + /* Buffer length for Gnuk pubkey */ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && (blob->id == 0xa400 || blob->id == 0xb600 || blob->id == 0xb800 @@ -1190,49 +1197,75 @@ pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len) LOG_FUNC_RETURN(card->ctx, apdu.resplen); } -/* ABI: PUT DATA */ -static int -pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) + +/* Internal: Write certificate for Gnuk */ +static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length) { + sc_context_t *ctx = card->ctx; + size_t i = 0; sc_apdu_t apdu; + u8 *part; + size_t plen; + int r = SC_SUCCESS; + + LOG_FUNC_CALLED(ctx); + + /* If null data is passed, delete certificate */ + if (buf == NULL || length == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xD6, 0x85, 0); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* Check response */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_FUNC_RETURN(card->ctx, length); + } + + /* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */ + /* Split data to segments of 256 bytes. Send each segment via command chaining, + * with particular P1 byte for each segment */ + while (i*256 < length) { + part = (u8 *)buf + i*256; + plen = MIN(length - i*256, 256); + + sc_log(card->ctx, "Write part %d from offset 0x%X, len %d", i+1, part, plen); + + if (i == 0) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, 0x85, 0); + } + else { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, i, 0); + } + apdu.flags |= SC_APDU_FLAGS_CHAINING; + apdu.data = part; + apdu.datalen = apdu.lc = plen; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + /* Check response */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "UPDATE BINARY returned error"); + + /* To next part */ + i++; + } + LOG_FUNC_RETURN(card->ctx, length); +} + + +/* Internal: Use PUT DATA command to write */ +static int +pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) +{ struct pgp_priv_data *priv = DRVDATA(card); - struct blob *affected_blob = NULL; - struct do_info *dinfo = NULL; + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; u8 ins = 0xDA; u8 p1 = tag >> 8; u8 p2 = tag & 0xFF; u8 apdu_case = SC_APDU_CASE_3; int r; - LOG_FUNC_CALLED(card->ctx); - - /* Check if the tag is writable */ - affected_blob = pgp_find_blob(card, tag); - - /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */ - if (affected_blob == NULL) - dinfo = pgp_get_info_by_tag(card, tag); - else - dinfo = affected_blob->info; - - if (dinfo == NULL) { - sc_log(card->ctx, "The DO %04X does not exist.", tag); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); - } - else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) { - sc_log(card->ctx, "DO %04X is not writable.", tag); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); - } - - /* Check data size. - * We won't check other DOs than 7F21 (certificate), because their capacity - * is hard-codded and may change in various version of the card. If we check here, - * the driver may be sticked to a limit version number of card. - * 7F21 size is soft-coded, so we can check it. */ - if (tag == DO_CERT && buf_len > priv->max_cert_size) { - sc_log(card->ctx, "Data size %ld exceeds DO size limit %ld.", buf_len, priv->max_cert_size); - LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); - } + LOG_FUNC_CALLED(ctx); /* Extended Header list (004D DO) needs a variant of PUT DATA command */ if (tag == 0x004D) { @@ -1258,15 +1291,70 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) apdu.lc = buf_len; } else { + /* This case is to empty DO */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2); } /* Send APDU to card */ r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); /* Check response */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r < 0) + LOG_FUNC_RETURN(ctx, r); + + LOG_FUNC_RETURN(ctx, buf_len); +} + +/* ABI: PUT DATA */ +static int +pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) +{ + struct pgp_priv_data *priv = DRVDATA(card); + struct blob *affected_blob = NULL; + struct do_info *dinfo = NULL; + int r; + + LOG_FUNC_CALLED(card->ctx); + + /* Check if the tag is writable */ + if (priv->current->id != tag) + affected_blob = pgp_find_blob(card, tag); + + /* Non-readable DOs have no represented blob, we have to check from pgp_get_info_by_tag */ + if (affected_blob == NULL) + dinfo = pgp_get_info_by_tag(card, tag); + else + dinfo = affected_blob->info; + + if (dinfo == NULL) { + sc_log(card->ctx, "The DO %04X does not exist.", tag); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) { + sc_log(card->ctx, "DO %04X is not writable.", tag); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); + } + + /* Check data size. + * We won't check other DOs than 7F21 (certificate), because their capacity + * is hard-codded and may change in various version of the card. If we check here, + * the driver may be sticked to a limit version number of card. + * 7F21 size is soft-coded, so we can check it. */ + if (tag == DO_CERT && buf_len > priv->max_cert_size) { + sc_log(card->ctx, "Data size %ld exceeds DO size limit %ld.", buf_len, priv->max_cert_size); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); + } + + if (tag == DO_CERT && card->type == SC_CARD_TYPE_OPENPGP_GNUK) { + /* Gnuk need a special way to write certificate. */ + r = gnuk_write_certificate(card, buf, buf_len); + } + else { + r = pgp_put_data_plain(card, tag, buf, buf_len); + } + /* Instruct more in case of error */ if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first."); -- 1.9.3