453 lines
13 KiB
C++
453 lines
13 KiB
C++
/*
|
|
* Modification History
|
|
*
|
|
* 2003-August-22 Jason Rohrer
|
|
* Created.
|
|
*
|
|
* 2003-September-22 Jason Rohrer
|
|
* Added base64 encoding.
|
|
*
|
|
* 2004-March-21 Jason Rohrer
|
|
* Fixed a variable scoping and redefinition bug pointed out by Benjamin Meyer.
|
|
*/
|
|
|
|
|
|
#include "encodingUtils.h"
|
|
|
|
|
|
#include "minorGems/util/SimpleVector.h"
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
|
|
char fourBitIntToHex( int inInt ) {
|
|
char outChar[2];
|
|
|
|
if( inInt < 10 ) {
|
|
sprintf( outChar, "%d", inInt );
|
|
}
|
|
else {
|
|
switch( inInt ) {
|
|
case 10:
|
|
outChar[0] = 'A';
|
|
break;
|
|
case 11:
|
|
outChar[0] = 'B';
|
|
break;
|
|
case 12:
|
|
outChar[0] = 'C';
|
|
break;
|
|
case 13:
|
|
outChar[0] = 'D';
|
|
break;
|
|
case 14:
|
|
outChar[0] = 'E';
|
|
break;
|
|
case 15:
|
|
outChar[0] = 'F';
|
|
break;
|
|
default:
|
|
outChar[0] = '0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
return outChar[0];
|
|
}
|
|
|
|
|
|
|
|
// returns -1 if inHex is not a valid hex character
|
|
int hexToFourBitInt( char inHex ) {
|
|
int returnInt;
|
|
|
|
switch( inHex ) {
|
|
case '0':
|
|
returnInt = 0;
|
|
break;
|
|
case '1':
|
|
returnInt = 1;
|
|
break;
|
|
case '2':
|
|
returnInt = 2;
|
|
break;
|
|
case '3':
|
|
returnInt = 3;
|
|
break;
|
|
case '4':
|
|
returnInt = 4;
|
|
break;
|
|
case '5':
|
|
returnInt = 5;
|
|
break;
|
|
case '6':
|
|
returnInt = 6;
|
|
break;
|
|
case '7':
|
|
returnInt = 7;
|
|
break;
|
|
case '8':
|
|
returnInt = 8;
|
|
break;
|
|
case '9':
|
|
returnInt = 9;
|
|
break;
|
|
case 'A':
|
|
case 'a':
|
|
returnInt = 10;
|
|
break;
|
|
case 'B':
|
|
case 'b':
|
|
returnInt = 11;
|
|
break;
|
|
case 'C':
|
|
case 'c':
|
|
returnInt = 12;
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
returnInt = 13;
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
returnInt = 14;
|
|
break;
|
|
case 'F':
|
|
case 'f':
|
|
returnInt = 15;
|
|
break;
|
|
default:
|
|
returnInt = -1;
|
|
break;
|
|
}
|
|
|
|
return returnInt;
|
|
}
|
|
|
|
|
|
|
|
char *hexEncode( unsigned char *inData, int inDataLength ) {
|
|
|
|
char *resultHexString = new char[ inDataLength * 2 + 1 ];
|
|
int hexStringIndex = 0;
|
|
|
|
for( int i=0; i<inDataLength; i++ ) {
|
|
|
|
unsigned char currentByte = inData[ i ];
|
|
|
|
int highBits = 0xF & ( currentByte >> 4 );
|
|
int lowBits = 0xF & ( currentByte );
|
|
|
|
resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits );
|
|
hexStringIndex++;
|
|
|
|
resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits );
|
|
hexStringIndex++;
|
|
}
|
|
|
|
resultHexString[ hexStringIndex ] = '\0';
|
|
|
|
return resultHexString;
|
|
}
|
|
|
|
|
|
|
|
unsigned char *hexDecode( char *inHexString ) {
|
|
|
|
int hexLength = strlen( inHexString );
|
|
|
|
if( hexLength % 2 != 0 ) {
|
|
// hex strings must be even in length
|
|
return NULL;
|
|
}
|
|
|
|
int dataLength = hexLength / 2;
|
|
|
|
unsigned char *rawData = new unsigned char[ dataLength ];
|
|
|
|
|
|
for( int i=0; i<dataLength; i++ ) {
|
|
|
|
int highBits = hexToFourBitInt( inHexString[ 2 * i ] );
|
|
int lowBits = hexToFourBitInt( inHexString[ 2 * i + 1 ] );
|
|
|
|
if( highBits == -1 || lowBits == -1 ) {
|
|
delete [] rawData;
|
|
return NULL;
|
|
}
|
|
|
|
rawData[i] = (unsigned char)( highBits << 4 | lowBits );
|
|
}
|
|
|
|
return rawData;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* These tables were taken from the GNU Privacy Guard source code.
|
|
*
|
|
* Wow... writing base64 functions would have been much more difficult
|
|
* without these tables, especially the reverse table.
|
|
*/
|
|
|
|
|
|
|
|
// The base-64 character list
|
|
// Maps base64 binary numbers to ascii characters
|
|
static const char *binaryToAscii =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
|
|
// The reverse base-64 list
|
|
// Maps ascii characters to base64 binary numbers
|
|
static unsigned char asciiToBinary[256] = {
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
|
|
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
|
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
|
|
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
|
|
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
|
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff };
|
|
|
|
|
|
char *base64Encode( unsigned char *inData, int inDataLength,
|
|
char inBreakLines ) {
|
|
|
|
SimpleVector<char> *encodingVector = new SimpleVector<char>();
|
|
|
|
int numInLine = 0;
|
|
|
|
// take groups of 3 data bytes and map them to 4 base64 digits
|
|
for( int i=0; i<inDataLength; i=i+3 ) {
|
|
|
|
if( i+2 < inDataLength ) {
|
|
// not at end yet
|
|
|
|
unsigned int block =
|
|
inData[i] << 16 |
|
|
inData[i+1] << 8 |
|
|
inData[i+2];
|
|
|
|
// base64 digits, with digitA at left
|
|
unsigned int digitA = 0x3F & ( block >> 18 );
|
|
unsigned int digitB = 0x3F & ( block >> 12 );
|
|
unsigned int digitC = 0x3F & ( block >> 6 );
|
|
unsigned int digitD = 0x3F & ( block );
|
|
|
|
encodingVector->push_back( binaryToAscii[ digitA ] );
|
|
encodingVector->push_back( binaryToAscii[ digitB ] );
|
|
encodingVector->push_back( binaryToAscii[ digitC ] );
|
|
encodingVector->push_back( binaryToAscii[ digitD ] );
|
|
numInLine += 4;
|
|
|
|
if( inBreakLines && numInLine == 76 ) {
|
|
// break the line
|
|
encodingVector->push_back( '\r' );
|
|
encodingVector->push_back( '\n' );
|
|
numInLine = 0;
|
|
}
|
|
|
|
}
|
|
else {
|
|
// at end
|
|
int numLeft = inDataLength - i;
|
|
|
|
switch( numLeft ) {
|
|
case 0:
|
|
// no padding
|
|
break;
|
|
case 1: {
|
|
// two digits, two pads
|
|
unsigned int block =
|
|
inData[i] << 16 |
|
|
0;
|
|
unsigned int digitA = 0x3F & ( block >> 18 );
|
|
unsigned int digitB = 0x3F & ( block >> 12 );
|
|
|
|
encodingVector->push_back( binaryToAscii[ digitA ] );
|
|
encodingVector->push_back( binaryToAscii[ digitB ] );
|
|
|
|
encodingVector->push_back( '=' );
|
|
encodingVector->push_back( '=' );
|
|
break;
|
|
}
|
|
case 2: {
|
|
// three digits, one pad
|
|
unsigned int block =
|
|
inData[i] << 16 |
|
|
inData[i+1] << 8 |
|
|
0;
|
|
|
|
// base64 digits, with digitA at left
|
|
unsigned int digitA = 0x3F & ( block >> 18 );
|
|
unsigned int digitB = 0x3F & ( block >> 12 );
|
|
unsigned int digitC = 0x3F & ( block >> 6 );
|
|
|
|
encodingVector->push_back( binaryToAscii[ digitA ] );
|
|
encodingVector->push_back( binaryToAscii[ digitB ] );
|
|
encodingVector->push_back( binaryToAscii[ digitC ] );
|
|
encodingVector->push_back( '=' );
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
// done with all data
|
|
i = inDataLength;
|
|
}
|
|
}
|
|
|
|
char *returnString = encodingVector->getElementString();
|
|
|
|
delete encodingVector;
|
|
|
|
return returnString;
|
|
}
|
|
|
|
|
|
|
|
unsigned char *base64Decode( char *inBase64String,
|
|
int *outDataLength ) {
|
|
|
|
SimpleVector<unsigned char> *decodedVector =
|
|
new SimpleVector<unsigned char>();
|
|
|
|
|
|
|
|
int encodingLength = strlen( inBase64String );
|
|
|
|
SimpleVector<unsigned char> *binaryEncodingVector =
|
|
new SimpleVector<unsigned char>();
|
|
|
|
int i;
|
|
for( i=0; i<encodingLength; i++ ) {
|
|
unsigned char currentChar = (unsigned char)( inBase64String[i] );
|
|
|
|
unsigned char currentBinary = asciiToBinary[ currentChar ];
|
|
|
|
if( currentBinary != 0xFF ) {
|
|
// in range
|
|
binaryEncodingVector->push_back( currentBinary );
|
|
}
|
|
}
|
|
|
|
int binaryEncodingLength = binaryEncodingVector->size();
|
|
|
|
unsigned char *binaryEncoding = binaryEncodingVector->getElementArray();
|
|
delete binaryEncodingVector;
|
|
|
|
int blockCount = binaryEncodingLength / 4;
|
|
|
|
if( binaryEncodingLength % 4 != 0 ) {
|
|
// extra, 0-padded block
|
|
blockCount += 1;
|
|
}
|
|
|
|
|
|
|
|
// take groups of 4 encoded digits and map them to 3 data bytes
|
|
for( i=0; i<binaryEncodingLength; i=i+4 ) {
|
|
|
|
if( i+3 < binaryEncodingLength ) {
|
|
// not at end yet
|
|
|
|
unsigned int block =
|
|
binaryEncoding[i] << 18 |
|
|
binaryEncoding[i+1] << 12 |
|
|
binaryEncoding[i+2] << 6 |
|
|
binaryEncoding[i+3];
|
|
|
|
// data byte digits, with digitA at left
|
|
unsigned int digitA = 0xFF & ( block >> 16 );
|
|
unsigned int digitB = 0xFF & ( block >> 8 );
|
|
unsigned int digitC = 0xFF & ( block );
|
|
|
|
decodedVector->push_back( digitA );
|
|
decodedVector->push_back( digitB );
|
|
decodedVector->push_back( digitC );
|
|
}
|
|
else {
|
|
// at end
|
|
int numLeft = binaryEncodingLength - i;
|
|
|
|
switch( numLeft ) {
|
|
case 0:
|
|
// no padding
|
|
break;
|
|
case 1: {
|
|
// impossible
|
|
break;
|
|
}
|
|
case 2: {
|
|
// two base64 digits, one data byte
|
|
unsigned int block =
|
|
binaryEncoding[i] << 18 |
|
|
binaryEncoding[i+1] << 12 |
|
|
0;
|
|
|
|
// data byte digits, with digitA at left
|
|
unsigned int digitA = 0xFF & ( block >> 16 );
|
|
|
|
decodedVector->push_back( digitA );
|
|
break;
|
|
}
|
|
case 3: {
|
|
// three base64 digits, two data bytes
|
|
unsigned int block =
|
|
binaryEncoding[i] << 18 |
|
|
binaryEncoding[i+1] << 12 |
|
|
binaryEncoding[i+2] << 6 |
|
|
0;
|
|
|
|
// data byte digits, with digitA at left
|
|
unsigned int digitA = 0xFF & ( block >> 16 );
|
|
unsigned int digitB = 0xFF & ( block >> 8 );
|
|
|
|
|
|
decodedVector->push_back( digitA );
|
|
decodedVector->push_back( digitB );
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
// done with all data
|
|
i = binaryEncodingLength;
|
|
}
|
|
}
|
|
|
|
delete [] binaryEncoding;
|
|
|
|
|
|
*outDataLength = decodedVector->size();
|
|
unsigned char* returnData = decodedVector->getElementArray();
|
|
|
|
delete decodedVector;
|
|
|
|
return returnData;
|
|
}
|
|
|
|
|
|
|
|
|
|
|