#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "internal.h"
#include "asn1.h"
#include "cardctl.h"
#include "errors.h"
#ifdef ENABLE_OPENSSL
#include <openssl/sha.h>
#endif
static const char default_cardname[] = "OpenPGP card";
static const char default_cardname_v1[] = "OpenPGP card v1.x";
static const char default_cardname_v2[] = "OpenPGP card v2.x";
static const char default_cardname_v3[] = "OpenPGP card v3.x";
static const 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, default_cardname_v1, 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, default_cardname_v2, 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",
"ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:ff:ff:00",
"Gnuk v1.0.x (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_GNUK, 0, NULL
},
{ "3b:fc:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:4e:45:4f:72:33:e1", NULL, "Yubikey NEO (OpenPGP v2.0)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
{ "3b:f8:13:00:00:81:31:fe:15:59:75:62:69:6b:65:79:34:d4", NULL, "Yubikey 4 (OpenPGP v2.1)", SC_CARD_TYPE_OPENPGP_V2, 0, NULL },
{ "3b:da:18:ff:81:b1:fe:75:1f:03:00:31:f5:73:c0:01:60:00:90:00:1c", NULL, default_cardname_v3, SC_CARD_TYPE_OPENPGP_V3, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
static struct sc_card_operations *iso_ops;
static struct sc_card_operations pgp_ops;
static struct sc_card_driver pgp_drv = {
"OpenPGP card",
"openpgp",
&pgp_ops,
NULL, 0, NULL
};
enum _type {
SIMPLE = SC_FILE_TYPE_WORKING_EF,
CONSTRUCTED = SC_FILE_TYPE_DF
};
enum _version {
OPENPGP_CARD_1_0 = 0x0100,
OPENPGP_CARD_1_1 = 0x0101,
OPENPGP_CARD_2_0 = 0x0200,
OPENPGP_CARD_2_1 = 0x0201,
OPENPGP_CARD_2_2 = 0x0202,
OPENPGP_CARD_3_0 = 0x0300,
OPENPGP_CARD_3_1 = 0x0301,
OPENPGP_CARD_3_2 = 0x0302,
OPENPGP_CARD_3_3 = 0x0303,
};
enum _access {
READ_NEVER = 0x0010,
READ_PIN1 = 0x0011,
READ_PIN2 = 0x0012,
READ_PIN3 = 0x0014,
READ_ALWAYS = 0x0018,
READ_MASK = 0x00FF,
WRITE_NEVER = 0x1000,
WRITE_PIN1 = 0x1100,
WRITE_PIN2 = 0x1200,
WRITE_PIN3 = 0x1400,
WRITE_ALWAYS = 0x1800,
WRITE_MASK = 0x1F00
};
enum _ext_caps {
EXT_CAP_ALG_ATTR_CHANGEABLE = 0x0004,
EXT_CAP_PRIVATE_DO = 0x0008,
EXT_CAP_C4_CHANGEABLE = 0x0010,
EXT_CAP_KEY_IMPORT = 0x0020,
EXT_CAP_GET_CHALLENGE = 0x0040,
EXT_CAP_SM = 0x0080,
EXT_CAP_LCS = 0x0100,
EXT_CAP_CHAINING = 0x1000,
EXT_CAP_APDU_EXT = 0x2000,
EXT_CAP_MSE = 0x4000
};
enum _card_state {
CARD_STATE_UNKNOWN = 0x00,
CARD_STATE_INITIALIZATION = 0x03,
CARD_STATE_ACTIVATED = 0x05
};
enum _sm_algo {
SM_ALGO_NONE = 0,
SM_ALGO_AES128 = 1,
SM_ALGO_AES256 = 2,
SM_ALGO_SCP11b = 3,
SM_ALGO_3DES = 256,
SM_ALGO_UNKNOWN = 257
};
static struct pgp_supported_ec_curves {
struct sc_object_id oid;
size_t size;
struct sc_object_id oid_binary;
} ec_curves[] = {
{{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256,
{{0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, -1}}},
{{{1, 3, 132, 0, 34, -1}}, 384,
{{0x2b, 0x81, 0x04, 0x00, 0x22, -1}}},
{{{1, 3, 132, 0, 35, -1}}, 521,
{{0x2b, 0x81, 0x04, 0x00, 0x23, -1}}},
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256,
{{0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07, -1}}},
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 11, -1}}, 384,
{{0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0b, -1}}},
{{{1, 3, 36, 3, 3, 2, 8, 1, 1, 13, -1}}, 512,
{{0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0d, -1}}},
{{{-1}}, 0, {{0x0}}}
};
typedef struct pgp_blob {
struct pgp_blob * next;
struct pgp_blob * parent;
struct do_info *info;
sc_file_t * file;
unsigned int id;
int status;
unsigned char * data;
unsigned int len;
struct pgp_blob * files;
} pgp_blob_t;
struct do_info {
unsigned int id;
enum _type type;
enum _access access;
int (*get_fn)(sc_card_t *, unsigned int, u8 *, size_t);
int (*put_fn)(sc_card_t *, unsigned int, const u8 *, size_t);
};
static int pgp_get_card_features(sc_card_t *card);
static int pgp_finish(sc_card_t *card);
static void pgp_iterate_blobs(pgp_blob_t *, int, void (*func)());
static int pgp_get_blob(sc_card_t *card, pgp_blob_t *blob,
unsigned int id, pgp_blob_t **ret);
static pgp_blob_t * pgp_new_blob(sc_card_t *, pgp_blob_t *, unsigned int, sc_file_t *);
static void pgp_free_blob(pgp_blob_t *);
static int pgp_get_pubkey(sc_card_t *, unsigned int,
u8 *, size_t);
static int pgp_get_pubkey_pem(sc_card_t *, unsigned int,
u8 *, size_t);
#define DO_CERT 0x7f21
#define DO_SIGN 0xb600
#define DO_ENCR 0xb800
#define DO_AUTH 0xa400
#define DO_SIGN_SYM 0xb601
#define DO_ENCR_SYM 0xb801
#define DO_AUTH_SYM 0xa401
#define DO_PRIV1 0x0101
#define DO_PRIV2 0x0102
#define DO_PRIV3 0x0103
#define DO_PRIV4 0x0104
#define DO_CARDHOLDER 0x65
#define DO_NAME 0x5b
#define DO_LANG_PREF 0x5f2d
#define DO_SEX 0x5f35
#define MAXLEN_RESP_PUBKEY 527
#define MAXLEN_RESP_PUBKEY_GNUK 271
#define MAX_OPENPGP_DO_SIZE 8192
static struct do_info pgp1x_objects[] = {
{ 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c1, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c2, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c3, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00e0, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00e1, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00e2, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data },
{ 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data },
{ 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0, 0, 0, NULL, NULL },
};
static struct do_info pgp33_objects[] = {
{ 0x00f9, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d7, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d8, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x7f66, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data },
{ 0x7f74, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, sc_put_data },
{ 0x00d5, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x004d, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x004f, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x005b, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x00c1, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c2, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c3, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d1, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d2, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00d3, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x00f4, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data },
{ 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data },
{ 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data },
{ 0x5f48, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data },
{ 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x5f52, SIMPLE, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL },
{ DO_CERT, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data },
{ 0x7f48, CONSTRUCTED, READ_NEVER | WRITE_NEVER, NULL, NULL },
{ 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL },
{ DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0, 0, 0, NULL, NULL },
};
static struct do_info *pgp30_objects = pgp33_objects + 1;
static struct do_info *pgp21_objects = pgp33_objects + 6;
static struct do_info *pgp20_objects = pgp33_objects + 7;
#define DRVDATA(card) ((struct pgp_priv_data *) ((card)->drv_data))
struct pgp_priv_data {
pgp_blob_t *mf;
pgp_blob_t *current;
enum _version bcd_version;
struct do_info *pgp_objects;
enum _card_state state;
enum _ext_caps ext_caps;
enum _sm_algo sm_algo;
size_t max_challenge_size;
size_t max_cert_size;
sc_security_env_t sec_env;
};
static int
get_full_pgp_aid(sc_card_t *card, sc_file_t *file)
{
int r = SC_ERROR_INVALID_ARGUMENTS;
if (file != NULL) {
r = sc_get_data(card, 0x004F, file->name, sizeof file->name);
file->namelen = MAX(r, 0);
}
return r;
}
#define BCD2UCHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F))
static int
pgp_match_card(sc_card_t *card)
{
int i;
LOG_FUNC_CALLED(card->ctx);
i = _sc_match_atr(card, pgp_atrs, &card->type);
if (i >= 0) {
card->name = pgp_atrs[i].name;
LOG_FUNC_RETURN(card->ctx, 1);
}
else {
sc_path_t partial_aid;
sc_file_t *file = NULL;
sc_format_path("D276:0001:2401", &partial_aid);
partial_aid.type = SC_PATH_TYPE_DF_NAME;
i = iso_ops->select_file(card, &partial_aid, &file);
if (SC_SUCCESS == i) {
card->type = SC_CARD_TYPE_OPENPGP_BASE;
card->name = default_cardname;
if (file->namelen != 16)
(void) get_full_pgp_aid(card, file);
if (file->namelen == 16) {
unsigned char major = BCD2UCHAR(file->name[6]);
switch (major) {
case 1:
card->type = SC_CARD_TYPE_OPENPGP_V1;
card->name = default_cardname_v1;
break;
case 2:
card->type = SC_CARD_TYPE_OPENPGP_V2;
card->name = default_cardname_v2;
break;
case 3:
card->type = SC_CARD_TYPE_OPENPGP_V3;
card->name = default_cardname_v3;
break;
default:
break;
}
}
sc_file_free(file);
LOG_FUNC_RETURN(card->ctx, 1);
}
}
LOG_FUNC_RETURN(card->ctx, 0);
}
static int
pgp_init(sc_card_t *card)
{
struct pgp_priv_data *priv;
sc_path_t path;
sc_file_t *file = NULL;
struct do_info *info;
int r, i;
LOG_FUNC_CALLED(card->ctx);
priv = calloc (1, sizeof *priv);
if (!priv)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
card->drv_data = priv;
card->cla = 0x00;
sc_format_path("D276:0001:2401", &path);
path.type = SC_PATH_TYPE_DF_NAME;
if ((r = iso_ops->select_file(card, &path, &file)) < 0) {
sc_file_free(file);
pgp_finish(card);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
}
if (!file) {
pgp_finish(card);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
}
if (file->namelen != 16) {
r = get_full_pgp_aid(card, file);
if (r < 0) {
sc_file_free(file);
pgp_finish(card);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
}
}
if (file->namelen == 16) {
static char card_name[SC_MAX_APDU_BUFFER_SIZE] = "OpenPGP card";
priv->bcd_version = bebytes2ushort(file->name + 6);
card->version.fw_major = card->version.hw_major = BCD2UCHAR(file->name[6]);
card->version.fw_minor = card->version.hw_minor = BCD2UCHAR(file->name[7]);
if (card->name == default_cardname_v1 ||
card->name == default_cardname_v2 ||
card->name == default_cardname_v3) {
snprintf(card_name, sizeof(card_name),
"OpenPGP card v%u.%u (%04X %08lX)",
card->version.hw_major, card->version.hw_minor,
bebytes2ushort(file->name + 8),
bebytes2ulong(file->name + 10));
}
else if (card->name != NULL) {
snprintf(card_name, sizeof(card_name),
"%s (%04X %08lX)",
card->name,
bebytes2ushort(file->name + 8),
bebytes2ulong(file->name + 10));
}
card->name = card_name;
memcpy(card->serialnr.value, file->name + 8, 6);
card->serialnr.len = 6;
} else {
switch (card->type) {
case SC_CARD_TYPE_OPENPGP_V3:
priv->bcd_version = OPENPGP_CARD_3_0;
break;
case SC_CARD_TYPE_OPENPGP_GNUK:
case SC_CARD_TYPE_OPENPGP_V2:
priv->bcd_version = OPENPGP_CARD_2_0;
break;
default:
priv->bcd_version = OPENPGP_CARD_1_1;
break;
}
}
priv->pgp_objects = (priv->bcd_version < OPENPGP_CARD_2_0) ? pgp1x_objects
: (priv->bcd_version < OPENPGP_CARD_2_1) ? pgp20_objects
: (priv->bcd_version < OPENPGP_CARD_3_0) ? pgp21_objects
: (priv->bcd_version < OPENPGP_CARD_3_3) ? pgp30_objects
: pgp33_objects;
sc_format_path("3f00", &file->path);
priv->mf = pgp_new_blob(card, NULL, 0x3f00, file);
if (!priv->mf) {
sc_file_free(file);
pgp_finish(card);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
priv->current = priv->mf;
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) {
if (((info->access & READ_MASK) != READ_NEVER) && (info->get_fn != NULL)) {
pgp_blob_t *child = NULL;
child = pgp_new_blob(card, priv->mf, info->id, sc_file_new());
if (child == NULL) {
pgp_finish(card);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
}
}
}
pgp_get_card_features(card);
if ((priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) &&
(strcmp(card->ctx->app_name, "pkcs15-init") == 0)) {
unsigned long flags_rsa, flags_ecc, ext_flags;
flags_rsa = SC_ALGORITHM_RSA_PAD_PKCS1|
SC_ALGORITHM_RSA_HASH_NONE|
SC_ALGORITHM_ONBOARD_KEY_GEN;
flags_ecc = SC_ALGORITHM_ECDSA_RAW|
SC_ALGORITHM_ECDH_CDH_RAW|
SC_ALGORITHM_ECDSA_HASH_NONE|
SC_ALGORITHM_ONBOARD_KEY_GEN;
ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE;
switch (card->type) {
case SC_CARD_TYPE_OPENPGP_V3:
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
_sc_card_add_rsa_alg(card, 3072, flags_rsa, 0);
_sc_card_add_rsa_alg(card, 4096, flags_rsa, 0);
for (i=0; ec_curves[i].oid.value[0] >= 0; i++)
{
_sc_card_add_ec_alg(card, ec_curves[i].size, flags_ecc, ext_flags, &ec_curves[i].oid);
}
break;
case SC_CARD_TYPE_OPENPGP_GNUK:
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
break;
case SC_CARD_TYPE_OPENPGP_V2:
default:
_sc_card_add_rsa_alg(card, 1024, flags_rsa, 0);
_sc_card_add_rsa_alg(card, 2048, flags_rsa, 0);
_sc_card_add_rsa_alg(card, 3072, flags_rsa, 0);
_sc_card_add_rsa_alg(card, 4096, flags_rsa, 0);
break;
}
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static void
pgp_parse_hist_bytes(sc_card_t *card, u8 *ctlv, size_t ctlv_len)
{
struct pgp_priv_data *priv = DRVDATA(card);
const u8 *ptr;
if ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x73, NULL)) != NULL) {
if (ptr[2] & 0x40) {
card->caps |= SC_CARD_CAP_APDU_EXT;
priv->ext_caps |= EXT_CAP_APDU_EXT;
}
if (ptr[2] & 0x80) {
priv->ext_caps |= EXT_CAP_CHAINING;
}
}
if ((priv->bcd_version >= OPENPGP_CARD_3_0) &&
((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x31, NULL)) != NULL)) {
}
}
static int
pgp_parse_algo_attr_blob(const pgp_blob_t *blob, sc_cardctl_openpgp_keygen_info_t *key_info)
{
struct sc_object_id oid;
unsigned int j;
if (blob == NULL || blob->data == NULL || blob->len == 0 ||
blob->id < 0x00c1 || blob->id > 0x00c3 || key_info == NULL)
return SC_ERROR_INCORRECT_PARAMETERS;
key_info->key_id = blob->id - 0x00c0;
switch (blob->data[0]) {
case SC_OPENPGP_KEYALGO_RSA:
if (blob->len < 5)
return SC_ERROR_INCORRECT_PARAMETERS;
key_info->algorithm = SC_OPENPGP_KEYALGO_RSA;
key_info->u.rsa.modulus_len = bebytes2ushort(blob->data + 1);
key_info->u.rsa.exponent_len = bebytes2ushort(blob->data + 3);
key_info->u.rsa.keyformat = (blob->len > 5)
? blob->data[5]
: SC_OPENPGP_KEYFORMAT_RSA_STD;
break;
case SC_OPENPGP_KEYALGO_ECDH:
case SC_OPENPGP_KEYALGO_ECDSA:
key_info->algorithm = blob->data[0];
sc_init_oid(&oid);
for (j=0; j < (blob->len-1) && j < SC_MAX_OBJECT_ID_OCTETS; j++) {
oid.value[j] = blob->data[j+1];
}
for (j=0; ec_curves[j].oid.value[0] >= 0; j++){
if (sc_compare_oid(&ec_curves[j].oid_binary, &oid)){
key_info->u.ec.oid = ec_curves[j].oid;
key_info->u.ec.key_length = ec_curves[j].size;
break;
}
}
break;
default:
return SC_ERROR_NOT_IMPLEMENTED;
}
return SC_SUCCESS;
}
static int
pgp_get_card_features(sc_card_t *card)
{
struct pgp_priv_data *priv = DRVDATA(card);
u8 *hist_bytes = card->reader->atr_info.hist_bytes;
size_t hist_bytes_len = card->reader->atr_info.hist_bytes_len;
size_t i;
pgp_blob_t *blob, *blob6e, *blob73;
if (hist_bytes_len > 0) {
switch (hist_bytes[0]) {
case 0x00:
if (hist_bytes_len > 4) {
pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-4);
}
break;
case 0x80:
if (hist_bytes_len > 1) {
pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-1);
}
break;
case 0x10:
if (hist_bytes_len > 2) {
pgp_parse_hist_bytes(card, hist_bytes+2, hist_bytes_len-2);
}
break;
}
}
priv->ext_caps &= ~EXT_CAP_LCS;
if (priv->bcd_version >= OPENPGP_CARD_2_0) {
if ((pgp_get_blob(card, priv->mf, 0x5f52, &blob) >= 0) &&
(blob->data != NULL) && (blob->data[0] == 0x00)) {
if (blob->len > 4) {
pgp_parse_hist_bytes(card, blob->data+1, blob->len-4);
}
if ((blob->data[0] == 0x00) && (blob->len >= 4)) {
priv->state = blob->data[blob->len-3];
if (priv->state != CARD_STATE_UNKNOWN)
priv->ext_caps |= EXT_CAP_LCS;
}
}
}
if (priv->bcd_version >= OPENPGP_CARD_3_1) {
card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO;
}
if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) &&
(pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) {
if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) &&
(blob->data != NULL) && (blob->len > 0)) {
if ((blob->data[0] & 0x04) &&
(priv->bcd_version >= OPENPGP_CARD_2_0))
priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE;
if (blob->data[0] & 0x08)
priv->ext_caps |= EXT_CAP_PRIVATE_DO;
if (blob->data[0] & 0x10)
priv->ext_caps |= EXT_CAP_C4_CHANGEABLE;
if (blob->data[0] & 0x20)
priv->ext_caps |= EXT_CAP_KEY_IMPORT;
if (blob->data[0] & 0x40) {
card->caps |= SC_CARD_CAP_RNG;
priv->ext_caps |= EXT_CAP_GET_CHALLENGE;
}
if ((blob->data[0] & 0x80) &&
(priv->bcd_version >= OPENPGP_CARD_2_0))
priv->ext_caps |= EXT_CAP_SM;
if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) {
priv->max_challenge_size = bebytes2ushort(blob->data + 2);
priv->max_cert_size = bebytes2ushort(blob->data + 4);
if (priv->bcd_version < OPENPGP_CARD_3_0) {
priv->sm_algo = blob->data[1];
if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
priv->sm_algo = SM_ALGO_3DES;
card->max_send_size = bebytes2ushort(blob->data + 6);
card->max_recv_size = bebytes2ushort(blob->data + 8);
}
else {
priv->sm_algo = blob->data[1];
if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
priv->sm_algo = SM_ALGO_UNKNOWN;
}
if (priv->bcd_version >= OPENPGP_CARD_3_3 && (blob->len >= 10)) {
if (blob->data[9])
priv->ext_caps |= EXT_CAP_MSE;
}
}
}
if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) &&
(blob->data != NULL) && (blob->len > 1)) {
card->max_pin_len = blob->data[1];
}
for (i = 0x00c1; i <= 0x00c3; i++) {
sc_cardctl_openpgp_keygen_info_t key_info;
if ((pgp_get_blob(card, blob73, i, &blob) >= 0) &&
(pgp_parse_algo_attr_blob(blob, &key_info) >= 0)) {
if (key_info.algorithm == SC_OPENPGP_KEYALGO_RSA){
unsigned long flags = SC_ALGORITHM_RSA_PAD_PKCS1 |
SC_ALGORITHM_RSA_HASH_NONE |
SC_ALGORITHM_ONBOARD_KEY_GEN;
_sc_card_add_rsa_alg(card, key_info.u.rsa.modulus_len, flags, 0);
}
else if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info.algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
unsigned long flags, ext_flags;
if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDH)
flags = SC_ALGORITHM_ECDH_CDH_RAW;
if (key_info.algorithm == SC_OPENPGP_KEYALGO_ECDSA)
flags = SC_ALGORITHM_ECDSA_RAW;
flags |= SC_ALGORITHM_ECDSA_HASH_NONE;
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE;
_sc_card_add_ec_alg(card, key_info.u.ec.key_length, flags, ext_flags,
&key_info.u.ec.oid);
}
}
}
if (priv->bcd_version >= OPENPGP_CARD_3_0) {
if ((pgp_get_blob(card, blob6e, 0x7f66, &blob) >= 0) &&
(blob->data != NULL) && (blob->len >= 8)) {
card->max_send_size = bebytes2ushort(blob->data + 2);
card->max_recv_size = bebytes2ushort(blob->data + 6);
}
}
}
return SC_SUCCESS;
}
static int
pgp_finish(sc_card_t *card)
{
if (card != NULL) {
struct pgp_priv_data *priv = DRVDATA(card);
if (priv != NULL) {
pgp_iterate_blobs(priv->mf, 99, pgp_free_blob);
free(priv);
}
card->drv_data = NULL;
}
return SC_SUCCESS;
}
static int
pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len)
{
if (blob->data)
free(blob->data);
blob->data = NULL;
blob->len = 0;
blob->status = 0;
if (len > 0) {
void *tmp = calloc(len, 1);
if (tmp == NULL)
return SC_ERROR_OUT_OF_MEMORY;
blob->data = tmp;
blob->len = (unsigned int)len;
if (data != NULL)
memcpy(blob->data, data, len);
}
if (blob->file)
blob->file->size = len;
return SC_SUCCESS;
}
static void
pgp_attach_acl(sc_card_t *card, sc_file_t *file, struct do_info *info)
{
unsigned int method = SC_AC_NONE;
unsigned long key_ref = SC_AC_KEY_REF_NONE;
switch (info->access & WRITE_MASK) {
case WRITE_NEVER:
method = SC_AC_NEVER;
break;
case WRITE_PIN1:
method = SC_AC_CHV;
key_ref = 0x01;
break;
case WRITE_PIN2:
method = SC_AC_CHV;
key_ref = 0x02;
break;
case WRITE_PIN3:
method = SC_AC_CHV;
key_ref = 0x03;
break;
}
if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) {
sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref);
sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref);
sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref);
sc_file_add_acl_entry(file, SC_AC_OP_CREATE, method, key_ref);
}
else {
sc_file_add_acl_entry(file, SC_AC_OP_DELETE_SELF, method, key_ref);
}
method = SC_AC_NONE;
key_ref = SC_AC_KEY_REF_NONE;
switch (info->access & READ_MASK) {
case READ_NEVER:
method = SC_AC_NEVER;
break;
case READ_PIN1:
method = SC_AC_CHV;
key_ref = 0x01;
break;
case READ_PIN2:
method = SC_AC_CHV;
key_ref = 0x02;
break;
case READ_PIN3:
method = SC_AC_CHV;
key_ref = 0x03;
break;
}
if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) {
sc_file_add_acl_entry(file, SC_AC_OP_READ, method, key_ref);
}
}
static pgp_blob_t *
pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id,
sc_file_t *file)
{
pgp_blob_t *blob = NULL;
if (file == NULL)
return NULL;
if ((blob = calloc(1, sizeof(pgp_blob_t))) != NULL) {
struct pgp_priv_data *priv = DRVDATA(card);
struct do_info *info;
blob->file = file;
blob->file->type = SC_FILE_TYPE_WORKING_EF;
blob->file->ef_structure = SC_FILE_EF_TRANSPARENT;
blob->file->id = file_id;
blob->id = file_id;
blob->parent = parent;
if (parent != NULL) {
pgp_blob_t **p;
blob->file->path = parent->file->path;
sc_append_file_id(&blob->file->path, file_id);
for (p = &parent->files; *p != NULL; p = &(*p)->next)
;
*p = blob;
}
else {
char path[10] = "0000";
if (4 != snprintf(path, sizeof(path), "%04X", file_id & 0xFFFF)) {
free(blob);
return NULL;
}
sc_format_path(path, &blob->file->path);
}
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) {
if (info->id == file_id) {
blob->info = info;
blob->file->type = blob->info->type;
pgp_attach_acl(card, blob->file, info);
break;
}
}
}
return blob;
}
static void
pgp_free_blob(pgp_blob_t *blob)
{
if (blob) {
if (blob->parent) {
pgp_blob_t **p;
for (p = &blob->parent->files; *p != NULL && *p != blob; p = &(*p)->next)
;
if (*p == blob)
*p = blob->next;
}
sc_file_free(blob->file);
if (blob->data)
free(blob->data);
free(blob);
}
}
static void
pgp_iterate_blobs(pgp_blob_t *blob, int level, void (*func)())
{
if (blob) {
if (level > 0) {
pgp_blob_t *child = blob->files;
while (child != NULL) {
pgp_blob_t *next = child->next;
pgp_iterate_blobs(child, level-1, func);
child = next;
}
}
func(blob);
}
}
static int
pgp_read_blob(sc_card_t *card, pgp_blob_t *blob)
{
struct pgp_priv_data *priv = DRVDATA(card);
if (blob->data != NULL)
return SC_SUCCESS;
if (blob->info == NULL)
return blob->status;
if (blob->info->get_fn) {
u8 buffer[MAX_OPENPGP_DO_SIZE];
size_t buf_len = sizeof(buffer);
int r = SC_SUCCESS;
if (blob->id == DO_CERT && priv->max_cert_size > 0) {
buf_len = MIN(priv->max_cert_size, sizeof(buffer));
}
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
(blob->id == DO_AUTH ||
blob->id == DO_SIGN ||
blob->id == DO_ENCR ||
blob->id == DO_AUTH_SYM ||
blob->id == DO_SIGN_SYM ||
blob->id == DO_ENCR_SYM)) {
buf_len = MIN(MAXLEN_RESP_PUBKEY_GNUK, sizeof(buffer));
}
r = blob->info->get_fn(card, blob->id, buffer, buf_len);
if (r < 0) {
blob->status = r;
return r;
}
return pgp_set_blob(blob, buffer, r);
}
else {
return SC_SUCCESS;
}
}
static int
pgp_enumerate_blob(sc_card_t *card, pgp_blob_t *blob)
{
const u8 *in;
int r;
if (blob->files != NULL)
return SC_SUCCESS;
if ((r = pgp_read_blob(card, blob)) < 0)
return r;
in = blob->data;
while ((int) blob->len > (in - blob->data)) {
unsigned int cla, tag, tmptag;
size_t len;
const u8 *data = in;
pgp_blob_t *new;
if (!in)
return SC_ERROR_OBJECT_NOT_VALID;
r = sc_asn1_read_tag(&data, blob->len - (in - blob->data),
&cla, &tag, &len);
if (r < 0 || data == NULL) {
sc_log(card->ctx,
"Unexpected end of contents\n");
return SC_ERROR_OBJECT_NOT_VALID;
}
if (data + len > blob->data + blob->len)
return SC_ERROR_OBJECT_NOT_VALID;
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
cla <<= 8;
}
tag |= cla;
if (tag == blob->id) {
in = data;
continue;
}
if ((new = pgp_new_blob(card, blob, tag, sc_file_new())) == NULL)
return SC_ERROR_OUT_OF_MEMORY;
pgp_set_blob(new, data, len);
in = data + len;
}
return SC_SUCCESS;
}
static int
pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, unsigned int id,
pgp_blob_t **ret)
{
pgp_blob_t *child;
int r;
if ((r = pgp_enumerate_blob(card, blob)) < 0)
return r;
for (child = blob->files; child; child = child->next) {
if (child->id == id) {
(void) pgp_read_blob(card, child);
*ret = child;
return SC_SUCCESS;
}
}
if (blob->id == DO_CARDHOLDER && (id == DO_NAME || id == DO_LANG_PREF || id == DO_SEX)) {
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",
id);
}
return SC_ERROR_FILE_NOT_FOUND;
}
static int
pgp_seek_blob(sc_card_t *card, pgp_blob_t *root, unsigned int id,
pgp_blob_t **ret)
{
pgp_blob_t *child;
int r;
if ((r = pgp_get_blob(card, root, id, ret)) == 0)
return r;
for (child = root->files; child; child = child->next) {
if ((child->info && child->info->type == SIMPLE) || child->id == DO_CERT)
continue;
r = pgp_seek_blob(card, child, id, ret);
if (r == 0)
return r;
}
return SC_ERROR_FILE_NOT_FOUND;
}
static pgp_blob_t *
pgp_find_blob(sc_card_t *card, unsigned int tag)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob = NULL;
int r;
if (priv->current->id == tag) {
return priv->current;
}
r = pgp_seek_blob(card, priv->mf, tag, &blob);
if (r < 0) {
sc_log(card->ctx, "Failed to seek the blob representing the tag %04X. Error %d.", tag, r);
return NULL;
}
return blob;
}
static struct do_info *
pgp_get_info_by_tag(sc_card_t *card, unsigned int tag)
{
struct pgp_priv_data *priv = DRVDATA(card);
struct do_info *info;
for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++)
if (tag == info->id)
return info;
return NULL;
}
static unsigned int
pgp_strip_path(sc_card_t *card, const sc_path_t *path)
{
unsigned int start_point = 0;
if (path->len == 0)
return 0;
start_point = (memcmp(path->value, "\x3f\x00", 2) == 0) ? 2 : 0;
start_point += (memcmp(path->value + start_point, "\x50\x15", 2) == 0) ? 2 : 0;
return start_point;
}
static int
pgp_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **ret)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob;
unsigned int path_start = 0;
unsigned int n;
sc_path_t dummy_path;
LOG_FUNC_CALLED(card->ctx);
if (path->type == SC_PATH_TYPE_DF_NAME)
LOG_FUNC_RETURN(card->ctx, iso_ops->select_file(card, path, ret));
if (path->len < 2 || (path->len & 1))
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid path length");
if (path->type == SC_PATH_TYPE_FILE_ID && path->len != 2)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid path type");
sc_format_path("11001101", &dummy_path);
if (sc_compare_path(path, &dummy_path)) {
if (ret != NULL) {
*ret = sc_file_new();
(*ret)->size = priv->max_cert_size;
}
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
path_start = pgp_strip_path(card, path);
blob = priv->mf;
for (n = path_start; n < path->len; n += 2) {
unsigned int id = bebytes2ushort(path->value + n);
int r = pgp_get_blob(card, blob, id, &blob);
if (id == 0x4402 || id == 0x5f48) {
if (ret == NULL)
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
*ret = sc_file_new();
(*ret)->size = priv->max_cert_size;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
if (r < 0) {
LOG_FUNC_RETURN(card->ctx, r);
}
}
priv->current = blob;
if (ret)
sc_file_dup(ret, blob->file);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob;
unsigned int k;
int r;
LOG_FUNC_CALLED(card->ctx);
blob = priv->current;
if (blob->file->type != SC_FILE_TYPE_DF)
LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID,
"invalid file type");
if ((r = pgp_enumerate_blob(card, blob)) < 0)
LOG_FUNC_RETURN(card->ctx, r);
for (k = 0, blob = blob->files; blob != NULL; blob = blob->next) {
if (blob->info != NULL && (blob->info->access & READ_MASK) != READ_NEVER) {
if (k + 2 > buflen)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
ushort2bebytes(buf + k, blob->id);
k += 2;
}
}
LOG_FUNC_RETURN(card->ctx, k);
}
static int
pgp_get_challenge(struct sc_card *card, u8 *rnd, size_t len)
{
struct pgp_priv_data *priv;
LOG_FUNC_CALLED(card->ctx);
priv = DRVDATA(card);
if (0 == (priv->ext_caps & EXT_CAP_GET_CHALLENGE)) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
if (priv->max_challenge_size > 0 && len > priv->max_challenge_size) {
len = priv->max_challenge_size;
}
LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len));
}
static int
pgp_read_binary(sc_card_t *card, unsigned int idx,
u8 *buf, size_t count, unsigned long flags)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob;
int r;
LOG_FUNC_CALLED(card->ctx);
blob = priv->current;
if (blob == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);
if (blob->file->type != SC_FILE_TYPE_WORKING_EF)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);
if ((r = pgp_read_blob(card, blob)) < 0)
LOG_FUNC_RETURN(card->ctx, r);
if (idx > blob->len)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
if (idx + count > blob->len)
count = blob->len - idx;
memcpy(buf, blob->data + idx, count);
LOG_FUNC_RETURN(card->ctx, (int)count);
}
static int
pgp_write_binary(sc_card_t *card, unsigned int idx,
const u8 *buf, size_t count, unsigned long flags)
{
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
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 = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4;
u8 idbuf[2];
int r;
sc_log(card->ctx, "called, tag=%04x\n", tag);
sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x81, 0);
apdu.lc = 2;
apdu.data = ushort2bebytes(idbuf, tag);
apdu.datalen = 2;
apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len;
apdu.resp = buf;
apdu.resplen = buf_len;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
}
static int
pgp_get_pubkey_pem(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob, *mod_blob, *exp_blob, *pubkey_blob;
sc_pkcs15_pubkey_t pubkey;
u8 *data;
size_t len;
int r;
sc_log(card->ctx, "called, tag=%04x\n", tag);
if ((r = pgp_get_blob(card, priv->mf, tag & 0xFFFE, &blob)) < 0
|| (r = pgp_get_blob(card, blob, 0x7F49, &blob)) < 0)
LOG_TEST_RET(card->ctx, r, "error getting elements");
if ((r = pgp_get_blob(card, blob, 0x0081, &mod_blob)) >= 0
&& (r = pgp_get_blob(card, blob, 0x0082, &exp_blob)) >= 0
&& (r = pgp_read_blob(card, mod_blob)) >= 0
&& (r = pgp_read_blob(card, exp_blob)) >= 0) {
memset(&pubkey, 0, sizeof(pubkey));
pubkey.algorithm = SC_ALGORITHM_RSA;
pubkey.u.rsa.modulus.data = mod_blob->data;
pubkey.u.rsa.modulus.len = mod_blob->len;
pubkey.u.rsa.exponent.data = exp_blob->data;
pubkey.u.rsa.exponent.len = exp_blob->len;
}
else if ((r = pgp_get_blob(card, blob, 0x0086, &pubkey_blob)) >= 0
&& (r = pgp_read_blob(card, pubkey_blob)) >= 0) {
memset(&pubkey, 0, sizeof(pubkey));
pubkey.algorithm = SC_ALGORITHM_EC;
pubkey.u.ec.ecpointQ.value = pubkey_blob->data;
pubkey.u.ec.ecpointQ.len = pubkey_blob->len;
}
else
LOG_TEST_RET(card->ctx, r, "error getting elements");
r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len);
LOG_TEST_RET(card->ctx, r, "public key encoding failed");
if (len > buf_len)
len = buf_len;
memcpy(buf, data, len);
free(data);
LOG_FUNC_RETURN(card->ctx, (int)len);
}
static int
pgp_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t buf_len)
{
sc_apdu_t apdu;
int r;
LOG_FUNC_CALLED(card->ctx);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, tag >> 8, tag);
apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len;
apdu.resp = buf;
apdu.resplen = buf_len;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK &&
(tag == DO_CERT ||
tag == DO_PRIV1 ||
tag == DO_PRIV2 ||
tag == DO_PRIV3 ||
tag == DO_PRIV4)) {
if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
r = SC_SUCCESS;
apdu.resplen = 0;
}
}
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
}
static int
gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length)
{
size_t i = 0;
sc_apdu_t apdu;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
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");
LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
for (i = 0; i*256 < length; i++) {
u8 *part = (u8 *)buf + i*256;
size_t plen = MIN(length - i*256, 256);
u8 roundbuf[256];
sc_log(card->ctx,
"Write part %"SC_FORMAT_LEN_SIZE_T"u from offset 0x%"SC_FORMAT_LEN_SIZE_T"X, len %"SC_FORMAT_LEN_SIZE_T"u",
i+1, i*256, plen);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xD6, (i == 0) ? 0x85 : i, 0);
apdu.flags |= SC_APDU_FLAGS_CHAINING;
apdu.data = part;
apdu.datalen = apdu.lc = plen;
if (plen < 256 && (plen % 2) != 0) {
memcpy(roundbuf, part, plen);
roundbuf[plen++] = 0;
apdu.data = roundbuf;
apdu.datalen = apdu.lc = plen;
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "UPDATE BINARY returned error");
}
LOG_FUNC_RETURN(card->ctx, (int)length);
}
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);
sc_apdu_t apdu;
u8 ins = 0xDA;
u8 p1 = tag >> 8;
u8 p2 = tag & 0xFF;
u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_3;
int r;
LOG_FUNC_CALLED(card->ctx);
if (tag == 0x004D) {
ins = 0xDB;
p1 = 0x3F;
p2 = 0xFF;
}
if (buf != NULL && buf_len > 0) {
sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2);
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
apdu.flags |= SC_APDU_FLAGS_CHAINING;
apdu.data = (u8 *)buf;
apdu.datalen = buf_len;
apdu.lc = buf_len;
}
else {
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2);
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, (int)buf_len);
}
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);
pgp_blob_t *affected_blob = NULL;
struct do_info *dinfo = NULL;
int r;
LOG_FUNC_CALLED(card->ctx);
if (priv->current->id != tag)
affected_blob = pgp_find_blob(card, 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);
}
if (tag == DO_CERT && buf_len > priv->max_cert_size) {
sc_log(card->ctx,
"Data size %"SC_FORMAT_LEN_SIZE_T"u exceeds DO size limit %"SC_FORMAT_LEN_SIZE_T"u.",
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) {
r = gnuk_write_certificate(card, buf, buf_len);
}
else {
r = pgp_put_data_plain(card, tag, buf, buf_len);
}
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
}
LOG_TEST_RET(card->ctx, r, "PUT DATA returned error");
if (affected_blob) {
sc_log(card->ctx, "Updating the corresponding blob data");
r = pgp_set_blob(affected_blob, buf, buf_len);
if (r < 0)
sc_log(card->ctx, "Failed to update blob %04X. Error %d.", affected_blob->id, r);
}
LOG_FUNC_RETURN(card->ctx, (int)buf_len);
}
static int
pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
struct pgp_priv_data *priv = DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if (data->pin_type != SC_AC_CHV)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid PIN type");
data->pin_reference |= 0x80;
if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) {
if (priv->bcd_version >= OPENPGP_CARD_2_0) {
if (data->pin_reference == 0x82)
data->pin_reference = 0x81;
if (data->cmd == SC_PIN_CMD_CHANGE) {
if (data->pin1.len == 0 &&
!(data->flags & SC_PIN_CMD_USE_PINPAD))
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"v2 cards don't support implicit old PIN for PIN change");
data->flags &= ~SC_PIN_CMD_IMPLICIT_CHANGE;
}
} else {
if (data->pin1.len != 0) {
sc_log(card->ctx,
"v1 cards don't support explicit old or CHV3 PIN, PIN ignored.");
sc_log(card->ctx,
"please make sure that you have verified the relevant PIN first.");
data->pin1.len = 0;
}
data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE;
}
}
if (data->cmd == SC_PIN_CMD_UNBLOCK && data->pin2.len == 0 &&
!(data->flags & SC_PIN_CMD_USE_PINPAD))
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"new PIN must be provided for unblock operation");
if (data->pin_reference < 0x81 || data->pin_reference > 0x83)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
if (data->cmd == SC_PIN_CMD_GET_INFO && (card->caps & SC_CARD_CAP_ISO7816_PIN_INFO) == 0) {
u8 c4data[10];
int r;
r = sc_get_data(card, 0x00c4, c4data, sizeof(c4data));
LOG_TEST_RET(card->ctx, r, "reading CHV status bytes failed");
if (r != 7)
LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID,
"CHV status bytes have unexpected length");
data->pin1.tries_left = c4data[4 + (data->pin_reference & 0x0F)];
data->pin1.max_tries = 3;
data->pin1.logged_in = SC_PIN_STATE_UNKNOWN;
if (tries_left != NULL)
*tries_left = data->pin1.tries_left;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left));
}
int pgp_logout(struct sc_card *card)
{
int r = SC_SUCCESS;
struct pgp_priv_data *priv = DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if (priv->bcd_version >= OPENPGP_CARD_3_1) {
unsigned char pin_reference;
for (pin_reference = 0x81; pin_reference <= 0x83; pin_reference++) {
int tmp = iso7816_logout(card, pin_reference);
if (r == SC_SUCCESS) {
r = tmp;
}
}
} else {
sc_path_t path;
sc_file_t *file = NULL;
sc_format_path("D276:0001:2401", &path);
path.type = SC_PATH_TYPE_DF_NAME;
r = iso_ops->select_file(card, &path, &file);
sc_file_free(file);
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_set_security_env(sc_card_t *card,
const sc_security_env_t *env, int se_num)
{
struct pgp_priv_data *priv = DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if ((env->flags & SC_SEC_ENV_ALG_PRESENT)
&& (env->algorithm != SC_ALGORITHM_RSA)
&& (priv->bcd_version < OPENPGP_CARD_3_0))
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"only RSA algorithm supported");
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len != 1))
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"exactly one key reference required");
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"passing file references not supported");
sc_log(card->ctx, "Key ref %d", env->key_ref[0]);
switch (env->operation) {
case SC_SEC_OPERATION_SIGN:
sc_log(card->ctx, "Operation: Sign.");
if (env->key_ref[0] != 0x00 && env->key_ref[0] != 0x02) {
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
"Key reference not compatible with "
"requested usage");
}
break;
case SC_SEC_OPERATION_DECIPHER:
sc_log(card->ctx, "Operation: Decipher.");
if (env->key_ref[0] != 0x01 && env->key_ref[0] != 0x02) {
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
"Key reference not compatible with "
"requested usage");
}
break;
default:
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid operation");
}
priv->sec_env = *env;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
pgp_set_MSE(sc_card_t *card, int key, u8 p2)
{
struct pgp_priv_data *priv = DRVDATA(card);
sc_apdu_t apdu;
u8 apdu_case = SC_APDU_CASE_3;
u8 apdu_data[3];
int r;
LOG_FUNC_CALLED(card->ctx);
if (!(priv->ext_caps & EXT_CAP_MSE))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
sc_format_apdu(card, &apdu, apdu_case, 0x22, 0x41, p2);
apdu.lc = 3;
apdu_data[0] = 0x83;
apdu_data[1] = 0x01;
apdu_data[2] = key;
apdu.data = apdu_data;
apdu.datalen = 3;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
pgp_compute_signature(sc_card_t *card, const u8 *data,
size_t data_len, u8 * out, size_t outlen)
{
struct pgp_priv_data *priv = DRVDATA(card);
sc_security_env_t *env = &priv->sec_env;
sc_apdu_t apdu;
u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK)
? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4;
int r;
LOG_FUNC_CALLED(card->ctx);
if (env->operation != SC_SEC_OPERATION_SIGN)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid operation");
switch (env->key_ref[0]) {
case 0x00:
sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x9E, 0x9A);
break;
case 0x02:
sc_format_apdu(card, &apdu, apdu_case, 0x88, 0, 0);
break;
case 0x01:
default:
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid key reference");
}
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
apdu.flags |= SC_APDU_FLAGS_CHAINING;
apdu.lc = data_len;
apdu.data = (u8 *)data;
apdu.datalen = data_len;
apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
apdu.resp = out;
apdu.resplen = outlen;
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
}
static int
pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen,
u8 *out, size_t outlen)
{
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;
LOG_FUNC_CALLED(card->ctx);
if (!(temp = malloc(inlen + 1)))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
(env->algorithm == SC_ALGORITHM_EC) ? (temp[0] = 0xa6) : (temp[0] = 0x00);
memcpy(temp + 1, in, inlen);
in = temp;
inlen += 1;
if (env->operation != SC_SEC_OPERATION_DECIPHER) {
free(temp);
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid operation");
}
switch (env->key_ref[0]) {
case 0x01:
case 0x02:
sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86);
break;
case 0x00:
default:
free(temp);
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid key reference");
}
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
apdu.flags |= SC_APDU_FLAGS_CHAINING;
}
if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING))
apdu.flags |= SC_APDU_FLAGS_CHAINING;
apdu.lc = inlen;
apdu.data = (u8 *)in;
apdu.datalen = inlen;
apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen;
apdu.resp = out;
apdu.resplen = outlen;
if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){
pgp_set_MSE(card, 3, 0xb8);
}
r = sc_transmit_apdu(card, &apdu);
free(temp);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(card->ctx, r, "Card returned error");
if (priv->bcd_version >= OPENPGP_CARD_3_3 && env->key_ref[0] == 0x02){
pgp_set_MSE(card, 2, 0xb8);
}
LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen);
}
#ifdef ENABLE_OPENSSL
static int
pgp_update_new_algo_attr(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *algo_blob;
const unsigned int tag = 0x00C0 | key_info->key_id;
u8 *data;
int data_len;
int r = SC_SUCCESS;
unsigned int i;
LOG_FUNC_CALLED(card->ctx);
r = pgp_seek_blob(card, priv->mf, tag, &algo_blob);
LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes");
if (priv->ext_caps & EXT_CAP_ALG_ATTR_CHANGEABLE) {
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
data_len = key_info->u.ec.oid_len+1;
data = malloc(data_len);
if (!data)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
data[0] = key_info->algorithm;
for (i=0; i < key_info->u.ec.oid_len; i++){
data[i+1] = key_info->u.ec.oid.value[i];
}
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
if (key_info->u.rsa.exponent_len == 0 || key_info->u.rsa.modulus_len == 0)
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
data_len = 6;
data = malloc(data_len);
if (!data)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
data[0] = key_info->algorithm;
ushort2bebytes(data+1, key_info->u.rsa.modulus_len);
ushort2bebytes(data+3, SC_OPENPGP_MAX_EXP_BITS);
data[5] = SC_OPENPGP_KEYFORMAT_RSA_STD;
}
else {
sc_log(card->ctx, "Unknown algorithm id");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
pgp_set_blob(algo_blob, data, data_len);
free(data);
r = pgp_put_data(card, tag, algo_blob->data, data_len);
LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes");
} else {
sc_cardctl_openpgp_keygen_info_t old_key_info;
if (pgp_parse_algo_attr_blob(algo_blob, &old_key_info) != SC_SUCCESS
|| old_key_info.algorithm != key_info->algorithm)
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
"Requested algorithm not supported");
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime)
{
int r;
time_t createtime = 0;
const size_t timestrlen = 64;
char timestring[65];
u8 buf[4];
struct tm tm;
LOG_FUNC_CALLED(card->ctx);
if (key_id < 1 || key_id > 3)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
if (outtime != NULL && *outtime != 0)
createtime = *outtime;
else if (outtime != NULL)
*outtime = createtime = time(NULL);
#ifdef _WIN32
if (0 != gmtime_s(&tm, &createtime))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
#else
if (NULL == gmtime_r(&createtime, &tm))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
#endif
strftime(timestring, timestrlen, "%c %Z", &tm);
sc_log(card->ctx, "Creation time %s.", timestring);
ulong2bebytes(buf, (unsigned long)createtime);
r = pgp_put_data(card, 0x00CD + key_id, buf, 4);
LOG_TEST_RET(card->ctx, r, "Cannot write to DO");
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime,
sc_cardctl_openpgp_keygen_info_t *key_info)
{
u8 fingerprint[SHA_DIGEST_LENGTH];
u8 *fp_buffer = NULL;
size_t fp_buffer_len;
u8 *p;
size_t pk_packet_len;
unsigned int tag = 0x00C6 + key_info->key_id;
pgp_blob_t *fpseq_blob = NULL;
u8 *newdata = NULL;
int r;
LOG_FUNC_CALLED(card->ctx);
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
if (key_info->u.rsa.modulus == NULL
|| key_info->u.rsa.exponent == NULL
|| (key_info->u.rsa.modulus_len) == 0
|| (key_info->u.rsa.exponent_len) == 0) {
sc_log(card->ctx, "Null data (modulus or exponent)");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
pk_packet_len = 1
+ 4
+ 1
+ 2
+ (BYTES4BITS(key_info->u.rsa.modulus_len))
+ 2
+ (BYTES4BITS(key_info->u.rsa.exponent_len));
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
if (key_info->u.ec.ecpoint == NULL || (key_info->u.ec.ecpoint_len) == 0) {
sc_log(card->ctx, "Error: ecpoint required!");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
pk_packet_len = 1
+ 4
+ 1
+ 1
+ (key_info->u.ec.oid_len)
+ (key_info->u.ec.ecpoint_len);
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) {
pk_packet_len += 1
+ 1
+ 1
+ 1;
}
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
sc_log(card->ctx, "pk_packet_len is %"SC_FORMAT_LEN_SIZE_T"u", pk_packet_len);
fp_buffer_len = 3 + pk_packet_len;
p = fp_buffer = calloc(fp_buffer_len, 1);
if (p == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
p[0] = 0x99;
ushort2bebytes(++p, (unsigned short)pk_packet_len);
p += 2;
*p = 4;
ulong2bebytes(++p, (unsigned long)ctime);
p += 4;
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
*p = 1;
p += 1;
ushort2bebytes(p, (unsigned short)key_info->u.rsa.modulus_len);
p += 2;
memcpy(p, key_info->u.rsa.modulus, (BYTES4BITS(key_info->u.rsa.modulus_len)));
p += (key_info->u.rsa.modulus_len >> 3);
ushort2bebytes(++p, (unsigned short)key_info->u.rsa.exponent_len);
p += 2;
memcpy(p, key_info->u.rsa.exponent, (BYTES4BITS(key_info->u.rsa.exponent_len)));
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
*p = key_info->algorithm + 6;
p += 1;
*p = key_info->u.ec.oid_len;
p += 1;
memcpy(p, key_info->u.ec.oid.value, key_info->u.ec.oid_len);
p += key_info->u.ec.oid_len;
memcpy(p, key_info->u.ec.ecpoint, key_info->u.ec.ecpoint_len);
if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH) {
p += key_info->u.ec.ecpoint_len;
*p = 0x03;
p += 1;
*p = 0x01;
p += 1;
if (key_info->u.ec.ecpoint_len <= 256){
*p = 0x08;
*(p+1) = 0x07;
}
else if (key_info->u.ec.ecpoint_len <= 384) {
*p = 0x09;
*(p+1) = 0x08;
}
else {
*p = 0x0a;
*(p+1) = 0x09;
}
}
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
p = NULL;
SHA1(fp_buffer, fp_buffer_len, fingerprint);
free(fp_buffer);
sc_log(card->ctx, "Writing to DO %04X.", tag);
r = pgp_put_data(card, tag, fingerprint, SHA_DIGEST_LENGTH);
LOG_TEST_RET(card->ctx, r, "Cannot write to DO");
sc_log(card->ctx, "Updating fingerprint blob 00C5.");
fpseq_blob = pgp_find_blob(card, 0x00C5);
if (fpseq_blob == NULL)
LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot find blob 00C5");
newdata = malloc(fpseq_blob->len);
if (newdata == NULL)
LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_OUT_OF_MEMORY,
"Not enough memory to update fingerprint blob 00C5");
memcpy(newdata, fpseq_blob->data, fpseq_blob->len);
p = newdata + 20 * (key_info->key_id - 1);
memcpy(p, fingerprint, 20);
pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len);
free(newdata);
err:
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_update_pubkey_blob(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *pk_blob;
unsigned int blob_id = 0;
sc_pkcs15_pubkey_t pubkey;
u8 *data = NULL;
size_t len;
int r;
LOG_FUNC_CALLED(card->ctx);
if (key_info->key_id == SC_OPENPGP_KEY_SIGN)
blob_id = DO_SIGN_SYM;
else if (key_info->key_id == SC_OPENPGP_KEY_ENCR)
blob_id = DO_ENCR_SYM;
else if (key_info->key_id == SC_OPENPGP_KEY_AUTH)
blob_id = DO_AUTH_SYM;
else {
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
}
sc_log(card->ctx, "Retrieving blob %04X.", blob_id);
r = pgp_get_blob(card, priv->mf, blob_id, &pk_blob);
LOG_TEST_RET(card->ctx, r, "Cannot get the blob");
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
memset(&pubkey, 0, sizeof(pubkey));
pubkey.algorithm = SC_ALGORITHM_RSA;
pubkey.u.rsa.modulus.data = key_info->u.rsa.modulus;
pubkey.u.rsa.modulus.len = BYTES4BITS(key_info->u.rsa.modulus_len);
pubkey.u.rsa.exponent.data = key_info->u.rsa.exponent;
pubkey.u.rsa.exponent.len = BYTES4BITS(key_info->u.rsa.exponent_len);
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
memset(&pubkey, 0, sizeof(pubkey));
pubkey.algorithm = SC_ALGORITHM_EC;
pubkey.u.ec.ecpointQ.value = key_info->u.ec.ecpoint;
pubkey.u.ec.ecpointQ.len = key_info->u.ec.ecpoint_len;
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len);
LOG_TEST_RET(card->ctx, r, "Cannot encode pubkey");
sc_log(card->ctx, "Updating blob %04X's content.", blob_id);
r = pgp_set_blob(pk_blob, data, len);
LOG_TEST_RET(card->ctx, r, "Cannot update blob content");
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_parse_and_set_pubkey_output(sc_card_t *card, u8* data, size_t data_len,
sc_cardctl_openpgp_keygen_info_t *key_info)
{
time_t ctime = 0;
u8 *in = data;
int r;
LOG_FUNC_CALLED(card->ctx);
r = pgp_store_creationtime(card, key_info->key_id, &ctime);
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
while (data_len > (size_t) (in - data)) {
unsigned int cla, tag, tmptag;
size_t len;
u8 *part = in;
r = sc_asn1_read_tag((const u8**)&part, data_len - (in - data), &cla, &tag, &len);
if (part == NULL)
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
LOG_TEST_RET(card->ctx, r, "Unexpected end of contents");
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
cla <<= 8;
}
tag |= cla;
if (tag == 0x0081) {
if ((BYTES4BITS(key_info->u.rsa.modulus_len) < len)
|| key_info->u.rsa.modulus == NULL) {
free(key_info->u.rsa.modulus);
key_info->u.rsa.modulus = malloc(len);
if (key_info->u.rsa.modulus == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
}
memcpy(key_info->u.rsa.modulus, part, len);
key_info->u.rsa.modulus_len = len * 8;
}
else if (tag == 0x0082) {
if ((BYTES4BITS(key_info->u.rsa.exponent_len) < len)
|| key_info->u.rsa.exponent == NULL) {
free(key_info->u.rsa.exponent);
key_info->u.rsa.exponent = malloc(len);
if (key_info->u.rsa.exponent == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
}
memcpy(key_info->u.rsa.exponent, part, len);
key_info->u.rsa.exponent_len = len * 8;
}
else if (tag == 0x0086) {
if ((key_info->u.ec.ecpoint_len) != (len - 1)
|| key_info->u.ec.ecpoint == NULL) {
free(key_info->u.ec.ecpoint);
key_info->u.ec.ecpoint = malloc(len);
if (key_info->u.ec.ecpoint == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
}
memcpy(key_info->u.ec.ecpoint, part + 1, len - 1);
}
in = part + ((tag != 0x7F49) ? len : 0);
}
sc_log(card->ctx, "Calculate and store fingerprint");
r = pgp_calculate_and_store_fingerprint(card, ctime, key_info);
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint");
sc_log(card->ctx, "Update blobs holding pubkey info.");
r = pgp_update_pubkey_blob(card, key_info);
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
sc_algorithm_info_t *algo;
u8 id = key_info->key_id;
LOG_FUNC_CALLED(card->ctx);
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
&& card->type < SC_CARD_TYPE_OPENPGP_V3)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
if (id > card->algorithm_count) {
sc_log(card->ctx,
"This key ID %u is out of the card's algorithm list.",
(unsigned int)id);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
algo = card->algorithms + (id - 1);
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA) {
algo->algorithm = SC_ALGORITHM_RSA;
algo->key_length = (unsigned int)key_info->u.rsa.modulus_len;
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) {
algo->algorithm = SC_ALGORITHM_EC;
algo->key_length = (unsigned int)((key_info->u.ec.ecpoint_len));
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
static int
pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
sc_apdu_t apdu;
u8 apdu_case;
u8 apdu_data[2] = { 0x00, 0x00 };
size_t apdu_le;
size_t resplen = 0;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
&& card->type < SC_CARD_TYPE_OPENPGP_V3)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
if (key_info->key_id == SC_OPENPGP_KEY_SIGN)
ushort2bebytes(apdu_data, DO_SIGN);
else if (key_info->key_id == SC_OPENPGP_KEY_ENCR)
ushort2bebytes(apdu_data, DO_ENCR);
else if (key_info->key_id == SC_OPENPGP_KEY_AUTH)
ushort2bebytes(apdu_data, DO_AUTH);
else {
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
}
if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && key_info->u.rsa.modulus_len != 2048)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Gnuk only supports generating keys up to 2048-bit");
r = pgp_update_new_algo_attr(card, key_info);
LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key");
if (card->caps & SC_CARD_CAP_APDU_EXT
&& key_info->u.rsa.modulus_len > 1900
&& card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
apdu_le = card->max_recv_size;
apdu_case = SC_APDU_CASE_4_EXT;
}
else {
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;
}
sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0);
apdu.data = apdu_data;
apdu.datalen = sizeof(apdu_data);
apdu.lc = sizeof(apdu_data);
apdu.le = apdu_le;
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);
}
sc_log(card->ctx, "Waiting for the card to generate key...");
r = sc_transmit_apdu(card, &apdu);
sc_log(card->ctx, "Card has done key generation.");
LOG_TEST_GOTO_ERR(card->ctx, r, "APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
goto err;
}
LOG_TEST_GOTO_ERR(card->ctx, r, "Card returned error");
pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info);
pgp_update_card_algorithms(card, key_info);
err:
free(apdu.resp);
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_build_tlv(sc_context_t *ctx, unsigned int tag, u8 *data, size_t len, u8 **out, size_t *outlen)
{
u8 highest_order = 0;
int r;
r = sc_asn1_write_element(ctx, tag, data, len, out, outlen);
LOG_TEST_RET(ctx, r, "Failed to write ASN.1 element");
while ((tag >> 8*highest_order) != 0) {
highest_order++;
}
highest_order--;
if (highest_order < 4)
*out[0] |= (tag >> 8*highest_order);
return SC_SUCCESS;
}
static size_t
set_taglength_tlv(u8 *buffer, unsigned int tag, size_t length)
{
u8 *p = buffer;
assert(tag <= 0xffff);
if (tag > 0xff)
*p++ = (tag >> 8) & 0xFF;
*p++ = tag;
if (length < 128)
*p++ = (u8)length;
else if (length < 256) {
*p++ = 0x81;
*p++ = (u8)length;
}
else {
if (length > 0xffff)
length = 0xffff;
*p++ = 0x82;
*p++ = (length >> 8) & 0xFF;
*p++ = length & 0xFF;
}
return p - buffer;
}
static int
pgp_build_extended_header_list(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info,
u8 **result, size_t *resultlen)
{
sc_context_t *ctx = card->ctx;
const size_t max_prtem_len = 7*(1 + 3);
u8 pritemplate[7*(1 + 3)];
size_t tpl_len = 0;
u8 kdata[3 + 256 + 256 + 512];
size_t kdata_len = 0;
u8 *tlvblock = NULL;
size_t tlvlen = 0;
u8 *tlv_5f48 = NULL;
size_t tlvlen_5f48 = 0;
u8 *tlv_7f48 = NULL;
size_t tlvlen_7f48 = 0;
u8 *data = NULL;
size_t len = 0;
u8 *p = NULL;
u8 *components[4];
size_t componentlens[4];
unsigned int componenttags[4];
char *componentnames[4];
size_t comp_to_add;
u8 i;
int r;
LOG_FUNC_CALLED(ctx);
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
components[0] = key_info->u.rsa.e;
components[1] = key_info->u.rsa.p;
components[2] = key_info->u.rsa.q;
componentlens[0] = key_info->u.rsa.e_len;
componentlens[1] = key_info->u.rsa.p_len;
componentlens[2] = key_info->u.rsa.q_len;
componenttags[0] = 0x91;
componenttags[1] = 0x92;
componenttags[2] = 0x93;
componentnames[0] = "public exponent";
componentnames[1] = "prime p";
componentnames[2] = "prime q";
comp_to_add = 3;
size_t max_e_len_bytes = BYTES4BITS(SC_OPENPGP_MAX_EXP_BITS);
size_t e_len_bytes = BYTES4BITS(key_info->u.rsa.e_len);
if (key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_STDN
|| key_info->u.rsa.keyformat == SC_OPENPGP_KEYFORMAT_RSA_CRTN){
components[3] = key_info->u.rsa.n;
componentlens[3] = key_info->u.rsa.n_len;
componenttags[3] = 0x97;
componentnames[3] = "modulus";
comp_to_add = 4;
}
if (comp_to_add == 4 && (key_info->u.rsa.n == NULL || key_info->u.rsa.n_len == 0)){
sc_log(ctx, "Error: Modulus required!");
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
memset(pritemplate, 0, max_prtem_len);
assert(key_info->u.rsa.e_len <= SC_OPENPGP_MAX_EXP_BITS);
if (key_info->u.rsa.e_len < SC_OPENPGP_MAX_EXP_BITS) {
p = calloc(max_e_len_bytes, 1);
if (!p)
LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
memcpy(p + (max_e_len_bytes - e_len_bytes), key_info->u.rsa.e, e_len_bytes);
free(key_info->u.rsa.e);
key_info->u.rsa.e = p;
components[0] = p;
key_info->u.rsa.e_len = SC_OPENPGP_MAX_EXP_BITS;
componentlens[0] = max_e_len_bytes;
}
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
components[0] = key_info->u.ec.privateD;
componentlens[0] = key_info->u.ec.privateD_len;
componenttags[0] = 0x92;
componentnames[0] = "private key";
comp_to_add = 1;
if ((key_info->u.ec.ecpoint == NULL || key_info->u.ec.ecpoint_len == 0)){
sc_log(ctx, "Error: ecpoint required!");
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
memset(pritemplate, 0, max_prtem_len);
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
p = pritemplate;
for (i = 0; i < comp_to_add; i++) {
sc_log(ctx, "Set Tag+Length for %s (%X).", componentnames[i], componenttags[i]);
len = set_taglength_tlv(p, componenttags[i], componentlens[i]);
tpl_len += len;
memcpy(kdata + kdata_len, components[i], componentlens[i]);
kdata_len += componentlens[i];
p += len;
}
r = pgp_build_tlv(ctx, 0x7F48, pritemplate, tpl_len, &tlv_7f48, &tlvlen_7f48);
LOG_TEST_RET(ctx, r, "Failed to build TLV for 7F48");
tlv_7f48[0] |= 0x7F;
r = pgp_build_tlv(ctx, 0x5f48, kdata, kdata_len, &tlv_5f48, &tlvlen_5f48);
LOG_TEST_GOTO_ERR(ctx, r, "Failed to build TLV for 5F48");
len = 2 + tlvlen_7f48 + tlvlen_5f48;
data = calloc(len, 1);
if (data == NULL)
LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_ENOUGH_MEMORY, "Not enough memory");
switch (key_info->key_id) {
case SC_OPENPGP_KEY_SIGN:
ushort2bebytes(data, DO_SIGN);
break;
case SC_OPENPGP_KEY_ENCR:
ushort2bebytes(data, DO_ENCR);
break;
case SC_OPENPGP_KEY_AUTH:
ushort2bebytes(data, DO_AUTH);
break;
default:
LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
}
memcpy(data + 2, tlv_7f48, tlvlen_7f48);
memcpy(data + 2 + tlvlen_7f48, tlv_5f48, tlvlen_5f48);
r = pgp_build_tlv(ctx, 0x4D, data, len, &tlvblock, &tlvlen);
LOG_TEST_GOTO_ERR(ctx, r, "Cannot build TLV for Extended Header list");
if (result != NULL) {
*result = tlvblock;
*resultlen = tlvlen;
} else {
free(tlvblock);
}
err:
free(data);
free(tlv_5f48);
free(tlv_7f48);
LOG_FUNC_RETURN(ctx, r);
}
static int
pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info)
{
sc_cardctl_openpgp_keygen_info_t pubkey;
u8 *data = NULL;
size_t len = 0;
int r;
LOG_FUNC_CALLED(card->ctx);
if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA
&& card->type < SC_CARD_TYPE_OPENPGP_V3)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
if (key_info->key_id < 1 || key_info->key_id > 3)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
switch (key_info->u.rsa.keyformat) {
case SC_OPENPGP_KEYFORMAT_RSA_STD:
case SC_OPENPGP_KEYFORMAT_RSA_STDN:
break;
case SC_OPENPGP_KEYFORMAT_RSA_CRT:
case SC_OPENPGP_KEYFORMAT_RSA_CRTN:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){
if (key_info->u.rsa.e_len > SC_OPENPGP_MAX_EXP_BITS) {
sc_log(card->ctx,
"Exponent %"SC_FORMAT_LEN_SIZE_T"u-bit (>32) is not supported.",
key_info->u.rsa.e_len);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
memset(&pubkey, 0, sizeof(pubkey));
pubkey.key_id = key_info->key_id;
pubkey.algorithm = key_info->algorithm;
if (key_info->u.rsa.n && key_info->u.rsa.n_len
&& key_info->u.rsa.e && key_info->u.rsa.e_len) {
pubkey.u.rsa.modulus = key_info->u.rsa.n;
pubkey.u.rsa.modulus_len = key_info->u.rsa.n_len;
pubkey.u.rsa.exponent = key_info->u.rsa.e;
pubkey.u.rsa.exponent_len = key_info->u.rsa.e_len;
}
else
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
}
else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH
|| key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){
memset(&pubkey, 0, sizeof(pubkey));
pubkey.key_id = key_info->key_id;
pubkey.algorithm = key_info->algorithm;
if (key_info->u.ec.ecpoint && key_info->u.ec.ecpoint_len){
pubkey.u.ec.ecpoint = key_info->u.ec.ecpoint;
pubkey.u.ec.ecpoint_len = key_info->u.ec.ecpoint_len;
}
else
LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS);
}
else
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
r = pgp_update_new_algo_attr(card, &pubkey);
LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes");
r = pgp_build_extended_header_list(card, key_info, &data, &len);
LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to build Extended Header list");
r = pgp_put_data(card, 0x4D, data, len);
LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to write to DO 004D");
r = pgp_store_creationtime(card, key_info->key_id, &key_info->creationtime);
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
sc_log(card->ctx, "Calculate and store fingerprint");
r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, &pubkey);
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint");
sc_log(card->ctx, "Update blobs holding pubkey info.");
r = pgp_update_pubkey_blob(card, &pubkey);
sc_log(card->ctx, "Update card algorithms");
pgp_update_card_algorithms(card, &pubkey);
err:
free(data);
LOG_FUNC_RETURN(card->ctx, r);
}
#endif
static int
pgp_erase_card(sc_card_t *card)
{
const char *apdu_hex[] = {
"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",
NULL
};
sc_apdu_t apdu;
int i;
int r = SC_SUCCESS;
struct pgp_priv_data *priv = DRVDATA(card);
LOG_FUNC_CALLED(card->ctx);
if ((priv->ext_caps & EXT_CAP_LCS) == 0) {
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
"Card does not offer life cycle management");
}
switch (priv->state) {
case CARD_STATE_ACTIVATED:
for (i = 0; apdu_hex[i] != NULL; i++) {
u8 apdu_bin[25];
size_t apdu_bin_len = sizeof(apdu_bin);
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
r = sc_hex_to_bin(apdu_hex[i], apdu_bin, &apdu_bin_len);
LOG_TEST_RET(card->ctx, r, "Failed to convert APDU bytes");
r = sc_bytes2apdu(card->ctx, apdu_bin, apdu_bin_len, &apdu);
if (r)
LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL,
"Failed to build APDU");
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
sc_log(card->ctx, "Sending APDU%d %s", i, apdu_hex[i]);
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed");
}
case CARD_STATE_INITIALIZATION:
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0, 0);
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "Transmitting APDU failed");
break;
default:
LOG_TEST_RET(card->ctx, SC_ERROR_NO_CARD_SUPPORT,
"Card does not offer life cycle management");
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
int r;
LOG_FUNC_CALLED(card->ctx);
switch(cmd) {
case SC_CARDCTL_GET_SERIALNR:
memmove((sc_serial_number_t *) ptr, &card->serialnr, sizeof(card->serialnr));
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
break;
#ifdef ENABLE_OPENSSL
case SC_CARDCTL_OPENPGP_GENERATE_KEY:
r = pgp_gen_key(card, (sc_cardctl_openpgp_keygen_info_t *) ptr);
LOG_FUNC_RETURN(card->ctx, r);
break;
case SC_CARDCTL_OPENPGP_STORE_KEY:
r = pgp_store_key(card, (sc_cardctl_openpgp_keystore_info_t *) ptr);
LOG_FUNC_RETURN(card->ctx, r);
break;
#endif
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);
}
static int
gnuk_delete_key(sc_card_t *card, u8 key_id)
{
sc_context_t *ctx = card->ctx;
int r = SC_SUCCESS;
u8 data[4] = { 0x4D, 0x02, 0x00, 0x00 };
LOG_FUNC_CALLED(ctx);
if (key_id < 1 || key_id > 3)
LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS,
"Invalid key ID; must be 1, 2, or 3");
sc_log(ctx, "Delete fingerprints");
r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
LOG_TEST_RET(ctx, r, "Failed to delete fingerprints");
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");
sc_log(ctx, "Rewrite Extended Header List");
if (key_id == SC_OPENPGP_KEY_SIGN)
ushort2bebytes(data+2, DO_SIGN);
else if (key_id == SC_OPENPGP_KEY_ENCR)
ushort2bebytes(data+2, DO_ENCR);
else if (key_id == SC_OPENPGP_KEY_AUTH)
ushort2bebytes(data+2, DO_AUTH);
r = pgp_put_data(card, 0x4D, data, sizeof(data));
LOG_FUNC_RETURN(ctx, r);
}
static int
pgp_delete_file(sc_card_t *card, const sc_path_t *path)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob;
sc_file_t *file;
int r;
LOG_FUNC_CALLED(card->ctx);
r = pgp_select_file(card, path, &file);
LOG_TEST_RET(card->ctx, r, "Cannot select file");
blob = priv->current;
if (blob == priv->mf)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
if (card->type != SC_CARD_TYPE_OPENPGP_GNUK &&
(file->id == DO_SIGN_SYM || file->id == DO_ENCR_SYM || file->id == DO_AUTH_SYM)) {
r = SC_SUCCESS;
}
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_SIGN_SYM) {
r = gnuk_delete_key(card, 1);
}
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_ENCR_SYM) {
r = gnuk_delete_key(card, 2);
}
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_AUTH_SYM) {
r = gnuk_delete_key(card, 3);
}
else {
r = pgp_put_data(card, file->id, NULL, 0);
}
priv->current = blob->parent;
LOG_FUNC_RETURN(card->ctx, r);
}
static int
pgp_update_binary(sc_card_t *card, unsigned int idx,
const u8 *buf, size_t count, unsigned long flags)
{
struct pgp_priv_data *priv = DRVDATA(card);
pgp_blob_t *blob = priv->current;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
if (idx > 0)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
if (blob != NULL) {
r = pgp_put_data(card, blob->id, buf, count);
}
LOG_FUNC_RETURN(card->ctx, r);
}
static int pgp_card_reader_lock_obtained(sc_card_t *card, int was_reset)
{
struct pgp_priv_data *priv = DRVDATA(card);
int r = SC_SUCCESS;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (card->flags & SC_CARD_FLAG_KEEP_ALIVE
&& was_reset <= 0
&& priv != NULL && priv->mf && priv->mf->file) {
unsigned char aid[16];
r = sc_get_data(card, 0x004F, aid, sizeof aid);
if ((size_t) r != priv->mf->file->namelen
|| 0 != memcmp(aid, priv->mf->file->name, r)) {
was_reset = 1;
}
r = SC_SUCCESS;
}
if (was_reset > 0) {
sc_file_t *file = NULL;
sc_path_t path;
sc_format_path("D276:0001:2401", &path);
path.type = SC_PATH_TYPE_DF_NAME;
r = iso_ops->select_file(card, &path, &file);
sc_file_free(file);
}
LOG_FUNC_RETURN(card->ctx, r);
}
struct sc_card_driver *
sc_get_openpgp_driver(void)
{
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
iso_ops = iso_drv->ops;
pgp_ops = *iso_ops;
pgp_ops.match_card = pgp_match_card;
pgp_ops.init = pgp_init;
pgp_ops.finish = pgp_finish;
pgp_ops.select_file = pgp_select_file;
pgp_ops.list_files = pgp_list_files;
pgp_ops.get_challenge = pgp_get_challenge;
pgp_ops.read_binary = pgp_read_binary;
pgp_ops.write_binary = pgp_write_binary;
pgp_ops.pin_cmd = pgp_pin_cmd;
pgp_ops.logout = pgp_logout;
pgp_ops.get_data = pgp_get_data;
pgp_ops.put_data = pgp_put_data;
pgp_ops.set_security_env= pgp_set_security_env;
pgp_ops.compute_signature= pgp_compute_signature;
pgp_ops.decipher = pgp_decipher;
pgp_ops.card_ctl = pgp_card_ctl;
pgp_ops.delete_file = pgp_delete_file;
pgp_ops.update_binary = pgp_update_binary;
pgp_ops.card_reader_lock_obtained = pgp_card_reader_lock_obtained;
return &pgp_drv;
}