HexEncoder

From Crypto++ Wiki
Jump to navigation Jump to search
HexEncoder
Documentation
#include <cryptopp/hex.h>

The HexEncoder encodes bytes into base 16 encoded data. The partner decoder is a HexDecoder.

The HexEncoder and HexDecoder alphabet is 0123456789ABCDEF. The decoder accepts both uppercase and lowercase values. The decoder ignores characters not in the alphabet.

The HexEncoder takes a pointer to a BufferedTransformation. Because a pointer is taken, the HexEncoder owns the attached transformation, and therefore will destroy it. See ownership for more details.

If you need to quickly format a byte array as a C style array or C string suitable for source code, then see ArrayEncoder.

HexDecoder
Documentation
#include <cryptopp/hex.h>

Construction

HexEncoder(BufferedTransformation *attachment=NULL,
           bool uppercase=true,
           int outputGroupSize=0,
           const std::string &separator=":",
           const std::string &terminator="")

attachment is a BufferedTransformation, such as another filter or sink. If attachment is NULL, then the HexEncoder object will internally accumulate the output byte stream.

uppercase is an output formatting option and determines if output is uppercase or lowercase.

outputGroupSize is an output formatting option and determines the number of hexadecimal digit groups. For example, if outputGroupSize = 4, then an output string is formatted as "FFEE:DDCC:BBAA:9988:7766:5544:3322:1100".

separator is a string used as a delimiter. The default is a colon, and a space (with a grouping of 4) will format the string as "FFEE DDCC BBAA 9988 7766 5544 3322 1100". Encoding a Binary String for C Output shows a slightly more interesting use of the delimiter.

terminator adds a terminator to the output string. If outputGroupSize = 0, then a terminator of 'h' could be used to signify a hexadecimal string: "FFEEDDCCBBAA998877665544332211h".

Examples

The following is a small collection of examples to demonstrate using the HexEncoder.

Encoding a Binary String (Non-Filter)

The following demonstrates decoding a string using Put and Get.

byte decoded[] = { 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
string encoded;

HexEncoder encoder;
encoder.Put(decoded, sizeof(decoded));
encoder.MessageEnd();

word64 size = encoder.MaxRetrievable();
if(size)
{
    encoded.resize(size);		
    encoder.Get((byte*)&encoded[0], encoded.size());
}

cout << encoded << endl;

Note that Get used &encoded[0]. It is the C++ way to get the non-const pointer to the string's data from the string.

A run of the above program produces the following output.

$ ./cryptopp-test.exe
FFEEDDCCBBAA99887766554433221100

Encoding a Binary String (Filter)

Encoding a String (Non-Filter) performed a Put/Get sequence to transform the data. Crypto++ offers filters, which can simplify the process as shown below by taking advantage of Crypto++'s pipeline design.

byte decoded[] = { 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
string encoded;

StringSource ss(decoded, sizeof(decoded), true,
    new HexEncoder(
        new StringSink(encoded)
    ) // HexEncoder
); // StringSource

cout << encoded << endl;

As with the previous example, a run produces the following output.

$ ./cryptopp-test.exe
FFEEDDCCBBAA99887766554433221100

Encoding a Binary String for C Output

The following produces C array style output. Notice the the encoded string is "0x", and the separator is specified as ", 0x". The separator ensures all element (except the first) have a "0x". To embue the first element we a "0x", we simply set the output string encoded to "0x".

byte decoded[] = { 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
string encoded = "0x";

StringSource ss(decoded, sizeof(decoded), true,
    new HexEncoder(
        new StringSink(encoded),
        true,   // uppercase
        2,      // grouping
        ", 0x"  // separator
    ) // HexDecoder
); // StringSource

cout << encoded << endl;

Output is as follows.

$ ./cryptopp-test.exe
0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00

Attaching a BufferedTransformation

Sometimes its advantageous to attach (or change an attached) BufferedTransformation on the fly. The code below attaches a StringSink at runtime.

byte decoded[] = { 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
string encoded;
   
HexEncoder encoder;
 
encoder.Attach( new StringSink( encoded ) );
encoder.Put( decoded, sizeof(decoded) );
encoder.MessageEnd();

Attach returns the previous attached transformation. The caller is responsible for deleting the previous filter if its non-NULL. If you want to attach a new transformation and delete the current one, then use the Detach method. Detach will free the currently attached filter, and add the new transformation.

Scripting and Strings

On occasion, the mailing list will receive questions on cross-validation. For example, see AES CTR Chiper. Different output between PHP-mcrypt and Crypto++. In the question, PHP-mcrypt strings are used as follows:

$key = "1234567890123456789012345678901234567890123456789012345678901234";
$key = pack("H".strlen($key), $key);
$iv = "1111111111222222222233333333334444444444555555555566666666667777";
$iv = pack("H".strlen($iv), $iv);

One of the easiest ways to avoid typos is via Copy/Paste and a HexDecoder:

string encodedKey = "1234567890123456789012345678901234567890123456789012345678901234";
string encodedIv = "1111111111222222222233333333334444444444555555555566666666667777";
string key, iv;

StringSource ssk(encodedKey, true /*pump all*/,
    new HexDecoder(
        new StringSink(key)
    ) // HexDecoder
); // StringSource

StringSource ssv(encodedIv, true /*pump all*/,
    new HexDecoder(
        new StringSink(iv)
    ) // HexDecoder
); // StringSource

After running the above code, key and iv are hexadecimal (i.e., binary) strings rather than printable (i.e., ASCII) strings. The binary strings can be examined using GDB's x command.

(gdb) p key
$1 = {
  static npos = <optimized out>, 
  _M_dataplus = {
    <std::allocator<char>> = {
      <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    members of std::basic_string<char>::_Alloc_hider: 
    _M_p = 0x100403c18 "\0224Vx?\0224Vx?\0224Vx?\0224Vx?\0224Vx?\0224Vx?\0224"
  }
}
(gdb) x/16b 0x100403c18
0x100403c18:	0x12	0x34	0x56	0x78	0x90	0x12	0x34	0x56
0x100403c20:	0x78	0x90	0x12	0x34	0x56	0x78	0x90	0x12

(gdb) p iv
$2 = {
  static npos = <optimized out>, 
  _M_dataplus = {
    <std::allocator<char>> = {
      <__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    members of std::basic_string<char>::_Alloc_hider: 
    _M_p = 0x100403cf8 "\021\021\021\021\021\"\"\"\"\"33333DDDDDUUUUUfffffww"
  }
}
(gdb) x/16b 0x100403cf8
0x100403cf8:	0x11	0x11	0x11	0x11	0x11	0x22	0x22	0x22
0x100403d00:	0x22	0x22	0x33	0x33	0x33	0x33	0x33	0x44

Finally, the strings key and iv can be used with encryption or decryption objects as follows.

CTR_Mode< AES >::Encryption enc;
enc.SetKeyWithIV(key.data(), key.size(), iv.data());

Missing Data

Its not uncommon to experience Missing Data in a pipeline. A source will send data through a pipeline but have nothing in the sink. This is usually due to the compiler matching the wrong function. For example:

string source = "FF 88 00", destination;
StringSink ss(source,
    new HexDecoder(
        new StringSink(destination)
    ) // HexDecoder
); // StringSink

After the above code executes, destination will likely be empty because the compiler coerces the HexDecoder (the pointer) to a bool (the pumpAll parameter), which leaves the StringSource's attached transformation NULL. The compiler will do so without warning, even with -Wall -Wextra -Wconversion. To resolve the issue, explicitly specify the pumpAll parameter:

string source = "FF 88 00", destination;
StringSink ss(source, true /*pumpAll*/,
    new HexDecoder(
        new StringSink(destination)
    ) // HexDecoder
); // StringSink

Another way data ends up missing is failing to call MessageEnd() when pumping data. For example, the following may not produce expected results:

// The 4-bit nibble will be buffered waiting for another nibble
string source = "FF 88 0", destination;

HexDecoder decoder(new StringSink(destination));
decoder.Put(source.data(), source.size());

// Do something with destination

Be sure to call MessageEnd() when data comes up missing:

string source = "FF 88 0", destination;

HexDecoder decoder(new StringSink(destination));
decoder.Put(source.data(), source.size());
decoder.MessageEnd();

// Do something with destination