Coverage Report

Created: 2025-10-10 06:29

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_helper_encode.h"
27
#include "sopc_mem_alloc.h"
28
29
// Return the decimal value of hexadecimal digit (0 for errors)
30
static uint8_t char_to_decimal(char c, bool* error)
31
0
{
32
0
    *error = false;
33
0
    if (('0' <= c) && ('9' >= c))
34
0
    {
35
0
        return (uint8_t)(c - '0');
36
0
    }
37
0
    if (('a' <= c) && ('f' >= c))
38
0
    {
39
0
        return (uint8_t)(c - 'a' + 10);
40
0
    }
41
0
    if (('A' <= c) && ('F' >= c))
42
0
    {
43
0
        return (uint8_t)(c - 'A' + 10);
44
0
    }
45
46
0
    *error = true;
47
0
    return 0;
48
0
}
49
50
// You should allocate strlen(src)*2 in dst. n is strlen(src)
51
// Returns n the number of translated chars (< 0 for errors)
52
static int hexlify(const unsigned char* src, char* dst, size_t n)
53
0
{
54
0
    SOPC_ASSERT(n <= INT32_MAX);
55
0
    size_t i;
56
0
    char buffer[3];
57
0
    int res = 0;
58
59
0
    if (!src || !dst)
60
0
        return -1;
61
62
0
    for (i = 0; i < n; ++i)
63
0
    {
64
0
        res = sprintf(buffer, "%02hhx", src[i]); // sprintf copies the last \0 too
65
0
        SOPC_ASSERT(2 == res);
66
0
        memcpy(dst + 2 * i, buffer, 2);
67
0
    }
68
69
0
    return (int) n;
70
0
}
71
72
// You should allocate strlen(src)/2 in dst. n is strlen(dst)
73
// Returns n the number of translated couples (< 0 for errors)
74
static int unhexlify(const char* src, unsigned char* dst, size_t n)
75
0
{
76
0
    SOPC_ASSERT(n <= INT32_MAX);
77
0
    bool error = false;
78
79
0
    if (NULL == src || NULL == dst)
80
0
    {
81
0
        return -1;
82
0
    }
83
84
0
    for (size_t i = 0; i < n; ++i)
85
0
    {
86
0
        uint8_t msb = (uint8_t)(char_to_decimal(src[2 * i], &error) << 4);
87
0
        if (error)
88
0
        {
89
0
            return -2;
90
0
        }
91
0
        uint8_t lsb = char_to_decimal(src[2 * i + 1], &error);
92
0
        if (error)
93
0
        {
94
0
            return -3;
95
0
        }
96
0
        dst[i] = (unsigned char) (msb + lsb);
97
0
    }
98
99
0
    return (int) n;
100
0
}
101
102
/**
103
 * \brief  Get the number of padding characters for base64 ('=').
104
 *
105
 * \param input     A valid pointer to the input.
106
 *
107
 * \param outLen    the number of padding characters (0, 1 or 2)
108
 *
109
 * \return   SOPC_STATUS_OK when successful otherwise SOPC_STATUS_INVALID_PARAMETERS if
110
 *           \p input is NULL, if there is one or multiple characters after padding or has not a number of padding
111
 * characters equal to 0, 1 or 2.
112
 */
113
114
static SOPC_ReturnStatus decode_base64_get_paddinglength(const char* input, size_t* outLen)
115
0
{
116
0
    size_t padding_length = 0;
117
0
    if (NULL == input)
118
0
    {
119
0
        return SOPC_STATUS_INVALID_PARAMETERS;
120
0
    }
121
122
0
    int i = (int) (strlen(input)) - 1;
123
124
0
    while ((0 <= i))
125
0
    {
126
0
        if ('=' == input[i])
127
0
        {
128
0
            if (input[i + 1] != '\000' && input[i + 1] != '=') /* verify that there is no character after an equal */
129
0
            {
130
0
                return SOPC_STATUS_INVALID_PARAMETERS;
131
0
            }
132
133
0
            padding_length++;
134
0
        }
135
136
0
        i--;
137
0
    }
138
0
    if (0 != padding_length && 1 != padding_length && 2 != padding_length)
139
0
    {
140
0
        return SOPC_STATUS_INVALID_PARAMETERS;
141
0
    }
142
0
    else
143
0
    {
144
0
        *outLen = padding_length;
145
0
        return SOPC_STATUS_OK;
146
0
    }
147
0
}
148
149
/* Using https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
150
 * to encode base64 */
151
static bool base64encode(const SOPC_Byte* pInput, size_t pInputLen, char* ppOut, size_t pOutLen)
152
0
{
153
0
    const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
154
0
    size_t pOutIndex = 0;
155
0
    uint32_t n = 0;
156
0
    size_t padCount = pInputLen % 3;
157
0
    uint8_t n0;
158
0
    uint8_t n1;
159
0
    uint8_t n2;
160
0
    uint8_t n3;
161
162
    /* increment over the length of the string, three characters at a time */
163
0
    for (size_t x = 0; x < pInputLen; x += 3)
164
0
    {
165
        /* these three 8-bit (ASCII) characters become one 24-bit number */
166
0
        n = ((uint32_t) pInput[x]) << 16; // parenthesis needed, compiler depending on flags can do the shifting before
167
                                          // conversion to uint32_t, resulting to 0
168
169
0
        if ((x + 1) < pInputLen)
170
0
        {
171
0
            n += ((uint32_t) pInput[x + 1]) << 8; // parenthesis needed, compiler depending on flags can do the shifting
172
                                                  // before conversion to uint32_t, resulting to 0
173
0
        }
174
175
0
        if ((x + 2) < pInputLen)
176
0
        {
177
0
            n += pInput[x + 2];
178
0
        }
179
180
        /* this 24-bit number gets separated into four 6-bit numbers */
181
0
        n0 = (uint8_t)(n >> 18) & 63;
182
0
        n1 = (uint8_t)(n >> 12) & 63;
183
0
        n2 = (uint8_t)(n >> 6) & 63;
184
0
        n3 = (uint8_t) n & 63;
185
186
        /*
187
         * if we have one byte available, then its encoding is spread
188
         * out over two characters
189
         */
190
0
        if (pOutIndex >= pOutLen)
191
0
        {
192
0
            return false; /* indicate failure: buffer too small */
193
0
        }
194
0
        ppOut[pOutIndex] = base64chars[n0];
195
0
        pOutIndex++;
196
0
        if (pOutIndex >= pOutLen)
197
0
        {
198
0
            return false; /* indicate failure: buffer too small */
199
0
        }
200
0
        ppOut[pOutIndex] = base64chars[n1];
201
0
        pOutIndex++;
202
203
        /*
204
         * if we have only two bytes available, then their encoding is
205
         * spread out over three chars
206
         */
207
0
        if ((x + 1) < pInputLen)
208
0
        {
209
0
            if (pOutIndex >= pOutLen)
210
0
            {
211
0
                return false; /* indicate failure: buffer too small */
212
0
            }
213
0
            ppOut[pOutIndex] = base64chars[n2];
214
0
            pOutIndex++;
215
0
        }
216
217
        /*
218
         * if we have all three bytes available, then their encoding is spread
219
         * out over four characters
220
         */
221
0
        if ((x + 2) < pInputLen)
222
0
        {
223
0
            if (pOutIndex >= pOutLen)
224
0
            {
225
0
                return false; /* indicate failure: buffer too small */
226
0
            }
227
0
            ppOut[pOutIndex] = base64chars[n3];
228
0
            pOutIndex++;
229
0
        }
230
0
    }
231
232
    /*
233
     * create and add padding that is required if we did not have a multiple of 3
234
     * number of characters available
235
     */
236
0
    if (padCount > 0)
237
0
    {
238
0
        for (; padCount < 3; padCount++)
239
0
        {
240
0
            if (pOutIndex >= pOutLen)
241
0
            {
242
0
                return false; /* indicate failure: buffer too small */
243
0
            }
244
0
            ppOut[pOutIndex] = '=';
245
0
            pOutIndex++;
246
0
        }
247
0
    }
248
0
    if (pOutIndex >= pOutLen)
249
0
    {
250
0
        return false; /* indicate failure: buffer too small */
251
0
    }
252
0
    ppOut[pOutIndex] = 0;
253
0
    return true; /* indicate success */
254
0
}
255
256
SOPC_ReturnStatus SOPC_HelperEncode_Base64(const SOPC_Byte* pInput, size_t inputLen, char** ppOut, size_t* pOutLen)
257
0
{
258
0
    if (NULL == pInput || NULL == ppOut || NULL == pOutLen)
259
0
    {
260
0
        return SOPC_STATUS_INVALID_PARAMETERS;
261
0
    }
262
0
    *pOutLen = 1 + 4 * ((inputLen + 2) / 3); // + 1 for '\0'
263
0
    if (INT32_MAX < *pOutLen)
264
0
    {
265
0
        return SOPC_STATUS_OUT_OF_MEMORY;
266
0
    }
267
268
0
    *ppOut = SOPC_Calloc(*pOutLen, sizeof(char));
269
0
    if (NULL == *ppOut)
270
0
    {
271
0
        return SOPC_STATUS_OUT_OF_MEMORY;
272
0
    }
273
0
    bool res = base64encode(pInput, inputLen, *ppOut, *pOutLen);
274
0
    if (!res)
275
0
    {
276
0
        SOPC_Free(*ppOut);
277
0
        *ppOut = NULL;
278
0
        *pOutLen = 0;
279
0
        return SOPC_STATUS_NOK;
280
0
    }
281
0
    return SOPC_STATUS_OK;
282
0
}
283
284
/* Using https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C_2
285
 * to decode base64 */
286
0
#define WHITESPACE 64
287
0
#define EQUALS 65
288
0
#define INVALID 66
289
290
static const unsigned char d[] = {
291
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, WHITESPACE, INVALID,
292
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
293
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
294
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, 62,      INVALID, INVALID, INVALID,    63,
295
    52,      53,      54,      55,      56,      57,      58,      59,      60,      61,      INVALID,    INVALID,
296
    INVALID, EQUALS,  INVALID, INVALID, INVALID, 0,       1,       2,       3,       4,       5,          6,
297
    7,       8,       9,       10,      11,      12,      13,      14,      15,      16,      17,         18,
298
    19,      20,      21,      22,      23,      24,      25,      INVALID, INVALID, INVALID, INVALID,    INVALID,
299
    INVALID, 26,      27,      28,      29,      30,      31,      32,      33,      34,      35,         36,
300
    37,      38,      39,      40,      41,      42,      43,      44,      45,      46,      47,         48,
301
    49,      50,      51,      INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
302
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
303
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
304
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
305
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
306
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
307
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
308
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
309
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
310
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
311
    INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,    INVALID,
312
    INVALID, INVALID, INVALID, INVALID};
313
314
/* This function decodes a base64 ByteString. Base64 ByteString shall be null terminated.
315
 * Otherwise, the result is undefined.*/
316
static bool base64decode(const char* input, unsigned char** ppOut, size_t* outLen)
317
0
{
318
0
    if (NULL == input || NULL == outLen)
319
0
    {
320
0
        return false;
321
0
    }
322
323
0
    size_t inputLen = strlen(input);
324
0
    const char* end = input + inputLen;
325
0
    char iter = 0;
326
0
    uint32_t buf = 0;
327
0
    size_t len = 0;
328
0
    size_t paddingLength = 0;
329
0
    bool return_status = true;
330
0
    SOPC_ReturnStatus status = decode_base64_get_paddinglength(input, &paddingLength);
331
332
0
    if (SOPC_STATUS_OK != status)
333
0
    {
334
0
        return false;
335
0
    }
336
337
0
    size_t resIter = (inputLen - paddingLength) % 4;
338
339
0
    if (resIter > 0)
340
0
    {
341
0
        resIter--;
342
0
    }
343
344
0
    size_t expectedLen = 3 * ((inputLen - paddingLength) / 4) + resIter;
345
346
0
    unsigned char* pBuffer = SOPC_Calloc(expectedLen + 1, sizeof(char)); // +1 for \0 end character
347
348
0
    if (NULL == pBuffer)
349
0
    {
350
0
        return false;
351
0
    }
352
353
0
    unsigned char* pHeadBuffer = pBuffer;
354
355
0
    while (return_status && input < end)
356
0
    {
357
0
        unsigned char c = d[(int) *input];
358
0
        input++;
359
360
0
        switch (c)
361
0
        {
362
0
        case WHITESPACE:
363
0
            break; /* skip whitespace */
364
0
        case INVALID:
365
0
            return_status = false; /* invalid input, return error */
366
0
            break;
367
0
        case EQUALS: /* pad character, end of data */
368
0
            input = end;
369
0
            break;
370
0
        default:
371
0
            SOPC_ASSERT(c < 64);
372
0
            buf = buf << 6 | c;
373
0
            iter++; // increment the number of iteration
374
            /* If the buffer is full, split it into bytes */
375
0
            if (iter == 4)
376
0
            {
377
0
                len += 3;
378
0
                if (len > expectedLen)
379
0
                {
380
0
                    return_status = false; /* buffer overflow */
381
0
                }
382
0
                else
383
0
                {
384
0
                    *(pBuffer++) = (buf >> 16) & 255;
385
0
                    *(pBuffer++) = (buf >> 8) & 255;
386
0
                    *(pBuffer++) = buf & 255;
387
0
                    buf = 0;
388
0
                    iter = 0;
389
0
                }
390
0
            }
391
0
            break;
392
0
        }
393
0
    }
394
395
0
    if (return_status && iter == 3)
396
0
    {
397
0
        if ((len += 2) > expectedLen)
398
0
        {
399
0
            return_status = false; /* buffer overflow */
400
0
        }
401
0
        else
402
0
        {
403
0
            *(pBuffer++) = (buf >> 10) & 255;
404
0
            *(pBuffer++) = (buf >> 2) & 255;
405
0
        }
406
0
    }
407
0
    else if (return_status && iter == 2)
408
0
    {
409
0
        if (++len > expectedLen)
410
0
        {
411
0
            return_status = false; /* buffer overflow */
412
0
        }
413
0
        else
414
0
        {
415
0
            *(pBuffer++) = (buf >> 4) & 255;
416
0
        }
417
0
    }
418
419
0
    if (expectedLen != len)
420
0
    {
421
0
        return_status = false;
422
0
    }
423
424
0
    if (return_status)
425
0
    {
426
0
        pHeadBuffer[expectedLen] = '\0';
427
0
        *ppOut = pHeadBuffer;
428
0
        *outLen = expectedLen;
429
0
    }
430
0
    else
431
0
    {
432
0
        *ppOut = NULL;
433
0
        *outLen = 0;
434
0
        return_status = false;
435
0
        SOPC_Free(pHeadBuffer);
436
0
    }
437
438
0
    return return_status;
439
0
}
440
441
SOPC_ReturnStatus SOPC_HelperDecode_Base64(const char* pInput, unsigned char** ppOut, size_t* pOutLen)
442
0
{
443
0
    bool res = base64decode(pInput, ppOut, pOutLen);
444
0
    return (res ? SOPC_STATUS_OK : SOPC_STATUS_NOK);
445
0
}
446
447
SOPC_ReturnStatus SOPC_HelperEncode_Hex(const unsigned char* pInput, char* pOut, size_t inputLen)
448
0
{
449
0
    int res = hexlify(pInput, pOut, inputLen);
450
0
    return (0 < res ? SOPC_STATUS_OK : SOPC_STATUS_NOK);
451
0
}
452
453
SOPC_ReturnStatus SOPC_HelperDecode_Hex(const char* pInput, unsigned char* pOut, size_t outputLen)
454
0
{
455
0
    int res = unhexlify(pInput, pOut, outputLen);
456
0
    return (0 < res ? SOPC_STATUS_OK : SOPC_STATUS_NOK);
457
0
}