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