1

I would like to encode very small files (smaller than buffer) with base64. Yes I know, many base64 source codes available, but I would like to use OpenSSL.

My working example:

BIO *bin = BIO_new_file("smallfile.bin", "r");
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bmem = BIO_new(BIO_s_mem());
BIO_push(b64, bmem);
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

char buff[8192];
int n=BIO_read(bin, buff, sizeof(buff));
BIO_write(b64, buff, n);
BIO_flush(b64);

n = BIO_read(bmem, buff, sizeof(buff));
cout << string(buff,n) << endl;

BIO_free_all(bin);
BIO_free_all(b64);

How can I make chain directly instead read/write BIOs, like: BIO_f_new_file -> BIO_f_base64 -> buffer or BIO_s_mem

It's possible?

2
  • Does pushing the file BIO onto the base64 one work? (And reading from the base64; no mem bio) Commented Mar 5, 2022 at 21:46
  • Sorry, misunderstand your question 1st. I have tired to test this, but not working. Because I can't know, how can I initiate the BIO_f_base64 to start the file reading... :D Commented Mar 5, 2022 at 22:03

2 Answers 2

2

It doesn't seem possible to combine a source (BIO_s_file) and sink (BIO_s_mem) in one chain of BIOs. It would likely require an OpenSSL event loop since it doesn't know how many reads are required to consume all the data from the source.

However, a simpler way of using OpenSSL functions to Base64 encode data is to use the EVP interface:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

int main(int argc, char **argv) {
  if (argc != 2) {
    fprintf(stderr, "Usage: %s filename\n", argv[0]);
    return EXIT_FAILURE;
  }

  FILE *fin = fopen(argv[1], "r");
  if (!fin) {
    fprintf(stderr, "Unable to open %s\n", argv[1]);
    return EXIT_FAILURE;
  }

  unsigned char inbuff[8192];
  int insize = fread(inbuff, 1, sizeof inbuff, fin);
  fclose(fin);

  // For every 3 bytes of input provided 4 bytes of output data will be produced. 
  // (Plus a trailing nul)
  int outsize = 4 * (insize/3 + 1) + 1;
  unsigned char outbuff[outsize];
  outsize = EVP_EncodeBlock(outbuff, inbuff, insize);
  if (outsize < 0) {
      fputs("Encoding error!\n", stderr);
      return EXIT_FAILURE;
  }
  fwrite(outbuff, 1, outsize, stdout);
  putchar('\n');
  return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

1

To answer your question, not it isn't possible with BIOs. Sooner or later a BIO_read/write is going to be required on your part to pull/push data through the BIO chain. In short, what you have is pretty much spot on with how it is generally done.

That being said, one thing worth mentioning: you don't have to pull that data back out of the memory BIO via a BIO_read operation. It is already sitting there, just waiting for the taking, using BIO_get_mem_data. An example speaks to this better than I can just say it, so...:

// like you did before...
char buff[8192];
int n=BIO_read(bin, buff, sizeof buff);
BIO_write(b64, buff, n);
BIO_flush(b64);

// ...but do this to get the final data
char *ptr = NULL;
long len = BIO_get_mem_data(bmem, &ptr);

Note no read from the memory bio back int buff is done. From there, if you want the data as a std::string it is as simple as:

std::string s(ptr, ptr+len);

If you want to just dump it to std:cout (or any std::ostream&) you can just use the stream write method, without the intermediate string object:

std::cout.write(ptr, len);

Fwiw, this is frequently how I transpose binary data into C++ std::string objects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.