Base64Encoder

From Crypto++ Wiki
Jump to: navigation, search

The Base64Encoder encodes bytes into base 64 characters. The partner decoder is a Base64Decoder. The character set differs from a Base64URLEncoder by using the plus (+) and forward slash (/) characters. The alphabet is specified in RFC 4648, The Base16, Base32, and Base64 Data Encodings.

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

If you need an encoder for web safe URLS and filenames, then see Base64URLEncoder.

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


Base64Encoder
Documentation
#include <cryptopp/base64.h>

Construction

Base64Encoder(BufferedTransformation *attachment = NULL,
              bool insertLineBreaks = true,
              int maxLineLength = 72)

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

insertLineBreaks determines if the encoder should add line breaks.

maxLineLength is the length of a line if insertLineBreaks is true. If insertLineBreaks is false, then the class discards maxLineLength and sets it to 0.

Sample Programs

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

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;

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

word64 size = encoder.MaxRetrievable();
if(size)
{
    encoded.resize(size);		
    encoder.Get((byte*)encoded.data(), encoded.size());
}

cout << encoded << endl;

A run of the above program produces the following output.

$ ./cryptopp-test.exe
/+7dzLuqmYh3ZlVEMyIRAA==

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 Base64Encoder(
        new StringSink(encoded)
    ) // Base64Encoder
); // StringSource

cout << encoded << endl;

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

$ ./cryptopp-test.exe
/+7dzLuqmYh3ZlVEMyIRAA==

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;
   
Base64Encoder 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.

Disabling Padding

The following program disables padding and line breaks from a Base64Encoder. You have to use NameValuePairs in this case because there is no way to disable padding through the constructor. If the use of MakeParameters does not quite look right (it does not), then visit NameValuePairs for a discussion.

using CryptoPP::Name::Pad;
using CryptoPP::Name::InsertLineBreaks;

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

string encoded, hexed;
Base64Encoder encoder;

AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
encoder.IsolatedInitialize(params);
encoder.Attach(new StringSink( encoded ));

ArraySource as(raw, sizeof(raw), true, new Redirector(encoder));
cout << encoded << endl;

StringSource ss(encoded, true, new Base64Decoder(new HexEncoder(new StringSink(hexed))));
cout << hexed << endl;

In the code above, the Base64Encoder constructor sets up a standard object. Then the parameters are tuned for this particular use. In this case, the use is (1) no padding and (2) no line breaks.

The call to IsolatedInitialize initializes or reinitializes the object. Under the covers, Base64Encoder's override of IsolatedInitialize uses CombinedNameValuePairs to blend the library's default parameters with the caller's parameters (the caller's parameter's are preferred). That means parameters like the encoding alphabet will always be present so a caller does not have to specify it again (see base64.cpp).

A run of the program produces the following. The padding is not added to the tail of the encoded data (which would be ...VEMyIRAA==).

$ ./cryptopp-test.exe
/+7dzLuqmYh3ZlVEMyIRAP/u3cy7qpmId2ZVRDMiEQD/7t3Mu6qZiHdmVUQzIhEA/+7dzLuqmYh3ZlVEMyIRAA
FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100

Changing Alphabets

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

// Encoder
Base64Encoder 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);

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

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