diff --git a/include/base64.h b/include/base64.h new file mode 100644 index 00000000000..7666e59f37b --- /dev/null +++ b/include/base64.h @@ -0,0 +1,79 @@ +/** + * + * RFC 1521 base64 encoding/decoding + * + * Copyright (C) 2018, Nordic Semiconductor ASA + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef __BASE64_H__ +#define __BASE64_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Encode a buffer into base64 format + * + * @param dst destination buffer + * @param dlen size of the destination buffer + * @param olen number of bytes written + * @param src source buffer + * @param slen amount of data to be encoded + * + * @return 0 if successful, or -ENOMEM if the buffer is too small. + * *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * If that length cannot be represented, then no data is + * written to the buffer and *olen is set to the maximum + * length representable as a size_t. + * + * @note Call this function with dlen = 0 to obtain the + * required buffer size in *olen + */ +int base64_encode(u8_t *dst, size_t dlen, size_t *olen, const u8_t *src, + size_t slen); + +/** + * @brief Decode a base64-formatted buffer + * + * @param dst destination buffer (can be NULL for checking size) + * @param dlen size of the destination buffer + * @param olen number of bytes written + * @param src source buffer + * @param slen amount of data to be decoded + * + * @return 0 if successful, -ENOMEM, or -EINVAL if the input data is + * not correct. *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * @note Call this function with *dst = NULL or dlen = 0 to obtain + * the required buffer size in *olen + */ +int base64_decode(u8_t *dst, size_t dlen, size_t *olen, const u8_t *src, + size_t slen); + +#ifdef __cplusplus +} +#endif + +#endif /* __BASE64_H__ */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ed01d0d7bbb..b1b8b7a5faf 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -4,3 +4,4 @@ if(NOT CONFIG_NATIVE_APPLICATION) add_subdirectory(libc) endif() add_subdirectory_if_kconfig(ring_buffer) +add_subdirectory_if_kconfig(base64) diff --git a/lib/Kconfig b/lib/Kconfig index 72d46dde222..da8892f0de6 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -24,4 +24,11 @@ config RING_BUFFER Enable usage of ring buffers. This is similar to kernel FIFOs but ring buffers manage their own buffer memory and can store arbitrary data. For optimal performance, use buffer sizes that are a power of 2. + +config BASE64 + bool + prompt "Enable base64 encoding and decoding" + default n + help + Enable base64 encoding and decoding functionality endmenu diff --git a/lib/base64/CMakeLists.txt b/lib/base64/CMakeLists.txt new file mode 100644 index 00000000000..e3c4aaf46f6 --- /dev/null +++ b/lib/base64/CMakeLists.txt @@ -0,0 +1 @@ +zephyr_sources(base64.c) diff --git a/lib/base64/base64.c b/lib/base64/base64.c new file mode 100644 index 00000000000..9a86839ac6d --- /dev/null +++ b/lib/base64/base64.c @@ -0,0 +1,215 @@ +/* + * RFC 1521 base64 encoding/decoding + * + * Copyright (C) 2018, Nordic Semiconductor ASA + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include +#include +#include + +static const u8_t base64_enc_map[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/' +}; + +static const u8_t base64_dec_map[128] = { + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, + 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 127, 127, 127, 127, 127 +}; + +#define BASE64_SIZE_T_MAX ((size_t) -1) /* SIZE_T_MAX is not standard */ + +/* + * Encode a buffer into base64 format + */ +int base64_encode(u8_t *dst, size_t dlen, size_t *olen, const u8_t *src, + size_t slen) +{ + size_t i, n; + int C1, C2, C3; + u8_t *p; + + if (slen == 0) { + *olen = 0; + return 0; + } + + n = slen / 3 + (slen % 3 != 0); + + if (n > (BASE64_SIZE_T_MAX - 1) / 4) { + *olen = BASE64_SIZE_T_MAX; + return -ENOMEM; + } + + n *= 4; + + if ((dlen < n + 1) || (!dst)) { + *olen = n + 1; + return -ENOMEM; + } + + n = (slen / 3) * 3; + + for (i = 0, p = dst; i < n; i += 3) { + C1 = *src++; + C2 = *src++; + C3 = *src++; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; + *p++ = base64_enc_map[C3 & 0x3F]; + } + + if (i < slen) { + C1 = *src++; + C2 = ((i + 1) < slen) ? *src++ : 0; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + + if ((i + 1) < slen) { + *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; + } else { + *p++ = '='; + } + + *p++ = '='; + } + + *olen = p - dst; + *p = 0; + + return 0; +} + +/* + * Decode a base64-formatted buffer + */ +int base64_decode(u8_t *dst, size_t dlen, size_t *olen, const u8_t *src, + size_t slen) +{ + size_t i, n; + u32_t j, x; + u8_t *p; + + /* First pass: check for validity and get output length */ + for (i = n = j = 0; i < slen; i++) { + /* Skip spaces before checking for EOL */ + x = 0; + while (i < slen && src[i] == ' ') { + ++i; + ++x; + } + + /* Spaces at end of buffer are OK */ + if (i == slen) { + break; + } + + if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n') { + continue; + } + + if (src[i] == '\n') { + continue; + } + + /* Space inside a line is an error */ + if (x != 0) { + return -EINVAL; + } + + if (src[i] == '=' && ++j > 2) { + return -EINVAL; + } + + if (src[i] > 127 || base64_dec_map[src[i]] == 127) { + return -EINVAL; + } + + if (base64_dec_map[src[i]] < 64 && j != 0) { + return -EINVAL; + } + + n++; + } + + if (n == 0) { + *olen = 0; + return 0; + } + + /* The following expression is to calculate the following formula + * without risk of integer overflow in n: + * n = ( ( n * 6 ) + 7 ) >> 3; + */ + n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3); + n -= j; + + if (dst == NULL || dlen < n) { + *olen = n; + return -ENOMEM; + } + + for (j = 3, n = x = 0, p = dst; i > 0; i--, src++) { + + if (*src == '\r' || *src == '\n' || *src == ' ') { + continue; + } + + j -= (base64_dec_map[*src] == 64); + x = (x << 6) | (base64_dec_map[*src] & 0x3F); + + if (++n == 4) { + n = 0; + if (j > 0) { + *p++ = (unsigned char)(x >> 16); + } + if (j > 1) { + *p++ = (unsigned char)(x >> 8); + } + if (j > 2) { + *p++ = (unsigned char)(x); + } + } + } + + *olen = p - dst; + + return 0; +} +