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