xref: /openbsd-src/usr.sbin/smtpd/crypto.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /* $OpenBSD: crypto.c,v 1.8 2019/06/28 13:32:50 deraadt 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) == -1)
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 	ctx = EVP_CIPHER_CTX_new();
93 	if (ctx == NULL)
94 		return 0;
95 
96 	EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
97 
98 	/* encrypt until end of file */
99 	while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in)) != 0) {
100 		if (!EVP_EncryptUpdate(ctx, obuf, &len, ibuf, r))
101 			goto end;
102 		if (len && (w = fwrite(obuf, len, 1, out)) != 1)
103 			goto end;
104 	}
105 	if (!feof(in))
106 		goto end;
107 
108 	/* finalize and write last chunk if any */
109 	if (!EVP_EncryptFinal_ex(ctx, obuf, &len))
110 		goto end;
111 	if (len && (w = fwrite(obuf, len, 1, out)) != 1)
112 		goto end;
113 
114 	/* get and append tag */
115 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
116 	if ((w = fwrite(tag, sizeof tag, 1, out)) != 1)
117 		goto end;
118 
119 	fflush(out);
120 	ret = 1;
121 
122 end:
123 	EVP_CIPHER_CTX_free(ctx);
124 	return ret;
125 }
126 
127 int
128 crypto_decrypt_file(FILE * in, FILE * out)
129 {
130 	EVP_CIPHER_CTX	*ctx;
131 	uint8_t		ibuf[CRYPTO_BUFFER_SIZE];
132 	uint8_t		obuf[CRYPTO_BUFFER_SIZE];
133 	uint8_t		iv[IV_SIZE];
134 	uint8_t		tag[GCM_TAG_SIZE];
135 	uint8_t		version;
136 	size_t		r, w;
137 	off_t		sz;
138 	int		len;
139 	int		ret = 0;
140 	struct stat	sb;
141 
142 	/* input file too small to be an encrypted file */
143 	if (fstat(fileno(in), &sb) == -1)
144 		return 0;
145 	if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv))
146 		return 0;
147 	sz = sb.st_size;
148 
149 	/* extract tag */
150 	if (fseek(in, -sizeof(tag), SEEK_END) == -1)
151 		return 0;
152 	if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag)
153 		return 0;
154 
155 	if (fseek(in, 0, SEEK_SET) == -1)
156 		return 0;
157 
158 	/* extract version */
159 	if ((r = fread(&version, 1, sizeof version, in)) != sizeof version)
160 		return 0;
161 	if (version != API_VERSION)
162 		return 0;
163 
164 	/* extract IV */
165 	memset(iv, 0, sizeof iv);
166 	if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv)
167 		return 0;
168 
169 	/* real ciphertext length */
170 	sz -= sizeof version;
171 	sz -= sizeof iv;
172 	sz -= sizeof tag;
173 
174 	ctx = EVP_CIPHER_CTX_new();
175 	if (ctx == NULL)
176 		return 0;
177 
178 	EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
179 
180 	/* set expected tag */
181 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
182 
183 	/* decrypt until end of ciphertext */
184 	while (sz) {
185 		if (sz > CRYPTO_BUFFER_SIZE)
186 			r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in);
187 		else
188 			r = fread(ibuf, 1, sz, in);
189 		if (!r)
190 			break;
191 		if (!EVP_DecryptUpdate(ctx, obuf, &len, ibuf, r))
192 			goto end;
193 		if (len && (w = fwrite(obuf, len, 1, out)) != 1)
194 			goto end;
195 		sz -= r;
196 	}
197 	if (ferror(in))
198 		goto end;
199 
200 	/* finalize, write last chunk if any and perform authentication check */
201 	if (!EVP_DecryptFinal_ex(ctx, obuf, &len))
202 		goto end;
203 	if (len && (w = fwrite(obuf, len, 1, out)) != 1)
204 		goto end;
205 
206 	fflush(out);
207 	ret = 1;
208 
209 end:
210 	EVP_CIPHER_CTX_free(ctx);
211 	return ret;
212 }
213 
214 size_t
215 crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
216 {
217 	EVP_CIPHER_CTX	*ctx;
218 	uint8_t		iv[IV_SIZE];
219 	uint8_t		tag[GCM_TAG_SIZE];
220 	uint8_t		version = API_VERSION;
221 	off_t		sz;
222 	int		olen;
223 	int		len = 0;
224 	int		ret = 0;
225 
226 	/* output buffer does not have enough room */
227 	if (outlen < inlen + sizeof version + sizeof tag + sizeof iv)
228 		return 0;
229 
230 	/* input should not exceed 64GB */
231 	sz = inlen;
232 	if (sz >= 0x1000000000LL)
233 		return 0;
234 
235 	/* prepend version */
236 	*out = version;
237 	len++;
238 
239 	/* generate IV */
240 	memset(iv, 0, sizeof iv);
241 	arc4random_buf(iv, sizeof iv);
242 	memcpy(out + len, iv, sizeof iv);
243 	len += sizeof iv;
244 
245 	ctx = EVP_CIPHER_CTX_new();
246 	if (ctx == NULL)
247 		return 0;
248 
249 	EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
250 
251 	/* encrypt buffer */
252 	if (!EVP_EncryptUpdate(ctx, out + len, &olen, in, inlen))
253 		goto end;
254 	len += olen;
255 
256 	/* finalize and write last chunk if any */
257 	if (!EVP_EncryptFinal_ex(ctx, out + len, &olen))
258 		goto end;
259 	len += olen;
260 
261 	/* get and append tag */
262 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
263 	memcpy(out + len, tag, sizeof tag);
264 	ret = len + sizeof tag;
265 
266 end:
267 	EVP_CIPHER_CTX_free(ctx);
268 	return ret;
269 }
270 
271 size_t
272 crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
273 {
274 	EVP_CIPHER_CTX	*ctx;
275 	uint8_t		iv[IV_SIZE];
276 	uint8_t		tag[GCM_TAG_SIZE];
277 	int		olen;
278 	int		len = 0;
279 	int		ret = 0;
280 
281 	/* out does not have enough room */
282 	if (outlen < inlen - sizeof tag + sizeof iv)
283 		return 0;
284 
285 	/* extract tag */
286 	memcpy(tag, in + inlen - sizeof tag, sizeof tag);
287 	inlen -= sizeof tag;
288 
289 	/* check version */
290 	if (*in != API_VERSION)
291 		return 0;
292 	in++;
293 	inlen--;
294 
295 	/* extract IV */
296 	memset(iv, 0, sizeof iv);
297 	memcpy(iv, in, sizeof iv);
298 	inlen -= sizeof iv;
299 	in += sizeof iv;
300 
301 	ctx = EVP_CIPHER_CTX_new();
302 	if (ctx == NULL)
303 		return 0;
304 
305 	EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
306 
307 	/* set expected tag */
308 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
309 
310 	/* decrypt buffer */
311 	if (!EVP_DecryptUpdate(ctx, out, &olen, in, inlen))
312 		goto end;
313 	len += olen;
314 
315 	/* finalize, write last chunk if any and perform authentication check */
316 	if (!EVP_DecryptFinal_ex(ctx, out + len, &olen))
317 		goto end;
318 	ret = len + olen;
319 
320 end:
321 	EVP_CIPHER_CTX_free(ctx);
322 	return ret;
323 }
324 
325 #if 0
326 int
327 main(int argc, char *argv[])
328 {
329 	if (argc != 3) {
330 		printf("usage: crypto <key> <buffer>\n");
331 		return 1;
332 	}
333 
334 	if (!crypto_setup(argv[1], strlen(argv[1]))) {
335 		printf("crypto_setup failed\n");
336 		return 1;
337 	}
338 
339 	{
340 		char            encbuffer[4096];
341 		size_t          enclen;
342 		char            decbuffer[4096];
343 		size_t          declen;
344 
345 		printf("encrypt/decrypt buffer: ");
346 		enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]),
347 					       encbuffer, sizeof encbuffer);
348 
349 		/* uncomment below to provoke integrity check failure */
350 		/*
351 		 * encbuffer[13] = 0x42;
352 		 * encbuffer[14] = 0x42;
353 		 * encbuffer[15] = 0x42;
354 		 * encbuffer[16] = 0x42;
355 		 */
356 
357 		declen = crypto_decrypt_buffer(encbuffer, enclen,
358 					       decbuffer, sizeof decbuffer);
359 		if (declen != 0 && !strncmp(argv[2], decbuffer, declen))
360 			printf("ok\n");
361 		else
362 			printf("nope\n");
363 	}
364 
365 	{
366 		FILE           *fpin;
367 		FILE           *fpout;
368 		printf("encrypt/decrypt file: ");
369 
370 		fpin = fopen("/etc/passwd", "r");
371 		fpout = fopen("/tmp/passwd.enc", "w");
372 		if (!crypto_encrypt_file(fpin, fpout)) {
373 			printf("encryption failed\n");
374 			return 1;
375 		}
376 		fclose(fpin);
377 		fclose(fpout);
378 
379 		/* uncomment below to provoke integrity check failure */
380 		/*
381 		 * fpin = fopen("/tmp/passwd.enc", "a");
382 		 * fprintf(fpin, "borken");
383 		 * fclose(fpin);
384 		 */
385 		fpin = fopen("/tmp/passwd.enc", "r");
386 		fpout = fopen("/tmp/passwd.dec", "w");
387 		if (!crypto_decrypt_file(fpin, fpout))
388 			printf("nope\n");
389 		else
390 			printf("ok\n");
391 		fclose(fpin);
392 		fclose(fpout);
393 	}
394 
395 
396 	return 0;
397 }
398 #endif
399