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