]>
Commit | Line | Data |
---|---|---|
266d0516 HX |
1 | /* |
2 | * Shared crypto simd helpers | |
3 | * | |
4 | * Copyright (c) 2012 Jussi Kivilinna <[email protected]> | |
5 | * Copyright (c) 2016 Herbert Xu <[email protected]> | |
6 | * | |
7 | * Based on aesni-intel_glue.c by: | |
8 | * Copyright (C) 2008, Intel Corp. | |
9 | * Author: Huang Ying <[email protected]> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or | |
14 | * (at your option) any later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
1af39daa | 22 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
266d0516 HX |
23 | * |
24 | */ | |
25 | ||
26 | #include <crypto/cryptd.h> | |
27 | #include <crypto/internal/simd.h> | |
28 | #include <crypto/internal/skcipher.h> | |
29 | #include <linux/kernel.h> | |
30 | #include <linux/module.h> | |
31 | #include <linux/preempt.h> | |
32 | #include <asm/simd.h> | |
33 | ||
34 | struct simd_skcipher_alg { | |
35 | const char *ialg_name; | |
36 | struct skcipher_alg alg; | |
37 | }; | |
38 | ||
39 | struct simd_skcipher_ctx { | |
40 | struct cryptd_skcipher *cryptd_tfm; | |
41 | }; | |
42 | ||
43 | static int simd_skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key, | |
44 | unsigned int key_len) | |
45 | { | |
46 | struct simd_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); | |
47 | struct crypto_skcipher *child = &ctx->cryptd_tfm->base; | |
48 | int err; | |
49 | ||
50 | crypto_skcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); | |
51 | crypto_skcipher_set_flags(child, crypto_skcipher_get_flags(tfm) & | |
52 | CRYPTO_TFM_REQ_MASK); | |
53 | err = crypto_skcipher_setkey(child, key, key_len); | |
54 | crypto_skcipher_set_flags(tfm, crypto_skcipher_get_flags(child) & | |
55 | CRYPTO_TFM_RES_MASK); | |
56 | return err; | |
57 | } | |
58 | ||
59 | static int simd_skcipher_encrypt(struct skcipher_request *req) | |
60 | { | |
61 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); | |
62 | struct simd_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); | |
63 | struct skcipher_request *subreq; | |
64 | struct crypto_skcipher *child; | |
65 | ||
66 | subreq = skcipher_request_ctx(req); | |
67 | *subreq = *req; | |
68 | ||
69 | if (!may_use_simd() || | |
70 | (in_atomic() && cryptd_skcipher_queued(ctx->cryptd_tfm))) | |
71 | child = &ctx->cryptd_tfm->base; | |
72 | else | |
73 | child = cryptd_skcipher_child(ctx->cryptd_tfm); | |
74 | ||
75 | skcipher_request_set_tfm(subreq, child); | |
76 | ||
77 | return crypto_skcipher_encrypt(subreq); | |
78 | } | |
79 | ||
80 | static int simd_skcipher_decrypt(struct skcipher_request *req) | |
81 | { | |
82 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); | |
83 | struct simd_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); | |
84 | struct skcipher_request *subreq; | |
85 | struct crypto_skcipher *child; | |
86 | ||
87 | subreq = skcipher_request_ctx(req); | |
88 | *subreq = *req; | |
89 | ||
90 | if (!may_use_simd() || | |
91 | (in_atomic() && cryptd_skcipher_queued(ctx->cryptd_tfm))) | |
92 | child = &ctx->cryptd_tfm->base; | |
93 | else | |
94 | child = cryptd_skcipher_child(ctx->cryptd_tfm); | |
95 | ||
96 | skcipher_request_set_tfm(subreq, child); | |
97 | ||
98 | return crypto_skcipher_decrypt(subreq); | |
99 | } | |
100 | ||
101 | static void simd_skcipher_exit(struct crypto_skcipher *tfm) | |
102 | { | |
103 | struct simd_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); | |
104 | ||
105 | cryptd_free_skcipher(ctx->cryptd_tfm); | |
106 | } | |
107 | ||
108 | static int simd_skcipher_init(struct crypto_skcipher *tfm) | |
109 | { | |
110 | struct simd_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); | |
111 | struct cryptd_skcipher *cryptd_tfm; | |
112 | struct simd_skcipher_alg *salg; | |
113 | struct skcipher_alg *alg; | |
114 | unsigned reqsize; | |
115 | ||
116 | alg = crypto_skcipher_alg(tfm); | |
117 | salg = container_of(alg, struct simd_skcipher_alg, alg); | |
118 | ||
119 | cryptd_tfm = cryptd_alloc_skcipher(salg->ialg_name, | |
120 | CRYPTO_ALG_INTERNAL, | |
121 | CRYPTO_ALG_INTERNAL); | |
122 | if (IS_ERR(cryptd_tfm)) | |
123 | return PTR_ERR(cryptd_tfm); | |
124 | ||
125 | ctx->cryptd_tfm = cryptd_tfm; | |
126 | ||
127 | reqsize = sizeof(struct skcipher_request); | |
128 | reqsize += crypto_skcipher_reqsize(&cryptd_tfm->base); | |
129 | ||
130 | crypto_skcipher_set_reqsize(tfm, reqsize); | |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | struct simd_skcipher_alg *simd_skcipher_create_compat(const char *algname, | |
136 | const char *drvname, | |
137 | const char *basename) | |
138 | { | |
139 | struct simd_skcipher_alg *salg; | |
140 | struct crypto_skcipher *tfm; | |
141 | struct skcipher_alg *ialg; | |
142 | struct skcipher_alg *alg; | |
143 | int err; | |
144 | ||
145 | tfm = crypto_alloc_skcipher(basename, CRYPTO_ALG_INTERNAL, | |
146 | CRYPTO_ALG_INTERNAL | CRYPTO_ALG_ASYNC); | |
147 | if (IS_ERR(tfm)) | |
148 | return ERR_CAST(tfm); | |
149 | ||
150 | ialg = crypto_skcipher_alg(tfm); | |
151 | ||
152 | salg = kzalloc(sizeof(*salg), GFP_KERNEL); | |
153 | if (!salg) { | |
154 | salg = ERR_PTR(-ENOMEM); | |
155 | goto out_put_tfm; | |
156 | } | |
157 | ||
158 | salg->ialg_name = basename; | |
159 | alg = &salg->alg; | |
160 | ||
161 | err = -ENAMETOOLONG; | |
162 | if (snprintf(alg->base.cra_name, CRYPTO_MAX_ALG_NAME, "%s", algname) >= | |
163 | CRYPTO_MAX_ALG_NAME) | |
164 | goto out_free_salg; | |
165 | ||
166 | if (snprintf(alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", | |
167 | drvname) >= CRYPTO_MAX_ALG_NAME) | |
168 | goto out_free_salg; | |
169 | ||
170 | alg->base.cra_flags = CRYPTO_ALG_ASYNC; | |
171 | alg->base.cra_priority = ialg->base.cra_priority; | |
172 | alg->base.cra_blocksize = ialg->base.cra_blocksize; | |
173 | alg->base.cra_alignmask = ialg->base.cra_alignmask; | |
174 | alg->base.cra_module = ialg->base.cra_module; | |
175 | alg->base.cra_ctxsize = sizeof(struct simd_skcipher_ctx); | |
176 | ||
177 | alg->ivsize = ialg->ivsize; | |
178 | alg->chunksize = ialg->chunksize; | |
179 | alg->min_keysize = ialg->min_keysize; | |
180 | alg->max_keysize = ialg->max_keysize; | |
181 | ||
182 | alg->init = simd_skcipher_init; | |
183 | alg->exit = simd_skcipher_exit; | |
184 | ||
185 | alg->setkey = simd_skcipher_setkey; | |
186 | alg->encrypt = simd_skcipher_encrypt; | |
187 | alg->decrypt = simd_skcipher_decrypt; | |
188 | ||
189 | err = crypto_register_skcipher(alg); | |
190 | if (err) | |
191 | goto out_free_salg; | |
192 | ||
193 | out_put_tfm: | |
194 | crypto_free_skcipher(tfm); | |
195 | return salg; | |
196 | ||
197 | out_free_salg: | |
198 | kfree(salg); | |
199 | salg = ERR_PTR(err); | |
200 | goto out_put_tfm; | |
201 | } | |
202 | EXPORT_SYMBOL_GPL(simd_skcipher_create_compat); | |
203 | ||
204 | struct simd_skcipher_alg *simd_skcipher_create(const char *algname, | |
205 | const char *basename) | |
206 | { | |
207 | char drvname[CRYPTO_MAX_ALG_NAME]; | |
208 | ||
209 | if (snprintf(drvname, CRYPTO_MAX_ALG_NAME, "simd-%s", basename) >= | |
210 | CRYPTO_MAX_ALG_NAME) | |
211 | return ERR_PTR(-ENAMETOOLONG); | |
212 | ||
213 | return simd_skcipher_create_compat(algname, drvname, basename); | |
214 | } | |
215 | EXPORT_SYMBOL_GPL(simd_skcipher_create); | |
216 | ||
217 | void simd_skcipher_free(struct simd_skcipher_alg *salg) | |
218 | { | |
219 | crypto_unregister_skcipher(&salg->alg); | |
220 | kfree(salg); | |
221 | } | |
222 | EXPORT_SYMBOL_GPL(simd_skcipher_free); | |
223 | ||
d14f0a1f EB |
224 | int simd_register_skciphers_compat(struct skcipher_alg *algs, int count, |
225 | struct simd_skcipher_alg **simd_algs) | |
226 | { | |
227 | int err; | |
228 | int i; | |
229 | const char *algname; | |
230 | const char *drvname; | |
231 | const char *basename; | |
232 | struct simd_skcipher_alg *simd; | |
233 | ||
234 | err = crypto_register_skciphers(algs, count); | |
235 | if (err) | |
236 | return err; | |
237 | ||
238 | for (i = 0; i < count; i++) { | |
239 | WARN_ON(strncmp(algs[i].base.cra_name, "__", 2)); | |
240 | WARN_ON(strncmp(algs[i].base.cra_driver_name, "__", 2)); | |
241 | algname = algs[i].base.cra_name + 2; | |
242 | drvname = algs[i].base.cra_driver_name + 2; | |
243 | basename = algs[i].base.cra_driver_name; | |
244 | simd = simd_skcipher_create_compat(algname, drvname, basename); | |
245 | err = PTR_ERR(simd); | |
246 | if (IS_ERR(simd)) | |
247 | goto err_unregister; | |
248 | simd_algs[i] = simd; | |
249 | } | |
250 | return 0; | |
251 | ||
252 | err_unregister: | |
253 | simd_unregister_skciphers(algs, count, simd_algs); | |
254 | return err; | |
255 | } | |
256 | EXPORT_SYMBOL_GPL(simd_register_skciphers_compat); | |
257 | ||
258 | void simd_unregister_skciphers(struct skcipher_alg *algs, int count, | |
259 | struct simd_skcipher_alg **simd_algs) | |
260 | { | |
261 | int i; | |
262 | ||
263 | crypto_unregister_skciphers(algs, count); | |
264 | ||
265 | for (i = 0; i < count; i++) { | |
266 | if (simd_algs[i]) { | |
267 | simd_skcipher_free(simd_algs[i]); | |
268 | simd_algs[i] = NULL; | |
269 | } | |
270 | } | |
271 | } | |
272 | EXPORT_SYMBOL_GPL(simd_unregister_skciphers); | |
273 | ||
266d0516 | 274 | MODULE_LICENSE("GPL"); |