#include <sys/stat.h>
#include <openssl/evp.h>
#include <string.h>
#define CRYPTO_BUFFER_SIZE 16384
#define GCM_TAG_SIZE 16
#define IV_SIZE 12
#define KEY_SIZE 32
#define API_VERSION 1
int crypto_setup(const char *, size_t);
int crypto_encrypt_file(FILE *, FILE *);
int crypto_decrypt_file(FILE *, FILE *);
size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t);
size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t);
static struct crypto_ctx {
unsigned char key[KEY_SIZE];
} cp;
int
crypto_setup(const char *key, size_t len)
{
if (len != KEY_SIZE)
return 0;
memset(&cp, 0, sizeof cp);
memcpy(cp.key, key, sizeof cp.key);
return 1;
}
int
crypto_encrypt_file(FILE * in, FILE * out)
{
EVP_CIPHER_CTX *ctx;
uint8_t ibuf[CRYPTO_BUFFER_SIZE];
uint8_t obuf[CRYPTO_BUFFER_SIZE];
uint8_t iv[IV_SIZE];
uint8_t tag[GCM_TAG_SIZE];
uint8_t version = API_VERSION;
size_t r;
int len;
int ret = 0;
struct stat sb;
if (fstat(fileno(in), &sb) == -1)
return 0;
if (sb.st_size >= 0x1000000000LL)
return 0;
if (fwrite(&version, 1, sizeof version, out) != sizeof version)
return 0;
memset(iv, 0, sizeof iv);
arc4random_buf(iv, sizeof iv);
if (fwrite(iv, 1, sizeof iv, out) != sizeof iv)
return 0;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return 0;
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in)) != 0) {
if (!EVP_EncryptUpdate(ctx, obuf, &len, ibuf, r))
goto end;
if (len && fwrite(obuf, len, 1, out) != 1)
goto end;
}
if (!feof(in))
goto end;
if (!EVP_EncryptFinal_ex(ctx, obuf, &len))
goto end;
if (len && fwrite(obuf, len, 1, out) != 1)
goto end;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
if (fwrite(tag, sizeof tag, 1, out) != 1)
goto end;
fflush(out);
ret = 1;
end:
EVP_CIPHER_CTX_free(ctx);
return ret;
}
int
crypto_decrypt_file(FILE * in, FILE * out)
{
EVP_CIPHER_CTX *ctx;
uint8_t ibuf[CRYPTO_BUFFER_SIZE];
uint8_t obuf[CRYPTO_BUFFER_SIZE];
uint8_t iv[IV_SIZE];
uint8_t tag[GCM_TAG_SIZE];
uint8_t version;
size_t r;
off_t sz;
int len;
int ret = 0;
struct stat sb;
if (fstat(fileno(in), &sb) == -1)
return 0;
if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv))
return 0;
sz = sb.st_size;
if (fseek(in, -sizeof(tag), SEEK_END) == -1)
return 0;
if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag)
return 0;
if (fseek(in, 0, SEEK_SET) == -1)
return 0;
if ((r = fread(&version, 1, sizeof version, in)) != sizeof version)
return 0;
if (version != API_VERSION)
return 0;
memset(iv, 0, sizeof iv);
if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv)
return 0;
sz -= sizeof version;
sz -= sizeof iv;
sz -= sizeof tag;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return 0;
EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
while (sz) {
if (sz > CRYPTO_BUFFER_SIZE)
r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in);
else
r = fread(ibuf, 1, sz, in);
if (!r)
break;
if (!EVP_DecryptUpdate(ctx, obuf, &len, ibuf, r))
goto end;
if (len && fwrite(obuf, len, 1, out) != 1)
goto end;
sz -= r;
}
if (ferror(in))
goto end;
if (!EVP_DecryptFinal_ex(ctx, obuf, &len))
goto end;
if (len && fwrite(obuf, len, 1, out) != 1)
goto end;
fflush(out);
ret = 1;
end:
EVP_CIPHER_CTX_free(ctx);
return ret;
}
size_t
crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
{
EVP_CIPHER_CTX *ctx;
uint8_t iv[IV_SIZE];
uint8_t tag[GCM_TAG_SIZE];
uint8_t version = API_VERSION;
off_t sz;
int olen;
int len = 0;
int ret = 0;
if (outlen < inlen + sizeof version + sizeof tag + sizeof iv)
return 0;
sz = inlen;
if (sz >= 0x1000000000LL)
return 0;
*out = version;
len++;
memset(iv, 0, sizeof iv);
arc4random_buf(iv, sizeof iv);
memcpy(out + len, iv, sizeof iv);
len += sizeof iv;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return 0;
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
if (!EVP_EncryptUpdate(ctx, out + len, &olen, in, inlen))
goto end;
len += olen;
if (!EVP_EncryptFinal_ex(ctx, out + len, &olen))
goto end;
len += olen;
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
memcpy(out + len, tag, sizeof tag);
ret = len + sizeof tag;
end:
EVP_CIPHER_CTX_free(ctx);
return ret;
}
size_t
crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
{
EVP_CIPHER_CTX *ctx;
uint8_t iv[IV_SIZE];
uint8_t tag[GCM_TAG_SIZE];
int olen;
int len = 0;
int ret = 0;
if (outlen < inlen - sizeof tag + sizeof iv)
return 0;
memcpy(tag, in + inlen - sizeof tag, sizeof tag);
inlen -= sizeof tag;
if (*in != API_VERSION)
return 0;
in++;
inlen--;
memset(iv, 0, sizeof iv);
memcpy(iv, in, sizeof iv);
inlen -= sizeof iv;
in += sizeof iv;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return 0;
EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
if (!EVP_DecryptUpdate(ctx, out, &olen, in, inlen))
goto end;
len += olen;
if (!EVP_DecryptFinal_ex(ctx, out + len, &olen))
goto end;
ret = len + olen;
end:
EVP_CIPHER_CTX_free(ctx);
return ret;
}
#if 0
int
main(int argc, char *argv[])
{
if (argc != 3) {
printf("usage: crypto <key> <buffer>\n");
return 1;
}
if (!crypto_setup(argv[1], strlen(argv[1]))) {
printf("crypto_setup failed\n");
return 1;
}
{
char encbuffer[4096];
size_t enclen;
char decbuffer[4096];
size_t declen;
printf("encrypt/decrypt buffer: ");
enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]),
encbuffer, sizeof encbuffer);
declen = crypto_decrypt_buffer(encbuffer, enclen,
decbuffer, sizeof decbuffer);
if (declen != 0 && !strncmp(argv[2], decbuffer, declen))
printf("ok\n");
else
printf("nope\n");
}
{
FILE *fpin;
FILE *fpout;
printf("encrypt/decrypt file: ");
fpin = fopen("/etc/passwd", "r");
fpout = fopen("/tmp/passwd.enc", "w");
if (!crypto_encrypt_file(fpin, fpout)) {
printf("encryption failed\n");
return 1;
}
fclose(fpin);
fclose(fpout);
fpin = fopen("/tmp/passwd.enc", "r");
fpout = fopen("/tmp/passwd.dec", "w");
if (!crypto_decrypt_file(fpin, fpout))
printf("nope\n");
else
printf("ok\n");
fclose(fpin);
fclose(fpout);
}
return 0;
}
#endif