// Copyright (c) 2010 R-Link Research and Consulting, Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // 3. Neither the name of the nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #import "RCBase64Encoder.h" // function: __rcb64_getEncodingTable // // Return is the table of encoding chars. unsigned char * __rcb64_getEncodingTable() { static unsigned char encTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; return encTable; } // function: __rcb64_getDecodingTable // // Return is the decoding table unsigned char * __rcb64_getDecodingTable() { static int initialized = 0; static unsigned char decTable[128]; if(initialized == 0) { // Clear //****** memset(decTable, 0, sizeof(decTable)); // Set the bytes corresponding to the // encoding chars to their 6-bit (0-64) // index values. //************************************* unsigned char * pEncTable = __rcb64_getEncodingTable(); for(unsigned char i = 0; i < 64; i++) { int asciiVal = (int) pEncTable[i]; decTable[asciiVal] = i; } initialized = 1; } return decTable; } // function: __rcb64dec_getPaddingLength // // Get the number of pad chars at the end of the buffer int __rcb64dec_getPaddingLength(const char * pRaw, int inputLength) { char * pByte = (char*) (pRaw + (inputLength - 1)); int ret = 0; for(int i = inputLength - 1; i >= 0; i--, pByte--) { char nextByte = *pByte; if(nextByte != '=') { break; } ret++; } return ret; } // function: __rcb64dec_calcDecodedLength // // Calculate the decoded length int __rcb64dec_calcDecodedLength(int inputLength, int paddingLength) { int baseLength = inputLength - paddingLength; int inputBits = baseLength * 6; int ret = inputBits / 8; return ret; } // function: __rcb64_decode // // Decode C-String unsigned char * __rcb64_decode(const char * pRaw) { unsigned char * pDecTable = __rcb64_getDecodingTable(); int inputLength = strlen(pRaw); int paddingLength = __rcb64dec_getPaddingLength(pRaw, inputLength); int decodedLength = __rcb64dec_calcDecodedLength(inputLength, paddingLength); unsigned char *pDec = malloc(decodedLength + 1); unsigned char *pRet = pDec; pDec[decodedLength] = 0; char *pEnc = (char*) pRaw; int decBitIndex = 0; // Iterate through the input, decoding // each char completely before moving on // to the next (assumes MSB first). //************************************** for(int i = 0; i < inputLength; i++,pEnc++) { char nextEncByte = *pEnc; if(decBitIndex == 0) { // all 6 bits of encoded sextet // first 6 bits of decoded byte *pDec = pDecTable[nextEncByte] << 2; decBitIndex = 6; } else if(decBitIndex == 6) { // first 2 bits from encoded sextet // last 2 bits of decoded byte *pDec |= (pDecTable[nextEncByte] & 0x30) >> 4; pDec++; // last 4 bits from sextet // first 4 bits of decoded byte *pDec = (pDecTable[nextEncByte] & 0x0F) << 4; decBitIndex = 4; } else if(decBitIndex == 4) { // first 4 bits from encoded sextet // last 4 bits of decoded byte *pDec |= (pDecTable[nextEncByte] & 0x3C) >> 2; pDec++; // last 2 bits from encoded sextet // first 2 bits of decoded byte *pDec = (pDecTable[nextEncByte] & 0x03) << 6; decBitIndex = 2; } else if(decBitIndex == 2) { // all 6 bits from encoded sextet // last 6 bits of decoded byte *pDec |= pDecTable[nextEncByte]; pDec++; decBitIndex = 0; } } return pRet; } // function: __rcb64enc_CalcEncodingLength // // Calculate encoding length int __rcb64enc_CalcEncodingLength(int inputLength) { // total bits to encode int inputBitCount = inputLength * 8; // total bytes necessary to re-encode // as 6-bit bytes int baseLength = inputBitCount / 6; if(inputBitCount % 6 > 0) // need 1 more byte to encode the remainder { baseLength++; } return baseLength; } // function: __rcb64enc_CalcPaddingLength // // Calculate padding length int __rcb64enc_CalcPaddingLength(int encodingLength) { int encodingBitsRequired = encodingLength * 8; // will be 0, 8, or 16 int finalQuantum = encodingBitsRequired % 24; if(finalQuantum == 8) { return 2; } else if(finalQuantum == 16) { return 1; } return 0; } // function: __rcb64_encode // // Encode buffer. char * __rcb64_encode(void * pRaw, int length) { unsigned char * pEncTable = __rcb64_getEncodingTable(); int encodingLength = __rcb64enc_CalcEncodingLength(length); int paddingLength = __rcb64enc_CalcPaddingLength(length); char * pRet = malloc(encodingLength + paddingLength + 1); char * pEnc = pRet; int encBitIndex = 0; char encByte = 0; char rawByte = 0; char * pBytes = (char*) pRaw; for(int i = 0; i < length; i++, pBytes++) { rawByte = *pBytes; if(encBitIndex == 0) { // first 6 bits of the raw byte // enc byte full encByte = (rawByte & 0xFC) >> 2; *pEnc = pEncTable[encByte]; pEnc++; encBitIndex = 0; // last 2 bits of the raw byte, // first 2 bits of enc byte encByte = (rawByte & 0x03) << 4; encBitIndex = 2; } else if(encBitIndex == 2) { // first 4 bits of the raw byte, // last 4 bits of enc byte encByte |= (rawByte & 0xF0) >> 4; *pEnc = pEncTable[encByte]; pEnc++; encBitIndex = 0; // last 4 bits of the raw byte, // first 4 bits of enc byte encByte = (rawByte & 0x0F) << 2; encBitIndex = 4; } else // encBitIndex == 4 { // first 2 bits of raw byte // last 2 bits of enc byte encByte |= (rawByte & 0xC0) >> 6; *pEnc = pEncTable[encByte]; pEnc++; encBitIndex = 0; // last 6 bits of raw byte // enc byte full encByte = rawByte & 0x3F; *pEnc = pEncTable[encByte]; pEnc++; encBitIndex = 0; } } // remainder if(encBitIndex > 0) { *pEnc = pEncTable[encByte]; pEnc++; } // padding for(int i = 0; i < paddingLength; i++) { *pEnc = '='; pEnc++; } // terminating zero *pEnc = 0; return pRet; } //****************************************************************************** // class: RCBase64Encoder // // Encode/decode text and arbitrary data in Base64. //****************************************************************************** @implementation RCBase64Encoder // method: encodeString // // Assumes UTF-8 encoding. + (NSString*) encodeString:(NSString*) raw; { if(raw == nil) { return @""; } // Convert to C string //******************** const char * pRaw = [raw cStringUsingEncoding:NSUTF8StringEncoding]; int length = strlen(pRaw); // Encode //******* const char * pEnc = __rcb64_encode((void*) pRaw, length); NSString * ret = [[NSString alloc] initWithCString:pEnc encoding:NSUTF8StringEncoding]; return ret; } // method: decodeStringAsString // // Assumes UTF-8 encoded string as output. + (NSString*) decodeStringAsString:(NSString*) raw { if(raw == nil) { return @""; } // Convert to C string //******************** const char * pRaw = [raw cStringUsingEncoding:NSUTF8StringEncoding]; // Decode //******* unsigned char * pDec = __rcb64_decode(pRaw); NSString *ret = [[NSString alloc] initWithCString:(char*) pDec encoding:NSUTF8StringEncoding]; return ret; } @end // end RCBase64Encoder