From: Matthew Joyce <matthew.jo...@embedded-brains.de> --- cpukit/base64/base64-decode.c | 165 ++++++++++++++++ cpukit/include/rtems/base64.h | 63 ++++++ spec/build/cpukit/librtemscpu.yml | 1 + .../build/testsuites/unit/unit-no-clock-0.yml | 1 + testsuites/unit/tc-base64-decode.c | 180 ++++++++++++++++++ 5 files changed, 410 insertions(+) create mode 100644 cpukit/base64/base64-decode.c create mode 100644 testsuites/unit/tc-base64-decode.c
diff --git a/cpukit/base64/base64-decode.c b/cpukit/base64/base64-decode.c new file mode 100644 index 0000000000..5739266528 --- /dev/null +++ b/cpukit/base64/base64-decode.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSImplBase64 + * + * @brief This source file contains the implementation of + * _Base64_Decode_initialize() and _Base64_Decode(). + */ + +/* + * Copyright (C) 2023 embedded brains GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rtems/base64.h> + +#define SPACE 253 + +#define PAD 254 + +#define INVALID 255 + +const uint8_t _Base64_Decoding[128] = { + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, + ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, + ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, + ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, + ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, + ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, + ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, + ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, + ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, + ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, + ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, + ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, + ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, + ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, + ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['-'] = 62, + ['/'] = 63, ['_'] = 63, ['='] = PAD, [' '] = SPACE, + ['\t'] = SPACE, ['\n'] = SPACE, ['\v'] = SPACE, ['\f'] = SPACE, + ['\r'] = SPACE, [0] = INVALID, [1] = INVALID, [2] = INVALID, + [3] = INVALID, [4] = INVALID, [5] = INVALID, [6] = INVALID, + [7] = INVALID, [8] = INVALID, [14] = INVALID, [15] = INVALID, + [16] = INVALID, [17] = INVALID, [18] = INVALID, [19] = INVALID, + [20] = INVALID, [21] = INVALID, [22] = INVALID, [23] = INVALID, + [24] = INVALID, [25] = INVALID, [26] = INVALID, [27] = INVALID, + [28] = INVALID, [29] = INVALID, [30] = INVALID, [31] = INVALID, + [33] = INVALID, [34] = INVALID, [35] = INVALID, [36] = INVALID, + [37] = INVALID, [38] = INVALID, [39] = INVALID, [40] = INVALID, + [41] = INVALID, [42] = INVALID, [44] = INVALID, [46] = INVALID, + [58] = INVALID, [59] = INVALID, [60] = INVALID, [62] = INVALID, + [63] = INVALID, [64] = INVALID, [91] = INVALID, [92] = INVALID, + [93] = INVALID, [94] = INVALID, [96] = INVALID, [123] = INVALID, + [124] = INVALID, [125] = INVALID, [126] = INVALID, [127] = INVALID}; + +void _Base64_Decode_initialize(Base64_Decode_control* self, + uint8_t* target, + size_t target_size) { + self->state = BASE64_DECODE_STATE_0; + self->target = target; + self->target_end = target + target_size; +} + +Base64_Decode_status _Base64_Decode(Base64_Decode_control* self, char ch) { + uint8_t decoded_ch; + uint8_t next_ch; + uint8_t* target; + const uint8_t* target_end; + Base64_Decode_state next_state; + + if ((unsigned char)ch >= 128) { + return BASE64_DECODE_INVALID_INPUT; + } + + decoded_ch = _Base64_Decoding[(unsigned char)ch]; + + if (decoded_ch == SPACE) { + return BASE64_DECODE_SUCCESS; + } + + target = self->target; + + if (decoded_ch == PAD) { + self->target_end = target; + return BASE64_DECODE_SUCCESS; + } + + if (decoded_ch == INVALID) { + return BASE64_DECODE_INVALID_INPUT; + } + + target_end = self->target_end; + + if (target == target_end) { + return BASE64_DECODE_OVERFLOW; + } + + switch (self->state) { + case BASE64_DECODE_STATE_0: + *target = decoded_ch << 2; + next_state = BASE64_DECODE_STATE_1; + break; + + case BASE64_DECODE_STATE_1: + *target |= decoded_ch >> 4; + next_ch = (decoded_ch & 0x0fU) << 4; + ++target; + + if (target != target_end) { + *target = next_ch; + } else if (next_ch != 0) { + return BASE64_DECODE_OVERFLOW; + } + + next_state = BASE64_DECODE_STATE_2; + break; + + case BASE64_DECODE_STATE_2: + *target |= decoded_ch >> 2; + next_ch = (decoded_ch & 0x03U) << 6; + ++target; + + if (target != target_end) { + *target = next_ch; + } else if (next_ch != 0) { + return BASE64_DECODE_OVERFLOW; + } + + next_state = BASE64_DECODE_STATE_3; + break; + + default: /* BASE64_DECODE_STATE_3 */ + *target |= decoded_ch; + ++target; + next_state = BASE64_DECODE_STATE_0; + break; + } + + self->state = next_state; + self->target = target; + return BASE64_DECODE_SUCCESS; +} diff --git a/cpukit/include/rtems/base64.h b/cpukit/include/rtems/base64.h index b9d88f7818..58e48a8c33 100644 --- a/cpukit/include/rtems/base64.h +++ b/cpukit/include/rtems/base64.h @@ -126,6 +126,69 @@ int _Base64url_Encode( int wordlen ); +/** + * @brief Represents the base64 and base64url decoder state. + */ +typedef enum { + BASE64_DECODE_STATE_0, + BASE64_DECODE_STATE_1, + BASE64_DECODE_STATE_2, + BASE64_DECODE_STATE_3 +} Base64_Decode_state; + +/** + * @brief Contains the base64 and base64url decoder control. + */ +typedef struct { + Base64_Decode_state state; + uint8_t *target; + const uint8_t *target_end; +} Base64_Decode_control; + +/** + * @brief Maps a 7-bit character to the associated 6-bit integer as defined by + * the base64 or base64url encoding or a special value. + */ +extern const uint8_t _Base64_Decoding[ 128 ]; + +/** + * @brief Initializes the base64 decoder. + * + * @param[out] self is the base64 decoder control to initialize. + * + * @param[out] target is the base address of the target area for decoding. + * + * @param target_size is the size in bytes of the target area for decoding. + */ +void _Base64_Decode_initialize( + Base64_Decode_control *self, + uint8_t *target, + size_t target_size +); + +/** + * @brief Represents the base64 and base64url decoder status. + */ +typedef enum { + BASE64_DECODE_SUCCESS, + BASE64_DECODE_OVERFLOW, + BASE64_DECODE_INVALID_INPUT +} Base64_Decode_status; + +/** + * @brief Decodes the character. + * + * The decoder accepts base64 and base64url encodings. White space is ignored. + * + * @param[in, out] self is the base64 decoder control. + * + * @param ch is the character to decode. + */ +Base64_Decode_status _Base64_Decode( + Base64_Decode_control *self, + char ch +); + /** @} */ #ifdef __cplusplus diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index 24b63af8de..45960099e8 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -529,6 +529,7 @@ source: - cpukit/compression/xz/xz_dec_lzma2.c - cpukit/compression/xz/xz_dec_stream.c - cpukit/base64/base64-encode.c +- cpukit/base64/base64-decode.c - cpukit/dev/flash/flashdev.c - cpukit/dev/i2c/eeprom.c - cpukit/dev/i2c/fpga-i2c-slave.c diff --git a/spec/build/testsuites/unit/unit-no-clock-0.yml b/spec/build/testsuites/unit/unit-no-clock-0.yml index 5d15bf52b7..d61c570183 100644 --- a/spec/build/testsuites/unit/unit-no-clock-0.yml +++ b/spec/build/testsuites/unit/unit-no-clock-0.yml @@ -11,6 +11,7 @@ includes: [] ldflags: [] links: [] source: +- testsuites/unit/tc-base64-decode.c - testsuites/unit/tc-compiler-builtins.c - testsuites/unit/tc-config.c - testsuites/unit/tc-misaligned-builtin-memcpy.c diff --git a/testsuites/unit/tc-base64-decode.c b/testsuites/unit/tc-base64-decode.c new file mode 100644 index 0000000000..dd9d50b069 --- /dev/null +++ b/testsuites/unit/tc-base64-decode.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2023 embedded brains GmbH & Co. KG + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rtems/base64.h> + +#include <rtems/test.h> + +typedef struct { + Base64_Decode_control base; + uint8_t buf[64]; +} test_control; + +static int test_run(test_control* self, const char* payload) { + size_t len = strlen(payload); + + for (size_t i = 0; i < len; ++i) { + int rv = _Base64_Decode(&self->base, payload[i]); + + if (rv != 0) { + return rv; + } + } + + return 0; +} + +static int test_payload(test_control* self, const char* payload) { + memset(self->buf, 0xff, sizeof(self->buf)); + _Base64_Decode_initialize(&self->base, &self->buf[0], sizeof(self->buf)); + return test_run(self, payload); +} + +T_TEST_CASE(IOBase64Decode) { + int rv; + test_control instance; + test_control* self = &instance; + + rv = test_payload(self, "POOL"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[3]); + const uint8_t expected1[] = {0x3c, 0xe3, 0x8b, 0xff}; + T_eq_mem(&self->buf[0], expected1, sizeof(expected1)); + + rv = test_payload(self, "ABCDEFGH"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[6]); + const uint8_t expected2[] = {0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0xff}; + T_eq_mem(&self->buf[0], expected2, sizeof(expected2)); + + /* Non-base64 character results in an error */ + rv = test_payload(self, "PO*OL"); + T_eq_int(rv, BASE64_DECODE_INVALID_INPUT); + T_eq_int(self->base.state, BASE64_DECODE_STATE_2); + T_eq_ptr(self->base.target, &self->buf[1]); + const uint8_t expected3[] = {0x3c}; + T_eq_mem(&self->buf[0], expected3, sizeof(expected3)); + + /* Other non-base64 character results in an error */ + rv = test_payload(self, "PO\x80OL"); + T_eq_int(rv, BASE64_DECODE_INVALID_INPUT); + T_eq_int(self->base.state, BASE64_DECODE_STATE_2); + T_eq_ptr(self->base.target, &self->buf[1]); + T_eq_mem(&self->buf[0], expected3, sizeof(expected3)); + + /* Space characters should be ignored */ + rv = test_payload(self, "P OOL"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[3]); + const uint8_t expected4[] = {0x3c, 0xe3, 0x8b, 0xff}; + T_eq_mem(&self->buf[0], expected4, sizeof(expected4)); + + /* Handle pad characters */ + rv = test_payload(self, "POOL=="); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[3]); + T_eq_ptr(self->base.target, self->base.target_end); + const uint8_t expected5[] = {0x3c, 0xe3, 0x8b, 0xff}; + T_eq_mem(&self->buf[0], expected5, sizeof(expected5)); + + /* If characters come after pad characters, an error results */ + rv = test_payload(self, "POOL==xy"); + T_eq_int(rv, BASE64_DECODE_OVERFLOW); + T_eq_int(self->base.state, BASE64_DECODE_SUCCESS); + T_eq_ptr(self->base.target, &self->buf[3]); + T_eq_ptr(self->base.target, self->base.target_end); + const uint8_t expected6[] = {0x3c, 0xe3, 0x8b, 0xff}; + T_eq_mem(&self->buf[0], expected6, sizeof(expected6)); + + rv = test_payload(self, "POOLPOOL"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[6]); + const uint8_t expected7[] = {0x3c, 0xe3, 0x8b, 0x3c, 0xe3, 0x8b, 0xff}; + T_eq_mem(&self->buf[0], expected7, sizeof(expected7)); + + /* + * Test valid payload with series of target sizes. All target sizes + * less than three are invalid for the given payload and will result + * in an error. + */ + const uint8_t expected9[] = {0x3c, 0xe3, 0x8b, 0xff}; + + for (size_t i = 0; i < 4; ++i) { + memset(&self->buf[0], 0xff, sizeof(self->buf)); + _Base64_Decode_initialize(&self->base, &self->buf[0], i); + rv = test_run(self, "POOL"); + + if (i < 3) { + T_eq_int(rv, BASE64_DECODE_OVERFLOW); + T_eq_int(self->base.state, i); + T_ne_ptr(self->base.target, &self->buf[3]); + T_ne_mem(&self->buf[0], expected9, sizeof(expected9)); + } else { + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->base.target, &self->buf[3]); + T_eq_mem(&self->buf[0], expected9, sizeof(expected9)); + } + } + + /* No overflow in state 1 */ + memset(&self->buf[0], 0xff, sizeof(self->buf)); + _Base64_Decode_initialize(&self->base, &self->buf[0], 1); + rv = test_run(self, "AA"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_2); + T_eq_ptr(self->base.target, &self->buf[1]); + const uint8_t expected10[] = {0x00, 0xff}; + T_eq_mem(&self->buf[0], expected10, sizeof(expected10)); + + /* No overflow in state 2 */ + memset(&self->buf[0], 0xff, sizeof(self->buf)); + _Base64_Decode_initialize(&self->base, &self->buf[0], 2); + rv = test_run(self, "AAA"); + T_eq_int(rv, BASE64_DECODE_SUCCESS); + T_eq_int(self->base.state, BASE64_DECODE_STATE_3); + T_eq_ptr(self->base.target, &self->buf[2]); + const uint8_t expected11[] = {0x00, 0x00, 0xff}; + T_eq_mem(&self->buf[0], expected11, sizeof(expected11)); +} + +T_TEST_CASE(IOBase64DecodeInitialize) { + Base64_Decode_control instance; + Base64_Decode_control* self = &instance; + uint8_t buf[1]; + + memset(self, 0xff, sizeof(*self)); + _Base64_Decode_initialize(self, buf, sizeof(buf)); + T_eq_int(self->state, BASE64_DECODE_STATE_0); + T_eq_ptr(self->target, &buf[0]); + T_eq_ptr(self->target_end, &buf[1]); +} -- 2.35.3 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel