Commit | Line | Data |
---|---|---|
e497c518 GBY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* | |
4 | * OFB: Output FeedBack mode | |
5 | * | |
6 | * Copyright (C) 2018 ARM Limited or its affiliates. | |
7 | * All rights reserved. | |
e497c518 GBY |
8 | */ |
9 | ||
10 | #include <crypto/algapi.h> | |
11 | #include <crypto/internal/skcipher.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/scatterlist.h> | |
17 | #include <linux/slab.h> | |
18 | ||
19 | struct crypto_ofb_ctx { | |
20 | struct crypto_cipher *child; | |
e497c518 GBY |
21 | }; |
22 | ||
23 | ||
24 | static int crypto_ofb_setkey(struct crypto_skcipher *parent, const u8 *key, | |
25 | unsigned int keylen) | |
26 | { | |
27 | struct crypto_ofb_ctx *ctx = crypto_skcipher_ctx(parent); | |
28 | struct crypto_cipher *child = ctx->child; | |
29 | int err; | |
30 | ||
31 | crypto_cipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); | |
32 | crypto_cipher_set_flags(child, crypto_skcipher_get_flags(parent) & | |
33 | CRYPTO_TFM_REQ_MASK); | |
34 | err = crypto_cipher_setkey(child, key, keylen); | |
35 | crypto_skcipher_set_flags(parent, crypto_cipher_get_flags(child) & | |
36 | CRYPTO_TFM_RES_MASK); | |
37 | return err; | |
38 | } | |
39 | ||
b3e3e2db | 40 | static int crypto_ofb_crypt(struct skcipher_request *req) |
e497c518 | 41 | { |
e497c518 | 42 | struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); |
e497c518 | 43 | struct crypto_ofb_ctx *ctx = crypto_skcipher_ctx(tfm); |
b3e3e2db EB |
44 | struct crypto_cipher *cipher = ctx->child; |
45 | const unsigned int bsize = crypto_cipher_blocksize(cipher); | |
46 | struct skcipher_walk walk; | |
47 | int err; | |
e497c518 | 48 | |
b3e3e2db | 49 | err = skcipher_walk_virt(&walk, req, false); |
e497c518 | 50 | |
b3e3e2db EB |
51 | while (walk.nbytes >= bsize) { |
52 | const u8 *src = walk.src.virt.addr; | |
53 | u8 *dst = walk.dst.virt.addr; | |
54 | u8 * const iv = walk.iv; | |
55 | unsigned int nbytes = walk.nbytes; | |
e497c518 | 56 | |
b3e3e2db EB |
57 | do { |
58 | crypto_cipher_encrypt_one(cipher, iv, iv); | |
59 | crypto_xor_cpy(dst, src, iv, bsize); | |
60 | dst += bsize; | |
61 | src += bsize; | |
62 | } while ((nbytes -= bsize) >= bsize); | |
e497c518 | 63 | |
b3e3e2db EB |
64 | err = skcipher_walk_done(&walk, nbytes); |
65 | } | |
e497c518 | 66 | |
b3e3e2db EB |
67 | if (walk.nbytes) { |
68 | crypto_cipher_encrypt_one(cipher, walk.iv, walk.iv); | |
69 | crypto_xor_cpy(walk.dst.virt.addr, walk.src.virt.addr, walk.iv, | |
70 | walk.nbytes); | |
71 | err = skcipher_walk_done(&walk, 0); | |
72 | } | |
73 | return err; | |
e497c518 GBY |
74 | } |
75 | ||
76 | static int crypto_ofb_init_tfm(struct crypto_skcipher *tfm) | |
77 | { | |
78 | struct skcipher_instance *inst = skcipher_alg_instance(tfm); | |
79 | struct crypto_spawn *spawn = skcipher_instance_ctx(inst); | |
80 | struct crypto_ofb_ctx *ctx = crypto_skcipher_ctx(tfm); | |
81 | struct crypto_cipher *cipher; | |
82 | ||
83 | cipher = crypto_spawn_cipher(spawn); | |
84 | if (IS_ERR(cipher)) | |
85 | return PTR_ERR(cipher); | |
86 | ||
87 | ctx->child = cipher; | |
88 | return 0; | |
89 | } | |
90 | ||
91 | static void crypto_ofb_exit_tfm(struct crypto_skcipher *tfm) | |
92 | { | |
93 | struct crypto_ofb_ctx *ctx = crypto_skcipher_ctx(tfm); | |
94 | ||
95 | crypto_free_cipher(ctx->child); | |
96 | } | |
97 | ||
98 | static void crypto_ofb_free(struct skcipher_instance *inst) | |
99 | { | |
100 | crypto_drop_skcipher(skcipher_instance_ctx(inst)); | |
101 | kfree(inst); | |
102 | } | |
103 | ||
104 | static int crypto_ofb_create(struct crypto_template *tmpl, struct rtattr **tb) | |
105 | { | |
106 | struct skcipher_instance *inst; | |
107 | struct crypto_attr_type *algt; | |
108 | struct crypto_spawn *spawn; | |
109 | struct crypto_alg *alg; | |
110 | u32 mask; | |
111 | int err; | |
112 | ||
113 | err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SKCIPHER); | |
114 | if (err) | |
115 | return err; | |
116 | ||
117 | inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); | |
118 | if (!inst) | |
119 | return -ENOMEM; | |
120 | ||
121 | algt = crypto_get_attr_type(tb); | |
122 | err = PTR_ERR(algt); | |
123 | if (IS_ERR(algt)) | |
124 | goto err_free_inst; | |
125 | ||
126 | mask = CRYPTO_ALG_TYPE_MASK | | |
127 | crypto_requires_off(algt->type, algt->mask, | |
128 | CRYPTO_ALG_NEED_FALLBACK); | |
129 | ||
130 | alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, mask); | |
131 | err = PTR_ERR(alg); | |
132 | if (IS_ERR(alg)) | |
133 | goto err_free_inst; | |
134 | ||
135 | spawn = skcipher_instance_ctx(inst); | |
136 | err = crypto_init_spawn(spawn, alg, skcipher_crypto_instance(inst), | |
137 | CRYPTO_ALG_TYPE_MASK); | |
138 | crypto_mod_put(alg); | |
139 | if (err) | |
140 | goto err_free_inst; | |
141 | ||
142 | err = crypto_inst_setname(skcipher_crypto_instance(inst), "ofb", alg); | |
143 | if (err) | |
144 | goto err_drop_spawn; | |
145 | ||
b3e3e2db EB |
146 | /* OFB mode is a stream cipher. */ |
147 | inst->alg.base.cra_blocksize = 1; | |
148 | ||
149 | /* | |
150 | * To simplify the implementation, configure the skcipher walk to only | |
151 | * give a partial block at the very end, never earlier. | |
152 | */ | |
153 | inst->alg.chunksize = alg->cra_blocksize; | |
154 | ||
e497c518 | 155 | inst->alg.base.cra_priority = alg->cra_priority; |
e497c518 GBY |
156 | inst->alg.base.cra_alignmask = alg->cra_alignmask; |
157 | ||
e497c518 GBY |
158 | inst->alg.ivsize = alg->cra_blocksize; |
159 | inst->alg.min_keysize = alg->cra_cipher.cia_min_keysize; | |
160 | inst->alg.max_keysize = alg->cra_cipher.cia_max_keysize; | |
161 | ||
162 | inst->alg.base.cra_ctxsize = sizeof(struct crypto_ofb_ctx); | |
163 | ||
164 | inst->alg.init = crypto_ofb_init_tfm; | |
165 | inst->alg.exit = crypto_ofb_exit_tfm; | |
166 | ||
167 | inst->alg.setkey = crypto_ofb_setkey; | |
b3e3e2db EB |
168 | inst->alg.encrypt = crypto_ofb_crypt; |
169 | inst->alg.decrypt = crypto_ofb_crypt; | |
e497c518 GBY |
170 | |
171 | inst->free = crypto_ofb_free; | |
172 | ||
173 | err = skcipher_register_instance(tmpl, inst); | |
174 | if (err) | |
175 | goto err_drop_spawn; | |
176 | ||
177 | out: | |
178 | return err; | |
179 | ||
180 | err_drop_spawn: | |
181 | crypto_drop_spawn(spawn); | |
182 | err_free_inst: | |
183 | kfree(inst); | |
184 | goto out; | |
185 | } | |
186 | ||
187 | static struct crypto_template crypto_ofb_tmpl = { | |
188 | .name = "ofb", | |
189 | .create = crypto_ofb_create, | |
190 | .module = THIS_MODULE, | |
191 | }; | |
192 | ||
193 | static int __init crypto_ofb_module_init(void) | |
194 | { | |
195 | return crypto_register_template(&crypto_ofb_tmpl); | |
196 | } | |
197 | ||
198 | static void __exit crypto_ofb_module_exit(void) | |
199 | { | |
200 | crypto_unregister_template(&crypto_ofb_tmpl); | |
201 | } | |
202 | ||
203 | module_init(crypto_ofb_module_init); | |
204 | module_exit(crypto_ofb_module_exit); | |
205 | ||
206 | MODULE_LICENSE("GPL"); | |
207 | MODULE_DESCRIPTION("OFB block cipher algorithm"); | |
208 | MODULE_ALIAS_CRYPTO("ofb"); |