From bbbedd3b358f80a7f98df2b22cf541cb007dd62e 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: Mon, 4 Mar 2013 18:13:03 +0700
Subject: [PATCH 06/18] openpgp-tool: Support deleting key in Gnuk.

---
 src/tools/openpgp-tool.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 143 insertions(+), 1 deletion(-)

diff --git a/src/tools/openpgp-tool.c b/src/tools/openpgp-tool.c
index 0d360a3..239c86b 100644
--- a/src/tools/openpgp-tool.c
+++ b/src/tools/openpgp-tool.c
@@ -39,6 +39,7 @@
 #define	OPT_PRETTY	257
 #define	OPT_VERIFY	258
 #define	OPT_PIN	    259
+#define	OPT_DELKEY  260
 
 /* define structures */
 struct ef_name_map {
@@ -77,6 +78,7 @@ static char *verifytype = NULL;
 static int opt_pin = 0;
 static char *pin = NULL;
 static int opt_erase = 0;
+static int opt_delkey = 0;
 
 static const char *app_name = "openpgp-tool";
 
@@ -96,6 +98,7 @@ static const struct option options[] = {
 	{ "erase",     no_argument,       NULL, 'E'        },
 	{ "verify",    required_argument, NULL, OPT_VERIFY },
 	{ "pin",       required_argument, NULL, OPT_PIN },
+	{ "del-key",   required_argument, NULL, OPT_DELKEY },
 	{ NULL, 0, NULL, 0 }
 };
 
@@ -114,7 +117,8 @@ static const char *option_help[] = {
 /* V */	"Show version number",
 /* E */	"Erase (reset) the card",
 	"Verify PIN (CHV1, CHV2, CHV3...)",
-	"PIN string"
+	"PIN string",
+	"Delete key (1, 2, 3 or all)"
 };
 
 static const struct ef_name_map openpgp_data[] = {
@@ -294,6 +298,14 @@ static int decode_options(int argc, char **argv)
 		case 'E':
 			opt_erase++;
 			break;
+		case OPT_DELKEY:
+			opt_delkey++;
+			if (strcmp(optarg, "all") != 0)   /* Arg string is not 'all' */
+				key_id = optarg[0] - '0';
+			else                              /* Arg string is 'all' */
+				key_id = 'a';
+			actions++;
+			break;
 		default:
 			util_print_usage_and_die(app_name, options, option_help, NULL);
 		}
@@ -452,6 +464,133 @@ int do_verify(sc_card_t *card, u8 *type, u8* pin)
 	return r;
 }
 
+/**
+ * Delete key, for Gnuk.
+ **/
+int delete_key_gnuk(sc_card_t *card, u8 key_id)
+{
+	sc_context_t *ctx = card->ctx;
+	int r = SC_SUCCESS;
+	u8 *data = NULL;
+
+	/* Delete fingerprint */
+	sc_log(ctx, "Delete fingerprints");
+	r |= sc_put_data(card, 0xC6 + key_id, NULL, 0);
+	/* Delete creation time */
+	sc_log(ctx, "Delete creation time");
+	r |= sc_put_data(card, 0xCD + key_id, NULL, 0);
+
+	/* Rewrite Extended Header List */
+	sc_log(ctx, "Rewrite Extended Header List");
+
+	if (key_id == 1)
+		data = "\x4D\x02\xB6";
+	else if (key_id == 2)
+		data = "\x4D\x02\xB8";
+	else if (key_id == 3)
+		data = "\x4D\x02\xA4";
+	else
+		return SC_ERROR_INVALID_ARGUMENTS;
+
+	r |= sc_put_data(card, 0x4D, data, strlen(data) + 1);
+	return r;
+}
+
+/**
+ * Delete key, for OpenPGP card.
+ * This function is not complete and is reserved for future version (> 2) of OpenPGP card.
+ **/
+int delete_key_openpgp(sc_card_t *card, u8 key_id)
+{
+	sc_context_t *ctx = card->ctx;
+	char *del_fingerprint = "00:DA:00:C6:14:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
+	char *del_creationtime = "00:DA:00:CD:04:00:00:00:00";
+	/* We need to replace the 4th byte later */
+	char *apdustring = NULL;
+	u8 buf[SC_MAX_APDU_BUFFER_SIZE];
+	u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
+	sc_apdu_t apdu;
+	size_t len0;
+	int i;
+	int r = SC_SUCCESS;
+
+	for (i = 0; i < 2; i++) {
+		if (i == 0)    /* Reset fingerprint */
+			apdustring = del_fingerprint;
+		else           /* Reset creation time */
+			apdustring = del_creationtime;
+		/* Convert the string to binary array */
+		len0 = sizeof(buf);
+		sc_hex_to_bin(apdustring, buf, &len0);
+
+		/* Replace DO tag, subject to key ID */
+		buf[3] = buf[3] + key_id;
+
+		/* Build APDU from binary array */
+		r = sc_bytes2apdu(card->ctx, buf, len0, &apdu);
+		if (r) {
+			sc_log(ctx, "Failed to build APDU");
+			LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL);
+		}
+		apdu.resp = rbuf;
+		apdu.resplen = sizeof(rbuf);
+
+		/* Send APDU to card */
+		r = sc_transmit_apdu(card, &apdu);
+		LOG_TEST_RET(ctx, r, "Transmiting APDU failed");
+	}
+	/* TODO: Rewrite Extended Header List.
+	 * Not support by OpenGPG v2 yet */
+	LOG_FUNC_RETURN(ctx, r);
+}
+
+int delete_key(sc_card_t *card, u8 key_id)
+{
+	sc_context_t *ctx = card->ctx;
+	int r;
+
+	LOG_FUNC_CALLED(ctx);
+	/* Check key ID */
+	if (key_id < 1 || key_id > 3) {
+		sc_log(ctx, "Invalid key ID %d", key_id);
+		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
+	}
+
+	if (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
+		r = delete_key_gnuk(card, key_id);
+	else
+		r = delete_key_openpgp(card, key_id);
+
+	LOG_FUNC_RETURN(ctx, r);
+}
+
+int do_delete_key(sc_card_t *card, u8 key_id)
+{
+	sc_context_t *ctx = card->ctx;
+	int r = SC_SUCCESS;
+
+	/* Currently, only Gnuk supports deleting keys */
+	if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
+		printf("Only Gnuk supports deleting keys. General OpenPGP doesn't.");
+		return SC_ERROR_NOT_SUPPORTED;
+	}
+
+	if (key_id < 1 || (key_id > 3 && key_id != 'a')) {
+		printf("Error: Invalid key id %d", key_id);
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+	if (key_id == 1 || key_id == 'a') {
+		r |= delete_key(card, 1);
+	}
+	if (key_id == 2 || key_id == 'a') {
+		r |= delete_key(card, 2);
+	}
+	if (key_id == 3 || key_id == 'a') {
+		r |= delete_key(card, 3);
+	}
+	return r;
+}
+
 int do_erase(sc_card_t *card)
 {
 	int r;
@@ -539,6 +678,9 @@ int main(int argc, char **argv)
 		exit(EXIT_FAILURE);
 	}
 
+	if (opt_delkey)
+		exit_status != do_delete_key(card, key_id);
+
 	if (opt_erase)
 		exit_status != do_erase(card);
 
-- 
1.9.3

