xref: /freebsd/sys/geom/eli/g_eli_crypto.c (revision d6b92ffa)
1 /*-
2  * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #ifdef _KERNEL
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #else
36 #include <stdint.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <openssl/evp.h>
42 #define	_OpenSSL_
43 #endif
44 #include <geom/eli/g_eli.h>
45 
46 #ifdef _KERNEL
47 MALLOC_DECLARE(M_ELI);
48 
49 static int
50 g_eli_crypto_done(struct cryptop *crp)
51 {
52 
53 	crp->crp_opaque = (void *)crp;
54 	wakeup(crp);
55 	return (0);
56 }
57 
58 static int
59 g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize,
60     const u_char *key, size_t keysize)
61 {
62 	struct cryptoini cri;
63 	struct cryptop *crp;
64 	struct cryptodesc *crd;
65 	uint64_t sid;
66 	u_char *p;
67 	int error;
68 
69 	KASSERT(algo != CRYPTO_AES_XTS,
70 	    ("%s: CRYPTO_AES_XTS unexpected here", __func__));
71 
72 	bzero(&cri, sizeof(cri));
73 	cri.cri_alg = algo;
74 	cri.cri_key = __DECONST(void *, key);
75 	cri.cri_klen = keysize;
76 	error = crypto_newsession(&sid, &cri, CRYPTOCAP_F_SOFTWARE);
77 	if (error != 0)
78 		return (error);
79 	p = malloc(sizeof(*crp) + sizeof(*crd), M_ELI, M_NOWAIT | M_ZERO);
80 	if (p == NULL) {
81 		crypto_freesession(sid);
82 		return (ENOMEM);
83 	}
84 	crp = (struct cryptop *)p;	p += sizeof(*crp);
85 	crd = (struct cryptodesc *)p;	p += sizeof(*crd);
86 
87 	crd->crd_skip = 0;
88 	crd->crd_len = datasize;
89 	crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
90 	if (enc)
91 		crd->crd_flags |= CRD_F_ENCRYPT;
92 	crd->crd_alg = algo;
93 	crd->crd_key = __DECONST(void *, key);
94 	crd->crd_klen = keysize;
95 	bzero(crd->crd_iv, sizeof(crd->crd_iv));
96 	crd->crd_next = NULL;
97 
98 	crp->crp_sid = sid;
99 	crp->crp_ilen = datasize;
100 	crp->crp_olen = datasize;
101 	crp->crp_opaque = NULL;
102 	crp->crp_callback = g_eli_crypto_done;
103 	crp->crp_buf = (void *)data;
104 	crp->crp_flags = CRYPTO_F_CBIFSYNC;
105 	crp->crp_desc = crd;
106 
107 	error = crypto_dispatch(crp);
108 	if (error == 0) {
109 		while (crp->crp_opaque == NULL)
110 			tsleep(crp, PRIBIO, "geli", hz / 5);
111 		error = crp->crp_etype;
112 	}
113 
114 	free(crp, M_ELI);
115 	crypto_freesession(sid);
116 	return (error);
117 }
118 #else	/* !_KERNEL */
119 static int
120 g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize,
121     const u_char *key, size_t keysize)
122 {
123 	EVP_CIPHER_CTX ctx;
124 	const EVP_CIPHER *type;
125 	u_char iv[keysize];
126 	int outsize;
127 
128 	assert(algo != CRYPTO_AES_XTS);
129 
130 	switch (algo) {
131 	case CRYPTO_NULL_CBC:
132 		type = EVP_enc_null();
133 		break;
134 	case CRYPTO_AES_CBC:
135 		switch (keysize) {
136 		case 128:
137 			type = EVP_aes_128_cbc();
138 			break;
139 		case 192:
140 			type = EVP_aes_192_cbc();
141 			break;
142 		case 256:
143 			type = EVP_aes_256_cbc();
144 			break;
145 		default:
146 			return (EINVAL);
147 		}
148 		break;
149 	case CRYPTO_BLF_CBC:
150 		type = EVP_bf_cbc();
151 		break;
152 #ifndef OPENSSL_NO_CAMELLIA
153 	case CRYPTO_CAMELLIA_CBC:
154 		switch (keysize) {
155 		case 128:
156 			type = EVP_camellia_128_cbc();
157 			break;
158 		case 192:
159 			type = EVP_camellia_192_cbc();
160 			break;
161 		case 256:
162 			type = EVP_camellia_256_cbc();
163 			break;
164 		default:
165 			return (EINVAL);
166 		}
167 		break;
168 #endif
169 	case CRYPTO_3DES_CBC:
170 		type = EVP_des_ede3_cbc();
171 		break;
172 	default:
173 		return (EINVAL);
174 	}
175 
176 	EVP_CIPHER_CTX_init(&ctx);
177 
178 	EVP_CipherInit_ex(&ctx, type, NULL, NULL, NULL, enc);
179 	EVP_CIPHER_CTX_set_key_length(&ctx, keysize / 8);
180 	EVP_CIPHER_CTX_set_padding(&ctx, 0);
181 	bzero(iv, sizeof(iv));
182 	EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, enc);
183 
184 	if (EVP_CipherUpdate(&ctx, data, &outsize, data, datasize) == 0) {
185 		EVP_CIPHER_CTX_cleanup(&ctx);
186 		return (EINVAL);
187 	}
188 	assert(outsize == (int)datasize);
189 
190 	if (EVP_CipherFinal_ex(&ctx, data + outsize, &outsize) == 0) {
191 		EVP_CIPHER_CTX_cleanup(&ctx);
192 		return (EINVAL);
193 	}
194 	assert(outsize == 0);
195 
196 	EVP_CIPHER_CTX_cleanup(&ctx);
197 	return (0);
198 }
199 #endif	/* !_KERNEL */
200 
201 int
202 g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize,
203     const u_char *key, size_t keysize)
204 {
205 
206 	/* We prefer AES-CBC for metadata protection. */
207 	if (algo == CRYPTO_AES_XTS)
208 		algo = CRYPTO_AES_CBC;
209 
210 	return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize));
211 }
212 
213 int
214 g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize,
215     const u_char *key, size_t keysize)
216 {
217 
218 	/* We prefer AES-CBC for metadata protection. */
219 	if (algo == CRYPTO_AES_XTS)
220 		algo = CRYPTO_AES_CBC;
221 
222 	return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize));
223 }
224