diff --git a/src/libopensc/card-etoken.c b/src/libopensc/card-etoken.c index ae968940..fd715ba0 100644 --- a/src/libopensc/card-etoken.c +++ b/src/libopensc/card-etoken.c @@ -472,6 +472,9 @@ static int etoken_create_file(struct sc_card *card, struct sc_file *file) } r = iso_ops->create_file(card, file); + /* FIXME: if this is a DF and there's an AID, set it here + * using PUT_DATA_FCI */ + out: SC_FUNC_RETURN(card->ctx, 1, r); } @@ -551,6 +554,48 @@ etoken_put_data_fci(struct sc_card *card, return r; } +static int +etoken_generate_key(struct sc_card *card, + struct sc_cardctl_etoken_genkey_info *args) +{ + struct sc_apdu apdu; + u8 data[8]; + int r; + + if (args->random_len) { + /* XXX FIXME: do a GIVE_RANDOM command with this data */ + error(card->ctx, + "initialization of card's random pool " + "not yet implemented\n"); + return SC_ERROR_INTERNAL; + } + + data[0] = 0x20; /* store as PSO object */ + data[1] = args->key_id; + data[2] = args->fid >> 8; + data[3] = args->fid & 0xff; + data[4] = 0; /* additional Rabin Miller tests */ + data[5] = 0; /* length difference between p, q (bits) */ + data[6] = 0; /* default length of exponent, MSB */ + data[7] = 0; /* default length of exponent, LSB */ + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.cla = 0x00; + apdu.ins = 0x46; + apdu.p1 = 0x00; + apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */ + apdu.data= data; + apdu.datalen = apdu.lc = sizeof(data); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "GENERATE_KEY failed"); + + return r; +} + static int etoken_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { @@ -561,6 +606,9 @@ etoken_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) return etoken_put_data_fci(card, (struct sc_cardctl_etoken_pin_info *) ptr); break; + case SC_CARDCTL_ETOKEN_GENERATE_KEY: + return etoken_generate_key(card, + (struct sc_cardctl_etoken_genkey_info *) ptr); } return SC_ERROR_NOT_SUPPORTED; } diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 98edf140..e09a063a 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -58,7 +58,8 @@ enum { */ SC_CARDCTL_ETOKEN_BASE = _CTL_PREFIX('E', 'T', 'K'), SC_CARDCTL_ETOKEN_PUT_DATA_FCI, - SC_CARDCTL_ETOKEN_PUT_DATA_OCI + SC_CARDCTL_ETOKEN_PUT_DATA_OCI, + SC_CARDCTL_ETOKEN_GENERATE_KEY }; /* @@ -113,8 +114,16 @@ struct sc_cardctl_miocos_ac_info { * eToken PIN info */ struct sc_cardctl_etoken_pin_info { - u8 *data; - size_t len; + u8 * data; + size_t len; +}; + +struct sc_cardctl_etoken_genkey_info { + u8 * random_data; + size_t random_len; + unsigned int key_id; + unsigned int key_bits; + unsigned short fid; }; #ifdef __cplusplus diff --git a/src/pkcs15init/pkcs15-etoken.c b/src/pkcs15init/pkcs15-etoken.c index f0c3aeca..b87c9874 100644 --- a/src/pkcs15init/pkcs15-etoken.c +++ b/src/pkcs15init/pkcs15-etoken.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pkcs15-init.h" #include "profile.h" @@ -41,6 +42,14 @@ struct tlv { unsigned char * current; unsigned char * next; }; +#define RSAKEY_MAX_BITS 1024 +#define RSAKEY_MAX_SIZE (RSAKEY_MAX_BITS/8) +struct rsakey { + struct bignum { + size_t len; + u8 data[RSAKEY_MAX_SIZE]; + } n, d; +}; /* * Local functions @@ -57,8 +66,10 @@ static void error(struct sc_profile *, const char *, ...); #define ETOKEN_PIN_ID(idx) (((idx) << 1) + 0x01) #define ETOKEN_PUK_ID(idx) (((idx) << 1) + 0x02) #define ETOKEN_MAX_PINS 0x10 +#define ETOKEN_KEY_ID(idx) (0x40 + (idx)) #define ETOKEN_AC_NEVER 0xFF +#define ETOKEN_ALGO_RSA 0x08 #define ETOKEN_ALGO_PIN 0x87 #if 0 @@ -195,7 +206,7 @@ static int etoken_erase(struct sc_profile *profile, struct sc_card *card) { struct sc_pkcs15_pin_info sopin, temp; - struct sc_file *df = profile->df_info->file; + struct sc_file *df = profile->df_info->file, *dir; int r; /* Frob: need to tell the upper layers about the SO PIN id */ @@ -214,8 +225,12 @@ etoken_erase(struct sc_profile *profile, struct sc_card *card) r = etoken_rm_rf(profile, card, df); sc_file_free(df); - if (r >= 0) { - /* Delete 2F00? */ + /* Delete EF(DIR) as well. This may not be very nice + * against other applications that use this file, but + * extremely useful for testing :) */ + if (r >= 0 && sc_profile_get_file(profile, "DIR", &dir) >= 0) { + r = etoken_rm_rf(profile, card, dir); + sc_file_free(dir); } /* Unfrob the SO pin reference, and return */ @@ -228,7 +243,7 @@ out: sc_profile_set_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); * Initialize pin file */ static int -etoken_new_pin(struct sc_profile *profile, struct sc_card *card, +etoken_store_pin(struct sc_profile *profile, struct sc_card *card, int pin_type, unsigned int pin_id, unsigned int puk_id, const u8 *pin, size_t pin_len) { @@ -330,13 +345,13 @@ etoken_init_app(struct sc_profile *profile, struct sc_card *card, if (r >= 0 && puk && puk_len) { puk_id = ETOKEN_PUK_ID(0); - r = etoken_new_pin(profile, card, + r = etoken_store_pin(profile, card, SC_PKCS15INIT_SO_PUK, puk_id, ETOKEN_AC_NEVER, puk, puk_len); } if (r >= 0) { - r = etoken_new_pin(profile, card, + r = etoken_store_pin(profile, card, SC_PKCS15INIT_SO_PIN, ETOKEN_PIN_ID(0), puk_id, pin, pin_len); @@ -350,7 +365,7 @@ etoken_init_app(struct sc_profile *profile, struct sc_card *card, * Store a PIN */ static int -etoken_put_pin(struct sc_profile *profile, struct sc_card *card, +etoken_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) @@ -371,7 +386,7 @@ etoken_put_pin(struct sc_profile *profile, struct sc_card *card, if (puk && puk_len) { puk_id = ETOKEN_PUK_ID(index); - r = etoken_new_pin(profile, card, + r = etoken_store_pin(profile, card, SC_PKCS15INIT_USER_PUK, puk_id, ETOKEN_AC_NEVER, puk, puk_len); @@ -379,7 +394,7 @@ etoken_put_pin(struct sc_profile *profile, struct sc_card *card, if (r >= 0) { pin_id = ETOKEN_PIN_ID(index); - r = etoken_new_pin(profile, card, + r = etoken_store_pin(profile, card, SC_PKCS15INIT_USER_PIN, pin_id, puk_id, pin, pin_len); @@ -390,6 +405,73 @@ etoken_put_pin(struct sc_profile *profile, struct sc_card *card, return r; } +/* + * Create a private key object + */ +#define ETOKEN_KEY_OPTIONS 0x82 +#define ETOKEN_KEY_FLAGS 0x40 +static int +etoken_store_key_component(struct sc_card *card, + unsigned int key_id, unsigned int pin_id, + unsigned int num, + const u8 *data, size_t len, + int last) +{ + struct sc_cardctl_etoken_pin_info args; + struct tlv tlv; + unsigned char buffer[256]; + + /* Initialize the TLV encoder */ + tlv_init(&tlv, buffer, sizeof(buffer)); + + /* Object address */ + tlv_next(&tlv, 0x83); + tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ + tlv_add(&tlv, key_id); + + /* Object parameters */ + tlv_next(&tlv, 0x85); + tlv_add(&tlv, ETOKEN_KEY_OPTIONS|(last? 0x00 : 0x20)); + tlv_add(&tlv, ETOKEN_KEY_FLAGS); + tlv_add(&tlv, ETOKEN_ALGO_RSA); + tlv_add(&tlv, 0x00); + tlv_add(&tlv, 0xFF); + tlv_add(&tlv, 0x00); + tlv_add(&tlv, 0x00); + tlv_add(&tlv, 0x00); + + /* AC bytes */ + tlv_next(&tlv, 0x86); + tlv_add(&tlv, pin_id); /* AC USE */ + tlv_add(&tlv, pin_id); /* AC CHANGE */ + tlv_add(&tlv, 0x00); /* AC GENKEY? */ + + /* modulus */ + tlv_next(&tlv, 0x8f); + while (len--) + tlv_add(&tlv, *data++); + + args.data = buffer; + args.len = tlv_len(&tlv); + return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args); +} + +static int +etoken_store_key(struct sc_profile *profile, struct sc_card *card, + unsigned int key_id, unsigned int pin_id, + struct rsakey *key) +{ + int r; + + r = etoken_store_key_component(card, key_id, pin_id, + 0, key->n.data, key->n.len, 0); + if (r < 0) + return r; + r = etoken_store_key_component(card, key_id, pin_id, + 1, key->d.data, key->d.len, 1); + return r; +} + /* * Store a private key */ @@ -479,6 +561,70 @@ etoken_new_file(struct sc_profile *profile, struct sc_card *card, return 0; } +static int +etoken_generate_key(struct sc_profile *profile, struct sc_card *card, + unsigned int index, unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey) +{ + struct sc_cardctl_etoken_genkey_info args; + struct sc_file *temp; + struct rsakey key_obj; + u8 randbuf[64], key_id, pin_id; + int r; + + keybits &= ~7UL; + if (keybits > RSAKEY_MAX_BITS) { + error(profile, "Unable to generate key, max size is %d\n", + RSAKEY_MAX_BITS); + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (sc_profile_get_file(profile, "tempfile", &temp) < 0) { + error(profile, "Profile doesn't define temporary file " + "for key generation.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + if ((r = sc_pkcs15init_create_file(profile, card, temp)) < 0) + goto out; + + key_id = ETOKEN_KEY_ID(index); + pin_id = 0x00; /* XXX extract user pin id */ + + /* Create a key object, initializing components to 0xff */ + memset(&key_obj, 0xff, sizeof(key_obj)); + key_obj.n.len = keybits >> 3; + key_obj.d.len = keybits >> 3; + r = etoken_store_key(profile, card, key_id, pin_id, &key_obj); + if (r < 0) + goto out; + + memset(&args, 0, sizeof(args)); +#ifdef notyet + if ((r = scrandom_get_data(randbuf, sizeof(randbuf))) < 0) + goto out; + + /* For now, we have to rely on the card's internal number + * generator because libscrandom is static, which causes + * all sorts of headaches when linking against it + * (some platforms don't allow non-PIC code in a shared lib, + * such as ia64). + */ + args.random_data = randbuf; + args.random_len = sizeof(randbuf); +#endif + args.key_id = key_id; + args.key_bits = keybits; + args.fid = temp->id; + r = sc_card_ctl(card, SC_CARDCTL_ETOKEN_GENERATE_KEY, &args); + memset(randbuf, 0, sizeof(randbuf)); + if (r < 0) + goto out; + + /* XXX: extract public key from file and delete it */ + +out: sc_file_free(temp); + return r; +} static void error(struct sc_profile *profile, const char *fmt, ...) @@ -496,7 +642,8 @@ error(struct sc_profile *profile, const char *fmt, ...) struct sc_pkcs15init_operations sc_pkcs15init_etoken_operations = { etoken_erase, etoken_init_app, - etoken_put_pin, + etoken_new_pin, etoken_new_key, etoken_new_file, + etoken_generate_key };