1 From bef9f0ba300a55d79a69aa172156072182176515 Mon Sep 17 00:00:00 2001
2 From: Harald Freudenberger <freude@linux.ibm.com>
3 Date: Thu, 23 May 2019 16:18:25 +0200
4 Subject: s390/crypto: fix gcm-aes-s390 selftest failures
6 From: Harald Freudenberger <freude@linux.ibm.com>
8 commit bef9f0ba300a55d79a69aa172156072182176515 upstream.
10 The current kernel uses improved crypto selftests. These
11 tests showed that the current implementation of gcm-aes-s390
12 is not able to deal with chunks of output buffers which are
13 not a multiple of 16 bytes. This patch introduces a rework
14 of the gcm aes s390 scatter walk handling which now is able
15 to handle any input and output scatter list chunk sizes
18 Code has been verified by the crypto selftests, the tcrypt
19 kernel module and additional tests ran via the af_alg interface.
21 Cc: <stable@vger.kernel.org>
22 Reported-by: Julian Wiedmann <jwi@linux.ibm.com>
23 Reviewed-by: Patrick Steuer <steuer@linux.ibm.com>
24 Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
25 Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
26 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
29 arch/s390/crypto/aes_s390.c | 148 +++++++++++++++++++++++++++++++-------------
30 1 file changed, 107 insertions(+), 41 deletions(-)
32 --- a/arch/s390/crypto/aes_s390.c
33 +++ b/arch/s390/crypto/aes_s390.c
34 @@ -826,19 +826,45 @@ static int gcm_aes_setauthsize(struct cr
38 -static void gcm_sg_walk_start(struct gcm_sg_walk *gw, struct scatterlist *sg,
40 +static void gcm_walk_start(struct gcm_sg_walk *gw, struct scatterlist *sg,
43 memset(gw, 0, sizeof(*gw));
44 gw->walk_bytes_remain = len;
45 scatterwalk_start(&gw->walk, sg);
48 -static int gcm_sg_walk_go(struct gcm_sg_walk *gw, unsigned int minbytesneeded)
49 +static inline unsigned int _gcm_sg_clamp_and_map(struct gcm_sg_walk *gw)
51 + struct scatterlist *nextsg;
53 + gw->walk_bytes = scatterwalk_clamp(&gw->walk, gw->walk_bytes_remain);
54 + while (!gw->walk_bytes) {
55 + nextsg = sg_next(gw->walk.sg);
58 + scatterwalk_start(&gw->walk, nextsg);
59 + gw->walk_bytes = scatterwalk_clamp(&gw->walk,
60 + gw->walk_bytes_remain);
62 + gw->walk_ptr = scatterwalk_map(&gw->walk);
63 + return gw->walk_bytes;
66 +static inline void _gcm_sg_unmap_and_advance(struct gcm_sg_walk *gw,
67 + unsigned int nbytes)
69 + gw->walk_bytes_remain -= nbytes;
70 + scatterwalk_unmap(&gw->walk);
71 + scatterwalk_advance(&gw->walk, nbytes);
72 + scatterwalk_done(&gw->walk, 0, gw->walk_bytes_remain);
73 + gw->walk_ptr = NULL;
76 +static int gcm_in_walk_go(struct gcm_sg_walk *gw, unsigned int minbytesneeded)
80 - /* minbytesneeded <= AES_BLOCK_SIZE */
81 if (gw->buf_bytes && gw->buf_bytes >= minbytesneeded) {
83 gw->nbytes = gw->buf_bytes;
84 @@ -851,13 +877,11 @@ static int gcm_sg_walk_go(struct gcm_sg_
88 - gw->walk_bytes = scatterwalk_clamp(&gw->walk, gw->walk_bytes_remain);
89 - if (!gw->walk_bytes) {
90 - scatterwalk_start(&gw->walk, sg_next(gw->walk.sg));
91 - gw->walk_bytes = scatterwalk_clamp(&gw->walk,
92 - gw->walk_bytes_remain);
93 + if (!_gcm_sg_clamp_and_map(gw)) {
98 - gw->walk_ptr = scatterwalk_map(&gw->walk);
100 if (!gw->buf_bytes && gw->walk_bytes >= minbytesneeded) {
101 gw->ptr = gw->walk_ptr;
102 @@ -869,51 +893,90 @@ static int gcm_sg_walk_go(struct gcm_sg_
103 n = min(gw->walk_bytes, AES_BLOCK_SIZE - gw->buf_bytes);
104 memcpy(gw->buf + gw->buf_bytes, gw->walk_ptr, n);
106 - gw->walk_bytes_remain -= n;
107 - scatterwalk_unmap(&gw->walk);
108 - scatterwalk_advance(&gw->walk, n);
109 - scatterwalk_done(&gw->walk, 0, gw->walk_bytes_remain);
111 + _gcm_sg_unmap_and_advance(gw, n);
112 if (gw->buf_bytes >= minbytesneeded) {
114 gw->nbytes = gw->buf_bytes;
118 - gw->walk_bytes = scatterwalk_clamp(&gw->walk,
119 - gw->walk_bytes_remain);
120 - if (!gw->walk_bytes) {
121 - scatterwalk_start(&gw->walk, sg_next(gw->walk.sg));
122 - gw->walk_bytes = scatterwalk_clamp(&gw->walk,
123 - gw->walk_bytes_remain);
124 + if (!_gcm_sg_clamp_and_map(gw)) {
129 - gw->walk_ptr = scatterwalk_map(&gw->walk);
136 -static void gcm_sg_walk_done(struct gcm_sg_walk *gw, unsigned int bytesdone)
137 +static int gcm_out_walk_go(struct gcm_sg_walk *gw, unsigned int minbytesneeded)
140 + if (gw->walk_bytes_remain == 0) {
146 + if (!_gcm_sg_clamp_and_map(gw)) {
152 + if (gw->walk_bytes >= minbytesneeded) {
153 + gw->ptr = gw->walk_ptr;
154 + gw->nbytes = gw->walk_bytes;
158 + scatterwalk_unmap(&gw->walk);
159 + gw->walk_ptr = NULL;
162 + gw->nbytes = sizeof(gw->buf);
168 +static int gcm_in_walk_done(struct gcm_sg_walk *gw, unsigned int bytesdone)
174 if (gw->ptr == gw->buf) {
175 - n = gw->buf_bytes - bytesdone;
176 + int n = gw->buf_bytes - bytesdone;
178 memmove(gw->buf, gw->buf + bytesdone, n);
179 - gw->buf_bytes -= n;
184 - gw->walk_bytes_remain -= bytesdone;
185 - scatterwalk_unmap(&gw->walk);
186 - scatterwalk_advance(&gw->walk, bytesdone);
187 - scatterwalk_done(&gw->walk, 0, gw->walk_bytes_remain);
190 + _gcm_sg_unmap_and_advance(gw, bytesdone);
195 +static int gcm_out_walk_done(struct gcm_sg_walk *gw, unsigned int bytesdone)
199 + if (gw->ptr == NULL)
202 + if (gw->ptr == gw->buf) {
203 + for (i = 0; i < bytesdone; i += n) {
204 + if (!_gcm_sg_clamp_and_map(gw))
206 + n = min(gw->walk_bytes, bytesdone - i);
207 + memcpy(gw->walk_ptr, gw->buf + i, n);
208 + _gcm_sg_unmap_and_advance(gw, n);
211 + _gcm_sg_unmap_and_advance(gw, bytesdone);
216 static int gcm_aes_crypt(struct aead_request *req, unsigned int flags)
217 @@ -926,7 +989,7 @@ static int gcm_aes_crypt(struct aead_req
218 unsigned int pclen = req->cryptlen;
221 - unsigned int len, in_bytes, out_bytes,
222 + unsigned int n, len, in_bytes, out_bytes,
223 min_bytes, bytes, aad_bytes, pc_bytes;
224 struct gcm_sg_walk gw_in, gw_out;
225 u8 tag[GHASH_DIGEST_SIZE];
226 @@ -963,14 +1026,14 @@ static int gcm_aes_crypt(struct aead_req
227 *(u32 *)(param.j0 + ivsize) = 1;
228 memcpy(param.k, ctx->key, ctx->key_len);
230 - gcm_sg_walk_start(&gw_in, req->src, len);
231 - gcm_sg_walk_start(&gw_out, req->dst, len);
232 + gcm_walk_start(&gw_in, req->src, len);
233 + gcm_walk_start(&gw_out, req->dst, len);
236 min_bytes = min_t(unsigned int,
237 aadlen > 0 ? aadlen : pclen, AES_BLOCK_SIZE);
238 - in_bytes = gcm_sg_walk_go(&gw_in, min_bytes);
239 - out_bytes = gcm_sg_walk_go(&gw_out, min_bytes);
240 + in_bytes = gcm_in_walk_go(&gw_in, min_bytes);
241 + out_bytes = gcm_out_walk_go(&gw_out, min_bytes);
242 bytes = min(in_bytes, out_bytes);
244 if (aadlen + pclen <= bytes) {
245 @@ -997,8 +1060,11 @@ static int gcm_aes_crypt(struct aead_req
246 gw_in.ptr + aad_bytes, pc_bytes,
247 gw_in.ptr, aad_bytes);
249 - gcm_sg_walk_done(&gw_in, aad_bytes + pc_bytes);
250 - gcm_sg_walk_done(&gw_out, aad_bytes + pc_bytes);
251 + n = aad_bytes + pc_bytes;
252 + if (gcm_in_walk_done(&gw_in, n) != n)
254 + if (gcm_out_walk_done(&gw_out, n) != n)
258 } while (aadlen + pclen > 0);