Base64URLDecoder

From Crypto++ Wiki
Jump to: navigation, search
Base64URLDecoder
Documentation
#include <cryptopp/base64.h>

The Base64URLDecoder converts URL and safe filename encoded data into binary data. The class is helpful for web technologies, like JSON and JSON Web Keys (JWK). The partner encoder is a Base64URLEncoder. The character set differs from a Base64Encoder by using the minus (-) and underscore (_) characters. The alphabet is specified in RFC 4648, The Base16, Base32, and Base64 Data Encodings.

The Base64URLDecoder takes a pointer to a BufferedTransformation. Because a pointer is taken, the Base64URLDecoder 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 choose a Base64Decoder instead of a Base64URLDecoder, then the mischosen decoder will silently skip unrecognized characters.

Note well: this class was added to Crypto++ at version 5.6.3 (SVN,GitHub). If using a downlevel version of the library, then you must download and apply the patch below.

Construction

Base64URLDecoder (BufferedTransformation *attachment=NULL)

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

Sample Programs

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

Decoding a String (Non-Filter)

The following demonstrates decoding a string using Put and Get.

string encoded = "_-7dzLuqmYh3ZlVEMyIRAA==";
string decoded;
   
Base64URLDecoder 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.

(gdb) p encoded
$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 = 0x1004039c8 "_-7dzLuqmYh3ZlVEMyIRAA=="
  }
}
(gdb) p decoded
$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 = 0x100403a08 "???̻???wfUD3\"\021"
  }
}
(gdb) x/16b 0x100403a08
0x100403a08:	0xff	0xee	0xdd	0xcc	0xbb	0xaa	0x99	0x88
0x100403a10:	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 = "_-7dzLuqmYh3ZlVEMyIRAA==";
string decoded;
   
StringSource ss(encoded, true,
    new Base64URLDecoder(
        new StringSink(decoded)
    ) // Base64URLDecoder
); // 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 = "_-7dzLuqmYh3ZlVEMyIRAA==";
string decoded;
   
Base64URLDecoder 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.

Changing Alphabets

The following program changes the Base64URLEncoder alphabet from a web safe alphabet to the original alphabet. The original alphabet is used by Base64Encoder and Base64Decoder.

// Encoder
Base64URLEncoder encoder;
const byte ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
AlgorithmParameters params = MakeParameters(Name::EncodingLookupArray(),(const byte *)ALPHABET);
encoder.IsolatedInitialize(params);

// Decoder
int lookup[256];
const byte ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Base64Decoder::InitializeDecodingLookupArray(lookup, ALPHABET, 64, false);

Base64URLDecoder decoder;
AlgorithmParameters params = MakeParameters(Name::DecodingLookupArray(),(const int *)lookup);
decoder.IsolatedInitialize(params);

Missing Padding

The Base64URLDecoder is very tolerant, and it will happily decode an encoded string without padding characters.

string encoded = "_-7dzLuqmYh3ZlVEMyIRAA";
string decoded;
    
Base64URLDecoder 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());
}

The program above will produce expected results:

(gdb) p encoded
$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 = 0x1004039c8 "_-7dzLuqmYh3ZlVEMyIRAA"
  }
}
(gdb) p decoded
$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 = 0x1004039f8 "???̻???wfUD3\"\021"
  }
}
(gdb) x/16b 0x1004039f8
0x1004039f8:	0xff	0xee	0xdd	0xcc	0xbb	0xaa	0x99	0x88
0x100403a00:	0x77	0x66	0x55	0x44	0x33	0x22	0x11	0x00

Wrong Decoder

Decoders skip characters that are not in the particular alphabet. If you incorrectly choose a Base64Decoder instead of a Base64URLDecoder, then the mischosen decoder will silently skip unrecognized characters. For example:

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

StringSource ss1(raw, sizeof(raw), true,
                    new Base64URLEncoder(
                        new StringSink(encoded)
                    ) // Base64URLEncoder
                ); // StringSource

StringSource ss2(encoded, true,
                     new Base64Decoder(
                         new StringSink(decoded)
                     ) // Base64Decoder
                 ); // StringSource

Running the program produces the following results:

(gdb) p decoded
$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 = 0x100403d68 "??˺???veTC2!\020"
  }
}
(gdb) x/16b 0x100403d68
0x100403d68:	0xed	0xdc	0xcb	0xba	0xa9	0x98	0x87	0x76
0x100403d70:	0x65	0x54	0x43	0x32	0x21	0x10	0x00	0x00

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

In the case of a Base64 encoder, the filter will buffer the first two octets while waiting on a third octet. 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

Downloads

Base64.zip - Updated base64.h and base64.cpp to add a Base64URLEncoder and Base64URLDecoder class.