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