1

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

Community
  • 1
  • 1
OOM
  • 728
  • 7
  • 22
  • Related, see [EVP Encryption | C++ Programs](http://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#C.2B.2B_Programs) on the OpenSSL wiki. It shows you how to use smart pointers to manage resources. Also see [How to get PKCS7_sign result into a char * or std::string](http://stackoverflow.com/a/38079093/608639). it provides a bunch of smart pointers for OpenSSL datatypes. – jww Aug 26 '16 at 23:55
  • Reading 1827500 into memory is OK on a desktop, but it will crush an Android or iOS device. You will probably need to switch to streaming the encryption and decryption (and possibly encoding and decoding). – jww Aug 27 '16 at 00:10
  • 1
    Thank you for the EVP Encryption. This project is not for other devices , thank you anyway – OOM Aug 27 '16 at 00:36

2 Answers2

1

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.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • Yes I 'm in debug mode. Sorry for code clarity . But the problem remains the same – OOM Aug 27 '16 at 00:31
  • Are you sure your input data is all on one line and doesn't end with a newline? If it's not, remove the `BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL);` line. – Miles Budnek Aug 27 '16 at 00:36
  • Thank you for your reply. I tried to comment the line but all end of lines is missing . https://s16.postimg.org/lqnny9qlh/befaft.jpg – OOM Aug 27 '16 at 00:52
  • Then you'll need to post a [mcve] along with input and expected output that demonstrates your problem. – Miles Budnek Aug 27 '16 at 01:19
  • 1
    Ok thank you ! I have this : https://drive.google.com/file/d/0B1i4Ez8N86wFblJnaFF6YVNVTWs/view and i want this : https://drive.google.com/file/d/0B1i4Ez8N86wFdl9OUE5UMFB3R28/view – OOM Aug 27 '16 at 01:53
1

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

OOM
  • 728
  • 7
  • 22