*/
#include <assert.h>
+#include <string.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <yaca/crypto.h>
+#include <yaca/encrypt.h>
#include <yaca/error.h>
+#include <yaca/key.h>
#include "internal.h"
-static const char *symmetric_algo_to_str(yaca_enc_algo_e algo)
+enum encrypt_op_type {
+ OP_ENCRYPT = 0,
+ OP_DECRYPT = 1
+};
+
+struct yaca_encrypt_ctx_s
+{
+ struct yaca_ctx_s ctx;
+
+ EVP_CIPHER_CTX *cipher_ctx;
+ enum encrypt_op_type op_type; /* Operation context was created for */
+};
+
+static struct yaca_encrypt_ctx_s *get_encrypt_ctx(const yaca_ctx_h ctx)
+{
+ if (ctx == YACA_CTX_NULL)
+ return NULL;
+
+ switch (ctx->type)
+ {
+ case YACA_CTX_ENCRYPT:
+ return (struct yaca_encrypt_ctx_s *)ctx;
+ default:
+ return NULL;
+ }
+}
+
+static void destroy_encrypt_ctx(const yaca_ctx_h ctx)
+{
+ struct yaca_encrypt_ctx_s *nc = get_encrypt_ctx(ctx);
+
+ if (nc == NULL)
+ return;
+
+ EVP_CIPHER_CTX_free(nc->cipher_ctx);
+ nc->cipher_ctx = NULL;
+}
+
+static int get_encrypt_output_length(const yaca_ctx_h ctx, size_t input_len)
+{
+ struct yaca_encrypt_ctx_s *nc = get_encrypt_ctx(ctx);
+ int block_size;
+
+ if (nc == NULL || nc->cipher_ctx == NULL)
+ return YACA_ERROR_INVALID_ARGUMENT;
+
+ block_size = EVP_CIPHER_CTX_block_size(nc->cipher_ctx);
+ if (block_size == 0)
+ return YACA_ERROR_OPENSSL_FAILURE; // TODO: extract openssl error here
+
+ if (input_len > 0)
+ return block_size + input_len - 1;
+ return block_size;
+}
+
+static const char *encrypt_algo_to_str(yaca_enc_algo_e algo)
{
switch(algo)
{
}
}
-int get_symmetric_algorithm(yaca_enc_algo_e algo,
- yaca_block_cipher_mode_e bcm,
- unsigned key_bits,
- const EVP_CIPHER **cipher)
+int get_encrypt_algorithm(yaca_enc_algo_e algo,
+ yaca_block_cipher_mode_e bcm,
+ unsigned key_bits,
+ const EVP_CIPHER **cipher)
{
char cipher_name[32];
- const char *algo_name = symmetric_algo_to_str(algo);
+ const char *algo_name = encrypt_algo_to_str(algo);
const char *bcm_name = bcm_to_str(bcm);
const EVP_CIPHER *lcipher;
int ret;
return 0;
}
+static int encrypt_init(yaca_ctx_h *ctx,
+ yaca_enc_algo_e algo,
+ yaca_block_cipher_mode_e bcm,
+ const yaca_key_h sym_key,
+ const yaca_key_h iv,
+ enum encrypt_op_type op_type)
+{
+ const struct yaca_key_simple_s *lkey;
+ const struct yaca_key_simple_s *liv;
+ struct yaca_encrypt_ctx_s *nc;
+ const EVP_CIPHER *cipher;
+ int key_bits;
+ int iv_bits;
+ int ret;
+
+ if (ctx == NULL || sym_key == YACA_KEY_NULL)
+ return YACA_ERROR_INVALID_ARGUMENT;
+
+ lkey = key_get_simple(sym_key);
+ if (lkey == NULL)
+ return YACA_ERROR_INVALID_ARGUMENT;
+
+ nc = yaca_malloc(sizeof(struct yaca_encrypt_ctx_s));
+ if (nc == NULL)
+ return YACA_ERROR_OUT_OF_MEMORY;
+
+ memset(nc, 0, sizeof(struct yaca_encrypt_ctx_s));
+
+ nc->ctx.type = YACA_CTX_ENCRYPT;
+ nc->ctx.ctx_destroy = destroy_encrypt_ctx;
+ nc->ctx.get_output_length = get_encrypt_output_length;
+ nc->op_type = op_type;
+
+ // TODO: handling of algorithms with variable key length
+ ret = yaca_key_get_length(sym_key);
+ if (ret < 0)
+ goto err_free;
+ key_bits = ret;
+
+ ret = get_encrypt_algorithm(algo, bcm, key_bits, &cipher);
+ if (ret != 0)
+ goto err_free;
+
+ ret = EVP_CIPHER_iv_length(cipher);
+ if (ret < 0)
+ goto err_free;
+
+ iv_bits = ret * 8;
+ if (iv_bits == 0 && iv != NULL) { /* 0 -> cipher doesn't use iv, but it was provided */
+ ret = YACA_ERROR_INVALID_ARGUMENT;
+ goto err_free;
+ }
+
+ liv = key_get_simple(iv);
+ if (ret != 0 && liv == NULL) { /* cipher requires iv, but none was provided */
+ ret = YACA_ERROR_INVALID_ARGUMENT;
+ goto err_free;
+ }
+
+ // TODO: handling of algorithms with variable IV length
+ if (iv_bits != yaca_key_get_length(iv)) { /* IV length doesn't match cipher */
+ ret = YACA_ERROR_INVALID_ARGUMENT;
+ goto err_free;
+ }
+
+ nc->cipher_ctx = EVP_CIPHER_CTX_new();
+ if (nc->cipher_ctx == NULL) {
+ ret = YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret);
+ goto err_free;
+ }
+
+ switch (op_type) {
+ case OP_ENCRYPT:
+ ret = EVP_EncryptInit(nc->cipher_ctx, cipher,
+ (unsigned char*)lkey->d,
+ (unsigned char*)liv->d);
+ break;
+ case OP_DECRYPT:
+ ret = EVP_DecryptInit(nc->cipher_ctx, cipher,
+ (unsigned char*)lkey->d,
+ (unsigned char*)liv->d);
+ break;
+ default:
+ ret = YACA_ERROR_INVALID_ARGUMENT;
+ goto err_ctx;
+ }
+
+ if (ret != 1) {
+ ret = YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret);
+ goto err_ctx;
+ }
+
+ *ctx = (yaca_ctx_h)nc;
+ return 0;
+
+err_ctx:
+ EVP_CIPHER_CTX_free(nc->cipher_ctx);
+err_free:
+ yaca_free(nc);
+ return ret;
+}
+
+static int encrypt_update(yaca_ctx_h ctx,
+ const unsigned char *input,
+ size_t input_len,
+ unsigned char *output,
+ size_t *output_len,
+ enum encrypt_op_type op_type)
+{
+ struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx);
+ int ret;
+ int loutput_len;
+
+ if (c == NULL || input == NULL || input_len == 0 ||
+ output == NULL || output_len == NULL || op_type != c->op_type)
+ return YACA_ERROR_INVALID_ARGUMENT;
+
+ loutput_len = *output_len;
+
+ switch (op_type) {
+ case OP_ENCRYPT:
+ ret = EVP_EncryptUpdate(c->cipher_ctx, output, &loutput_len,
+ input, input_len);
+ break;
+ case OP_DECRYPT:
+ ret = EVP_DecryptUpdate(c->cipher_ctx, output, &loutput_len,
+ input, input_len);
+ break;
+ default:
+ return YACA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (ret != 1)
+ return YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret);
+
+ *output_len = loutput_len;
+ return 0;
+}
+
+static int encrypt_final(yaca_ctx_h ctx,
+ unsigned char *output,
+ size_t *output_len,
+ enum encrypt_op_type op_type)
+{
+ struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx);
+ int ret;
+ int loutput_len;
+
+ if (c == NULL || output == NULL || output_len == NULL ||
+ op_type != c->op_type)
+ return YACA_ERROR_INVALID_ARGUMENT;
+
+ loutput_len = *output_len;
+
+ switch (op_type) {
+ case OP_ENCRYPT:
+ ret = EVP_EncryptFinal(c->cipher_ctx, output, &loutput_len);
+ break;
+ case OP_DECRYPT:
+ ret = EVP_DecryptFinal(c->cipher_ctx, output, &loutput_len);
+ break;
+ default:
+ return YACA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (ret != 1)
+ return YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret);
+
+ *output_len = loutput_len;
+ return 0;
+}
+
API int yaca_get_iv_bits(yaca_enc_algo_e algo,
yaca_block_cipher_mode_e bcm,
size_t key_bits)
const EVP_CIPHER *cipher;
int ret;
- ret = get_symmetric_algorithm(algo, bcm, key_bits, &cipher);
+ ret = get_encrypt_algorithm(algo, bcm, key_bits, &cipher);
if (ret < 0)
return ret;
const yaca_key_h sym_key,
const yaca_key_h iv)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_init(ctx, algo, bcm, sym_key, iv, OP_ENCRYPT);
}
API int yaca_encrypt_update(yaca_ctx_h ctx,
char *cipher,
size_t *cipher_len)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_update(ctx, (const unsigned char*)plain, plain_len,
+ (unsigned char*)cipher, cipher_len, OP_ENCRYPT);
}
API int yaca_encrypt_final(yaca_ctx_h ctx,
char *cipher,
size_t *cipher_len)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_final(ctx, (unsigned char*)cipher,
+ cipher_len, OP_ENCRYPT);
}
API int yaca_decrypt_init(yaca_ctx_h *ctx,
const yaca_key_h sym_key,
const yaca_key_h iv)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_init(ctx, algo, bcm, sym_key, iv, OP_DECRYPT);
}
API int yaca_decrypt_update(yaca_ctx_h ctx,
char *plain,
size_t *plain_len)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_update(ctx, (const unsigned char*)cipher, cipher_len,
+ (unsigned char*)plain, plain_len, OP_DECRYPT);
}
API int yaca_decrypt_final(yaca_ctx_h ctx,
char *plain,
size_t *plain_len)
{
- return YACA_ERROR_NOT_IMPLEMENTED;
+ return encrypt_final(ctx,(unsigned char*)plain, plain_len,
+ OP_DECRYPT);
}
API int yaca_seal_init(yaca_ctx_h *ctx,