HexDecoder

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

The HexDecoder converts base 16 encoded data to bytes. The partner encoder is a HexEncoder.

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

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

Note well: decoders skip characters that are not in the particular alphabet. If you incorrectly choose the wrong encoder, like a Base64Encoder instead of a HexEncoder, then the decoder will silently skip unrecognized characters.

Construction

HexDecoder (BufferedTransformation *attachment=NULL)

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

Hex Formats

A HexDecoder can parse many formats, including colon, comma, and whitespace delimited. Each of the example strings below will decode correctly using a HexDecoder.

string str1 = "FF, EE, DD, CC, BB, AA, 99, 88, 77, 66, 55, 44, 33, 22, 11, 00";
string str2 = "FF EE DD CC BB AA 99 88 77 66 55 44 33 22 11 00";
string str3 = "FF:EE:DD:CC:BB:AA:99:88:77:66:55:44:33:22:11:00";
string str4 = "FFEEDDCCBBAA99887766554433221100";

The HexDecoder cannot parse a string with a prefix or a suffix. For example, the string "0xFF 0xEE" will incorrectly parse to the bytes 0x0F, 0xF0, 0x0E, 0xE0 because the leading 0 in 0x is used as a hex digit instead of a prefix.

Sample Programs

The following is a small collection of sample programs to demonstrate using the HexDecoder.

Decoding a String (Non-Filter)

The following demonstrates decoding a string using Put and Get.

string encoded = "FFEEDDCCBBAA99887766554433221100";
string decoded;
   
HexDecoder decoder;
decoder.Put( (byte*)encoded.data(), encoded.size() );
decoder.MessageEnd();

word64 size = decoder.MaxRetrievable();
if(size && size <= SIZE_MAX)
{
    decoded.resize(size);		
    decoder.Get((byte*)&decoded[0], decoded.size());
}

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

Running the program under GDB shows the binary string contained in decoded.

Breakpoint 1, main (argc=1, argv=0x7fffffffe398) at cryptopp-test.cpp:44
(gdb) p encoded
$1 = {static npos = 18446744073709551615, 
  _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    _M_p = 0x60c078 "FFEEDDCCBBAA99887766554433221100"}}
(gdb) p decoded
$2 = {static npos = 18446744073709551615, 
  _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
    _M_p = 0x60c868 "\377\356\335̻\252\231\210wfUD3\"\021"}}
(gdb) x/16b 0x60c868
0x60c868:	0xff	0xee	0xdd	0xcc	0xbb	0xaa	0x99	0x88
0x60c870:	0x77	0x66	0x55	0x44	0x33	0x22	0x11	0x00

Decoding a String (Filter)

Decoding 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.

string encoded = "FFEEDDCCBBAA99887766554433221100";
string decoded;
   
StringSource ss(encoded, true,
    new HexDecoder(
        new StringSink(decoded)
    ) // HexDecoder
); // StringSource

Attaching a BufferedTransformation

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

string encoded = "FFEEDDCCBBAA99887766554433221100";
string decoded;
   
HexDecoder decoder;
 
decoder.Attach( new StringSink( decoded ) );
decoder.Put( (byte*)encoded.data(), encoded.size() );
decoder.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 /*pumpAll*/,
    new HexDecoder(
        new StringSink(key)
    ) // HexDecoder
); // StringSource

StringSource ssv(encodedIv, true /*pumpAll*/,
    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