aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/algif_hash.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-10-19 09:23:00 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2010-11-19 04:47:58 -0500
commitfe869cdb89c95d060c77eea20204d6c91f233b53 (patch)
treefb665d1f162e8bdf978d264ca24d8a07d7380316 /crypto/algif_hash.c
parent03c8efc1ffeb6b82a22c1af8dd908af349563314 (diff)
crypto: algif_hash - User-space interface for hash operations
This patch adds the af_alg plugin for hash, corresponding to the ahash kernel operation type. Keys can optionally be set through the setsockopt interface. Each sendmsg call will finalise the hash unless sent with a MSG_MORE flag. Partial hash states can be cloned using accept(2). The interface is completely synchronous, all operations will complete prior to the system call returning. Both sendmsg(2) and splice(2) support reading the user-space data directly without copying (except that the Crypto API itself may copy the data if alignment is off). For now only the splice(2) interface supports performing digest instead of init/update/final. In future the sendmsg(2) interface will also be modified to use digest/finup where possible so that hardware that cannot return a partial hash state can still benefit from this interface. Thakns to Miloslav Trmac for reviewing this and contributing fixes and improvements. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: David S. Miller <davem@davemloft.net> Tested-by: Martin Willi <martin@strongswan.org>
Diffstat (limited to 'crypto/algif_hash.c')
-rw-r--r--crypto/algif_hash.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
new file mode 100644
index 000000000000..62122a1a2f7a
--- /dev/null
+++ b/crypto/algif_hash.c
@@ -0,0 +1,319 @@
1/*
2 * algif_hash: User-space interface for hash algorithms
3 *
4 * This file provides the user-space API for hash algorithms.
5 *
6 * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 */
14
15#include <crypto/hash.h>
16#include <crypto/if_alg.h>
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/mm.h>
20#include <linux/module.h>
21#include <linux/net.h>
22#include <net/sock.h>
23
24struct hash_ctx {
25 struct af_alg_sgl sgl;
26
27 u8 *result;
28
29 struct af_alg_completion completion;
30
31 unsigned int len;
32 bool more;
33
34 struct ahash_request req;
35};
36
37static int hash_sendmsg(struct kiocb *unused, struct socket *sock,
38 struct msghdr *msg, size_t ignored)
39{
40 int limit = ALG_MAX_PAGES * PAGE_SIZE;
41 struct sock *sk = sock->sk;
42 struct alg_sock *ask = alg_sk(sk);
43 struct hash_ctx *ctx = ask->private;
44 unsigned long iovlen;
45 struct iovec *iov;
46 long copied = 0;
47 int err;
48
49 if (limit > sk->sk_sndbuf)
50 limit = sk->sk_sndbuf;
51
52 lock_sock(sk);
53 if (!ctx->more) {
54 err = crypto_ahash_init(&ctx->req);
55 if (err)
56 goto unlock;
57 }
58
59 ctx->more = 0;
60
61 for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
62 iovlen--, iov++) {
63 unsigned long seglen = iov->iov_len;
64 char __user *from = iov->iov_base;
65
66 while (seglen) {
67 int len = min_t(unsigned long, seglen, limit);
68 int newlen;
69
70 newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
71 if (newlen < 0)
72 goto unlock;
73
74 ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
75 newlen);
76
77 err = af_alg_wait_for_completion(
78 crypto_ahash_update(&ctx->req),
79 &ctx->completion);
80
81 af_alg_free_sg(&ctx->sgl);
82
83 if (err)
84 goto unlock;
85
86 seglen -= newlen;
87 from += newlen;
88 copied += newlen;
89 }
90 }
91
92 err = 0;
93
94 ctx->more = msg->msg_flags & MSG_MORE;
95 if (!ctx->more) {
96 ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
97 err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
98 &ctx->completion);
99 }
100
101unlock:
102 release_sock(sk);
103
104 return err ?: copied;
105}
106
107static ssize_t hash_sendpage(struct socket *sock, struct page *page,
108 int offset, size_t size, int flags)
109{
110 struct sock *sk = sock->sk;
111 struct alg_sock *ask = alg_sk(sk);
112 struct hash_ctx *ctx = ask->private;
113 int err;
114
115 lock_sock(sk);
116 sg_init_table(ctx->sgl.sg, 1);
117 sg_set_page(ctx->sgl.sg, page, size, offset);
118
119 ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size);
120
121 if (!(flags & MSG_MORE)) {
122 if (ctx->more)
123 err = crypto_ahash_finup(&ctx->req);
124 else
125 err = crypto_ahash_digest(&ctx->req);
126 } else {
127 if (!ctx->more) {
128 err = crypto_ahash_init(&ctx->req);
129 if (err)
130 goto unlock;
131 }
132
133 err = crypto_ahash_update(&ctx->req);
134 }
135
136 err = af_alg_wait_for_completion(err, &ctx->completion);
137 if (err)
138 goto unlock;
139
140 ctx->more = flags & MSG_MORE;
141
142unlock:
143 release_sock(sk);
144
145 return err ?: size;
146}
147
148static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
149 struct msghdr *msg, size_t len, int flags)
150{
151 struct sock *sk = sock->sk;
152 struct alg_sock *ask = alg_sk(sk);
153 struct hash_ctx *ctx = ask->private;
154 unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));
155 int err;
156
157 if (len > ds)
158 len = ds;
159 else if (len < ds)
160 msg->msg_flags |= MSG_TRUNC;
161
162 lock_sock(sk);
163 if (ctx->more) {
164 ctx->more = 0;
165 ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
166 err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
167 &ctx->completion);
168 if (err)
169 goto unlock;
170 }
171
172 err = memcpy_toiovec(msg->msg_iov, ctx->result, len);
173
174unlock:
175 release_sock(sk);
176
177 return err ?: len;
178}
179
180static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
181{
182 struct sock *sk = sock->sk;
183 struct alg_sock *ask = alg_sk(sk);
184 struct hash_ctx *ctx = ask->private;
185 struct ahash_request *req = &ctx->req;
186 char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
187 struct sock *sk2;
188 struct alg_sock *ask2;
189 struct hash_ctx *ctx2;
190 int err;
191
192 err = crypto_ahash_export(req, state);
193 if (err)
194 return err;
195
196 err = af_alg_accept(ask->parent, newsock);
197 if (err)
198 return err;
199
200 sk2 = newsock->sk;
201 ask2 = alg_sk(sk2);
202 ctx2 = ask2->private;
203 ctx2->more = 1;
204
205 err = crypto_ahash_import(&ctx2->req, state);
206 if (err) {
207 sock_orphan(sk2);
208 sock_put(sk2);
209 }
210
211 return err;
212}
213
214static struct proto_ops algif_hash_ops = {
215 .family = PF_ALG,
216
217 .connect = sock_no_connect,
218 .socketpair = sock_no_socketpair,
219 .getname = sock_no_getname,
220 .ioctl = sock_no_ioctl,
221 .listen = sock_no_listen,
222 .shutdown = sock_no_shutdown,
223 .getsockopt = sock_no_getsockopt,
224 .mmap = sock_no_mmap,
225 .bind = sock_no_bind,
226 .setsockopt = sock_no_setsockopt,
227 .poll = sock_no_poll,
228
229 .release = af_alg_release,
230 .sendmsg = hash_sendmsg,
231 .sendpage = hash_sendpage,
232 .recvmsg = hash_recvmsg,
233 .accept = hash_accept,
234};
235
236static void *hash_bind(const char *name, u32 type, u32 mask)
237{
238 return crypto_alloc_ahash(name, type, mask);
239}
240
241static void hash_release(void *private)
242{
243 crypto_free_ahash(private);
244}
245
246static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
247{
248 return crypto_ahash_setkey(private, key, keylen);
249}
250
251static void hash_sock_destruct(struct sock *sk)
252{
253 struct alg_sock *ask = alg_sk(sk);
254 struct hash_ctx *ctx = ask->private;
255
256 sock_kfree_s(sk, ctx->result,
257 crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
258 sock_kfree_s(sk, ctx, ctx->len);
259 af_alg_release_parent(sk);
260}
261
262static int hash_accept_parent(void *private, struct sock *sk)
263{
264 struct hash_ctx *ctx;
265 struct alg_sock *ask = alg_sk(sk);
266 unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
267 unsigned ds = crypto_ahash_digestsize(private);
268
269 ctx = sock_kmalloc(sk, len, GFP_KERNEL);
270 if (!ctx)
271 return -ENOMEM;
272
273 ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);
274 if (!ctx->result) {
275 sock_kfree_s(sk, ctx, len);
276 return -ENOMEM;
277 }
278
279 memset(ctx->result, 0, ds);
280
281 ctx->len = len;
282 ctx->more = 0;
283 af_alg_init_completion(&ctx->completion);
284
285 ask->private = ctx;
286
287 ahash_request_set_tfm(&ctx->req, private);
288 ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
289 af_alg_complete, &ctx->completion);
290
291 sk->sk_destruct = hash_sock_destruct;
292
293 return 0;
294}
295
296static const struct af_alg_type algif_type_hash = {
297 .bind = hash_bind,
298 .release = hash_release,
299 .setkey = hash_setkey,
300 .accept = hash_accept_parent,
301 .ops = &algif_hash_ops,
302 .name = "hash",
303 .owner = THIS_MODULE
304};
305
306static int __init algif_hash_init(void)
307{
308 return af_alg_register_type(&algif_type_hash);
309}
310
311static void __exit algif_hash_exit(void)
312{
313 int err = af_alg_unregister_type(&algif_type_hash);
314 BUG_ON(err);
315}
316
317module_init(algif_hash_init);
318module_exit(algif_hash_exit);
319MODULE_LICENSE("GPL");