opensc/src/libopensc/card-openpgp.c

2590 lines
78 KiB
C
Raw Normal View History

/*
* 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)
{
2012-06-14 02:17:28 +00:00
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);
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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 */
2013-05-27 14:44:32 +00:00
/* 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;
}
2012-06-01 15:45:12 +00:00
/* 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;
2012-06-01 15:40:39 +00:00
sc_apdu_t apdu;
u8 *part;
size_t plen;
/* Two round_ variables below are to build APDU data
* with even length for Gnuk */
u8 roundbuf[256];
size_t roundlen = 0;
int r = SC_SUCCESS;
LOG_FUNC_CALLED(ctx);
/* 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);
2013-04-16 03:19:34 +00:00
if (r < 0)
LOG_FUNC_RETURN(card->ctx, r);
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;
/* If the last part has odd length, we add zero padding to make it even.
* Gnuk does not allow data with odd length */
if (plen < 256 && (plen % 2) != 0) {
roundlen = plen + 1;
memset(roundbuf, 0, roundlen);
memcpy(roundbuf, part, plen);
apdu.data = roundbuf;
apdu.datalen = apdu.lc = roundlen;
}
else {
apdu.data = part;
apdu.datalen = apdu.lc = plen;
}
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
/* 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)
{
2012-06-01 15:40:39 +00:00
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;
2012-06-01 15:40:39 +00:00
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;
}
2012-06-01 15:40:39 +00:00
/* 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;
2012-06-01 15:40:39 +00:00
}
else {
/* This case is to empty DO */
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2);
}
2012-06-01 15:40:39 +00:00
/* Send APDU to card */
r = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, r, "APDU transmit failed");
2012-06-01 15:40:39 +00:00
/* 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);
}
2012-06-01 15:40:39 +00:00
/* 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");
2012-06-01 15:40:39 +00:00
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 */
}
2012-06-01 15:40:39 +00:00
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");
2012-06-01 15:41:17 +00:00
/* 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. */
Do not cast the return value of malloc(3) and calloc(3) From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety " Casting and type safety malloc returns a void pointer (void *), which indicates that it is a pointer to a region of unknown data type. One may "cast" (see type conversion) this pointer to a specific type, as in int *ptr = (int*)malloc(10 * sizeof (int)); When using C, this is considered bad practice; it is redundant under the C standard. Moreover, putting in a cast may mask failure to include the header stdlib.h, in which the prototype for malloc is found. In the absence of a prototype for malloc, the C compiler will assume that malloc returns an int, and will issue a warning in a context such as the above, provided the error is not masked by a cast. On certain architectures and data models (such as LP64 on 64 bit systems, where long and pointers are 64 bit and int is 32 bit), this error can actually result in undefined behavior, as the implicitly declared malloc returns a 32 bit value whereas the actually defined function returns a 64 bit value. Depending on calling conventions and memory layout, this may result in stack smashing. The returned pointer need not be explicitly cast to a more specific pointer type, since ANSI C defines an implicit conversion between the void pointer type and other pointers to objects. An explicit cast of malloc's return value is sometimes performed because malloc originally returned a char *, but this cast is unnecessary in standard C code.[4][5] Omitting the cast, however, creates an incompatibility with C++, which does require it. The lack of a specific pointer type returned from malloc is type-unsafe behaviour: malloc allocates based on byte count but not on type. This distinguishes it from the C++ new operator that returns a pointer whose type relies on the operand. (see C Type Safety). " See also http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
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;
2013-05-27 14:44:32 +00:00
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);
2013-05-25 02:22:28 +00:00
/* 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)
2013-05-27 14:44:32 +00:00
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)
2013-05-27 14:44:32 +00:00
apdu_data = (unsigned char *) "\xb8";
else if (key_info->keytype == SC_OPENPGP_KEY_AUTH)
2013-05-27 14:44:32 +00:00
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
2012-06-14 02:17:28 +00:00
* 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);
}
2013-04-12 08:33:31 +00:00
/* Internal: Delete key */
static int
gnuk_delete_key(sc_card_t *card, u8 key_id)
{
sc_context_t *ctx = card->ctx;
int r = SC_SUCCESS;
u8 *data = NULL;
LOG_FUNC_CALLED(ctx);
2013-04-16 03:19:34 +00:00
if (key_id < 1 || key_id > 3) {
sc_log(ctx, "Key ID %d is invalid. Should be 1, 2 or 3.", key_id);
LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
}
2013-04-12 08:33:31 +00:00
/* Delete fingerprint */
sc_log(ctx, "Delete fingerprints");
r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
LOG_TEST_RET(ctx, r, "Failed to delete fingerprints");
/* Delete creation time */
sc_log(ctx, "Delete creation time");
r = pgp_put_data(card, 0xCD + key_id, NULL, 0);
LOG_TEST_RET(ctx, r, "Failed to delete creation time");
/* Rewrite Extended Header List */
sc_log(ctx, "Rewrite Extended Header List");
if (key_id == 1)
data = "\x4D\x02\xB6";
else if (key_id == 2)
data = "\x4D\x02\xB8";
else if (key_id == 3)
data = "\x4D\x02\xA4";
r = pgp_put_data(card, 0x4D, data, strlen(data) + 1);
LOG_FUNC_RETURN(ctx, r);
}
/* ABI: DELETE FILE */
static int
pgp_delete_file(sc_card_t *card, const sc_path_t *path)
2012-06-01 15:59:20 +00:00
{
struct pgp_priv_data *priv = DRVDATA(card);
struct blob *blob;
2012-06-01 15:59:20 +00:00
sc_file_t *file;
2013-04-12 08:33:31 +00:00
u8 key_id;
2012-06-01 15:59:20 +00:00
int r;
LOG_FUNC_CALLED(card->ctx);
/* In sc_pkcs15init_delete_by_path(), the path type was set to SC_PATH_TYPE_FILE_ID */
2012-06-01 15:59:20 +00:00
r = pgp_select_file(card, path, &file);
LOG_TEST_RET(card->ctx, r, "Cannot select file.");
/* save "current" blob */
blob = priv->current;
2012-06-01 15:59:20 +00:00
/* do try to delete MF */
if (blob == priv->mf)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
2013-04-12 08:33:31 +00:00
if (card->type != SC_CARD_TYPE_OPENPGP_GNUK &&
(file->id == 0xB601 || file->id == 0xB801 || file->id == 0xA401)) {
/* These tags are just symbolic. We don't really delete it. */
r = SC_SUCCESS;
}
2013-04-12 08:33:31 +00:00
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xB601) {
r = gnuk_delete_key(card, 1);
}
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xB801) {
r = gnuk_delete_key(card, 2);
}
else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == 0xA401) {
r = gnuk_delete_key(card, 3);
}
else {
/* call pgp_put_data() with zero-sized NULL-buffer to zap the DO contents */
r = pgp_put_data(card, file->id, NULL, 0);
}
/* set "current" blob to parent */
priv->current = blob->parent;
LOG_FUNC_RETURN(card->ctx, r);
2012-06-01 15:59:20 +00:00
}
/* 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;
2012-06-01 15:59:20 +00:00
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();
}