opensc/src/pkcs15init/pkcs15-cflex.c

689 lines
19 KiB
C
Raw Normal View History

/*
* Cryptoflex specific operation for PKCS #15 initialization
*
* Copyright (C) 2002 Juha Yrj<EFBFBD>l<EFBFBD> <juha.yrjola@iki.fi>
*
* 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 <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <opensc/opensc.h>
#include <opensc/cardctl.h>
#include "pkcs15-init.h"
#include "profile.h"
#define FLEX_EXTKEY_ATTEMPTS 3
static const char *TMP_PIN = "0000";
static const char *TMP_PUK = "000000";
static void invert_buf(u8 *dest, const u8 *src, size_t c);
static int cflex_update_pin(struct sc_profile *profile, struct sc_card *card,
sc_file_t *file,
const u8 *pin, size_t pin_len, int pin_tries,
const u8 *puk, size_t puk_len, int puk_tries);
static int cflex_delete_file(struct sc_card *card, struct sc_profile *profile,
struct sc_file *df)
{
struct sc_path path;
struct sc_file *parent;
int r = 0;
/* Select the parent DF */
path = df->path;
path.len -= 2;
r = sc_select_file(card, &path, &parent);
if (r < 0)
return r;
r = sc_pkcs15init_authenticate(profile, card, parent, SC_AC_OP_DELETE);
sc_file_free(parent);
if (r < 0)
return r;
/* cryptoflex has no ERASE AC */
memset(&path, 0, sizeof(path));
path.type = SC_PATH_TYPE_FILE_ID;
path.value[0] = df->id >> 8;
path.value[1] = df->id & 0xFF;
path.len = 2;
card->ctx->log_errors = 0;
r = sc_delete_file(card, &path);
card->ctx->log_errors = 1;
return r;
}
/*
* Erase the card via rm
*/
static int cflex_erase_card(struct sc_profile *profile, struct sc_card *card)
{
struct sc_file *df = profile->df_info->file, *dir;
int r;
/* Delete EF(DIR). This may not be very nice
* against other applications that use this file, but
* extremely useful for testing :)
* Note we need to delete if before the DF because we create
* it *after* the DF.
* */
if (sc_profile_get_file(profile, "DIR", &dir) >= 0) {
r = cflex_delete_file(card, profile, dir);
sc_file_free(dir);
if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND)
goto out;
}
r=cflex_delete_file(card, profile, df);
/* Unfrob the SO pin reference, and return */
out: sc_profile_forget_secrets(profile, SC_AC_CHV, -1);
sc_free_apps(card);
if (r == SC_ERROR_FILE_NOT_FOUND)
r=0;
return r;
}
/*
* Initialize the Application DF
*/
static int cflex_init_app(struct sc_profile *profile, struct sc_card *card,
const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len)
{
sc_file_t *pinfile, *keyfile = NULL, *userpinfile;
struct sc_pkcs15_pin_info sopin, tmpinfo;
int pin_tries, puk_tries;
int block_extkey = 1;
int r;
char extkey_contents[15];
if (pin && pin_len) {
if (sc_profile_get_file(profile, "sopinfile", &pinfile) < 0) {
profile->cbs->error("Profile doesn't define \"sopinfile\"");
return SC_ERROR_NOT_SUPPORTED;
}
if (sc_profile_get_file(profile, "extkey", &keyfile) < 0) {
profile->cbs->error("Profile doesn't define \"extkey\"");
return SC_ERROR_NOT_SUPPORTED;
}
if (pin_len > 8)
pin_len = 8;
if (puk_len > 8)
puk_len = 8;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin);
sopin.reference=0x2; /* XXX where did this come from? */
memcpy(&sopin.path, &profile->df_info->file->path, sizeof(sc_path_t));
sc_profile_set_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin);
}
/* Create the application DF */
if (sc_pkcs15init_create_file(profile, card, profile->df_info->file))
return 1;
if (pin && pin_len) {
pin_tries = sopin.tries_left;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &tmpinfo);
puk_tries = tmpinfo.tries_left;
r = cflex_update_pin(profile, card, pinfile, pin, pin_len, pin_tries,
puk, puk_len, puk_tries);
if (r) {
profile->cbs->error("update_pin failed for SOPIN\n");
return r;
}
block_extkey = 1;
}
/* If the user pin file isn't in a sub-DF of the pkcs15 DF, create
* it now with a temporary PIN/PUK and don't fill in the AODF yet.
* This is the case if the 'onepin' profile is used. */
if (sc_profile_get_file(profile, "pinfile-1", &userpinfile) >= 0 &&
userpinfile->path.len == profile->df_info->file->path.len + 2) {
if (sc_profile_get_file(profile, "extkey", &keyfile) < 0) {
profile->cbs->error("Profile doesn't define \"extkey\"");
return SC_ERROR_NOT_SUPPORTED;
}
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo);
pin_tries = tmpinfo.tries_left;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &tmpinfo);
puk_tries = tmpinfo.tries_left;
r = cflex_update_pin(profile, card, userpinfile,
(const u8 *) TMP_PIN, strlen(TMP_PIN), pin_tries,
(const u8 *) TMP_PUK, strlen(TMP_PUK), puk_tries);
if (r != 0) {
profile->cbs->error("Couldn't create PIN file\n");
return r;
}
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo);
tmpinfo.reference = 0x1;
memcpy(&tmpinfo.path, &userpinfile->path, sizeof(sc_path_t));
sc_profile_set_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo);
sc_profile_set_secret(profile, SC_AC_CHV, 1, (const u8 *) TMP_PIN, strlen(TMP_PIN));
/* Don't block the AUT1 key yet, (unless there's an SO pin), this is done
* only after 'officially' creating the PIN with sc_pkcs15init_store_pin() */
block_extkey = 0;
}
if (keyfile != NULL) {
int keylen = 8;
memset(&extkey_contents, 0, sizeof(extkey_contents));
extkey_contents[0]=0; /* RFU */
extkey_contents[1]=1; /* skip AUT0 */
extkey_contents[2]=8; /* AUT1 length; single DES */
extkey_contents[3]=0; /* single DES */
/* Fill in the same AUT1 key value as in the one in the MF */
r = sc_profile_get_secret(profile, SC_AC_AUT, 1,
extkey_contents + 4, &keylen);
if (r < 0) {
profile->cbs->error("sc_profile_get_secret(AUT1) failed: %d\n", r);
return r;
}
extkey_contents[12]=FLEX_EXTKEY_ATTEMPTS; /* # allowed verification attempts */
if (block_extkey)
extkey_contents[13]=255; /* block key */
else
extkey_contents[13]=FLEX_EXTKEY_ATTEMPTS; /* remaining attempts */
extkey_contents[14]=0; /* no more keys */
r=sc_pkcs15init_update_file(profile, card, keyfile,
extkey_contents, 15);
if (r != 15) {
profile->cbs->error("update_file failed for extkey file\n");
return r;
}
}
return 0;
}
/*
* Update the contents of a PIN file
*/
static int cflex_update_pin(struct sc_profile *profile, struct sc_card *card,
sc_file_t *file,
const u8 *pin, size_t pin_len, int pin_tries,
const u8 *puk, size_t puk_len, int puk_tries)
{
u8 buffer[23], *p = buffer;
int r;
size_t len;
memset(p, 0xFF, 3);
p += 3;
memset(p, profile->pin_pad_char, 8);
strncpy((char *) p, (const char *) pin, pin_len);
p += 8;
*p++ = pin_tries;
*p++ = pin_tries;
memset(p, profile->pin_pad_char, 8);
strncpy((char *) p, (const char *) puk, puk_len);
p += 8;
*p++ = puk_tries;
*p++ = puk_tries;
len = 23;
r = sc_pkcs15init_update_file(profile, card, file, buffer, len);
if (r < 0)
return r;
return 0;
}
/*
* Store a PIN
*/
static int
cflex_new_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)
{
sc_file_t *pinfile;
struct sc_pkcs15_pin_info tmpinfo;
char _template[30];
int pin_tries, puk_tries;
int r;
index++;
sprintf(_template, "pinfile-%d", index);
/* Profile must define a "pinfile" for each PIN */
if (sc_profile_get_file(profile, _template, &pinfile) < 0) {
sprintf(_template, "pinfile-%d", 1);
/* Little hack: the 'so' profile start counting at pinfile-2
* because the SO pin has index 1. But for the 'onepin' profile,
* you may not have an SO pin and so the user pin is pinfile-1 */
if (sc_profile_get_file(profile, _template, &pinfile) >= 0 &&
pinfile->path.len == profile->df_info->file->path.len + 2) {
index = 1; /* OK, it's the 'onepin' profile */
}
else {
sprintf(_template, "pinfile-%d", index);
profile->cbs->error("Profile doesn't define \"%s\"", _template);
return SC_ERROR_INVALID_ARGUMENTS;
}
}
info->path = pinfile->path;
if (info->path.len > 2)
info->path.len -= 2;
info->reference = 1;
if (pin_len > 8)
pin_len = 8;
if (puk_len > 8)
puk_len = 8;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &tmpinfo);
pin_tries = tmpinfo.tries_left;
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &tmpinfo);
puk_tries = tmpinfo.tries_left;
r = cflex_update_pin(profile, card, pinfile, pin, pin_len, pin_tries,
puk, puk_len, puk_tries);
sc_file_free(pinfile);
/* In case of the 'onepin' profile, we have to block the AUT1 key now */
if (index == 1 && pinfile->path.len == profile->df_info->file->path.len + 2) {
unsigned char wrong_key[] = {0xBE, 0xBE, 0x0E, 0x0E, 1, 2, 4, 8};
int r1, i;
r1 = sc_select_file(card, &profile->df_info->file->path, NULL);
if (r1 < 0) {
profile->cbs->error("Couldn't select the pkcs15 DF: %d", r1);
return r1;
}
for (i = 0; i < FLEX_EXTKEY_ATTEMPTS; i++)
r1 = sc_verify(card, SC_AC_AUT, 1, wrong_key, sizeof(wrong_key), NULL);
if (r1 != SC_ERROR_AUTH_METHOD_BLOCKED) {
profile->cbs->error("Couldn't lock the AUT1 file in the pkcs15 DF: %d", r1);
return r1;
}
}
return r;
}
/*
* Allocate a file
*/
static int
cflex_new_file(struct sc_profile *profile, struct sc_card *card,
unsigned int type, unsigned int num,
struct sc_file **out)
{
struct sc_file *file;
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;
case SC_PKCS15_TYPE_PUBKEY_DSA:
desc = "DSA public key";
tag = "public-key";
break;
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)) {
profile->cbs->error("File type %X not supported by card driver", type);
return SC_ERROR_INVALID_ARGUMENTS;
}
type &= SC_PKCS15_TYPE_CLASS_MASK;
}
snprintf(name, sizeof(name), "template-%s-%d", tag, num+1);
if (sc_profile_get_file(profile, name, &file) < 0) {
profile->cbs->error("Profile doesn't define %s template '%s'\n",
desc, name);
return SC_ERROR_NOT_SUPPORTED;
}
*out = file;
return 0;
}
/*
* Get the EF-pubkey corresponding to the EF-prkey
*/
int
cflex_pubkey_file(struct sc_file **ret, struct sc_file *prkf, unsigned int size)
{
struct sc_file *pukf;
sc_file_dup(&pukf, prkf);
sc_file_clear_acl_entries(pukf, SC_AC_OP_READ);
sc_file_add_acl_entry(pukf, SC_AC_OP_READ, SC_AC_NONE, SC_AC_KEY_REF_NONE);
pukf->path.len -= 2;
sc_append_path_id(&pukf->path, (const u8 *) "\x10\x12", 2);
pukf->id = 0x1012;
pukf->size = size;
*ret = pukf;
return 0;
}
/*
* RSA key generation
*/
static int
cflex_generate_key(struct sc_profile *profile, struct sc_card *card,
unsigned int index, unsigned int keybits,
sc_pkcs15_pubkey_t *pubkey,
struct sc_pkcs15_prkey_info *info)
{
struct sc_cardctl_cryptoflex_genkey_info args;
struct sc_file *prkf = NULL, *pukf = NULL;
unsigned char raw_pubkey[256];
unsigned char pinbuf[12];
size_t pinlen;
int r, delete_pukf = 0;
if ((r = cflex_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, &prkf)) < 0)
goto failed;
switch (keybits) {
case 512: prkf->size = 166; break;
case 768: prkf->size = 246; break;
case 1024: prkf->size = 326; break;
case 2048: prkf->size = 646; break;
default:
profile->cbs->error("Unsupported key size %u\n", keybits);
return SC_ERROR_INVALID_ARGUMENTS;
}
if ((r = cflex_pubkey_file(&pukf, prkf, prkf->size + 3)) < 0)
goto failed;
/* Get the CHV1 PIN */
pinlen = sizeof(pinbuf);
memset(pinbuf, 0, sizeof(pinbuf));
if ((r = sc_pkcs15init_get_secret(profile, card, SC_AC_CHV, 1, pinbuf, &pinlen)) < 0)
goto failed;
if ((r = sc_pkcs15init_create_file(profile, card, prkf)) < 0
|| (r = sc_pkcs15init_create_file(profile, card, pukf)) < 0)
goto failed;
delete_pukf = 1;
/* Present the PIN */
if ((r = sc_select_file(card, &pukf->path, NULL))
|| (r = sc_verify(card, SC_AC_CHV, 1, pinbuf, pinlen, NULL)) < 0)
goto failed;
memset(&args, 0, sizeof(args));
args.exponent = 0x10001;
args.key_bits = keybits;
r = sc_card_ctl(card, SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, &args);
if (r < 0)
goto failed;
/* extract public key */
pubkey->algorithm = SC_ALGORITHM_RSA;
pubkey->u.rsa.modulus.len = keybits / 8;
pubkey->u.rsa.modulus.data = (u8 *) malloc(keybits / 8);
pubkey->u.rsa.exponent.len = 3;
pubkey->u.rsa.exponent.data = (u8 *) malloc(3);
memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3);
if ((r = sc_select_file(card, &pukf->path, NULL)) < 0
|| (r = sc_read_binary(card, 3, raw_pubkey, pubkey->u.rsa.modulus.len, 0)) < 0)
goto failed;
invert_buf(pubkey->u.rsa.modulus.data, raw_pubkey, pubkey->u.rsa.modulus.len);
info->key_reference = 1;
info->path = prkf->path;
failed: if (delete_pukf)
sc_pkcs15init_rmdir(card, profile, pukf);
if (r < 0)
sc_pkcs15init_rmdir(card, profile, prkf);
sc_file_free(pukf);
sc_file_free(prkf);
return r;
}
static void invert_buf(u8 *dest, const u8 *src, size_t c)
{
int i;
for (i = 0; i < c; i++)
dest[i] = src[c-1-i];
}
static int bn2cf(sc_pkcs15_bignum_t *num, u8 *buf)
{
invert_buf(buf, num->data, num->len);
return num->len;
}
static int cflex_encode_private_key(struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_num)
{
u8 buf[5 * 128 + 6], *p = buf;
u8 bnbuf[256];
int base = 0;
int r;
switch (rsa->modulus.len) {
case 512 / 8:
base = 32;
break;
case 768 / 8:
base = 48;
break;
case 1024 / 8:
base = 64;
break;
case 2048 / 8:
base = 128;
break;
}
if (base == 0) {
fprintf(stderr, "Key length invalid.\n");
return 2;
}
*p++ = (5 * base + 3) >> 8;
*p++ = (5 * base + 3) & 0xFF;
*p++ = key_num;
r = bn2cf(&rsa->p, bnbuf);
if (r != base) {
fprintf(stderr, "Invalid private key.\n");
return 2;
}
memcpy(p, bnbuf, base);
p += base;
r = bn2cf(&rsa->q, bnbuf);
if (r != base) {
fprintf(stderr, "Invalid private key.\n");
return 2;
}
memcpy(p, bnbuf, base);
p += base;
r = bn2cf(&rsa->iqmp, bnbuf);
if (r != base) {
fprintf(stderr, "Invalid private key.\n");
return 2;
}
memcpy(p, bnbuf, base);
p += base;
r = bn2cf(&rsa->dmp1, bnbuf);
if (r != base) {
fprintf(stderr, "Invalid private key.\n");
return 2;
}
memcpy(p, bnbuf, base);
p += base;
r = bn2cf(&rsa->dmq1, bnbuf);
if (r != base) {
fprintf(stderr, "Invalid private key.\n");
return 2;
}
memcpy(p, bnbuf, base);
p += base;
*p++ = 0;
*p++ = 0;
*p++ = 0;
memcpy(key, buf, p - buf);
*keysize = p - buf;
return 0;
}
static int cflex_encode_public_key(struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_num)
{
u8 buf[5 * 128 + 10], *p = buf;
u8 bnbuf[256];
int base = 0;
int r;
switch (rsa->modulus.len) {
case 512 / 8:
base = 32;
break;
case 768 / 8:
base = 48;
break;
case 1024 / 8:
base = 64;
break;
case 2048 / 8:
base = 128;
break;
}
if (base == 0) {
fprintf(stderr, "Key length invalid.\n");
return 2;
}
*p++ = (5 * base + 7) >> 8;
*p++ = (5 * base + 7) & 0xFF;
*p++ = key_num;
r = bn2cf(&rsa->modulus, bnbuf);
if (r != 2*base) {
fprintf(stderr, "Invalid public key.\n");
return 2;
}
memcpy(p, bnbuf, 2*base);
p += 2*base;
memset(p, 0, base);
p += base;
memset(bnbuf, 0, 2*base);
memcpy(p, bnbuf, 2*base);
p += 2*base;
r = bn2cf(&rsa->exponent, bnbuf);
memcpy(p, bnbuf, 4);
p += 4;
*p++ = 0;
*p++ = 0;
*p++ = 0;
memcpy(key, buf, p - buf);
*keysize = p - buf;
return 0;
}
/*
* Store a private key
*/
static int
cflex_new_key(struct sc_profile *profile, struct sc_card *card,
struct sc_pkcs15_prkey *key, unsigned int index,
struct sc_pkcs15_prkey_info *info)
{
u8 prv[1024], pub[1024];
size_t prvsize, pubsize;
struct sc_file *keyfile = NULL, *tmpfile = NULL;
struct sc_pkcs15_prkey_rsa *rsa = NULL;
int r;
if (key->algorithm != SC_ALGORITHM_RSA) {
profile->cbs->error("Cryptoflex supports only RSA keys.");
return SC_ERROR_NOT_SUPPORTED;
}
rsa = &key->u.rsa;
r = cflex_encode_private_key(rsa, prv, &prvsize, 1);
if (r)
goto err;
r = cflex_encode_public_key(rsa, pub, &pubsize, 1);
if (r)
goto err;
printf("Updating RSA private key...\n");
r = cflex_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index,
&keyfile);
if (r < 0)
goto err;
keyfile->size = prvsize;
r = sc_pkcs15init_update_file(profile, card, keyfile, prv, prvsize);
if (r < 0)
goto err;
info->path = keyfile->path;
info->modulus_length = rsa->modulus.len << 3;
if ((r = cflex_pubkey_file(&tmpfile, keyfile, pubsize)) < 0)
goto err;
printf("Updating RSA public key...\n");
r = sc_pkcs15init_update_file(profile, card, tmpfile, pub, pubsize);
err:
if (tmpfile)
sc_file_free(tmpfile);
return r;
}
struct sc_pkcs15init_operations sc_pkcs15init_cflex_operations = {
cflex_erase_card,
cflex_init_app,
cflex_new_pin,
cflex_new_key,
cflex_new_file,
cflex_generate_key,
};