| /* SPDX-License-Identifier: GPL-2.0-or-later |
| * Copyright (C) 2023 Eneas Ulir de Queiroz |
| */ |
| |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include "uencrypt.h" |
| |
| #if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */ |
| static inline mbedtls_cipher_mode_t mbedtls_cipher_info_get_mode( |
| const mbedtls_cipher_info_t *info) |
| { |
| if (info == NULL) { |
| return MBEDTLS_MODE_NONE; |
| } else { |
| return info->mode; |
| } |
| } |
| |
| static inline size_t mbedtls_cipher_info_get_key_bitlen( |
| const mbedtls_cipher_info_t *info) |
| { |
| if (info == NULL) { |
| return 0; |
| } else { |
| return info->key_bitlen; |
| } |
| } |
| |
| static inline const char *mbedtls_cipher_info_get_name( |
| const mbedtls_cipher_info_t *info) |
| { |
| if (info == NULL) { |
| return NULL; |
| } else { |
| return info->name; |
| } |
| } |
| |
| static inline size_t mbedtls_cipher_info_get_iv_size( |
| const mbedtls_cipher_info_t *info) |
| { |
| if (info == NULL) { |
| return 0; |
| } |
| |
| return info->iv_size; |
| } |
| |
| static inline size_t mbedtls_cipher_info_get_block_size( |
| const mbedtls_cipher_info_t *info) |
| { |
| if (info == NULL) { |
| return 0; |
| } |
| |
| return info->block_size; |
| } |
| #endif |
| |
| unsigned char *hexstr2buf(const char *str, long *len) |
| { |
| unsigned char *buf; |
| long inlen = strlen(str); |
| |
| *len = 0; |
| if (inlen % 2) |
| return NULL; |
| |
| *len = inlen >> 1; |
| buf = malloc(*len); |
| for (long x = 0; x < *len; x++) |
| sscanf(str + x * 2, "%2hhx", buf + x); |
| return buf; |
| } |
| |
| const cipher_t *get_default_cipher(void) |
| { |
| return mbedtls_cipher_info_from_type (MBEDTLS_CIPHER_AES_128_CBC); |
| } |
| |
| static char* upperstr(char *str) { |
| for (char *s = str; *s; s++) |
| *s = toupper((unsigned char) *s); |
| return str; |
| } |
| |
| const cipher_t *get_cipher_or_print_error(char *name) |
| { |
| const mbedtls_cipher_info_t *cipher; |
| |
| cipher = mbedtls_cipher_info_from_string(upperstr(name)); |
| if (cipher) |
| return cipher; |
| |
| fprintf(stderr, "Error: invalid cipher: %s.\n", name); |
| fprintf(stderr, "Supported ciphers: \n"); |
| for (const int *list = mbedtls_cipher_list(); *list; list++) { |
| cipher = mbedtls_cipher_info_from_type(*list); |
| if (!cipher) |
| continue; |
| fprintf(stderr, "\t%s\n", mbedtls_cipher_info_get_name(cipher)); |
| } |
| return NULL; |
| } |
| |
| int get_cipher_ivsize(const cipher_t *cipher) |
| { |
| const mbedtls_cipher_info_t *c = cipher; |
| |
| return mbedtls_cipher_info_get_iv_size(c); |
| } |
| |
| int get_cipher_keysize(const cipher_t *cipher) |
| { |
| const mbedtls_cipher_info_t *c = cipher; |
| |
| return mbedtls_cipher_info_get_key_bitlen(c) >> 3; |
| } |
| |
| ctx_t *create_ctx(const cipher_t *cipher, const unsigned char *key, |
| const unsigned char *iv, int enc, int padding) |
| { |
| mbedtls_cipher_context_t *ctx; |
| const mbedtls_cipher_info_t *cipher_info=cipher; |
| int ret; |
| |
| ctx = malloc(sizeof (mbedtls_cipher_context_t)); |
| if (!ctx) { |
| fprintf (stderr, "Error: create_ctx: out of memory.\n"); |
| return NULL; |
| } |
| |
| mbedtls_cipher_init(ctx); |
| ret = mbedtls_cipher_setup(ctx, cipher_info); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_setup: %d\n", ret); |
| goto abort; |
| } |
| ret = mbedtls_cipher_setkey(ctx, key, |
| (int) mbedtls_cipher_get_key_bitlen(ctx), |
| enc ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_setkey: %d\n", ret); |
| goto abort; |
| } |
| if (iv) { |
| ret = mbedtls_cipher_set_iv(ctx, iv, mbedtls_cipher_get_iv_size(ctx)); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_set_iv: %d\n", ret); |
| goto abort; |
| } |
| } |
| |
| if (mbedtls_cipher_info_get_mode(cipher_info) == MBEDTLS_MODE_CBC) { |
| ret = mbedtls_cipher_set_padding_mode(ctx, padding ? |
| MBEDTLS_PADDING_PKCS7 : |
| MBEDTLS_PADDING_NONE); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_set_padding_mode: %d\n", |
| ret); |
| goto abort; |
| } |
| } else { |
| if (mbedtls_cipher_info_get_block_size(cipher_info) > 1 && padding) { |
| fprintf(stderr, |
| "Error: mbedTLS only allows padding with CBC ciphers.\n"); |
| goto abort; |
| } |
| } |
| |
| ret = mbedtls_cipher_reset(ctx); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_reset: %d\n", ret); |
| goto abort; |
| } |
| return ctx; |
| |
| abort: |
| free_ctx(ctx); |
| return NULL; |
| } |
| |
| int do_crypt(FILE *infile, FILE *outfile, ctx_t *ctx) |
| { |
| unsigned char inbuf[CRYPT_BUF_SIZE]; |
| unsigned char outbuf[CRYPT_BUF_SIZE + MBEDTLS_MAX_BLOCK_LENGTH]; |
| size_t inlen, outlen, step; |
| int ret; |
| |
| if (mbedtls_cipher_get_cipher_mode(ctx) == MBEDTLS_MODE_ECB) { |
| step = mbedtls_cipher_get_block_size(ctx); |
| if (step > CRYPT_BUF_SIZE) { |
| step = CRYPT_BUF_SIZE; |
| } |
| } else { |
| step = CRYPT_BUF_SIZE; |
| } |
| |
| for (;;) { |
| inlen = fread(inbuf, 1, step, infile); |
| if (inlen <= 0) |
| break; |
| ret = mbedtls_cipher_update(ctx, inbuf, inlen, outbuf, &outlen); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_update: %d\n", ret); |
| return ret; |
| } |
| ret = fwrite(outbuf, 1, outlen, outfile); |
| if (ret != outlen) { |
| fprintf(stderr, "Error: cipher_update short write.\n"); |
| return ret - outlen; |
| } |
| } |
| ret = mbedtls_cipher_finish(ctx, outbuf, &outlen); |
| if (ret) { |
| fprintf(stderr, "Error: mbedtls_cipher_finish: %d\n", ret); |
| return ret; |
| } |
| ret = fwrite(outbuf, 1, outlen, outfile); |
| if (ret != outlen) { |
| fprintf(stderr, "Error: cipher_finish short write.\n"); |
| return ret - outlen; |
| } |
| |
| return 0; |
| } |
| |
| void free_ctx(ctx_t *ctx) |
| { |
| if (ctx) { |
| mbedtls_cipher_free(ctx); |
| free(ctx); |
| } |
| } |