From: Michael Tremer Date: Sun, 20 Apr 2025 13:56:48 +0000 (+0000) Subject: base64: Replace the encoder/decoder with low-level functions X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a56cfb97799f445f016e8433f09fbfa85098a3f9;p=pakfire.git base64: Replace the encoder/decoder with low-level functions The BIO interface is large and very well suited to encode larger messages. However, we only have very small messages to encode and therefore will spend a long time on setting up and destroying the BIO interface all of the time. These low-level functions are a lot faster and easier to set up. Signed-off-by: Michael Tremer --- diff --git a/src/pakfire/base64.c b/src/pakfire/base64.c index 78f8eb05..7c162a81 100644 --- a/src/pakfire/base64.c +++ b/src/pakfire/base64.c @@ -18,171 +18,101 @@ # # #############################################################################*/ +#include +#include +#include +#include + // OpenSSL -#include -#include #include -#include #include -#include -#define OPENSSL_ERROR_MAX 1024 +#define BASE64_ENCODED_LEN(n) (4 * ((n + 2) / 3)) +#define BASE64_DECODED_LEN(n) ((n / 4) * 3) -int pakfire_b64encode(struct pakfire_ctx* ctx, char** output, - const void* buffer, const size_t length) { - char error[OPENSSL_ERROR_MAX]; - BIO* b64 = NULL; - BIO* bio = NULL; - const char* p = NULL; +int pakfire_b64encode(char** output, const unsigned char* input, const size_t length) { int r; - // Initialize the base64 encoder - b64 = BIO_new(BIO_f_base64()); - if (!b64) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); - - ERROR(ctx, "Could not initialize the base64 encoder: %s\n", error); - r = 1; - goto ERROR; - } - - // Initialize a memory buffer - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); - - ERROR(ctx, "Could not initialize memory buffer: %s\n", error); - r = 1; - goto ERROR; - } - - // Connect both things - bio = BIO_push(b64, bio); + // Check inputs + if (!output || !input || !length) + return -EINVAL; - // Disable line breaks and a trailing newline - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + // Determine the length of the output + size_t l = BASE64_ENCODED_LEN(length); - // Write the input - r = BIO_write(bio, buffer, length); - if (r < 1) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + // Allocate an output buffer + char* buffer = malloc(l + 1); + if (!buffer) + return -errno; - ERROR(ctx, "%s\n", error); - r = 1; + // Encode the block + r = EVP_EncodeBlock((unsigned char*)buffer, input, length); + if (r < 0) { + r = -EBADMSG; goto ERROR; } - // Flush - BIO_flush(bio); + // Terminate the buffer + buffer[l] = '\0'; - // Fetch a pointer to the output and determine its length - const size_t l = BIO_get_mem_data(bio, &p); - - // Copy the output to the heap - *output = strndup(p, l); - if (!*output) { - ERROR(ctx, "Could not copy base64 encoded string to heap: %m\n"); - r = 1; - goto ERROR; - } - - // Success - r = 0; + // Return the buffer + *output = buffer; + return 0; ERROR: - if (bio) - BIO_free_all(bio); + if (buffer) + free(buffer); return r; } -int pakfire_b64decode(struct pakfire_ctx* ctx, void** output, size_t* length, - const char* buffer) { - char error[OPENSSL_ERROR_MAX]; - char chunk[1024]; - BIO* b64 = NULL; - BIO* bio = NULL; - char* p = NULL; +int pakfire_b64decode(unsigned char** output, size_t* length, const char* input) { + unsigned char* buffer = NULL; + size_t block; int r; - // Reset length - *length = 0; + // Check inputs + if (!input || !output || !length) + return -EINVAL; - // Initialize the base64 decoder - b64 = BIO_new(BIO_f_base64()); - if (!b64) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + // Determine the length of the input + size_t l = strlen(input); - ERROR(ctx, "Could not initialize the base64 decoder: %s\n", error); - r = 1; - goto ERROR; - } + // Remove any trailing whitespace + while (l >= 1 && isspace(input[l - 1])) + l--; - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + // Determine the length of the block + block = BASE64_DECODED_LEN(l); + if (!block) + return -EINVAL; - // Initialize a memory buffer - bio = BIO_new_mem_buf(buffer, strlen(buffer)); - if (!bio) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + // Allocate the output buffer + buffer = malloc(block); + if (!buffer) + return -errno; - ERROR(ctx, "Could not initialize memory buffer: %s\n", error); - r = 1; + // Decode the block + r = EVP_DecodeBlock(buffer, (const unsigned char*)input, l); + if (r < 0) { + r = -EBADMSG; goto ERROR; } - // Connect both things - bio = BIO_push(b64, bio); - - for (;;) { - // Read a chunk of data - ssize_t bytes_read = BIO_read(bio, chunk, sizeof(chunk)); - - // Handle any errors - if (bytes_read < 0) { - ERR_error_string_n(ERR_get_error(), error, sizeof(error)); - - ERROR(ctx, "Could not read data: %s\n", error); - r = -EINVAL; - goto ERROR; - - // Break if no more data could be read - } else if (bytes_read == 0) { - break; - - // Handle the chunk - } else { - // Update total length of data - *length += bytes_read; - - // Allocate an output buffer - p = pakfire_realloc(p, *length); - if (!p) { - ERROR(ctx, "Could not allocate buffer: %m\n"); - r = -errno; - goto ERROR; - } - - // Copy the chunk - memcpy(p + *length - bytes_read, chunk, bytes_read); - } - } - - // Set output pointer - *output = p; + // Remove the padding + while (l > 0 && input[--l] == '=') + r--; - // Success! - r = 0; + // Return the output + *output = buffer; + *length = r; - goto CLEANUP; + return 0; ERROR: - if (p) - free(p); - -CLEANUP: - if (bio) - BIO_free_all(bio); + if (buffer) + free(buffer); return r; } diff --git a/src/pakfire/base64.h b/src/pakfire/base64.h index bd56ce2a..b114c79d 100644 --- a/src/pakfire/base64.h +++ b/src/pakfire/base64.h @@ -21,12 +21,10 @@ #ifndef PAKFIRE_BASE64_H #define PAKFIRE_BASE64_H -#include +#include -int pakfire_b64encode( - struct pakfire_ctx* ctx, char** output, const void* buffer, const size_t length); +int pakfire_b64encode(char** output, const unsigned char* input, const size_t length); -int pakfire_b64decode( - struct pakfire_ctx* ctx, void** output, size_t* length, const char* buffer); +int pakfire_b64decode(unsigned char** output, size_t* length, const char* input); #endif /* PAKFIRE_BASE64_H */ diff --git a/src/pakfire/key.c b/src/pakfire/key.c index 4f4fd0c3..db945dec 100644 --- a/src/pakfire/key.c +++ b/src/pakfire/key.c @@ -390,7 +390,7 @@ ERROR: int pakfire_key_import(struct pakfire_key** key, struct pakfire_ctx* ctx, FILE* f) { - void* buffer = NULL; + unsigned char* buffer = NULL; size_t buffer_length = 0; int r; @@ -430,10 +430,9 @@ int pakfire_key_import(struct pakfire_key** key, // The second line should hold the key case 2: // Decode the key - r = pakfire_b64decode(ctx, &buffer, &buffer_length, line); - if (r) { - ERROR(ctx, "Could not decode the key: %m\n"); - r = -EINVAL; + r = pakfire_b64decode(&buffer, &buffer_length, line); + if (r < 0) { + ERROR(ctx, "Could not decode the key: %s\n", strerror(-r)); goto ERROR; } @@ -845,7 +844,7 @@ int pakfire_key_sign_string(struct pakfire_key* key, } // Encode the signature to base64 - r = pakfire_b64encode(key->ctx, &buffer, &signature, sizeof(signature)); + r = pakfire_b64encode(&buffer, (const unsigned char*)&signature, sizeof(signature)); if (r < 0) goto ERROR; @@ -892,7 +891,7 @@ ERROR: static int pakfire_key_read_signature(struct pakfire_key* key, struct pakfire_key_signature* signature, FILE* f) { - void* buffer = NULL; + unsigned char* buffer = NULL; size_t buffer_length = 0; int r; @@ -913,10 +912,9 @@ static int pakfire_key_read_signature(struct pakfire_key* key, continue; // Decode the signature - r = pakfire_b64decode(key->ctx, &buffer, &buffer_length, line); - if (r) { - ERROR(key->ctx, "Could not decode the signature: %m\n"); - r = -EINVAL; + r = pakfire_b64decode(&buffer, &buffer_length, line); + if (r < 0) { + ERROR(key->ctx, "Could not decode the signature: %s\n", strerror(-r)); goto ERROR; } diff --git a/tests/libpakfire/util.c b/tests/libpakfire/util.c index e4c4e21a..ff710304 100644 --- a/tests/libpakfire/util.c +++ b/tests/libpakfire/util.c @@ -55,19 +55,19 @@ FAIL: static int test_base64(const struct test* t) { int r = EXIT_FAILURE; - const char data[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + const unsigned char data[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + unsigned char* output = NULL; char* base64 = NULL; - void* output = NULL; size_t length = 0; // Encode data as base64 - ASSERT_SUCCESS(pakfire_b64encode(t->ctx, &base64, data, sizeof(data))); + ASSERT_SUCCESS(pakfire_b64encode(&base64, data, sizeof(data))); // Print the encoded data printf("%s\n", base64); // Decode the data - ASSERT_SUCCESS(pakfire_b64decode(t->ctx, &output, &length, base64)); + ASSERT_SUCCESS(pakfire_b64decode(&output, &length, base64)); // Print the decoded data printf("%.*s\n", (int)length, (char*)output);