/* * pkcs15-muscle.c: Support for MuscleCard Applet from musclecard.com * * Copyright (C) 2006, Identity Alliance, Thomas Harning * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include "libopensc/pkcs15.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #define MUSCLE_KEY_ID_MIN 0x00 #define MUSCLE_KEY_ID_MAX 0x0F static int muscle_erase_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { int r; struct sc_file *file; struct sc_path path; memset(&file, 0, sizeof(file)); sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_ERASE)) < 0) return r; if ((r = sc_delete_file(p15card->card, &path)) < 0) return r; return 0; } static int muscle_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { return 0; } static int muscle_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { int r; struct sc_file *file; struct sc_path path; memset(&file, 0, sizeof(file)); sc_format_path("3F00", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) return r; if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_CREATE)) < 0) return r; /* Create the application DF */ if ((r = sc_pkcs15init_create_file(profile, p15card, df)) < 0) return r; if ((r = sc_select_file(p15card->card, &df->path, NULL)) < 0) return r; return 0; } static int muscle_create_pin(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df, sc_pkcs15_object_t *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len) { sc_file_t *file; sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data; int r; if ((r = sc_select_file(p15card->card, &df->path, &file)) < 0) return r; if ((r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_WRITE)) < 0) return r; pin_info->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; return 0; } static int muscle_select_pin_reference(sc_profile_t *profike, sc_pkcs15_card_t *p15card, sc_pkcs15_pin_info_t *pin_info) { int preferred; if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { preferred = 0; } else { preferred = 1; } if (pin_info->reference <= preferred) { pin_info->reference = preferred; return 0; } if (pin_info->reference > 2) return SC_ERROR_INVALID_ARGUMENTS; /* Caller, please select a different PIN reference */ return SC_ERROR_INVALID_PIN_REFERENCE; } /* * Select a key reference */ static int muscle_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { if (key_info->key_reference < MUSCLE_KEY_ID_MIN) key_info->key_reference = MUSCLE_KEY_ID_MIN; if (key_info->key_reference > MUSCLE_KEY_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; return 0; } /* * Create a private key object. * This is a no-op. */ static int muscle_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { return 0; } /* * Store a private key object. */ static int muscle_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t* prkf; struct sc_pkcs15_prkey_rsa *rsa; sc_cardctl_muscle_key_info_t info; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports RSA keys only."); return SC_ERROR_NOT_SUPPORTED; } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); if(!prkf) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) { sc_file_free(prkf); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } sc_file_free(prkf); r = muscle_select_key_reference(profile, p15card, key_info); if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,r); } rsa = &key->u.rsa; info.keySize = rsa->modulus.len << 3; info.keyType = 0x03; /* CRT type */ info.keyLocation = key_info->key_reference * 2; /* Mult by 2 to preserve even/odd keynumber structure */ info.pLength = rsa->p.len; info.pValue = rsa->p.data; info.qLength = rsa->q.len; info.qValue = rsa->q.data; info.pqLength = rsa->iqmp.len; info.pqValue = rsa->iqmp.data; info.dp1Length = rsa->dmp1.len; info.dp1Value = rsa->dmp1.data; info.dq1Length = rsa->dmq1.len; info.dq1Value = rsa->dmq1.data; r = sc_card_ctl(p15card->card, SC_CARDCTL_MUSCLE_IMPORT_KEY, &info); if (r < 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to import key"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,r); } return r; } static int muscle_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_cardctl_muscle_gen_key_info_t args; sc_cardctl_muscle_key_info_t extArgs; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_file_t* prkf; unsigned int keybits; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Muscle supports only RSA keys (for now)."); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } keybits = key_info->modulus_length & ~7UL; if (keybits > 2048) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key, max size is %d", 2048); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); } /* Verification stuff */ /* Used for verification AND for obtaining private key acls */ r = sc_profile_get_file_by_path(profile, &key_info->path, &prkf); if(!prkf) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) { sc_file_free(prkf); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } sc_file_free(prkf); /* END VERIFICATION STUFF */ /* Public key acls... get_file_by_path as well? */ memset(&args, 0, sizeof(args)); args.keyType = 0x01; /* RSA forced */ args.privateKeyLocation = key_info->key_reference * 2; args.publicKeyLocation = key_info->key_reference * 2 + 1; args.keySize = keybits; r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_GENERATE_KEY, &args); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to generate key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } memset(&extArgs, 0, sizeof(extArgs)); memset(pubkey, 0, sizeof(*pubkey)); extArgs.keyType = 0x01; extArgs.keyLocation = args.publicKeyLocation; r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_EXTRACT_KEY, &extArgs); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unable to extract the public key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = extArgs.modLength; pubkey->u.rsa.modulus.data = extArgs.modValue; pubkey->u.rsa.exponent.len = extArgs.expLength; pubkey->u.rsa.exponent.data = extArgs.expValue; if (r < 0) { if (pubkey->u.rsa.modulus.data) free (pubkey->u.rsa.modulus.data); if (pubkey->u.rsa.exponent.data) free (pubkey->u.rsa.exponent.data); } return r; } static struct sc_pkcs15init_operations sc_pkcs15init_muscle_operations = { muscle_erase_card, /* erase card */ muscle_init_card, /* init_card */ muscle_create_dir, /* create_dir */ NULL, /* create_domain */ muscle_select_pin_reference, /* select pin reference */ muscle_create_pin, /* Create PIN */ muscle_select_key_reference, /* select_key_reference */ muscle_create_key, /* create_key */ muscle_store_key, /* store_key */ muscle_generate_key, /* generate_key */ NULL, NULL, /* encode private/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_muscle_ops(void) { return &sc_pkcs15init_muscle_operations; }