GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../crypto.c Lines: 0 130 0.0 %
Date: 2017-11-07 Branches: 0 92 0.0 %

Line Branch Exec Source
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