diff --git a/components/crypt.c b/components/crypt.c new file mode 100644 index 0000000..c6ff6d1 --- /dev/null +++ b/components/crypt.c @@ -0,0 +1,265 @@ +/****************************************************************************** + * + * 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; +} + diff --git a/components/crypt.h b/components/crypt.h new file mode 100644 index 0000000..ea76776 --- /dev/null +++ b/components/crypt.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * Copyright (c) 2017-2019 by Löwenware Ltd + * Please, refer LICENSE file for legal information + * + ******************************************************************************/ + +/** + * @file crypt.h + * @author Ilja Kartašov + * @brief + * + * @see https://lowenware.com/ + */ + +#ifndef AX_CRYPT_H_F18E0303_66D9_4793_A902_230BA0A0A46F +#define AX_CRYPT_H_F18E0303_66D9_4793_A902_230BA0A0A46F + +#include +#include +#include +#include + +#define AX_CRYPT_KEY_SIZE 32 +#define AX_CRYPT_SALT_SIZE 8 + +struct ax_crypt { + EVP_CIPHER_CTX *e_ctx; + EVP_CIPHER_CTX *d_ctx; +}; + + +typedef struct ax_crypt * AxCrypt; + + +AislStatus +ax_crypt_init(AxSession crypt, const char *key_file); + + +void +ax_crypt_release(AxSession crypt); + + +/* encrypt */ +int +ax_crypt_write(AxSession crypt, char **out, const char *input, int in_len); + + +/* decrypt */ +int +ax_crypt_read(AxSession crypt, char **out, const char *input, int in_len); + + +/* encrypt and convert to BASE64 */ +int +ax_crypt_encode(AxSession crypt, char **out, const char *input, int in_len); + + +/* decrypt from BASE64 */ +int +ax_crypt_decode(AxSession crypt, char **out, const char *input, int in_len); + +#endif /* !AX_CRYPT_H */