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