/src/S2OPC/src/Common/helpers/sopc_helper_encode.c
Line | Count | Source |
1 | | /* |
2 | | * Licensed to Systerel under one or more contributor license |
3 | | * agreements. See the NOTICE file distributed with this work |
4 | | * for additional information regarding copyright ownership. |
5 | | * Systerel licenses this file to you under the Apache |
6 | | * License, Version 2.0 (the "License"); you may not use this |
7 | | * file except in compliance with the License. You may obtain |
8 | | * a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, |
13 | | * software distributed under the License is distributed on an |
14 | | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
15 | | * KIND, either express or implied. See the License for the |
16 | | * specific language governing permissions and limitations |
17 | | * under the License. |
18 | | */ |
19 | | |
20 | | #include <inttypes.h> |
21 | | #include <stdbool.h> |
22 | | #include <stdio.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include "sopc_assert.h" |
26 | | #include "sopc_common_constants.h" |
27 | | #include "sopc_helper_encode.h" |
28 | | #include "sopc_mem_alloc.h" |
29 | | |
30 | | // Return the decimal value of hexadecimal digit (0 for errors) |
31 | | static uint8_t char_to_decimal(char c, bool* error) |
32 | 0 | { |
33 | 0 | *error = false; |
34 | 0 | if (('0' <= c) && ('9' >= c)) |
35 | 0 | { |
36 | 0 | return (uint8_t)(c - '0'); |
37 | 0 | } |
38 | 0 | if (('a' <= c) && ('f' >= c)) |
39 | 0 | { |
40 | 0 | return (uint8_t)(c - 'a' + 10); |
41 | 0 | } |
42 | 0 | if (('A' <= c) && ('F' >= c)) |
43 | 0 | { |
44 | 0 | return (uint8_t)(c - 'A' + 10); |
45 | 0 | } |
46 | | |
47 | 0 | *error = true; |
48 | 0 | return 0; |
49 | 0 | } |
50 | | |
51 | | // You should allocate strlen(src)*2 in dst. n is strlen(src) |
52 | | // Returns n the number of translated chars (< 0 for errors) |
53 | | static int hexlify(const unsigned char* src, char* dst, size_t n) |
54 | 0 | { |
55 | 0 | SOPC_ASSERT(n <= INT32_MAX); |
56 | 0 | size_t i; |
57 | 0 | char buffer[3]; |
58 | 0 | int res = 0; |
59 | |
|
60 | 0 | if (!src || !dst) |
61 | 0 | return -1; |
62 | | |
63 | 0 | for (i = 0; i < n; ++i) |
64 | 0 | { |
65 | 0 | res = sprintf(buffer, "%02hhx", src[i]); // sprintf copies the last \0 too |
66 | 0 | SOPC_ASSERT(2 == res); |
67 | 0 | memcpy(dst + 2 * i, buffer, 2); |
68 | 0 | } |
69 | | |
70 | 0 | return (int) n; |
71 | 0 | } |
72 | | |
73 | | // Scan src up to maxLen and return its length if a '\0' is found |
74 | | static size_t sopc_strnlen(const char* src, size_t maxLen) |
75 | 0 | { |
76 | 0 | const void* p = memchr(src, '\0', maxLen); |
77 | 0 | return (NULL != p) ? (size_t)((const char*) p - src) : maxLen; |
78 | 0 | } |
79 | | |
80 | | // You should allocate strlen(src)/2 in dst. n is strlen(dst) |
81 | | // Returns n the number of translated couples (< 0 for errors) |
82 | | static int unhexlify(const char* src, unsigned char* dst, size_t n) |
83 | 0 | { |
84 | 0 | SOPC_ASSERT(n <= INT32_MAX); |
85 | 0 | bool error = false; |
86 | |
|
87 | 0 | if (NULL == src || NULL == dst) |
88 | 0 | { |
89 | 0 | return -1; |
90 | 0 | } |
91 | | |
92 | 0 | for (size_t i = 0; i < n; ++i) |
93 | 0 | { |
94 | 0 | uint8_t msb = (uint8_t)(char_to_decimal(src[2 * i], &error) << 4); |
95 | 0 | if (error) |
96 | 0 | { |
97 | 0 | return -2; |
98 | 0 | } |
99 | 0 | uint8_t lsb = char_to_decimal(src[2 * i + 1], &error); |
100 | 0 | if (error) |
101 | 0 | { |
102 | 0 | return -3; |
103 | 0 | } |
104 | 0 | dst[i] = (unsigned char) (msb + lsb); |
105 | 0 | } |
106 | | |
107 | 0 | return (int) n; |
108 | 0 | } |
109 | | |
110 | | /* The following functions have been extracted and slightly adapted from MbedTLS library. */ |
111 | | |
112 | | /** Output buffer too small. */ |
113 | 0 | #define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL -0x002A |
114 | | /** Invalid character in input. */ |
115 | 0 | #define MBEDTLS_ERR_BASE64_INVALID_CHARACTER -0x002C |
116 | | |
117 | | static const unsigned char base64_enc_map[64] = { |
118 | | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', |
119 | | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', |
120 | | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; |
121 | | |
122 | | static const unsigned char base64_dec_map[128] = { |
123 | | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, |
124 | | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62, |
125 | | 127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0, |
126 | | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
127 | | 23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, |
128 | | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127}; |
129 | | |
130 | 0 | #define BASE64_SIZE_T_MAX ((size_t) -1) /* SIZE_T_MAX is not standard */ |
131 | | |
132 | | /* |
133 | | * Encode a buffer into base64 format |
134 | | */ |
135 | | // Note: olen is the size of the output buffer necessary including the null terminator, |
136 | | // and the output buffer is null-terminated. |
137 | | static int mbedtls_base64_encode(unsigned char* dst, size_t dlen, size_t* olen, const unsigned char* src, size_t slen) |
138 | 0 | { |
139 | 0 | size_t i, n; |
140 | 0 | int C1, C2, C3; |
141 | 0 | unsigned char* p; |
142 | |
|
143 | 0 | if (slen == 0) |
144 | 0 | { |
145 | 0 | *olen = 0; |
146 | 0 | return (0); |
147 | 0 | } |
148 | | |
149 | 0 | n = slen / 3 + (slen % 3 != 0); |
150 | |
|
151 | 0 | if (n > (BASE64_SIZE_T_MAX - 1) / 4) |
152 | 0 | { |
153 | 0 | *olen = BASE64_SIZE_T_MAX; |
154 | 0 | return (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL); |
155 | 0 | } |
156 | | |
157 | 0 | n *= 4; |
158 | |
|
159 | 0 | if (dst == NULL || dlen < n + 1) |
160 | 0 | { |
161 | 0 | *olen = n + 1; // +1 for the null terminator |
162 | 0 | return (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL); |
163 | 0 | } |
164 | | |
165 | 0 | n = (slen / 3) * 3; |
166 | |
|
167 | 0 | for (i = 0, p = dst; i < n; i += 3) |
168 | 0 | { |
169 | 0 | C1 = *src++; |
170 | 0 | C2 = *src++; |
171 | 0 | C3 = *src++; |
172 | |
|
173 | 0 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; |
174 | 0 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; |
175 | 0 | *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; |
176 | 0 | *p++ = base64_enc_map[C3 & 0x3F]; |
177 | 0 | } |
178 | |
|
179 | 0 | if (i < slen) |
180 | 0 | { |
181 | 0 | C1 = *src++; |
182 | 0 | C2 = ((i + 1) < slen) ? *src++ : 0; |
183 | |
|
184 | 0 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; |
185 | 0 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; |
186 | |
|
187 | 0 | if ((i + 1) < slen) |
188 | 0 | *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; |
189 | 0 | else |
190 | 0 | *p++ = '='; |
191 | |
|
192 | 0 | *p++ = '='; |
193 | 0 | } |
194 | |
|
195 | 0 | *olen = (size_t)(p - dst + 1); // +1 for the null terminator |
196 | 0 | *p = '\0'; |
197 | |
|
198 | 0 | return (0); |
199 | 0 | } |
200 | | |
201 | | // Note: olen is the size of the output buffer including the null terminator, and the output buffer is null-terminated. |
202 | | static int mbedtls_base64_decode(unsigned char** dst, size_t* olen, const unsigned char* src, size_t slen) |
203 | 0 | { |
204 | 0 | size_t i, n, expectedLen = 0; |
205 | 0 | uint32_t j, x; |
206 | 0 | unsigned char* p; |
207 | |
|
208 | 0 | i = n = j = 0; |
209 | | /* First pass: check for validity and get output length */ |
210 | 0 | while (i < slen) |
211 | 0 | { |
212 | | /* Skip spaces before checking for EOL */ |
213 | 0 | x = 0; |
214 | 0 | while (i < slen && src[i] == ' ') |
215 | 0 | { |
216 | 0 | ++i; |
217 | 0 | ++x; |
218 | 0 | } |
219 | | |
220 | | /* Spaces at end of buffer are OK */ |
221 | 0 | if (i < slen) |
222 | 0 | { |
223 | 0 | if (((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n') || (src[i] == '\n')) |
224 | 0 | { |
225 | | /* Do nothing */ |
226 | 0 | } |
227 | 0 | else |
228 | 0 | { |
229 | | /* Space inside a line is an error */ |
230 | 0 | if (x != 0) |
231 | 0 | return (MBEDTLS_ERR_BASE64_INVALID_CHARACTER); |
232 | | |
233 | 0 | if (src[i] == '=' && ++j > 2) |
234 | 0 | return (MBEDTLS_ERR_BASE64_INVALID_CHARACTER); |
235 | | |
236 | 0 | if (src[i] > 127 || base64_dec_map[src[i]] == 127) |
237 | 0 | return (MBEDTLS_ERR_BASE64_INVALID_CHARACTER); |
238 | | |
239 | 0 | if (base64_dec_map[src[i]] < 64 && j != 0) |
240 | 0 | return (MBEDTLS_ERR_BASE64_INVALID_CHARACTER); |
241 | | |
242 | 0 | n++; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | ++i; |
246 | 0 | } |
247 | | |
248 | 0 | if (n == 0) |
249 | 0 | { |
250 | 0 | *olen = 0; |
251 | 0 | return (0); |
252 | 0 | } |
253 | | |
254 | 0 | n = ((n * 6) + 7) >> 3; |
255 | 0 | n -= j; |
256 | 0 | expectedLen = n + 1; // +1 for the null terminator |
257 | | |
258 | | /* Allocate the necessary buffer */ |
259 | 0 | unsigned char* pBuffer = SOPC_Calloc(expectedLen, sizeof(char)); |
260 | 0 | if (pBuffer == NULL) |
261 | 0 | { |
262 | 0 | *olen = 0; |
263 | 0 | return (SOPC_STATUS_OUT_OF_MEMORY); |
264 | 0 | } |
265 | | |
266 | 0 | for (j = 3, n = x = 0, p = pBuffer; i > 0; i--, src++) |
267 | 0 | { |
268 | 0 | if (*src != '\r' && *src != '\n' && *src != ' ') |
269 | 0 | { |
270 | 0 | j -= (base64_dec_map[*src] == 64); |
271 | 0 | x = (x << 6) | (base64_dec_map[*src] & 0x3F); |
272 | |
|
273 | 0 | if (++n == 4) |
274 | 0 | { |
275 | 0 | n = 0; |
276 | 0 | if (j > 0) |
277 | 0 | *p++ = (unsigned char) (x >> 16); |
278 | 0 | if (j > 1) |
279 | 0 | *p++ = (unsigned char) (x >> 8); |
280 | 0 | if (j > 2) |
281 | 0 | *p++ = (unsigned char) (x); |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | 0 | *p++ = '\0'; // Null-terminate the output buffer |
286 | 0 | *olen = (size_t)(p - pBuffer); |
287 | | /* check that length is the expected one */ |
288 | 0 | SOPC_ASSERT(*olen == expectedLen); |
289 | | |
290 | 0 | *dst = pBuffer; // Assign the allocated buffer to the output pointer |
291 | |
|
292 | 0 | return (0); |
293 | 0 | } |
294 | | |
295 | | SOPC_ReturnStatus SOPC_HelperDecode_Base64(const char* pInput, unsigned char** ppOut, size_t* pOutLen) |
296 | 0 | { |
297 | 0 | if (NULL == pInput || NULL == ppOut || NULL == pOutLen) |
298 | 0 | { |
299 | 0 | return SOPC_STATUS_INVALID_PARAMETERS; |
300 | 0 | } |
301 | 0 | size_t inputLen = sopc_strnlen(pInput, (size_t) SOPC_DEFAULT_MAX_STRING_LENGTH + 1); |
302 | 0 | if (inputLen == 0 || inputLen == (SOPC_DEFAULT_MAX_STRING_LENGTH + 1)) |
303 | 0 | { |
304 | 0 | return SOPC_STATUS_INVALID_PARAMETERS; |
305 | 0 | } |
306 | 0 | int ret = mbedtls_base64_decode(ppOut, pOutLen, (const unsigned char*) pInput, inputLen); |
307 | 0 | if (ret != 0) |
308 | 0 | { |
309 | 0 | SOPC_Free(*ppOut); |
310 | 0 | *ppOut = NULL; |
311 | 0 | *pOutLen = 0; |
312 | 0 | return SOPC_STATUS_NOK; |
313 | 0 | } |
314 | 0 | else if (*pOutLen > 0) |
315 | 0 | { |
316 | 0 | *pOutLen = *pOutLen - 1; // Exclude the null terminator for the final output length |
317 | 0 | } |
318 | | |
319 | 0 | return SOPC_STATUS_OK; |
320 | 0 | } |
321 | | |
322 | | SOPC_ReturnStatus SOPC_HelperEncode_Base64(const SOPC_Byte* pInput, size_t inputLen, char** ppOut, size_t* pOutLen) |
323 | 0 | { |
324 | 0 | if (NULL == pInput || NULL == ppOut || NULL == pOutLen) |
325 | 0 | { |
326 | 0 | return SOPC_STATUS_INVALID_PARAMETERS; |
327 | 0 | } |
328 | | |
329 | 0 | size_t buffer_len = 0; |
330 | | |
331 | | // First call to get required output length |
332 | 0 | int ret = mbedtls_base64_encode(NULL, 0, &buffer_len, (const unsigned char*) pInput, inputLen); |
333 | 0 | if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) |
334 | 0 | { |
335 | 0 | return SOPC_STATUS_NOK; |
336 | 0 | } |
337 | | |
338 | | // Allocate buffer |
339 | 0 | *ppOut = SOPC_Calloc(buffer_len, sizeof(char)); |
340 | 0 | if (NULL == *ppOut) |
341 | 0 | { |
342 | 0 | return SOPC_STATUS_OUT_OF_MEMORY; |
343 | 0 | } |
344 | | |
345 | | // Actual encoding |
346 | 0 | ret = mbedtls_base64_encode((unsigned char*) *ppOut, buffer_len, pOutLen, (const unsigned char*) pInput, inputLen); |
347 | 0 | SOPC_ASSERT(*pOutLen == buffer_len); |
348 | 0 | if (ret != 0) |
349 | 0 | { |
350 | 0 | SOPC_Free(*ppOut); |
351 | 0 | *ppOut = NULL; |
352 | 0 | *pOutLen = 0; |
353 | 0 | return SOPC_STATUS_NOK; |
354 | 0 | } |
355 | 0 | else if (*pOutLen > 0) |
356 | 0 | { |
357 | 0 | *pOutLen = *pOutLen - 1; // Exclude the null terminator for the final output length |
358 | 0 | } |
359 | 0 | return SOPC_STATUS_OK; |
360 | 0 | } |
361 | | |
362 | | SOPC_ReturnStatus SOPC_HelperEncode_Hex(const unsigned char* pInput, char* pOut, size_t inputLen) |
363 | 0 | { |
364 | 0 | int res = hexlify(pInput, pOut, inputLen); |
365 | 0 | return (0 < res ? SOPC_STATUS_OK : SOPC_STATUS_NOK); |
366 | 0 | } |
367 | | |
368 | | SOPC_ReturnStatus SOPC_HelperDecode_Hex(const char* pInput, unsigned char* pOut, size_t outputLen) |
369 | 0 | { |
370 | 0 | int res = unhexlify(pInput, pOut, outputLen); |
371 | 0 | return (0 < res ? SOPC_STATUS_OK : SOPC_STATUS_NOK); |
372 | 0 | } |