/* * Cryptoflex specific operation for PKCS #15 initialization * * Copyright (C) 2002 Juha Yrjölä * * 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 "config.h" #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" static void invert_buf(u8 *dest, const u8 *src, size_t c); static int cflex_create_dummy_chvs(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *, int, sc_file_t **); static void cflex_delete_dummy_chvs(sc_profile_t *, sc_pkcs15_card_t *, int, sc_file_t **); static int cflex_create_pin_file(sc_profile_t *, sc_pkcs15_card_t *, sc_path_t *, int, const u8 *, size_t, int, const u8 *, size_t, int, sc_file_t **, int); static int cflex_create_empty_pin_file(sc_profile_t *, sc_pkcs15_card_t *, sc_path_t *, int, sc_file_t **); static int cflex_get_keyfiles(sc_profile_t *, sc_card_t *, const sc_path_t *, sc_file_t **, sc_file_t **); unsigned char dummy_pin_value[6] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30}; static int cflex_delete_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { sc_path_t path; sc_file_t *parent; int r = 0; /* Select the parent DF */ path = df->path; path.len -= 2; r = sc_select_file(p15card->card, &path, &parent); if (r < 0) return r; r = sc_pkcs15init_authenticate(profile, p15card, 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; r = sc_delete_file(p15card->card, &path); return r; } /* * Erase the card via rm */ static int cflex_erase_card(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; sc_file_t *df = profile->df_info->file, *dir, *userpinfile = NULL; int r; LOG_FUNC_CALLED(ctx); /* 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(profile, p15card, dir); sc_file_free(dir); if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) goto out; } r=cflex_delete_file(profile, p15card, df); /* If the user pin file isn't in a sub-DF of the pkcs15 DF, delete it */ if (sc_profile_get_file(profile, "pinfile-1", &userpinfile) >= 0 && userpinfile->path.len <= profile->df_info->file->path.len + 2 && memcmp(userpinfile->path.value, profile->df_info->file->path.value, userpinfile->path.len) != 0) { r = cflex_delete_file(profile, p15card, userpinfile); sc_file_free(userpinfile); userpinfile=NULL; } out: /* Forget all cached keys, the pin files on card are all gone. */ sc_file_free(userpinfile); sc_free_apps(p15card->card); if (r == SC_ERROR_FILE_NOT_FOUND) r=0; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Card initialization. * For the cryptoflex, read the card's serial number from 3F00 0002 */ static int cryptoflex_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { sc_path_t path; sc_file_t *file; u8 buf[32]; char serial[128]; size_t len; int r; sc_format_path("3F000002", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) { if (r == SC_ERROR_FILE_NOT_FOUND) return 0; return r; } if ((len = file->size) > sizeof(buf)) len = sizeof(buf); sc_file_free(file); if ((r = sc_read_binary(p15card->card, 0, buf, len, 0)) < 0) return r; len = r; if (len == 0) return 0; if ((r = sc_bin_to_hex(buf, len, serial, sizeof(serial), '\0')) < 0) return r; sc_pkcs15init_set_serial(profile, serial); return 0; } /* * Create a DF */ static int cflex_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { /* Create the application DF */ return sc_pkcs15init_create_file(profile, p15card, df); } /* * Create a PIN domain (i.e. a sub-directory holding a user PIN) */ static int cflex_create_domain(sc_profile_t *profile, sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, sc_file_t **ret) { return sc_pkcs15_create_pin_domain(profile, p15card, id, ret); } /* * Select the PIN reference */ static int cflex_select_pin_reference(sc_profile_t *profike, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { preferred = 2; } else { preferred = 1; } if (auth_info->attrs.pin.reference <= preferred) { auth_info->attrs.pin.reference = preferred; return 0; } if (auth_info->attrs.pin.reference > 2) return SC_ERROR_INVALID_ARGUMENTS; /* Caller, please select a different PIN reference */ return SC_ERROR_INVALID_PIN_REFERENCE; } /* * Create a new PIN inside a DF */ static int cflex_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; sc_file_t *dummies[2]; int ndummies, pin_type, puk_type, r; sc_file_t *file = NULL; LOG_FUNC_CALLED(ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* If the profile doesn't specify a reference for this PIN, guess */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { pin_type = SC_PKCS15INIT_SO_PIN; puk_type = SC_PKCS15INIT_SO_PUK; if (pin_attrs->reference != 2) return SC_ERROR_INVALID_ARGUMENTS; } else { pin_type = SC_PKCS15INIT_USER_PIN; puk_type = SC_PKCS15INIT_USER_PUK; if (pin_attrs->reference != 1) return SC_ERROR_INVALID_ARGUMENTS; } /* Get file definition from the profile */ if (sc_profile_get_file(profile, (pin_attrs->reference == 1)? "CHV1" : "CHV2", &file) < 0 && sc_profile_get_file(profile, "CHV", &file) < 0) LOG_TEST_RET(ctx, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); ndummies = cflex_create_dummy_chvs(profile, p15card, file, SC_AC_OP_CREATE, dummies); sc_file_free(file); LOG_TEST_RET(ctx, ndummies, "Unable to create dummy CHV file"); r = cflex_create_pin_file(profile, p15card, &df->path, pin_attrs->reference, pin, pin_len, sc_profile_get_pin_retries(profile, pin_type), puk, puk_len, sc_profile_get_pin_retries(profile, puk_type), NULL, 0); cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Create a new key file */ static int cflex_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t *prkf = NULL, *pukf = NULL; size_t size; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(p15card->card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, p15card->card, &key_info->path, &prkf, &pukf); if (r < 0) return r; /* Adjust the file sizes, if necessary */ switch (key_info->modulus_length) { case 512: size = 166; break; case 768: size = 246; break; case 1024: size = 326; break; case 2048: size = 646; break; default: sc_log(p15card->card->ctx, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", key_info->modulus_length); r = SC_ERROR_INVALID_ARGUMENTS; goto out; } if (prkf && prkf->size < size) prkf->size = size; if (pukf && pukf->size < size + 4) pukf->size = size + 4; /* Now create the files */ if ((r = sc_pkcs15init_create_file(profile, p15card, prkf)) < 0 || (r = sc_pkcs15init_create_file(profile, p15card, pukf)) < 0) goto out; key_info->key_reference = 0; out: sc_file_free(prkf); sc_file_free(pukf); return r; } /* * Generate key */ static int cflex_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { struct sc_cardctl_cryptoflex_genkey_info args; sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; unsigned int keybits; unsigned char raw_pubkey[256]; sc_file_t *prkf = NULL, *pukf = NULL; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, card, &key_info->path, &prkf, &pukf); if (r < 0) return r; if (! prkf) return SC_ERROR_NOT_SUPPORTED; /* Make sure we authenticate first */ r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) goto out; keybits = key_info->modulus_length; /* Perform key generation */ memset(&args, 0, sizeof(args)); args.exponent = 0x10001; args.key_bits = keybits; args.key_num = key_info->key_reference; r = sc_card_ctl(card, SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, &args); 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 = malloc(keybits / 8); pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = 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, keybits / 8, 0)) < 0) goto out; invert_buf(pubkey->u.rsa.modulus.data, raw_pubkey, pubkey->u.rsa.modulus.len); out: sc_file_free(pukf); sc_file_free(prkf); return r; } /* * Store a private key */ static int cflex_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, 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_card_t *card = p15card->card; sc_file_t *prkf = NULL, *pukf = NULL; unsigned char keybuf[1024]; size_t size; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, card, &key_info->path, &prkf, &pukf); if (r < 0) return r; /* Write the private key */ size = sizeof(keybuf); r = profile->ops->encode_private_key(profile, card, &key->u.rsa, keybuf, &size, key_info->key_reference); if (r < 0) goto out; r = sc_pkcs15init_update_file(profile, p15card, prkf, keybuf, size); if (r < 0) goto out; /* Write the public key */ size = sizeof(keybuf); r = profile->ops->encode_public_key(profile, card, &key->u.rsa, keybuf, &size, key_info->key_reference); if (r < 0) goto out; r = sc_pkcs15init_update_file(profile, p15card, pukf, keybuf, size); out: sc_file_free(prkf); sc_file_free(pukf); return r; } /* * If an access condition references e.g. CHV1, but we don't have * a CHV1 file yet, create an unprotected dummy file in the MF. */ static int cflex_create_dummy_chvs(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, int op, sc_file_t **dummies) { struct sc_context *ctx = p15card->card->ctx; const sc_acl_entry_t *acl; int r = 0, ndummies = 0; LOG_FUNC_CALLED(ctx); /* See if the DF is supposed to be PIN protected, and if * it is, whether that CHV file actually exists. If it doesn't, * create it. */ acl = sc_file_get_acl_entry(file, op); for (; acl; acl = acl->next) { sc_path_t parent, ef; if (acl->method != SC_AC_CHV) continue; parent = file->path; parent.len -= 2; r = SC_ERROR_FILE_NOT_FOUND; while (parent.len >= 2 && r == SC_ERROR_FILE_NOT_FOUND) { ef = parent; ef.value[ef.len++] = acl->key_ref - 1; ef.value[ef.len++] = 0; parent.len -= 2; if (ef.len == parent.len && !memcmp(ef.value, parent.value, ef.len)) continue; r = sc_select_file(p15card->card, &ef, NULL); } /* If a valid EF(CHVx) was found, we're fine */ if (r == 0) continue; if (r != SC_ERROR_FILE_NOT_FOUND) break; /* Create a CHV file in the MF */ parent = file->path; parent.len = 2; r = cflex_create_empty_pin_file(profile, p15card, &parent, acl->key_ref, &dummies[ndummies]); if (r < 0) break; ndummies++; } if (r < 0) { cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); return r; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, ndummies); } static void cflex_delete_dummy_chvs(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int ndummies, sc_file_t **dummies) { while (ndummies--) { cflex_delete_file(profile, p15card, dummies[ndummies]); sc_file_free(dummies[ndummies]); } } /* * Create a pin file */ static void put_pin(sc_profile_t *profile, unsigned char *buf, const u8 *pin, size_t len, int retry) { if (len > 8) len = 8; memset(buf, profile->pin_pad_char, 8); memcpy(buf, pin, len); buf[8] = retry; buf[9] = retry; } static int cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_path_t *df_path, int ref, const u8 *pin, size_t pin_len, int pin_tries, const u8 *puk, size_t puk_len, int puk_tries, sc_file_t **file_ret, int unprotected) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; unsigned char buffer[23]; sc_path_t path; sc_file_t *dummies[2], *file; int r, ndummies; LOG_FUNC_CALLED(ctx); if (file_ret) *file_ret = NULL; /* Build the CHV path */ path = *df_path; path.value[path.len++] = ref - 1; path.value[path.len++] = 0; /* See if the CHV already exists */ r = sc_select_file(p15card->card, &path, NULL); if (r >= 0) return SC_ERROR_FILE_ALREADY_EXISTS; /* Get the file definition from the profile */ if (sc_profile_get_file_by_path(profile, &path, &file) < 0 && sc_profile_get_file(profile, (ref == 1)? "CHV1" : "CHV2", &file) < 0 && sc_profile_get_file(profile, "CHV", &file) < 0) LOG_TEST_RET(ctx, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); file->path = path; file->size = 23; file->id = (ref == 1)? 0x0000 : 0x0100; if (unprotected) { sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); } /* Build the contents of the file */ buffer[0] = buffer[1] = buffer[2] = 0xFF; put_pin(profile, buffer + 3, pin, pin_len, pin_tries); put_pin(profile, buffer + 13, puk, puk_len, puk_tries); /* For updating the file, create a dummy CHV files if * necessary */ ndummies = cflex_create_dummy_chvs(profile, p15card, file, SC_AC_OP_UPDATE, dummies); LOG_TEST_RET(ctx, ndummies, "Unable to create dummy CHV file"); if (!unprotected) { struct sc_pin_cmd_data pin_cmd; memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.cmd = SC_PIN_CMD_VERIFY; pin_cmd.pin_type = SC_AC_CHV; pin_cmd.pin_reference = ref; pin_cmd.pin1.data = dummy_pin_value; pin_cmd.pin1.len = sizeof(dummy_pin_value); r = sc_pin_cmd(p15card->card, &pin_cmd, NULL); LOG_TEST_RET(ctx, r, "Cannot verify dummy PIN"); }; if (ref == 2) { /* Cache dummy SOPIN value */ r = sc_pkcs15_find_pin_by_type_and_reference(p15card, NULL, SC_AC_CHV, ref, &pin_obj); if (!r && pin_obj) sc_pkcs15_pincache_add(p15card, pin_obj, dummy_pin_value, sizeof(dummy_pin_value)); } r = sc_pkcs15init_create_file(profile, p15card, file); LOG_TEST_RET(ctx, r, "Failed to create PIN file"); r = sc_update_binary(p15card->card, 0, buffer, 23, 0); LOG_TEST_RET(ctx, r, "Failed to update PIN file"); if (r < 0 || file_ret == NULL) sc_file_free(file); else *file_ret = file; /* Delete the dummy CHV files */ cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); if (pin_obj) { /* Cache new SOPIN value */ sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Create a faux pin file */ static int cflex_create_empty_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_path_t *path, int ref, sc_file_t **file_ret) { int r; LOG_FUNC_CALLED(p15card->card->ctx); *file_ret = NULL; r = cflex_create_pin_file(profile, p15card, path, ref, dummy_pin_value, sizeof(dummy_pin_value), 8, NULL, 0, 0, file_ret, 1); if (r == SC_ERROR_FILE_ALREADY_EXISTS) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, r); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Get private and public key file */ static int cflex_get_keyfiles(sc_profile_t *profile, sc_card_t *card, const sc_path_t *df_path, sc_file_t **prkf, sc_file_t **pukf) { sc_path_t path = *df_path; int r; /* Get the private key file */ r = sc_profile_get_file_by_path(profile, &path, prkf); if (r < 0) { char pbuf[SC_MAX_PATH_STRING_SIZE]; r = sc_path_print(pbuf, sizeof(pbuf), &path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "Cannot find private key file info " "in profile (path=%s).", pbuf); return r; } /* Get the public key file */ path.len -= 2; sc_append_file_id(&path, 0x1012); r = sc_profile_get_file_by_path(profile, &path, pukf); if (r < 0) { sc_log(card->ctx, "Cannot find public key file info in profile."); sc_file_free(*prkf); return r; } return 0; } static void invert_buf(u8 *dest, const u8 *src, size_t c) { unsigned int i; for (i = 0; i < c; i++) dest[i] = src[c-1-i]; } static int bn2cf(sc_pkcs15_bignum_t *num, u8 *buf, size_t bufsize) { size_t len = num->len; if (len > bufsize) return SC_ERROR_INVALID_ARGUMENTS; invert_buf(buf, num->data, len); while (len < bufsize) buf[len++] = 0; return 0; } static int bn2cft(sc_pkcs15_bignum_t *num, u8 tag, u8 *buf, size_t bufsize) { size_t len = num->len; if (len + 3 > bufsize) return SC_ERROR_INVALID_ARGUMENTS; memset(buf, 0, bufsize); buf[0] = tag; buf[1] = len + 1; memcpy(buf + 3, num->data, len); return 0; } /* * Cryptoflex key encoding */ static int cryptoflex_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len / 2, key_blob_size; int r, key_num = key_ref + 1; switch (rsa->modulus.len) { case 512 / 8: case 768 / 8: case 1024 / 8: case 2048 / 8: break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 5 * base + 3; if (*keysize < key_blob_size + 3) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size + 3; *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; if ((r = bn2cf(&rsa->p, key + 0 * base, base)) < 0 || (r = bn2cf(&rsa->q, key + 1 * base, base)) < 0 || (r = bn2cf(&rsa->iqmp, key + 2 * base, base)) < 0 || (r = bn2cf(&rsa->dmp1, key + 3 * base, base)) < 0 || (r = bn2cf(&rsa->dmq1, key + 4 * base, base)) < 0) return r; key += 5 * base; *key++ = 0; *key++ = 0; *key = 0; return 0; } static int cryptoflex_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base; int r, key_num = key_ref + 1; switch (rsa->modulus.len) { case 512 / 8: case 768 / 8: case 1024 / 8: case 2048 / 8: break; default: return SC_ERROR_INVALID_ARGUMENTS; } base = rsa->modulus.len / 2; if (*keysize < (5 * base + 10)) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = 5 * base + 10; memset(key, 0, *keysize); *key++ = (5 * base + 7) >> 8; *key++ = (5 * base + 7) & 0xFF; *key++ = key_num; /* Funny code - not sure why we do it this way: * * Specs say: We store: (Length) * modulus modulus (N bytes) * J0 Montgomery const 0 (N/2 bytes) * H Montgomery const 0 (N bytes) * exponent exponent 4 * * --okir */ if ((r = bn2cf(&rsa->modulus, key + 0 * base, 2 * base)) < 0 || (r = bn2cf(&rsa->exponent, key + 5 * base, 4)) < 0) return r; return 0; } /* * Cyberflex key encoding */ static int cyberflex_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len / 2, key_blob_size, bnlen; int r, key_num = key_ref + 1, alg_id; switch (rsa->modulus.len) { case 512 / 8: alg_id = 0xC4; break; case 768 / 8: alg_id = 0xC6; break; case 1024 / 8: alg_id = 0xC8; break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 12 + 5 * (base + 3) + 4; if (*keysize < key_blob_size) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size; memset(key, 0, *keysize); *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; *key++ = alg_id; /* key blob header: * "C2:06:C1:08:13:00:00:05" */ memcpy(key, "\xc2\x06\xc1\x08\x12\x00\x00\x05", 8); key += 8; /* Each bignum is encoded with a 2 byte header and a * NULL pad byte */ bnlen = base + 3; if ((r = bn2cft(&rsa->q, 0xC2, key + 0 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->p, 0xC2, key + 1 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->iqmp, 0xC2, key + 2 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->dmq1, 0xC2, key + 3 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->dmp1, 0xC2, key + 4 * bnlen, bnlen)) < 0) return r; key += 5 * bnlen; key[0] = 0x0A; key[1] = 0x0A; key[2] = 0x00; key[3] = 0x00; return 0; } static int cyberflex_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len, key_blob_size, bnlen; int r, key_num = key_ref + 1, alg_id; switch (rsa->modulus.len) { case 512 / 8: alg_id = 0xC5; break; case 768 / 8: alg_id = 0xC7; break; case 1024 / 8: alg_id = 0xC9; break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 12 + 3 + base + 7 + 4; if (*keysize < key_blob_size) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size; memset(key, 0, *keysize); *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; *key++ = alg_id; /* Key blob header */ memcpy(key, "\xC1\x06\xC0\x08\x13\x00\x00\x05", 8); key += 8; bnlen = rsa->modulus.len + 3; if ((r = bn2cft(&rsa->modulus, 0xC0, key, bnlen)) < 0 || (r = bn2cft(&rsa->exponent, 0xC0, key + bnlen, 3 + 4)) < 0) return r; key += bnlen + 3 + 4; key[0] = 0x0A; key[1] = 0x0A; key[2] = 0x00; key[3] = 0x00; return 0; } static struct sc_pkcs15init_operations sc_pkcs15init_cryptoflex_operations = { cflex_erase_card, cryptoflex_init_card, cflex_create_dir, cflex_create_domain, cflex_select_pin_reference, cflex_create_pin, NULL, /* select_key_reference */ cflex_create_key, cflex_store_key, cflex_generate_key, cryptoflex_encode_private_key, cryptoflex_encode_public_key, NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; static struct sc_pkcs15init_operations sc_pkcs15init_cyberflex_operations = { cflex_erase_card, NULL, /* init_card */ cflex_create_dir, cflex_create_domain, cflex_select_pin_reference, cflex_create_pin, NULL, /* select_key_reference */ cflex_create_key, cflex_store_key, cflex_generate_key, cyberflex_encode_private_key, cyberflex_encode_public_key, NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_cryptoflex_ops(void) { return &sc_pkcs15init_cryptoflex_operations; } struct sc_pkcs15init_operations * sc_pkcs15init_get_cyberflex_ops(void) { return &sc_pkcs15init_cyberflex_operations; }