--- /dev/null
+#
+# Copyright (C) 2011-2014 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=opensc
+PKG_VERSION:=20140317
+PKG_RELEASE:=1
+
+PKG_RELEASE=$(PKG_SOURCE_VERSION)
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL:=https://github.com/OpenSC/OpenSC.git
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_SOURCE_VERSION:=de6d61405b271e22244376e4817e16b49018e1ce
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_BUILD_DEPENDS:=+libpcsclite
+PKG_FIXUP:=libtool
+
+PKG_INSTALL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/libopensc
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=OpenSC libraries for smart cards
+ URL:=https://www.opensc-project.org/opensc/wiki/
+ DEPENDS:=+libopenssl +libpthread
+ MENU:=1
+endef
+
+define Package/libopensc/description
+ OpenSC provides a set of libraries and utilities to work with smart cards.
+ Its main focus is on cards that support cryptographic operations, and
+ facilitate their use in security applications such as authentication,
+ mail encryption and digital signatures.
+endef
+
+define Package/libopensc-pkcs11
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=OpenSC - PKCS11 provider
+ URL:=https://www.opensc-project.org/opensc/wiki/
+ DEPENDS:=libopensc
+endef
+
+define Package/libopensc-pkcs11/description
+ OpenSC PKCS#11 provider
+endef
+
+define Package/libpkcs11-spy
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=PKCS11 spying wrapper
+ URL:=https://www.opensc-project.org/opensc/wiki/
+endef
+
+define Package/libpkcs11-spy/dscription
+ PKCS#11 spying wrapper
+endef
+
+define Package/opensc-utils
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=OpenSC - tools for smart cards
+ URL:=https://www.opensc-project.org/opensc/wiki/
+ DEPENDS:=+libopensc
+ MENU:=1
+endef
+
+define Package/opensc-utils/description
+ OpenSC utilities
+endef
+
+define ToolGen
+define Package/opensc-utils-$(subst _,-,$(firstword $(subst :, ,$(1))))
+ TITLE:=$(firstword $(subst :, ,$(1))) utility from opensc
+ URL:=https://www.opensc-project.org/opensc/wiki/
+ SECTION:=utils
+ CATEGORY:=Utilities
+ DEPENDS:=opensc-utils $(wordlist 2,$(words $(subst :, ,$(1))),$(subst :, ,$(1)))
+endef
+endef
+
+define ProfileGen
+define Package/libopensc-profile-$(subst _,-,$(firstword $(subst :, ,$(1))))
+ TITLE:=$(firstword $(subst :, ,$(1))) card profile for opensc
+ URL:=https://www.opensc-project.org/opensc/wiki/
+ SECTION:=lib
+ CATEGORY:=Libraries
+ DEPENDS:=libopensc
+endef
+endef
+
+TOOLS:= \
+ cardos-tool \
+ cryptoflex-tool \
+ dnie-tool \
+ eidenv \
+ iasecc-tool \
+ netkey-tool \
+ openpgp-tool \
+ opensc-tool \
+ opensc-explorer:+libncurses:+libreadline \
+ piv-tool \
+ pkcs11-tool \
+ pkcs15-crypt \
+ pkcs15-init \
+ pkcs15-tool \
+ sc-hsm-tool \
+ westcos-tool
+
+PROFILES:= \
+ asepcos \
+ authentic \
+ cardos \
+ cyberflex \
+ entersafe \
+ epass2003 \
+ flex \
+ gpk \
+ ias_adele_admin1 \
+ ias_adele_admin2 \
+ ias_adele_common \
+ iasecc_admin_eid \
+ iasecc_generic_oberthur \
+ iasecc_generic_pki \
+ iasecc \
+ incrypto34 \
+ jcop \
+ miocos \
+ muscle \
+ myeid \
+ oberthur \
+ openpgp \
+ pkcs15 \
+ rutoken_ecp \
+ rutoken \
+ sc-hsm \
+ setcos \
+ starcos \
+ westcos
+
+$(foreach file,$(TOOLS),$(eval $(call ToolGen,$(file))))
+$(foreach file,$(PROFILES),$(eval $(call ProfileGen,$(file))))
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libopensc.{a,so}* $(1)/usr/lib/
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libsmm-local.{a,so}* $(1)/usr/lib/
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/opensc-pkcs11.so $(1)/usr/lib/
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/pkcs11-spy.so $(1)/usr/lib/
+ $(INSTALL_DIR) $(1)/usr/lib/pkcs11
+ $(LN) ../pkcs11-spy.so $(1)/usr/lib/pkcs11/
+ $(LN) ../opensc-pkcs11.so $(1)/usr/lib/pkcs11/
+ $(INSTALL_DIR) $(1)/usr/share/opensc
+ $(CP) $(PKG_INSTALL_DIR)/usr/share/opensc/* $(1)/usr/share/opensc/
+endef
+
+define Package/libopensc/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libopensc.so* $(1)/usr/lib/
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libsmm-local.so* $(1)/usr/lib/
+ $(INSTALL_DIR) $(1)/etc
+ $(CP) $(PKG_INSTALL_DIR)/etc/opensc.conf $(1)/etc/
+endef
+
+define Package/libopensc-pkcs11/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/opensc-pkcs11.so $(1)/usr/lib/
+ $(INSTALL_DIR) $(1)/usr/lib/pkcs11
+ $(LN) ../opensc-pkcs11.so $(1)/usr/lib/pkcs11/
+endef
+
+define Package/libpkcs11-spy/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/pkcs11-spy.so $(1)/usr/lib/
+ $(INSTALL_DIR) $(1)/usr/lib/pkcs11
+ $(LN) ../pkcs11-spy.so $(1)/usr/lib/pkcs11/
+endef
+
+define Package/opensc-card-profiles
+ $(INSTALL_DIR) $(1)/usr/share/opensc
+ $(CP) $(PKG_INSTALL_DIR)/usr/share/opensc/* $(1)/usr/share/opensc/
+endef
+
+define Package/opensc-utils/install
+ true
+endef
+
+define ToolInstall
+define Package/opensc-utils-$(subst _,-,$(firstword $(subst :, ,$(1))))/install
+ $(INSTALL_DIR) $$(1)/usr/bin
+ $(INSTALL_BIN) \
+ $(PKG_INSTALL_DIR)/usr/bin/$(firstword $(subst :, ,$(1))) \
+ $$(1)/usr/bin/
+endef
+endef
+
+define ProfileInstall
+define Package/libopensc-profile-$(subst _,-,$(firstword $(subst :, ,$(1))))/install
+ $(INSTALL_DIR) $$(1)/usr/share/opensc
+ $(INSTALL_BIN) \
+ $(PKG_INSTALL_DIR)/usr/share/opensc/$(firstword $(subst :, ,$(1))).profile \
+ $$(1)/usr/share/opensc
+endef
+endef
+
+$(foreach file,$(TOOLS),$(eval $(call ToolInstall,$(file))))
+$(foreach file,$(PROFILES),$(eval $(call ProfileInstall,$(file))))
+
+$(eval $(call BuildPackage,libopensc))
+$(eval $(call BuildPackage,libopensc-pkcs11))
+$(eval $(call BuildPackage,libpkcs11-spy))
+
+$(eval $(call BuildPackage,opensc-utils))
+$(foreach file,$(TOOLS),$(eval $(call BuildPackage,opensc-utils-$(subst _,-,$(firstword $(subst :, ,$(file)))))))
+$(foreach file,$(PROFILES),$(eval $(call BuildPackage,libopensc-profile-$(subst _,-,$(firstword $(subst :, ,$(file)))))))
--- /dev/null
+From c706491fc9b08d4cc6d7b254cf936d6b8d8691bc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Wed, 20 Feb 2013 11:54:30 +0700
+Subject: [PATCH 01/18] OpenPGP: Detect and support Gnuk Token.
+
+http://www.fsij.org/gnuk/
+---
+ src/libopensc/card-openpgp.c | 61 ++++++++++++++++++++++++++++++++++----------
+ src/libopensc/cards.h | 1 +
+ src/tools/openpgp-tool.c | 9 +++++--
+ 3 files changed, 56 insertions(+), 15 deletions(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 743e79c..716052b 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -43,6 +43,7 @@
+ static struct sc_atr_table pgp_atrs[] = {
+ { "3b:fa:13:00:ff:81:31:80:45:00:31:c1:73:c0:01:00:00:90:00:b1", NULL, "OpenPGP card v1.0/1.1", SC_CARD_TYPE_OPENPGP_V1, 0, NULL },
+ { "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:c5:73:c0:01:40:00:90:00:0c", NULL, "CryptoStick v1.2 (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
++ { "3b:da:11:ff:81:b1:fe:55:1f:03:00:31:84:73:80:01:80:00:90:00:e4", NULL, "Gnuk v1.0.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL },
+ { NULL, NULL, NULL, 0, 0, NULL }
+ };
+
+@@ -307,6 +308,8 @@ pgp_init(sc_card_t *card)
+ int r;
+ struct blob *child = NULL;
+
++ LOG_FUNC_CALLED(card->ctx);
++
+ priv = calloc (1, sizeof *priv);
+ if (!priv)
+ return SC_ERROR_OUT_OF_MEMORY;
+@@ -315,11 +318,11 @@ pgp_init(sc_card_t *card)
+ card->cla = 0x00;
+
+ /* set pointer to correct list of card objects */
+- priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2)
++ priv->pgp_objects = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)
+ ? pgp2_objects : pgp1_objects;
+
+ /* set detailed card version */
+- priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2)
++ priv->bcd_version = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)
+ ? OPENPGP_CARD_2_0 : OPENPGP_CARD_1_1;
+
+ /* select application "OpenPGP" */
+@@ -428,7 +431,8 @@ pgp_get_card_features(sc_card_t *card)
+ if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) &&
+ (blob->data != NULL) && (blob->len > 0)) {
+ /* in v2.0 bit 0x04 in first byte means "algorithm attributes changeable */
+- if ((blob->data[0] & 0x04) && (card->type == SC_CARD_TYPE_OPENPGP_V2))
++ if ((blob->data[0] & 0x04) &&
++ (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK))
+ priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE;
+ /* bit 0x08 in first byte means "support for private use DOs" */
+ if (blob->data[0] & 0x08)
+@@ -445,7 +449,8 @@ pgp_get_card_features(sc_card_t *card)
+ priv->ext_caps |= EXT_CAP_GET_CHALLENGE;
+ }
+ /* in v2.0 bit 0x80 in first byte means "support Secure Messaging" */
+- if ((blob->data[0] & 0x80) && (card->type == SC_CARD_TYPE_OPENPGP_V2))
++ if ((blob->data[0] & 0x80) &&
++ (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK))
+ priv->ext_caps |= EXT_CAP_SM;
+
+ if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) {
+@@ -1055,12 +1060,18 @@ static int
+ pgp_get_pubkey(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
+ {
+ sc_apdu_t apdu;
++ u8 apdu_case = SC_APDU_CASE_4;
+ u8 idbuf[2];
+ int r;
+
+ sc_log(card->ctx, "called, tag=%04x\n", tag);
+
+- sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x47, 0x81, 0);
++ /* With Gnuk token, force to use short APDU */
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
++ apdu_case = SC_APDU_CASE_4_SHORT;
++ }
++
++ sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x81, 0);
+ apdu.lc = 2;
+ apdu.data = ushort2bebytes(idbuf, tag);
+ apdu.datalen = 2;
+@@ -1152,6 +1163,7 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
+ 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);
+@@ -1193,13 +1205,17 @@ pgp_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len)
+
+ /* Build APDU */
+ if (buf != NULL && buf_len > 0) {
+- sc_format_apdu(card, &apdu, SC_APDU_CASE_3, ins, p1, p2);
++ /* Force short APDU for Gnuk */
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
++ apdu_case = SC_APDU_CASE_3_SHORT;
++ }
++ sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2);
+
+ /* if card/reader does not support extended APDUs, but chaining, then set it */
+ if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
+ apdu.flags |= SC_APDU_FLAGS_CHAINING;
+
+- apdu.data = buf;
++ apdu.data = (u8 *)buf;
+ apdu.datalen = buf_len;
+ apdu.lc = buf_len;
+ }
+@@ -1326,6 +1342,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data,
+ struct pgp_priv_data *priv = DRVDATA(card);
+ sc_security_env_t *env = &priv->sec_env;
+ sc_apdu_t apdu;
++ u8 apdu_case = SC_APDU_CASE_4;
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+@@ -1334,14 +1351,19 @@ pgp_compute_signature(sc_card_t *card, const u8 *data,
+ LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
+ "invalid operation");
+
++ /* Force short APDU for Gnuk Token */
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
++ apdu_case = SC_APDU_CASE_4_SHORT;
++ }
++
+ switch (env->key_ref[0]) {
+ case 0x00: /* signature key */
+ /* PSO SIGNATURE */
+- sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A);
++ sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x9E, 0x9A);
+ break;
+ case 0x02: /* authentication key */
+ /* INTERNAL AUTHENTICATE */
+- sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x88, 0, 0);
++ sc_format_apdu(card, &apdu, apdu_case, 0x88, 0, 0);
+ break;
+ case 0x01:
+ default:
+@@ -1350,7 +1372,7 @@ pgp_compute_signature(sc_card_t *card, const u8 *data,
+ }
+
+ apdu.lc = data_len;
+- apdu.data = data;
++ apdu.data = (u8 *)data;
+ apdu.datalen = data_len;
+ apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
+ apdu.resp = out;
+@@ -1374,6 +1396,7 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen,
+ struct pgp_priv_data *priv = DRVDATA(card);
+ sc_security_env_t *env = &priv->sec_env;
+ sc_apdu_t apdu;
++ u8 apdu_case = SC_APDU_CASE_4;
+ u8 *temp = NULL;
+ int r;
+
+@@ -1398,7 +1421,7 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen,
+ case 0x01: /* Decryption key */
+ case 0x02: /* authentication key */
+ /* PSO DECIPHER */
+- sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86);
++ sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86);
+ break;
+ case 0x00: /* signature key */
+ default:
+@@ -1407,8 +1430,13 @@ pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen,
+ "invalid key reference");
+ }
+
++ /* Gnuk only supports short APDU, so we need to use command chaining */
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
++ apdu.flags |= SC_APDU_FLAGS_CHAINING;
++ }
++
+ apdu.lc = inlen;
+- apdu.data = in;
++ apdu.data = (u8 *)in;
+ apdu.datalen = inlen;
+ apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
+ apdu.resp = out;
+@@ -1794,6 +1822,11 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
+ }
+
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && key_info->modulus_len != 2048) {
++ sc_log(card->ctx, "Gnuk does not support other key length than 2048.");
++ LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
++ }
++
+ /* Set attributes for new-generated key */
+ r = pgp_update_new_algo_attr(card, key_info);
+ LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key");
+@@ -1801,7 +1834,9 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in
+ /* Test whether we will need extended APDU. 1900 is an
+ * arbitrary modulus length which for sure fits into a short APDU.
+ * This idea is borrowed from GnuPG code. */
+- if (card->caps & SC_CARD_CAP_APDU_EXT && key_info->modulus_len > 1900) {
++ if (card->caps & SC_CARD_CAP_APDU_EXT
++ && key_info->modulus_len > 1900
++ && card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
+ /* We won't store to apdu variable yet, because it will be reset in
+ * sc_format_apdu() */
+ apdu_le = card->max_recv_size;
+diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
+index 0fbf9ca..01b08fd 100644
+--- a/src/libopensc/cards.h
++++ b/src/libopensc/cards.h
+@@ -104,6 +104,7 @@ enum {
+ SC_CARD_TYPE_OPENPGP_BASE = 9000,
+ SC_CARD_TYPE_OPENPGP_V1,
+ SC_CARD_TYPE_OPENPGP_V2,
++ SC_CARD_TYPE_OPENPGP_GNUK,
+
+ /* jcop driver */
+ SC_CARD_TYPE_JCOP_BASE = 10000,
+diff --git a/src/tools/openpgp-tool.c b/src/tools/openpgp-tool.c
+index 7058aaa..8b5e327 100644
+--- a/src/tools/openpgp-tool.c
++++ b/src/tools/openpgp-tool.c
+@@ -32,6 +32,7 @@
+ #include "libopensc/asn1.h"
+ #include "libopensc/cards.h"
+ #include "libopensc/cardctl.h"
++#include "libopensc/log.h"
+ #include "util.h"
+
+ #define OPT_RAW 256
+@@ -216,7 +217,7 @@ static void display_data(const struct ef_name_map *mapping, char *value)
+ } else {
+ const char *label = mapping->name;
+
+- printf("%s:%*s%s\n", label, 10-strlen(label), "", value);
++ printf("%s:%*s%s\n", label, 10 - (int)strlen(label), "", value);
+ }
+ }
+ }
+@@ -390,6 +391,8 @@ int do_genkey(sc_card_t *card, u8 key_id, unsigned int key_len)
+ sc_path_t path;
+ sc_file_t *file;
+
++ LOG_FUNC_CALLED(card->ctx);
++
+ if (key_id < 1 || key_id > 3) {
+ printf("Unknown key ID %d.\n", key_id);
+ return 1;
+@@ -481,8 +484,10 @@ int main(int argc, char **argv)
+
+ /* check card type */
+ if ((card->type != SC_CARD_TYPE_OPENPGP_V1) &&
+- (card->type != SC_CARD_TYPE_OPENPGP_V2)) {
++ (card->type != SC_CARD_TYPE_OPENPGP_V2) &&
++ (card->type != SC_CARD_TYPE_OPENPGP_GNUK)) {
+ util_error("not an OpenPGP card");
++ sc_log(card->ctx, "Card type %X", card->type);
+ exit_status = EXIT_FAILURE;
+ goto out;
+ }
+--
+1.9.3
+
--- /dev/null
+From ecc6460d17147b37def27a9b776e1fc5a61408d0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Fri, 12 Apr 2013 17:24:00 +0700
+Subject: [PATCH 02/18] OpenPGP: Add Gnuk in pkcs15 emulation layer.
+
+---
+ src/libopensc/pkcs15-openpgp.c | 6 ++++--
+ src/libopensc/pkcs15-syn.c | 1 +
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c
+index d9dc074..5a8a1ca 100644
+--- a/src/libopensc/pkcs15-openpgp.c
++++ b/src/libopensc/pkcs15-openpgp.c
+@@ -155,7 +155,8 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card)
+ u8 c4data[10];
+ u8 c5data[70];
+ int r, i;
+- const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2) ? pin_cfg_v2 : pin_cfg_v1;
++ const pgp_pin_cfg_t *pin_cfg = (card->type == SC_CARD_TYPE_OPENPGP_V2 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)
++ ? pin_cfg_v2 : pin_cfg_v1;
+ sc_path_t path;
+ sc_file_t *file;
+
+@@ -367,7 +368,8 @@ failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP e
+
+ static int openpgp_detect_card(sc_pkcs15_card_t *p15card)
+ {
+- if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2)
++ if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2
++ || p15card->card->type == SC_CARD_TYPE_OPENPGP_GNUK)
+ return SC_SUCCESS;
+ else
+ return SC_ERROR_WRONG_CARD;
+diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
+index e2f6004..a9f8c0b 100644
+--- a/src/libopensc/pkcs15-syn.c
++++ b/src/libopensc/pkcs15-syn.c
+@@ -112,6 +112,7 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card)
+ case SC_CARD_TYPE_GEMSAFEV1_PTEID:
+ case SC_CARD_TYPE_OPENPGP_V1:
+ case SC_CARD_TYPE_OPENPGP_V2:
++ case SC_CARD_TYPE_OPENPGP_GNUK:
+ case SC_CARD_TYPE_SC_HSM:
+ return 1;
+ default:
+--
+1.9.3
+
--- /dev/null
+From 5f751ba5628f9d85e9d8dca9939a93f49d2525d0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Fri, 22 Mar 2013 17:37:16 +0700
+Subject: [PATCH 03/18] OpenPGP: Include private DO to filesystem at driver
+ initialization.
+
+In old implementation, the DOs which their access is restricted by
+PIN (like DOs 0101 -> 0104) were excluded from the fake filesystem,
+leading to that we cannot read their data later, even if we verified PIN.
+---
+ src/libopensc/card-openpgp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 716052b..ead07ae 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -357,7 +357,7 @@ pgp_init(sc_card_t *card)
+
+ /* Populate MF - add matching blobs listed in the pgp_objects table. */
+ for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) {
+- if (((info->access & READ_MASK) == READ_ALWAYS) &&
++ if (((info->access & READ_MASK) != READ_NEVER) &&
+ (info->get_fn != NULL)) {
+ child = pgp_new_blob(card, priv->mf, info->id, sc_file_new());
+
+--
+1.9.3
+
--- /dev/null
+From fbf8e392db4456de97796259a62ccb972fe24df8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Tue, 26 Feb 2013 17:37:16 +0700
+Subject: [PATCH 04/18] PKCS15-OpenPGP: Declare DATA objects.
+
+Begin to support read/write DATA object for PKCS-OpenPGP binding.
+This object is used by TrueCrypt.
+---
+ src/libopensc/pkcs15-openpgp.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c
+index 5a8a1ca..9f239ef 100644
+--- a/src/libopensc/pkcs15-openpgp.c
++++ b/src/libopensc/pkcs15-openpgp.c
+@@ -36,6 +36,7 @@ typedef USHORT ushort;
+ #endif
+
+ int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
++static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *);
+
+
+ #define PGP_USER_PIN_FLAGS (SC_PKCS15_PIN_FLAG_CASE_SENSITIVE \
+@@ -45,6 +46,8 @@ int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
+ | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED \
+ | SC_PKCS15_PIN_FLAG_SO_PIN)
+
++#define PGP_NUM_PRIVDO 4
++
+ typedef struct _pgp_pin_cfg {
+ const char *label;
+ int reference;
+@@ -359,6 +362,9 @@ sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card)
+ goto failed;
+ }
+
++ /* PKCS#15 DATA object from OpenPGP private DOs */
++ r = sc_pkcs15emu_openpgp_add_data(p15card);
++
+ return 0;
+
+ failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP emulation: %s\n",
+@@ -366,6 +372,35 @@ failed: sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to initialize OpenPGP e
+ return r;
+ }
+
++static int
++sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card)
++{
++ sc_context_t *ctx = p15card->card->ctx;
++ int i, r;
++
++ LOG_FUNC_CALLED(ctx);
++ /* There is 4 private DO from 0101 to 0104 */
++ for (i = 1; i <= PGP_NUM_PRIVDO; i++) {
++ sc_pkcs15_data_info_t dat_info;
++ sc_pkcs15_object_t dat_obj;
++ char name[8];
++ char path[9];
++ memset(&dat_info, 0, sizeof(dat_info));
++ memset(&dat_obj, 0, sizeof(dat_obj));
++
++ sprintf(name, "PrivDO%d", i);
++ sprintf(path, "3F00010%d", i);
++
++ sc_format_path(path, &dat_info.path);
++ strlcpy(dat_obj.label, name, sizeof(dat_obj.label));
++ strlcpy(dat_info.app_label, name, sizeof(dat_info.app_label));
++
++ sc_log(ctx, "Add %s data object", name);
++ r = sc_pkcs15emu_add_data_object(p15card, &dat_obj, &dat_info);
++ }
++ LOG_FUNC_RETURN(ctx, r);
++}
++
+ static int openpgp_detect_card(sc_pkcs15_card_t *p15card)
+ {
+ if (p15card->card->type == SC_CARD_TYPE_OPENPGP_V1 || p15card->card->type == SC_CARD_TYPE_OPENPGP_V2
+--
+1.9.3
+
--- /dev/null
+From 4cdc5f3102f5ad93d263eea2f8206bb5e9fffc6c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Mon, 4 Mar 2013 11:28:08 +0700
+Subject: [PATCH 05/18] OpenPGP: Support erasing (reset) card.
+
+Command: openpgp-tool --erase
+---
+ src/libopensc/card-openpgp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++
+ src/tools/openpgp-tool.c | 23 +++++++++++++++-
+ 2 files changed, 86 insertions(+), 1 deletion(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index ead07ae..42a9684 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -2197,6 +2197,66 @@ out:
+
+ #endif /* ENABLE_OPENSSL */
+
++/**
++ * Erase card
++ **/
++static int pgp_erase_card(sc_card_t *card)
++{
++ sc_context_t *ctx = card->ctx;
++ u8 *apdustring[10] = {
++ "00:20:00:81:08:40:40:40:40:40:40:40:40",
++ "00:20:00:81:08:40:40:40:40:40:40:40:40",
++ "00:20:00:81:08:40:40:40:40:40:40:40:40",
++ "00:20:00:81:08:40:40:40:40:40:40:40:40",
++ "00:20:00:83:08:40:40:40:40:40:40:40:40",
++ "00:20:00:83:08:40:40:40:40:40:40:40:40",
++ "00:20:00:83:08:40:40:40:40:40:40:40:40",
++ "00:20:00:83:08:40:40:40:40:40:40:40:40",
++ "00:e6:00:00",
++ "00:44:00:00"
++ };
++ u8 buf[SC_MAX_APDU_BUFFER_SIZE];
++ u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
++ sc_apdu_t apdu;
++ size_t len0;
++ int commandsnum = 10;
++ int i, r;
++
++ LOG_FUNC_CALLED(ctx);
++
++ /* Check card version */
++ if (card->type != SC_CARD_TYPE_OPENPGP_V2) {
++ sc_log(ctx, "Card is not OpenPGP v2");
++ LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT);
++ }
++ sc_log(ctx, "Card is OpenPGP v2. Erase card.");
++
++ /* Iterate over 10 commands above */
++ for (i = 0; i < commandsnum; i++) {
++ /* Convert the string to binary array */
++ len0 = sizeof(buf);
++ sc_hex_to_bin(apdustring[i], buf, &len0);
++ printf("Sending: ");
++ for (r = 0; r < len0; r++)
++ printf("%02X ", buf[r]);
++ printf("\n");
++
++ /* 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");
++ }
++ LOG_FUNC_RETURN(ctx, r);
++}
++
+ /* ABI: card ctl: perform special card-specific operations */
+ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+ {
+@@ -2221,6 +2281,10 @@ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+ LOG_FUNC_RETURN(card->ctx, r);
+ break;
+ #endif /* ENABLE_OPENSSL */
++ case SC_CARDCTL_ERASE_CARD:
++ r = pgp_erase_card(card);
++ LOG_FUNC_RETURN(card->ctx, r);
++ break;
+ }
+
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+diff --git a/src/tools/openpgp-tool.c b/src/tools/openpgp-tool.c
+index 8b5e327..0d360a3 100644
+--- a/src/tools/openpgp-tool.c
++++ b/src/tools/openpgp-tool.c
+@@ -76,6 +76,7 @@ static int opt_verify = 0;
+ static char *verifytype = NULL;
+ static int opt_pin = 0;
+ static char *pin = NULL;
++static int opt_erase = 0;
+
+ static const char *app_name = "openpgp-tool";
+
+@@ -92,6 +93,7 @@ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
++ { "erase", no_argument, NULL, 'E' },
+ { "verify", required_argument, NULL, OPT_VERIFY },
+ { "pin", required_argument, NULL, OPT_PIN },
+ { NULL, 0, NULL, 0 }
+@@ -110,6 +112,7 @@ static const char *option_help[] = {
+ /* h */ "Print this help message",
+ /* v */ "Verbose operation. Use several times to enable debug output.",
+ /* V */ "Show version number",
++/* E */ "Erase (reset) the card",
+ "Verify PIN (CHV1, CHV2, CHV3...)",
+ "PIN string"
+ };
+@@ -228,7 +231,7 @@ static int decode_options(int argc, char **argv)
+ {
+ int c;
+
+- while ((c = getopt_long(argc, argv,"r:x:CUG:L:hwvV", options, (int *) 0)) != EOF) {
++ while ((c = getopt_long(argc, argv,"r:x:CUG:L:hwvVE", options, (int *) 0)) != EOF) {
+ switch (c) {
+ case 'r':
+ opt_reader = optarg;
+@@ -288,6 +291,9 @@ static int decode_options(int argc, char **argv)
+ show_version();
+ exit(EXIT_SUCCESS);
+ break;
++ case 'E':
++ opt_erase++;
++ break;
+ default:
+ util_print_usage_and_die(app_name, options, option_help, NULL);
+ }
+@@ -446,6 +452,18 @@ int do_verify(sc_card_t *card, u8 *type, u8* pin)
+ return r;
+ }
+
++int do_erase(sc_card_t *card)
++{
++ int r;
++ /* Check card version */
++ if (card->type != SC_CARD_TYPE_OPENPGP_V2) {
++ printf("Do not erase card which is not OpenPGP v2\n");
++ }
++ printf("Erase card\n");
++ r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL);
++ return r;
++}
++
+ int main(int argc, char **argv)
+ {
+ sc_context_t *ctx = NULL;
+@@ -521,6 +539,9 @@ int main(int argc, char **argv)
+ exit(EXIT_FAILURE);
+ }
+
++ if (opt_erase)
++ exit_status != do_erase(card);
++
+ out:
+ sc_unlock(card);
+ sc_disconnect_card(card);
+--
+1.9.3
+
--- /dev/null
+From bbbedd3b358f80a7f98df2b22cf541cb007dd62e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+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
+
--- /dev/null
+From b6bc7a497e1fe20104f923de1092a35d137ba553 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Mon, 4 Mar 2013 18:14:51 +0700
+Subject: [PATCH 07/18] OpenPGP: Correct building Extended Header List when
+ importing keys.
+
+---
+ src/libopensc/card-openpgp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 42a9684..47c1938 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -1978,7 +1978,7 @@ pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info
+ u8 *p = NULL;
+ u8 *components[] = {key_info->e, key_info->p, key_info->q, key_info->n};
+ size_t componentlens[] = {key_info->e_len, key_info->p_len, key_info->q_len, key_info->n_len};
+- unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x95};
++ unsigned int componenttags[] = {0x91, 0x92, 0x93, 0x97};
+ char *componentnames[] = {
+ "public exponent",
+ "prime p",
+--
+1.9.3
+
--- /dev/null
+From d1b8d3588336abac4876c1d537d8e8e5e578bc02 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Mon, 25 Mar 2013 11:58:38 +0700
+Subject: [PATCH 08/18] OpenPGP: Read some empty DOs from Gnuk.
+
+In Gnuk, some empty DOs are returned as not exist, instead of existing with empty value.
+So, we will consider them exist in driver.
+---
+ src/libopensc/card-openpgp.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 47c1938..9b08bbb 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -813,6 +813,23 @@ pgp_get_blob(sc_card_t *card, struct blob *blob, unsigned int id,
+ }
+ }
+
++ /* This part is for "NOT FOUND" cases */
++
++ /* Special case:
++ * Gnuk does not have default value for children of DO 65 (DOs 5B, 5F2D, 5F35)
++ * So, if these blob was not found, we create it. */
++ if (blob->id == 0x65 && (id == 0x5B || id == 0x5F2D || id == 0x5F35)) {
++ sc_log(card->ctx, "Create blob %X under %X", id, blob->id);
++ child = pgp_new_blob(card, blob, id, sc_file_new());
++ if (child) {
++ pgp_set_blob(child, NULL, 0);
++ *ret = child;
++ return SC_SUCCESS;
++ }
++ else
++ sc_log(card->ctx, "Not enough memory to create blob for DO %X");
++ }
++
+ return SC_ERROR_FILE_NOT_FOUND;
+ }
+
+@@ -1147,6 +1164,14 @@ pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
++
++ /* For Gnuk card, if there is no certificate, it returns error instead of empty data.
++ * So, for this case, we ignore error and consider success */
++ if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND && card->type == SC_CARD_TYPE_OPENPGP_GNUK
++ && (tag == DO_CERT || tag == 0x0101 || tag == 0x0102 || tag == 0x0103 || tag == 0x0104)) {
++ r = SC_SUCCESS;
++ apdu.resplen = 0;
++ }
+ LOG_TEST_RET(card->ctx, r, "Card returned error");
+
+ LOG_FUNC_RETURN(card->ctx, apdu.resplen);
+--
+1.9.3
+
--- /dev/null
+From 6a4457cde65ef44f05b0689415ae7165b06fb8bf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Wed, 27 Mar 2013 11:38:42 +0700
+Subject: [PATCH 09/18] PKCS15-OpenPGP: Do not show empty DO in pkcs15
+ emu_init.
+
+---
+ src/libopensc/pkcs15-openpgp.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c
+index 9f239ef..850dd74 100644
+--- a/src/libopensc/pkcs15-openpgp.c
++++ b/src/libopensc/pkcs15-openpgp.c
+@@ -385,16 +385,34 @@ sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card)
+ sc_pkcs15_object_t dat_obj;
+ char name[8];
+ char path[9];
++ u8 content[254];
+ memset(&dat_info, 0, sizeof(dat_info));
+ memset(&dat_obj, 0, sizeof(dat_obj));
+
+ sprintf(name, "PrivDO%d", i);
+ sprintf(path, "3F00010%d", i);
+
++ /* Check if the DO can be read.
++ * We won't expose pkcs15 DATA object if DO is empty.
++ */
++ r = read_file(p15card->card, path, content, sizeof(content));
++ if (r <= 0 ) {
++ sc_log(ctx, "Cannot read DO 010%d or there is no data in it", i);
++ /* Skip */
++ continue;
++ }
+ sc_format_path(path, &dat_info.path);
+ strlcpy(dat_obj.label, name, sizeof(dat_obj.label));
+ strlcpy(dat_info.app_label, name, sizeof(dat_info.app_label));
+
++ /* Add DATA object to slot protected by PIN2 (PW1 with Ref 0x82) */
++ dat_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | SC_PKCS15_CO_FLAG_MODIFIABLE;
++ dat_obj.auth_id.len = 1;
++ if (i == 1 || i == 3)
++ dat_obj.auth_id.value[0] = 2;
++ else
++ dat_obj.auth_id.value[0] = 3;
++
+ sc_log(ctx, "Add %s data object", name);
+ r = sc_pkcs15emu_add_data_object(p15card, &dat_obj, &dat_info);
+ }
+--
+1.9.3
+
--- /dev/null
+From 88ded8fc5802c073caa71b649cee5a3116699b2a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Wed, 27 Mar 2013 11:39:33 +0700
+Subject: [PATCH 10/18] PKCS15-OpenPGP: Allow to store data to pkcs15 data
+ object.
+
+Only one DO is supported now.
+---
+ src/libopensc/pkcs15-openpgp.c | 2 +-
+ src/pkcs15init/pkcs15-openpgp.c | 38 +++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 38 insertions(+), 2 deletions(-)
+
+diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c
+index 850dd74..b701041 100644
+--- a/src/libopensc/pkcs15-openpgp.c
++++ b/src/libopensc/pkcs15-openpgp.c
+@@ -397,7 +397,7 @@ sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *p15card)
+ */
+ r = read_file(p15card->card, path, content, sizeof(content));
+ if (r <= 0 ) {
+- sc_log(ctx, "Cannot read DO 010%d or there is no data in it", i);
++ sc_log(ctx, "No data get from DO 010%d", i);
+ /* Skip */
+ continue;
+ }
+diff --git a/src/pkcs15init/pkcs15-openpgp.c b/src/pkcs15init/pkcs15-openpgp.c
+index f3a4962..1455580 100755
+--- a/src/pkcs15init/pkcs15-openpgp.c
++++ b/src/pkcs15init/pkcs15-openpgp.c
+@@ -236,13 +236,16 @@ static int openpgp_emu_update_tokeninfo(sc_profile_t *profile, sc_pkcs15_card_t
+ }
+
+ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile,
+- struct sc_pkcs15_object *obj, struct sc_pkcs15_der *content,
++ struct sc_pkcs15_object *obj, struct sc_pkcs15_der *content,
+ struct sc_path *path)
+ {
+ sc_card_t *card = p15card->card;
++ sc_context_t *ctx = card->ctx;
+ sc_file_t *file;
+ sc_pkcs15_cert_info_t *cinfo;
+ sc_pkcs15_id_t *cid;
++ sc_pkcs15_data_info_t *dinfo;
++ u8 buf[254];
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+@@ -282,6 +285,39 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile
+ content->len, 0);
+ break;
+
++ case SC_PKCS15_TYPE_DATA_OBJECT:
++ dinfo = (sc_pkcs15_data_info_t *) obj->data;
++ /* dinfo->app_label contains filename */
++ sc_log(ctx, "===== App label %s", dinfo->app_label);
++ /* Currently, we only support DO 0101. The reason is that when initializing this
++ * pkcs15 emulation, PIN authentication is not applied and we can expose only this DO,
++ * which is "read always".
++ * If we support other DOs, they will not be exposed, and not helpful to user.
++ * I haven't found a way to refresh the list of exposed DOs after verifying PIN yet.
++ * http://sourceforge.net/mailarchive/message.php?msg_id=30646373
++ **/
++ sc_log(ctx, "About to write to DO 0101");
++ sc_format_path("0101", path);
++ r = sc_select_file(card, path, &file);
++ LOG_TEST_RET(card->ctx, r, "Cannot select private DO");
++ r = sc_read_binary(card, 0, buf, sizeof(buf), 0);
++ if (r < 0) {
++ sc_log(ctx, "Cannot read DO 0101");
++ break;
++ }
++ if (r > 0) {
++ sc_log(ctx, "DO 0101 is full.");
++ r = SC_ERROR_TOO_MANY_OBJECTS;
++ break;
++ }
++ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE);
++ if (r >= 0 && content->len) {
++ r = sc_update_binary(p15card->card, 0,
++ (const unsigned char *) content->value,
++ content->len, 0);
++ }
++ break;
++
+ default:
+ r = SC_ERROR_NOT_IMPLEMENTED;
+ }
+--
+1.9.3
+
--- /dev/null
+From 7231ee09bb628f0401939778decce818ef6e3665 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Fri, 5 Apr 2013 17:18:50 +0700
+Subject: [PATCH 11/18] OpenPGP: Provide enough buffer to read pubkey from
+ Gnuk.
+
+---
+ src/libopensc/card-openpgp.c | 28 +++++++++++++++++++++++-----
+ 1 file changed, 23 insertions(+), 5 deletions(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 9b08bbb..8a1a270 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -263,7 +263,12 @@ static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */
+
+ /* The DO holding X.509 certificate is constructed but does not contain child DO.
+ * We should notice this when building fake file system later. */
+-#define DO_CERT 0x7f21
++#define DO_CERT 0x7f21
++/* Maximum length for response buffer when reading pubkey. This value is calculated with
++ * 4096-bit key length */
++#define MAXLEN_RESP_PUBKEY 527
++/* Gnuk only support 1 key length (2048 bit) */
++#define MAXLEN_RESP_PUBKEY_GNUK 271
+
+ #define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data))
+ struct pgp_priv_data {
+@@ -729,6 +734,14 @@ pgp_read_blob(sc_card_t *card, struct blob *blob)
+ u8 buffer[2048];
+ size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT)
+ ? sizeof(buffer) : 256;
++
++ /* Buffer length for Gnuk pubkey */
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
++ (blob->id == 0xa400 || blob->id == 0xb600 || blob->id == 0xb800
++ || blob->id == 0xa401 || blob->id == 0xb601 || blob->id == 0xb801)) {
++ buf_len = MAXLEN_RESP_PUBKEY_GNUK;
++ }
++
+ int r = blob->info->get_fn(card, blob->id, buffer, buf_len);
+
+ if (r < 0) { /* an error occurred */
+@@ -1830,6 +1843,7 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in
+ u8 apdu_case;
+ u8 *apdu_data;
+ size_t apdu_le;
++ size_t resplen = 0;
+ int r = SC_SUCCESS;
+
+ LOG_FUNC_CALLED(card->ctx);
+@@ -1868,23 +1882,27 @@ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_in
+ apdu_case = SC_APDU_CASE_4_EXT;
+ }
+ else {
+- apdu_le = 256;
+ apdu_case = SC_APDU_CASE_4_SHORT;
++ apdu_le = 256;
++ resplen = MAXLEN_RESP_PUBKEY;
++ }
++ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
++ resplen = MAXLEN_RESP_PUBKEY_GNUK;
+ }
+
+ /* Prepare APDU */
+- sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0);
++ sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0);
+ apdu.data = apdu_data;
+ apdu.datalen = 2; /* Data = B600 */
+ apdu.lc = 2;
+ apdu.le = apdu_le;
+
+ /* Buffer to receive response */
+- apdu.resp = calloc(apdu.le, 1);
++ apdu.resplen = (resplen > 0) ? resplen : apdu_le;
++ apdu.resp = calloc(apdu.resplen, 1);
+ if (apdu.resp == NULL) {
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
+ }
+- apdu.resplen = apdu.le;
+
+ /* Send */
+ sc_log(card->ctx, "Waiting for the card to generate key...");
+--
+1.9.3
+
--- /dev/null
+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
+
--- /dev/null
+From e5c94d3f1f7e6a96a98815d6e51190498c357fb6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Wed, 10 Apr 2013 18:35:58 +0700
+Subject: [PATCH 13/18] pkcs15-openpgp: Change to sc_put_data instead of
+ sc_update_binary when writing certificate.
+
+---
+ src/pkcs15init/pkcs15-openpgp.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/src/pkcs15init/pkcs15-openpgp.c b/src/pkcs15init/pkcs15-openpgp.c
+index 1455580..be1291e 100755
+--- a/src/pkcs15init/pkcs15-openpgp.c
++++ b/src/pkcs15init/pkcs15-openpgp.c
+@@ -279,10 +279,9 @@ static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile
+ r = sc_select_file(card, path, &file);
+ LOG_TEST_RET(card->ctx, r, "Cannot select cert file");
+ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE);
++ sc_log(card->ctx, "Data to write is %d long", content->len);
+ if (r >= 0 && content->len)
+- r = sc_update_binary(p15card->card, 0,
+- (const unsigned char *) content->value,
+- content->len, 0);
++ r = sc_put_data(p15card->card, 0x7F21, (const unsigned char *) content->value, content->len);
+ break;
+
+ case SC_PKCS15_TYPE_DATA_OBJECT:
+--
+1.9.3
+
--- /dev/null
+From df8a78e3c8c9d9d591c0d3fa31db7e010eb2c8c2 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 16:18:31 +0700
+Subject: [PATCH 14/18] OpenPGP: Overcome the restriction of even data length
+ of Gnuk.
+
+When write certificate with odd length to Gnuk, we add zero padding to make it even.
+---
+ src/libopensc/card-openpgp.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index d9db948..a666163 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -1206,6 +1206,10 @@ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
+ sc_apdu_t apdu;
+ u8 *part;
+ size_t plen;
++ /* Two round_ variables below are to build APDU data
++ * with even length for Gnuk */
++ u8 roundbuf[256];
++ size_t roundlen = 0;
+ int r = SC_SUCCESS;
+
+ LOG_FUNC_CALLED(ctx);
+@@ -1236,8 +1240,20 @@ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
+ 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;
++
++ /* If the last part has odd length, we add zero padding to make it even.
++ * Gnuk does not allow data with odd length */
++ if (plen < 256 && (plen % 2) != 0) {
++ roundlen = plen + 1;
++ memset(roundbuf, 0, roundlen);
++ memcpy(roundbuf, part, plen);
++ apdu.data = roundbuf;
++ apdu.datalen = apdu.lc = roundlen;
++ }
++ else {
++ apdu.data = part;
++ apdu.datalen = apdu.lc = plen;
++ }
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+--
+1.9.3
+
--- /dev/null
+From 693b3ac5a53e89a0cdeab0f728d24a6e16864f5c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Fri, 12 Apr 2013 15:33:31 +0700
+Subject: [PATCH 15/18] OpenPGP: Delete key as file, for Gnuk.
+
+---
+ src/libopensc/card-openpgp.c | 51 +++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 50 insertions(+), 1 deletion(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index a666163..19d3b04 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -2437,6 +2437,44 @@ static int pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+ }
+
++
++/* Internal: Delete key */
++static int
++gnuk_delete_key(sc_card_t *card, u8 key_id)
++{
++ sc_context_t *ctx = card->ctx;
++ int r = SC_SUCCESS;
++ u8 *data = NULL;
++
++ LOG_FUNC_CALLED(ctx);
++
++ /* Delete fingerprint */
++ sc_log(ctx, "Delete fingerprints");
++ r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
++ LOG_TEST_RET(ctx, r, "Failed to delete fingerprints");
++ /* Delete creation time */
++ sc_log(ctx, "Delete creation time");
++ r = pgp_put_data(card, 0xCD + key_id, NULL, 0);
++ LOG_TEST_RET(ctx, r, "Failed to delete creation time");
++
++ /* 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
++ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
++
++ r = pgp_put_data(card, 0x4D, data, strlen(data) + 1);
++
++ LOG_FUNC_RETURN(ctx, r);
++}
++
++
+ /* ABI: DELETE FILE */
+ static int
+ pgp_delete_file(sc_card_t *card, const sc_path_t *path)
+@@ -2444,6 +2482,7 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path)
+ struct pgp_priv_data *priv = DRVDATA(card);
+ struct blob *blob;
+ sc_file_t *file;
++ u8 key_id;
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+@@ -2459,10 +2498,20 @@ pgp_delete_file(sc_card_t *card, const sc_path_t *path)
+ if (blob == priv->mf)
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+
+- if (file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401) {
++ if (card->type != SC_CARD_TYPE_OPENPGP_GNUK &&
++ (file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401)) {
+ /* These tags are just symbolic. We don't really delete it. */
+ r = SC_SUCCESS;
+ }
++ else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xB601) {
++ r = gnuk_delete_key(card, 1);
++ }
++ else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xB801) {
++ r = gnuk_delete_key(card, 2);
++ }
++ else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xA401) {
++ r = gnuk_delete_key(card, 3);
++ }
+ else {
+ /* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */
+ r = pgp_put_data(card, file->id, NULL, 0);
+--
+1.9.3
+
--- /dev/null
+From f96f7536a8c2efd0ba41fd94fe3334e5fa556854 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Tue, 16 Apr 2013 10:19:34 +0700
+Subject: [PATCH 16/18] OpenPGP: Correct parameter checking.
+
+---
+ src/libopensc/card-openpgp.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 19d3b04..196c094 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -1221,6 +1221,8 @@ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+ /* Check response */
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
++ if (r < 0)
++ LOG_FUNC_RETURN(card->ctx, r);
+ LOG_FUNC_RETURN(card->ctx, length);
+ }
+
+@@ -2448,6 +2450,11 @@ gnuk_delete_key(sc_card_t *card, u8 key_id)
+
+ LOG_FUNC_CALLED(ctx);
+
++ if (key_id < 1 || key_id > 3) {
++ sc_log(ctx, "Key ID %d is invalid. Should be 1, 2 or 3.", key_id);
++ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
++ }
++
+ /* Delete fingerprint */
+ sc_log(ctx, "Delete fingerprints");
+ r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
+@@ -2466,8 +2473,6 @@ gnuk_delete_key(sc_card_t *card, u8 key_id)
+ data = "\x4D\x02\xB8";
+ else if (key_id == 3)
+ data = "\x4D\x02\xA4";
+- else
+- LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
+
+ r = pgp_put_data(card, 0x4D, data, strlen(data) + 1);
+
+--
+1.9.3
+
--- /dev/null
+From 8a69525a60391b46db4994033527d219d2adaa4e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Tue, 16 Apr 2013 16:02:17 +0700
+Subject: [PATCH 17/18] OpenPGP: Make code neater
+
+---
+ src/libopensc/card-openpgp.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index 196c094..c4ef3b6 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -1220,10 +1220,7 @@ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
+ 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);
+- if (r < 0)
+- LOG_FUNC_RETURN(card->ctx, r);
+- LOG_FUNC_RETURN(card->ctx, length);
++ LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "Certificate writing failed");
+ }
+
+ /* Ref: gnuk_put_binary_libusb.py and gnuk_token.py in Gnuk source tree */
+@@ -1260,8 +1257,7 @@ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
+ 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");
++ LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "UPDATE BINARY returned error");
+
+ /* To next part */
+ i++;
+--
+1.9.3
+
--- /dev/null
+From a099f951d085d3abfefeead14a4af06913cb67d2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?=
+Date: Wed, 8 May 2013 16:51:21 +0700
+Subject: [PATCH 18/18] Move declaration to top of block.
+
+---
+ src/libopensc/card-openpgp.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/libopensc/card-openpgp.c b/src/libopensc/card-openpgp.c
+index c4ef3b6..7f2006e 100644
+--- a/src/libopensc/card-openpgp.c
++++ b/src/libopensc/card-openpgp.c
+@@ -736,6 +736,7 @@ pgp_read_blob(sc_card_t *card, struct blob *blob)
+ u8 buffer[2048];
+ size_t buf_len = (card->caps & SC_CARD_CAP_APDU_EXT)
+ ? sizeof(buffer) : 256;
++ int r = SC_SUCCESS;
+
+ /* Buffer length for certificate */
+ if (blob->id == DO_CERT && priv->max_cert_size > 0) {
+@@ -749,7 +750,7 @@ pgp_read_blob(sc_card_t *card, struct blob *blob)
+ buf_len = MAXLEN_RESP_PUBKEY_GNUK;
+ }
+
+- int r = blob->info->get_fn(card, blob->id, buffer, buf_len);
++ r = blob->info->get_fn(card, blob->id, buffer, buf_len);
+
+ if (r < 0) { /* an error occurred */
+ blob->status = r;
+--
+1.9.3
+