Line data Source code
1 : #include "base64_functions.h"
2 : #include <kdbassert.h>
3 : #include <kdberrors.h>
4 :
5 : static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6 : #define ALPHABET_LENGTH (sizeof (alphabet) - 1)
7 : static const char padding = '=';
8 :
9 : /**
10 : * @brief encodes arbitrary binary data using the Base64 encoding scheme (RFC4648)
11 : * @param input holds the data to be encoded
12 : * @param inputLength tells how many bytes the input buffer is holding.
13 : * @returns an allocated string holding the Base64 encoded input data or NULL if the string can not be allocated. Must be freed by the
14 : caller.
15 : */
16 53 : char * base64Encode (const kdb_octet_t * input, const size_t inputLength)
17 : #ifdef __llvm__
18 : __attribute__ ((annotate ("oclint:suppress[long method]")))
19 : #endif
20 : {
21 53 : size_t encodedLength = 0;
22 53 : if (inputLength % 3 == 0)
23 : {
24 21 : encodedLength = (inputLength / 3 * 4) + 1;
25 : }
26 : else
27 : {
28 32 : encodedLength = ((inputLength + (3 - (inputLength % 3))) / 3 * 4) + 1;
29 : }
30 53 : ELEKTRA_ASSERT (encodedLength > 0, "Base64 output array size smaller or equal to 0.");
31 :
32 53 : size_t out = 0;
33 53 : char * encoded = elektraMalloc (encodedLength);
34 53 : if (!encoded) return NULL;
35 :
36 354 : for (size_t i = 0; i < inputLength; i += 3)
37 : {
38 301 : if (inputLength - i < 3)
39 : {
40 : // padding required
41 32 : kdb_octet_t padded[3] = { 0 };
42 32 : memcpy (padded, input + i, inputLength - i);
43 :
44 32 : encoded[out++] = alphabet[padded[0] >> 2];
45 32 : encoded[out++] = alphabet[((padded[0] << 4) + (padded[1] >> 4)) & 0x3f];
46 :
47 32 : if (inputLength - i == 2)
48 : {
49 : // 2 octets available in input
50 23 : encoded[out++] = alphabet[((padded[1] << 2) + (padded[2] >> 6)) & 0x3f];
51 23 : encoded[out++] = padding;
52 : }
53 : else
54 : {
55 : // 1 octet available in input
56 9 : encoded[out++] = padding;
57 9 : encoded[out++] = padding;
58 : }
59 : }
60 : else
61 : {
62 : // no padding required
63 269 : encoded[out++] = alphabet[input[i] >> 2];
64 269 : encoded[out++] = alphabet[((input[i] << 4) + (input[i + 1] >> 4)) & 0x3f];
65 269 : encoded[out++] = alphabet[((input[i + 1] << 2) + (input[i + 2] >> 6)) & 0x3f];
66 269 : encoded[out++] = alphabet[input[i + 2] & 0x3f];
67 : }
68 : }
69 53 : encoded[out++] = '\0';
70 53 : return encoded;
71 : }
72 :
73 : /**
74 : * @brief lookup Base64 character in the alphabet and return the index.
75 : * @param character the character to look up
76 : * @param errorFlag set to 1 in case of error
77 : * @returns the index in the Base64 alphabet or 0 if the padding character was detected.
78 : */
79 : static kdb_octet_t getBase64Index (const char character, int * errorFlag)
80 : {
81 2692 : if (character == padding) return 0;
82 :
83 81321 : for (kdb_octet_t i = 0; i < ALPHABET_LENGTH; i++)
84 : {
85 81309 : if (alphabet[i] == character) return i;
86 : }
87 : *errorFlag = 1;
88 : return 0;
89 : }
90 :
91 : /**
92 : * @brief decodes Base64 encoded data.
93 : * @param input holds the Base64 encoded data string
94 : * @param output will be set to an allocated buffer holding the decoded data or NULL if the allocation failed. Must be freed by the caller
95 : on success.
96 : * @param outputLength will be set to the amount of decoded bytes.
97 : * @retval 1 on success
98 : * @retval -1 if the provided string has not been encoded with Base64
99 : * @retval -2 if the output buffer allocation failed
100 : */
101 75 : int base64Decode (const char * input, kdb_octet_t ** output, size_t * outputLength)
102 : #ifdef __llvm__
103 : __attribute__ ((annotate ("oclint:suppress[high ncss method]"), annotate ("oclint:suppress[high npath complexity]"),
104 : annotate ("oclint:suppress[long method]"), annotate ("oclint:suppress[high cyclomatic complexity]")))
105 : #endif
106 : {
107 75 : const size_t inputLen = strlen (input);
108 75 : if (inputLen == 0 || (inputLen == 1 && input[0] == '\0'))
109 : {
110 6 : *output = NULL;
111 6 : *outputLength = 0;
112 6 : return 1;
113 : }
114 :
115 69 : if (inputLen % 4 != 0)
116 : {
117 0 : *output = NULL;
118 0 : return -1;
119 : }
120 :
121 69 : *outputLength = inputLen / 4 * 3;
122 69 : if (input[inputLen - 1] == padding) (*outputLength)--;
123 69 : if (input[inputLen - 2] == padding) (*outputLength)--;
124 :
125 69 : *output = elektraMalloc (*outputLength);
126 69 : if (!(*output)) return -2;
127 :
128 : size_t position = 0;
129 : size_t outputIndex = 0;
130 :
131 739 : for (position = 0; position < inputLen; position += 4)
132 : {
133 673 : int errorFlag = 0;
134 673 : const kdb_octet_t byte0 = getBase64Index (input[position], &errorFlag);
135 673 : const kdb_octet_t byte1 = getBase64Index (input[position + 1], &errorFlag);
136 673 : const kdb_octet_t byte2 = getBase64Index (input[position + 2], &errorFlag);
137 673 : const kdb_octet_t byte3 = getBase64Index (input[position + 3], &errorFlag);
138 :
139 673 : if (errorFlag)
140 : {
141 : // invalid character detected in input string
142 3 : elektraFree (*output);
143 3 : *output = NULL;
144 3 : return -1;
145 : }
146 :
147 670 : (*output)[outputIndex++] = (kdb_octet_t) ((byte0 << 2) + (byte1 >> 4));
148 670 : if (input[position + 2] != padding)
149 : {
150 648 : (*output)[outputIndex++] = (kdb_octet_t) ((byte1 << 4) + (byte2 >> 2));
151 : }
152 670 : if (input[position + 3] != padding)
153 : {
154 619 : (*output)[outputIndex++] = (kdb_octet_t) ((byte2 << 6) + byte3);
155 : }
156 : }
157 : return 1;
158 : }
|