static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file,
	u8 *buf, size_t *outlen)
{
	int       i, byte;
	const int *idx;

	if (buf == NULL || *outlen < 9)
		return SC_ERROR_INVALID_ARGUMENTS;

	idx = (file->type == SC_FILE_TYPE_DF) ?  df_acl : ef_acl;
	for (i = 0; i < 9; i++) {
		if (idx[i] < 0)
			byte = 0x00;
		else
			byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i]));
		if (byte < 0) {
			sc_error(card->ctx, "Invalid ACL\n");
			return SC_ERROR_INVALID_ARGUMENTS;
		}
		buf[i] = byte;
	}
	*outlen = 9;

	return SC_SUCCESS;
}
Exemplo n.º 2
0
static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def)
{
	u8 def = (u8)in_def;
	const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method);
	if (!entry)
	{
		return def;
	}
	else if (entry->method & SC_AC_CHV)
	{
		unsigned int key_ref = entry->key_ref;
		if (key_ref == SC_AC_KEY_REF_NONE)
			return def;
		else
			return ENTERSAFE_AC_ALWAYS&0x04;
	}
	else if (entry->method & SC_AC_NEVER)
	{
		return ENTERSAFE_AC_NEVER;
	}
	else
	{
		return def;
	}
}
Exemplo n.º 3
0
static void
check_ret(int r, int op, const char *err, const sc_file_t *file)
{
	fprintf(stderr, "%s: %s\n", err, sc_strerror(r));
	if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)
		fprintf(stderr, "ACL for operation: %s\n", util_acl_to_str(sc_file_get_acl_entry(file, op)));
}
Exemplo n.º 4
0
static int entersafe_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 *kinfo = (sc_pkcs15_prkey_info_t *) obj->data;
	sc_card_t *card = p15card->card;
	sc_entersafe_wkey_data data;
	sc_file_t              *tfile;
	const sc_acl_entry_t   *acl_entry;
	int r;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	if (key->algorithm != SC_ALGORITHM_RSA)
		 /* ignore DSA keys */
		 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS);

	r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile);
	if (r < 0)
		 return r;
	acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE);
	if (acl_entry->method  != SC_AC_NONE) {
		 r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_UPDATE);
		 if(r<0)
			  r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
	}
	sc_file_free(tfile);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin");

	data.key_id = (u8) kinfo->key_reference;
	data.usage=0x22;
	data.key_data.rsa=&key->u.rsa;
	return sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data);
}
Exemplo n.º 5
0
/** starcos_process_acl
 * \param card pointer to the sc_card object
 * \param file pointer to the sc_file object
 * \param data pointer to a sc_starcos_create_data structure
 * \return SC_SUCCESS if no error occured otherwise error code
 *
 * This function tries to create a somewhat useable Starcos spk 2.3 acl
 * from the OpenSC internal acl (storing the result in the supplied
 * sc_starcos_create_data structure).
 */
static int starcos_process_acl(sc_card_t *card, sc_file_t *file,
                               sc_starcos_create_data *data)
{
    u8     tmp, *p;
    static const u8 def_key[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

    if (file->type == SC_FILE_TYPE_DF && file->id == 0x3f00) {
        p    = data->data.mf.header;
        memcpy(p, def_key, 8);
        p   += 8;
        *p++ = (file->size >> 8) & 0xff;
        *p++ = file->size & 0xff;
        /* guess isf size (mf_size / 4) */
        *p++ = (file->size >> 10) & 0xff;
        *p++ = (file->size >> 2)  & 0xff;
        /* ac create ef  */
        *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS);
        /* ac create key */
        *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS);
        /* ac create df  */
        *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS);
        /* use the same ac for register df and create df */
        *p++ = data->data.mf.header[14];
        /* if sm is required use combined mode */
        if (file->acl[SC_AC_OP_CREATE] && (sc_file_get_acl_entry(file, SC_AC_OP_CREATE))->method & SC_AC_PRO)
            tmp = 0x03;	/* combinde mode */
        else
            tmp = 0x00;	/* no sm */
        *p++ = tmp;	/* use the same sm mode for all ops */
        *p++ = tmp;
        *p = tmp;
        data->type = SC_STARCOS_MF_DATA;

        return SC_SUCCESS;
    } else if (file->type == SC_FILE_TYPE_DF) {
Exemplo n.º 6
0
static int entersafe_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card,
								  sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey)
{
	int r;
	sc_entersafe_gen_key_data	gendat;
	sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data;
	sc_card_t *card = p15card->card;
	sc_file_t              *tfile;
	const sc_acl_entry_t   *acl_entry;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA)
		return SC_ERROR_NOT_SUPPORTED;

	r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile);
	if (r < 0)
		 return r;
	acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE);
	if (acl_entry->method  != SC_AC_NONE) {
		 r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_UPDATE);
		 if(r<0)
			  r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
	}
	sc_file_free(tfile);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin");

	/* generate key pair */
	gendat.key_id     = (u8) kinfo->key_reference;
	gendat.key_length = (size_t) kinfo->modulus_length;
	gendat.modulus    = NULL;
	r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, &gendat);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "EnterSafe generate RSA key pair failed");

	/* get the modulus via READ PUBLIC KEY */
	if (pubkey) {
		u8 *buf;
		struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa;
		/* set the modulus */
		rsa->modulus.data = gendat.modulus;
		rsa->modulus.len  = kinfo->modulus_length >> 3;
		/* set the exponent (always 0x10001) */
		buf = malloc(3);
		if (!buf)
			return SC_ERROR_OUT_OF_MEMORY;
		buf[0] = 0x01;
		buf[1] = 0x00;
		buf[2] = 0x01;
		rsa->exponent.data = buf;
		rsa->exponent.len  = 3;

		pubkey->algorithm = SC_ALGORITHM_RSA;
	} else
Exemplo n.º 7
0
/* encodes the opensc file attributes into the card specific format
 */
static int asepcos_set_security_attributes(sc_card_t *card, sc_file_t *file)
{
	size_t i;
	const amode_entry_t *table;
	u8     buf[64], *p;
	int    r = SC_SUCCESS;

	/* first check wether the security attributes in encoded form
	 * are already set. If present use these */
	if (file->sec_attr != NULL && file->sec_attr_len != 0)
		return asepcos_set_sec_attributes(card, file->sec_attr,
				file->sec_attr_len, file->type == SC_FILE_TYPE_DF ? 0:1);
	/* otherwise construct the ACL from the opensc ACLs */
	if (file->type == SC_FILE_TYPE_DF)
		table = df_amode_table;
	else if (file->type == SC_FILE_TYPE_WORKING_EF)
		table = wef_amode_table;
	else if (file->type == SC_FILE_TYPE_INTERNAL_EF)
		table = ief_amode_table;
	else
		return SC_ERROR_INVALID_ARGUMENTS;

	p = buf;
	for (i = 0; table[i].am != 0; i++) {
		const struct sc_acl_entry *ent = sc_file_get_acl_entry(file, table[i].sc);
		if (ent == NULL)
			continue;
		*p++ = 0x80;
		*p++ = 0x01;
		*p++ = table[i].am & 0xff;
		if (ent->method == SC_AC_NONE) {
			*p++ = 0x90;
			*p++ = 0x00;
		} else if (ent->method == SC_AC_NEVER) {
			*p++ = 0x97;
			*p++ = 0x00;
		} else if (ent->method == SC_AC_CHV) {
			sc_cardctl_asepcos_akn2fileid_t st;
			st.akn = ent->key_ref;
			r = asepcos_akn_to_fileid(card, &st);
			if (r != SC_SUCCESS)
				return r;
			*p++ = 0xa0;
			*p++ = 0x05;
			*p++ = 0x89;
			*p++ = 0x03;
			*p++ = (st.fileid >> 16) & 0xff;
			*p++ = (st.fileid >> 8 ) & 0xff;
			*p++ = st.fileid & 0xff;
		} else {
Exemplo n.º 8
0
static int entersafe_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 *kinfo = (sc_pkcs15_prkey_info_t *) obj->data;
	sc_card_t *card = p15card->card;
	sc_entersafe_wkey_data data;
	sc_file_t              *tfile;
	const sc_acl_entry_t   *acl_entry;
	int r;

	struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data;
	size_t keybits = key_info->modulus_length;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	if ( key->algorithm != SC_ALGORITHM_RSA )
	{
		 /* ignore DSA keys */
		 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS);
	}

	/* Disable RSA:512bits */
	if ( ( keybits < 1024 ) ||
		 ( keybits > 2048 ) ||
		 ( keybits % 0x20 ) )
	{
		sc_debug(card->ctx,
			 SC_LOG_DEBUG_NORMAL,
			 "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n",
			 keybits);
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile);
	if (r < 0)
		 return r;
	acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE);
	if (acl_entry->method  != SC_AC_NONE) {
		 r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_UPDATE);
		 if(r<0)
			  r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
	}
	sc_file_free(tfile);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "cant verify pin");

	data.key_id = (u8) kinfo->key_reference;
	data.usage=0x22;
	data.key_data.rsa=&key->u.rsa;
	return sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data);
}
Exemplo n.º 9
0
/*
 * Parse ACL list.
 * The way we do this is we first split things like CHV1
 * into a method (SC_AC_CHV) and a reference (1).
 * When we're finished parsing the profile, the fake references
 * are replaced by the real references given in KEY or PIN
 * commands
 */
static int
do_acl(struct state *cur, int argc, char **argv)
{
	struct sc_file	*file = cur->file->file;
	char		oper[64], *what = NULL;

	memset(oper, 0, sizeof(oper));
	while (argc--) {
		unsigned int	op, method, id;

		strlcpy(oper, *argv++, sizeof(oper));
		if ((what = strchr(oper, '=')) == NULL)
			goto bad;
		*what++ = '\0';

		if (*what == '$') {
			method = SC_AC_SYMBOLIC;
			if (map_str2int(cur, what+1, &id, pinIdNames))
				return 1;
		} else
		if (get_authid(cur, what, &method, &id))
			goto bad;

		if (!strcmp(oper, "*")) {
			for (op = 0; op < SC_MAX_AC_OPS; op++) {
				sc_file_clear_acl_entries(file, op);
				sc_file_add_acl_entry(file, op, method, id);
			}
		} else {
			const sc_acl_entry_t *acl;

			if (map_str2int(cur, oper, &op, fileOpNames))
				goto bad;
			acl = sc_file_get_acl_entry(file, op);
			if (acl->method == SC_AC_NEVER
			 || acl->method == SC_AC_NONE
			 || acl->method == SC_AC_UNKNOWN)
				sc_file_clear_acl_entries(file, op);
			sc_file_add_acl_entry(file, op, method, id);
		}
	}
	return 0;

bad:	parse_error(cur, 
		"Invalid ACL \"%s%s%s\"\n",
		oper, what? "=" : "", what? what : "");
	return 1;
}
Exemplo n.º 10
0
static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def)
{
    u8 def = (u8)in_def;
    const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method);
    if (!entry)
        return def;
    else if (entry->method & SC_AC_CHV) {
        unsigned int key_ref = entry->key_ref;
        if (key_ref == SC_AC_KEY_REF_NONE)
            return def;
        else if ((key_ref & 0x0f) == 1)
            /* SOPIN */
            return (key_ref & 0x80 ? 0x10 : 0x00) | 0x01;
        else
            return (key_ref & 0x80 ? 0x10 : 0x00) | STARCOS_PINID2STATE(key_ref);
    } else if (entry->method & SC_AC_NEVER)
        return STARCOS_AC_NEVER;
    else
        return def;
}
Exemplo n.º 11
0
static int do_info(int argc, char **argv)
{
	sc_file_t *file;
	sc_path_t path;
	size_t i;
	const char *st;
	int r, not_current = 1;
	const id2str_t *ac_ops = NULL;

	if (!argc) {
		path = current_path;
		file = current_file;
		not_current = 0;
	} else if (argc == 1) {
		if (arg_to_path(argv[0], &path, 0) != 0) 
			return usage(do_info);

		r = sc_select_file(card, &path, &file);
		if (r) {
			printf("unable to select file: %s\n", sc_strerror(r));
			return -1;
		}
	} else 
		return usage(do_info);

	switch (file->type) {
	case SC_FILE_TYPE_WORKING_EF:
	case SC_FILE_TYPE_INTERNAL_EF:
		st = "Elementary File";
		break;
	case SC_FILE_TYPE_DF:
		st = "Dedicated File";
		break;
	default:
		st = "Unknown File";
		break;
	}
	printf("\n%s  ID %04X\n\n", st, file->id);
	printf("%-15s%s\n", "File path:", path_to_filename(&path, '/'));
	printf("%-15s%lu bytes\n", "File size:", (unsigned long) file->size);

	if (file->type == SC_FILE_TYPE_DF) {
		static const id2str_t ac_ops_df[] = {
			{ SC_AC_OP_SELECT,       "SELECT"       },
			{ SC_AC_OP_LOCK,         "LOCK"         },
			{ SC_AC_OP_DELETE,       "DELETE"       },
			{ SC_AC_OP_CREATE,       "CREATE"       },
			{ SC_AC_OP_REHABILITATE, "REHABILITATE" },
			{ SC_AC_OP_INVALIDATE,   "INVALIDATE"   },
			{ SC_AC_OP_LIST_FILES,   "LIST FILES"   },
			{ SC_AC_OP_CRYPTO,       "CRYPTO"       },
			{ SC_AC_OP_DELETE_SELF,  "DELETE SELF"  },
			{ 0, NULL }
		};

		if (file->namelen) {
			printf("%-15s", "DF name:");
			util_print_binary(stdout, file->name, file->namelen);
			printf("\n");
		}

		ac_ops = ac_ops_df;
	} else {
		static const id2str_t ac_ops_ef[] = {
			{ SC_AC_OP_READ,         "READ"         },
			{ SC_AC_OP_UPDATE,       "UPDATE"       },
			{ SC_AC_OP_DELETE,       "DELETE"       },
			{ SC_AC_OP_WRITE,        "WRITE"        },
			{ SC_AC_OP_REHABILITATE, "REHABILITATE" },
			{ SC_AC_OP_INVALIDATE,   "INVALIDATE"   },
			{ SC_AC_OP_LIST_FILES,   "LIST FILES"   },
			{ SC_AC_OP_CRYPTO,       "CRYPTO"       },
			{ 0, NULL }
		};
		const id2str_t ef_type_name[] = {
			{ SC_FILE_EF_TRANSPARENT,         "Transparent"                 },
			{ SC_FILE_EF_LINEAR_FIXED,        "Linear fixed"                },
			{ SC_FILE_EF_LINEAR_FIXED_TLV,    "Linear fixed, SIMPLE-TLV"    },
			{ SC_FILE_EF_LINEAR_VARIABLE,     "Linear variable"             },
			{ SC_FILE_EF_LINEAR_VARIABLE_TLV, "Linear variable, SIMPLE-TLV" },
			{ SC_FILE_EF_CYCLIC,              "Cyclic"                      },
			{ SC_FILE_EF_CYCLIC_TLV,          "Cyclic, SIMPLE-TLV"          },
			{ 0, NULL }
		};
		const char *ef_type = "Unknown";

		for (i = 0; ef_type_name[i].str != NULL; i++)
			if (file->ef_structure == ef_type_name[i].id)
				ef_type = ef_type_name[i].str;
		printf("%-15s%s\n", "EF structure:", ef_type);

		ac_ops = ac_ops_ef;
	}

	for (i = 0; ac_ops != NULL && ac_ops[i].str != NULL; i++) {
		int len = strlen(ac_ops[i].str);

		printf("ACL for %s:%*s %s\n",
			ac_ops[i].str,
			(12 > len) ? (12 - len) : 0, "",
			util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id)));
	}

	if (file->prop_attr_len) {
		printf("%-25s", "Proprietary attributes:");
		util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, " ");
		printf("\n");
	}
	if (file->sec_attr_len) {
		printf("%-25s", "Security attributes:");
		util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, " ");
		printf("\n");
	}
	printf("\n");
	if (not_current) {
		sc_file_free(file);
		select_current_path_or_die();
	}
	return 0;
}
Exemplo n.º 12
0
/*
 * 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);
}
Exemplo n.º 13
0
/*
 * 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);
}
Exemplo n.º 14
0
/*
 * 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;

	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
	/* 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);
}
Exemplo n.º 15
0
/*
 * The reason for this function is that OpenSC doesn't set any
 * Security Attribute Tag in the FCI upon file creation if there
 * is no file->sec_attr. I set the file->sec_attr to a format
 * understood by the applet (ISO 7816-4 tables 16, 17 and 20).
 * The iso7816_create_file will then set this as Tag 86 - Sec.
 * Attr. Prop. Format.
 * The applet will then be able to set and enforce access rights
 * for any file created by OpenSC. Without this function, the
 * applet would not know where to enforce security rules and
 * when.
 *
 * Note: IsoApplet currently only supports a "onepin" option.
 *
 * Format of the sec_attr: 8 Bytes:
 *  7      - ISO 7816-4 table 16 or 17
 *  6 to 0 - ISO 7816-4 table 20
 */
static int
isoApplet_create_file(sc_card_t *card, sc_file_t *file)
{
	int r = 0;

	LOG_FUNC_CALLED(card->ctx);

	if(file->sec_attr_len == 0)
	{
		u8 access_buf[8];
		int idx[8], i;

		if(file->type == SC_FILE_TYPE_DF)
		{
			const int df_idx[8] = /* These are the SC operations. */
			{
				0, /* Reserved. */
				SC_AC_OP_DELETE_SELF, /* b6 */
				SC_AC_OP_LOCK,        /* b5 */
				SC_AC_OP_ACTIVATE,    /* b4 */
				SC_AC_OP_DEACTIVATE,  /* b3 */
				SC_AC_OP_CREATE_DF,   /* b2 */
				SC_AC_OP_CREATE_EF,   /* b1 */
				SC_AC_OP_DELETE       /* b0 */
			};
			for(i=0; i<8; i++)
			{
				idx[i] = df_idx[i];
			}
		}
		else   /* EF */
		{
			const int ef_idx[8] =
			{
				0, /* Reserved. */
				SC_AC_OP_DELETE_SELF, /* b6 */
				SC_AC_OP_LOCK,        /* b5 */
				SC_AC_OP_ACTIVATE,    /* b4 */
				SC_AC_OP_DEACTIVATE,  /* b3 */
				SC_AC_OP_WRITE,       /* b2 */
				SC_AC_OP_UPDATE,      /* b1 */
				SC_AC_OP_READ         /* b0 */
			};
			for(i=0; i<8; i++)
			{
				idx[i] = ef_idx[i];
			}
		}
		/* Now idx contains the operation identifiers.
		 * We now search for the OPs. */
		access_buf[0] = 0xFF; /* A security condition byte is present for every OP. (Table 19) */
		for(i=1; i<8; i++)
		{
			const sc_acl_entry_t *entry;
			entry = sc_file_get_acl_entry(file, idx[i]);
			access_buf[i] = isoApplet_acl_to_security_condition_byte(entry);
		}

		r = sc_file_set_sec_attr(file, access_buf, 8);
		LOG_TEST_RET(card->ctx, r, "Error adding security attribute.");
	}

	r = iso_ops->create_file(card, file);
	LOG_FUNC_RETURN(card->ctx, r);
}
Exemplo n.º 16
0
/*
 * Initialize pin file
 */
static int
gpk_init_pinfile(struct sc_profile *profile, sc_pkcs15_card_t *p15card,
		sc_file_t *file)
{
	const sc_acl_entry_t *acl;
	unsigned char	buffer[GPK_MAX_PINS * 8], *blk;
	struct sc_file	*pinfile;
	unsigned int	so_attempts[2], user_attempts[2];
	unsigned int	npins, i, j, cks;
	int		r;

	SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE);
	/* Set defaults */
	so_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PIN);
	so_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_SO_PUK);
	user_attempts[0] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PIN);
	user_attempts[1] = sc_profile_get_pin_retries(profile, SC_PKCS15INIT_USER_PUK);

	sc_file_dup(&pinfile, file);
	if (pinfile == NULL)
		return SC_ERROR_OUT_OF_MEMORY;

	/* Create the PIN file. */
	acl = sc_file_get_acl_entry(pinfile, SC_AC_OP_WRITE);
	if (acl->method != SC_AC_NEVER) {
		sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL,
			"PIN file most be protected by WRITE=NEVER");
		sc_file_free(pinfile);
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_file_add_acl_entry(pinfile, SC_AC_OP_WRITE, SC_AC_NONE, 0);

	if (pinfile->size == 0)
		pinfile->size = GPK_MAX_PINS * 8;

	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Now create file");
	/* Now create the file */
	if ((r = sc_pkcs15init_create_file(profile, p15card, pinfile)) < 0
	 || (r = sc_select_file(p15card->card, &pinfile->path, NULL)) < 0)   {
		goto out;
	}

	/* Set up the PIN file contents.
	 * We assume the file will contain pairs of PINs/PUKs */
	npins = pinfile->size / 8;
	memset(buffer, 0, sizeof(buffer));
	for (i = 0, blk = buffer; i < npins; blk += 8, i += 1) {
		/* Determine the number of PIN/PUK presentation
		 * attempts. If the profile defines a SO PIN,
		 * it will be stored in the first PIN/PUK pair.
		 */
		blk[0] = user_attempts[i & 1];
		if (i < 2 && so_attempts[0])
			blk[0] = so_attempts[i & 1];
		if ((i & 1) == 0) {
			/* This is a PIN. If there's room in the file,
			 * the next will be a PUK so take note of the
			 * unlock code */
			if (i + 1 < npins)
				blk[2] = GPK_PIN_SCOPE | (i + 1);
		}

		/* Compute the CKS */
		for (j = 0, cks = 0; j < 8; j++)
			cks ^= blk[j];
		blk[3] = ~cks;
	}

	r = sc_write_binary(p15card->card, 0, buffer, npins * 8, 0);
	if (r >= 0)
		r = gpk_lock_pinfile(profile, p15card, pinfile);

out:	sc_file_free(pinfile);
	SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
Exemplo n.º 17
0
/*
 * 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;
}
Exemplo n.º 18
0
static int 
sc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path, 
		unsigned char **out, size_t *out_len,
		int verify_pin)
{
	struct sc_context *ctx = p15card->card->ctx;
	struct sc_card *card = p15card->card;
	struct sc_file *file = NULL;
	struct sc_path path;
	size_t sz;
	int rv;

	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
	if (!in_path || !out || !out_len)
		SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS, "Cannot read oberthur file");
		
	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "read file '%s'; verify_pin:%i", in_path, verify_pin);
	
	*out = NULL;
	*out_len = 0;
	
	sc_format_path(in_path, &path);
	rv = sc_select_file(card, &path, &file);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot select oberthur file to read");

	if (file->ef_structure == SC_FILE_EF_TRANSPARENT)
		sz = file->size;
	else
		sz = (file->record_length + 2) * file->record_count;
	
	*out = calloc(sz, 1);
	if (*out == NULL)
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_MEMORY_FAILURE, "Cannot read oberthur file");

	if (file->ef_structure == SC_FILE_EF_TRANSPARENT)   {
		rv = sc_read_binary(card, 0, *out, sz, 0);
	}
	else	{
		int rec;
		int offs = 0;
		int rec_len = file->record_length;
		
		for (rec = 1; ; rec++)   {
			rv = sc_read_record(card, rec, *out + offs + 2, rec_len, SC_RECORD_BY_REC_NR);
			if (rv == SC_ERROR_RECORD_NOT_FOUND)   {
				rv = 0;
				break;
			}
			else if (rv < 0)   {
				break;
			}

			rec_len = rv; 
				
			*(*out + offs) = 'R';
			*(*out + offs + 1) = rv;
			
			offs += rv + 2;
		}

		sz = offs;
	}

	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "read oberthur file result %i", rv);
	if (verify_pin && rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)   {
		struct sc_pkcs15_object *objs[0x10], *pin_obj = NULL;
		const struct sc_acl_entry *acl = sc_file_get_acl_entry(file, SC_AC_OP_READ);
		int ii;

		rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 0x10);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot read oberthur file: get AUTH objects error");

		for (ii=0; ii<rv; ii++)   {
			struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) objs[ii]->data;
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "compare PIN/ACL refs:%i/%i, method:%i/%i", 
					auth_info->attrs.pin.reference, acl->key_ref, auth_info->auth_method, acl->method);
			if (auth_info->attrs.pin.reference == (int)acl->key_ref && auth_info->auth_method == (unsigned)acl->method)   {
				pin_obj = objs[ii];
				break;
			}
		}

		if (!pin_obj || !pin_obj->content.value)    {
			rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
		}
		else    {
			rv = sc_pkcs15_verify_pin(p15card, pin_obj, pin_obj->content.value, pin_obj->content.len);
			if (!rv)
				rv = sc_oberthur_read_file(p15card, in_path, out, out_len, 0);
		}
	};
			
	sc_file_free(file);

	if (rv < 0)   {
		free(*out);
		*out = NULL;
		*out_len = 0;
	}

	*out_len = sz;

	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, rv);
}
Exemplo n.º 19
0
static int print_file(sc_card_t *in_card, const sc_file_t *file,
	const sc_path_t *path, int depth)
{
	int r;
	const char *tmps;
	const char *ac_ops_df[] = {
		"select", "lock", "delete", "create", "rehab", "inval",
		"list"
	};
	const char *ac_ops_ef[] = {
		"read", "update", "write", "erase", "rehab", "inval"
	};
	
	for (r = 0; r < depth; r++)
		printf("  ");
	printf("%s ", sc_print_path(path));
	if (file->namelen) {
		printf("[");
		print_binary(stdout, file->name, file->namelen);
		printf("] ");
	}
	switch (file->type) {
	case SC_FILE_TYPE_WORKING_EF:
		tmps = "wEF";
		break;
	case SC_FILE_TYPE_INTERNAL_EF:
		tmps = "iEF";
		break;
	case SC_FILE_TYPE_DF:
		tmps = " DF";
		break;
	default:
		tmps = "unknown";
		break;
	}
	printf("type: %-3s, ", tmps);
	if (file->type != SC_FILE_TYPE_DF) {
		const char *structs[] = {
			"unknown", "transpnt", "linrfix", "linrfix(TLV)",
			"linvar", "linvar(TLV)", "lincyc", "lincyc(TLV)"
		};
		int ef_type = file->ef_structure;
		if (ef_type < 0 || ef_type > 7)
			ef_type = 0;	/* invalid or unknow ef type */
		printf("ef structure: %s, ", structs[ef_type]);
	}
	printf("size: %lu\n", (unsigned long) file->size);
	for (r = 0; r < depth; r++)
		printf("  ");
	if (file->type == SC_FILE_TYPE_DF)
		for (r = 0; r < (int) (sizeof(ac_ops_df)/sizeof(ac_ops_df[0])); r++)
			printf("%s[%s] ", ac_ops_df[r], acl_to_str(sc_file_get_acl_entry(file, r)));
	else
		for (r = 0; r < (int) (sizeof(ac_ops_ef)/sizeof(ac_ops_ef[0])); r++)
			printf("%s[%s] ", ac_ops_ef[r], acl_to_str(sc_file_get_acl_entry(file, r)));

	if (file->sec_attr_len) {
		printf("sec: ");
		/* Octets are as follows:
		 *   DF: select, lock, delete, create, rehab, inval
		 *   EF: read, update, write, erase, rehab, inval
		 * 4 MSB's of the octet mean:			 
		 *  0 = ALW, 1 = PIN1, 2 = PIN2, 4 = SYS,
		 * 15 = NEV */
		hex_dump(stdout, file->sec_attr, file->sec_attr_len, ":");
	}
	if (file->prop_attr_len) {
		printf("\n");
		for (r = 0; r < depth; r++)
			printf("  ");
		printf("prop: ");
		hex_dump(stdout, file->prop_attr, file->prop_attr_len, ":");
	}
	printf("\n\n");

	if (file->type == SC_FILE_TYPE_DF)
		return 0;

	if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
		unsigned char *buf;
		
		if (!(buf = (unsigned char *) malloc(file->size))) {
			fprintf(stderr, "out of memory");
			return 1;
		}

		r = sc_read_binary(in_card, 0, buf, file->size, 0);
		if (r > 0)
			hex_dump_asc(stdout, buf, r, 0);
		free(buf);
	} else {
		unsigned char buf[256];
		int i;

		for (i=0; i < file->record_count; i++) {
			printf("Record %d\n", i);
			r = sc_read_record(in_card, i, buf, 256, 0);
			if (r > 0)
				hex_dump_asc(stdout, buf, r, 0);
		}
	}
	return 0;
}
Exemplo n.º 20
0
static int do_info(int argc, char **argv)
{
	sc_file_t *file;
	sc_path_t path;
	size_t i;
	const char *st;
	int r, not_current = 1;

	if (!argc) {
		path = current_path;
		file = current_file;
		not_current = 0;
	} else if (argc == 1) {
		if (arg_to_path(argv[0], &path, 0) != 0) 
			goto usage;
		r = sc_select_file(card, &path, &file);
		if (r) {
			printf("unable to select file: %s\n", sc_strerror(r));
			return -1;
		}
	} else 
		goto usage;

	switch (file->type) {
	case SC_FILE_TYPE_WORKING_EF:
	case SC_FILE_TYPE_INTERNAL_EF:
		st = "Elementary File";
		break;
	case SC_FILE_TYPE_DF:
		st = "Dedicated File";
		break;
	default:
		st = "Unknown File";
		break;
	}
	printf("\n%s  ID %04X\n\n", st, file->id);
	printf("%-15s", "File path:");
	for (i = 0; i < path.len; i++) {
		for (i = 0; i < path.len; i++) {
			if ((i & 1) == 0 && i)
				printf("/");
			printf("%02X", path.value[i]);
		}
	}
	printf("\n%-15s%lu bytes\n", "File size:", (unsigned long) file->size);

	if (file->type == SC_FILE_TYPE_DF) {
		const char *ops[] = {
			"SELECT", "LOCK", "DELETE", "CREATE", "REHABILITATE",
			"INVALIDATE", "LIST FILES", "CRYPTO", "DELETE SELF"
		};
		if (file->namelen) {
			printf("%-15s", "DF name:");
			util_print_binary(stdout, file->name, file->namelen);
			printf("\n");
		}
		for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
			char buf[80];
			
			sprintf(buf, "ACL for %s:", ops[i]);
			printf("%-25s%s\n", buf, util_acl_to_str(sc_file_get_acl_entry(file, i)));
		}
	} else {
		const char *structs[] = {
			"Unknown", "Transparent", "Linear fixed",
			"Linear fixed, SIMPLE-TLV", "Linear variable",
			"Linear variable TLV", "Cyclic, SIMPLE-TLV",
		};
		const struct {
			const char * label;
			int op;
		} ops[] = {
			{ "READ", SC_AC_OP_READ },
			{ "UPDATE", SC_AC_OP_UPDATE },
			{ "DELETE", SC_AC_OP_DELETE },
			{ "WRITE", SC_AC_OP_WRITE },
			{ "REHABILITATE", SC_AC_OP_REHABILITATE },
			{ "INVALIDATE", SC_AC_OP_INVALIDATE },
			{ "LIST_FILES", SC_AC_OP_LIST_FILES },
			{ "CRYPTO", SC_AC_OP_CRYPTO },
		};
		printf("%-15s%s\n", "EF structure:", structs[file->ef_structure]);
		for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
			char buf[80];
			
			sprintf(buf, "ACL for %s:", ops[i].label);
			printf("%-25s%s\n", buf, util_acl_to_str(sc_file_get_acl_entry(file, ops[i].op)));
		}
	}	
	if (file->prop_attr_len) {
		printf("%-25s", "Proprietary attributes:");
		for (i = 0; i < file->prop_attr_len; i++)
			printf("%02X ", file->prop_attr[i]);
		printf("\n");
	}
	if (file->sec_attr_len) {
		printf("%-25s", "Security attributes:");
		for (i = 0; i < file->sec_attr_len; i++)
			printf("%02X ", file->sec_attr[i]);
		printf("\n");
	}
	printf("\n");
	if (not_current) {
		sc_file_free(file);
		select_current_path_or_die();
	}
	return 0;

usage:
	puts("Usage: info [file_id]");
	return -1;
}