From 54a8a2a6b9f1df849bf275a4d55003d412cc30c4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 16 Jun 2014 14:16:59 +0200 Subject: [PATCH] add OpenSC package OpenSC is a smart card middleware. Patches for support of the GnuK USB token have been added. Signed-off-by: Daniel Golle --- utils/opensc/Makefile | 224 +++++++++++++++ ...penPGP-Detect-and-support-Gnuk-Token.patch | 267 ++++++++++++++++++ ...P-Add-Gnuk-in-pkcs15-emulation-layer.patch | 50 ++++ ...private-DO-to-filesystem-at-driver-i.patch | 30 ++ ...-PKCS15-OpenPGP-Declare-DATA-objects.patch | 82 ++++++ ...5-OpenPGP-Support-erasing-reset-card.patch | 173 ++++++++++++ ...gp-tool-Support-deleting-key-in-Gnuk.patch | 210 ++++++++++++++ ...building-Extended-Header-List-when-i.patch | 27 ++ ...penPGP-Read-some-empty-DOs-from-Gnuk.patch | 58 ++++ ...o-not-show-empty-DO-in-pkcs15-emu_in.patch | 53 ++++ ...llow-to-store-data-to-pkcs15-data-ob.patch | 91 ++++++ ...enough-buffer-to-read-pubkey-from-Gn.patch | 87 ++++++ ...P-Support-write-certificate-for-Gnuk.patch | 220 +++++++++++++++ ...hange-to-sc_put_data-instead-of-sc_u.patch | 31 ++ ...-the-restriction-of-even-data-length.patch | 53 ++++ ...-OpenPGP-Delete-key-as-file-for-Gnuk.patch | 92 ++++++ ...6-OpenPGP-Correct-parameter-checking.patch | 47 +++ .../0017-OpenPGP-Make-code-neater.patch | 39 +++ ...018-Move-declaration-to-top-of-block.patch | 34 +++ 19 files changed, 1868 insertions(+) create mode 100644 utils/opensc/Makefile create mode 100644 utils/opensc/patches/0001-OpenPGP-Detect-and-support-Gnuk-Token.patch create mode 100644 utils/opensc/patches/0002-OpenPGP-Add-Gnuk-in-pkcs15-emulation-layer.patch create mode 100644 utils/opensc/patches/0003-OpenPGP-Include-private-DO-to-filesystem-at-driver-i.patch create mode 100644 utils/opensc/patches/0004-PKCS15-OpenPGP-Declare-DATA-objects.patch create mode 100644 utils/opensc/patches/0005-OpenPGP-Support-erasing-reset-card.patch create mode 100644 utils/opensc/patches/0006-openpgp-tool-Support-deleting-key-in-Gnuk.patch create mode 100644 utils/opensc/patches/0007-OpenPGP-Correct-building-Extended-Header-List-when-i.patch create mode 100644 utils/opensc/patches/0008-OpenPGP-Read-some-empty-DOs-from-Gnuk.patch create mode 100644 utils/opensc/patches/0009-PKCS15-OpenPGP-Do-not-show-empty-DO-in-pkcs15-emu_in.patch create mode 100644 utils/opensc/patches/0010-PKCS15-OpenPGP-Allow-to-store-data-to-pkcs15-data-ob.patch create mode 100644 utils/opensc/patches/0011-OpenPGP-Provide-enough-buffer-to-read-pubkey-from-Gn.patch create mode 100644 utils/opensc/patches/0012-OpenPGP-Support-write-certificate-for-Gnuk.patch create mode 100644 utils/opensc/patches/0013-pkcs15-openpgp-Change-to-sc_put_data-instead-of-sc_u.patch create mode 100644 utils/opensc/patches/0014-OpenPGP-Overcome-the-restriction-of-even-data-length.patch create mode 100644 utils/opensc/patches/0015-OpenPGP-Delete-key-as-file-for-Gnuk.patch create mode 100644 utils/opensc/patches/0016-OpenPGP-Correct-parameter-checking.patch create mode 100644 utils/opensc/patches/0017-OpenPGP-Make-code-neater.patch create mode 100644 utils/opensc/patches/0018-Move-declaration-to-top-of-block.patch diff --git a/utils/opensc/Makefile b/utils/opensc/Makefile new file mode 100644 index 0000000000..b004a9196c --- /dev/null +++ b/utils/opensc/Makefile @@ -0,0 +1,224 @@ +# +# 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_MAINTAINER:=Daniel Golle + +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))))))) diff --git a/utils/opensc/patches/0001-OpenPGP-Detect-and-support-Gnuk-Token.patch b/utils/opensc/patches/0001-OpenPGP-Detect-and-support-Gnuk-Token.patch new file mode 100644 index 0000000000..0d79422ca0 --- /dev/null +++ b/utils/opensc/patches/0001-OpenPGP-Detect-and-support-Gnuk-Token.patch @@ -0,0 +1,267 @@ +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 + diff --git a/utils/opensc/patches/0002-OpenPGP-Add-Gnuk-in-pkcs15-emulation-layer.patch b/utils/opensc/patches/0002-OpenPGP-Add-Gnuk-in-pkcs15-emulation-layer.patch new file mode 100644 index 0000000000..cf8cae6d73 --- /dev/null +++ b/utils/opensc/patches/0002-OpenPGP-Add-Gnuk-in-pkcs15-emulation-layer.patch @@ -0,0 +1,50 @@ +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 + diff --git a/utils/opensc/patches/0003-OpenPGP-Include-private-DO-to-filesystem-at-driver-i.patch b/utils/opensc/patches/0003-OpenPGP-Include-private-DO-to-filesystem-at-driver-i.patch new file mode 100644 index 0000000000..fa15d792c5 --- /dev/null +++ b/utils/opensc/patches/0003-OpenPGP-Include-private-DO-to-filesystem-at-driver-i.patch @@ -0,0 +1,30 @@ +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 + diff --git a/utils/opensc/patches/0004-PKCS15-OpenPGP-Declare-DATA-objects.patch b/utils/opensc/patches/0004-PKCS15-OpenPGP-Declare-DATA-objects.patch new file mode 100644 index 0000000000..114412f229 --- /dev/null +++ b/utils/opensc/patches/0004-PKCS15-OpenPGP-Declare-DATA-objects.patch @@ -0,0 +1,82 @@ +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 + diff --git a/utils/opensc/patches/0005-OpenPGP-Support-erasing-reset-card.patch b/utils/opensc/patches/0005-OpenPGP-Support-erasing-reset-card.patch new file mode 100644 index 0000000000..c28ed10cbf --- /dev/null +++ b/utils/opensc/patches/0005-OpenPGP-Support-erasing-reset-card.patch @@ -0,0 +1,173 @@ +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 + diff --git a/utils/opensc/patches/0006-openpgp-tool-Support-deleting-key-in-Gnuk.patch b/utils/opensc/patches/0006-openpgp-tool-Support-deleting-key-in-Gnuk.patch new file mode 100644 index 0000000000..f73cb22f5c --- /dev/null +++ b/utils/opensc/patches/0006-openpgp-tool-Support-deleting-key-in-Gnuk.patch @@ -0,0 +1,210 @@ +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 + diff --git a/utils/opensc/patches/0007-OpenPGP-Correct-building-Extended-Header-List-when-i.patch b/utils/opensc/patches/0007-OpenPGP-Correct-building-Extended-Header-List-when-i.patch new file mode 100644 index 0000000000..1d487af32d --- /dev/null +++ b/utils/opensc/patches/0007-OpenPGP-Correct-building-Extended-Header-List-when-i.patch @@ -0,0 +1,27 @@ +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 + diff --git a/utils/opensc/patches/0008-OpenPGP-Read-some-empty-DOs-from-Gnuk.patch b/utils/opensc/patches/0008-OpenPGP-Read-some-empty-DOs-from-Gnuk.patch new file mode 100644 index 0000000000..25a69d4cb6 --- /dev/null +++ b/utils/opensc/patches/0008-OpenPGP-Read-some-empty-DOs-from-Gnuk.patch @@ -0,0 +1,58 @@ +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 + diff --git a/utils/opensc/patches/0009-PKCS15-OpenPGP-Do-not-show-empty-DO-in-pkcs15-emu_in.patch b/utils/opensc/patches/0009-PKCS15-OpenPGP-Do-not-show-empty-DO-in-pkcs15-emu_in.patch new file mode 100644 index 0000000000..5abf6f8db2 --- /dev/null +++ b/utils/opensc/patches/0009-PKCS15-OpenPGP-Do-not-show-empty-DO-in-pkcs15-emu_in.patch @@ -0,0 +1,53 @@ +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 + diff --git a/utils/opensc/patches/0010-PKCS15-OpenPGP-Allow-to-store-data-to-pkcs15-data-ob.patch b/utils/opensc/patches/0010-PKCS15-OpenPGP-Allow-to-store-data-to-pkcs15-data-ob.patch new file mode 100644 index 0000000000..a3c75309c9 --- /dev/null +++ b/utils/opensc/patches/0010-PKCS15-OpenPGP-Allow-to-store-data-to-pkcs15-data-ob.patch @@ -0,0 +1,91 @@ +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 + diff --git a/utils/opensc/patches/0011-OpenPGP-Provide-enough-buffer-to-read-pubkey-from-Gn.patch b/utils/opensc/patches/0011-OpenPGP-Provide-enough-buffer-to-read-pubkey-from-Gn.patch new file mode 100644 index 0000000000..8fc34642d0 --- /dev/null +++ b/utils/opensc/patches/0011-OpenPGP-Provide-enough-buffer-to-read-pubkey-from-Gn.patch @@ -0,0 +1,87 @@ +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 + diff --git a/utils/opensc/patches/0012-OpenPGP-Support-write-certificate-for-Gnuk.patch b/utils/opensc/patches/0012-OpenPGP-Support-write-certificate-for-Gnuk.patch new file mode 100644 index 0000000000..0d54d96fc3 --- /dev/null +++ b/utils/opensc/patches/0012-OpenPGP-Support-write-certificate-for-Gnuk.patch @@ -0,0 +1,220 @@ +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 + diff --git a/utils/opensc/patches/0013-pkcs15-openpgp-Change-to-sc_put_data-instead-of-sc_u.patch b/utils/opensc/patches/0013-pkcs15-openpgp-Change-to-sc_put_data-instead-of-sc_u.patch new file mode 100644 index 0000000000..67d79dd6f8 --- /dev/null +++ b/utils/opensc/patches/0013-pkcs15-openpgp-Change-to-sc_put_data-instead-of-sc_u.patch @@ -0,0 +1,31 @@ +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 + diff --git a/utils/opensc/patches/0014-OpenPGP-Overcome-the-restriction-of-even-data-length.patch b/utils/opensc/patches/0014-OpenPGP-Overcome-the-restriction-of-even-data-length.patch new file mode 100644 index 0000000000..cf1a07c647 --- /dev/null +++ b/utils/opensc/patches/0014-OpenPGP-Overcome-the-restriction-of-even-data-length.patch @@ -0,0 +1,53 @@ +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 + diff --git a/utils/opensc/patches/0015-OpenPGP-Delete-key-as-file-for-Gnuk.patch b/utils/opensc/patches/0015-OpenPGP-Delete-key-as-file-for-Gnuk.patch new file mode 100644 index 0000000000..cc88a12db7 --- /dev/null +++ b/utils/opensc/patches/0015-OpenPGP-Delete-key-as-file-for-Gnuk.patch @@ -0,0 +1,92 @@ +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 + diff --git a/utils/opensc/patches/0016-OpenPGP-Correct-parameter-checking.patch b/utils/opensc/patches/0016-OpenPGP-Correct-parameter-checking.patch new file mode 100644 index 0000000000..c49de13c0a --- /dev/null +++ b/utils/opensc/patches/0016-OpenPGP-Correct-parameter-checking.patch @@ -0,0 +1,47 @@ +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 + diff --git a/utils/opensc/patches/0017-OpenPGP-Make-code-neater.patch b/utils/opensc/patches/0017-OpenPGP-Make-code-neater.patch new file mode 100644 index 0000000000..50501e0958 --- /dev/null +++ b/utils/opensc/patches/0017-OpenPGP-Make-code-neater.patch @@ -0,0 +1,39 @@ +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 + diff --git a/utils/opensc/patches/0018-Move-declaration-to-top-of-block.patch b/utils/opensc/patches/0018-Move-declaration-to-top-of-block.patch new file mode 100644 index 0000000000..b05cc59c4e --- /dev/null +++ b/utils/opensc/patches/0018-Move-declaration-to-top-of-block.patch @@ -0,0 +1,34 @@ +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 +