]>
Commit | Line | Data |
---|---|---|
c8e5329d GKH |
1 | From b3e3e2db7de4a1ffe8845876c3520b866cd48de1 Mon Sep 17 00:00:00 2001 |
2 | From: Eric Biggers <ebiggers@google.com> | |
3 | Date: Thu, 3 Jan 2019 20:16:12 -0800 | |
4 | Subject: crypto: ofb - fix handling partial blocks and make thread-safe | |
5 | ||
6 | From: Eric Biggers <ebiggers@google.com> | |
7 | ||
8 | commit b3e3e2db7de4a1ffe8845876c3520b866cd48de1 upstream. | |
9 | ||
10 | Fix multiple bugs in the OFB implementation: | |
11 | ||
12 | 1. It stored the per-request state 'cnt' in the tfm context, which can be | |
13 | used by multiple threads concurrently (e.g. via AF_ALG). | |
14 | 2. It didn't support messages not a multiple of the block cipher size, | |
15 | despite being a stream cipher. | |
16 | 3. It didn't set cra_blocksize to 1 to indicate it is a stream cipher. | |
17 | ||
18 | To fix these, set the 'chunksize' property to the cipher block size to | |
19 | guarantee that when walking through the scatterlist, a partial block can | |
20 | only occur at the end. Then change the implementation to XOR a block at | |
21 | a time at first, then XOR the partial block at the end if needed. This | |
22 | is the same way CTR and CFB are implemented. As a bonus, this also | |
23 | improves performance in most cases over the current approach. | |
24 | ||
25 | Fixes: e497c51896b3 ("crypto: ofb - add output feedback mode") | |
26 | Cc: <stable@vger.kernel.org> # v4.20+ | |
27 | Cc: Gilad Ben-Yossef <gilad@benyossef.com> | |
28 | Signed-off-by: Eric Biggers <ebiggers@google.com> | |
29 | Reviewed-by: Gilad Ben-Yossef <gilad@benyossef.com> | |
30 | Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> | |
31 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
32 | ||
33 | --- | |
34 | crypto/ofb.c | 91 ++++++++++++++++++++++--------------------------------- | |
35 | crypto/testmgr.h | 28 +++++++++++++++- | |
36 | 2 files changed, 63 insertions(+), 56 deletions(-) | |
37 | ||
38 | --- a/crypto/ofb.c | |
39 | +++ b/crypto/ofb.c | |
40 | @@ -5,9 +5,6 @@ | |
41 | * | |
42 | * Copyright (C) 2018 ARM Limited or its affiliates. | |
43 | * All rights reserved. | |
44 | - * | |
45 | - * Based loosely on public domain code gleaned from libtomcrypt | |
46 | - * (https://github.com/libtom/libtomcrypt). | |
47 | */ | |
48 | ||
49 | #include <crypto/algapi.h> | |
50 | @@ -21,7 +18,6 @@ | |
51 | ||
52 | struct crypto_ofb_ctx { | |
53 | struct crypto_cipher *child; | |
54 | - int cnt; | |
55 | }; | |
56 | ||
57 | ||
58 | @@ -41,58 +37,40 @@ static int crypto_ofb_setkey(struct cryp | |
59 | return err; | |
60 | } | |
61 | ||
62 | -static int crypto_ofb_encrypt_segment(struct crypto_ofb_ctx *ctx, | |
63 | - struct skcipher_walk *walk, | |
64 | - struct crypto_cipher *tfm) | |
65 | -{ | |
66 | - int bsize = crypto_cipher_blocksize(tfm); | |
67 | - int nbytes = walk->nbytes; | |
68 | - | |
69 | - u8 *src = walk->src.virt.addr; | |
70 | - u8 *dst = walk->dst.virt.addr; | |
71 | - u8 *iv = walk->iv; | |
72 | - | |
73 | - do { | |
74 | - if (ctx->cnt == bsize) { | |
75 | - if (nbytes < bsize) | |
76 | - break; | |
77 | - crypto_cipher_encrypt_one(tfm, iv, iv); | |
78 | - ctx->cnt = 0; | |
79 | - } | |
80 | - *dst = *src ^ iv[ctx->cnt]; | |
81 | - src++; | |
82 | - dst++; | |
83 | - ctx->cnt++; | |
84 | - } while (--nbytes); | |
85 | - return nbytes; | |
86 | -} | |
87 | - | |
88 | -static int crypto_ofb_encrypt(struct skcipher_request *req) | |
89 | +static int crypto_ofb_crypt(struct skcipher_request *req) | |
90 | { | |
91 | - struct skcipher_walk walk; | |
92 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); | |
93 | - unsigned int bsize; | |
94 | struct crypto_ofb_ctx *ctx = crypto_skcipher_ctx(tfm); | |
95 | - struct crypto_cipher *child = ctx->child; | |
96 | - int ret = 0; | |
97 | + struct crypto_cipher *cipher = ctx->child; | |
98 | + const unsigned int bsize = crypto_cipher_blocksize(cipher); | |
99 | + struct skcipher_walk walk; | |
100 | + int err; | |
101 | ||
102 | - bsize = crypto_cipher_blocksize(child); | |
103 | - ctx->cnt = bsize; | |
104 | + err = skcipher_walk_virt(&walk, req, false); | |
105 | ||
106 | - ret = skcipher_walk_virt(&walk, req, false); | |
107 | + while (walk.nbytes >= bsize) { | |
108 | + const u8 *src = walk.src.virt.addr; | |
109 | + u8 *dst = walk.dst.virt.addr; | |
110 | + u8 * const iv = walk.iv; | |
111 | + unsigned int nbytes = walk.nbytes; | |
112 | + | |
113 | + do { | |
114 | + crypto_cipher_encrypt_one(cipher, iv, iv); | |
115 | + crypto_xor_cpy(dst, src, iv, bsize); | |
116 | + dst += bsize; | |
117 | + src += bsize; | |
118 | + } while ((nbytes -= bsize) >= bsize); | |
119 | ||
120 | - while (walk.nbytes) { | |
121 | - ret = crypto_ofb_encrypt_segment(ctx, &walk, child); | |
122 | - ret = skcipher_walk_done(&walk, ret); | |
123 | + err = skcipher_walk_done(&walk, nbytes); | |
124 | } | |
125 | ||
126 | - return ret; | |
127 | -} | |
128 | - | |
129 | -/* OFB encrypt and decrypt are identical */ | |
130 | -static int crypto_ofb_decrypt(struct skcipher_request *req) | |
131 | -{ | |
132 | - return crypto_ofb_encrypt(req); | |
133 | + if (walk.nbytes) { | |
134 | + crypto_cipher_encrypt_one(cipher, walk.iv, walk.iv); | |
135 | + crypto_xor_cpy(walk.dst.virt.addr, walk.src.virt.addr, walk.iv, | |
136 | + walk.nbytes); | |
137 | + err = skcipher_walk_done(&walk, 0); | |
138 | + } | |
139 | + return err; | |
140 | } | |
141 | ||
142 | static int crypto_ofb_init_tfm(struct crypto_skcipher *tfm) | |
143 | @@ -165,13 +143,18 @@ static int crypto_ofb_create(struct cryp | |
144 | if (err) | |
145 | goto err_drop_spawn; | |
146 | ||
147 | + /* OFB mode is a stream cipher. */ | |
148 | + inst->alg.base.cra_blocksize = 1; | |
149 | + | |
150 | + /* | |
151 | + * To simplify the implementation, configure the skcipher walk to only | |
152 | + * give a partial block at the very end, never earlier. | |
153 | + */ | |
154 | + inst->alg.chunksize = alg->cra_blocksize; | |
155 | + | |
156 | inst->alg.base.cra_priority = alg->cra_priority; | |
157 | - inst->alg.base.cra_blocksize = alg->cra_blocksize; | |
158 | inst->alg.base.cra_alignmask = alg->cra_alignmask; | |
159 | ||
160 | - /* We access the data as u32s when xoring. */ | |
161 | - inst->alg.base.cra_alignmask |= __alignof__(u32) - 1; | |
162 | - | |
163 | inst->alg.ivsize = alg->cra_blocksize; | |
164 | inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize; | |
165 | inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize; | |
166 | @@ -182,8 +165,8 @@ static int crypto_ofb_create(struct cryp | |
167 | inst->alg.exit = crypto_ofb_exit_tfm; | |
168 | ||
169 | inst->alg.setkey = crypto_ofb_setkey; | |
170 | - inst->alg.encrypt = crypto_ofb_encrypt; | |
171 | - inst->alg.decrypt = crypto_ofb_decrypt; | |
172 | + inst->alg.encrypt = crypto_ofb_crypt; | |
173 | + inst->alg.decrypt = crypto_ofb_crypt; | |
174 | ||
175 | inst->free = crypto_ofb_free; | |
176 | ||
177 | --- a/crypto/testmgr.h | |
178 | +++ b/crypto/testmgr.h | |
179 | @@ -16681,8 +16681,7 @@ static const struct cipher_testvec aes_c | |
180 | }; | |
181 | ||
182 | static const struct cipher_testvec aes_ofb_tv_template[] = { | |
183 | - /* From NIST Special Publication 800-38A, Appendix F.5 */ | |
184 | - { | |
185 | + { /* From NIST Special Publication 800-38A, Appendix F.5 */ | |
186 | .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6" | |
187 | "\xab\xf7\x15\x88\x09\xcf\x4f\x3c", | |
188 | .klen = 16, | |
189 | @@ -16705,6 +16704,31 @@ static const struct cipher_testvec aes_o | |
190 | "\x30\x4c\x65\x28\xf6\x59\xc7\x78" | |
191 | "\x66\xa5\x10\xd9\xc1\xd6\xae\x5e", | |
192 | .len = 64, | |
193 | + .also_non_np = 1, | |
194 | + .np = 2, | |
195 | + .tap = { 31, 33 }, | |
196 | + }, { /* > 16 bytes, not a multiple of 16 bytes */ | |
197 | + .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6" | |
198 | + "\xab\xf7\x15\x88\x09\xcf\x4f\x3c", | |
199 | + .klen = 16, | |
200 | + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" | |
201 | + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", | |
202 | + .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96" | |
203 | + "\xe9\x3d\x7e\x11\x73\x93\x17\x2a" | |
204 | + "\xae", | |
205 | + .ctext = "\x3b\x3f\xd9\x2e\xb7\x2d\xad\x20" | |
206 | + "\x33\x34\x49\xf8\xe8\x3c\xfb\x4a" | |
207 | + "\x77", | |
208 | + .len = 17, | |
209 | + }, { /* < 16 bytes */ | |
210 | + .key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6" | |
211 | + "\xab\xf7\x15\x88\x09\xcf\x4f\x3c", | |
212 | + .klen = 16, | |
213 | + .iv = "\x00\x01\x02\x03\x04\x05\x06\x07" | |
214 | + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", | |
215 | + .ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f", | |
216 | + .ctext = "\x3b\x3f\xd9\x2e\xb7\x2d\xad", | |
217 | + .len = 7, | |
218 | } | |
219 | }; | |
220 |