简体   繁体   中英

How to convert an array of 16-bit values to base64?

So I'm working on an encryption/decryption method in C++ right now. It takes an std::string as an input (plus a "key" which will be used for encrypting the message), and it produces an std::string output which represents the encrypted string.

During the encryption process, I convert the std::string to an array of uint16_t and do some calculations on that as a part of the encryption. The reason for that is simply because a uint_16_t value gives much more headroom to encrypt the original value via an algorithm then a char does.

The problem is that in order to give back the encrypted message as an std::string I need to somehow convert the array of uint_16_t values to something readable (that is something that fits inside a char array without overflow). For that, I thought I could use base64 but all the base64 implementations I found only take std::string or char* as an input (8 bits/element). Obviously if I would provide it with my uint16_t array, I would never be able to get my original values back because the base64 function casts it down to 8 bits before converting it.

So here's my question: does anyone know a method of encoding a uint16_t array into a printable string (like base64), and back without any loss of data?

I know that I have to obtain the bytes of my data in order to use base64 but I'm not sure how to do that.

Thanks for any help in advance!

You can use base-n mini-library, which provides generic, iterator-based I/O.

The following code outputs "1 2 3 4 65535" to stdout, as expected:

uint16_t arr[] { 1, 2, 3, 4, 65535 };
const int len = sizeof(arr)/sizeof(arr[0]);
std::string encoded;
bn::encode_b64(arr, arr + len, std::back_inserter(encoded));
uint16_t out[len] { 0 };
bn::decode_b64(encoded.begin(), encoded.end(), out);
for (auto c : out) {
    std::cout << c << " ";
}

Mandatory disclosure: I'm the lib's author

Assuming that the uint16_t values range from zero to 63 and that you're using ASCII, just add 0x21 (hex 21) to each value and output it. This will create a printable string, but for display purposed you may also want to print a new line after some number of characters instead of having one very long string being displayed. Any decoder will have to subtract 0x21 from each character read from a file (and if there are newlines in the file ignore those (do this check before subtracting 0x21)).

See previous question here: base64 decode snippet in c++

Cast uint16_t* to unsigned const char* and encode, like so:

// Data to base64 encode
std::vector<uint16_t> some_data;

// Populate some_data...
// ...

// base64 encode it
std::string base64_data = base64_encode((unsigned char const*)&some_data[0], some_data.size()*2 );

So I finally solved it. I'm posting it in case someone else needs stuff like this. Basically I split the uint16_t values into two uint8_t each, and since those are 8-bit values, they can be used with any base64 implementation out there. Here's my method:

#include <iostream>
using namespace std;

#define BYTE_T uint8_t
#define TWOBYTE_T uint16_t
#define LOWBYTE(x)          ((BYTE_T)x)
#define HIGHBYTE(x)         ((TWOBYTE_T)x >> 0x8)
#define BYTE_COMBINE(h, l)  (((BYTE_T)h << 0x8) + (BYTE_T)l)

int main() {

    // an array with 16-bit integers
    uint16_t values[5] = {1, 2, 3, 4, 65535};

    // split the 16-bit integers into an array of 8-bit ones
    uint8_t split_values[10]; // notice that you need an array twice as big (16/8 = 2)
    int val_count = 0;
    for (int i=0; i<10; i+=2) {
        split_values[i] = HIGHBYTE(values[val_count]);
        split_values[i+1] = LOWBYTE(values[val_count]);
        val_count++;
    }

    // base64 encode the 8-bit values, then decode them back
    // or do whatever you want with them that requires 8-bit numbers

    // then reunite the 8-bit integers to the original array of 16-bit ones
    uint16_t restored[5];
    int rest_count = 0;
    for (int i=0; i<10; i+=2) {
        restored[rest_count] = BYTE_COMBINE(split_values[i], split_values[i+1]);
        rest_count++;
    }

    for (const auto &i : restored) cout << i << " ";
    cout << endl;

    return 0;
}

Of course the same method would work with any lengths. You just need to alter the bit shifting the the for loops. This code can be easily modified to split 32-bit ints to 16-bit ones, or whatever really.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM