Coverage Report

Created: 2026-02-17 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}