0x00
0x00

Reputation: 113

OpenSSL BIO_f_base64 does not read the whole buffer

I'm trying to read and base64-decode a file. For some reason OpenSSL reads only a part of it, then on all subsequent calls it simply returns 0. Here is the code I'm using:

#include <stdio.h>
#include <stdlib.h>

#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>

size_t b64_get_datalen(const char* b64, size_t b64len) {
    size_t actual_len = 0;
    int padding = 0;

    for (int i = 0; i < b64len; i++) {
        if (b64[i] != '\n') actual_len++;
    }

    int last = b64len-1;
    if (b64[last] == '\n') {
        last--;
    }
    if (b64[last] == '=') {
        padding = b64[last-1] == '=' ? 2 : 1;
    }

    return (actual_len*3)/4 - padding;
}


int main(int argc, char** argv) {
    FILE* fp = fopen("7.txt", "r");
    if (fp == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    fseek(fp, 0, SEEK_END);
    size_t b64_len = ftell(fp);
    rewind(fp);

    char b64[b64_len];
    fread(b64, b64_len, 1, fp);
    fclose(fp);

    size_t data_len = b64_get_datalen(b64, b64_len);
    char data[data_len];

    BIO *bio, *base64;
    base64 = BIO_new(BIO_f_base64());
    bio = BIO_new_mem_buf(b64, -1);
    bio = BIO_push(base64, bio);

    int read = BIO_read(bio, data, b64_len);
    printf("expected %d bytes, got %d bytes\n", (int)data_len, (int)read);
    BIO_free_all(bio);
}

The program outputs: expected 2880 bytes, got 2244 bytes

The file I'm trying to read is from cryptopals challenges (http://cryptopals.com/static/challenge-data/7.txt). I solved that challenge in Python, there were no issues with reading and base64-decoding the file.

What can be a reason of such behavior? And a more general question, how one should debug such issues with OpenSSL?

Upvotes: 1

Views: 600

Answers (1)

Matt Caswell
Matt Caswell

Reputation: 9382

The problem is in this line:

bio = BIO_new_mem_buf(b64, -1);

From the man page for BIO_new_mem_buf:

https://www.openssl.org/docs/man1.1.0/crypto/BIO_s_mem.html

BIO_new_mem_buf() creates a memory BIO using len bytes of data at buf, if len is -1 then the buf is assumed to be nul terminated and its length is determined by strlen.

But you have passed -1 and your buffer is not nul terminated! Therefore the strlen will overrun the buffer and keep going until it does find a nul terminator. This will likely include lots of values which are not valid base64, and hence the base64 decode fails.

It appears that the behaviour of the BIO_f_base64() BIO is to read and return what it has successfully read, but give up trying to read any more as soon as it encounters an error - as opposed to failing the entire read. It's not clear to me whether that is intended behaviour or a bug.

And a more general question, how one should debug such issues with OpenSSL?

In general you should inspect the OpenSSL error stack which usually gives good information about the source of a problem. For example by using ERR_print_errors_fp():

https://www.openssl.org/docs/man1.1.0/crypto/ERR_print_errors_fp.html

Not that it would have helped in this particular issue because it seems no error is placed on the error stack for this problem.

Upvotes: 2

Related Questions