Search code examples
c++crypto++

Integer to string conversion issues


I am experiencing a few problems with Crypto++'s Integer class. I am using the latest release, 5.6.2.

I'm attempting to convert Integer to string with the following code:

CryptoPP::Integer i("12345678900987654321");

std::ostrstream oss;
oss << i;
std::string s(oss.str());
LOGDEBUG(oss.str()); // Pumps log to console and log file

The output appears to have extra garbage data:

12345678900987654321.ÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««îþîþ

I get the same thing when I output directly to the console:

std::cout << "Dec: " << i << std::endl; // Same result

Additionally, I cannot get precision or scientific notation working. The following will output the same results:

std::cout.precision(5); // Does nothing with CryptoPP::Integer
std::cout << "Dec: " << std::setprecision(1) << std::dec << i << std::endl;
std::cout << "Sci: " << std::setprecision(5) << std::scientific << i << std::endl;

On top of all of this, sufficiently large numbers breaks the entire thing.

CryptoPP::Integer i("12345");

// Calculate i^16
for (int x = 0; x < 16; x++)
{
    i *= i;
}

std::cout  << i << std::endl; // Will never finish

Ultimately I'm trying to get something where I can work with large Integer numbers, and can output a string in scientific notation. I have no problems with extracting the Integer library or modifying it as necessary, but I would prefer working with stable code.

Am I doing something wrong, or is there a way that I can get this working correctly?


Solution

  • I'm attempting to convert Integer to string with the following code:

    CryptoPP::Integer i("12345678900987654321");
    
    std::ostrstream oss;
    oss << i;
    std::string s(oss.str());
    LOGDEBUG(oss.str()); // Pumps log to console and log file
    

    The output appears to have extra garbage data:

    12345678900987654321.ÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««îþîþ
    

    I can't reproduce this with Crypto++ 5.6.2 on Visual Studio 2010. The corrupted output is likely the result of some other issue, not a bug in Crypto++. If you haven't done so already, I'd suggest trying to reproduce this in a minimal program just using CryptoPP::Integer and std::cout, and none of your other application code, to eliminate all other possible problems. If it's not working in a trivial stand-alone test (which would be surprising), there could be problems with the way the library was built (e.g. maybe it was built with a different C++ runtime or compiler version from what your application is using). If your stand-alone test passes, you can add in other string operations, logging code etc. until you find the culprit.

    I do notice though that you're using std::ostrstream which is deprecated. You may want to use std::ostringstream instead. This Stack Overflow answer to the question "Why was std::strstream deprecated?" may be of interest, and it may even the case that the issues mentioned in that answer are causing your problems here.

    Additionally, I cannot get precision or scientific notation working. The following will output the same results:

    std::cout.precision(5); // Does nothing with CryptoPP::Integer
    std::cout << "Dec: " << std::setprecision(1) << std::dec << i << std::endl;
    std::cout << "Sci: " << std::setprecision(5) << std::scientific << i << std::endl;
    

    std::setprecision and std::scientific modify floating-point input/output. So, with regular integer types in C++ like int or long long this wouldn't work either (but I can see that especially with arbitrary-length integers like CryptoPP:Integer being able to output in scientific notation with a specified precision would make sense).

    Even if C++ didn't define it like this, Crypto++'s implementation would still need to heed those flags. From looking at the Crypto++ implementation of std::ostream& operator<<(std::ostream& out, const Integer &a), I can see that the only iostream flags it recognizes are std::ios::oct and std::ios::hex (for octal and hex format numbers respectively).

    If you want scientific notation, you'll have to format the output yourself (or use a different library).

    On top of all of this, sufficiently large numbers breaks the entire thing.

    CryptoPP::Integer i("12345");
    
    // Calculate i^16
    for (int x = 0; x < 16; x++)
    {
        i *= i;
    }
    
    std::cout  << i << std::endl; // Will never finish
    

    That will actually calculate i^(2^16) = i^65536, not i^16, because on each loop you're multiplying i with its new intermediate value, not with its original value. The actual result with this code would be 268,140 digits long, so I expect it's just taking Crypto++ a long time to produce that output.

    Here is the code adjusted to produce the correct result:

    CryptoPP::Integer i("12345");
    CryptoPP::Integer i_to_16(1);
    
    // Calculate i^16
    for (int x = 0; x < 16; x++)
    {
        i_to_16 *= i;
    }
    
    std::cout << i_to_16 << std::endl;