crypto.c revision 1.9
1/* $OpenBSD: crypto.c,v 1.9 2021/01/23 16:11:11 rob 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
38int	crypto_setup(const char *, size_t);
39int	crypto_encrypt_file(FILE *, FILE *);
40int	crypto_decrypt_file(FILE *, FILE *);
41size_t	crypto_encrypt_buffer(const char *, size_t, char *, size_t);
42size_t	crypto_decrypt_buffer(const char *, size_t, char *, size_t);
43
44static struct crypto_ctx {
45	unsigned char  		key[KEY_SIZE];
46} cp;
47
48int
49crypto_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
62int
63crypto_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;
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 (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 (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 && 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 && 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 (fwrite(tag, sizeof tag, 1, out) != 1)
117		goto end;
118
119	fflush(out);
120	ret = 1;
121
122end:
123	EVP_CIPHER_CTX_free(ctx);
124	return ret;
125}
126
127int
128crypto_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;
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 && 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 && fwrite(obuf, len, 1, out) != 1)
204		goto end;
205
206	fflush(out);
207	ret = 1;
208
209end:
210	EVP_CIPHER_CTX_free(ctx);
211	return ret;
212}
213
214size_t
215crypto_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
266end:
267	EVP_CIPHER_CTX_free(ctx);
268	return ret;
269}
270
271size_t
272crypto_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
320end:
321	EVP_CIPHER_CTX_free(ctx);
322	return ret;
323}
324
325#if 0
326int
327main(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