[CRYPTO] chainiv: Add chain IV generator
[linux-2.6.git] / crypto / chainiv.c
1 /*
2  * chainiv: Chain IV Generator
3  *
4  * Generate IVs simply be using the last block of the previous encryption.
5  * This is mainly useful for CBC with a synchronous algorithm.
6  *
7  * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 2 of the License, or (at your option)
12  * any later version.
13  *
14  */
15
16 #include <crypto/internal/skcipher.h>
17 #include <linux/err.h>
18 #include <linux/init.h>
19 #include <linux/module.h>
20 #include <linux/random.h>
21 #include <linux/spinlock.h>
22 #include <linux/string.h>
23
24 struct chainiv_ctx {
25         spinlock_t lock;
26         char iv[];
27 };
28
29 static int chainiv_givencrypt(struct skcipher_givcrypt_request *req)
30 {
31         struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
32         struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
33         struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req);
34         unsigned int ivsize;
35         int err;
36
37         ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv));
38         ablkcipher_request_set_callback(subreq, req->creq.base.flags &
39                                                 ~CRYPTO_TFM_REQ_MAY_SLEEP,
40                                         req->creq.base.complete,
41                                         req->creq.base.data);
42         ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst,
43                                      req->creq.nbytes, req->creq.info);
44
45         spin_lock_bh(&ctx->lock);
46
47         ivsize = crypto_ablkcipher_ivsize(geniv);
48
49         memcpy(req->giv, ctx->iv, ivsize);
50         memcpy(subreq->info, ctx->iv, ivsize);
51
52         err = crypto_ablkcipher_encrypt(subreq);
53         if (err)
54                 goto unlock;
55
56         memcpy(ctx->iv, subreq->info, ivsize);
57
58 unlock:
59         spin_unlock_bh(&ctx->lock);
60
61         return err;
62 }
63
64 static int chainiv_givencrypt_first(struct skcipher_givcrypt_request *req)
65 {
66         struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
67         struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
68
69         spin_lock_bh(&ctx->lock);
70         if (crypto_ablkcipher_crt(geniv)->givencrypt !=
71             chainiv_givencrypt_first)
72                 goto unlock;
73
74         crypto_ablkcipher_crt(geniv)->givencrypt = chainiv_givencrypt;
75         get_random_bytes(ctx->iv, crypto_ablkcipher_ivsize(geniv));
76
77 unlock:
78         spin_unlock_bh(&ctx->lock);
79
80         return chainiv_givencrypt(req);
81 }
82
83 static int chainiv_init(struct crypto_tfm *tfm)
84 {
85         struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
86         struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
87
88         spin_lock_init(&ctx->lock);
89
90         tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request);
91
92         return skcipher_geniv_init(tfm);
93 }
94
95 static struct crypto_template chainiv_tmpl;
96
97 static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
98 {
99         struct crypto_instance *inst;
100
101         inst = skcipher_geniv_alloc(&chainiv_tmpl, tb, 0,
102                                     CRYPTO_ALG_ASYNC);
103         if (IS_ERR(inst))
104                 goto out;
105
106         inst->alg.cra_ablkcipher.givencrypt = chainiv_givencrypt_first;
107
108         inst->alg.cra_init = chainiv_init;
109         inst->alg.cra_exit = skcipher_geniv_exit;
110
111         inst->alg.cra_ctxsize = sizeof(struct chainiv_ctx) +
112                                 inst->alg.cra_ablkcipher.ivsize;
113
114 out:
115         return inst;
116 }
117
118 static struct crypto_template chainiv_tmpl = {
119         .name = "chainiv",
120         .alloc = chainiv_alloc,
121         .free = skcipher_geniv_free,
122         .module = THIS_MODULE,
123 };
124
125 static int __init chainiv_module_init(void)
126 {
127         return crypto_register_template(&chainiv_tmpl);
128 }
129
130 static void __exit chainiv_module_exit(void)
131 {
132         crypto_unregister_template(&chainiv_tmpl);
133 }
134
135 module_init(chainiv_module_init);
136 module_exit(chainiv_module_exit);
137
138 MODULE_LICENSE("GPL");
139 MODULE_DESCRIPTION("Chain IV Generator");