/*
 * sm.h: Support of Secure Messaging
 *
 * Copyright (C) 2010  Viktor Tarasov <vtarasov@opentrust.com>
 *                      OpenTrust <www.opentrust.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
 */

#ifndef _SM_H
#define _SM_H

#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

#include <libopensc/errors.h>
#include <libopensc/types.h>
#include <common/libscdl.h>

#define SM_TYPE_GP_SCP01	0x100
#define SM_TYPE_CWA14890	0x400
#define SM_TYPE_DH_RSA		0x500

/** don't use SM */
#define SM_MODE_NONE		0x0
/** let the card driver decide when to use SM, possibly based on the card's ACLs */
#define SM_MODE_ACL		0x100
/** use SM for all commands */
#define SM_MODE_TRANSMIT	0x200

#define SM_CMD_INITIALIZE		0x10
#define SM_CMD_MUTUAL_AUTHENTICATION	0x20
#define SM_CMD_RSA			0x100
#define SM_CMD_RSA_GENERATE		0x101
#define SM_CMD_RSA_UPDATE		0x102
#define SM_CMD_RSA_READ_PUBLIC		0x103
#define SM_CMD_FILE			0x200
#define SM_CMD_FILE_READ		0x201
#define SM_CMD_FILE_UPDATE		0x202
#define SM_CMD_FILE_CREATE		0x203
#define SM_CMD_FILE_DELETE		0x204
#define SM_CMD_PIN			0x300
#define SM_CMD_PIN_VERIFY		0x301
#define SM_CMD_PIN_RESET		0x302
#define SM_CMD_PIN_SET_PIN		0x303
#define SM_CMD_PSO			0x400
#define SM_CMD_PSO_DST			0x401
#define SM_CMD_APDU			0x500
#define SM_CMD_APDU_TRANSMIT		0x501
#define SM_CMD_APDU_RAW			0x502
#define SM_CMD_APPLET			0x600
#define SM_CMD_APPLET_DELETE		0x601
#define SM_CMD_APPLET_LOAD		0x602
#define SM_CMD_APPLET_INSTALL		0x603
#define SM_CMD_EXTERNAL_AUTH		0x700
#define SM_CMD_EXTERNAL_AUTH_INIT	0x701
#define SM_CMD_EXTERNAL_AUTH_CHALLENGE	0x702
#define SM_CMD_EXTERNAL_AUTH_DOIT	0x703
#define SM_CMD_SDO_UPDATE		0x800
#define SM_CMD_FINALIZE			0x900

#define SM_RESPONSE_CONTEXT_TAG		0xA1
#define SM_RESPONSE_CONTEXT_DATA_TAG	0xA2

#define SM_MAX_DATA_SIZE    0xE0

#define SM_SMALL_CHALLENGE_LEN	8

#define SM_GP_SECURITY_NO		0x00
#define SM_GP_SECURITY_MAC		0x01
#define SM_GP_SECURITY_ENC		0x03

/* As in OpenSSL include/openssl/des.h */
typedef unsigned char sm_des_cblock[8];
typedef /* const */ unsigned char sm_const_des_cblock[8];

/* Global Platform (SCP01) data types */
/*
 * @struct sm_type_params_gp
 *	Global Platform SM channel parameters
 */
struct sm_type_params_gp {
	unsigned level;
	unsigned index;
	unsigned version;

	struct sc_cplc cplc;
};

/*
 * @struct sm_gp_keyset
 *	Global Platform keyset:
 *	- version, index;
 *	- keyset presented in three parts: 'ENC', 'MAC' and 'KEK';
 *	- keyset presented in continuous manner - raw or 'to be diversified'.
 */
struct sm_gp_keyset {
        int version;
        int index;
        unsigned char enc[16];
        unsigned char mac[16];
        unsigned char kek[16];

        unsigned char kmc[48];
        unsigned kmc_len;
};

/*
 * @struct sm_gp_session
 *	Global Platform SM session data
 */
struct sm_gp_session {
	struct sm_gp_keyset gp_keyset;

	struct sm_type_params_gp params;

	unsigned char host_challenge[SM_SMALL_CHALLENGE_LEN];
	unsigned char card_challenge[SM_SMALL_CHALLENGE_LEN];

	unsigned char *session_enc, *session_mac, *session_kek;
	unsigned char mac_icv[8];
};


/* CWA, IAS/ECC data types */

/*
 * @struct sm_type_params_cwa
 */
struct sm_type_params_cwa {
	struct sc_crt crt_at;
};

/*
 * @struct sm_cwa_keyset
 *	CWA keyset:
 *	- SDO reference;
 *	- 'ENC' and 'MAC' 3DES keys.
 */
struct sm_cwa_keyset {
	unsigned sdo_reference;
	unsigned char enc[16];
	unsigned char mac[16];
};

/*
 * @struct sm_cwa_token_data
 *	CWA token data:
 *	- serial;
 *	- 'small' random;
 *	- 'big' random.
 */
struct sm_cwa_token_data  {
	unsigned char sn[8];
	unsigned char rnd[8];
	unsigned char k[32];
};

/*
 * @struct sm_cwa_session
 *	CWA working SM session data:
 *	- ICC and IFD token data;
 *	- ENC and MAC session keys;
 *	- SSC (SM Sequence Counter);
 *	- 'mutual authentication' data.
 */
struct sm_cwa_session {
	struct sm_cwa_keyset cwa_keyset;

	struct sm_type_params_cwa params;

	struct sm_cwa_token_data icc;
	struct sm_cwa_token_data ifd;

	unsigned char session_enc[16];
	unsigned char session_mac[16];

	unsigned char ssc[8];

	unsigned char host_challenge[SM_SMALL_CHALLENGE_LEN];
	unsigned char card_challenge[SM_SMALL_CHALLENGE_LEN];

	unsigned char mdata[0x48];
	size_t mdata_len;
};

/*
 * @struct sm_dh_session
 *	DH SM session data:
 */
struct sm_dh_session {
	struct sc_tlv_data g;
	struct sc_tlv_data N;
	struct sc_tlv_data ifd_p;
	struct sc_tlv_data ifd_y;
	struct sc_tlv_data icc_p;
	struct sc_tlv_data shared_secret;

	unsigned char session_enc[16];
	unsigned char session_mac[16];

	unsigned char card_challenge[32];

	unsigned char ssc[8];
};

/*
 * @struct sc_info is the
 *	placehold for the secure messaging working data:
 *	- SM type;
 *	- SM session state;
 *	- command to execute by external SM module;
 *	- data related to the current card context.
 */
struct sm_info   {
	char config_section[64];
	unsigned card_type;

	unsigned cmd;
	void *cmd_data;

	unsigned sm_type;
	union {
		struct sm_gp_session gp;
		struct sm_cwa_session cwa;
		struct sm_dh_session dh;
	} session;

	struct sc_serial_number serialnr;

	unsigned security_condition;

	struct sc_path current_path_df;
	struct sc_path current_path_ef;
	struct sc_aid current_aid;

	unsigned char *rdata;
	size_t rdata_len;
};

/*
 * @struct sm_card_response
 *	data type to return card response.
 */
typedef struct sm_card_response   {
	int num;

	unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
	size_t data_len;

	unsigned char mac[8];
	size_t mac_len;

	unsigned char sw1, sw2;

	struct sm_card_response *next;
	struct sm_card_response *prev;
} sm_card_response_t;

struct sc_context;
struct sc_card;

/*
 * @struct sm_card_operations
 *	card driver handlers related to secure messaging (in 'APDU TRANSMIT' mode)
 *	- 'open' - initialize SM session;
 *	- 'encode apdu' - SM encoding of the raw APDU;
 *	- 'decrypt response' - decode card answer;
 *	- 'close' - close SM session.
 */
struct sm_card_operations {
	int (*open)(struct sc_card *card);
	int (*get_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
	int (*free_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
	int (*close)(struct sc_card *card);

	int (*read_binary)(struct sc_card *card, unsigned int idx,
			unsigned char * buf, size_t count);
	int (*update_binary)(struct sc_card *card, unsigned int idx,
			const unsigned char * buf, size_t count);
};

/*
 * @struct sm_module_operations
 *	API to use external SM modules:
 *	- 'initialize' - get APDU(s) to initialize SM session;
 *	- 'get apdus' - get secured APDUs to execute particular command;
 *	- 'finalize' - get APDU(s) to finalize SM session;
 *	- 'module init' - initialize external module (allocate data, read configuration, ...);
 *	- 'module cleanup' - free resources allocated by external module.
 */
struct sm_module_operations {
	int (*initialize)(struct sc_context *ctx, struct sm_info *info,
			struct sc_remote_data *out);
	int (*get_apdus)(struct sc_context *ctx, struct sm_info *sm_info,
			unsigned char *init_data, size_t init_len,
	                struct sc_remote_data *out);
	int (*finalize)(struct sc_context *ctx, struct sm_info *info, struct sc_remote_data *rdata,
			unsigned char *out, size_t out_len);
	int (*module_init)(struct sc_context *ctx, const char *data);
	int (*module_cleanup)(struct sc_context *ctx);

	int (*test)(struct sc_context *ctx, struct sm_info *info, char *out);
};

typedef struct sm_module {
	char filename[128];
	void *handle;

	struct sm_module_operations ops;
} sm_module_t;

/* @struct sm_context
 *	SM context -- top level of the SM data type
 *	- SM mode ('ACL' or 'APDU TRANSMIT'), flags;
 *	- working SM data;
 *	- card operations related to SM in 'APDU TRANSMIT' mode;
 *	- external SM module;
 *	- 'lock'/'unlock' handlers to allow SM transfer in the locked card session.
 */
typedef struct sm_context   {
	char config_section[64];
	unsigned sm_mode, sm_flags;

	struct sm_info info;

	struct sm_card_operations ops;

	struct sm_module module;

	unsigned long (*app_lock)(void);
	void (*app_unlock)(void);
} sm_context_t;

int sc_sm_parse_answer(struct sc_card *, unsigned char *, size_t, struct sm_card_response *);
int sc_sm_update_apdu_response(struct sc_card *, unsigned char *, size_t, int, struct sc_apdu *);
int sc_sm_single_transmit(struct sc_card *, struct sc_apdu *);

/**
 * @brief Stops SM and frees allocated resources.
 *
 * Calls \a card->sm_ctx.ops.close() if available and \c card->sm_ctx.sm_mode
 * is \c SM_MODE_TRANSMIT
 *
 * @param[in] card card
 *
 * @return \c SC_SUCCESS or error code if an error occurred
 */
int sc_sm_stop(struct sc_card *card);

#ifdef __cplusplus
}
#endif

#endif
