OOM
OOM

Reputation: 806

base64 decode c++ fail end of line byte

I would like to decode a string of a large file in c++.

The size of the chain is: 1827500 characters and the file is: 1370626 bytes

My problem is that the decoding function does not work. The decoded file is different from the original file.

Below are two ways I've tried

First :

char  *base64_decode(const std::string &input, long *size)
{
    BIO *p_bio_mem = nullptr;
    BIO *p_bio_b64 = nullptr;
    char *retrnvalue;

    p_bio_b64 = BIO_new(BIO_f_base64());
    if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
    BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines

    p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
    if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
    BIO_push(p_bio_b64, p_bio_mem);

    // read result from chain
    // read sequence (reverse to write): buf <<-- p_bio_b64 <<-- p_bio_mem
    std::vector<char> buf((input.size() * 3 / 4) + 1);
    std::string result;
    for (;;)
    {
        auto nread = BIO_read(p_bio_b64, buf.data(), buf.size());
        if (nread < 0) { return NULL; //fail}
        if (nread == 0) { break; } // eof
        result.append(buf.data(), nread);
    }

    BIO_free_all(p_bio_b64);
    *size = buf.size();
    retrnvalue = new char[*size];
    memcpy(retrnvalue, buf.data(), *size);

    return retrnvalue;
}

Second:

Code from here : How do I base64 encode (decode) in C?

Some end of lines are different:

Diff with WinMerge

But not the entire file:

Left visualiser of WinMerge diff

Can you tell me why? and / or tell me another way to encode a file for easy transfert ?

I have this in input : drive.google.com/file/d/0B1i4Ez8N86wFblJnaFF6YVNVTWs/view

And i want this in output : drive.google.com/file/d/0B1i4Ez8N86wFdl9OUE5UMFB3R28/view

(sorry i can't put more than 2 links)

PS: When i decode with "certutil -decode" in batch, it works without problems.

SOLVED : Problem solved, the problem was fwrite . Fix with ofstream write function

Upvotes: 1

Views: 1534

Answers (2)

OOM
OOM

Reputation: 806

SOLVED : Problem solved, the problem was fwrite . Fix with ofstream write function

Upvotes: 1

Miles Budnek
Miles Budnek

Reputation: 30494

Your code to read the base64-ed data is pretty messed up:

std::vector<char> buf((input.size() * 3 / 4) + 1);
std::string result;
for (;;)
{
    auto nread = BIO_read(p_bio_b64, buf.data(), buf.size());
    if (nread < 0) { return NULL; } //fail
    if (nread == 0) { break; } // eof
    result.append(buf.data(), nread);
}

BIO_free_all(p_bio_b64);
*size = buf.size();
retrnvalue = new char[*size];
memcpy(retrnvalue, buf.data(), *size);

return retrnvalue;

You read your data into a string called result, but you never do anything with it. Then you copy the contents of your scratch buffer into your output buffer. You could also do away with your size pointer, and just return a std::string instead of a raw char*.

Another possible issue is the line

BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines

If your input does contain newlines, then that line will cause problems. If you want to work with input that may or may not contain newlines, you'll need to make that line conditional:

if (input.find('\n') == std::string::npos) {
    BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines
}

All together, that would look something like this:

std::string base64_decode(const std::string &input)
{
    BIO *p_bio_mem = nullptr;
    BIO *p_bio_b64 = nullptr;

    p_bio_b64 = BIO_new(BIO_f_base64());
    if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
    if (input.find('\n') == std::string::npos) {
        BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines
    }

    p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
    if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
    BIO_push(p_bio_b64, p_bio_mem);

    std::stringstream result;
    std::vector<char> buf(1024);
    while (auto nread = BIO_read(p_bio_b64, buf.data(), buf.size()))
    {
        if (nread < 0) { throw std::runtime_error("OMGZ"); } //fail
        result.write(buf.data(), nread);
    }

    BIO_free_all(p_bio_b64);
    return result.str();
}

LIVE DEMO

You'll also probably want to add some error handling to clean up your BIO instances in case of error (like in the answer you linked to), but that isn't what's causing the wrong result.

Upvotes: 1

Related Questions