summaryrefslogtreecommitdiff
path: root/drivers/mxc/security/rng/fsl_shw_sym.c
blob: bcd8a0efcced1bd5e40b50ea044247342ad91e00 (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
309
310
311
312
313
314
315
316
317
/*
 * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */


/*!
 * @file fsl_shw_sym.c
 *
 * This file implements the Symmetric Cipher functions of the FSL SHW API. Its
 * features are limited to what can be done with the combination of SCC and
 * DryIce.
 */
#include "fsl_platform.h"
#include "shw_driver.h"

#if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE)

#include "../dryice.h"
#include <linux/mxc_scc_driver.h>
#ifdef DIAG_SECURITY_FUNC
#include "apihelp.h"
#endif

#include <diagnostic.h>

#define SYM_DECRYPT 0
#define SYM_ENCRYPT 1

extern fsl_shw_return_t shw_convert_pf_key(fsl_shw_pf_key_t shw_pf_key,
					   di_key_t * di_keyp);

/*! 'Initial' IV for presence of FSL_SYM_CTX_LOAD flag */
static uint8_t zeros[8] = {
	0, 0, 0, 0, 0, 0, 0, 0
};

/*!
 * Common function for encryption and decryption
 *
 * This is for a device with DryIce.
 *
 * A key must either refer to a 'pure' HW key, or, if PRG or PRG_IIM,
 * established, then that key will be programmed.  Then, the HW_key in the
 * object will be selected.  After this setup, the ciphering will be performed
 * by calling the SCC driver..
 *
 * The function 'releases' the reservations before it completes.
 */
fsl_shw_return_t do_symmetric(fsl_shw_uco_t * user_ctx,
			      fsl_shw_sko_t * key_info,
			      fsl_shw_scco_t * sym_ctx,
			      int encrypt,
			      uint32_t length,
			      const uint8_t * in, uint8_t * out)
{
	fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
	int key_selected = 0;
	uint8_t *iv = NULL;
	unsigned long count_out = length;
	di_key_t di_key = DI_KEY_PK;	/* default for user key */
	di_key_t di_key_orig;	/* currently selected key */
	di_key_t selected_key = -1;
	di_return_t di_code;
	scc_return_t scc_code;

	/* For now, only blocking mode calls are supported */
	if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
		ret = FSL_RETURN_BAD_FLAG_S;
		goto out;
	}

	/* No software keys allowed */
	if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
		ret = FSL_RETURN_BAD_FLAG_S;
	}

	/* The only algorithm the SCC supports */
	if (key_info->algorithm != FSL_KEY_ALG_TDES) {
		ret = FSL_RETURN_BAD_ALGORITHM_S;
		goto out;
	}

	/* Validate key length */
	if ((key_info->key_length != 16)
	    && (key_info->key_length != 21)
	    && (key_info->key_length != 24)) {
		ret = FSL_RETURN_BAD_KEY_LENGTH_S;
		goto out;
	}

	/* Validate data is multiple of DES/TDES block */
	if ((length & 7) != 0) {
		ret = FSL_RETURN_BAD_DATA_LENGTH_S;
		goto out;
	}

	/* Do some setup according to where the key lives */
	if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
		if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
		    && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) {
			ret = FSL_RETURN_ERROR_S;
		}
	} else if (key_info->flags & FSL_SKO_KEY_PRESENT) {
		ret = FSL_RETURN_BAD_FLAG_S;
	} else if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
		/*
		 * No key present or established, just refer to HW
		 * as programmed.
		 */
	} else {
		ret = FSL_RETURN_BAD_FLAG_S;
		goto out;
	}

	/* Now make proper selection */
	ret = shw_convert_pf_key(key_info->pf_key, &di_key);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	/* Determine the current DI key selection */
	di_code = dryice_check_key(&di_key_orig);
	if (di_code != DI_SUCCESS) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG_ARGS("Could not save current DI key state: %s\n",
					  di_error_string(di_code));
#endif
		ret = FSL_RETURN_ERROR_S;
		goto out;
	}

	/* If the requested DI key is already selected, don't re-select it. */
	if (di_key != di_key_orig) {
	   di_code = dryice_select_key(di_key, 0);
	   if (di_code != DI_SUCCESS) {
#ifdef DIAG_SECURITY_FUNC
		  LOG_DIAG_ARGS("Error from select_key: %s\n",
			      di_error_string(di_code));
#endif
		  ret = FSL_RETURN_INTERNAL_ERROR_S;
		  goto out;
	   }
	}
	key_selected = 1;

	/* Verify that we are using the key we want */
	di_code = dryice_check_key(&selected_key);
	if (di_code != DI_SUCCESS) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG_ARGS("Error from check_key: %s\n",
			      di_error_string(di_code));
#endif
		ret = FSL_RETURN_INTERNAL_ERROR_S;
		goto out;
	}

	if (di_key != selected_key) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG_ARGS("Wrong key in use: %d instead of %d\n\n",
			      selected_key, di_key);
#endif
		ret = FSL_RETURN_ERROR_S;
		goto out;
	}

	if (sym_ctx->mode == FSL_SYM_MODE_CBC) {
		if ((sym_ctx->flags & FSL_SYM_CTX_LOAD)
		    && !(sym_ctx->flags & FSL_SYM_CTX_INIT)) {
			iv = sym_ctx->context;
		} else if ((sym_ctx->flags & FSL_SYM_CTX_INIT)
			   && !(sym_ctx->flags & FSL_SYM_CTX_LOAD)) {
			iv = zeros;
		} else {
			/* Exactly one must be set! */
			ret = FSL_RETURN_BAD_FLAG_S;
			goto out;
		}
	}

	/* Now run the data through the SCC */
	scc_code = scc_crypt(length, in, iv,
			     encrypt ? SCC_ENCRYPT : SCC_DECRYPT,
			     (sym_ctx->mode == FSL_SYM_MODE_ECB)
			     ? SCC_ECB_MODE : SCC_CBC_MODE,
			     SCC_VERIFY_MODE_NONE, out, &count_out);
	if (scc_code != SCC_RET_OK) {
		ret = FSL_RETURN_INTERNAL_ERROR_S;
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG_ARGS("scc_code from scc_crypt() is %d\n", scc_code);
#endif
		goto out;
	}

	if ((sym_ctx->mode == FSL_SYM_MODE_CBC)
	    && (sym_ctx->flags & FSL_SYM_CTX_SAVE)) {
		/* Save the context for the caller */
		if (encrypt) {
			/* Last ciphertext block ... */
			memcpy(sym_ctx->context, out + length - 8, 8);
		} else {
			/* Last ciphertext block ... */
			memcpy(sym_ctx->context, in + length - 8, 8);
		}
	}

	ret = FSL_RETURN_OK_S;

      out:
	if (key_selected) {
		(void)dryice_release_key_selection();
	}

	return ret;
}

EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
/*!
 * Compute symmetric encryption
 *
 *
 * @param    user_ctx
 * @param    key_info
 * @param    sym_ctx
 * @param    length
 * @param    pt
 * @param    ct
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
					   fsl_shw_sko_t * key_info,
					   fsl_shw_scco_t * sym_ctx,
					   uint32_t length,
					   const uint8_t * pt, uint8_t * ct)
{
	fsl_shw_return_t ret;

	ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_ENCRYPT,
			   length, pt, ct);

	return ret;
}

EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
/*!
 * Compute symmetric decryption
 *
 *
 * @param    user_ctx
 * @param    key_info
 * @param    sym_ctx
 * @param    length
 * @param    pt
 * @param    ct
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
					   fsl_shw_sko_t * key_info,
					   fsl_shw_scco_t * sym_ctx,
					   uint32_t length,
					   const uint8_t * ct, uint8_t * pt)
{
	fsl_shw_return_t ret;

	ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT,
			   length, ct, pt);

	return ret;
}

#else				/* __KERNEL__ && DRYICE */

fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
					   fsl_shw_sko_t * key_info,
					   fsl_shw_scco_t * sym_ctx,
					   uint32_t length,
					   const uint8_t * pt, uint8_t * ct)
{
	/* Unused */
	(void)user_ctx;
	(void)key_info;
	(void)sym_ctx;
	(void)length;
	(void)pt;
	(void)ct;

	return FSL_RETURN_ERROR_S;
}

fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
					   fsl_shw_sko_t * key_info,
					   fsl_shw_scco_t * sym_ctx,
					   uint32_t length,
					   const uint8_t * ct, uint8_t * pt)
{
	/* Unused */
	(void)user_ctx;
	(void)key_info;
	(void)sym_ctx;
	(void)length;
	(void)ct;
	(void)pt;

	return FSL_RETURN_ERROR_S;
}

#endif				/* __KERNEL__ and DRYICE */