xsec/enc/XSCrypt/XSCryptCryptoBase64.cpp (230 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * XSEC * * XSCryptCryptoBase64 := Internal implementation of a base64 * encoder/decoder * * Author(s): Berin Lautenbach * * $Id$ * */ #include <xsec/enc/XSCrypt/XSCryptCryptoBase64.hpp> #include <xsec/enc/XSECCryptoException.hpp> // -------------------------------------------------------------------------------- // Lookup tables and macros // -------------------------------------------------------------------------------- char Base64LookupTable[] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/', }; #define IS_UPPER(c) (c >= 'A' && c <= 'Z') #define IS_LOWER(c) (c >= 'a' && c <= 'z') #define IS_NUMBR(c) (c >= '0' && c <= '9') #define IS_OTHER(c) (c == '+' || c == '/') #define IS_B64CH(c) (IS_LOWER(c) || IS_UPPER(c) || IS_NUMBR(c) || IS_OTHER(c)) #define IS_B64OE(c) (IS_B64CH(c) || c == '=') // -------------------------------------------------------------------------------- // Decoding // -------------------------------------------------------------------------------- unsigned char decodeCh(unsigned char c) { if (IS_UPPER(c)) return c - 'A'; if (IS_LOWER(c)) return (c - 'a') + 26; if (IS_NUMBR(c)) return (c - '0') + 52; if (c == '+') return 62; if (c == '/') return 63; if (c == '=') return 64; return 65; // error; } void XSCryptCryptoBase64::canonicaliseInput(const unsigned char *inData, unsigned int inLength) { // Canonicalise the input buffer into m_inputBuffer unsigned char buf[400]; // Do 400 bytes at a time unsigned int i, j; j = 0; for (i = 0; i < inLength; ++i) { if (IS_B64OE(inData[i])) { // Have a base64 or '=' char buf[j++] = inData[i]; if (j == 400) { m_inputBuffer.sbMemcpyIn(m_remainingInput, buf, 400); m_remainingInput += 400; j = 0; } } } if (j > 0) { m_inputBuffer.sbMemcpyIn(m_remainingInput, buf, j); m_remainingInput += j; } } void XSCryptCryptoBase64::decodeInit(void) { m_remainingInput = m_remainingOutput = 0; m_allDone = false; m_state = B64_DECODE; } unsigned int XSCryptCryptoBase64::decode(const unsigned char * inData, unsigned int inLength, unsigned char * outData, unsigned int outLength) { // Ensure we are in an appropriate state if (m_state != B64_DECODE) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Attempt to decode when not in decode state"); } // Copy the data into our input buffer canonicaliseInput(inData, inLength); unsigned int i = 0; unsigned char t; while (m_allDone != true && m_remainingInput - i >= 4) { // BYTE 1 t = decodeCh(m_inputBuffer[i++]); if (t > 63) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Invalid character at start of base 64 block"); } m_outputBuffer[m_remainingOutput] = (t << 2); // BYTE 2 t = decodeCh(m_inputBuffer[i++]); if (t > 63) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Invalid character at start of base 64 block"); } // Take top two bits and place at end of current byte m_outputBuffer[m_remainingOutput] = m_outputBuffer[m_remainingOutput] | (t >> 4); m_remainingOutput++; // Have a new complete byte // Take remaining 4 bits and add to outputBuffer m_outputBuffer[m_remainingOutput] = ( t << 4); // BYTE 3 t = decodeCh(m_inputBuffer[i++]); if (t > 64) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Invalid character at start of base 64 block"); } if (t == 64) { // '=' character found m_allDone = true; break; } // Take 4 bits and append to current buffer m_outputBuffer[m_remainingOutput] = m_outputBuffer[m_remainingOutput] | (t >> 2); m_remainingOutput++; // Take last 2 bits and append to buffer m_outputBuffer[m_remainingOutput] = (t << 6); // BYTE 4 t = decodeCh(m_inputBuffer[i++]); if (t > 64) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Invalid character at start of base 64 block"); } if (t == 64) { m_allDone = true; break; } // Place all six bits and end of current byte m_outputBuffer[m_remainingOutput] = m_outputBuffer[m_remainingOutput] | t; m_remainingOutput++; } // Now whatever we've decoded can be placed in the output buffer unsigned int cpyOut = (m_remainingOutput < outLength ? m_remainingOutput : outLength); m_outputBuffer.sbMemcpyOut(outData, cpyOut); // Move the buffers down if (cpyOut != m_remainingOutput) { m_remainingOutput = m_remainingOutput - cpyOut; m_outputBuffer.sbMemshift(0, cpyOut, m_remainingOutput); } else m_remainingOutput = 0; if (i != m_remainingInput) { m_remainingInput -= i; m_inputBuffer.sbMemshift(0, i, m_remainingInput); } else m_remainingInput = 0; // Return however much we have decoded return cpyOut; } unsigned int XSCryptCryptoBase64::decodeFinish(unsigned char * outData, unsigned int outLength) { if (m_state != B64_DECODE) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Attempt to complete a decode when not in decode state"); } m_allDone = true; unsigned int cpyOut = (m_remainingOutput < outLength ? m_remainingOutput : outLength); m_outputBuffer.sbMemcpyOut(outData, cpyOut); // Move the buffers down if (cpyOut != m_remainingOutput) { m_remainingOutput = m_remainingOutput - cpyOut; m_outputBuffer.sbMemshift(0, cpyOut, m_remainingOutput); } else { m_remainingOutput = 0; } return cpyOut; } // -------------------------------------------------------------------------------- // Encoding // -------------------------------------------------------------------------------- void XSCryptCryptoBase64::encodeInit(void) { m_remainingInput = m_remainingOutput = 0; m_allDone = false; m_charCount = 0; m_state = B64_ENCODE; } unsigned int XSCryptCryptoBase64::encode(const unsigned char * inData, unsigned int inLength, unsigned char * outData, unsigned int outLength) { if (m_state != B64_ENCODE) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Attempt to encode when not in encoding state"); } // Copy input data into end of input buffer m_inputBuffer.sbMemcpyIn(m_remainingInput, inData, inLength); m_remainingInput += inLength; unsigned int i = 0; unsigned char t; while (m_allDone == false && m_remainingInput - i >= 3) { // Have a complete block of three bytes to encode // First 6 bits; t = (m_inputBuffer[i] >> 2); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; // 2 bits from byte one and 4 from byte 2 t = ((m_inputBuffer[i++] << 4) & 0x30); t |= (m_inputBuffer[i] >> 4); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; // 4 from byte 2 and 2 from byte 3 t = ((m_inputBuffer[i++] << 2) & 0x3C); t |= (m_inputBuffer[i] >> 6); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; // last 6 bits from byte 3 t = m_inputBuffer[i++] & 0x3F; m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; m_charCount += 4; if (m_charCount >= 76) { m_outputBuffer[m_remainingOutput++] = '\n'; m_charCount = 0; } } // Now copy data out to output buffer unsigned int cpyOut = (m_remainingOutput < outLength ? m_remainingOutput : outLength); m_outputBuffer.sbMemcpyOut(outData, cpyOut); // Move the buffers down if (cpyOut != m_remainingOutput) { m_remainingOutput = m_remainingOutput - cpyOut; m_outputBuffer.sbMemshift(0, cpyOut, m_remainingOutput); } else m_remainingOutput = 0; if (i != m_remainingInput) { m_remainingInput -= i; m_inputBuffer.sbMemshift(0, i, m_remainingInput); } else m_remainingInput = 0; // Return however much we have decoded return cpyOut; } unsigned int XSCryptCryptoBase64::encodeFinish(unsigned char * outData, unsigned int outLength) { if (m_state != B64_ENCODE) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Attempt to complete an encode when not in encoding state"); } if (m_allDone == false && m_remainingInput > 0) { // Will always be < 3 characters remaining in inputBuffer // If necessary - terminate the Base64 string if (m_remainingInput >= 3) { throw XSECCryptoException(XSECCryptoException::Base64Error, "XSCrypt:Base64 - Too much remaining input in input buffer"); } // First 6 bits; unsigned int t = (m_inputBuffer[0] >> 2); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; // 2 bits from byte one and 4 from byte 2 t = ((m_inputBuffer[0] << 4) & 0x30); if (m_remainingInput == 1) { m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; m_outputBuffer[m_remainingOutput++] = '='; m_outputBuffer[m_remainingOutput++] = '='; } else { t |= (m_inputBuffer[1] >> 4); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; // 4 from byte 2 t = ((m_inputBuffer[1] << 2) & 0x3C); m_outputBuffer[m_remainingOutput++] = Base64LookupTable[t]; m_outputBuffer[m_remainingOutput++] = '='; } } m_allDone = true; // Copy out unsigned int cpyOut = (m_remainingOutput < outLength ? m_remainingOutput : outLength); m_outputBuffer.sbMemcpyOut(outData, cpyOut); // Move the buffers down if (cpyOut != m_remainingOutput) { m_remainingOutput = m_remainingOutput - cpyOut; m_outputBuffer.sbMemshift(0, cpyOut, m_remainingOutput); } else { m_remainingOutput = 0; } return cpyOut; }