Root/crypto/cbc.c

1/*
2 * CBC: Cipher Block Chaining mode
3 *
4 * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 */
12
13#include <crypto/algapi.h>
14#include <linux/err.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/log2.h>
18#include <linux/module.h>
19#include <linux/scatterlist.h>
20#include <linux/slab.h>
21
22struct crypto_cbc_ctx {
23    struct crypto_cipher *child;
24};
25
26static int crypto_cbc_setkey(struct crypto_tfm *parent, const u8 *key,
27                 unsigned int keylen)
28{
29    struct crypto_cbc_ctx *ctx = crypto_tfm_ctx(parent);
30    struct crypto_cipher *child = ctx->child;
31    int err;
32
33    crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
34    crypto_cipher_set_flags(child, crypto_tfm_get_flags(parent) &
35                       CRYPTO_TFM_REQ_MASK);
36    err = crypto_cipher_setkey(child, key, keylen);
37    crypto_tfm_set_flags(parent, crypto_cipher_get_flags(child) &
38                     CRYPTO_TFM_RES_MASK);
39    return err;
40}
41
42static int crypto_cbc_encrypt_segment(struct blkcipher_desc *desc,
43                      struct blkcipher_walk *walk,
44                      struct crypto_cipher *tfm)
45{
46    void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
47        crypto_cipher_alg(tfm)->cia_encrypt;
48    int bsize = crypto_cipher_blocksize(tfm);
49    unsigned int nbytes = walk->nbytes;
50    u8 *src = walk->src.virt.addr;
51    u8 *dst = walk->dst.virt.addr;
52    u8 *iv = walk->iv;
53
54    do {
55        crypto_xor(iv, src, bsize);
56        fn(crypto_cipher_tfm(tfm), dst, iv);
57        memcpy(iv, dst, bsize);
58
59        src += bsize;
60        dst += bsize;
61    } while ((nbytes -= bsize) >= bsize);
62
63    return nbytes;
64}
65
66static int crypto_cbc_encrypt_inplace(struct blkcipher_desc *desc,
67                      struct blkcipher_walk *walk,
68                      struct crypto_cipher *tfm)
69{
70    void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
71        crypto_cipher_alg(tfm)->cia_encrypt;
72    int bsize = crypto_cipher_blocksize(tfm);
73    unsigned int nbytes = walk->nbytes;
74    u8 *src = walk->src.virt.addr;
75    u8 *iv = walk->iv;
76
77    do {
78        crypto_xor(src, iv, bsize);
79        fn(crypto_cipher_tfm(tfm), src, src);
80        iv = src;
81
82        src += bsize;
83    } while ((nbytes -= bsize) >= bsize);
84
85    memcpy(walk->iv, iv, bsize);
86
87    return nbytes;
88}
89
90static int crypto_cbc_encrypt(struct blkcipher_desc *desc,
91                  struct scatterlist *dst, struct scatterlist *src,
92                  unsigned int nbytes)
93{
94    struct blkcipher_walk walk;
95    struct crypto_blkcipher *tfm = desc->tfm;
96    struct crypto_cbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
97    struct crypto_cipher *child = ctx->child;
98    int err;
99
100    blkcipher_walk_init(&walk, dst, src, nbytes);
101    err = blkcipher_walk_virt(desc, &walk);
102
103    while ((nbytes = walk.nbytes)) {
104        if (walk.src.virt.addr == walk.dst.virt.addr)
105            nbytes = crypto_cbc_encrypt_inplace(desc, &walk, child);
106        else
107            nbytes = crypto_cbc_encrypt_segment(desc, &walk, child);
108        err = blkcipher_walk_done(desc, &walk, nbytes);
109    }
110
111    return err;
112}
113
114static int crypto_cbc_decrypt_segment(struct blkcipher_desc *desc,
115                      struct blkcipher_walk *walk,
116                      struct crypto_cipher *tfm)
117{
118    void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
119        crypto_cipher_alg(tfm)->cia_decrypt;
120    int bsize = crypto_cipher_blocksize(tfm);
121    unsigned int nbytes = walk->nbytes;
122    u8 *src = walk->src.virt.addr;
123    u8 *dst = walk->dst.virt.addr;
124    u8 *iv = walk->iv;
125
126    do {
127        fn(crypto_cipher_tfm(tfm), dst, src);
128        crypto_xor(dst, iv, bsize);
129        iv = src;
130
131        src += bsize;
132        dst += bsize;
133    } while ((nbytes -= bsize) >= bsize);
134
135    memcpy(walk->iv, iv, bsize);
136
137    return nbytes;
138}
139
140static int crypto_cbc_decrypt_inplace(struct blkcipher_desc *desc,
141                      struct blkcipher_walk *walk,
142                      struct crypto_cipher *tfm)
143{
144    void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
145        crypto_cipher_alg(tfm)->cia_decrypt;
146    int bsize = crypto_cipher_blocksize(tfm);
147    unsigned int nbytes = walk->nbytes;
148    u8 *src = walk->src.virt.addr;
149    u8 last_iv[bsize];
150
151    /* Start of the last block. */
152    src += nbytes - (nbytes & (bsize - 1)) - bsize;
153    memcpy(last_iv, src, bsize);
154
155    for (;;) {
156        fn(crypto_cipher_tfm(tfm), src, src);
157        if ((nbytes -= bsize) < bsize)
158            break;
159        crypto_xor(src, src - bsize, bsize);
160        src -= bsize;
161    }
162
163    crypto_xor(src, walk->iv, bsize);
164    memcpy(walk->iv, last_iv, bsize);
165
166    return nbytes;
167}
168
169static int crypto_cbc_decrypt(struct blkcipher_desc *desc,
170                  struct scatterlist *dst, struct scatterlist *src,
171                  unsigned int nbytes)
172{
173    struct blkcipher_walk walk;
174    struct crypto_blkcipher *tfm = desc->tfm;
175    struct crypto_cbc_ctx *ctx = crypto_blkcipher_ctx(tfm);
176    struct crypto_cipher *child = ctx->child;
177    int err;
178
179    blkcipher_walk_init(&walk, dst, src, nbytes);
180    err = blkcipher_walk_virt(desc, &walk);
181
182    while ((nbytes = walk.nbytes)) {
183        if (walk.src.virt.addr == walk.dst.virt.addr)
184            nbytes = crypto_cbc_decrypt_inplace(desc, &walk, child);
185        else
186            nbytes = crypto_cbc_decrypt_segment(desc, &walk, child);
187        err = blkcipher_walk_done(desc, &walk, nbytes);
188    }
189
190    return err;
191}
192
193static int crypto_cbc_init_tfm(struct crypto_tfm *tfm)
194{
195    struct crypto_instance *inst = (void *)tfm->__crt_alg;
196    struct crypto_spawn *spawn = crypto_instance_ctx(inst);
197    struct crypto_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
198    struct crypto_cipher *cipher;
199
200    cipher = crypto_spawn_cipher(spawn);
201    if (IS_ERR(cipher))
202        return PTR_ERR(cipher);
203
204    ctx->child = cipher;
205    return 0;
206}
207
208static void crypto_cbc_exit_tfm(struct crypto_tfm *tfm)
209{
210    struct crypto_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
211    crypto_free_cipher(ctx->child);
212}
213
214static struct crypto_instance *crypto_cbc_alloc(struct rtattr **tb)
215{
216    struct crypto_instance *inst;
217    struct crypto_alg *alg;
218    int err;
219
220    err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER);
221    if (err)
222        return ERR_PTR(err);
223
224    alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER,
225                  CRYPTO_ALG_TYPE_MASK);
226    if (IS_ERR(alg))
227        return ERR_CAST(alg);
228
229    inst = ERR_PTR(-EINVAL);
230    if (!is_power_of_2(alg->cra_blocksize))
231        goto out_put_alg;
232
233    inst = crypto_alloc_instance("cbc", alg);
234    if (IS_ERR(inst))
235        goto out_put_alg;
236
237    inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER;
238    inst->alg.cra_priority = alg->cra_priority;
239    inst->alg.cra_blocksize = alg->cra_blocksize;
240    inst->alg.cra_alignmask = alg->cra_alignmask;
241    inst->alg.cra_type = &crypto_blkcipher_type;
242
243    /* We access the data as u32s when xoring. */
244    inst->alg.cra_alignmask |= __alignof__(u32) - 1;
245
246    inst->alg.cra_blkcipher.ivsize = alg->cra_blocksize;
247    inst->alg.cra_blkcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
248    inst->alg.cra_blkcipher.max_keysize = alg->cra_cipher.cia_max_keysize;
249
250    inst->alg.cra_ctxsize = sizeof(struct crypto_cbc_ctx);
251
252    inst->alg.cra_init = crypto_cbc_init_tfm;
253    inst->alg.cra_exit = crypto_cbc_exit_tfm;
254
255    inst->alg.cra_blkcipher.setkey = crypto_cbc_setkey;
256    inst->alg.cra_blkcipher.encrypt = crypto_cbc_encrypt;
257    inst->alg.cra_blkcipher.decrypt = crypto_cbc_decrypt;
258
259out_put_alg:
260    crypto_mod_put(alg);
261    return inst;
262}
263
264static void crypto_cbc_free(struct crypto_instance *inst)
265{
266    crypto_drop_spawn(crypto_instance_ctx(inst));
267    kfree(inst);
268}
269
270static struct crypto_template crypto_cbc_tmpl = {
271    .name = "cbc",
272    .alloc = crypto_cbc_alloc,
273    .free = crypto_cbc_free,
274    .module = THIS_MODULE,
275};
276
277static int __init crypto_cbc_module_init(void)
278{
279    return crypto_register_template(&crypto_cbc_tmpl);
280}
281
282static void __exit crypto_cbc_module_exit(void)
283{
284    crypto_unregister_template(&crypto_cbc_tmpl);
285}
286
287module_init(crypto_cbc_module_init);
288module_exit(crypto_cbc_module_exit);
289
290MODULE_LICENSE("GPL");
291MODULE_DESCRIPTION("CBC block cipher algorithm");
292

Archive Download this file



interactive