diff options
author | Tom Lendacky <thomas.lendacky@amd.com> | 2013-11-12 12:46:34 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2013-12-05 08:28:39 -0500 |
commit | 7c1853711fc11df00f20bb6989358f1d3ce0fc04 (patch) | |
tree | 2133e7f2e14eaf7a24f9bc347fd355360e2a32c5 /drivers/crypto/ccp | |
parent | 2b789435d7f36ed918d92db647f3a2f3fec9bb1f (diff) |
crypto: ccp - CCP AES CMAC mode crypto API support
These routines provide crypto API support for the CMAC mode of AES
on the AMD CCP.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/crypto/ccp')
-rw-r--r-- | drivers/crypto/ccp/ccp-crypto-aes-cmac.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c new file mode 100644 index 000000000000..5b9cd982339d --- /dev/null +++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c | |||
@@ -0,0 +1,355 @@ | |||
1 | /* | ||
2 | * AMD Cryptographic Coprocessor (CCP) AES CMAC crypto API support | ||
3 | * | ||
4 | * Copyright (C) 2013 Advanced Micro Devices, Inc. | ||
5 | * | ||
6 | * Author: Tom Lendacky <thomas.lendacky@amd.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/scatterlist.h> | ||
17 | #include <linux/crypto.h> | ||
18 | #include <crypto/algapi.h> | ||
19 | #include <crypto/aes.h> | ||
20 | #include <crypto/hash.h> | ||
21 | #include <crypto/internal/hash.h> | ||
22 | #include <crypto/scatterwalk.h> | ||
23 | |||
24 | #include "ccp-crypto.h" | ||
25 | |||
26 | |||
27 | static int ccp_aes_cmac_complete(struct crypto_async_request *async_req, | ||
28 | int ret) | ||
29 | { | ||
30 | struct ahash_request *req = ahash_request_cast(async_req); | ||
31 | struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); | ||
32 | struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req); | ||
33 | unsigned int digest_size = crypto_ahash_digestsize(tfm); | ||
34 | |||
35 | if (ret) | ||
36 | goto e_free; | ||
37 | |||
38 | if (rctx->hash_rem) { | ||
39 | /* Save remaining data to buffer */ | ||
40 | scatterwalk_map_and_copy(rctx->buf, rctx->cmd.u.aes.src, | ||
41 | rctx->hash_cnt, rctx->hash_rem, 0); | ||
42 | rctx->buf_count = rctx->hash_rem; | ||
43 | } else | ||
44 | rctx->buf_count = 0; | ||
45 | |||
46 | memcpy(req->result, rctx->iv, digest_size); | ||
47 | |||
48 | e_free: | ||
49 | sg_free_table(&rctx->data_sg); | ||
50 | |||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | static int ccp_do_cmac_update(struct ahash_request *req, unsigned int nbytes, | ||
55 | unsigned int final) | ||
56 | { | ||
57 | struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); | ||
58 | struct ccp_ctx *ctx = crypto_ahash_ctx(tfm); | ||
59 | struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req); | ||
60 | struct scatterlist *sg, *cmac_key_sg = NULL; | ||
61 | unsigned int block_size = | ||
62 | crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); | ||
63 | unsigned int len, need_pad, sg_count; | ||
64 | int ret; | ||
65 | |||
66 | if (!ctx->u.aes.key_len) { | ||
67 | pr_err("AES key not set\n"); | ||
68 | return -EINVAL; | ||
69 | } | ||
70 | |||
71 | if (nbytes) | ||
72 | rctx->null_msg = 0; | ||
73 | |||
74 | if (!final && ((nbytes + rctx->buf_count) <= block_size)) { | ||
75 | scatterwalk_map_and_copy(rctx->buf + rctx->buf_count, req->src, | ||
76 | 0, nbytes, 0); | ||
77 | rctx->buf_count += nbytes; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | len = rctx->buf_count + nbytes; | ||
83 | |||
84 | rctx->final = final; | ||
85 | rctx->hash_cnt = final ? len : len & ~(block_size - 1); | ||
86 | rctx->hash_rem = final ? 0 : len & (block_size - 1); | ||
87 | if (!final && (rctx->hash_cnt == len)) { | ||
88 | /* CCP can't do zero length final, so keep some data around */ | ||
89 | rctx->hash_cnt -= block_size; | ||
90 | rctx->hash_rem = block_size; | ||
91 | } | ||
92 | |||
93 | if (final && (rctx->null_msg || (len & (block_size - 1)))) | ||
94 | need_pad = 1; | ||
95 | else | ||
96 | need_pad = 0; | ||
97 | |||
98 | sg_init_one(&rctx->iv_sg, rctx->iv, sizeof(rctx->iv)); | ||
99 | |||
100 | /* Build the data scatterlist table - allocate enough entries for all | ||
101 | * possible data pieces (buffer, input data, padding) | ||
102 | */ | ||
103 | sg_count = (nbytes) ? sg_nents(req->src) + 2 : 2; | ||
104 | ret = sg_alloc_table(&rctx->data_sg, sg_count, GFP_KERNEL); | ||
105 | if (ret) | ||
106 | return ret; | ||
107 | |||
108 | sg = NULL; | ||
109 | if (rctx->buf_count) { | ||
110 | sg_init_one(&rctx->buf_sg, rctx->buf, rctx->buf_count); | ||
111 | sg = ccp_crypto_sg_table_add(&rctx->data_sg, &rctx->buf_sg); | ||
112 | } | ||
113 | |||
114 | if (nbytes) | ||
115 | sg = ccp_crypto_sg_table_add(&rctx->data_sg, req->src); | ||
116 | |||
117 | if (need_pad) { | ||
118 | int pad_length = block_size - (len & (block_size - 1)); | ||
119 | |||
120 | rctx->hash_cnt += pad_length; | ||
121 | |||
122 | memset(rctx->pad, 0, sizeof(rctx->pad)); | ||
123 | rctx->pad[0] = 0x80; | ||
124 | sg_init_one(&rctx->pad_sg, rctx->pad, pad_length); | ||
125 | sg = ccp_crypto_sg_table_add(&rctx->data_sg, &rctx->pad_sg); | ||
126 | } | ||
127 | if (sg) | ||
128 | sg_mark_end(sg); | ||
129 | |||
130 | /* Initialize the K1/K2 scatterlist */ | ||
131 | if (final) | ||
132 | cmac_key_sg = (need_pad) ? &ctx->u.aes.k2_sg | ||
133 | : &ctx->u.aes.k1_sg; | ||
134 | |||
135 | memset(&rctx->cmd, 0, sizeof(rctx->cmd)); | ||
136 | INIT_LIST_HEAD(&rctx->cmd.entry); | ||
137 | rctx->cmd.engine = CCP_ENGINE_AES; | ||
138 | rctx->cmd.u.aes.type = ctx->u.aes.type; | ||
139 | rctx->cmd.u.aes.mode = ctx->u.aes.mode; | ||
140 | rctx->cmd.u.aes.action = CCP_AES_ACTION_ENCRYPT; | ||
141 | rctx->cmd.u.aes.key = &ctx->u.aes.key_sg; | ||
142 | rctx->cmd.u.aes.key_len = ctx->u.aes.key_len; | ||
143 | rctx->cmd.u.aes.iv = &rctx->iv_sg; | ||
144 | rctx->cmd.u.aes.iv_len = AES_BLOCK_SIZE; | ||
145 | rctx->cmd.u.aes.src = (sg) ? rctx->data_sg.sgl : NULL; | ||
146 | rctx->cmd.u.aes.src_len = rctx->hash_cnt; | ||
147 | rctx->cmd.u.aes.dst = NULL; | ||
148 | rctx->cmd.u.aes.cmac_key = cmac_key_sg; | ||
149 | rctx->cmd.u.aes.cmac_key_len = ctx->u.aes.kn_len; | ||
150 | rctx->cmd.u.aes.cmac_final = final; | ||
151 | |||
152 | ret = ccp_crypto_enqueue_request(&req->base, &rctx->cmd); | ||
153 | |||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | static int ccp_aes_cmac_init(struct ahash_request *req) | ||
158 | { | ||
159 | struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req); | ||
160 | |||
161 | memset(rctx, 0, sizeof(*rctx)); | ||
162 | |||
163 | rctx->null_msg = 1; | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int ccp_aes_cmac_update(struct ahash_request *req) | ||
169 | { | ||
170 | return ccp_do_cmac_update(req, req->nbytes, 0); | ||
171 | } | ||
172 | |||
173 | static int ccp_aes_cmac_final(struct ahash_request *req) | ||
174 | { | ||
175 | return ccp_do_cmac_update(req, 0, 1); | ||
176 | } | ||
177 | |||
178 | static int ccp_aes_cmac_finup(struct ahash_request *req) | ||
179 | { | ||
180 | return ccp_do_cmac_update(req, req->nbytes, 1); | ||
181 | } | ||
182 | |||
183 | static int ccp_aes_cmac_digest(struct ahash_request *req) | ||
184 | { | ||
185 | int ret; | ||
186 | |||
187 | ret = ccp_aes_cmac_init(req); | ||
188 | if (ret) | ||
189 | return ret; | ||
190 | |||
191 | return ccp_do_cmac_update(req, req->nbytes, 1); | ||
192 | } | ||
193 | |||
194 | static int ccp_aes_cmac_setkey(struct crypto_ahash *tfm, const u8 *key, | ||
195 | unsigned int key_len) | ||
196 | { | ||
197 | struct ccp_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); | ||
198 | struct ccp_crypto_ahash_alg *alg = | ||
199 | ccp_crypto_ahash_alg(crypto_ahash_tfm(tfm)); | ||
200 | u64 k0_hi, k0_lo, k1_hi, k1_lo, k2_hi, k2_lo; | ||
201 | u64 rb_hi = 0x00, rb_lo = 0x87; | ||
202 | __be64 *gk; | ||
203 | int ret; | ||
204 | |||
205 | switch (key_len) { | ||
206 | case AES_KEYSIZE_128: | ||
207 | ctx->u.aes.type = CCP_AES_TYPE_128; | ||
208 | break; | ||
209 | case AES_KEYSIZE_192: | ||
210 | ctx->u.aes.type = CCP_AES_TYPE_192; | ||
211 | break; | ||
212 | case AES_KEYSIZE_256: | ||
213 | ctx->u.aes.type = CCP_AES_TYPE_256; | ||
214 | break; | ||
215 | default: | ||
216 | crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); | ||
217 | return -EINVAL; | ||
218 | } | ||
219 | ctx->u.aes.mode = alg->mode; | ||
220 | |||
221 | /* Set to zero until complete */ | ||
222 | ctx->u.aes.key_len = 0; | ||
223 | |||
224 | /* Set the key for the AES cipher used to generate the keys */ | ||
225 | ret = crypto_cipher_setkey(ctx->u.aes.tfm_cipher, key, key_len); | ||
226 | if (ret) | ||
227 | return ret; | ||
228 | |||
229 | /* Encrypt a block of zeroes - use key area in context */ | ||
230 | memset(ctx->u.aes.key, 0, sizeof(ctx->u.aes.key)); | ||
231 | crypto_cipher_encrypt_one(ctx->u.aes.tfm_cipher, ctx->u.aes.key, | ||
232 | ctx->u.aes.key); | ||
233 | |||
234 | /* Generate K1 and K2 */ | ||
235 | k0_hi = be64_to_cpu(*((__be64 *)ctx->u.aes.key)); | ||
236 | k0_lo = be64_to_cpu(*((__be64 *)ctx->u.aes.key + 1)); | ||
237 | |||
238 | k1_hi = (k0_hi << 1) | (k0_lo >> 63); | ||
239 | k1_lo = k0_lo << 1; | ||
240 | if (ctx->u.aes.key[0] & 0x80) { | ||
241 | k1_hi ^= rb_hi; | ||
242 | k1_lo ^= rb_lo; | ||
243 | } | ||
244 | gk = (__be64 *)ctx->u.aes.k1; | ||
245 | *gk = cpu_to_be64(k1_hi); | ||
246 | gk++; | ||
247 | *gk = cpu_to_be64(k1_lo); | ||
248 | |||
249 | k2_hi = (k1_hi << 1) | (k1_lo >> 63); | ||
250 | k2_lo = k1_lo << 1; | ||
251 | if (ctx->u.aes.k1[0] & 0x80) { | ||
252 | k2_hi ^= rb_hi; | ||
253 | k2_lo ^= rb_lo; | ||
254 | } | ||
255 | gk = (__be64 *)ctx->u.aes.k2; | ||
256 | *gk = cpu_to_be64(k2_hi); | ||
257 | gk++; | ||
258 | *gk = cpu_to_be64(k2_lo); | ||
259 | |||
260 | ctx->u.aes.kn_len = sizeof(ctx->u.aes.k1); | ||
261 | sg_init_one(&ctx->u.aes.k1_sg, ctx->u.aes.k1, sizeof(ctx->u.aes.k1)); | ||
262 | sg_init_one(&ctx->u.aes.k2_sg, ctx->u.aes.k2, sizeof(ctx->u.aes.k2)); | ||
263 | |||
264 | /* Save the supplied key */ | ||
265 | memset(ctx->u.aes.key, 0, sizeof(ctx->u.aes.key)); | ||
266 | memcpy(ctx->u.aes.key, key, key_len); | ||
267 | ctx->u.aes.key_len = key_len; | ||
268 | sg_init_one(&ctx->u.aes.key_sg, ctx->u.aes.key, key_len); | ||
269 | |||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | static int ccp_aes_cmac_cra_init(struct crypto_tfm *tfm) | ||
274 | { | ||
275 | struct ccp_ctx *ctx = crypto_tfm_ctx(tfm); | ||
276 | struct crypto_ahash *ahash = __crypto_ahash_cast(tfm); | ||
277 | struct crypto_cipher *cipher_tfm; | ||
278 | |||
279 | ctx->complete = ccp_aes_cmac_complete; | ||
280 | ctx->u.aes.key_len = 0; | ||
281 | |||
282 | crypto_ahash_set_reqsize(ahash, sizeof(struct ccp_aes_cmac_req_ctx)); | ||
283 | |||
284 | cipher_tfm = crypto_alloc_cipher("aes", 0, | ||
285 | CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); | ||
286 | if (IS_ERR(cipher_tfm)) { | ||
287 | pr_warn("could not load aes cipher driver\n"); | ||
288 | return PTR_ERR(cipher_tfm); | ||
289 | } | ||
290 | ctx->u.aes.tfm_cipher = cipher_tfm; | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static void ccp_aes_cmac_cra_exit(struct crypto_tfm *tfm) | ||
296 | { | ||
297 | struct ccp_ctx *ctx = crypto_tfm_ctx(tfm); | ||
298 | |||
299 | if (ctx->u.aes.tfm_cipher) | ||
300 | crypto_free_cipher(ctx->u.aes.tfm_cipher); | ||
301 | ctx->u.aes.tfm_cipher = NULL; | ||
302 | } | ||
303 | |||
304 | int ccp_register_aes_cmac_algs(struct list_head *head) | ||
305 | { | ||
306 | struct ccp_crypto_ahash_alg *ccp_alg; | ||
307 | struct ahash_alg *alg; | ||
308 | struct hash_alg_common *halg; | ||
309 | struct crypto_alg *base; | ||
310 | int ret; | ||
311 | |||
312 | ccp_alg = kzalloc(sizeof(*ccp_alg), GFP_KERNEL); | ||
313 | if (!ccp_alg) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | INIT_LIST_HEAD(&ccp_alg->entry); | ||
317 | ccp_alg->mode = CCP_AES_MODE_CMAC; | ||
318 | |||
319 | alg = &ccp_alg->alg; | ||
320 | alg->init = ccp_aes_cmac_init; | ||
321 | alg->update = ccp_aes_cmac_update; | ||
322 | alg->final = ccp_aes_cmac_final; | ||
323 | alg->finup = ccp_aes_cmac_finup; | ||
324 | alg->digest = ccp_aes_cmac_digest; | ||
325 | alg->setkey = ccp_aes_cmac_setkey; | ||
326 | |||
327 | halg = &alg->halg; | ||
328 | halg->digestsize = AES_BLOCK_SIZE; | ||
329 | |||
330 | base = &halg->base; | ||
331 | snprintf(base->cra_name, CRYPTO_MAX_ALG_NAME, "cmac(aes)"); | ||
332 | snprintf(base->cra_driver_name, CRYPTO_MAX_ALG_NAME, "cmac-aes-ccp"); | ||
333 | base->cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC | | ||
334 | CRYPTO_ALG_KERN_DRIVER_ONLY | | ||
335 | CRYPTO_ALG_NEED_FALLBACK; | ||
336 | base->cra_blocksize = AES_BLOCK_SIZE; | ||
337 | base->cra_ctxsize = sizeof(struct ccp_ctx); | ||
338 | base->cra_priority = CCP_CRA_PRIORITY; | ||
339 | base->cra_type = &crypto_ahash_type; | ||
340 | base->cra_init = ccp_aes_cmac_cra_init; | ||
341 | base->cra_exit = ccp_aes_cmac_cra_exit; | ||
342 | base->cra_module = THIS_MODULE; | ||
343 | |||
344 | ret = crypto_register_ahash(alg); | ||
345 | if (ret) { | ||
346 | pr_err("%s ahash algorithm registration error (%d)\n", | ||
347 | base->cra_name, ret); | ||
348 | kfree(ccp_alg); | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | list_add(&ccp_alg->entry, head); | ||
353 | |||
354 | return 0; | ||
355 | } | ||