2003-12-29 12:28:37 +00:00
|
|
|
/*
|
|
|
|
* JCOP specific operation for PKCS15 initialization
|
|
|
|
*
|
|
|
|
* Copyright 2003 Chaskiel Grundman <cg2v@andrew.cmu.edu>
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2003-12-29 12:28:37 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdarg.h>
|
2010-03-04 08:14:36 +00:00
|
|
|
|
|
|
|
#include "libopensc/opensc.h"
|
|
|
|
#include "libopensc/cardctl.h"
|
|
|
|
#include "libopensc/log.h"
|
2003-12-29 12:28:37 +00:00
|
|
|
#include "pkcs15-init.h"
|
|
|
|
#include "profile.h"
|
|
|
|
|
|
|
|
#define JCOP_MAX_PINS 3
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Erase the card
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) {
|
2003-12-29 12:28:37 +00:00
|
|
|
/* later */
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2010-02-02 14:50:56 +00:00
|
|
|
#if 0
|
2003-12-29 12:28:37 +00:00
|
|
|
/*
|
|
|
|
* Create a new DF
|
|
|
|
* This will usually be the application DF
|
|
|
|
* for JCOP, it must be the application DF. no other DF's may exist.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
jcop_init_app(sc_profile_t *profile, sc_card_t *card,
|
|
|
|
struct sc_pkcs15_pin_info *pin_info,
|
|
|
|
const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) {
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2010-02-02 14:50:56 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file)
|
2010-02-02 14:50:56 +00:00
|
|
|
{
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2003-12-29 12:28:37 +00:00
|
|
|
/*
|
|
|
|
* Select a PIN reference
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
|
2003-12-29 12:28:37 +00:00
|
|
|
sc_pkcs15_pin_info_t *pin_info) {
|
|
|
|
int preferred, current;
|
|
|
|
|
|
|
|
if ((current = pin_info->reference) < 0)
|
|
|
|
current = 0;
|
|
|
|
|
|
|
|
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) {
|
|
|
|
preferred = 3;
|
|
|
|
} else {
|
|
|
|
preferred = current;
|
|
|
|
if (preferred < 1)
|
|
|
|
preferred=1;
|
|
|
|
if (preferred > 2)
|
|
|
|
return SC_ERROR_TOO_MANY_OBJECTS;
|
|
|
|
}
|
|
|
|
if (current > preferred)
|
|
|
|
return SC_ERROR_TOO_MANY_OBJECTS;
|
|
|
|
pin_info->reference = preferred;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store a PIN
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df,
|
2003-12-29 12:28:37 +00:00
|
|
|
sc_pkcs15_object_t *pin_obj,
|
|
|
|
const unsigned char *pin, size_t pin_len,
|
|
|
|
const unsigned char *puk, size_t puk_len)
|
|
|
|
{
|
|
|
|
sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data;
|
|
|
|
unsigned char nulpin[16];
|
|
|
|
unsigned char padpin[16];
|
|
|
|
int r, type;
|
|
|
|
|
|
|
|
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) {
|
|
|
|
type = SC_PKCS15INIT_SO_PIN;
|
|
|
|
|
|
|
|
/* SO PIN reference must be 0 */
|
|
|
|
if (pin_info->reference != 3)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
} else {
|
|
|
|
type = SC_PKCS15INIT_USER_PIN;
|
|
|
|
if (pin_info->reference >= 3)
|
|
|
|
return SC_ERROR_TOO_MANY_OBJECTS;
|
|
|
|
}
|
|
|
|
if (puk != NULL && puk_len > 0) {
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_select_file(p15card->card, &df->path, NULL);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/* Current PIN is 00:00:00:00:00:00:00:00... */
|
|
|
|
memset(nulpin, 0, sizeof(nulpin));
|
|
|
|
memset(padpin, 0, sizeof(padpin));
|
|
|
|
memcpy(padpin, pin, pin_len);
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_change_reference_data(p15card->card, SC_AC_CHV,
|
2003-12-29 12:28:37 +00:00
|
|
|
pin_info->reference,
|
|
|
|
nulpin, sizeof(nulpin),
|
|
|
|
padpin, sizeof(padpin), NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
pin_info->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new key file
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj)
|
2003-12-29 12:28:37 +00:00
|
|
|
{
|
|
|
|
sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
|
2005-03-09 00:04:44 +00:00
|
|
|
sc_file_t *keyfile = NULL;
|
2003-12-29 12:28:37 +00:00
|
|
|
size_t bytes, mod_len, exp_len, prv_len, pub_len;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys.");
|
2003-12-29 12:28:37 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
/* The caller is supposed to have chosen a key file path for us */
|
|
|
|
if (key_info->path.len == 0 || key_info->modulus_length == 0)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
/* Get the file we're supposed to create */
|
|
|
|
r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
mod_len = key_info->modulus_length / 8;
|
|
|
|
exp_len = 4;
|
|
|
|
bytes = mod_len / 2;
|
|
|
|
pub_len = 2 + mod_len + exp_len;
|
|
|
|
prv_len = 2 + 5 * bytes;
|
|
|
|
keyfile->size = prv_len;
|
|
|
|
|
|
|
|
/* Fix up PIN references in file ACL */
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_fixup_file(profile, p15card, keyfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
|
|
|
|
if (r >= 0)
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_create_file(profile, p15card, keyfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
|
|
|
|
if (keyfile)
|
|
|
|
sc_file_free(keyfile);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
jcop_bn2bin(unsigned char *dest, sc_pkcs15_bignum_t *bn, unsigned int size)
|
|
|
|
{
|
|
|
|
u8 *src;
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
assert(bn->len <= size);
|
|
|
|
memset(dest, 0, size);
|
|
|
|
for (n = size-bn->len, src = bn->data; n < size; n++,src++)
|
|
|
|
dest[n] = *src;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store a private key
|
|
|
|
* Private key file formats: (transparent file)
|
|
|
|
* Non-CRT:
|
|
|
|
* byte 0 0x05
|
|
|
|
* byte 1 Modulus length (in byte/4)
|
|
|
|
* byte 2 Modulus (n)
|
|
|
|
* byte 2+x private exponent (d)
|
|
|
|
*
|
|
|
|
* CRT:
|
|
|
|
* byte 0 0x06
|
|
|
|
* byte 1 component length (in byte/2; component length is half
|
|
|
|
* of modulus length
|
|
|
|
* byte 2 Prime (p)
|
|
|
|
* byte 2+x Prime (q)
|
|
|
|
* byte 2+2*x Exponent 1 (d mod (p-1))
|
|
|
|
* byte 2+3*x Exponent 2 (d mod (q-1))
|
|
|
|
* byte 2+4*x Coefficient ((p ^ -1) mod q
|
|
|
|
*
|
|
|
|
* We use the CRT format, since that's what key generation does.
|
|
|
|
*
|
|
|
|
* Numbers are stored big endian.
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
|
2003-12-29 12:28:37 +00:00
|
|
|
sc_pkcs15_object_t *obj,
|
|
|
|
sc_pkcs15_prkey_t *key)
|
|
|
|
{
|
|
|
|
sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
|
|
|
|
sc_file_t *keyfile;
|
|
|
|
unsigned char keybuf[1024];
|
|
|
|
size_t size,base;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys.");
|
2003-12-29 12:28:37 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
r = sc_profile_get_file_by_path(profile, &key_info->path, &keyfile);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
base=key_info->modulus_length / 16;
|
|
|
|
size=2+5*base;
|
|
|
|
keybuf[0]=6;
|
|
|
|
keybuf[1]=base/4;
|
|
|
|
jcop_bn2bin(&keybuf[2 + 0 * base], &key->u.rsa.p, base);
|
|
|
|
jcop_bn2bin(&keybuf[2 + 1 * base], &key->u.rsa.q, base);
|
|
|
|
jcop_bn2bin(&keybuf[2 + 2 * base], &key->u.rsa.dmp1, base);
|
|
|
|
jcop_bn2bin(&keybuf[2 + 3 * base], &key->u.rsa.dmq1, base);
|
|
|
|
jcop_bn2bin(&keybuf[2 + 4 * base], &key->u.rsa.iqmp, base);
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_update_file(profile, p15card, keyfile, keybuf, size);
|
2003-12-29 12:28:37 +00:00
|
|
|
|
|
|
|
sc_file_free(keyfile);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a keypair
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-21 16:21:57 +00:00
|
|
|
jcop_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
|
2003-12-29 12:28:37 +00:00
|
|
|
sc_pkcs15_object_t *obj,
|
2010-02-21 16:21:57 +00:00
|
|
|
sc_pkcs15_pubkey_t *pubkey)
|
|
|
|
{
|
2003-12-29 12:28:37 +00:00
|
|
|
sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data;
|
|
|
|
struct sc_cardctl_jcop_genkey args;
|
|
|
|
sc_file_t *temppubfile=NULL, *keyfile=NULL;
|
|
|
|
unsigned char *keybuf=NULL;
|
|
|
|
size_t bytes, mod_len, exp_len, pub_len, keybits;
|
|
|
|
int r,delete_ok=0;
|
|
|
|
|
|
|
|
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
|
2010-03-15 12:17:13 +00:00
|
|
|
sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys.");
|
2003-12-29 12:28:37 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
r=sc_profile_get_file(profile, "temp-pubkey", &temppubfile);
|
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_select_file(p15card->card, &key_info->path, &keyfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
mod_len = key_info->modulus_length / 8;
|
|
|
|
exp_len = 4;
|
|
|
|
bytes = mod_len / 2;
|
|
|
|
pub_len = 2 + mod_len + exp_len;
|
|
|
|
temppubfile->size = pub_len;
|
|
|
|
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_fixup_file(profile, p15card, temppubfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_create_file(profile, p15card, temppubfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
delete_ok=1;
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_authenticate(profile, p15card, temppubfile, SC_AC_OP_UPDATE);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_pkcs15init_authenticate(profile, p15card, keyfile, SC_AC_OP_UPDATE);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
keybits = key_info->modulus_length;
|
|
|
|
|
|
|
|
/* generate key */
|
|
|
|
/* keysize is _not_ passed to the card at any point. it appears to
|
|
|
|
infer it from the file size */
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
args.exponent = 0x10001;
|
|
|
|
sc_append_file_id(&args.pub_file_ref, temppubfile->id);
|
|
|
|
sc_append_file_id(&args.pri_file_ref, keyfile->id);
|
2004-01-08 15:23:11 +00:00
|
|
|
keybuf=(unsigned char *) malloc(keybits / 8);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (!keybuf) {
|
|
|
|
r=SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
args.pubkey = keybuf;
|
|
|
|
args.pubkey_len = keybits / 8;
|
|
|
|
|
2010-02-21 16:21:57 +00:00
|
|
|
r = sc_card_ctl(p15card->card, SC_CARDCTL_JCOP_GENERATE_KEY, (void *)&args);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (r < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* extract public key */
|
|
|
|
pubkey->algorithm = SC_ALGORITHM_RSA;
|
|
|
|
pubkey->u.rsa.modulus.len = keybits / 8;
|
|
|
|
pubkey->u.rsa.modulus.data = keybuf;
|
|
|
|
pubkey->u.rsa.exponent.len = 3;
|
|
|
|
pubkey->u.rsa.exponent.data = (u8 *) malloc(3);
|
|
|
|
if (!pubkey->u.rsa.exponent.data) {
|
|
|
|
pubkey->u.rsa.modulus.data = NULL;
|
|
|
|
r=SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (r < 0 && keybuf)
|
|
|
|
free(keybuf);
|
|
|
|
if (delete_ok)
|
2010-02-21 16:21:57 +00:00
|
|
|
sc_pkcs15init_rmdir(p15card, profile, temppubfile);
|
2003-12-29 12:28:37 +00:00
|
|
|
if (keyfile)
|
|
|
|
sc_file_free(keyfile);
|
|
|
|
if (temppubfile)
|
|
|
|
sc_file_free(temppubfile);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-08-10 21:31:18 +00:00
|
|
|
static struct sc_pkcs15init_operations sc_pkcs15init_jcop_operations = {
|
2005-08-14 22:33:43 +00:00
|
|
|
jcop_erase_card,
|
|
|
|
NULL, /* init_card */
|
2010-02-02 14:50:56 +00:00
|
|
|
jcop_create_dir,
|
2005-08-14 22:33:43 +00:00
|
|
|
NULL, /* create_domain */
|
|
|
|
jcop_select_pin_reference,
|
|
|
|
jcop_create_pin,
|
|
|
|
NULL, /* select_key_reference */
|
|
|
|
jcop_create_key,
|
|
|
|
jcop_store_key,
|
|
|
|
jcop_generate_key,
|
|
|
|
NULL, NULL, /* encode private/public key */
|
|
|
|
NULL, /* finalize_card */
|
2010-03-28 11:37:13 +00:00
|
|
|
NULL, /* delete_object */
|
|
|
|
NULL, NULL, NULL, NULL /* pkcs15init emulation */
|
2005-08-10 21:31:18 +00:00
|
|
|
};
|
2003-12-29 12:28:37 +00:00
|
|
|
|
|
|
|
struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void)
|
|
|
|
{
|
|
|
|
return &sc_pkcs15init_jcop_operations;
|
|
|
|
}
|
|
|
|
|
|
|
|
|