/****************************************************************************** * * Copyright (c) 2017-2019 by Löwenware Ltd * Please, refer LICENSE file for legal information * ******************************************************************************/ /** * @file crypt.c * @author Ilja Kartašov * @brief * * @see https://lowenware.com/ */ #include #include "crypt.h" static AislStatus ax_crypt__read_key_file(const char *key_file, unsigned char *key, unsigned char *salt) { AislStatus result = AISL_SYSCALL_ERROR; int i; FILE *f; char buff[2*(AX_CRYPT_KEY_SIZE + AX_CRYPT_SALT_SIZE) + 1], char hex[2 + 1], *ptr = buff, *ep = NULL; if (!(f = fopen(key_file))) { goto finally; } if (fread(buff, 1, sizeof (buff), f) != sizeof (buff)) { goto e_input; } hex[2] = 0; for (i = 0; i < AX_CRYPT_SALT_SIZE; i++) { memcpy(hex, ptr, 2); salt[i] = strtol(hex, &ep, 16); if (ep != &hex[2]) { goto e_input; } ptr += 2; } if (*(ptr++) != '$') { goto e_input; } for (i = 0; i < AX_CRYPT_KEY_SIZE; i++) { memcpy(hex, ptr, 2); key[i] = strtol(hex, &ep, 16); if (ep != &hex[2]) { goto e_input; } ptr += 2; } result = AISL_SUCCESS; goto cleanup; e_input: result = AISL_INPUT_ERROR; cleanup: fclose(f); finally: return result; } static AislStatus ax_crypt__write_key_file(const char *key_file, unsigned char *key, unsigned char *salt) { AislStatus result = AISL_SYSCALL_ERROR; int i; FILE *f; if (!(f = fopen(key_file))) { goto finally; } for (i = 0; i < AX_CRYPT_SALT_SIZE; i++) { if (fprintf(f, "%02x", salt[i] & 0xFF) < 0) { goto cleanup; } } if (fputc('$', f) == EOF) { goto cleanup; } for (i = 0; i < AX_CRYPT_KEY_SIZE; i++) { if (fprintf(f, "%02x", key[i] & 0xFF) < 0) { goto cleanup; } } result = AISL_SUCCESS; goto cleanup; cleanup: fclose(f); chmod(key_file, S_IRUSR); finally: return result; } AislStatus ax_crypt_init(AxSession crypt, const char *key_file) { AislStatus result; unsigned char key[AX_CRYPT_KEY_SIZE], salt[AX_CRYPT_SALT_SIZE], evp_key[AX_CRYPT_KEY_SIZE], iv[AX_CRYPT_KEY_SIZE]; if (cstuff_file_exists(key_file)) { result = ax_crypt__read_key_file(key_file, key, salt); } else { RAND_bytes(key, sizeof (key)); RAND_bytes(salt, sizeof (salt)); result = ax_crypt__write_key_file(key_file, key, salt); } if (result != AISL_SUCCESS) { return result; } if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key, sizeof (key), 5, key, iv)) { return AISL_SYSCALL_ERROR; } if ((crypt->e_ctx = EVP_CIPHER_CTX_new())) { EVP_CIPHER_CTX_init(crypt->e_ctx); EVP_EncryptInit_ex(crypt->e_ctx, EVP_aes_256_cbc(), NULL, key, iv); if ((crypt->d_ctx = EVP_CIPHER_CTX_new())) { EVP_CIPHER_CTX_init(crypt->d_ctx); EVP_DecryptInit_ex(crypt->d_ctx, EVP_aes_256_cbc(), NULL, key, iv); return result; } else { EVP_CIPHER_CTX_free(crypt->e_ctx); } } return AISL_MALLOC_ERROR; } void ax_crypt_release(AxSession crypt) { if (crypt->e_ctx) { EVP_CIPHER_CTX_free(crypt->e_ctx); } if (crypt->d_ctx) { EVP_CIPHER_CTX_free(crypt->d_ctx); } } int ax_crypt_write(AxSession crypt, char **p_out, const char *input, int in_len) { int result = in_len, l; unsigned char *out; EVP_CIPHER_CTX *e_ctx = crypt->e_ctx; if (!(out = calloc(result + EVP_MAX_BLOCK_LENGTH, sizeof (char)))) { return -1; } EVP_EncryptInit_ex(e_ctx, NULL, NULL, NULL, NULL); EVP_EncryptUpdate(e_ctx, out, &result, (const unsigned char *)input, in_len); EVP_EncryptFinal_ex(e_ctx, &out[result], &l); *p_out = (char *)out; return result + l; } int ax_crypt_read(AxSession crypt, char **p_out, const char *input, int in_len) { int result = in_len, l; unsigned char *out; EVP_CIPHER_CTX *d_ctx = crypt->d_ctx; if (!(out = malloc(in_len + EVP_MAX_BLOCK_LENGTH))) { return -1; } EVP_DecryptInit_ex(d_ctx, NULL, NULL, NULL, NULL); EVP_DecryptUpdate(d_ctx, out, &result, input, in_len); EVP_DecryptFinal_ex(d_ctx, &out[result], &l); *p_out = (char *)out; return result + l; } int ax_crypt_encode(AxSession crypt, char **p_out, const char *input, int in_len) { int result = -1; BIO *bio, *b64; BUF_MEM *bptr; unsigned char *out, *cipher; if ((in_len = ax_crypt_write(crypt, &cipher, input, in_len)) != -1) { if ((b64 = BIO_new( BIO_f_base64()))) { if ((bio = BIO_new( BIO_s_mem()))) { if ((b64 = BIO_push(b64, bio))) { BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_write(b64, cipher, in_len); BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); result = bptr->length; if ((out = malloc(result + 1))) { memcpy(out, bptr->data, result); out[result] = '\0'; *p_out = (char *)out; } else { result = -1; } } } BIO_free_all(b64); } free(cipher); } return result; } int ax_crypt_decode(AxSession crypt, char **p_out, const char *input, int in_len) { int result = -1; BIO *bio, *b64; BUF_MEM *bptr; unsigned char *out, *cipher; if ((cipher = malloc(in_len * sizeof (unsigned char)))) { if ((bio = BIO_new_mem_buf((void*)input, in_len))) { if ((b64 = BIO_new(BIO_f_base64()))) { if ((bio = BIO_push(b64, bio))) { BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); in_len = BIO_read(bio, cipher, in_len); result = ax_crypt_read(crypt, p_out, cipher, in_len); } } BIO_free_all(bio); } free(cipher); } return result; }