Linux debugging

Check our new training course

Linux debugging, tracing, profiling & perf. analysis

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2014, Linaro Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <tee_api_types.h>
#include <trace.h>
#include <kernel/tee_common_unpg.h>
#include <tee/se/reader.h>
#include <tee/se/session.h>
#include <tee/se/iso7816.h>
#include <tee/se/aid.h>
#include <tee/se/apdu.h>
#include <tee/se/channel.h>
#include <tee/se/util.h>
#include <tee/se/reader/interface.h>

#include <malloc.h>
#include <stdlib.h>
#include <string.h>

#include "session_priv.h"
#include "aid_priv.h"
#include "apdu_priv.h"

TEE_Result iso7816_exchange_apdu(struct tee_se_reader_proxy *proxy,
		struct cmd_apdu *cmd, struct resp_apdu *resp)
{
	TEE_Result ret;

	TEE_ASSERT(cmd != NULL && resp != NULL);
	ret = tee_se_reader_transmit(proxy,
			cmd->base.data_buf, cmd->base.length,
			resp->base.data_buf, &resp->base.length);

	if (ret == TEE_SUCCESS)
		parse_resp_apdu(resp);

	return ret;
}

int iso7816_get_cla_channel(int channel_id)
{
	int cla_channel;
	/*
	 * From GP Card Spec,
	 * the logical channel number 0~3 should have CLA: 0x00 ~ 0x03,
	 * for channel number 4~19 should have CLA: 0x40 ~ 0x4f
	 */
	if (channel_id < 4)
		cla_channel = channel_id;
	else
		cla_channel = 0x40 | (channel_id - 4);

	return cla_channel;
}

static TEE_Result internal_select(struct tee_se_channel *c,
		struct tee_se_aid *aid, int select_ops)
{
	struct cmd_apdu *cmd;
	struct resp_apdu *resp;
	struct tee_se_session *s;
	TEE_Result ret;
	TEE_SEReaderProperties prop;
	size_t rx_buf_len = 0;
	int channel_id;
	uint8_t cla_channel;

	TEE_ASSERT(c != NULL);

	s = tee_se_channel_get_session(c);
	channel_id = tee_se_channel_get_id(c);

	TEE_ASSERT(channel_id < MAX_LOGICAL_CHANNEL);

	cla_channel = iso7816_get_cla_channel(channel_id);
	if (select_ops == FIRST_OR_ONLY_OCCURRENCE) {
		TEE_ASSERT(aid != NULL);
		cmd = alloc_cmd_apdu(ISO7816_CLA | cla_channel,
				SELECT_CMD, SELECT_BY_AID,
				select_ops, aid->length,
				rx_buf_len, aid->aid);
	} else {
		cmd = alloc_cmd_apdu(ISO7816_CLA | cla_channel,
				SELECT_CMD, SELECT_BY_AID,
				select_ops, 0, rx_buf_len, NULL);
	}

	resp = alloc_resp_apdu(rx_buf_len);

	ret = tee_se_session_transmit(s, cmd, resp);
	if (ret != TEE_SUCCESS) {
		EMSG("exchange apdu failed: %d", ret);
		return ret;
	}

	tee_se_reader_get_properties(s->reader_proxy, &prop);
	if (prop.selectResponseEnable)
		tee_se_channel_set_select_response(c, resp);
	if (aid)
		tee_se_channel_set_aid(c, aid);

	if (resp->sw1 == CMD_OK_SW1 && resp->sw2 == CMD_OK_SW2) {
		ret = TEE_SUCCESS;
	} else {
		EMSG("operation failed, sw1:%02X, sw2:%02X",
				resp->sw1, resp->sw2);
		if (resp->sw1 == 0x6A && resp->sw2 == 0x83)
			ret = TEE_ERROR_ITEM_NOT_FOUND;
		else
			ret = TEE_ERROR_NOT_SUPPORTED;
	}

	apdu_release(to_apdu_base(cmd));
	apdu_release(to_apdu_base(resp));

	return ret;
}

static TEE_Result internal_manage_channel(struct tee_se_session *s,
		bool open_ops, int *channel_id)
{
	struct cmd_apdu *cmd;
	struct resp_apdu *resp;
	TEE_Result ret;
	size_t tx_buf_len = 0, rx_buf_len = 1;

	uint8_t open_flag = (open_ops) ? OPEN_CHANNEL : CLOSE_CHANNEL;
	uint8_t channel_flag =
		(open_flag == OPEN_CHANNEL) ? OPEN_NEXT_AVAILABLE : *channel_id;

	TEE_ASSERT(s != NULL);

	cmd = alloc_cmd_apdu(ISO7816_CLA, MANAGE_CHANNEL_CMD, open_flag,
			channel_flag, tx_buf_len, rx_buf_len, NULL);

	resp = alloc_resp_apdu(rx_buf_len);

	ret = tee_se_session_transmit(s, cmd, resp);
	if (ret != TEE_SUCCESS) {
		EMSG("exchange apdu failed: %d", ret);
		return ret;
	}

	if (resp->sw1 == CMD_OK_SW1 && resp->sw2 == CMD_OK_SW2) {
		if (open_ops)
			*channel_id = resp->base.data_buf[0];
		ret = TEE_SUCCESS;
	} else {
		EMSG("operation failed, sw1:%02X, sw2:%02X",
				resp->sw1, resp->sw2);
		ret = TEE_ERROR_NOT_SUPPORTED;
	}

	apdu_release(to_apdu_base(cmd));
	apdu_release(to_apdu_base(resp));

	return ret;
}

TEE_Result iso7816_open_available_logical_channel(struct tee_se_session *s,
		int *channel_id)
{
	return internal_manage_channel(s, true, channel_id);
}

TEE_Result iso7816_close_logical_channel(struct tee_se_session *s,
		int channel_id)
{
	return internal_manage_channel(s, false, &channel_id);
}

TEE_Result iso7816_select(struct tee_se_channel *c, struct tee_se_aid *aid)
{
	return internal_select(c, aid, FIRST_OR_ONLY_OCCURRENCE);
}

TEE_Result iso7816_select_next(struct tee_se_channel *c)
{
	return internal_select(c, NULL, NEXT_OCCURRENCE);
}