2003-10-31 12:27:14 +00:00
|
|
|
/*
|
|
|
|
* PKCS15 emulation layer for OpenPGP card.
|
2003-10-31 16:01:35 +00:00
|
|
|
* To see how this works, run p15dump on your OpenPGP card.
|
2003-10-31 12:27:14 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "pkcs15.h"
|
|
|
|
#include "asn1.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
static char * pgp_pin_name[3] = {
|
|
|
|
"Signature PIN",
|
|
|
|
"Encryption PIN",
|
|
|
|
"Admin PIN"
|
|
|
|
};
|
|
|
|
static char * pgp_key_name[3] = {
|
|
|
|
"Signature key",
|
|
|
|
"Encryption key",
|
|
|
|
"Authentication key"
|
|
|
|
};
|
|
|
|
static char * pgp_pubkey_path[3] = {
|
2003-11-20 15:41:28 +00:00
|
|
|
"B601",
|
|
|
|
"B801",
|
|
|
|
"A401"
|
2003-10-31 16:01:35 +00:00
|
|
|
};
|
|
|
|
|
2003-10-31 12:27:14 +00:00
|
|
|
/*
|
|
|
|
* Much of this code probably needs to be shared between
|
|
|
|
* emulators. Will move this to pkcs15-syn.c when needed.
|
|
|
|
*/
|
|
|
|
static sc_pkcs15_df_t *
|
|
|
|
sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, int type)
|
|
|
|
{
|
|
|
|
sc_pkcs15_df_t *df;
|
|
|
|
sc_file_t *file;
|
|
|
|
int created = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
for (df = p15card->df_list; df; df = df->next) {
|
|
|
|
if (df->type == type) {
|
|
|
|
if (created)
|
|
|
|
df->enumerated = 1;
|
|
|
|
return df;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(created == 0);
|
|
|
|
|
|
|
|
file = sc_file_new();
|
|
|
|
sc_format_path("DEAD", &file->path);
|
|
|
|
sc_pkcs15_add_df(p15card, type, &file->path, file);
|
|
|
|
created++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
static int
|
2003-10-31 12:27:14 +00:00
|
|
|
sc_pkcs15emu_add_object(sc_pkcs15_card_t *p15card, int type,
|
2003-10-31 16:01:35 +00:00
|
|
|
const char *label, void *data,
|
|
|
|
const sc_pkcs15_id_t *auth_id)
|
2003-10-31 12:27:14 +00:00
|
|
|
{
|
|
|
|
sc_pkcs15_object_t *obj;
|
|
|
|
int df_type;
|
|
|
|
|
|
|
|
obj = calloc(1, sizeof(*obj));
|
|
|
|
obj->type = type;
|
|
|
|
obj->data = data;
|
|
|
|
if (label)
|
|
|
|
strncpy(obj->label, label, sizeof(obj->label)-1);
|
|
|
|
|
|
|
|
if (!(p15card->flags & SC_PKCS15_CARD_FLAG_READONLY))
|
|
|
|
obj->flags |= SC_PKCS15_CO_FLAG_MODIFIABLE;
|
2003-11-20 15:41:28 +00:00
|
|
|
if (auth_id)
|
|
|
|
obj->auth_id = *auth_id;
|
2003-10-31 12:27:14 +00:00
|
|
|
|
|
|
|
switch (type & SC_PKCS15_TYPE_CLASS_MASK) {
|
|
|
|
case SC_PKCS15_TYPE_AUTH:
|
|
|
|
obj->flags |= SC_PKCS15_CO_FLAG_PRIVATE;
|
|
|
|
df_type = SC_PKCS15_AODF;
|
|
|
|
break;
|
|
|
|
case SC_PKCS15_TYPE_PRKEY:
|
|
|
|
obj->flags |= SC_PKCS15_CO_FLAG_PRIVATE;
|
|
|
|
df_type = SC_PKCS15_PRKDF;
|
|
|
|
break;
|
|
|
|
case SC_PKCS15_TYPE_PUBKEY:
|
|
|
|
df_type = SC_PKCS15_PUKDF;
|
|
|
|
break;
|
|
|
|
case SC_PKCS15_TYPE_CERT:
|
|
|
|
df_type = SC_PKCS15_CDF;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sc_error(p15card->card->ctx,
|
|
|
|
"Unknown PKCS15 object type %d\n", type);
|
2003-10-31 16:01:35 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
2003-10-31 12:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
obj->df = sc_pkcs15emu_get_df(p15card, df_type);
|
|
|
|
sc_pkcs15_add_object(p15card, obj);
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
return 0;
|
2003-10-31 12:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card,
|
2003-10-31 16:01:35 +00:00
|
|
|
const sc_pkcs15_id_t *id, const char *label,
|
2003-10-31 12:27:14 +00:00
|
|
|
const sc_path_t *path, int ref, int type,
|
|
|
|
unsigned int min_length,
|
|
|
|
unsigned int max_length,
|
|
|
|
int flags, int tries_left)
|
|
|
|
{
|
|
|
|
sc_pkcs15_pin_info_t *info;
|
|
|
|
|
|
|
|
info = (sc_pkcs15_pin_info_t *) calloc(1, sizeof(*info));
|
2003-10-31 16:01:35 +00:00
|
|
|
info->auth_id = *id;
|
2003-10-31 12:27:14 +00:00
|
|
|
info->min_length = min_length;
|
|
|
|
info->max_length = max_length;
|
|
|
|
info->stored_length = max_length;
|
|
|
|
info->type = type;
|
|
|
|
info->reference = ref;
|
|
|
|
info->flags = flags;
|
|
|
|
info->tries_left = tries_left;
|
2003-10-31 16:01:35 +00:00
|
|
|
info->magic = SC_PKCS15_PIN_MAGIC;
|
2003-10-31 12:27:14 +00:00
|
|
|
|
|
|
|
if (path)
|
|
|
|
info->path = *path;
|
|
|
|
if (type == SC_PKCS15_PIN_TYPE_BCD)
|
|
|
|
info->stored_length /= 2;
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
return sc_pkcs15emu_add_object(p15card,
|
|
|
|
SC_PKCS15_TYPE_AUTH_PIN,
|
|
|
|
label, info, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card,
|
|
|
|
const sc_pkcs15_id_t *id,
|
|
|
|
const char *label,
|
|
|
|
int type, unsigned int modulus_length, int usage,
|
|
|
|
const sc_path_t *path, int ref,
|
|
|
|
sc_pkcs15_id_t *auth_id)
|
|
|
|
{
|
|
|
|
sc_pkcs15_prkey_info_t *info;
|
|
|
|
|
|
|
|
info = (sc_pkcs15_prkey_info_t *) calloc(1, sizeof(*info));
|
|
|
|
info->id = *id;
|
|
|
|
info->modulus_length = modulus_length;
|
|
|
|
info->usage = usage;
|
|
|
|
info->native = 1;
|
|
|
|
info->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE
|
|
|
|
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
|
|
|
|
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE
|
|
|
|
| SC_PKCS15_PRKEY_ACCESS_LOCAL;
|
|
|
|
info->key_reference = ref;
|
2003-10-31 12:27:14 +00:00
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
if (path)
|
|
|
|
info->path = *path;
|
|
|
|
|
|
|
|
return sc_pkcs15emu_add_object(p15card,
|
|
|
|
type, label, info, auth_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
sc_pkcs15emu_add_pubkey(sc_pkcs15_card_t *p15card,
|
|
|
|
const sc_pkcs15_id_t *id,
|
|
|
|
const char *label, int type,
|
|
|
|
unsigned int modulus_length, int usage,
|
2003-11-20 15:41:28 +00:00
|
|
|
const sc_path_t *path, int ref,
|
|
|
|
const sc_pkcs15_id_t *auth_id)
|
2003-10-31 16:01:35 +00:00
|
|
|
{
|
|
|
|
sc_pkcs15_pubkey_info_t *info;
|
|
|
|
|
|
|
|
info = (sc_pkcs15_pubkey_info_t *) calloc(1, sizeof(*info));
|
|
|
|
info->id = *id;
|
|
|
|
info->modulus_length = modulus_length;
|
|
|
|
info->usage = usage;
|
|
|
|
info->access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
2003-11-20 15:41:28 +00:00
|
|
|
info->key_reference = ref;
|
2003-10-31 16:01:35 +00:00
|
|
|
|
|
|
|
if (path)
|
|
|
|
info->path = *path;
|
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
return sc_pkcs15emu_add_object(p15card, type, label, info, auth_id);
|
2003-10-31 12:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_string(char **strp, const char *value)
|
|
|
|
{
|
|
|
|
if (*strp)
|
|
|
|
free(strp);
|
|
|
|
*strp = value? strdup(value) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function pretty much follows what find_tlv in the GNUpg
|
|
|
|
* code does.
|
|
|
|
*/
|
|
|
|
static int
|
2003-10-31 16:01:35 +00:00
|
|
|
read_file(sc_card_t *card, const char *path_name, void *buf, size_t len)
|
2003-10-31 12:27:14 +00:00
|
|
|
{
|
2003-10-31 16:01:35 +00:00
|
|
|
sc_path_t path;
|
|
|
|
sc_file_t *file;
|
2003-10-31 12:27:14 +00:00
|
|
|
int r;
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
sc_format_path(path_name, &path);
|
|
|
|
if ((r = sc_select_file(card, &path, &file)) < 0)
|
|
|
|
return r;
|
2003-10-31 12:27:14 +00:00
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
if (file->size < len)
|
|
|
|
len = file->size;
|
|
|
|
return sc_read_binary(card, 0, buf, len, 0);
|
2003-10-31 12:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card)
|
|
|
|
{
|
|
|
|
sc_card_t *card = p15card->card;
|
|
|
|
sc_context_t *ctx = card->ctx;
|
|
|
|
char string[256];
|
2003-10-31 16:01:35 +00:00
|
|
|
u8 buffer[256];
|
2003-10-31 12:27:14 +00:00
|
|
|
size_t length;
|
|
|
|
int r, i;
|
|
|
|
|
|
|
|
set_string(&p15card->label, "OpenPGP Card");
|
|
|
|
set_string(&p15card->manufacturer_id, "OpenPGP project");
|
|
|
|
|
2003-10-31 16:01:35 +00:00
|
|
|
if ((r = read_file(card, "004f", buffer, sizeof(buffer))) < 0)
|
2003-10-31 12:27:14 +00:00
|
|
|
goto failed;
|
|
|
|
sc_bin_to_hex(buffer, r, string, sizeof(string), 0);
|
|
|
|
set_string(&p15card->serial_number, string);
|
|
|
|
p15card->version = (buffer[6] << 8) | buffer[7];
|
|
|
|
|
|
|
|
p15card->flags = SC_PKCS15_CARD_FLAG_LOGIN_REQUIRED |
|
|
|
|
SC_PKCS15_CARD_FLAG_PRN_GENERATION |
|
|
|
|
SC_PKCS15_CARD_FLAG_EID_COMPLIANT;
|
|
|
|
|
|
|
|
/* Extract preferred language */
|
2003-10-31 16:01:35 +00:00
|
|
|
r = read_file(card, "00655f2d", string, sizeof(string)-1);
|
|
|
|
if (r < 0)
|
2003-10-31 12:27:14 +00:00
|
|
|
goto failed;
|
2003-10-31 16:01:35 +00:00
|
|
|
string[r] = '\0';
|
|
|
|
set_string(&p15card->preferred_language, string);
|
2003-10-31 12:27:14 +00:00
|
|
|
|
|
|
|
/* Get Application Related Data (006E) */
|
|
|
|
if ((r = sc_get_data(card, 0x006E, buffer, sizeof(buffer))) < 0)
|
|
|
|
goto failed;
|
|
|
|
length = r;
|
|
|
|
|
|
|
|
/* TBD: extract algorithm info */
|
|
|
|
|
|
|
|
/* Get CHV status bytes:
|
|
|
|
* 00: ??
|
|
|
|
* 01-03: max length of pins 1-3
|
|
|
|
* 04-07: tries left for pins 1-3
|
|
|
|
*/
|
2003-10-31 16:01:35 +00:00
|
|
|
if ((r = read_file(card, "006E007300C4", buffer, sizeof(buffer))) < 0)
|
2003-10-31 12:27:14 +00:00
|
|
|
goto failed;
|
|
|
|
if (r != 7) {
|
|
|
|
sc_error(ctx,
|
|
|
|
"CHV status bytes have unexpected length "
|
|
|
|
"(expected 7, got %d)\n", r);
|
|
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
2003-11-20 15:41:28 +00:00
|
|
|
sc_path_t path;
|
2003-10-31 16:01:35 +00:00
|
|
|
sc_pkcs15_id_t auth_id;
|
2003-10-31 12:27:14 +00:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE |
|
|
|
|
SC_PKCS15_PIN_FLAG_INITIALIZED |
|
|
|
|
SC_PKCS15_PIN_FLAG_LOCAL;
|
|
|
|
if (i == 2) {
|
|
|
|
flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED |
|
|
|
|
SC_PKCS15_PIN_FLAG_SO_PIN;
|
|
|
|
}
|
2003-10-31 16:01:35 +00:00
|
|
|
|
2003-11-20 15:41:28 +00:00
|
|
|
sc_format_path("3F00", &path);
|
2003-10-31 16:01:35 +00:00
|
|
|
auth_id.value[0] = i + 1;
|
|
|
|
auth_id.len = 1;
|
|
|
|
sc_pkcs15emu_add_pin(p15card, &auth_id,
|
2003-11-20 15:41:28 +00:00
|
|
|
pgp_pin_name[i], &path, i+1,
|
2003-10-31 16:01:35 +00:00
|
|
|
SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
|
|
|
|
0, buffer[1+i], flags, buffer[4+i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
static int prkey_pin[3] = { 1, 2, 2 };
|
|
|
|
static int prkey_usage[3] = {
|
|
|
|
SC_PKCS15_PRKEY_USAGE_SIGN
|
|
|
|
| SC_PKCS15_PRKEY_USAGE_SIGNRECOVER
|
|
|
|
| SC_PKCS15_PRKEY_USAGE_NONREPUDIATION,
|
|
|
|
SC_PKCS15_PRKEY_USAGE_DECRYPT
|
|
|
|
| SC_PKCS15_PRKEY_USAGE_UNWRAP,
|
|
|
|
SC_PKCS15_PRKEY_USAGE_NONREPUDIATION
|
|
|
|
};
|
|
|
|
sc_pkcs15_id_t id, auth_id;
|
|
|
|
|
|
|
|
id.value[0] = i + 1;
|
|
|
|
id.len = 1;
|
|
|
|
auth_id.value[0] = prkey_pin[i];
|
|
|
|
auth_id.len = 1;
|
|
|
|
sc_pkcs15emu_add_prkey(p15card, &id,
|
|
|
|
pgp_key_name[i],
|
|
|
|
SC_PKCS15_TYPE_PRKEY_RSA,
|
|
|
|
1024, prkey_usage[i],
|
2003-11-20 15:41:28 +00:00
|
|
|
NULL, i,
|
2003-10-31 16:01:35 +00:00
|
|
|
&auth_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
static int pubkey_usage[3] = {
|
|
|
|
SC_PKCS15_PRKEY_USAGE_VERIFY
|
|
|
|
| SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER,
|
|
|
|
SC_PKCS15_PRKEY_USAGE_ENCRYPT
|
|
|
|
| SC_PKCS15_PRKEY_USAGE_WRAP,
|
|
|
|
SC_PKCS15_PRKEY_USAGE_VERIFY
|
|
|
|
};
|
2003-11-20 15:41:28 +00:00
|
|
|
sc_pkcs15_id_t id, auth_id;
|
2003-10-31 16:01:35 +00:00
|
|
|
sc_path_t path;
|
|
|
|
|
|
|
|
id.value[0] = i + 1;
|
|
|
|
id.len = 1;
|
2003-11-20 15:41:28 +00:00
|
|
|
auth_id.value[0] = 3;
|
|
|
|
auth_id.len = 1;
|
2003-10-31 16:01:35 +00:00
|
|
|
sc_format_path(pgp_pubkey_path[i], &path);
|
|
|
|
sc_pkcs15emu_add_pubkey(p15card, &id,
|
|
|
|
pgp_key_name[i],
|
|
|
|
SC_PKCS15_TYPE_PUBKEY_RSA,
|
|
|
|
1024, pubkey_usage[i],
|
2003-11-20 15:41:28 +00:00
|
|
|
&path, 0, &auth_id);
|
2003-10-31 12:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
failed: sc_error(card->ctx, "Failed to initialize OpenPGP emulation: %s\n",
|
|
|
|
sc_strerror(r));
|
|
|
|
return r;
|
|
|
|
|
|
|
|
}
|