4

I'm having a problem trying to convert SecByteBlock to string. Here's my case:

I want to encrypt user access data using AES with static key and dynamic iv. My code is something like this:

AesKeyIvFactory aesKeyIvFactory;
SecByteBlock key = aesKeyIvFactory.loadKey();
SecByteBlock iv = aesKeyIvFactory.createIv();

encryptionService->encode(&userAccess, key, iv);
std::string token = std::string(iv.begin(), iv.end()) + userAccess;

The code above is supposed to:

  1. Load key from file;

  2. Create iv;

  3. Encrypt (AES) user access data;

  4. Concatenate the iv with the user data access encrypted to create a "token";

Running a test several times, sometimes (1 to 10 times) the std::string(iv.begin(), iv.end()) doesn't work correctly. It seems like there is a "line break" in the iv that makes the conversion fail.

I tried a lot of things, but nothing works and I don't have experience with c++.

I hope that someone can help me.

3isenHeim
  • 243
  • 5
  • 22
  • How do you know it isn't working? (It's more likely that printing the string does the wrong thing than constructing it.) – Alan Stokes Aug 10 '15 at 21:56
  • Related (but not a duplicate), see [string to SecByteBlock conversion](http://stackoverflow.com/q/26145776). – jww Aug 13 '15 at 07:02
  • Now available on the Crypto++ wiki: [`SecBlock`](https://www.cryptopp.com/wiki/SecBlock). The wiki page includes conversion examples. – jww Mar 23 '18 at 19:21

2 Answers2

5

I'm having a problem trying to convert SecByteBlock to string

If the issue is with conversion from SecByteBlock and its byte array to a std::string and its char array, then you should:

SecByteBlock iv;
...

// C-style cast
std::string token = std::string((const char*)iv.data(), iv.size()) + userAccess;

Or,

SecByteBlock iv;
...

// C++-style cast
std::string token = std::string(reinterpret_cast<const char*>(iv.data()), iv.size()) + userAccess;

You can also forgo the assignment, and just initialize and later append:

SecByteBlock iv;
...

std::string token(reinterpret_cast<const char*>(iv.data()), iv.size());
...

std::string userAccess;
...

token += userAccess;

The other problem you might have is string to SecByteBlock. You should do this:

std::string str;
...

// C-style cast
SecByteBlock sbb((const byte*)str.data(), str.size());

Or:

std::string str;
...

// C++-style cast
SecByteBlock sbb(reinterpret_cast<const byte*>(str.data()), str.size());
jww
  • 97,681
  • 90
  • 411
  • 885
Eric Tsui
  • 1,924
  • 12
  • 21
  • I've already tried this, but this line breaks with the message: "Error: no instance of constructor..." I've tried too: `std::string token = std::string((char *)iv.data(), iv.size()) + userAccess;` But the test still breaks sometimes. – Paulo Fernando Ozório Ferraz Aug 11 '15 at 12:36
  • I have the same problem, and when I try this, i get : *error: invalid conversion from ‘CryptoPP::AllocatorWithCleanup::pointer {aka unsigned char*}’ to ‘const char*’ * – 3isenHeim Sep 08 '15 at 13:31
3

I think Eric answered your primary question on how to convert the SecByteBlock to a std::string (including the explicit conversions between char* and byte*). But here's how you might approach std::string token = std::string(iv.begin(), iv.end()) + userAccess; issue.

string token;

SecByteBlock iv(16), userAccess(16);
OS_GenerateRandomBlock(false, iv, iv.size());
OS_GenerateRandomBlock(false, userAccess, userAccess.size());

SecByteBlock nil;
nil.CleanNew(HMAC<SHA256>::DEFAULT_KEYLENGTH);

HMAC<SHA256> hmac;
hmac.SetKey(nil.data(), nil.size());

HashFilter filter(hmac, new HexEncoder(new StringSink(token)));
filter.Put(iv.data(), iv.size());
filter.Put(userAccess.data(), userAccess.size());
filter.MessageEnd();

cout << token << endl;

The SecByteBlock nil creates an object with no memory or size. The nil.CleanNew(HMAC<SHA256>::DEFAULT_KEYLENGTH) sizes and initializes the SecByteBlock to 0. Otherwise, you have an uninitialized block of memory.

It is possible to declare it and size it with a 0-inialized array, but you have to be familiar with the sources because its no Doxygen-docimented as of Crypto++ 5.6.2. That way is to use a NULL pointer, but a non-0 size. Here's what it would look like, but its very non-intuitive:

SecByteBlock nil(NULL, HMAC<SHA256>::DEFAULT_KEYLENGTH);

The trick relies on this SecBlock<T> constructor:

00250    SecBlock(const T *t, size_type len)
00251        : m_size(len)
00252    {
00253        m_ptr = m_alloc.allocate(len, NULL);
00254        if (t == NULL)
00255            memset_z(m_ptr, 0, len*sizeof(T));
00256        else
00257            memcpy(m_ptr, t, len*sizeof(T));
00258    }

If possible, you should use HKDF instead of the HMAC<SHA> with a nil vector to extract the entropy from the security parameters. You can find the HKDF in the repo at the HKDF class. Its a stand alone header, so it will "just work".


A typical run of the program with random values for iv and userAccess is:

$ ./cryptopp-test.exe
061CF705259058C4E01A2BF22830FC3F2A7E97F12FE605B38405B1E1B19A9E0F

Another way to approach it could be concatenation based on SecByteBlock's operator +=. The result is a binary string, and not a human readable ASCII string.

SecByteBlock result;

result += iv;
result += SecByteBlock(userAccess.data(), userAccess.size());

string token(result.data(), result.size());

If you need a human readable string, then run it through a HexEncoder:

HexEncoder hex(new StringSink(token));
hex.Put(result.data(), result.size());
hex.MessageEnd();

But it does not extract the entropy from the parameters, so I personally like it less.


When you move from a SecByteBlock to a std::string, you effectively lose your secure allocator. That means the data in the copies will not be zeroized after egressing data to the string object.


The HexEncoder is a convenience item, and it allows you to dump the binary string.

Another useful one might be the Base64URLEncoder. It uses the web safe alphabet.

jww
  • 97,681
  • 90
  • 411
  • 885