diff options
-rw-r--r-- | crypto/authenc.c | 136 |
1 files changed, 114 insertions, 22 deletions
diff --git a/crypto/authenc.c b/crypto/authenc.c index 2d609b72f5be..ed8ac5a6fa5f 100644 --- a/crypto/authenc.c +++ b/crypto/authenc.c | |||
@@ -10,6 +10,7 @@ | |||
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <crypto/aead.h> | ||
13 | #include <crypto/internal/skcipher.h> | 14 | #include <crypto/internal/skcipher.h> |
14 | #include <crypto/authenc.h> | 15 | #include <crypto/authenc.h> |
15 | #include <crypto/scatterwalk.h> | 16 | #include <crypto/scatterwalk.h> |
@@ -87,6 +88,20 @@ badkey: | |||
87 | goto out; | 88 | goto out; |
88 | } | 89 | } |
89 | 90 | ||
91 | static void authenc_chain(struct scatterlist *head, struct scatterlist *sg, | ||
92 | int chain) | ||
93 | { | ||
94 | if (chain) { | ||
95 | head->length += sg->length; | ||
96 | sg = scatterwalk_sg_next(sg); | ||
97 | } | ||
98 | |||
99 | if (sg) | ||
100 | scatterwalk_sg_chain(head, 2, sg); | ||
101 | else | ||
102 | sg_mark_end(head); | ||
103 | } | ||
104 | |||
90 | static u8 *crypto_authenc_hash(struct aead_request *req, unsigned int flags, | 105 | static u8 *crypto_authenc_hash(struct aead_request *req, unsigned int flags, |
91 | struct scatterlist *cipher, | 106 | struct scatterlist *cipher, |
92 | unsigned int cryptlen) | 107 | unsigned int cryptlen) |
@@ -127,18 +142,31 @@ auth_unlock: | |||
127 | return hash; | 142 | return hash; |
128 | } | 143 | } |
129 | 144 | ||
130 | static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags) | 145 | static int crypto_authenc_genicv(struct aead_request *req, u8 *iv, |
146 | unsigned int flags) | ||
131 | { | 147 | { |
132 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); | 148 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); |
133 | struct scatterlist *dst = req->dst; | 149 | struct scatterlist *dst = req->dst; |
134 | unsigned int cryptlen = req->cryptlen; | 150 | struct scatterlist cipher[2]; |
151 | struct page *dstp; | ||
152 | unsigned int ivsize = crypto_aead_ivsize(authenc); | ||
153 | unsigned int cryptlen; | ||
154 | u8 *vdst; | ||
135 | u8 *hash; | 155 | u8 *hash; |
136 | 156 | ||
137 | hash = crypto_authenc_hash(req, flags, dst, cryptlen); | 157 | dstp = sg_page(dst); |
158 | vdst = PageHighMem(dstp) ? NULL : page_address(dstp) + dst->offset; | ||
159 | |||
160 | sg_init_table(cipher, 2); | ||
161 | sg_set_buf(cipher, iv, ivsize); | ||
162 | authenc_chain(cipher, dst, vdst == iv + ivsize); | ||
163 | |||
164 | cryptlen = req->cryptlen + ivsize; | ||
165 | hash = crypto_authenc_hash(req, flags, cipher, cryptlen); | ||
138 | if (IS_ERR(hash)) | 166 | if (IS_ERR(hash)) |
139 | return PTR_ERR(hash); | 167 | return PTR_ERR(hash); |
140 | 168 | ||
141 | scatterwalk_map_and_copy(hash, dst, cryptlen, | 169 | scatterwalk_map_and_copy(hash, cipher, cryptlen, |
142 | crypto_aead_authsize(authenc), 1); | 170 | crypto_aead_authsize(authenc), 1); |
143 | return 0; | 171 | return 0; |
144 | } | 172 | } |
@@ -146,8 +174,16 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags) | |||
146 | static void crypto_authenc_encrypt_done(struct crypto_async_request *req, | 174 | static void crypto_authenc_encrypt_done(struct crypto_async_request *req, |
147 | int err) | 175 | int err) |
148 | { | 176 | { |
149 | if (!err) | 177 | if (!err) { |
150 | err = crypto_authenc_genicv(req->data, 0); | 178 | struct aead_request *areq = req->data; |
179 | struct crypto_aead *authenc = crypto_aead_reqtfm(areq); | ||
180 | struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc); | ||
181 | struct ablkcipher_request *abreq = aead_request_ctx(areq); | ||
182 | u8 *iv = (u8 *)(abreq + 1) + | ||
183 | crypto_ablkcipher_reqsize(ctx->enc); | ||
184 | |||
185 | err = crypto_authenc_genicv(areq, iv, 0); | ||
186 | } | ||
151 | 187 | ||
152 | aead_request_complete(req->data, err); | 188 | aead_request_complete(req->data, err); |
153 | } | 189 | } |
@@ -157,45 +193,99 @@ static int crypto_authenc_encrypt(struct aead_request *req) | |||
157 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); | 193 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); |
158 | struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc); | 194 | struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc); |
159 | struct ablkcipher_request *abreq = aead_request_ctx(req); | 195 | struct ablkcipher_request *abreq = aead_request_ctx(req); |
196 | struct crypto_ablkcipher *enc = ctx->enc; | ||
197 | struct scatterlist *dst = req->dst; | ||
198 | unsigned int cryptlen = req->cryptlen; | ||
199 | u8 *iv = (u8 *)(abreq + 1) + crypto_ablkcipher_reqsize(enc); | ||
160 | int err; | 200 | int err; |
161 | 201 | ||
162 | ablkcipher_request_set_tfm(abreq, ctx->enc); | 202 | ablkcipher_request_set_tfm(abreq, enc); |
163 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | 203 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), |
164 | crypto_authenc_encrypt_done, req); | 204 | crypto_authenc_encrypt_done, req); |
165 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, req->cryptlen, | 205 | ablkcipher_request_set_crypt(abreq, req->src, dst, cryptlen, req->iv); |
166 | req->iv); | 206 | |
207 | memcpy(iv, req->iv, crypto_aead_ivsize(authenc)); | ||
167 | 208 | ||
168 | err = crypto_ablkcipher_encrypt(abreq); | 209 | err = crypto_ablkcipher_encrypt(abreq); |
169 | if (err) | 210 | if (err) |
170 | return err; | 211 | return err; |
171 | 212 | ||
172 | return crypto_authenc_genicv(req, CRYPTO_TFM_REQ_MAY_SLEEP); | 213 | return crypto_authenc_genicv(req, iv, CRYPTO_TFM_REQ_MAY_SLEEP); |
214 | } | ||
215 | |||
216 | static void crypto_authenc_givencrypt_done(struct crypto_async_request *req, | ||
217 | int err) | ||
218 | { | ||
219 | if (!err) { | ||
220 | struct aead_givcrypt_request *greq = req->data; | ||
221 | |||
222 | err = crypto_authenc_genicv(&greq->areq, greq->giv, 0); | ||
223 | } | ||
224 | |||
225 | aead_request_complete(req->data, err); | ||
226 | } | ||
227 | |||
228 | static int crypto_authenc_givencrypt(struct aead_givcrypt_request *req) | ||
229 | { | ||
230 | struct crypto_aead *authenc = aead_givcrypt_reqtfm(req); | ||
231 | struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc); | ||
232 | struct aead_request *areq = &req->areq; | ||
233 | struct skcipher_givcrypt_request *greq = aead_request_ctx(areq); | ||
234 | u8 *iv = req->giv; | ||
235 | int err; | ||
236 | |||
237 | skcipher_givcrypt_set_tfm(greq, ctx->enc); | ||
238 | skcipher_givcrypt_set_callback(greq, aead_request_flags(areq), | ||
239 | crypto_authenc_givencrypt_done, areq); | ||
240 | skcipher_givcrypt_set_crypt(greq, areq->src, areq->dst, areq->cryptlen, | ||
241 | areq->iv); | ||
242 | skcipher_givcrypt_set_giv(greq, iv, req->seq); | ||
243 | |||
244 | err = crypto_skcipher_givencrypt(greq); | ||
245 | if (err) | ||
246 | return err; | ||
247 | |||
248 | return crypto_authenc_genicv(areq, iv, CRYPTO_TFM_REQ_MAY_SLEEP); | ||
173 | } | 249 | } |
174 | 250 | ||
175 | static int crypto_authenc_verify(struct aead_request *req, | 251 | static int crypto_authenc_verify(struct aead_request *req, |
252 | struct scatterlist *cipher, | ||
176 | unsigned int cryptlen) | 253 | unsigned int cryptlen) |
177 | { | 254 | { |
178 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); | 255 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); |
179 | u8 *ohash; | 256 | u8 *ohash; |
180 | u8 *ihash; | 257 | u8 *ihash; |
181 | struct scatterlist *src = req->src; | ||
182 | unsigned int authsize; | 258 | unsigned int authsize; |
183 | 259 | ||
184 | ohash = crypto_authenc_hash(req, CRYPTO_TFM_REQ_MAY_SLEEP, src, | 260 | ohash = crypto_authenc_hash(req, CRYPTO_TFM_REQ_MAY_SLEEP, cipher, |
185 | cryptlen); | 261 | cryptlen); |
186 | if (IS_ERR(ohash)) | 262 | if (IS_ERR(ohash)) |
187 | return PTR_ERR(ohash); | 263 | return PTR_ERR(ohash); |
188 | 264 | ||
189 | authsize = crypto_aead_authsize(authenc); | 265 | authsize = crypto_aead_authsize(authenc); |
190 | ihash = ohash + authsize; | 266 | ihash = ohash + authsize; |
191 | scatterwalk_map_and_copy(ihash, src, cryptlen, authsize, 0); | 267 | scatterwalk_map_and_copy(ihash, cipher, cryptlen, authsize, 0); |
192 | return memcmp(ihash, ohash, authsize) ? -EBADMSG: 0; | 268 | return memcmp(ihash, ohash, authsize) ? -EBADMSG: 0; |
193 | } | 269 | } |
194 | 270 | ||
195 | static void crypto_authenc_decrypt_done(struct crypto_async_request *req, | 271 | static int crypto_authenc_iverify(struct aead_request *req, u8 *iv, |
196 | int err) | 272 | unsigned int cryptlen) |
197 | { | 273 | { |
198 | aead_request_complete(req->data, err); | 274 | struct crypto_aead *authenc = crypto_aead_reqtfm(req); |
275 | struct scatterlist *src = req->src; | ||
276 | struct scatterlist cipher[2]; | ||
277 | struct page *srcp; | ||
278 | unsigned int ivsize = crypto_aead_ivsize(authenc); | ||
279 | u8 *vsrc; | ||
280 | |||
281 | srcp = sg_page(src); | ||
282 | vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset; | ||
283 | |||
284 | sg_init_table(cipher, 2); | ||
285 | sg_set_buf(cipher, iv, ivsize); | ||
286 | authenc_chain(cipher, src, vsrc == iv + ivsize); | ||
287 | |||
288 | return crypto_authenc_verify(req, cipher, cryptlen + ivsize); | ||
199 | } | 289 | } |
200 | 290 | ||
201 | static int crypto_authenc_decrypt(struct aead_request *req) | 291 | static int crypto_authenc_decrypt(struct aead_request *req) |
@@ -205,21 +295,21 @@ static int crypto_authenc_decrypt(struct aead_request *req) | |||
205 | struct ablkcipher_request *abreq = aead_request_ctx(req); | 295 | struct ablkcipher_request *abreq = aead_request_ctx(req); |
206 | unsigned int cryptlen = req->cryptlen; | 296 | unsigned int cryptlen = req->cryptlen; |
207 | unsigned int authsize = crypto_aead_authsize(authenc); | 297 | unsigned int authsize = crypto_aead_authsize(authenc); |
298 | u8 *iv = req->iv; | ||
208 | int err; | 299 | int err; |
209 | 300 | ||
210 | if (cryptlen < authsize) | 301 | if (cryptlen < authsize) |
211 | return -EINVAL; | 302 | return -EINVAL; |
212 | cryptlen -= authsize; | 303 | cryptlen -= authsize; |
213 | 304 | ||
214 | err = crypto_authenc_verify(req, cryptlen); | 305 | err = crypto_authenc_iverify(req, iv, cryptlen); |
215 | if (err) | 306 | if (err) |
216 | return err; | 307 | return err; |
217 | 308 | ||
218 | ablkcipher_request_set_tfm(abreq, ctx->enc); | 309 | ablkcipher_request_set_tfm(abreq, ctx->enc); |
219 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), | 310 | ablkcipher_request_set_callback(abreq, aead_request_flags(req), |
220 | crypto_authenc_decrypt_done, req); | 311 | req->base.complete, req->base.data); |
221 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, | 312 | ablkcipher_request_set_crypt(abreq, req->src, req->dst, cryptlen, iv); |
222 | req->iv); | ||
223 | 313 | ||
224 | return crypto_ablkcipher_decrypt(abreq); | 314 | return crypto_ablkcipher_decrypt(abreq); |
225 | } | 315 | } |
@@ -248,8 +338,9 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm) | |||
248 | (crypto_hash_alignmask(auth) & | 338 | (crypto_hash_alignmask(auth) & |
249 | ~(crypto_tfm_ctx_alignment() - 1)) + | 339 | ~(crypto_tfm_ctx_alignment() - 1)) + |
250 | crypto_hash_digestsize(auth) * 2, | 340 | crypto_hash_digestsize(auth) * 2, |
251 | sizeof(struct ablkcipher_request) + | 341 | sizeof(struct skcipher_givcrypt_request) + |
252 | crypto_ablkcipher_reqsize(enc)); | 342 | crypto_ablkcipher_reqsize(enc) + |
343 | crypto_ablkcipher_ivsize(enc)); | ||
253 | 344 | ||
254 | spin_lock_init(&ctx->auth_lock); | 345 | spin_lock_init(&ctx->auth_lock); |
255 | 346 | ||
@@ -347,6 +438,7 @@ static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb) | |||
347 | inst->alg.cra_aead.setkey = crypto_authenc_setkey; | 438 | inst->alg.cra_aead.setkey = crypto_authenc_setkey; |
348 | inst->alg.cra_aead.encrypt = crypto_authenc_encrypt; | 439 | inst->alg.cra_aead.encrypt = crypto_authenc_encrypt; |
349 | inst->alg.cra_aead.decrypt = crypto_authenc_decrypt; | 440 | inst->alg.cra_aead.decrypt = crypto_authenc_decrypt; |
441 | inst->alg.cra_aead.givencrypt = crypto_authenc_givencrypt; | ||
350 | 442 | ||
351 | out: | 443 | out: |
352 | crypto_mod_put(auth); | 444 | crypto_mod_put(auth); |