aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/susb/dwc_common_port/dwc_crypto.c
blob: 3b0353296148f6297b5bdc2f45e9fe92a17c680d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/* =========================================================================
 * $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 */