/*
 * card-cac1.c: Support for legacy CAC-1
 * card-default.c: Support for cards with no driver
 *
 * Copyright (C) 2001, 2002  Juha Yrjölä <juha.yrjola@iki.fi>
 * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
 * Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
 * Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
 * Copyright (C) 2016 - 2018, Red Hat, Inc.
 *
 * CAC driver author: Robert Relyea <rrelyea@redhat.com>
 * Further work: Jakub Jelen <jjelen@redhat.com>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif

#ifdef ENABLE_OPENSSL
#include <openssl/sha.h>
#endif /* ENABLE_OPENSSL */

#include "internal.h"
#include "simpletlv.h"
#include "cardctl.h"
#include "iso7816.h"
#include "card-cac-common.h"
#include "pkcs15.h"

/*
 *  CAC hardware and APDU constants
 */
#define CAC_INS_GET_CERTIFICATE       0x36  /* CAC1 command to read a certificate */

/*
 * OLD cac read certificate, only use with CAC-1 card.
 */
static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len)
{
	u8 buf[CAC_MAX_SIZE];
	u8 *out_ptr;
	size_t size = 0;
	size_t left = 0;
	size_t len;
	sc_apdu_t apdu;
	int r = SC_SUCCESS;
	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
	/* get the size */
	size = left = *out_buf ? *out_len : sizeof(buf);
	out_ptr = *out_buf ? *out_buf : buf;
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 );
	len = MIN(left, 100);
	while (left > 0) {
		apdu.resp = out_ptr;
		apdu.le = len;
		apdu.resplen = left;
		r = sc_transmit_apdu(card, &apdu);
		if (r < 0) {
			break;
		}
		if (apdu.resplen == 0) {
			r = SC_ERROR_INTERNAL;
			break;
		}
		/* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */
		if (apdu.sw1 != 0x63 || apdu.sw2 < 1)  {
			/* we've either finished reading, or hit an error, break */
			r = sc_check_sw(card, apdu.sw1, apdu.sw2);
			left -= apdu.resplen;
			break;
		}
		/* Adjust the lengths */
		left -= apdu.resplen;
		out_ptr += apdu.resplen;
		len = MIN(left, apdu.sw2);
	}
	if (r < 0) {
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
	}
	r = (int)(size - left);
	if (*out_buf == NULL) {
		*out_buf = malloc(r);
		if (*out_buf == NULL) {
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,
				SC_ERROR_OUT_OF_MEMORY);
		}
		memcpy(*out_buf, buf, r);
	}
	*out_len = r;
	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
}

/*
 * Callers of this may be expecting a certificate,
 * select file will have saved the object type for us
 * as well as set that we want the cert from the object.
 */
static int cac_read_binary(sc_card_t *card, unsigned int idx,
		unsigned char *buf, size_t count, unsigned long *flags)
{
	cac_private_data_t * priv = CAC_DATA(card);
	int r = 0;
	u8 *val = NULL;
	u8 *cert_ptr;
	size_t val_len = 0;
	size_t len, cert_len;
	u8 cert_type;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	/* if we didn't return it all last time, return the remainder */
	if (priv->cached) {
		sc_log(card->ctx,
			"returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
			idx, count);
		if (idx > priv->cache_buf_len) {
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED);
		}
		len = MIN(count, priv->cache_buf_len-idx);
		memcpy(buf, &priv->cache_buf[idx], len);
		LOG_FUNC_RETURN(card->ctx, (int)len);
	}

	sc_log(card->ctx,
		"clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
		idx, count);
	free(priv->cache_buf);
	priv->cache_buf = NULL;
	priv->cache_buf_len = 0;

	r = cac_cac1_get_certificate(card, &val, &val_len);
	if (r < 0)
		goto done;
	if (val_len < 1) {
		r = SC_ERROR_INVALID_DATA;
		goto done;
	}

	cert_type = val[0];
	cert_ptr = val + 1;
	cert_len = val_len - 1;

	/* if the info byte is 1, then the cert is compressed, decompress it */
	if ((cert_type & 0x3) == 1) {
		*flags |= SC_FILE_FLAG_COMPRESSED_AUTO;
	}
	if (cert_len > 0) {
		priv->cache_buf = malloc(cert_len);
		if (priv->cache_buf == NULL) {
			r = SC_ERROR_OUT_OF_MEMORY;
			goto done;
		}
		priv->cache_buf_len = cert_len;
		memcpy(priv->cache_buf, cert_ptr, cert_len);
	}

	/* OK we've read the data, now copy the required portion out to the callers buffer */
	priv->cached = 1;
	len = MIN(count, priv->cache_buf_len-idx);
	if (len && priv->cache_buf)
		memcpy(buf, &priv->cache_buf[idx], len);
	r = (int)len;
done:
	if (val)
		free(val);
	LOG_FUNC_RETURN(card->ctx, r);
}

/*
 * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more
 * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead
 * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS
 * if it doesn't like anything about the select, so we always 'request' FCI for CAC1
 *
 * The rest is just copied from iso7816_select_file
 */
static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
{
	struct sc_context *ctx;
	struct sc_apdu apdu;
	unsigned char buf[SC_MAX_APDU_BUFFER_SIZE];
	unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
	int r, pathtype;
	size_t pathlen;
	struct sc_file *file = NULL;
	cac_private_data_t * priv = CAC_DATA(card);

	assert(card != NULL && in_path != NULL);
	ctx = card->ctx;

	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);

	memcpy(path, in_path->value, in_path->len);
	pathlen = in_path->len;
	pathtype = in_path->type;

	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
		"path=%s, path->value=%s path->type=%d (%x)",
		sc_print_path(in_path),
		sc_dump_hex(in_path->value, in_path->len),
		in_path->type, in_path->type);
	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n",
		file_out, in_path->index, in_path->count);

	/* Sigh, iso7816_select_file expects paths to keys to have specific
	 * formats. There is no override. We have to add some bytes to the
	 * path to make it happy.
	 * We only need to do this for private keys.
	 */
	if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) {
		path += 2;
		pathlen -= 2;
	}


	/* CAC has multiple different type of objects that aren't PKCS #15. When we read
	 * them we need convert them to something PKCS #15 would understand. Find the object
	 * and object type here:
	 */
	if (priv) { /* don't record anything if we haven't been initialized yet */
		/* forget any old cached values */
		if (priv->cache_buf) {
			free(priv->cache_buf);
			priv->cache_buf = NULL;
		}
		priv->cache_buf_len = 0;
		priv->cached = 0;
	}

	if (in_path->aid.len) {
		if (!pathlen) {
			memcpy(path, in_path->aid.value, in_path->aid.len);
			pathlen = in_path->aid.len;
			pathtype = SC_PATH_TYPE_DF_NAME;
		} else {
			/* First, select the application */
			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" );
			sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0);
			apdu.data = in_path->aid.value;
			apdu.datalen = in_path->aid.len;
			apdu.lc = in_path->aid.len;

			r = sc_transmit_apdu(card, &apdu);
			LOG_TEST_RET(ctx, r, "APDU transmit failed");
			r = sc_check_sw(card, apdu.sw1, apdu.sw2);
			if (r)
				LOG_FUNC_RETURN(ctx, r);

		}
	}

	sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);

	switch (pathtype) {
	/* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select.
	 * Unfortunately we'd also need to update the caching code as well. For now just
	 * use FILE_ID and change p1 here */
	case SC_PATH_TYPE_FILE_ID:
		apdu.p1 = 2;
		if (pathlen != 2)
			return SC_ERROR_INVALID_ARGUMENTS;
		break;
	case SC_PATH_TYPE_DF_NAME:
		apdu.p1 = 4;
		break;
	default:
		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
	}
	apdu.lc = pathlen;
	apdu.data = path;
	apdu.datalen = pathlen;
	apdu.resp = buf;
	apdu.resplen = sizeof(buf);
	apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256;
	apdu.p2 = 0x00;

	r = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, r, "APDU transmit failed");

	if (file_out == NULL) {
		/* For some cards 'SELECT' can be only with request to return FCI/FCP. */
		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
		if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86)   {
			apdu.p2 = 0x00;
			apdu.resplen = sizeof(buf);
			if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS)
				r = sc_check_sw(card, apdu.sw1, apdu.sw2);
		}
		if (apdu.sw1 == 0x61)
			LOG_FUNC_RETURN(ctx, SC_SUCCESS);
		LOG_FUNC_RETURN(ctx, r);
	}

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if (r)
		LOG_FUNC_RETURN(ctx, r);

	/* CAC cards never return FCI, fake one */
	file = sc_file_new();
	if (file == NULL)
			LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
	file->path = *in_path;
	file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */

	*file_out = file;
	LOG_FUNC_RETURN(ctx, SC_SUCCESS);

}

static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
{
	return cac_select_file_by_type(card, in_path, file_out);
}

static int cac_finish(sc_card_t *card)
{
	cac_private_data_t * priv = CAC_DATA(card);

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
	if (priv) {
		cac_free_private_data(priv);
	}
	return SC_SUCCESS;
}


/* select a CAC pki applet by index */
static int cac_select_pki_applet(sc_card_t *card, int index)
{
	sc_path_t applet_path = cac_cac_pki_obj.path;
	applet_path.aid.value[applet_path.aid.len-1] = index;
	return cac_select_file_by_type(card, &applet_path, NULL);
}

/*
 *  Find the first existing CAC applet. If none found, then this isn't a CAC
 */
static int cac_find_first_pki_applet(sc_card_t *card, int *index_out)
{
	int r, i;

	for (i = 0; i < MAX_CAC_SLOTS; i++) {
		r = cac_select_pki_applet(card, i);
		if (r == SC_SUCCESS) {
			u8 data[2];
			sc_apdu_t apdu;

			/* Try to read first two bytes of the buffer to
			 * make sure it is not just malfunctioning card
			 */
			sc_format_apdu(card, &apdu, SC_APDU_CASE_2,
				CAC_INS_GET_CERTIFICATE, 0x00, 0x00);
			apdu.le = 0x02;
			apdu.resplen = 2;
			apdu.resp = data;
			r = sc_transmit_apdu(card, &apdu);
			/* SW1 = 0x63 means more data in CAC1 */
			if (r == SC_SUCCESS && apdu.sw1 != 0x63)
				continue;

			*index_out = i;
			return SC_SUCCESS;
		}
	}
	return SC_ERROR_OBJECT_NOT_FOUND;
}

static int cac_populate_cac1(sc_card_t *card, int index, cac_private_data_t *priv)
{
	int r, i;
	cac_object_t pki_obj = cac_cac_pki_obj;
	u8 buf[100];
	u8 *val;
	size_t val_len;

	/* populate PKI objects */
	for (i = index; i < MAX_CAC_SLOTS; i++) {
		r = cac_select_pki_applet(card, i);
		if (r == SC_SUCCESS) {
			pki_obj.name = get_cac_label(i);
			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
				"CAC: pki_object found, cert_next=%d (%s)",
				i, pki_obj.name);
			pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i;
			pki_obj.fd = i+1; /* don't use id of zero */
			cac_add_object_to_list(&priv->pki_list, &pki_obj);
		}
	}

	/*
	 * create a cuid to simulate the cac 2 cuid.
	 */
	priv->cuid = cac_cac_cuid;
	/* create a serial number by hashing the first 100 bytes of the
	 * first certificate on the card */
	r = cac_select_pki_applet(card, index);
	if (r < 0) {
		return r; /* shouldn't happen unless the card has been removed or is malfunctioning */
	}
	val = buf;
	val_len = sizeof(buf);
	r = cac_cac1_get_certificate(card, &val, &val_len);
	if (r >= 0) {
		priv->cac_id = malloc(20);
		if (priv->cac_id == NULL) {
			return SC_ERROR_OUT_OF_MEMORY;
		}
#ifdef ENABLE_OPENSSL
		SHA1(val, val_len, priv->cac_id);
		priv->cac_id_len = 20;
		sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE,
			"cuid", priv->cac_id, priv->cac_id_len);
#else
		sc_log(card->ctx, "OpenSSL Required");
		return SC_ERROR_NOT_SUPPORTED;
#endif /* ENABLE_OPENSSL */
	}
	return SC_SUCCESS;
}

/*
 * Look for a CAC card. If it exists, initialize our data structures
 */
static int cac_find_and_initialize(sc_card_t *card, int initialize)
{
	int r, index;
	cac_private_data_t *priv = NULL;

	/* already initialized? */
	if (card->drv_data) {
		return SC_SUCCESS;
	}

	/* is this a CAC Alt token without any accompanying structures */
	r = cac_find_first_pki_applet(card, &index);
	if (r == SC_SUCCESS) {
		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC-1");
		if (!initialize) /* match card only */
			return r;

		if (!priv) {
			priv = cac_new_private_data();
			if (!priv)
				return SC_ERROR_OUT_OF_MEMORY;
		}
		card->drv_data = priv; /* needed for the read_binary() */
		r = cac_populate_cac1(card, index, priv);
		if (r == SC_SUCCESS) {
			card->type = SC_CARD_TYPE_CAC_I;
			return r;
		}
		card->drv_data = NULL; /* reset on failure */
	}
	if (priv) {
		cac_free_private_data(priv);
	}
	return r;
}


/* NOTE: returns a bool, 1 card matches, 0 it does not */
static int cac_match_card(sc_card_t *card)
{
	int r;
	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	r = cac_find_and_initialize(card, 0);
	return (r == SC_SUCCESS); /* never match */
}


static int cac_init(sc_card_t *card)
{
	int r;
	unsigned long flags;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	r = cac_find_and_initialize(card, 1);
	if (r < 0) {
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD);
	}
	flags = SC_ALGORITHM_RSA_RAW;

	_sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */
	_sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
	_sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */

	card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

static int cac_logout(sc_card_t *card)
{
	int index;
	return cac_find_first_pki_applet(card, &index);
}

static struct sc_card_operations cac_ops;

static struct sc_card_driver cac1_drv = {
	"Common Access Card (CAC 1)",
	"cac1",
	&cac_ops,
	NULL, 0, NULL
};

static struct sc_card_driver * sc_get_driver(void)
{
	/* Inherit most of the things from the CAC driver */
	struct sc_card_driver *cac_drv = sc_get_cac_driver();

	cac_ops = *cac_drv->ops;
	cac_ops.match_card = cac_match_card;
	cac_ops.init = cac_init;
	cac_ops.finish = cac_finish;

	cac_ops.select_file =  cac_select_file; /* need to record object type */
	cac_ops.read_binary = cac_read_binary;
	cac_ops.logout = cac_logout;

	return &cac1_drv;
}


struct sc_card_driver * sc_get_cac1_driver(void)
{
	return sc_get_driver();
}
