]> git.ipfire.org Git - pakfire.git/commitdiff
base64: Replace the encoder/decoder with low-level functions
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 20 Apr 2025 13:56:48 +0000 (13:56 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 20 Apr 2025 13:56:48 +0000 (13:56 +0000)
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 <michael.tremer@ipfire.org>
src/pakfire/base64.c
src/pakfire/base64.h
src/pakfire/key.c
tests/libpakfire/util.c

index 78f8eb05a03275dad4158b0c3baa61355c9565f0..7c162a8110262aafb3cf8f5db3921707e6b1f4e8 100644 (file)
 #                                                                             #
 #############################################################################*/
 
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
 // OpenSSL
-#include <openssl/bio.h>
-#include <openssl/err.h>
 #include <openssl/evp.h>
 
-#include <pakfire/ctx.h>
 #include <pakfire/base64.h>
-#include <pakfire/util.h>
 
-#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;
 }
index bd56ce2a432923332455206e644c5dd41f372512..b114c79d19c8de7c28a39261503af5c02bd61691 100644 (file)
 #ifndef PAKFIRE_BASE64_H
 #define PAKFIRE_BASE64_H
 
-#include <pakfire/ctx.h>
+#include <stddef.h>
 
-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 */
index 4f4fd0c39078e4ae7009049c65db06cff82efb0d..db945dec6471b11d9d4ace9eeec01938100c9a7a 100644 (file)
@@ -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;
                }
 
index e4c4e21acb05cab91abe126fd58595649461d6ba..ff710304dd3820efaed19ea94d5b0d56b345b054 100644 (file)
@@ -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);