openwrt-packages/utils/opensc/patches/0012-OpenPGP-Support-write-...

218 lines
7.0 KiB
Diff

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.");