1e497c518SGilad Ben-Yossef // SPDX-License-Identifier: GPL-2.0
2e497c518SGilad Ben-Yossef
3e497c518SGilad Ben-Yossef /*
4e497c518SGilad Ben-Yossef * OFB: Output FeedBack mode
5e497c518SGilad Ben-Yossef *
6e497c518SGilad Ben-Yossef * Copyright (C) 2018 ARM Limited or its affiliates.
7e497c518SGilad Ben-Yossef * All rights reserved.
8e497c518SGilad Ben-Yossef */
9e497c518SGilad Ben-Yossef
10e497c518SGilad Ben-Yossef #include <crypto/algapi.h>
11*0eb76ba2SArd Biesheuvel #include <crypto/internal/cipher.h>
12e497c518SGilad Ben-Yossef #include <crypto/internal/skcipher.h>
13e497c518SGilad Ben-Yossef #include <linux/err.h>
14e497c518SGilad Ben-Yossef #include <linux/init.h>
15e497c518SGilad Ben-Yossef #include <linux/kernel.h>
16e497c518SGilad Ben-Yossef #include <linux/module.h>
17e497c518SGilad Ben-Yossef
crypto_ofb_crypt(struct skcipher_request * req)18b3e3e2dbSEric Biggers static int crypto_ofb_crypt(struct skcipher_request *req)
19e497c518SGilad Ben-Yossef {
20b3e3e2dbSEric Biggers struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
2121f3ca6cSEric Biggers struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
22b3e3e2dbSEric Biggers const unsigned int bsize = crypto_cipher_blocksize(cipher);
23b3e3e2dbSEric Biggers struct skcipher_walk walk;
24b3e3e2dbSEric Biggers int err;
25e497c518SGilad Ben-Yossef
26b3e3e2dbSEric Biggers err = skcipher_walk_virt(&walk, req, false);
27b3e3e2dbSEric Biggers
28b3e3e2dbSEric Biggers while (walk.nbytes >= bsize) {
29b3e3e2dbSEric Biggers const u8 *src = walk.src.virt.addr;
30b3e3e2dbSEric Biggers u8 *dst = walk.dst.virt.addr;
31b3e3e2dbSEric Biggers u8 * const iv = walk.iv;
32b3e3e2dbSEric Biggers unsigned int nbytes = walk.nbytes;
33e497c518SGilad Ben-Yossef
34e497c518SGilad Ben-Yossef do {
35b3e3e2dbSEric Biggers crypto_cipher_encrypt_one(cipher, iv, iv);
36b3e3e2dbSEric Biggers crypto_xor_cpy(dst, src, iv, bsize);
37b3e3e2dbSEric Biggers dst += bsize;
38b3e3e2dbSEric Biggers src += bsize;
39b3e3e2dbSEric Biggers } while ((nbytes -= bsize) >= bsize);
40b3e3e2dbSEric Biggers
41b3e3e2dbSEric Biggers err = skcipher_walk_done(&walk, nbytes);
42e497c518SGilad Ben-Yossef }
43e497c518SGilad Ben-Yossef
44b3e3e2dbSEric Biggers if (walk.nbytes) {
45b3e3e2dbSEric Biggers crypto_cipher_encrypt_one(cipher, walk.iv, walk.iv);
46b3e3e2dbSEric Biggers crypto_xor_cpy(walk.dst.virt.addr, walk.src.virt.addr, walk.iv,
47b3e3e2dbSEric Biggers walk.nbytes);
48b3e3e2dbSEric Biggers err = skcipher_walk_done(&walk, 0);
49e497c518SGilad Ben-Yossef }
50b3e3e2dbSEric Biggers return err;
51e497c518SGilad Ben-Yossef }
52e497c518SGilad Ben-Yossef
crypto_ofb_create(struct crypto_template * tmpl,struct rtattr ** tb)53e497c518SGilad Ben-Yossef static int crypto_ofb_create(struct crypto_template *tmpl, struct rtattr **tb)
54e497c518SGilad Ben-Yossef {
55e497c518SGilad Ben-Yossef struct skcipher_instance *inst;
56e497c518SGilad Ben-Yossef struct crypto_alg *alg;
57e497c518SGilad Ben-Yossef int err;
58e497c518SGilad Ben-Yossef
59b3c16bfcSHerbert Xu inst = skcipher_alloc_instance_simple(tmpl, tb);
6021f3ca6cSEric Biggers if (IS_ERR(inst))
6121f3ca6cSEric Biggers return PTR_ERR(inst);
62e497c518SGilad Ben-Yossef
63b3c16bfcSHerbert Xu alg = skcipher_ialg_simple(inst);
64b3c16bfcSHerbert Xu
65b3e3e2dbSEric Biggers /* OFB mode is a stream cipher. */
66b3e3e2dbSEric Biggers inst->alg.base.cra_blocksize = 1;
67e497c518SGilad Ben-Yossef
68b3e3e2dbSEric Biggers /*
69b3e3e2dbSEric Biggers * To simplify the implementation, configure the skcipher walk to only
70b3e3e2dbSEric Biggers * give a partial block at the very end, never earlier.
71b3e3e2dbSEric Biggers */
72b3e3e2dbSEric Biggers inst->alg.chunksize = alg->cra_blocksize;
73b3e3e2dbSEric Biggers
74b3e3e2dbSEric Biggers inst->alg.encrypt = crypto_ofb_crypt;
75b3e3e2dbSEric Biggers inst->alg.decrypt = crypto_ofb_crypt;
76e497c518SGilad Ben-Yossef
77e497c518SGilad Ben-Yossef err = skcipher_register_instance(tmpl, inst);
78e497c518SGilad Ben-Yossef if (err)
7921f3ca6cSEric Biggers inst->free(inst);
80e497c518SGilad Ben-Yossef
81e497c518SGilad Ben-Yossef return err;
82e497c518SGilad Ben-Yossef }
83e497c518SGilad Ben-Yossef
84e497c518SGilad Ben-Yossef static struct crypto_template crypto_ofb_tmpl = {
85e497c518SGilad Ben-Yossef .name = "ofb",
86e497c518SGilad Ben-Yossef .create = crypto_ofb_create,
87e497c518SGilad Ben-Yossef .module = THIS_MODULE,
88e497c518SGilad Ben-Yossef };
89e497c518SGilad Ben-Yossef
crypto_ofb_module_init(void)90e497c518SGilad Ben-Yossef static int __init crypto_ofb_module_init(void)
91e497c518SGilad Ben-Yossef {
92e497c518SGilad Ben-Yossef return crypto_register_template(&crypto_ofb_tmpl);
93e497c518SGilad Ben-Yossef }
94e497c518SGilad Ben-Yossef
crypto_ofb_module_exit(void)95e497c518SGilad Ben-Yossef static void __exit crypto_ofb_module_exit(void)
96e497c518SGilad Ben-Yossef {
97e497c518SGilad Ben-Yossef crypto_unregister_template(&crypto_ofb_tmpl);
98e497c518SGilad Ben-Yossef }
99e497c518SGilad Ben-Yossef
100c4741b23SEric Biggers subsys_initcall(crypto_ofb_module_init);
101e497c518SGilad Ben-Yossef module_exit(crypto_ofb_module_exit);
102e497c518SGilad Ben-Yossef
103e497c518SGilad Ben-Yossef MODULE_LICENSE("GPL");
10421f3ca6cSEric Biggers MODULE_DESCRIPTION("OFB block cipher mode of operation");
105e497c518SGilad Ben-Yossef MODULE_ALIAS_CRYPTO("ofb");
106*0eb76ba2SArd Biesheuvel MODULE_IMPORT_NS(CRYPTO_INTERNAL);
107