/* * CyoDecode.c - part of the CyoEncode library * * Copyright (c) 2009-2012, Graham Bull. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * 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. * * 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. */ #include "CyoDecode.h" #include #include //TEMP /********************************** Shared ***********************************/ static int cyoBaseXXValidate( const char* src, size_t size, size_t inputBytes, size_t maxPadding, unsigned char maxValue, const unsigned char table[] ) { /* * returns 0 if the source is a valid baseXX encoding */ if (!src) return -1; /*ERROR - NULL pointer*/ if (size % inputBytes != 0) return -1; /*ERROR - extra characters*/ /* check the bytes */ for (; size >= 1; --size, ++src) { unsigned char ch = *src; if ((ch >= 0x80) || (table[ ch ] > maxValue)) break; } /* check any padding */ for (; 1 <= size && size <= maxPadding; --size, ++src) { unsigned char ch = *src; if ((ch >= 0x80) || (table[ ch ] != maxValue + 1)) break; } /* if size isn't zero then the encoded string isn't valid */ if (size != 0) return -2; /*ERROR - invalid baseXX character*/ /* OK */ return 0; } static size_t cyoBaseXXDecodeGetLength( size_t size, size_t inputBytes, size_t outputBytes ) { if (size % inputBytes != 0) return 0; /*ERROR - extra characters*/ /* OK */ return (((size + inputBytes - 1) / inputBytes) * outputBytes) + 1; /*plus terminator*/ } /****************************** Base16 Decoding ******************************/ static const size_t BASE16_INPUT = 2; static const size_t BASE16_OUTPUT = 1; static const size_t BASE16_MAX_PADDING = 0; static const unsigned char BASE16_MAX_VALUE = 15; static const unsigned char BASE16_TABLE[ 0x80 ] = { /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*28-2f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*30-37*/ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /*8 = '0'-'7'*/ /*38-3f*/ 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*2 = '8'-'9'*/ /*40-47*/ 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, /*6 = 'A'-'F'*/ /*48-4f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*50-57*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*58-5f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*60-67*/ 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xFF, /*6 = 'a'-'f' (same as 'A'-'F')*/ /*68-6f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*70-77*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*78-7f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; int cyoBase16Validate( const char* src, size_t size ) { return cyoBaseXXValidate( src, size, BASE16_INPUT, BASE16_MAX_PADDING, BASE16_MAX_VALUE, BASE16_TABLE ); } size_t cyoBase16DecodeGetLength( size_t size ) { return cyoBaseXXDecodeGetLength( size, BASE16_INPUT, BASE16_OUTPUT ); } size_t cyoBase16Decode( void* dest, const char* src, size_t size ) { /* * output 1 byte for every 2 input: * * outputs: 1 * inputs: 1 = ----1111 = 1111---- * 2 = ----2222 = ----2222 */ if (dest && src && (size % BASE16_INPUT == 0)) { unsigned char* pDest = (unsigned char*)dest; size_t dwSrcSize = size; size_t dwDestSize = 0; unsigned char in1, in2; while (dwSrcSize >= 1) { /* 2 inputs */ in1 = *src++; in2 = *src++; dwSrcSize -= BASE16_INPUT; /* Validate ascii */ if (in1 >= 0x80 || in2 >= 0x80) return 0; /*ERROR - invalid base16 character*/ /* Convert ascii to base16 */ in1 = BASE16_TABLE[ in1 ]; in2 = BASE16_TABLE[ in2 ]; /* Validate base16 */ if (in1 > BASE16_MAX_VALUE || in2 > BASE16_MAX_VALUE) return 0; /*ERROR - invalid base16 character*/ /* 1 output */ *pDest++ = ((in1 << 4) | in2); dwDestSize += BASE16_OUTPUT; } *pDest++ = '\x0'; /*append terminator*/ return dwDestSize; } else return 0; /*ERROR - null pointer, or size isn't a multiple of 2*/ } /****************************** Base32 Decoding ******************************/ static const size_t BASE32_INPUT = 8; static const size_t BASE32_OUTPUT = 5; static const size_t BASE32_MAX_PADDING = 6; static const unsigned char BASE32_MAX_VALUE = 31; static const unsigned char BASE32_TABLE[ 0x80 ] = { /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*28-2f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*30-37*/ 0xFF, 0xFF, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /*6 = '2'-'7'*/ /*38-3f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0xFF, 0xFF, /*1 = '='*/ /*40-47*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'A'-'G'*/ /*48-4f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'H'-'O'*/ /*50-57*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'P'-'W'*/ /*58-5f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*3 = 'X'-'Z'*/ /*60-67*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'a'-'g' (same as 'A'-'G')*/ /*68-6f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'h'-'o' (same as 'H'-'O')*/ /*70-77*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'p'-'w' (same as 'P'-'W')*/ /*78-7f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF /*3 = 'x'-'z' (same as 'X'-'Z')*/ }; int cyoBase32Validate( const char* src, size_t size ) { return cyoBaseXXValidate( src, size, BASE32_INPUT, BASE32_MAX_PADDING, BASE32_MAX_VALUE, BASE32_TABLE ); } size_t cyoBase32DecodeGetLength( size_t size ) { return cyoBaseXXDecodeGetLength( size, BASE32_INPUT, BASE32_OUTPUT ); } size_t cyoBase32Decode( void* dest, const char* src, size_t size ) { /* * output 5 bytes for every 8 input: * * outputs: 1 2 3 4 5 * inputs: 1 = ---11111 = 11111--- * 2 = ---222XX = -----222 XX------ * 3 = ---33333 = --33333- * 4 = ---4XXXX = -------4 XXXX---- * 5 = ---5555X = ----5555 X------- * 6 = ---66666 = -66666-- * 7 = ---77XXX = ------77 XXX----- * 8 = ---88888 = ---88888 */ if (dest && src && (size % BASE32_INPUT == 0)) { unsigned char* pDest = (unsigned char*)dest; size_t dwSrcSize = size; size_t dwDestSize = 0; unsigned char in1, in2, in3, in4, in5, in6, in7, in8; while (dwSrcSize >= 1) { /* 8 inputs */ in1 = *src++; in2 = *src++; in3 = *src++; in4 = *src++; in5 = *src++; in6 = *src++; in7 = *src++; in8 = *src++; dwSrcSize -= BASE32_INPUT; /* Validate ascii */ if ( in1 >= 0x80 || in2 >= 0x80 || in3 >= 0x80 || in4 >= 0x80 || in5 >= 0x80 || in6 >= 0x80 || in7 >= 0x80 || in8 >= 0x80) return 0; /*ERROR - invalid base32 character*/ /* Convert ascii to base16 */ in1 = BASE32_TABLE[ in1 ]; in2 = BASE32_TABLE[ in2 ]; in3 = BASE32_TABLE[ in3 ]; in4 = BASE32_TABLE[ in4 ]; in5 = BASE32_TABLE[ in5 ]; in6 = BASE32_TABLE[ in6 ]; in7 = BASE32_TABLE[ in7 ]; in8 = BASE32_TABLE[ in8 ]; /* Validate base32 */ if (in1 > BASE32_MAX_VALUE || in2 > BASE32_MAX_VALUE) return 0; /*ERROR - invalid base32 character*/ /*the following can be padding*/ if ( in3 > BASE32_MAX_VALUE + 1 || in4 > BASE32_MAX_VALUE + 1 || in5 > BASE32_MAX_VALUE + 1 || in6 > BASE32_MAX_VALUE + 1 || in7 > BASE32_MAX_VALUE + 1 || in8 > BASE32_MAX_VALUE + 1) return 0; /*ERROR - invalid base32 character*/ /* 5 outputs */ *pDest++ = ((in1 & 0x1f) << 3) | ((in2 & 0x1c) >> 2); *pDest++ = ((in2 & 0x03) << 6) | ((in3 & 0x1f) << 1) | ((in4 & 0x10) >> 4); *pDest++ = ((in4 & 0x0f) << 4) | ((in5 & 0x1e) >> 1); *pDest++ = ((in5 & 0x01) << 7) | ((in6 & 0x1f) << 2) | ((in7 & 0x18) >> 3); *pDest++ = ((in7 & 0x07) << 5) | (in8 & 0x1f); dwDestSize += BASE32_OUTPUT; /* Padding */ if (in8 == BASE32_MAX_VALUE + 1) { --dwDestSize; assert( (in7 == BASE32_MAX_VALUE + 1 && in6 == BASE32_MAX_VALUE + 1) || (in7 != BASE32_MAX_VALUE + 1) ); if (in6 == BASE32_MAX_VALUE + 1) { --dwDestSize; if (in5 == BASE32_MAX_VALUE + 1) { --dwDestSize; assert( (in4 == BASE32_MAX_VALUE + 1 && in3 == BASE32_MAX_VALUE + 1) || (in4 != BASE32_MAX_VALUE + 1) ); if (in3 == BASE32_MAX_VALUE + 1) { --dwDestSize; } } } } } *pDest++ = '\x0'; /*append terminator*/ return dwDestSize; } else return 0; /*ERROR - null pointer, or size isn't a multiple of 8*/ } /****************************** Base64 Decoding ******************************/ static const size_t BASE64_INPUT = 4; static const size_t BASE64_OUTPUT = 3; static const size_t BASE64_MAX_PADDING = 2; static const unsigned char BASE64_MAX_VALUE = 63; static const unsigned char BASE64_TABLE[ 0x80 ] = { /*00-07*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*08-0f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*10-17*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*18-1f*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*20-27*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*28-2f*/ 0xFF, 0xFF, 0xFF, 0x3e, 0xFF, 0xFF, 0xFF, 0x3f, /*2 = '+' and '/'*/ /*30-37*/ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /*8 = '0'-'7'*/ /*38-3f*/ 0x3c, 0x3d, 0xFF, 0xFF, 0xFF, 0x40, 0xFF, 0xFF, /*2 = '8'-'9' and '='*/ /*40-47*/ 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /*7 = 'A'-'G'*/ /*48-4f*/ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /*8 = 'H'-'O'*/ /*50-57*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /*8 = 'P'-'W'*/ /*58-5f*/ 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*3 = 'X'-'Z'*/ /*60-67*/ 0xFF, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /*7 = 'a'-'g'*/ /*68-6f*/ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /*8 = 'h'-'o'*/ /*70-77*/ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /*8 = 'p'-'w'*/ /*78-7f*/ 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF /*3 = 'x'-'z'*/ }; int cyoBase64Validate( const char* src, size_t size ) { return cyoBaseXXValidate( src, size, BASE64_INPUT, BASE64_MAX_PADDING, BASE64_MAX_VALUE, BASE64_TABLE ); } size_t cyoBase64DecodeGetLength( size_t size ) { return cyoBaseXXDecodeGetLength( size, BASE64_INPUT, BASE64_OUTPUT ); } size_t cyoBase64Decode( void* dest, const char* src, size_t size ) { /* * output 3 bytes for every 4 input: * * outputs: 1 2 3 * inputs: 1 = --111111 = 111111-- * 2 = --22XXXX = ------22 XXXX---- * 3 = --3333XX = ----3333 XX------ * 4 = --444444 = --444444 */ if (dest && src && (size % BASE64_INPUT == 0)) { unsigned char* pDest = (unsigned char*)dest; size_t dwSrcSize = size; size_t dwDestSize = 0; unsigned char in1, in2, in3, in4; while (dwSrcSize >= 1) { /* 4 inputs */ in1 = *src++; in2 = *src++; in3 = *src++; in4 = *src++; dwSrcSize -= BASE64_INPUT; /* Validate ascii */ if (in1 >= 0x80 || in2 >= 0x80 || in3 >= 0x80 || in4 >= 0x80) return 0; /*ERROR - invalid base64 character*/ /* Convert ascii to base64 */ in1 = BASE64_TABLE[ in1 ]; in2 = BASE64_TABLE[ in2 ]; in3 = BASE64_TABLE[ in3 ]; in4 = BASE64_TABLE[ in4 ]; /* Validate base64 */ if (in1 > BASE64_MAX_VALUE || in2 > BASE64_MAX_VALUE) return 0; /*ERROR - invalid base64 character*/ /*the following can be padding*/ if (in3 > BASE64_MAX_VALUE + 1 || in4 > BASE64_MAX_VALUE + 1) return 0; /*ERROR - invalid base64 character*/ /* 3 outputs */ *pDest++ = ((in1 & 0x3f) << 2) | ((in2 & 0x30) >> 4); *pDest++ = ((in2 & 0x0f) << 4) | ((in3 & 0x3c) >> 2); *pDest++ = ((in3 & 0x03) << 6) | (in4 & 0x3f); dwDestSize += BASE64_OUTPUT; /* Padding */ if (in4 == BASE64_MAX_VALUE + 1) { --dwDestSize; if (in3 == BASE64_MAX_VALUE + 1) { --dwDestSize; } } } *pDest++ = '\x0'; /*append terminator*/ return dwDestSize; } else return 0; /*ERROR - null pointer, or size isn't a multiple of 4*/ }