Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert SecByteBlock to string?

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.

like image 443
Paulo Fernando Ozório Ferraz Avatar asked Feb 09 '23 08:02

Paulo Fernando Ozório Ferraz


2 Answers

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());
like image 159
Eric Tsui Avatar answered Feb 23 '23 03:02

Eric Tsui


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.

like image 31
jww Avatar answered Feb 23 '23 04:02

jww