Hexdecoder

From Crypto++ Wiki
Jump to: navigation, search

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

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.


HexDecoder
Documentation
#include <cryptopp/hex.h>

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 bellow will decode correctly using a HexDecoder.

string str1 = "0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00";
string str2 = "0xFF 0xEE 0xDD 0xCC 0xBB 0xAA 0x99 0x88 0x77 0x66 0x55 0x44 0x33 0x22 0x11 0x00";
string str4 = "FFh EEh DDh CCh BBh AAh 99h 88h 77h 66h 55h 44h 33h 22h 11h 00h";
string str4 = "FF:EE:DD:CC:BB:AA:99:88:77:66:55:44:33:22:11:00";
string str5 = "FFEEDDCCBBAA99887766554433221100";

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.data(), decoded.size());
}

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,
    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