383 lines
8.7 KiB
C
383 lines
8.7 KiB
C
|
/*
|
||
|
* eToken PRO specific operation for PKCS15 initialization
|
||
|
*
|
||
|
* Copyright (C) 2002 Olaf Kirch <okir@lst.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
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
#include <sys/types.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <opensc/opensc.h>
|
||
|
#include <opensc/cardctl.h>
|
||
|
#include "pkcs15-init.h"
|
||
|
#include "profile.h"
|
||
|
|
||
|
struct tlv {
|
||
|
unsigned char * base;
|
||
|
unsigned char * end;
|
||
|
unsigned char * current;
|
||
|
unsigned char * next;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* Local functions
|
||
|
*/
|
||
|
static int etoken_new_file(struct sc_profile *, struct sc_card *,
|
||
|
unsigned int, unsigned int,
|
||
|
struct sc_file **);
|
||
|
static void error(struct sc_profile *, const char *, ...);
|
||
|
|
||
|
/* We should make these object IDs configurable via the profile */
|
||
|
#define ETOKEN_SO_PIN_ID 0x01
|
||
|
#define ETOKEN_SO_PUK_ID 0x02
|
||
|
#define ETOKEN_PIN_ID 0x03
|
||
|
#define ETOKEN_PUK_ID 0x04
|
||
|
#define ETOKEN_AC_NEVER 0xFF
|
||
|
|
||
|
#define ETOKEN_ALGO_PIN 0x87
|
||
|
|
||
|
#define ETOKEN_DEFAULT_PIN "null-pin"
|
||
|
#define ETOKEN_DEFAULT_PIN_LEN (sizeof(ETOKEN_DEFAULT_PIN)-1)
|
||
|
|
||
|
struct etoken_pin_info {
|
||
|
int profile_id;
|
||
|
u8 id;
|
||
|
u8 unblock;
|
||
|
};
|
||
|
static struct etoken_pin_info etoken_so_pin = {
|
||
|
SC_PKCS15INIT_SO_PIN,
|
||
|
ETOKEN_SO_PIN_ID,
|
||
|
ETOKEN_SO_PUK_ID
|
||
|
};
|
||
|
static struct etoken_pin_info etoken_so_puk = {
|
||
|
SC_PKCS15INIT_SO_PUK,
|
||
|
ETOKEN_SO_PUK_ID,
|
||
|
ETOKEN_AC_NEVER
|
||
|
};
|
||
|
static struct etoken_pin_info etoken_user_pin = {
|
||
|
SC_PKCS15INIT_USER_PIN,
|
||
|
ETOKEN_PIN_ID,
|
||
|
ETOKEN_PUK_ID
|
||
|
};
|
||
|
static struct etoken_pin_info etoken_user_puk = {
|
||
|
SC_PKCS15INIT_USER_PUK,
|
||
|
ETOKEN_PUK_ID,
|
||
|
ETOKEN_SO_PIN_ID
|
||
|
};
|
||
|
|
||
|
static inline void
|
||
|
tlv_init(struct tlv *tlv, u8 *base, size_t size)
|
||
|
{
|
||
|
tlv->base = base;
|
||
|
tlv->end = base + size;
|
||
|
tlv->current = tlv->next = base;
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
tlv_next(struct tlv *tlv, u8 tag)
|
||
|
{
|
||
|
assert(tlv->next + 2 < tlv->end);
|
||
|
tlv->current = tlv->next;
|
||
|
*(tlv->next++) = tag;
|
||
|
*(tlv->next++) = 0;
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
tlv_add(struct tlv *tlv, u8 val)
|
||
|
{
|
||
|
assert(tlv->next + 1 < tlv->end);
|
||
|
*(tlv->next++) = val;
|
||
|
tlv->current[1]++;
|
||
|
}
|
||
|
|
||
|
static size_t
|
||
|
tlv_len(struct tlv *tlv)
|
||
|
{
|
||
|
return tlv->next - tlv->base;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
etoken_set_ac(struct sc_file *file, int op, struct sc_acl_entry *acl)
|
||
|
{
|
||
|
/* XXX TBD */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initialize pin file
|
||
|
*/
|
||
|
static int
|
||
|
etoken_new_pin(struct sc_profile *profile, struct sc_card *card,
|
||
|
struct etoken_pin_info *info,
|
||
|
const u8 *pin, size_t pin_len)
|
||
|
{
|
||
|
struct sc_pkcs15_pin_info params;
|
||
|
struct sc_cardctl_etoken_pin_info args;
|
||
|
unsigned char buffer[256];
|
||
|
struct tlv tlv;
|
||
|
unsigned int pin_id, puk_id, attempts, minlen;
|
||
|
|
||
|
/* XXX hack for testing */
|
||
|
if (!pin_len) {
|
||
|
pin = ETOKEN_DEFAULT_PIN;
|
||
|
pin_len = ETOKEN_DEFAULT_PIN_LEN;
|
||
|
}
|
||
|
|
||
|
sc_profile_get_pin_info(profile, info->profile_id, ¶ms);
|
||
|
attempts = params.tries_left;
|
||
|
minlen = params.min_length;
|
||
|
|
||
|
pin_id = info->id;
|
||
|
puk_id = info->unblock;
|
||
|
|
||
|
tlv_init(&tlv, buffer, sizeof(buffer));
|
||
|
|
||
|
/* object address: class, id */
|
||
|
tlv_next(&tlv, 0x83);
|
||
|
tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */
|
||
|
tlv_add(&tlv, pin_id);
|
||
|
|
||
|
/* parameters */
|
||
|
tlv_next(&tlv, 0x85);
|
||
|
tlv_add(&tlv, 0x02); /* options byte */
|
||
|
tlv_add(&tlv, attempts & 0xf); /* flags byte */
|
||
|
tlv_add(&tlv, ETOKEN_ALGO_PIN); /* algorithm = pin-test */
|
||
|
tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */
|
||
|
tlv_add(&tlv, 0x00); /* usecount = 0 */
|
||
|
tlv_add(&tlv, 0x00); /* DEK (?) */
|
||
|
tlv_add(&tlv, 0x00); /* ARA counter (?) */
|
||
|
tlv_add(&tlv, minlen);
|
||
|
|
||
|
/* data: PIN */
|
||
|
tlv_next(&tlv, 0x8f);
|
||
|
while (pin_len--)
|
||
|
tlv_add(&tlv, *pin++);
|
||
|
|
||
|
/* AC conditions */
|
||
|
tlv_next(&tlv, 0x86);
|
||
|
tlv_add(&tlv, 0x00); /* use: always */
|
||
|
tlv_add(&tlv, puk_id); /* change: PUK */
|
||
|
tlv_add(&tlv, puk_id); /* unblock: PUK */
|
||
|
|
||
|
args.data = buffer;
|
||
|
args.len = tlv_len(&tlv);
|
||
|
|
||
|
return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initialize the Application DF and pin file
|
||
|
*/
|
||
|
static int
|
||
|
etoken_init_app(struct sc_profile *profile, struct sc_card *card,
|
||
|
const unsigned char *pin, size_t pin_len,
|
||
|
const unsigned char *puk, size_t puk_len)
|
||
|
{
|
||
|
struct sc_file *df = profile->df_info->file;
|
||
|
int r;
|
||
|
|
||
|
/* Create the application DF */
|
||
|
r = sc_pkcs15init_create_file(profile, card, df);
|
||
|
|
||
|
if (r >= 0)
|
||
|
r = sc_select_file(card, &df->path, NULL);
|
||
|
|
||
|
/* Create the PIN objects */
|
||
|
if (r >= 0)
|
||
|
r = etoken_new_pin(profile, card,
|
||
|
&etoken_so_puk, puk, puk_len);
|
||
|
if (r >= 0)
|
||
|
r = etoken_new_pin(profile, card,
|
||
|
&etoken_so_pin, pin, pin_len);
|
||
|
if (r >= 0)
|
||
|
r = etoken_new_pin(profile, card, &etoken_user_pin, 0, 0);
|
||
|
if (r >= 0)
|
||
|
r = etoken_new_pin(profile, card, &etoken_user_puk, 0, 0);
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Store a PIN
|
||
|
*/
|
||
|
static int
|
||
|
etoken_put_pin(struct sc_profile *profile, struct sc_card *card,
|
||
|
struct sc_pkcs15_pin_info *info, unsigned int index,
|
||
|
const u8 *pin, size_t pin_len,
|
||
|
const u8 *puk, size_t puk_len)
|
||
|
{
|
||
|
return SC_ERROR_NOT_SUPPORTED;
|
||
|
#if 0
|
||
|
unsigned char nulpin[8];
|
||
|
int r;
|
||
|
|
||
|
/* Profile must define a "pinfile" */
|
||
|
if (sc_profile_get_path(profile, "pinfile", &info->path) < 0) {
|
||
|
error(profile, "Profile doesn't define a \"pinfile\"");
|
||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||
|
}
|
||
|
if (info->path.len > 2)
|
||
|
info->path.len -= 2;
|
||
|
|
||
|
r = sc_select_file(card, &info->path, NULL);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
index <<= 2;
|
||
|
if (index >= GPK_MAX_PINS)
|
||
|
return SC_ERROR_TOO_MANY_OBJECTS;
|
||
|
if (puk == NULL || puk_len == 0) {
|
||
|
puk = pin;
|
||
|
puk_len = pin_len;
|
||
|
}
|
||
|
|
||
|
/* Current PIN is 00:00:00:00:00:00:00:00 */
|
||
|
memset(nulpin, 0, sizeof(nulpin));
|
||
|
r = sc_change_reference_data(card, SC_AC_CHV,
|
||
|
0x8 | index,
|
||
|
nulpin, sizeof(nulpin),
|
||
|
pin, pin_len, NULL);
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
/* Current PUK is 00:00:00:00:00:00:00:00 */
|
||
|
r = sc_change_reference_data(card, SC_AC_CHV,
|
||
|
0x8 | (index + 1),
|
||
|
nulpin, sizeof(nulpin),
|
||
|
puk, puk_len, NULL);
|
||
|
|
||
|
info->reference = 0x8 | index;
|
||
|
return r;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Store a private key
|
||
|
*/
|
||
|
static int
|
||
|
etoken_new_key(struct sc_profile *profile, struct sc_card *card,
|
||
|
struct sc_pkcs15_prkey *key, unsigned int index,
|
||
|
struct sc_pkcs15_prkey_info *info)
|
||
|
{
|
||
|
return SC_ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Allocate a file
|
||
|
*/
|
||
|
static int
|
||
|
etoken_new_file(struct sc_profile *profile, struct sc_card *card,
|
||
|
unsigned int type, unsigned int num,
|
||
|
struct sc_file **out)
|
||
|
{
|
||
|
struct sc_file *file;
|
||
|
struct sc_path *p;
|
||
|
char name[64], *tag, *desc;
|
||
|
|
||
|
desc = tag = NULL;
|
||
|
while (1) {
|
||
|
switch (type) {
|
||
|
case SC_PKCS15_TYPE_PRKEY_RSA:
|
||
|
desc = "RSA private key";
|
||
|
tag = "private-key";
|
||
|
break;
|
||
|
case SC_PKCS15_TYPE_PUBKEY_RSA:
|
||
|
desc = "RSA public key";
|
||
|
tag = "public-key";
|
||
|
break;
|
||
|
#ifdef SC_PKCS15_TYPE_PRKEY_DSA
|
||
|
case SC_PKCS15_TYPE_PRKEY_DSA:
|
||
|
desc = "DSA private key";
|
||
|
tag = "private-key";
|
||
|
break;
|
||
|
case SC_PKCS15_TYPE_PUBKEY_DSA:
|
||
|
desc = "DSA public key";
|
||
|
tag = "public-key";
|
||
|
break;
|
||
|
#endif
|
||
|
case SC_PKCS15_TYPE_PRKEY:
|
||
|
desc = "extractable private key";
|
||
|
tag = "extractable-key";
|
||
|
break;
|
||
|
case SC_PKCS15_TYPE_CERT:
|
||
|
desc = "certificate";
|
||
|
tag = "certificate";
|
||
|
break;
|
||
|
case SC_PKCS15_TYPE_DATA_OBJECT:
|
||
|
desc = "data object";
|
||
|
tag = "data";
|
||
|
break;
|
||
|
}
|
||
|
if (tag)
|
||
|
break;
|
||
|
/* If this is a specific type such as
|
||
|
* SC_PKCS15_TYPE_CERT_FOOBAR, fall back to
|
||
|
* the generic class (SC_PKCS15_TYPE_CERT)
|
||
|
*/
|
||
|
if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) {
|
||
|
error(profile, "File type not supported by card driver");
|
||
|
return SC_ERROR_INVALID_ARGUMENTS;
|
||
|
}
|
||
|
type &= SC_PKCS15_TYPE_CLASS_MASK;
|
||
|
}
|
||
|
|
||
|
snprintf(name, sizeof(name), "template-%s", tag);
|
||
|
if (sc_profile_get_file(profile, name, &file) < 0) {
|
||
|
error(profile, "Profile doesn't define %s template (%s)\n",
|
||
|
desc, name);
|
||
|
return SC_ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
/* Now construct file from template */
|
||
|
file->id += num;
|
||
|
|
||
|
p = &file->path;
|
||
|
*p = profile->df_info->file->path;
|
||
|
p->value[p->len++] = file->id >> 8;
|
||
|
p->value[p->len++] = file->id;
|
||
|
|
||
|
*out = file;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
error(struct sc_profile *profile, const char *fmt, ...)
|
||
|
{
|
||
|
char buffer[256];
|
||
|
va_list ap;
|
||
|
|
||
|
va_start(ap, fmt);
|
||
|
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||
|
va_end(ap);
|
||
|
if (profile->cbs)
|
||
|
profile->cbs->error("%s", buffer);
|
||
|
}
|
||
|
|
||
|
struct sc_pkcs15init_operations sc_pkcs15init_etoken_operations = {
|
||
|
NULL,
|
||
|
etoken_init_app,
|
||
|
etoken_put_pin,
|
||
|
etoken_new_key,
|
||
|
etoken_new_file,
|
||
|
};
|