1/*
2 * Copyright (c) 2004 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 1-Oct-2004
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33
34#include <stdlib.h>
35#include <string.h>
36
37#ifdef _BTEST_
38int main(int argc, char* argv[]) {
39    unsigned char* enc = benc(argv[1], strlen(argv[1]));
40    printf("%s", enc);
41    printf("%s\n", bdec(enc, strlen(enc)));
42}
43#endif
44
45
46/*
47 * The code below derives from "Secure Programming Cookbook for C and
48 * C++"* and adapted by Kogule, Ryo (kogule@opendarwin.org).
49 *
50 * *John Viega and Matt Messier, O'Reilly, 2003
51 *  http://www.secureprogramming.com/
52 *
53 * Readability improvements by Luke Bellandi, 2007 (luke@apple.com)
54 */
55
56typedef enum _B64CommandCodes {
57	B64_NullTerminator		= -3,
58	B64_PaddingCharacter	= -2,
59	B64_IgnorableCharacter	= -1
60} B64CommandCodes;
61
62static char b64revtb[256] = {
63  -3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*0-15*/
64  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16-31*/
65  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /*32-47*/
66  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, /*48-63*/
67  -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /*64-79*/
68  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /*80-95*/
69  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /*96-111*/
70  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /*112-127*/
71  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128-143*/
72  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*144-159*/
73  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*160-175*/
74  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*176-191*/
75  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*192-207*/
76  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*208-223*/
77  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*224-239*/
78  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  /*240-255*/
79};
80
81typedef enum _B64CodecErrorCodes {
82	B64_noError			= 0,
83	B64_decodeError		= 1,
84	B64_bufferOverrun	= 2
85} B64CodecErrorCodes;
86
87#define B64_INPUT_BLOCK_OFFSET	((inputIndex - 1 - ignorableCharacterCount) % 4)
88
89static unsigned int raw_base64_decode(
90  const unsigned char *input, unsigned char *output, size_t inLengthToDecode,
91  size_t *outputDecodedLength)
92{
93    int currentBase64Value;
94	unsigned int inputIndex = 0;
95	unsigned int ignorableCharacterCount = 0;
96	unsigned int i;
97    unsigned char decodedBuffer[3];
98	unsigned char currentInputBlockPaddingCharacterCount = 0;
99	size_t *decodedCharacterCount;
100	size_t dummyValue;
101
102	if (outputDecodedLength == NULL) {
103		// do this so that if caller passes in NULL for outputDecodedLength
104		// it can still be handled easily
105		decodedCharacterCount = &dummyValue;
106	} else {
107		decodedCharacterCount = outputDecodedLength;
108	}
109	*decodedCharacterCount = 0;
110
111    while ( (inputIndex <= inLengthToDecode) &&
112			(currentInputBlockPaddingCharacterCount == 0) )
113	{
114        currentBase64Value = b64revtb[input[inputIndex]];
115		inputIndex++;
116
117        switch (currentBase64Value) {
118			// [1] Handle control characters
119            case B64_NullTerminator:
120                if (B64_INPUT_BLOCK_OFFSET != 0) return B64_decodeError;
121				// copy remaining characters with padding
122				if (currentInputBlockPaddingCharacterCount > 0) {
123				    for (i = 0; i < (3 - currentInputBlockPaddingCharacterCount); i++) {
124						*output++ = decodedBuffer[i];
125						(*decodedCharacterCount)++;
126					}
127				}
128                return B64_noError;
129
130            case B64_PaddingCharacter:
131                if (B64_INPUT_BLOCK_OFFSET < 2) {
132                    /* Invalid here -- only characters 3 and/or 4 of the
133					   input block can be padding */
134                    return B64_decodeError;
135                } else if (B64_INPUT_BLOCK_OFFSET == 2) {
136                    /* Make sure there's appropriate padding */
137                    if (input[inputIndex] != '=') return B64_decodeError;
138                    decodedBuffer[2] = 0;
139                    currentInputBlockPaddingCharacterCount = 2;
140                    break;
141                } else {
142                    currentInputBlockPaddingCharacterCount = 1;
143                    break;
144                }
145                return B64_noError;
146
147            case B64_IgnorableCharacter:
148                ignorableCharacterCount++;
149                break;
150
151            default:
152				// [2] Handle encoded data
153                switch (B64_INPUT_BLOCK_OFFSET) {
154                    case 0:
155                        decodedBuffer[0] = currentBase64Value << 2;
156                        break;
157                    case 1:
158                        decodedBuffer[0] |= (currentBase64Value >> 4);
159                        decodedBuffer[1] = currentBase64Value << 4;
160                        break;
161                    case 2:
162                        decodedBuffer[1] |= (currentBase64Value >> 2);
163                        decodedBuffer[2] = currentBase64Value << 6;
164                        break;
165                    case 3:
166                        decodedBuffer[2] |= currentBase64Value;
167                        for (i = 0; i < (3 - currentInputBlockPaddingCharacterCount); i++) {
168							*output++ = decodedBuffer[i];
169							(*decodedCharacterCount)++;
170						}
171                        break;
172                }
173                break;
174        }
175    }
176
177    if (inputIndex > inLengthToDecode) return B64_bufferOverrun;
178
179    for (i = 0; i < (3 - currentInputBlockPaddingCharacterCount); i++) {
180		*output++ = decodedBuffer[i];
181		(*decodedCharacterCount)++;
182	}
183
184    return B64_noError;
185}
186
187unsigned char* xar_from_base64(const unsigned char* input, size_t inputLength, size_t *outputLength)
188{
189    int err;
190    unsigned char *output;
191
192	// N.B.: This is a conservative estimate of space needed.  It is NOT
193	// an exact value -- the exact length of the decoded data will be
194	// calculated during the decode operation.
195    output = malloc(3 * (inputLength / 4 + 1));
196    if (output == NULL) return NULL;
197
198    err = raw_base64_decode(input, output, inputLength, outputLength);
199
200    if (err != B64_noError) {
201        free(output);
202        return NULL;
203    }
204
205    return output;
206}
207