Blame view

drivers/block/cryptoloop.c 4.89 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  /*
     Linux loop encryption enabling module
  
     Copyright (C)  2002 Herbert Valerio Riedel <hvr@gnu.org>
     Copyright (C)  2003 Fruhwirth Clemens <clemens@endorphin.org>
  
     This module is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
  
     This module is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
  
     You should have received a copy of the GNU General Public License
     along with this module; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  #include <linux/module.h>
  
  #include <linux/init.h>
  #include <linux/string.h>
  #include <linux/crypto.h>
  #include <linux/blkdev.h>
  #include <linux/loop.h>
45711f1af   Jens Axboe   [SG] Update drive...
29
  #include <linux/scatterlist.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
  #include <asm/uaccess.h>
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
  MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
  
  #define LOOP_IV_SECTOR_BITS 9
  #define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
  
  static int
  cryptoloop_init(struct loop_device *lo, const struct loop_info64 *info)
  {
  	int err = -EINVAL;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
43
44
  	int cipher_len;
  	int mode_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
  	char cms[LO_NAME_SIZE];			/* cipher-mode string */
  	char *cipher;
  	char *mode;
  	char *cmsp = cms;			/* c-m string pointer */
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
49
  	struct crypto_blkcipher *tfm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
  
  	/* encryption breaks for non sector aligned offsets */
  
  	if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
  		goto out;
  
  	strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE);
  	cms[LO_NAME_SIZE - 1] = 0;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  
  	cipher = cmsp;
  	cipher_len = strcspn(cmsp, "-");
  
  	mode = cmsp + cipher_len;
  	mode_len = 0;
  	if (*mode) {
  		mode++;
  		mode_len = strcspn(mode, "-");
  	}
  
  	if (!mode_len) {
  		mode = "cbc";
  		mode_len = 3;
  	}
  
  	if (cipher_len + mode_len + 3 > LO_NAME_SIZE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  		return -EINVAL;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  	memmove(cms, mode, mode_len);
  	cmsp = cms + mode_len;
  	*cmsp++ = '(';
  	memcpy(cmsp, info->lo_crypt_name, cipher_len);
  	cmsp += cipher_len;
  	*cmsp++ = ')';
  	*cmsp = 0;
  
  	tfm = crypto_alloc_blkcipher(cms, 0, CRYPTO_ALG_ASYNC);
  	if (IS_ERR(tfm))
  		return PTR_ERR(tfm);
  
  	err = crypto_blkcipher_setkey(tfm, info->lo_encrypt_key,
  				      info->lo_encrypt_key_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
  	
  	if (err != 0)
  		goto out_free_tfm;
  
  	lo->key_data = tfm;
  	return 0;
  
   out_free_tfm:
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
98
  	crypto_free_blkcipher(tfm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
  
   out:
  	return err;
  }
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
103
  typedef int (*encdec_cbc_t)(struct blkcipher_desc *desc,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
  			struct scatterlist *sg_out,
  			struct scatterlist *sg_in,
  			unsigned int nsg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  static int
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
108
109
110
111
  cryptoloop_transfer(struct loop_device *lo, int cmd,
  		    struct page *raw_page, unsigned raw_off,
  		    struct page *loop_page, unsigned loop_off,
  		    int size, sector_t IV)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  {
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
113
114
115
116
117
  	struct crypto_blkcipher *tfm = lo->key_data;
  	struct blkcipher_desc desc = {
  		.tfm = tfm,
  		.flags = CRYPTO_TFM_REQ_MAY_SLEEP,
  	};
45711f1af   Jens Axboe   [SG] Update drive...
118
119
  	struct scatterlist sg_out;
  	struct scatterlist sg_in;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
  
  	encdec_cbc_t encdecfunc;
  	struct page *in_page, *out_page;
  	unsigned in_offs, out_offs;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
124
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125

45711f1af   Jens Axboe   [SG] Update drive...
126
127
  	sg_init_table(&sg_out, 1);
  	sg_init_table(&sg_in, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
  	if (cmd == READ) {
  		in_page = raw_page;
  		in_offs = raw_off;
  		out_page = loop_page;
  		out_offs = loop_off;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
133
  		encdecfunc = crypto_blkcipher_crt(tfm)->decrypt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
  	} else {
  		in_page = loop_page;
  		in_offs = loop_off;
  		out_page = raw_page;
  		out_offs = raw_off;
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
139
  		encdecfunc = crypto_blkcipher_crt(tfm)->encrypt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
  	}
  
  	while (size > 0) {
  		const int sz = min(size, LOOP_IV_SECTOR_SIZE);
  		u32 iv[4] = { 0, };
  		iv[0] = cpu_to_le32(IV & 0xffffffff);
642f14903   Jens Axboe   SG: Change sg_set...
146
147
  		sg_set_page(&sg_in, in_page, sz, in_offs);
  		sg_set_page(&sg_out, out_page, sz, out_offs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148

69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
149
150
151
152
  		desc.info = iv;
  		err = encdecfunc(&desc, &sg_out, &sg_in, sz);
  		if (err)
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
  
  		IV++;
  		size -= sz;
  		in_offs += sz;
  		out_offs += sz;
  	}
  
  	return 0;
  }
  
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
  cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
  {
  	return -EINVAL;
  }
  
  static int
  cryptoloop_release(struct loop_device *lo)
  {
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
172
  	struct crypto_blkcipher *tfm = lo->key_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  	if (tfm != NULL) {
69affe7fc   Herbert Xu   [BLOCK] cryptoloo...
174
  		crypto_free_blkcipher(tfm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  		lo->key_data = NULL;
  		return 0;
  	}
  	printk(KERN_ERR "cryptoloop_release(): tfm == NULL?
  ");
  	return -EINVAL;
  }
  
  static struct loop_func_table cryptoloop_funcs = {
  	.number = LO_CRYPT_CRYPTOAPI,
  	.init = cryptoloop_init,
  	.ioctl = cryptoloop_ioctl,
  	.transfer = cryptoloop_transfer,
  	.release = cryptoloop_release,
  	.owner = THIS_MODULE
  };
  
  static int __init
  init_cryptoloop(void)
  {
  	int rc = loop_register_transfer(&cryptoloop_funcs);
  
  	if (rc)
  		printk(KERN_ERR "cryptoloop: loop_register_transfer failed
  ");
  	return rc;
  }
  
  static void __exit
  cleanup_cryptoloop(void)
  {
  	if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
  		printk(KERN_ERR
  			"cryptoloop: loop_unregister_transfer failed
  ");
  }
  
  module_init(init_cryptoloop);
  module_exit(cleanup_cryptoloop);