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
34int	crypto_setup(const char *, size_t);
35int	crypto_encrypt_file(FILE *, FILE *);
36int	crypto_decrypt_file(FILE *, FILE *);
37size_t	crypto_encrypt_buffer(const char *, size_t, char *, size_t);
38size_t	crypto_decrypt_buffer(const char *, size_t, char *, size_t);
39
40static struct crypto_ctx {
41	unsigned char  		key[KEY_SIZE];
42} cp;
43
44int
45crypto_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
58int
59crypto_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
118end:
119	EVP_CIPHER_CTX_free(ctx);
120	return ret;
121}
122
123int
124crypto_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
205end:
206	EVP_CIPHER_CTX_free(ctx);
207	return ret;
208}
209
210size_t
211crypto_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
262end:
263	EVP_CIPHER_CTX_free(ctx);
264	return ret;
265}
266
267size_t
268crypto_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
316end:
317	EVP_CIPHER_CTX_free(ctx);
318	return ret;
319}
320
321#if 0
322int
323main(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