/* =========================================================================
 * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.c $
 * $Revision: #5 $
 * $Date: 2010/09/28 $
 * $Change: 1596182 $
 *
 * Synopsys Portability Library Software and documentation
 * (hereinafter, "Software") is an Unsupported proprietary work of
 * Synopsys, Inc. unless otherwise expressly agreed to in writing
 * between Synopsys and you.
 *
 * The Software IS NOT an item of Licensed Software or Licensed Product
 * under any End User Software License Agreement or Agreement for
 * Licensed Product with Synopsys or any supplement thereto. You are
 * permitted to use and redistribute this Software in source and binary
 * forms, with or without modification, provided that redistributions
 * of source code must retain this notice. You may not view, use,
 * disclose, copy or distribute this file or any information contained
 * herein except pursuant to this license grant from Synopsys. If you
 * do not agree with this notice, including the disclaimer below, then
 * you are not authorized to use the Software.
 *
 * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
 * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
 * SYNOPSYS 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.
 * ========================================================================= */

/** @file
 * This file contains the WUSB cryptographic routines.
 */

#ifdef DWC_CRYPTOLIB

#include "dwc_crypto.h"
#include "usb.h"

#ifdef DEBUG
static inline void dump_bytes(char *name, uint8_t *bytes, int len)
{
	int i;
	DWC_PRINTF("%s: ", name);
	for (i = 0; i < len; i++)
		DWC_PRINTF("%02x ", bytes[i]);

	DWC_PRINTF("\n");
}
#else
#define dump_bytes(x...)
#endif

/* Display a block */
void show_block(const u8 *blk, const char *prefix, const char *suffix, int a)
{
#ifdef DWC_DEBUG_CRYPTO
	int i, blksize = 16;

	DWC_DEBUG("%s", prefix);

	if (suffix == NULL) {
		suffix = "\n";
		blksize = a;
	}

	for (i = 0; i < blksize; i++)
		DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? "  " : " ");
	DWC_PRINT(suffix);
#endif
}

/**
 * Encrypts an array of bytes using the AES encryption engine.
 * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted
 * in-place.
 *
 * @return  0 on success, negative error code on error.
 */
int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst)
{
	u8 block_t[16];
	DWC_MEMSET(block_t, 0, 16);

	return DWC_AES_CBC(src, 16, key, 16, block_t, dst);
}

/**
 * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec.
 * This function takes a data string and returns the encrypted CBC
 * Counter-mode MIC.
 *
 * @param key     The 128-bit symmetric key.
 * @param nonce   The CCM nonce.
 * @param label   The unique 14-byte ASCII text label.
 * @param bytes   The byte array to be encrypted.
 * @param len     Length of the byte array.
 * @param result  Byte array to receive the 8-byte encrypted MIC.
 */
void dwc_wusb_cmf(u8 *key, u8 *nonce,
		  char *label, u8 *bytes, int len, u8 *result)
{
	u8 block_m[16];
	u8 block_x[16];
	u8 block_t[8];
	int idx, blkNum;
	u16 la = (u16)(len + 14);

	/* Set the AES-128 key */
	/*dwc_aes_setkey(tfm, key, 16);*/

	/* Fill block B0 from flags = 0x59, N, and l(m) = 0 */
	block_m[0] = 0x59;
	for (idx = 0; idx < 13; idx++)
		block_m[idx + 1] = nonce[idx];
	block_m[14] = 0;
	block_m[15] = 0;

	/* Produce the CBC IV */
	dwc_wusb_aes_encrypt(block_m, key, block_x);
	show_block(block_m, "CBC IV in: ", "\n", 0);
	show_block(block_x, "CBC IV out:", "\n", 0);

	/* Fill block B1 from l(a) = Blen + 14, and A */
	block_x[0] ^= (u8)(la >> 8);
	block_x[1] ^= (u8)la;
	for (idx = 0; idx < 14; idx++)
		block_x[idx + 2] ^= label[idx];
	show_block(block_x, "After xor: ", "b1\n", 16);

	dwc_wusb_aes_encrypt(block_x, key, block_x);
	show_block(block_x, "After AES: ", "b1\n", 16);

	idx = 0;
	blkNum = 0;

	/* Fill remaining blocks with B */
	while (len-- > 0) {
		block_x[idx] ^= *bytes++;
		if (++idx >= 16) {
			idx = 0;
			show_block(block_x, "After xor: ", "\n", blkNum);
			dwc_wusb_aes_encrypt(block_x, key, block_x);
			show_block(block_x, "After AES: ", "\n", blkNum);
			blkNum++;
		}
	}

	/* Handle partial last block */
	if (idx > 0) {
		show_block(block_x, "After xor: ", "\n", blkNum);
		dwc_wusb_aes_encrypt(block_x, key, block_x);
		show_block(block_x, "After AES: ", "\n", blkNum);
	}

	/* Save the MIC tag */
	DWC_MEMCPY(block_t, block_x, 8);
	show_block(block_t, "MIC tag  : ", NULL, 8);

	/* Fill block A0 from flags = 0x01, N, and counter = 0 */
	block_m[0] = 0x01;
	block_m[14] = 0;
	block_m[15] = 0;

	/* Encrypt the counter */
	dwc_wusb_aes_encrypt(block_m, key, block_x);
	show_block(block_x, "CTR[MIC] : ", NULL, 8);

	/* XOR with MIC tag */
	for (idx = 0; idx < 8; idx++)
		block_t[idx] ^= block_x[idx];

	/* Return result to caller */
	DWC_MEMCPY(result, block_t, 8);
	show_block(result, "CCM-MIC  : ", NULL, 8);
}

/**
 * The PRF function described in section 6.5 of the WUSB spec. This function
 * concatenates MIC values returned from dwc_cmf() to create a value of
 * the requested length.
 *
 * @param prf_len  Length of the PRF function in bits (64, 128, or 256).
 * @param key, nonce, label, bytes, len  Same as for dwc_cmf().
 * @param result   Byte array to receive the result.
 */
void dwc_wusb_prf(int prf_len, u8 *key,
		  u8 *nonce, char *label, u8 *bytes, int len, u8 *result)
{
	int i;

	nonce[0] = 0;
	for (i = 0; i < prf_len >> 6; i++, nonce[0]++) {
		dwc_wusb_cmf(key, nonce, label, bytes, len, result);
		result += 8;
	}
}

/**
 * Fills in CCM Nonce per the WUSB spec.
 *
 * @param[in] haddr Host address.
 * @param[in] daddr Device address.
 * @param[in] tkid Session Key(PTK) identifier.
 * @param[out] nonce Pointer to where the CCM Nonce output is to be written.
 */
void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
			     uint8_t *nonce)
{

	DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr);

	DWC_MEMSET(&nonce[0], 0, 16);

	DWC_MEMCPY(&nonce[6], tkid, 3);
	nonce[9] = daddr & 0xFF;
	nonce[10] = (daddr >> 8) & 0xFF;
	nonce[11] = haddr & 0xFF;
	nonce[12] = (haddr >> 8) & 0xFF;

	dump_bytes("CCM nonce", nonce, 16);
}

/**
 * Generates a 16-byte cryptographic-grade random number for the Host/Device
 * Nonce.
 */
void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce)
{
	uint8_t inonce[16];
	uint32_t temp[4];

	/* Fill in the Nonce */
	DWC_MEMSET(&inonce[0], 0, sizeof(inonce));
	inonce[9] = addr & 0xFF;
	inonce[10] = (addr >> 8) & 0xFF;
	inonce[11] = inonce[9];
	inonce[12] = inonce[10];

	/* Collect "randomness samples" */
	DWC_RANDOM_BYTES((uint8_t *)temp, 16);

	dwc_wusb_prf_128((uint8_t *)temp, nonce,
			 "Random Numbers", (uint8_t *)temp, sizeof(temp),
			 nonce);
}

/**
 * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the
 * WUSB spec.
 *
 * @param[in] ccm_nonce Pointer to CCM Nonce.
 * @param[in] mk Master Key to derive the session from
 * @param[in] hnonce Pointer to Host Nonce.
 * @param[in] dnonce Pointer to Device Nonce.
 * @param[out] kck Pointer to where the KCK output is to be written.
 * @param[out] ptk Pointer to where the PTK output is to be written.
 */
void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce,
		      uint8_t *dnonce, uint8_t *kck, uint8_t *ptk)
{
	uint8_t idata[32];
	uint8_t odata[32];

	dump_bytes("ck", mk, 16);
	dump_bytes("hnonce", hnonce, 16);
	dump_bytes("dnonce", dnonce, 16);

	/* The data is the HNonce and DNonce concatenated */
	DWC_MEMCPY(&idata[0], hnonce, 16);
	DWC_MEMCPY(&idata[16], dnonce, 16);

	dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata);

	/* Low 16 bytes of the result is the KCK, high 16 is the PTK */
	DWC_MEMCPY(kck, &odata[0], 16);
	DWC_MEMCPY(ptk, &odata[16], 16);

	dump_bytes("kck", kck, 16);
	dump_bytes("ptk", ptk, 16);
}

/**
 * Generates the Message Integrity Code over the Handshake data per the
 * WUSB spec.
 *
 * @param ccm_nonce Pointer to CCM Nonce.
 * @param kck   Pointer to Key Confirmation Key.
 * @param data  Pointer to Handshake data to be checked.
 * @param mic   Pointer to where the MIC output is to be written.
 */
void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck,
		      uint8_t *data, uint8_t *mic)
{

	dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC",
			data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic);
}

#endif	/* DWC_CRYPTOLIB */
