/* * Create the MF and global pin file if they don't exist. */ static int setcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; sc_file_t *mf = profile->mf_info->file; sc_file_t *pinfile; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* Create the MF if it doesn't exist yet */ r = sc_select_file(p15card->card, &mf->path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "MF doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, mf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF fixup failed"); mf->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, mf); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "MF creation failed"); } SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select MF"); /* Create the global pin file if it doesn't exist yet */ r = sc_profile_get_file(profile, "pinfile", &pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot get 'pinfile' from profile"); r = sc_select_file(p15card->card, &pinfile->path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Global pin file doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, pinfile); if (r < 0) sc_file_free(pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Pinfile fixup failed"); /* Set life cycle state to SC_FILE_STATUS_CREATION, * which means that all ACs are ignored. */ pinfile->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, pinfile); if (r < 0) sc_file_free(pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Pinfile creation failed"); } sc_file_free(pinfile); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Select pinfile failed"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); }
/* * Create a new key file */ static int jcop_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 *keyfile = NULL; size_t bytes, mod_len, prv_len; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); 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; bytes = mod_len / 2; prv_len = 2 + 5 * bytes; keyfile->size = prv_len; /* Fix up PIN references in file ACL */ r = sc_pkcs15init_fixup_file(profile, p15card, keyfile); if (r >= 0) r = sc_pkcs15init_create_file(profile, p15card, keyfile); sc_file_free(keyfile); return r; }
/* * Create a DF */ static int setcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_context *ctx = p15card->card->ctx; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); r = sc_pkcs15init_fixup_file(profile, p15card, df); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS file ACL fixup failed"); r = sc_create_file(p15card->card, df); SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "SetCOS create file failed"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); }
/* * Set up the public key record for a signature only public key */ static int gpk_pkfile_init_public(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, unsigned int algo, unsigned int bits, unsigned int usage) { struct sc_context *ctx = p15card->card->ctx; const sc_acl_entry_t *acl; sc_file_t *tmp = NULL; u8 sysrec[7], buffer[256]; unsigned int n, npins; int r, card_type; /* Find out what sort of GPK we're using */ if ((r = sc_card_ctl(p15card->card, SC_CARDCTL_GPK_VARIANT, &card_type)) < 0) return r; /* Set up the system record */ memset(sysrec, 0, sizeof(sysrec)); /* Mapping keyUsage to sysrec[2]: * 0x00 sign & unwrap * 0x10 sign only * 0x20 unwrap only * 0x30 CA key * * We start with a value of 0x30. * If the key allows decryption, clear the sign only bit. * Likewise, if it allows signing, clear the unwrap only bit. */ sysrec[2] = 0x30; if (usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP)) sysrec[2] &= ~0x10; if (usage & (SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) sysrec[2] &= ~0x20; if (sysrec[2] == 0x30) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Key usage should specify at least one of sign or decipher"); return SC_ERROR_INVALID_ARGUMENTS; } /* Set the key size and algorithm */ if ((r = gpk_pkfile_keybits(bits, &sysrec[1])) < 0 || (r = gpk_pkfile_keyalgo(algo, &sysrec[5])) < 0) return r; /* Set PIN protection if requested. * As the crypto ACLs are stored inside the file, * we have to get them from the profile here. */ r = sc_profile_get_file_by_path(profile, &file->path, &tmp); if (r < 0) return r; /* Fix up PIN references in file ACL */ if ((r = sc_pkcs15init_fixup_file(profile, p15card, tmp)) < 0) goto out; acl = sc_file_get_acl_entry(tmp, SC_AC_OP_CRYPTO); for (npins = 0; acl; acl = acl->next) { if (acl->method == SC_AC_NONE || acl->method == SC_AC_NEVER) continue; if (acl->method != SC_AC_CHV) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Authentication method not " "supported for private key files.\n"); r = SC_ERROR_NOT_SUPPORTED; goto out; } if (++npins >= 2) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Too many pins for PrKEY file!\n"); r = SC_ERROR_NOT_SUPPORTED; goto out; } sysrec[2] += 0x40; sysrec[3] >>= 4; sysrec[3] |= acl->key_ref << 4; } /* compute checksum - yet another slightly different * checksum algorithm courtesy of Gemplus */ if (card_type >= SC_CARD_TYPE_GPK_GPK8000) { /* This is according to the gpk reference manual */ sysrec[6] = 0xA5; } else { /* And this is what you have to use for the GPK4000 */ sysrec[6] = 0xFF; } for (n = 0; n < 6; n++) sysrec[6] ^= sysrec[n]; r = sc_read_record(p15card->card, 1, buffer, sizeof(buffer), SC_RECORD_BY_REC_NR); if (r >= 0) { if (r != 7 || buffer[0] != 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "first record of public key file is not Lsys0"); r = SC_ERROR_OBJECT_NOT_VALID; goto out; } r = sc_update_record(p15card->card, 1, sysrec, sizeof(sysrec), SC_RECORD_BY_REC_NR); } else { r = sc_append_record(p15card->card, sysrec, sizeof(sysrec), 0); } out: if (tmp) sc_file_free(tmp); return r; }
/* * Create a key file */ static int gpk_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; struct sc_file *keyfile = NULL; size_t bytes, mod_len, exp_len, prv_len, pub_len; int r, algo; /* 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; /* Compute the file size. * We assume private keys are stored as CRT elements. * - 512, 768 bit keys: all CRT elements fit into one record * - >= 1024: each CRT element into a record of its own * * We also assume the public exponent is 32bit max * * Rules * - private key records must have a length divisible by 8 */ mod_len = key_info->modulus_length / 8; exp_len = 4; bytes = mod_len / 2; pub_len = 8 + ((3 + mod_len + 3 + exp_len + 3) & ~3UL); if (5 * bytes < 256) { prv_len = 8 + ((3 + 5 * bytes + 7) & ~7UL); } else { prv_len = 8 + 5 * ((3 + bytes + 7) & ~7UL); } keyfile->size = pub_len + prv_len; switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: algo = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PRKEY_DSA: algo = SC_ALGORITHM_DSA; break; default: sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported public key algorithm"); return SC_ERROR_NOT_SUPPORTED; } /* Fix up PIN references in file ACL and create the PK file */ if ((r = sc_pkcs15init_fixup_file(profile, p15card, keyfile)) < 0 || (r = gpk_pkfile_create(profile, p15card, keyfile)) < 0) goto done; #ifdef PK_INIT_IMMEDIATELY /* Initialize the public key header */ r = gpk_pkfile_init_public(profile, p15card, keyfile, algo, key_info->modulus_length, key_info->usage); if (r < 0) goto done; /* Create the private key portion */ r = gpk_pkfile_init_private(p15card->card, keyfile, prv_len); #endif done: sc_file_free(keyfile); return r; }
/* * Create a PIN object within the given DF */ static int rtecp_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_context_t *ctx; sc_pkcs15_auth_info_t *auth_info; sc_file_t *file = NULL; /* GCHV min-length Flags Attempts Reserve */ unsigned char prop[] = { 0x01, '?', 0x01, '?', 0, 0 }; /* AccessMode Unblock Change Delete */ unsigned char sec[15] = { 0x43, '?', '?', 0, 0, 0, 0, 0xFF }; char pin_sname[0x10]; int r, reset_by_sopin = 0; (void)puk; /* no warning */ if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !df || !pin_obj || !pin_obj->data || !pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (puk_len != 0) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Do not enter User unblocking PIN (PUK): %s\n", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; } auth_info = (sc_pkcs15_auth_info_t *)pin_obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.reference != RTECP_SO_PIN_REF && auth_info->attrs.pin.reference != RTECP_USER_PIN_REF) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "PIN reference %i not found in standard" " (Rutoken ECP) PINs\n", auth_info->attrs.pin.reference); return SC_ERROR_NOT_SUPPORTED; } snprintf(pin_sname, sizeof(pin_sname), "CHV%i", auth_info->attrs.pin.reference); if (auth_info->attrs.pin.reference == RTECP_USER_PIN_REF) { r = sc_profile_get_file(profile, pin_sname, &file); if (!r) { const struct sc_acl_entry *acl = NULL; r = sc_pkcs15init_fixup_file(profile, p15card, file); if (r < 0) sc_file_free(file); SC_TEST_RET(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot fixup the ACLs of PIN file"); acl = sc_file_get_acl_entry(file, SC_AC_OP_PIN_RESET); if (acl && acl->method == SC_AC_CHV && acl->key_ref == RTECP_SO_PIN_REF) { sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Allow reset of User PIN with SoPIN\n"); reset_by_sopin = 1; } sc_file_free(file); } } file = sc_file_new(); if (!file) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); file->id = auth_info->attrs.pin.reference; file->size = pin_len; assert(sizeof(sec)/sizeof(sec[0]) > 2); sec[1] = (auth_info->attrs.pin.reference == RTECP_SO_PIN_REF) ? 0xFF : RTECP_SO_PIN_REF; sec[2] = (unsigned char)auth_info->attrs.pin.reference | (reset_by_sopin ? RTECP_SO_PIN_REF : 0); r = sc_file_set_sec_attr(file, sec, sizeof(sec)); if (r == SC_SUCCESS) { assert(sizeof(prop)/sizeof(prop[0]) > 3); prop[1] = (unsigned char)auth_info->attrs.pin.min_length; prop[3] = 0x11 * (unsigned char)(auth_info->tries_left & 0x0F); r = sc_file_set_prop_attr(file, prop, sizeof(prop)); } if (r == SC_SUCCESS) r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2); if (r == SC_SUCCESS) r = sc_create_file(p15card->card, file); sc_file_free(file); if (r == SC_SUCCESS) r = sc_change_reference_data(p15card->card, SC_AC_CHV, auth_info->attrs.pin.reference, NULL, 0, pin, pin_len, NULL); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, r); }
/* * Get 'Initialize Applet' data * using the ACLs defined in card profile. */ static int myeid_get_init_applet_data(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned char *data, size_t data_len) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *tmp_file = NULL; const struct sc_acl_entry *entry = NULL; int r; LOG_FUNC_CALLED(ctx); if (data_len < 8) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot get init applet data"); *(data + 0) = 0xFF; *(data + 1) = 0xFF; /* MF acls */ sc_file_dup(&tmp_file, profile->mf_info->file); if (tmp_file == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate MF file"); r = sc_pkcs15init_fixup_file(profile, p15card, tmp_file); LOG_TEST_RET(ctx, r, "MF fixup failed"); /* AC 'Create DF' and 'Create EF' */ *(data + 2) = 0x00; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_CREATE); if (entry->method == SC_AC_CHV) *(data + 2) = entry->key_ref | (entry->key_ref << 4); /* 'CHVx'. */ else if (entry->method == SC_AC_NEVER) *(data + 2) = 0xFF; /* 'NEVER'. */ /* AC 'INITIALISE APPLET'. */ *(data + 3) = 0x0F; /* 'NONE' */ #ifndef KEEP_AC_NONE_FOR_INIT_APPLET entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_DELETE); if (entry->method == SC_AC_CHV) *(data + 3) = (entry->key_ref << 4) | 0xF; else if (entry->method == SC_AC_NEVER) *(data + 3) = 0xFF; #endif *(data + 4) = 0xFF; sc_file_free(tmp_file); tmp_file = NULL; /* Application DF (5015) acls */ sc_file_dup(&tmp_file, profile->df_info->file); if (tmp_file == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate Application DF file"); r = sc_pkcs15init_fixup_file(profile, p15card, tmp_file); LOG_TEST_RET(ctx, r, "Application DF fixup failed"); /* AC 'Create DF' and 'Create EF' */ *(data + 5) = 0x00; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_CREATE); if (entry->method == SC_AC_CHV) *(data + 5) = entry->key_ref | (entry->key_ref << 4); /* 'CHVx' */ else if (entry->method == SC_AC_NEVER) *(data + 5) = 0xFF; /* 'NEVER'. */ /* AC 'Self delete' */ *(data + 6) = 0x0F; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_DELETE); if (entry->method == SC_AC_CHV) *(data + 6) = (entry->key_ref << 4) | 0xF; /* 'CHVx' */ else if (entry->method == SC_AC_NEVER) *(data + 6) = 0xFF; /* 'NEVER'. */ *(data + 7) = 0xFF; sc_file_free(tmp_file); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); }
/* * Generate a keypair */ static int jcop_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { 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 mod_len, exp_len, pub_len, keybits; int r,delete_ok=0; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "JCOP supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } r=sc_profile_get_file(profile, "temp-pubkey", &temppubfile); if (r < 0) goto out; r = sc_select_file(p15card->card, &key_info->path, &keyfile); if (r < 0) goto out; mod_len = key_info->modulus_length / 8; exp_len = 4; pub_len = 2 + mod_len + exp_len; temppubfile->size = pub_len; r = sc_pkcs15init_fixup_file(profile, p15card, temppubfile); if (r < 0) goto out; r = sc_pkcs15init_create_file(profile, p15card, temppubfile); if (r < 0) goto out; delete_ok=1; r = sc_pkcs15init_authenticate(profile, p15card, temppubfile, SC_AC_OP_UPDATE); if (r < 0) goto out; r = sc_pkcs15init_authenticate(profile, p15card, keyfile, SC_AC_OP_UPDATE); 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); keybuf = malloc(keybits / 8); if (!keybuf) { r=SC_ERROR_OUT_OF_MEMORY; goto out; } args.pubkey = keybuf; args.pubkey_len = keybits / 8; r = sc_card_ctl(p15card->card, SC_CARDCTL_JCOP_GENERATE_KEY, (void *)&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 = keybuf; pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = 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) sc_pkcs15init_rmdir(p15card, profile, temppubfile); if (keyfile) sc_file_free(keyfile); if (temppubfile) sc_file_free(temppubfile); return r; }