|
|
- From 5110ae3ba33d165c43ea5eca8f929a82d81cb3fe Mon Sep 17 00:00:00 2001
- From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
- <ng.hong.quan@gmail.com>
- Date: Thu, 11 Apr 2013 11:47:51 +0700
- Subject: [PATCH 12/26] OpenPGP: Support write certificate for Gnuk.
-
- ---
- src/libopensc/card-openpgp.c | 158 +++++++++++++++++++++++++++++++++----------
- 1 file changed, 123 insertions(+), 35 deletions(-)
-
- Index: opensc-20150513/src/libopensc/card-openpgp.c
- ===================================================================
- --- opensc-20150513.orig/src/libopensc/card-openpgp.c
- +++ opensc-20150513/src/libopensc/card-openpgp.c
- @@ -735,6 +735,8 @@ pgp_iterate_blobs(struct blob *blob, int
- 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)
- @@ -745,6 +747,11 @@ pgp_read_blob(sc_card_t *card, struct bl
- 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
- @@ -1200,49 +1207,75 @@ pgp_get_data(sc_card_t *card, unsigned i
- 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) {
- @@ -1268,15 +1301,70 @@ pgp_put_data(sc_card_t *card, unsigned i
- 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.");
|