/*
 *
 *  Wireless daemon for Linux
 *
 *  Copyright (C) 2017-2019  Intel Corporation. All rights reserved.
 *
 *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/*
 * EAP-SIM/EAP-AKA shared values
 */
#define EAP_SIM_MK_LEN		20
#define EAP_SIM_K_ENCR_LEN	16
#define EAP_SIM_K_AUT_LEN	16
#define EAP_AKA_PRIME_K_AUT_LEN	32
#define EAP_SIM_MSK_LEN		64
#define EAP_SIM_EMSK_LEN	64
#define EAP_SIM_IV_LEN		16
#define EAP_SIM_MAC_LEN		16
#define EAP_SIM_RAND_LEN	16
#define EAP_AKA_K_RE_LEN	32
#define EAP_AKA_IK_LEN		16
#define EAP_AKA_CK_LEN		16

/*
 * Possible pad types for EAP-SIM/EAP-AKA attributes
 *
 * NONE - No padding, data passed in immediately follows type/size
 * ZERO - Zero pad where "real" length would be
 * LENGTH - A "real" data length in bytes immediately follows type/size
 * LENGTH_BITS - A "real" data length in bits follows type/size
 */
#define EAP_SIM_PAD_NONE		0
#define EAP_SIM_PAD_ZERO		1
#define EAP_SIM_PAD_LENGTH		2
#define EAP_SIM_PAD_LENGTH_BITS		3

/*
 * Round up value to nearest word
 */
#define EAP_SIM_ROUND(x) ((x + 3) & ~0x3)

struct eap_sim_tlv_iter {
	const uint8_t *pos;
	const uint8_t *end;
	uint8_t tag;
	uint16_t len;
	const uint8_t *data;
};

/*
 * RFC 4187, section 11
 */
enum eap_sim_at {
	EAP_SIM_AT_RAND			= 0x01,
	EAP_SIM_AT_AUTN			= 0x02,
	EAP_SIM_AT_RES			= 0x03,
	EAP_SIM_AT_AUTS			= 0x04,
	EAP_SIM_AT_PADDING		= 0x06,
	EAP_SIM_AT_NONCE		= 0x07,
	EAP_SIM_AT_PERMANENT_ID_REQ	= 0x0a,
	EAP_SIM_AT_MAC			= 0x0b,
	EAP_SIM_AT_NOTIFICATION		= 0x0c,
	EAP_SIM_AT_ANY_ID_REQ		= 0x0d,
	EAP_SIM_AT_IDENTITY		= 0x0e,
	EAP_SIM_AT_VERSION_LIST		= 0x0f,
	EAP_SIM_AT_SELECTED_VERSION	= 0x10,
	EAP_SIM_AT_FULLAUTH_ID_REQ	= 0x11,
	EAP_SIM_AT_COUNTER		= 0x13,
	EAP_SIM_AT_NONCE_S		= 0x15,
	EAP_SIM_AT_CLIENT_ERROR_CODE	= 0x16,
	EAP_SIM_AT_KDF_INPUT		= 0x17,
	EAP_SIM_AT_KDF			= 0x18,
	EAP_SIM_AT_IV			= 0x81,
	EAP_SIM_AT_ENCR_DATA		= 0x82,
	EAP_SIM_AT_NEXT_PSEUDONYM	= 0x84,
	EAP_SIM_AT_NEXT_REAUTH_ID	= 0x85,
	EAP_SIM_AT_CHECKCODE		= 0x86,
	EAP_SIM_AT_RESULT_IND		= 0x87,
	EAP_SIM_AT_BIDDING		= 0x88
};

/*
 * Possible client error's
 */
enum eap_sim_error {
	EAP_SIM_ERROR_PROCESS = 0,
	EAP_SIM_ERROR_VERSION_VERSION,
	EAP_SIM_ERROR_CHALLENGE,
	EAP_SIM_ERROR_RANDS
};

/*
 * Notification error codes (and success).
 */
enum eap_sim_fail {
	EAP_SIM_FAIL_AFTER_AUTH		= 0,
	EAP_SIM_FAIL_DENIED_ACCESS	= 1026,
	EAP_SIM_FAIL_NOT_SUBSCRIBED	= 1031,
	EAP_SIM_FAIL_BEFORE_AUTH	= 16384,
	EAP_SIM_SUCCESS			= 32768
};

/*
 * RFC 4186 Appendix B. FIPS 186 Pseudo-random number generator
 *
 * seed - PRF seed, e.g. the Master Key (MK)
 * slen - seed length
 * out - PRF output buffer
 * olen - length of out
 */
void eap_sim_fips_prf(const void *seed, size_t slen, uint8_t *out, size_t olen);

/*
 * 3GPP TS 35.206
 *
 * Algorithm for generating milenage parameters.
 *
 * opc - Input: OPc value
 * k - Input: K key value
 * rand - Input: Rand from server
 * sqn - Input: Sequence number
 * amf - Input: AMF value
 *
 * autn - Output: AUTN computed
 * ck - Output: CK computed
 * ik - Output: IK computed
 * res - Output: RES computed
 *
 * Internal Functions used:
 * f1 and f1* output MAC-A and MAC-S. MAC-A along with SQN/AK/AMF make AUTN
 * f2 outputs RES where RES == OUT2[8-16]
 * f3 outputs CK where CK == OUT3[0-16]
 * f4 outputs IK where IK == OUT4[0-16]
 * f5 outputs AK where AK == OUT2[0-6]
 *
 * f5* outputs AK', not used with EAP-AKA
 */
bool eap_aka_get_milenage(const uint8_t *opc, const uint8_t *k,
		const uint8_t *rand, const uint8_t *sqn, const uint8_t *amf,
		uint8_t *autn, uint8_t *ck, uint8_t *ik, uint8_t *res);

/*
 * 3GPP TS 33.402
 * Derivation of CK' and IK' from CK and IK
 *
 * FC = 0x20
 * P0 = access network identity ('network')
 * L0 = length of P0
 * P1 = SQN ^ AK = first 6 bytes of AUTN
 * L1 = length of P1 = 0x0006
 *
 * (CK' | IK') = HMAC_SHA256(FC |P0 | L0 | P1 | L1)
 */
bool eap_aka_derive_primes(const uint8_t *ck, const uint8_t *ik,
		const uint8_t *autn, const uint8_t *network, uint16_t net_len,
		uint8_t *ck_p, uint8_t *ik_p);

/*
 * RFC 5448, Section 3.4.1
 *
 * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
 *
 *     where:
 *     T1 = HMAC-SHA-256 (K, S | 0x01)
 *     T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
 *     T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
 *     T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
 *     ...
 *
 * RFC 5448, Section 3.3
 *
 * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
 *      K_encr = MK[0..127]
 *      K_aut  = MK[128..383]
 *      K_re   = MK[384..639]
 *      MSK    = MK[640..1151]
 *      EMSK   = MK[1152..1663]
 */
bool eap_aka_prf_prime(const uint8_t *ik_p, const uint8_t *ck_p,
		const char *identity, uint8_t *k_encr, uint8_t *k_aut,
		uint8_t *k_re, uint8_t *msk, uint8_t *emsk);

/*
 * Separate PRNG data into encryption keys. k_encr and k_aut may be NULL in the
 * case of fast re-authentication.
 *
 * buf - output data from the PRNG, 160 bytes
 * k_encr - first 16 bytes of buf
 * k_aut - next 16 bytes of buf
 * msk - next 64 bytes of buf
 * emsk - next 64 bytes of buf
 */
bool eap_sim_get_encryption_keys(const uint8_t *buf, uint8_t *k_encr,
		uint8_t *k_aut, uint8_t *msk, uint8_t *emsk);

/*
 * Derive a packets MAC. This can be used to compute the packets MAC in place,
 * by setting mac to the proper zero'ed location in buf.
 *
 * buf - the SIM packet, including MAC portion zero'ed, plus extra (e.g. SRES)
 * len - the total length of buf
 * key - encryption key to use (e.g. K_encr)
 * mac - buffer for the 16 byte MAC
 */
bool eap_sim_derive_mac(enum eap_type type, const uint8_t *buf, size_t len,
		const uint8_t *key, uint8_t *mac);

/*
 * Helper to build the EAP packet header
 *
 * eap - eap_state, used to get the identifier
 * method - EAP method (SIM or AKA)
 * type - EAP-SIM subtype
 * buf - EAP packet
 * len - length of packet
 */
size_t eap_sim_build_header(struct eap_state *eap, enum eap_type method,
		uint8_t type, uint8_t *buf, uint16_t len);

/*
 * Signal that the client has detected an error
 *
 * eap - eap_state
 * type - type of EAP method (SIM or AKA)
 * code - error code to send
 */
void eap_sim_client_error(struct eap_state *eap, enum eap_type type,
		uint16_t code);

/*
 * Add an EAP-SIM attribute to a buffer.
 *
 * buf - pointer to start of EAP-SIM attribute
 * attr - type of attribute
 * ptype - Padding type AT_PAD_ZERO, AT_PAD_NONE or AT_PAD_LENGTH
 * data - EAP-SIM attribute data, if NULL zeros will be written
 * dlen - length of data pointer in bytes
 *
 * Returns the number of bytes written to buf.
 */
size_t eap_sim_add_attribute(uint8_t *buf, enum eap_sim_at attr,
		uint8_t ptype, const uint8_t *data, uint16_t dlen);

/*
 * Verify a packets MAC
 *
 * eap - eap_state pointer, used to rebuild the EAP header
 * buf - should point to the start of the EAP-SIM packet
 * len - length of EAP packet
 * extra - Any extra block of data needed to compute the MAC
 * elen - Length of 'extra'
 */
bool eap_sim_verify_mac(struct eap_state *eap, enum eap_type type,
		const uint8_t *buf, uint16_t len, uint8_t *k_aut,
		uint8_t *extra, size_t elen);

bool eap_sim_tlv_iter_init(struct eap_sim_tlv_iter *iter, const uint8_t *data,
		uint32_t len);

bool eap_sim_tlv_iter_next(struct eap_sim_tlv_iter *iter);

uint8_t eap_sim_tlv_iter_get_type(struct eap_sim_tlv_iter *iter);

uint16_t eap_sim_tlv_iter_get_length(struct eap_sim_tlv_iter *iter);

const void *eap_sim_tlv_iter_get_data(struct eap_sim_tlv_iter *iter);
