xref: /openbsd-src/usr.sbin/smtpd/crypto.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: crypto.c,v 1.6 2016/09/03 14:42:08 gilles Exp $	 */
2 
3 /*
4  * Copyright (c) 2013 Gilles Chehade <gilles@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <openssl/evp.h>
26 
27 
28 #define	CRYPTO_BUFFER_SIZE	16384
29 
30 #define	GCM_TAG_SIZE		16
31 #define	IV_SIZE			12
32 #define	KEY_SIZE		32
33 
34 /* bump if we ever switch from aes-256-gcm to anything else */
35 #define	API_VERSION    		1
36 
37 
38 int	crypto_setup(const char *, size_t);
39 int	crypto_encrypt_file(FILE *, FILE *);
40 int	crypto_decrypt_file(FILE *, FILE *);
41 size_t	crypto_encrypt_buffer(const char *, size_t, char *, size_t);
42 size_t	crypto_decrypt_buffer(const char *, size_t, char *, size_t);
43 
44 static struct crypto_ctx {
45 	unsigned char  		key[KEY_SIZE];
46 } cp;
47 
48 int
49 crypto_setup(const char *key, size_t len)
50 {
51 	if (len != KEY_SIZE)
52 		return 0;
53 
54 	memset(&cp, 0, sizeof cp);
55 
56 	/* openssl rand -hex 16 */
57 	memcpy(cp.key, key, sizeof cp.key);
58 
59 	return 1;
60 }
61 
62 int
63 crypto_encrypt_file(FILE * in, FILE * out)
64 {
65 	EVP_CIPHER_CTX	ctx;
66 	uint8_t		ibuf[CRYPTO_BUFFER_SIZE];
67 	uint8_t		obuf[CRYPTO_BUFFER_SIZE];
68 	uint8_t		iv[IV_SIZE];
69 	uint8_t		tag[GCM_TAG_SIZE];
70 	uint8_t		version = API_VERSION;
71 	size_t		r, w;
72 	int		len;
73 	int		ret = 0;
74 	struct stat	sb;
75 
76 	/* XXX - Do NOT encrypt files bigger than 64GB */
77 	if (fstat(fileno(in), &sb) < 0)
78 		return 0;
79 	if (sb.st_size >= 0x1000000000LL)
80 		return 0;
81 
82 	/* prepend version byte*/
83 	if ((w = fwrite(&version, 1, sizeof version, out)) != sizeof version)
84 		return 0;
85 
86 	/* generate and prepend IV */
87 	memset(iv, 0, sizeof iv);
88 	arc4random_buf(iv, sizeof iv);
89 	if ((w = fwrite(iv, 1, sizeof iv, out)) != sizeof iv)
90 		return 0;
91 
92 	EVP_CIPHER_CTX_init(&ctx);
93 	EVP_EncryptInit_ex(&ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
94 
95 	/* encrypt until end of file */
96 	while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in)) != 0) {
97 		if (!EVP_EncryptUpdate(&ctx, obuf, &len, ibuf, r))
98 			goto end;
99 		if (len && (w = fwrite(obuf, len, 1, out)) != 1)
100 			goto end;
101 	}
102 	if (!feof(in))
103 		goto end;
104 
105 	/* finalize and write last chunk if any */
106 	if (!EVP_EncryptFinal_ex(&ctx, obuf, &len))
107 		goto end;
108 	if (len && (w = fwrite(obuf, len, 1, out)) != 1)
109 		goto end;
110 
111 	/* get and append tag */
112 	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
113 	if ((w = fwrite(tag, sizeof tag, 1, out)) != 1)
114 		goto end;
115 
116 	fflush(out);
117 	ret = 1;
118 
119 end:
120 	EVP_CIPHER_CTX_cleanup(&ctx);
121 	return ret;
122 }
123 
124 int
125 crypto_decrypt_file(FILE * in, FILE * out)
126 {
127 	EVP_CIPHER_CTX	ctx;
128 	uint8_t		ibuf[CRYPTO_BUFFER_SIZE];
129 	uint8_t		obuf[CRYPTO_BUFFER_SIZE];
130 	uint8_t		iv[IV_SIZE];
131 	uint8_t		tag[GCM_TAG_SIZE];
132 	uint8_t		version;
133 	size_t		r, w;
134 	off_t		sz;
135 	int		len;
136 	int		ret = 0;
137 	struct stat	sb;
138 
139 	/* input file too small to be an encrypted file */
140 	if (fstat(fileno(in), &sb) < 0)
141 		return 0;
142 	if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv))
143 		return 0;
144 	sz = sb.st_size;
145 
146 	/* extract tag */
147 	if (fseek(in, -sizeof(tag), SEEK_END) == -1)
148 		return 0;
149 	if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag)
150 		return 0;
151 
152 	if (fseek(in, 0, SEEK_SET) == -1)
153 		return 0;
154 
155 	/* extract version */
156 	if ((r = fread(&version, 1, sizeof version, in)) != sizeof version)
157 		return 0;
158 	if (version != API_VERSION)
159 		return 0;
160 
161 	/* extract IV */
162 	memset(iv, 0, sizeof iv);
163 	if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv)
164 		return 0;
165 
166 	/* real ciphertext length */
167 	sz -= sizeof version;
168 	sz -= sizeof iv;
169 	sz -= sizeof tag;
170 
171 
172 	EVP_CIPHER_CTX_init(&ctx);
173 	EVP_DecryptInit_ex(&ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
174 
175 	/* set expected tag */
176 	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
177 
178 	/* decrypt until end of ciphertext */
179 	while (sz) {
180 		if (sz > CRYPTO_BUFFER_SIZE)
181 			r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in);
182 		else
183 			r = fread(ibuf, 1, sz, in);
184 		if (!r)
185 			break;
186 		if (!EVP_DecryptUpdate(&ctx, obuf, &len, ibuf, r))
187 			goto end;
188 		if (len && (w = fwrite(obuf, len, 1, out)) != 1)
189 			goto end;
190 		sz -= r;
191 	}
192 	if (ferror(in))
193 		goto end;
194 
195 	/* finalize, write last chunk if any and perform authentication check */
196 	if (!EVP_DecryptFinal_ex(&ctx, obuf, &len))
197 		goto end;
198 	if (len && (w = fwrite(obuf, len, 1, out)) != 1)
199 		goto end;
200 
201 	fflush(out);
202 	ret = 1;
203 
204 end:
205 	EVP_CIPHER_CTX_cleanup(&ctx);
206 	return ret;
207 }
208 
209 size_t
210 crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
211 {
212 	EVP_CIPHER_CTX	ctx;
213 	uint8_t		iv[IV_SIZE];
214 	uint8_t		tag[GCM_TAG_SIZE];
215 	uint8_t		version = API_VERSION;
216 	off_t		sz;
217 	int		olen;
218 	int		len = 0;
219 	int		ret = 0;
220 
221 	/* output buffer does not have enough room */
222 	if (outlen < inlen + sizeof version + sizeof tag + sizeof iv)
223 		return 0;
224 
225 	/* input should not exceed 64GB */
226 	sz = inlen;
227 	if (sz >= 0x1000000000LL)
228 		return 0;
229 
230 	/* prepend version */
231 	*out = version;
232 	len++;
233 
234 	/* generate IV */
235 	memset(iv, 0, sizeof iv);
236 	arc4random_buf(iv, sizeof iv);
237 	memcpy(out + len, iv, sizeof iv);
238 	len += sizeof iv;
239 
240 	EVP_CIPHER_CTX_init(&ctx);
241 	EVP_EncryptInit_ex(&ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
242 
243 	/* encrypt buffer */
244 	if (!EVP_EncryptUpdate(&ctx, out + len, &olen, in, inlen))
245 		goto end;
246 	len += olen;
247 
248 	/* finalize and write last chunk if any */
249 	if (!EVP_EncryptFinal_ex(&ctx, out + len, &olen))
250 		goto end;
251 	len += olen;
252 
253 	/* get and append tag */
254 	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
255 	memcpy(out + len, tag, sizeof tag);
256 	ret = len + sizeof tag;
257 
258 end:
259 	EVP_CIPHER_CTX_cleanup(&ctx);
260 	return ret;
261 }
262 
263 size_t
264 crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
265 {
266 	EVP_CIPHER_CTX	ctx;
267 	uint8_t		iv[IV_SIZE];
268 	uint8_t		tag[GCM_TAG_SIZE];
269 	int		olen;
270 	int		len = 0;
271 	int		ret = 0;
272 
273 	/* out does not have enough room */
274 	if (outlen < inlen - sizeof tag + sizeof iv)
275 		return 0;
276 
277 	/* extract tag */
278 	memcpy(tag, in + inlen - sizeof tag, sizeof tag);
279 	inlen -= sizeof tag;
280 
281 	/* check version */
282 	if (*in != API_VERSION)
283 		return 0;
284 	in++;
285 	inlen--;
286 
287 	/* extract IV */
288 	memset(iv, 0, sizeof iv);
289 	memcpy(iv, in, sizeof iv);
290 	inlen -= sizeof iv;
291 	in += sizeof iv;
292 
293 	EVP_CIPHER_CTX_init(&ctx);
294 	EVP_DecryptInit_ex(&ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
295 
296 	/* set expected tag */
297 	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
298 
299 	/* decrypt buffer */
300 	if (!EVP_DecryptUpdate(&ctx, out, &olen, in, inlen))
301 		goto end;
302 	len += olen;
303 
304 	/* finalize, write last chunk if any and perform authentication check */
305 	if (!EVP_DecryptFinal_ex(&ctx, out + len, &olen))
306 		goto end;
307 	ret = len + olen;
308 
309 end:
310 	EVP_CIPHER_CTX_cleanup(&ctx);
311 	return ret;
312 }
313 
314 #if 0
315 int
316 main(int argc, char *argv[])
317 {
318 	if (argc != 3) {
319 		printf("usage: crypto <key> <buffer>\n");
320 		return 1;
321 	}
322 
323 	if (!crypto_setup(argv[1], strlen(argv[1]))) {
324 		printf("crypto_setup failed\n");
325 		return 1;
326 	}
327 
328 	{
329 		char            encbuffer[4096];
330 		size_t          enclen;
331 		char            decbuffer[4096];
332 		size_t          declen;
333 
334 		printf("encrypt/decrypt buffer: ");
335 		enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]),
336 					       encbuffer, sizeof encbuffer);
337 
338 		/* uncomment below to provoke integrity check failure */
339 		/*
340 		 * encbuffer[13] = 0x42;
341 		 * encbuffer[14] = 0x42;
342 		 * encbuffer[15] = 0x42;
343 		 * encbuffer[16] = 0x42;
344 		 */
345 
346 		declen = crypto_decrypt_buffer(encbuffer, enclen,
347 					       decbuffer, sizeof decbuffer);
348 		if (declen != 0 && !strncmp(argv[2], decbuffer, declen))
349 			printf("ok\n");
350 		else
351 			printf("nope\n");
352 	}
353 
354 	{
355 		FILE           *fpin;
356 		FILE           *fpout;
357 		printf("encrypt/decrypt file: ");
358 
359 		fpin = fopen("/etc/passwd", "r");
360 		fpout = fopen("/tmp/passwd.enc", "w");
361 		if (!crypto_encrypt_file(fpin, fpout)) {
362 			printf("encryption failed\n");
363 			return 1;
364 		}
365 		fclose(fpin);
366 		fclose(fpout);
367 
368 		/* uncomment below to provoke integrity check failure */
369 		/*
370 		 * fpin = fopen("/tmp/passwd.enc", "a");
371 		 * fprintf(fpin, "borken");
372 		 * fclose(fpin);
373 		 */
374 		fpin = fopen("/tmp/passwd.enc", "r");
375 		fpout = fopen("/tmp/passwd.dec", "w");
376 		if (!crypto_decrypt_file(fpin, fpout))
377 			printf("nope\n");
378 		else
379 			printf("ok\n");
380 		fclose(fpin);
381 		fclose(fpout);
382 	}
383 
384 
385 	return 0;
386 }
387 #endif
388