opensc/src/libopensc/card-openpgp.c

2520 lines
76 KiB
C

/*
* card-openpgp.c: Support for OpenPGP card
*
* Copyright (C) 2003 Olaf Kirch <okir@suse.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Specifications:
* http://www.g10code.de/docs/openpgp-card-1.0.pdf (obsolete)
* http://www.g10code.de/docs/openpgp-card-1.1.pdf
* http://www.g10code.de/docs/openpgp-card-2.0.pdf
*/
#include "config.h"
#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 /* ENABLE_OPENSSL */
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 }
};
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
};
/*
* The OpenPGP card doesn't have a file system, instead everything
* is stored in data objects that are accessed through GET/PUT.
*
* However, much inside OpenSC's pkcs15 implementation is based on
* the assumption that we have a file system. So we fake one here.
*
* Selecting the MF causes us to select the OpenPGP AID.
*
* Everything else is mapped to "file" IDs.
*/
enum _type { /* DO type */
SIMPLE = SC_FILE_TYPE_WORKING_EF,
CONSTRUCTED = SC_FILE_TYPE_DF
};
enum _version { /* 2-byte BCD-alike encoded version number */
OPENPGP_CARD_1_0 = 0x0100,
OPENPGP_CARD_1_1 = 0x0101,
OPENPGP_CARD_2_0 = 0x0200
};
enum _access { /* access flags for the respective DO/file */
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 { /* extended capabilities/features */
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_CHAINING = 0x1000,
EXT_CAP_APDU_EXT = 0x2000
};
enum _card_state {
CARD_STATE_UNKNOWN = 0x00,
CARD_STATE_INITIALIZATION = 0x03,
CARD_STATE_ACTIVATED = 0x05
};
struct blob {
struct blob * next; /* pointer to next sibling */
struct blob * parent; /* pointer to parent */
struct do_info *info;
sc_file_t * file;
unsigned int id;
int status;
unsigned char * data;
unsigned int len;
struct blob * files; /* pointer to 1st child */
};
struct do_info {
unsigned int id; /* ID of the DO in question */
enum _type type; /* constructed DO or not */
enum _access access; /* R/W access levels for the DO */
/* function to get the DO from the card:
* only != NULL is DO if readable and not only a part of a constructed DO */
int (*get_fn)(sc_card_t *, unsigned int, u8 *, size_t);
/* function to write the DO to the card:
* only != NULL if DO is writeable under some conditions */
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(struct blob *, int, void (*func)());
static int pgp_get_blob(sc_card_t *card, struct blob *blob,
unsigned int id, struct blob **ret);
static struct blob * pgp_new_blob(sc_card_t *, struct blob *, unsigned int, sc_file_t *);
static void pgp_free_blob(struct blob *);
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);
static struct do_info pgp1_objects[] = { /* OpenPGP card spec 1.1 */
{ 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 },
{ 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0, 0, 0, NULL, NULL },
};
static struct do_info pgp2_objects[] = { /* OpenPGP card spec 2.0 */
{ 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 },
/* The 7F21 is constructed DO in spec, but in practice, its content can be retrieved
* as simple DO (no need to parse TLV). */
{ 0x7f21, 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 },
{ 0xa400, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
/* The 0xA401, 0xB601, 0xB801 are just symbolic, it does not represent any real DO.
* However, their R/W access condition may block the process of importing key in pkcs15init.
* So we set their accesses condition as WRITE_PIN3 (writable). */
{ 0xa401, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0xb600, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ 0xb601, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0xb800, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL },
{ 0xb801, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL },
{ 0, 0, 0, NULL, NULL },
};
/* 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
/* 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 {
struct blob * mf;
struct blob * current; /* currently selected file */
enum _version bcd_version;
struct do_info *pgp_objects;
enum _card_state state; /* card state */
enum _ext_caps ext_caps; /* extended capabilities */
size_t max_challenge_size;
size_t max_cert_size;
sc_security_env_t sec_env;
};
/* ABI: check if card's ATR matches one of driver's */
static int
pgp_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, pgp_atrs, &card->type);
if (i >= 0) {
card->name = pgp_atrs[i].name;
return 1;
}
return 0;
}
/* ABI: initialize driver */
static int
pgp_init(sc_card_t *card)
{
struct pgp_priv_data *priv;
sc_path_t aid;
sc_file_t *file = NULL;
struct do_info *info;
int r;
struct blob *child = NULL;
LOG_FUNC_CALLED(card->ctx);
priv = calloc (1, sizeof *priv);
if (!priv)
return SC_ERROR_OUT_OF_MEMORY;
card->drv_data = priv;
card->cla = 0x00;
/* set pointer to correct list of card objects */
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 || card->type == SC_CARD_TYPE_OPENPGP_GNUK)
? OPENPGP_CARD_2_0 : OPENPGP_CARD_1_1;
/* select application "OpenPGP" */
sc_format_path("D276:0001:2401", &aid);
aid.type = SC_PATH_TYPE_DF_NAME;
if ((r = iso_ops->select_file(card, &aid, &file)) < 0) {
pgp_finish(card);
return r;
}
/* read information from AID */
if (file && file->namelen == 16) {
/* OpenPGP card spec 1.1 & 2.0, section 4.2.1 & 4.1.2.1 */
priv->bcd_version = bebytes2ushort(file->name + 6);
/* kludge: get card's serial number from manufacturer ID + serial number */
memcpy(card->serialnr.value, file->name + 8, 6);
card->serialnr.len = 6;
}
/* change file path to MF for re-use in MF */
sc_format_path("3f00", &file->path);
/* set up the root of our fake file tree */
priv->mf = pgp_new_blob(card, NULL, 0x3f00, file);
if (!priv->mf) {
pgp_finish(card);
return SC_ERROR_OUT_OF_MEMORY;
}
/* select MF */
priv->current = priv->mf;
/* 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_NEVER) &&
(info->get_fn != NULL)) {
child = pgp_new_blob(card, priv->mf, info->id, sc_file_new());
/* catch out of memory condition */
if (child == NULL) {
pgp_finish(card);
return SC_ERROR_OUT_OF_MEMORY;
}
}
}
/* get card_features from ATR & DOs */
pgp_get_card_features(card);
return SC_SUCCESS;
}
/* internal: get features of the card: capabilities, ... */
static int
pgp_get_card_features(sc_card_t *card)
{
struct pgp_priv_data *priv = DRVDATA (card);
unsigned char *hist_bytes = card->atr.value;
size_t atr_len = card->atr.len;
size_t i = 0;
struct blob *blob, *blob6e, *blob73;
/* parse card capabilities from historical bytes */
while ((i < atr_len) && (hist_bytes[i] != 0x73))
i++;
/* IS07816-4 hist bytes 3rd function table */
if ((hist_bytes[i] == 0x73) && (atr_len > i+3)) {
/* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */
if (hist_bytes[i+3] & 0x40) {
card->caps |= SC_CARD_CAP_APDU_EXT;
priv->ext_caps |= EXT_CAP_APDU_EXT;
}
/* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */
if (hist_bytes[i+3] & 0x80)
priv->ext_caps |= EXT_CAP_CHAINING;
}
if (priv->bcd_version >= OPENPGP_CARD_2_0) {
/* get card capabilities from "historical bytes" DO */
if ((pgp_get_blob(card, priv->mf, 0x5f52, &blob) >= 0) &&
(blob->data != NULL) && (blob->data[0] == 0x00)) {
while ((i < blob->len) && (blob->data[i] != 0x73))
i++;
/* IS07816-4 hist bytes 3rd function table */
if ((blob->data[i] == 0x73) && (blob->len > i+3)) {
/* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */
if (blob->data[i+3] & 0x40) {
card->caps |= SC_CARD_CAP_APDU_EXT;
priv->ext_caps |= EXT_CAP_APDU_EXT;
}
/* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */
if (hist_bytes[i+3] & 0x80)
priv->ext_caps |= EXT_CAP_CHAINING;
}
/* get card status from historical bytes status indicator */
if ((blob->data[0] == 0x00) && (blob->len >= 4))
priv->state = blob->data[blob->len-3];
}
}
if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) &&
(pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) {
/* get "extended capabilities" DO */
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 || 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)
priv->ext_caps |= EXT_CAP_PRIVATE_DO;
/* bit 0x10 in first byte means "support for CHV status byte changeable" */
if (blob->data[0] & 0x10)
priv->ext_caps |= EXT_CAP_C4_CHANGEABLE;
/* bit 0x20 in first byte means "support for Key Import" */
if (blob->data[0] & 0x20)
priv->ext_caps |= EXT_CAP_KEY_IMPORT;
/* bit 0x40 in first byte means "support for Get Challenge" */
if (blob->data[0] & 0x40) {
card->caps |= SC_CARD_CAP_RNG;
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 || card->type == SC_CARD_TYPE_OPENPGP_GNUK))
priv->ext_caps |= EXT_CAP_SM;
if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) {
/* max. challenge size is at bytes 3-4 */
priv->max_challenge_size = bebytes2ushort(blob->data + 2);
/* max. cert size it at bytes 5-6 */
priv->max_cert_size = bebytes2ushort(blob->data + 4);
/* max. send/receive sizes are at bytes 7-8 resp. 9-10 */
card->max_send_size = bebytes2ushort(blob->data + 6);
card->max_recv_size = bebytes2ushort(blob->data + 8);
}
}
/* get max. PIN length from "CHV status bytes" DO */
if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) &&
(blob->data != NULL) && (blob->len > 1)) {
/* 2nd byte in "CHV status bytes" DO means "max. PIN length" */
card->max_pin_len = blob->data[1];
}
/* get supported algorithms & key lengths from "algorithm attributes" DOs */
for (i = 0x00c1; i <= 0x00c3; i++) {
unsigned long flags;
/* Is this correct? */
/* OpenPGP card spec 1.1 & 2.0, section 2.1 */
flags = SC_ALGORITHM_RSA_RAW;
/* OpenPGP card spec 1.1 & 2.0, section 7.2.9 & 7.2.10 */
flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
flags |= SC_ALGORITHM_RSA_HASH_NONE;
/* Can be generated in card */
flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;
if ((pgp_get_blob(card, blob73, i, &blob) >= 0) &&
(blob->data != NULL) && (blob->len >= 4)) {
if (blob->data[0] == 0x01) { /* Algorithm ID [RFC4880]: RSA */
unsigned int keylen = bebytes2ushort(blob->data + 1); /* Measured in bit */
_sc_card_add_rsa_alg(card, keylen, flags, 0);
}
}
}
}
return SC_SUCCESS;
}
/* ABI: terminate driver */
static int
pgp_finish(sc_card_t *card)
{
if (card != NULL) {
struct pgp_priv_data *priv = DRVDATA (card);
if (priv != NULL) {
/* delete fake file hierarchy */
pgp_iterate_blobs(priv->mf, 99, pgp_free_blob);
/* delete private data */
free(priv);
}
card->drv_data = NULL;
}
return SC_SUCCESS;
}
/* internal: fill a blob's data */
static int
pgp_set_blob(struct blob *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 = len;
if (data != NULL)
memcpy(blob->data, data, len);
}
if (blob->file)
blob->file->size = len;
return SC_SUCCESS;
}
/**
* Internal: Implement Access Control List for emulated file.
* The Access Control is derived from the DO access permission.
**/
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;
/* Write access */
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 {
/* When SC_AC_OP_DELETE is absent, we need to provide
* SC_AC_OP_DELETE_SELF for sc_pkcs15init_delete_by_path() */
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;
/* Read access */
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);
}
}
/* internal: append a blob to the list of children of a given parent blob */
static struct blob *
pgp_new_blob(sc_card_t *card, struct blob *parent, unsigned int file_id,
sc_file_t *file)
{
struct blob *blob = NULL;
if (file == NULL)
return NULL;
if ((blob = calloc(1, sizeof(struct blob))) != NULL) {
struct pgp_priv_data *priv = DRVDATA (card);
struct do_info *info;
blob->file = file;
blob->file->type = SC_FILE_TYPE_WORKING_EF; /* default */
blob->file->ef_structure = SC_FILE_EF_TRANSPARENT;
blob->file->id = file_id;
blob->id = file_id;
blob->parent = parent;
if (parent != NULL) {
struct blob **p;
/* set file's path = parent's path + file's id */
blob->file->path = parent->file->path;
sc_append_file_id(&blob->file->path, file_id);
/* append blob to list of parent's children */
for (p = &parent->files; *p != NULL; p = &(*p)->next)
;
*p = blob;
}
else {
u8 id_str[2];
/* no parent: set file's path = file's id */
/* FIXME sc_format_path expects an hex string of an file
* identifier. ushort2bebytes instead delivers a two bytes binary
* string */
sc_format_path((char *) ushort2bebytes(id_str, file_id), &blob->file->path);
}
/* find matching DO info: set file type depending on it */
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;
}
/* internal: free a blob including its content */
static void
pgp_free_blob(struct blob *blob)
{
if (blob) {
if (blob->parent) {
struct blob **p;
/* remove blob from list of parent's children */
for (p = &blob->parent->files; *p != NULL && *p != blob; p = &(*p)->next)
;
if (*p == blob)
*p = blob->next;
}
if (blob->file)
sc_file_free(blob->file);
if (blob->data)
free(blob->data);
free(blob);
}
}
/* internal: iterate through the blob tree, calling a function for each blob */
static void
pgp_iterate_blobs(struct blob *blob, int level, void (*func)())
{
if (blob) {
if (level > 0) {
struct blob *child = blob->files;
while (child != NULL) {
struct blob *next = child->next;
pgp_iterate_blobs(child, level-1, func);
child = next;
}
}
func(blob);
}
}
/* internal: read a blob's contents from card */
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)
return blob->status;
if (blob->info->get_fn) { /* readable, top-level DO */
u8 buffer[2048];
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
|| 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 */
blob->status = r;
return r;
}
return pgp_set_blob(blob, buffer, r);
}
else { /* un-readable DO or part of a constructed DO */
return SC_SUCCESS;
}
}
/*
* internal: Enumerate contents of a data blob.
* The OpenPGP card has a TLV encoding according ASN.1 BER-encoding rules.
*/
static int
pgp_enumerate_blob(sc_card_t *card, struct blob *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;
struct blob *new;
r = sc_asn1_read_tag(&data, blob->len - (in - blob->data),
&cla, &tag, &len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Unexpected end of contents\n");
return SC_ERROR_OBJECT_NOT_VALID;
}
/* undo ASN1's split of tag & class */
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
cla <<= 8;
}
tag |= cla;
/* create fake file system hierarchy by
* using constructed DOs as DF */
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;
}
/* internal: find a blob by ID below a given parent, filling its contents when necessary */
static int
pgp_get_blob(sc_card_t *card, struct blob *blob, unsigned int id,
struct blob **ret)
{
struct blob *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;
}
}
/* 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;
}
/* Internal: search recursively for a blob by ID below a given root */
static int
pgp_seek_blob(sc_card_t *card, struct blob *root, unsigned int id,
struct blob **ret)
{
struct blob *child;
int r;
if ((r = pgp_get_blob(card, root, id, ret)) == 0)
/* The sought blob is right under root */
return r;
/* Not found, seek deeper */
for (child = root->files; child; child = child->next) {
/* The DO of SIMPLE type or the DO holding certificate
* does not contain children */
if (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;
}
/* internal: find a blob by tag - pgp_seek_blob with optimizations */
static struct blob *
pgp_find_blob(sc_card_t *card, unsigned int tag)
{
struct pgp_priv_data *priv = DRVDATA(card);
struct blob *blob = NULL;
int r;
/* Check if current selected blob is which we want to test*/
if (priv->current->id == tag) {
return priv->current;
}
/* Look for the blob representing the DO */
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;
}
/* Internal: get info for a specific tag */
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;
}
/**
* Strip out the parts of PKCS15 file layout in the path. Get the reduced version
* which is understood by the OpenPGP card driver.
* Return the index whose preceding part will be ignored.
**/
static unsigned int pgp_strip_path(sc_card_t *card, const sc_path_t *path)
{
unsigned int start_point = 0;
/* start_point will move through the path string */
if (path->value == NULL || path->len == 0)
return 0;
/* Ignore 3F00 (MF) at the beginning */
start_point = (memcmp(path->value, "\x3f\x00", 2) == 0) ? 2 : 0;
/* Strip path of PKCS15-AppDF (5015) */
start_point += (memcmp(path->value + start_point, "\x50\x15", 2) == 0) ? 2 : 0;
return start_point;
}
/* ABI: SELECT FILE */
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);
struct blob *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");
/* Due to pkcs15init implemetation, sometimes a file at path "11001101"
* need to be written (1 use case is when importing key&cert from p12 file).
* This file does not exist in OpenPGP but pkcs15 requires that
* writing this file must be successfully.
* So, we pretend that selecting & writing this file is successful.
* The "11001101"is defined in sc_pkcs15emu_get_df() function, pkcs15-sync.c file. */
sc_format_path("11001101", &dummy_path);
if (sc_compare_path(path, &dummy_path)) {
if (ret != NULL) {
*ret = sc_file_new();
/* One use case of this dummy file is after writing certificate in pkcs15init.
* So we set its size to be the same as max certificate size the card supports. */
(*ret)->size = priv->max_cert_size;
}
priv->current = NULL;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/* ignore explicitely mentioned MF at the path's beginning */
path_start = pgp_strip_path(card, path);
/* starting with the MF ... */
blob = priv->mf;
/* ... recurse through the tree following the path */
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);
/* This file ID is refered when importing key&certificate via pkcs15init, like above.
* We pretend to successfully find this inexistent file. */
if (id == 0x4402 || id == 0x5f48) {
priv->current = NULL;
if (ret == NULL)
/* No need to return file */
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
/* Else, need to return file */
*ret = sc_file_new();
(*ret)->size = priv->max_cert_size;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
if (r < 0) { /* failure */
priv->current = NULL;
LOG_FUNC_RETURN(card->ctx, r);
}
}
/* success: select file = set "current" pointer to blob found */
priv->current = blob;
if (ret)
sc_file_dup(ret, blob->file);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/* ABI: LIST FILES */
static int
pgp_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
struct pgp_priv_data *priv = DRVDATA(card);
struct blob *blob;
unsigned int k;
int r;
LOG_FUNC_CALLED(card->ctx);
/* jump to selected file */
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);
}
/* ABI: READ BINARY */
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);
struct blob *blob;
int r;
LOG_FUNC_CALLED(card->ctx);
/* jump to selected file */
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, count);
}
/* ABI: WRITE BINARY */
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);
}
/* internal: get public key from card: as DF + sub-wEFs */
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);
/* 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;
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, apdu.resplen);
}
/* internal: get public key from card: as one wEF */
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);
struct blob *blob, *mod_blob, *exp_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
|| (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)
LOG_TEST_RET(card->ctx, r, "error getting elements");
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;
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, len);
}
/* ABI: GET DATA */
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);
/* 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);
}
/* 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);
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(ctx);
/* Extended Header list (004D DO) needs a variant of PUT DATA command */
if (tag == 0x004D) {
ins = 0xDB;
p1 = 0x3F;
p2 = 0xFF;
}
/* Build APDU */
if (buf != NULL && buf_len > 0) {
/* 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 = (u8 *)buf;
apdu.datalen = 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(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.");
}
LOG_TEST_RET(card->ctx, r, "PUT DATA returned error");
if (affected_blob) {
/* Update the corresponding file */
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);
/* pgp_set_blob()'s failures do not impact pgp_put_data()'s result */
}
LOG_FUNC_RETURN(card->ctx, buf_len);
}
/* ABI: PIN cmd: verify/change/unblock a PIN */
static int
pgp_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
LOG_FUNC_CALLED(card->ctx);
if (data->pin_type != SC_AC_CHV)
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"invalid PIN type");
/* In general, the PIN Reference is extracted from the key-id, for
* example, CHV0 -> Ref=0, CHV1 -> Ref=1.
* However, in the case of OpenGPG, the PIN Ref to compose APDU
* must be 81, 82, 83.
* So, if we receive Ref=1, Ref=2, we must convert to 81, 82...
* In OpenPGP ver 1, the PINs are named CHV1, CHV2, CHV3. In ver 2, they
* are named PW1, PW3 (PW1 operates in 2 modes). However, the PIN references (P2 in APDU)
* are the same between 2 version:
* 81 (CHV1 or PW1), 82 (CHV2 or PW1-mode 2), 83 (CHV3 or PW3).
*
* Note that if this function is called from sc_pkcs15_verify_pin() in pkcs15-pin.c,
* the Ref is already 81, 82, 83.
*/
/* Convert the PIN Reference if needed */
data->pin_reference |= 0x80;
/* Ensure pin_reference is 81, 82, 83 */
if (!(data->pin_reference == 0x81 || data->pin_reference == 0x82 || data->pin_reference == 0x83)) {
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"key-id should be 1, 2, 3.");
}
LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left));
}
/* ABI: set security environment */
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))
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.");
/* We allow key ref 2 (auth key) to be used for deciphering */
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);
}
/* ABI: COMPUTE DIGITAL SIGNATURE */
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 = 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");
/* 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, apdu_case, 0x2A, 0x9E, 0x9A);
break;
case 0x02: /* authentication key */
/* INTERNAL AUTHENTICATE */
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");
}
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, apdu.resplen);
}
/* ABI: DECIPHER */
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);
/* There's some funny padding indicator that must be
* prepended... hmm. */
if (!(temp = malloc(inlen + 1)))
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
temp[0] = '\0';
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: /* Decryption key */
case 0x02: /* authentication key */
/* PSO DECIPHER */
sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86);
break;
case 0x00: /* signature key */
default:
free(temp);
LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS,
"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 = (u8 *)in;
apdu.datalen = inlen;
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);
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");
LOG_FUNC_RETURN(card->ctx, apdu.resplen);
}
#ifdef ENABLE_OPENSSL
/**
* Internal: Update algorithm attribute for new key size (before generating key).
**/
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);
struct blob *algo_blob;
unsigned int old_modulus_len; /* Measured in bit */
unsigned int old_exponent_len;
const unsigned int tag = 0x00C0 | key_info->keytype;
u8 changed = 0;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
/* Get old algorithm attributes */
r = pgp_seek_blob(card, priv->mf, (0x00C0 | key_info->keytype), &algo_blob);
LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes");
old_modulus_len = bebytes2ushort(algo_blob->data + 1); /* The modulus length is coded in byte 2 & 3 */
sc_log(card->ctx, "Old modulus length %d, new %d.", old_modulus_len, key_info->modulus_len);
old_exponent_len = bebytes2ushort(algo_blob->data + 3); /* The exponent length is coded in byte 3 & 4 */
sc_log(card->ctx, "Old exponent length %d, new %d.", old_exponent_len, key_info->exponent_len);
/* Modulus */
/* If passed modulus_len is zero, it means using old key size */
if (key_info->modulus_len == 0) {
sc_log(card->ctx, "Use old modulus length (%d).", old_modulus_len);
key_info->modulus_len = old_modulus_len;
}
/* To generate key with new key size */
else if (old_modulus_len != key_info->modulus_len) {
algo_blob->data[1] = key_info->modulus_len >> 8;
algo_blob->data[2] = key_info->modulus_len;
changed = 1;
}
/* Exponent */
if (key_info->exponent_len == 0) {
sc_log(card->ctx, "Use old exponent length (%d).", old_exponent_len);
key_info->exponent_len = old_exponent_len;
}
else if (old_exponent_len != key_info->exponent_len) {
algo_blob->data[3] = key_info->exponent_len >> 8;
algo_blob->data[4] = key_info->exponent_len;
changed = 1;
}
/* If to-be-generated key has different size, we will set this new value for
* GENERATE ASYMMETRIC KEY PAIR to work */
if (changed) {
r = pgp_put_data(card, tag, algo_blob->data, 6);
/* Note: Don't use pgp_set_blob to set data, because it won't touch the real DO */
LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes");
}
LOG_FUNC_RETURN(card->ctx, r);
}
/**
* Internal: Store creation time of key.
* Pass non-zero outtime to use predefined time.
* Pass zero/null outtime to calculate current time. outtime then will be output.
* Pass null outtime to not receive output.
**/
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];
LOG_FUNC_CALLED(card->ctx);
if (key_id == 0 || key_id > 3) {
sc_log(card->ctx, "Invalid key ID %d.", key_id);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA);
}
if (outtime != NULL && *outtime != 0)
createtime = *outtime;
else if (outtime != NULL)
/* Set output */
*outtime = createtime = time(NULL);
strftime(timestring, timestrlen, "%c %Z", gmtime(&createtime));
sc_log(card->ctx, "Creation time %s.", timestring);
/* Code borrowed from GnuPG */
ulong2bebytes(buf, 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);
}
/**
* Internal: Calculate PGP fingerprints.
* Reference: GnuPG, app-openpgp.c.
* modulus and exponent are passed separately from key_info
* because key_info->exponent may be null.
**/
static int
pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime,
u8* modulus, u8* exponent,
sc_cardctl_openpgp_keygen_info_t *key_info)
{
u8 fingerprint[SHA_DIGEST_LENGTH];
size_t mlen = key_info->modulus_len >> 3; /* 1/8 */
size_t elen = key_info->exponent_len >> 3; /* 1/8 */
u8 *fp_buffer = NULL; /* Fingerprint buffer, not hashed */
size_t fp_buffer_len;
u8 *p; /* Use this pointer to set fp_buffer content */
size_t pk_packet_len;
unsigned int tag;
struct blob *fpseq_blob;
u8 *newdata;
int r;
LOG_FUNC_CALLED(card->ctx);
if (modulus == NULL || exponent == NULL || mlen == 0 || elen == 0) {
sc_log(card->ctx, "Null data (modulus or exponent)");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* http://tools.ietf.org/html/rfc4880 page 41, 72 */
pk_packet_len = 1 /* For ver number */
+ 4 /* Creation time */
+ 1 /* Algorithm */
+ 2 /* Algorithm-specific fields */
+ mlen
+ 2
+ elen;
fp_buffer_len = 3 + pk_packet_len;
p = fp_buffer = calloc(fp_buffer_len, 1);
if (!p) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
}
p[0] = 0x99; /* http://tools.ietf.org/html/rfc4880 page 71 */
ushort2bebytes(++p, pk_packet_len);
/* Start pk_packet */
p += 2;
*p = 4; /* Version 4 key */
ulong2bebytes(++p, ctime); /* Creation time */
p += 4;
*p = 1; /* RSA */
/* Algorithm-specific fields */
ushort2bebytes(++p, key_info->modulus_len);
p += 2;
memcpy(p, modulus, mlen);
p += mlen;
ushort2bebytes(++p, key_info->exponent_len);
p += 2;
memcpy(p, exponent, elen);
p = NULL;
/* Hash with SHA-1 */
SHA1(fp_buffer, fp_buffer_len, fingerprint);
free(fp_buffer);
/* Store to DO */
tag = 0x00C6 + key_info->keytype;
sc_log(card->ctx, "Write to DO %04X.", tag);
r = pgp_put_data(card, 0x00C6 + key_info->keytype, fingerprint, SHA_DIGEST_LENGTH);
LOG_TEST_RET(card->ctx, r, "Cannot write to DO.");
/* Update the blob containing fingerprints (00C5) */
sc_log(card->ctx, "Update the blob containing fingerprints (00C5)");
fpseq_blob = pgp_find_blob(card, 0x00C5);
if (!fpseq_blob) {
sc_log(card->ctx, "Not found 00C5");
goto exit;
}
/* Save the fingerprints sequence */
newdata = malloc(fpseq_blob->len);
if (!newdata) {
sc_log(card->ctx, "Not enough memory to update fingerprints blob.");
goto exit;
}
memcpy(newdata, fpseq_blob->data, fpseq_blob->len);
/* Move p to the portion holding the fingerprint of the current key */
p = newdata + 20*(key_info->keytype - 1);
/* Copy new fingerprint value */
memcpy(p, fingerprint, 20);
/* Set blob's data */
pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len);
free(newdata);
exit:
LOG_FUNC_RETURN(card->ctx, r);
}
/**
* Internal: Update pubkey blob.
* Note that modulus_len, exponent_len is measured in bit.
**/
static int
pgp_update_pubkey_blob(sc_card_t *card, u8* modulus, size_t modulus_len,
u8* exponent, size_t exponent_len, u8 key_id)
{
struct pgp_priv_data *priv = DRVDATA(card);
struct blob *pk_blob;
unsigned int blob_id;
sc_pkcs15_pubkey_t pubkey;
u8 *data = NULL;
size_t len;
int r;
LOG_FUNC_CALLED(card->ctx);
if (key_id == SC_OPENPGP_KEY_SIGN)
blob_id = 0xB601;
else if (key_id == SC_OPENPGP_KEY_ENCR)
blob_id = 0xB801;
else if (key_id == SC_OPENPGP_KEY_AUTH)
blob_id = 0xA401;
else {
sc_log(card->ctx, "Unknown key id %X.", key_id);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
sc_log(card->ctx, "Get the blob %X.", blob_id);
r = pgp_get_blob(card, priv->mf, blob_id, &pk_blob);
LOG_TEST_RET(card->ctx, r, "Cannot get the blob.");
/* Encode pubkey */
memset(&pubkey, 0, sizeof(pubkey));
pubkey.algorithm = SC_ALGORITHM_RSA;
pubkey.u.rsa.modulus.data = modulus;
pubkey.u.rsa.modulus.len = modulus_len >> 3; /* 1/8 */
pubkey.u.rsa.exponent.data = exponent;
pubkey.u.rsa.exponent.len = exponent_len >> 3;
r = sc_pkcs15_encode_pubkey(card->ctx, &pubkey, &data, &len);
sc_log(card->ctx, "Update blob content.");
r = pgp_set_blob(pk_blob, data, len);
LOG_TEST_RET(card->ctx, r, "Cannot update blob content.");
LOG_FUNC_RETURN(card->ctx, r);
}
/**
* Internal: Parse response data and set output
**/
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;
u8 *modulus = NULL;
u8 *exponent = NULL;
int r;
LOG_FUNC_CALLED(card->ctx);
/* Store creation time */
r = pgp_store_creationtime(card, key_info->keytype, &ctime);
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
/* Parse response. Ref: pgp_enumerate_blob() */
while (data_len > (size_t) (in - data)) {
unsigned int cla, tag, tmptag;
size_t len;
u8 *part = in;
/* Parse TLV structure */
r = sc_asn1_read_tag((const u8**)&part,
data_len - (in - data),
&cla, &tag, &len);
LOG_TEST_RET(card->ctx, r, "Unexpected end of contents.");
/* Undo ASN1's split of tag & class */
for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) {
cla <<= 8;
}
tag |= cla;
if (tag == 0x0081) {
/* Set the output data */
if (key_info->modulus) {
memcpy(key_info->modulus, part, len);
}
/* Always set output for modulus_len */
key_info->modulus_len = len*8;
/* Remember the modulus to calculate fingerprint later */
modulus = part;
}
else if (tag == 0x0082) {
/* Set the output data */
if (key_info->exponent) {
memcpy(key_info->exponent, part, len);
}
/* Always set output for exponent_len */
key_info->exponent_len = len*8;
/* Remember the exponent to calculate fingerprint later */
exponent = part;
}
/* Go to next part to parse */
/* This will be different from pgp_enumerate_blob() a bit */
in = part + ((tag != 0x7F49) ? len : 0);
}
/* Calculate and store fingerprint */
sc_log(card->ctx, "Calculate and store fingerprint");
r = pgp_calculate_and_store_fingerprint(card, ctime, modulus, exponent, key_info);
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint.");
/* Update pubkey blobs (B601,B801, A401) */
sc_log(card->ctx, "Update blobs holding pubkey info.");
r = pgp_update_pubkey_blob(card, modulus, key_info->modulus_len,
exponent, key_info->exponent_len, key_info->keytype);
LOG_FUNC_RETURN(card->ctx, r);
}
/**
* Internal: Update card->algorithms
*/
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->keytype;
LOG_FUNC_CALLED(card->ctx);
if (id > card->algorithm_count) {
sc_log(card->ctx, "This key ID %d is out of the card's algorithm list.");
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* Get the algorithm corresponding to the key ID */
algo = card->algorithms + (id - 1);
/* Update new key length attribute */
algo->key_length = key_info->modulus_len;
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
/**
* Generate key.
* Set key_info->modulus_len to zero if want to use old key size.
* Similarly for exponent length.
* key_info->modulus_len and key_info->exponent_len will be returned with new values.
**/
static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
sc_apdu_t apdu;
/* Temporary variables to hold APDU params */
u8 apdu_case;
u8 *apdu_data;
size_t apdu_le;
size_t resplen = 0;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
/* FIXME the compilers doesn't assure that the buffers set here as
* apdu_data are present until the end of the function */
/* Set Control Reference Template for key */
if (key_info->keytype == SC_OPENPGP_KEY_SIGN)
apdu_data = (unsigned char *) "\xb6";
/* As a string, apdu_data will end with '\0' (B6 00) */
else if (key_info->keytype == SC_OPENPGP_KEY_ENCR)
apdu_data = (unsigned char *) "\xb8";
else if (key_info->keytype == SC_OPENPGP_KEY_AUTH)
apdu_data = (unsigned char *) "\xa4";
else {
sc_log(card->ctx, "Unknown key type %X.", key_info->keytype);
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");
/* 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
&& 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;
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;
}
/* Prepare APDU */
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.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);
}
/* Send */
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.");
if (r < 0) {
sc_log(card->ctx, "APDU transmit failed. Error %s.", sc_strerror(r));
goto finish;
}
/* Check response */
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
/* 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.");
goto finish;
}
/* Parse response data and set output */
pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info);
pgp_update_card_algorithms(card, key_info);
finish:
free(apdu.resp);
LOG_FUNC_RETURN(card->ctx, r);
}
/**
* Internal: Build TLV.
* @param[in] data The data ("value") part to build TLV.
* @param[in] len Data length
* @param[out] out The buffer of overall TLV. This buffer should be freed later.
* @param[out] outlen The length of buffer out.
**/
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;
u8 cla;
int r;
r = sc_asn1_write_element(ctx, tag, data, len, out, outlen);
LOG_TEST_RET(ctx, r, "Failed to write ASN.1 element");
/* Restore class bits stripped by sc_asn1_write_element */
/* Determine the left most byte of tag, which contains class bits */
while (tag >> 8*highest_order) {
highest_order++;
}
highest_order--;
cla = tag >> 8*highest_order;
/* Restore class bits */
*out[0] |= cla;
return SC_SUCCESS;
}
/**
* Internal: Set Tag & Length components for TLV, store them in buffer.
* Return the total length of Tag + Length.
* Note that the Value components is not counted.
* Ref: add_tlv() of GnuPG code.
**/
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++ = length;
else if (length < 256) {
*p++ = 0x81;
*p++ = length;
}
else {
if (length > 0xffff)
length = 0xffff;
*p++ = 0x82;
*p++ = (length >> 8) & 0xFF;
*p++ = length & 0xFF;
}
return p - buffer;
}
/**
* Internal: Build Extended Header list (sec 4.3.3.7 - OpenPGP card spec v.2)
**/
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;
/* The Cardholder private key template (7F48) part */
const size_t max_prtem_len = 7*(1 + 3); /* 7 components */
/* 1 for tag name (91, 92... 97)
* 3 for storing length */
u8 pritemplate[7*(1 + 3)];
size_t tpl_len = 0; /* Actual size of pritemplate */
/* Concatenation of key data */
u8 kdata[3 + 256 + 256 + 512]; /* Exponent is stored in 3 bytes
* With maximum 4096-bit key,
* p and q can be stored in 256 bytes (2048 bits).
* Maximum 4096-bit modulus is stored in 512 bytes */
size_t kdata_len = 0; /* Actual size of kdata */
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[] = {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, 0x97};
char *componentnames[] = {
"public exponent",
"prime p",
"prime q",
"modulus"
};
size_t comp_to_add = 3;
size_t req_e_len = 0; /* The exponent length specified in Algorithm Attributes */
struct blob *alat_blob;
u8 i;
int r;
LOG_FUNC_CALLED(ctx);
if (key_info->keyformat == SC_OPENPGP_KEYFORMAT_STDN
|| key_info->keyformat == SC_OPENPGP_KEYFORMAT_CRTN)
comp_to_add = 4;
/* Validate */
if (comp_to_add == 4 && (key_info->n == NULL || key_info->n_len == 0)){
sc_log(ctx, "Error: Modulus required!");
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* Cardholder private key template's data part */
memset(pritemplate, 0, max_prtem_len);
/* Get required exponent length */
alat_blob = pgp_find_blob(card, 0x00C0 | key_info->keytype);
if (!alat_blob) {
sc_log(ctx, "Cannot read Algorithm Attributes.");
LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND);
}
req_e_len = bebytes2ushort(alat_blob->data + 3) >> 3; /* 1/8 */
assert(key_info->e_len <= req_e_len);
/* We need to right justify the exponent with required length, for example,
* from 01 00 01 to 00 01 00 01 */
if (key_info->e_len < req_e_len) {
/* Create new buffer */
p = calloc(req_e_len, 1);
memcpy(p + req_e_len - key_info->e_len, key_info->e, key_info->e_len);
key_info->e_len = req_e_len;
/* Set key_info->e to new buffer */
free(key_info->e);
key_info->e = p;
components[0] = p;
componentlens[0] = req_e_len;
}
/* Start from beginning of pritemplate */
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;
/*
* <-- kdata_len --><-- Copy here -->
* kdata |===============|___________________
*/
memcpy(kdata + kdata_len, components[i], componentlens[i]);
kdata_len += componentlens[i];
/* Move p to next part and build */
p += len;
}
/* TODO: Components for CRT format */
/* TLV block for 7F48 */
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_RET(ctx, r, "Failed to build TLV for 5F48.");
/* Data part's length for Extended Header list */
len = 2 + tlvlen_7f48 + tlvlen_5f48;
/* Set data part content */
data = calloc(len, 1);
if (data == NULL) {
sc_log(ctx, "Not enough memory.");
r = SC_ERROR_NOT_ENOUGH_MEMORY;
goto out2;
}
switch (key_info->keytype) {
case SC_OPENPGP_KEY_SIGN:
data[0] = 0xB6;
break;
case SC_OPENPGP_KEY_ENCR:
data[0] = 0xB8;
break;
case SC_OPENPGP_KEY_AUTH:
data[0] = 0xA4;
break;
default:
sc_log(ctx, "Unknown key type %d.", key_info->keytype);
r = SC_ERROR_INVALID_ARGUMENTS;
goto out1;
}
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);
if (r < 0) {
sc_log(ctx, "Cannot build TLV for Extended Header list.");
goto out1;
}
/* Set output */
if (result != NULL) {
*result = tlvblock;
*resultlen = tlvlen;
} else {
free(tlvblock);
}
out1:
free(data);
out2:
free(tlv_7f48);
free(tlv_5f48);
LOG_FUNC_RETURN(ctx, r);
}
/**
* Store key.
**/
static int pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info)
{
sc_context_t *ctx = card->ctx;
sc_cardctl_openpgp_keygen_info_t pubkey;
u8 *data;
size_t len;
int r;
LOG_FUNC_CALLED(ctx);
/* Validate */
if (key_info->keytype < 1 || key_info->keytype > 3) {
sc_log(ctx, "Unknown key type %d.", key_info->keytype);
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* We just support standard key format */
switch (key_info->keyformat) {
case SC_OPENPGP_KEYFORMAT_STD:
case SC_OPENPGP_KEYFORMAT_STDN:
break;
case SC_OPENPGP_KEYFORMAT_CRT:
case SC_OPENPGP_KEYFORMAT_CRTN:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
default:
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
}
/* We only support exponent of maximum 32 bits */
if (key_info->e_len > 4) {
sc_log(card->ctx, "Exponent %bit (>32) is not supported.", key_info->e_len*8);
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
/* Set algorithm attributes */
memset(&pubkey, 0, sizeof(pubkey));
pubkey.keytype = key_info->keytype;
if (key_info->n && key_info->n_len) {
pubkey.modulus = key_info->n;
pubkey.modulus_len = 8*key_info->n_len;
/* We won't update exponent length, because smaller exponent length
* will be padded later */
}
r = pgp_update_new_algo_attr(card, &pubkey);
LOG_TEST_RET(card->ctx, r, "Failed to update new algorithm attributes");
/* Build Extended Header list */
r = pgp_build_extended_header_list(card, key_info, &data, &len);
if (r < 0) {
sc_log(ctx, "Failed to build Extended Header list.");
goto out;
}
/* Write to DO */
r = pgp_put_data(card, 0x4D, data, len);
if (r < 0) {
sc_log(ctx, "Failed to write to DO.");
goto out;
}
free(data);
data = NULL;
/* Store creation time */
r = pgp_store_creationtime(card, key_info->keytype, &key_info->creationtime);
LOG_TEST_RET(card->ctx, r, "Cannot store creation time");
/* Calculate and store fingerprint */
sc_log(card->ctx, "Calculate and store fingerprint");
r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, key_info->n, key_info->e, &pubkey);
LOG_TEST_RET(card->ctx, r, "Cannot store fingerprint.");
/* Update pubkey blobs (B601,B801, A401) */
sc_log(card->ctx, "Update blobs holding pubkey info.");
r = pgp_update_pubkey_blob(card, key_info->n, 8*key_info->n_len,
key_info->e, 8*key_info->e_len, key_info->keytype);
sc_log(ctx, "Update card algorithms.");
pgp_update_card_algorithms(card, &pubkey);
out:
if (data) {
free(data);
data = NULL;
}
LOG_FUNC_RETURN(ctx, r);
}
#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)
{
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 /* 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);
}
/* ABI: DELETE FILE */
static int
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;
int r;
LOG_FUNC_CALLED(card->ctx);
/* In sc_pkcs15init_delete_by_path(), the path type was set to SC_PATH_TYPE_FILE_ID */
r = pgp_select_file(card, path, &file);
LOG_TEST_RET(card->ctx, r, "Cannot select file.");
/* save "current" blob */
blob = priv->current;
/* do try to delete MF */
if (blob == priv->mf)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
if (file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401) {
/* These tags are just symbolic. We don't really delete it. */
r = SC_SUCCESS;
}
else {
/* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */
r = pgp_put_data(card, file->id, NULL, 0);
}
/* set "current" blob to parent */
priv->current = blob->parent;
LOG_FUNC_RETURN(card->ctx, r);
}
/* ABI: UPDATE BINARY */
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);
struct blob *blob = priv->current;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(card->ctx);
/* We will use PUT DATA to write to DO.
* As PUT DATA does not support idx, we don't either */
if (idx > 0)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS);
/* When a dummy file, e.g "11001101", is selected, the current blob
* is set to NULL. We don't really put data to dummy file. */
if (blob != NULL) {
r = pgp_put_data(card, blob->id, buf, count);
}
LOG_FUNC_RETURN(card->ctx, r);
}
/* ABI: driver binding stuff */
static struct sc_card_driver *
sc_get_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.read_binary = pgp_read_binary;
pgp_ops.write_binary = pgp_write_binary;
pgp_ops.pin_cmd = pgp_pin_cmd;
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;
return &pgp_drv;
}
struct sc_card_driver *
sc_get_openpgp_driver(void)
{
return sc_get_driver();
}