18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci   Linux loop encryption enabling module
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci   Copyright (C)  2002 Herbert Valerio Riedel <hvr@gnu.org>
68c2ecf20Sopenharmony_ci   Copyright (C)  2003 Fruhwirth Clemens <clemens@endorphin.org>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <crypto/skcipher.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
168c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include "loop.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define LOOP_IV_SECTOR_BITS 9
258c2ecf20Sopenharmony_ci#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int
288c2ecf20Sopenharmony_cicryptoloop_init(struct loop_device *lo, const struct loop_info64 *info)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int err = -EINVAL;
318c2ecf20Sopenharmony_ci	int cipher_len;
328c2ecf20Sopenharmony_ci	int mode_len;
338c2ecf20Sopenharmony_ci	char cms[LO_NAME_SIZE];			/* cipher-mode string */
348c2ecf20Sopenharmony_ci	char *mode;
358c2ecf20Sopenharmony_ci	char *cmsp = cms;			/* c-m string pointer */
368c2ecf20Sopenharmony_ci	struct crypto_sync_skcipher *tfm;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/* encryption breaks for non sector aligned offsets */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
418c2ecf20Sopenharmony_ci		goto out;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE);
448c2ecf20Sopenharmony_ci	cms[LO_NAME_SIZE - 1] = 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	cipher_len = strcspn(cmsp, "-");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	mode = cmsp + cipher_len;
498c2ecf20Sopenharmony_ci	mode_len = 0;
508c2ecf20Sopenharmony_ci	if (*mode) {
518c2ecf20Sopenharmony_ci		mode++;
528c2ecf20Sopenharmony_ci		mode_len = strcspn(mode, "-");
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (!mode_len) {
568c2ecf20Sopenharmony_ci		mode = "cbc";
578c2ecf20Sopenharmony_ci		mode_len = 3;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (cipher_len + mode_len + 3 > LO_NAME_SIZE)
618c2ecf20Sopenharmony_ci		return -EINVAL;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	memmove(cms, mode, mode_len);
648c2ecf20Sopenharmony_ci	cmsp = cms + mode_len;
658c2ecf20Sopenharmony_ci	*cmsp++ = '(';
668c2ecf20Sopenharmony_ci	memcpy(cmsp, info->lo_crypt_name, cipher_len);
678c2ecf20Sopenharmony_ci	cmsp += cipher_len;
688c2ecf20Sopenharmony_ci	*cmsp++ = ')';
698c2ecf20Sopenharmony_ci	*cmsp = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	tfm = crypto_alloc_sync_skcipher(cms, 0, 0);
728c2ecf20Sopenharmony_ci	if (IS_ERR(tfm))
738c2ecf20Sopenharmony_ci		return PTR_ERR(tfm);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	err = crypto_sync_skcipher_setkey(tfm, info->lo_encrypt_key,
768c2ecf20Sopenharmony_ci					  info->lo_encrypt_key_size);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (err != 0)
798c2ecf20Sopenharmony_ci		goto out_free_tfm;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	lo->key_data = tfm;
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci out_free_tfm:
858c2ecf20Sopenharmony_ci	crypto_free_sync_skcipher(tfm);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci out:
888c2ecf20Sopenharmony_ci	return err;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_citypedef int (*encdec_cbc_t)(struct skcipher_request *req);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int
958c2ecf20Sopenharmony_cicryptoloop_transfer(struct loop_device *lo, int cmd,
968c2ecf20Sopenharmony_ci		    struct page *raw_page, unsigned raw_off,
978c2ecf20Sopenharmony_ci		    struct page *loop_page, unsigned loop_off,
988c2ecf20Sopenharmony_ci		    int size, sector_t IV)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct crypto_sync_skcipher *tfm = lo->key_data;
1018c2ecf20Sopenharmony_ci	SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
1028c2ecf20Sopenharmony_ci	struct scatterlist sg_out;
1038c2ecf20Sopenharmony_ci	struct scatterlist sg_in;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	encdec_cbc_t encdecfunc;
1068c2ecf20Sopenharmony_ci	struct page *in_page, *out_page;
1078c2ecf20Sopenharmony_ci	unsigned in_offs, out_offs;
1088c2ecf20Sopenharmony_ci	int err;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	skcipher_request_set_sync_tfm(req, tfm);
1118c2ecf20Sopenharmony_ci	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
1128c2ecf20Sopenharmony_ci				      NULL, NULL);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sg_init_table(&sg_out, 1);
1158c2ecf20Sopenharmony_ci	sg_init_table(&sg_in, 1);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (cmd == READ) {
1188c2ecf20Sopenharmony_ci		in_page = raw_page;
1198c2ecf20Sopenharmony_ci		in_offs = raw_off;
1208c2ecf20Sopenharmony_ci		out_page = loop_page;
1218c2ecf20Sopenharmony_ci		out_offs = loop_off;
1228c2ecf20Sopenharmony_ci		encdecfunc = crypto_skcipher_decrypt;
1238c2ecf20Sopenharmony_ci	} else {
1248c2ecf20Sopenharmony_ci		in_page = loop_page;
1258c2ecf20Sopenharmony_ci		in_offs = loop_off;
1268c2ecf20Sopenharmony_ci		out_page = raw_page;
1278c2ecf20Sopenharmony_ci		out_offs = raw_off;
1288c2ecf20Sopenharmony_ci		encdecfunc = crypto_skcipher_encrypt;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	while (size > 0) {
1328c2ecf20Sopenharmony_ci		const int sz = min(size, LOOP_IV_SECTOR_SIZE);
1338c2ecf20Sopenharmony_ci		u32 iv[4] = { 0, };
1348c2ecf20Sopenharmony_ci		iv[0] = cpu_to_le32(IV & 0xffffffff);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		sg_set_page(&sg_in, in_page, sz, in_offs);
1378c2ecf20Sopenharmony_ci		sg_set_page(&sg_out, out_page, sz, out_offs);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		skcipher_request_set_crypt(req, &sg_in, &sg_out, sz, iv);
1408c2ecf20Sopenharmony_ci		err = encdecfunc(req);
1418c2ecf20Sopenharmony_ci		if (err)
1428c2ecf20Sopenharmony_ci			goto out;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		IV++;
1458c2ecf20Sopenharmony_ci		size -= sz;
1468c2ecf20Sopenharmony_ci		in_offs += sz;
1478c2ecf20Sopenharmony_ci		out_offs += sz;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	err = 0;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciout:
1538c2ecf20Sopenharmony_ci	skcipher_request_zero(req);
1548c2ecf20Sopenharmony_ci	return err;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int
1588c2ecf20Sopenharmony_cicryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return -EINVAL;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int
1648c2ecf20Sopenharmony_cicryptoloop_release(struct loop_device *lo)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct crypto_sync_skcipher *tfm = lo->key_data;
1678c2ecf20Sopenharmony_ci	if (tfm != NULL) {
1688c2ecf20Sopenharmony_ci		crypto_free_sync_skcipher(tfm);
1698c2ecf20Sopenharmony_ci		lo->key_data = NULL;
1708c2ecf20Sopenharmony_ci		return 0;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
1738c2ecf20Sopenharmony_ci	return -EINVAL;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic struct loop_func_table cryptoloop_funcs = {
1778c2ecf20Sopenharmony_ci	.number = LO_CRYPT_CRYPTOAPI,
1788c2ecf20Sopenharmony_ci	.init = cryptoloop_init,
1798c2ecf20Sopenharmony_ci	.ioctl = cryptoloop_ioctl,
1808c2ecf20Sopenharmony_ci	.transfer = cryptoloop_transfer,
1818c2ecf20Sopenharmony_ci	.release = cryptoloop_release,
1828c2ecf20Sopenharmony_ci	.owner = THIS_MODULE
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int __init
1868c2ecf20Sopenharmony_ciinit_cryptoloop(void)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int rc = loop_register_transfer(&cryptoloop_funcs);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (rc)
1918c2ecf20Sopenharmony_ci		printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
1928c2ecf20Sopenharmony_ci	else
1938c2ecf20Sopenharmony_ci		pr_warn("the cryptoloop driver has been deprecated and will be removed in in Linux 5.16\n");
1948c2ecf20Sopenharmony_ci	return rc;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void __exit
1988c2ecf20Sopenharmony_cicleanup_cryptoloop(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
2018c2ecf20Sopenharmony_ci		printk(KERN_ERR
2028c2ecf20Sopenharmony_ci			"cryptoloop: loop_unregister_transfer failed\n");
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cimodule_init(init_cryptoloop);
2068c2ecf20Sopenharmony_cimodule_exit(cleanup_cryptoloop);
207