2014-08-12 18:14:11 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Hochschule für Angewandte Wissenschaften Hamburg (HAW)
|
|
|
|
* Copyright (C) 2014 Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
|
|
|
|
*
|
|
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
|
|
* directory for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2018-06-01 13:19:39 +02:00
|
|
|
* @ingroup sys_base64
|
2014-08-12 18:14:11 +02:00
|
|
|
* @{
|
2015-05-22 07:34:41 +02:00
|
|
|
* @file
|
2014-08-12 18:14:11 +02:00
|
|
|
* @brief Functions to encode and decode base64
|
|
|
|
*
|
|
|
|
* @author Martin Landsmann <Martin.Landsmann@HAW-Hamburg.de>
|
|
|
|
* @}
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
#include <stdbool.h>
|
2020-06-30 16:26:50 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
|
2014-08-12 18:14:11 +02:00
|
|
|
#include "base64.h"
|
2020-03-30 15:09:02 +02:00
|
|
|
#include "kernel_defines.h"
|
2014-08-12 18:14:11 +02:00
|
|
|
|
|
|
|
#define BASE64_CAPITAL_UPPER_BOUND (25) /**< base64 'Z' */
|
|
|
|
#define BASE64_SMALL_UPPER_BOUND (51) /**< base64 'z' */
|
|
|
|
#define BASE64_NUMBER_UPPER_BOUND (61) /**< base64 '9' */
|
|
|
|
#define BASE64_PLUS (62) /**< base64 '+' */
|
2020-03-30 15:09:02 +02:00
|
|
|
#define BASE64_MINUS (62) /**< base64 '-' */
|
2014-08-12 18:14:11 +02:00
|
|
|
#define BASE64_SLASH (63) /**< base64 '/' */
|
2020-03-30 15:09:02 +02:00
|
|
|
#define BASE64_UNDERLINE (63) /**< base64 '_' */
|
2014-08-12 18:14:11 +02:00
|
|
|
#define BASE64_EQUALS (0xFE) /**< no base64 symbol '=' */
|
|
|
|
#define BASE64_NOT_DEFINED (0xFF) /**< no base64 symbol */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* returns the corresponding ascii symbol value for the given base64 code
|
|
|
|
*/
|
2020-06-30 16:26:50 +02:00
|
|
|
static char getsymbol(uint8_t code, bool urlsafe)
|
2014-08-12 18:14:11 +02:00
|
|
|
{
|
2020-03-30 15:09:02 +02:00
|
|
|
if (!IS_ACTIVE(MODULE_BASE64URL)) {
|
|
|
|
urlsafe = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (urlsafe && code == BASE64_UNDERLINE) {
|
|
|
|
return '_';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (urlsafe && code == BASE64_MINUS) {
|
|
|
|
return '-';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!urlsafe && code == BASE64_SLASH) {
|
2014-08-12 18:14:11 +02:00
|
|
|
return '/';
|
|
|
|
}
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
if (!urlsafe && code == BASE64_PLUS) {
|
2014-08-12 18:14:11 +02:00
|
|
|
return '+';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code <= BASE64_CAPITAL_UPPER_BOUND) {
|
|
|
|
return (code + 'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code <= BASE64_SMALL_UPPER_BOUND) {
|
|
|
|
return (code + ('z' - BASE64_SMALL_UPPER_BOUND));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code <= BASE64_NUMBER_UPPER_BOUND) {
|
|
|
|
return (code + ('9' - BASE64_NUMBER_UPPER_BOUND));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (char)BASE64_NOT_DEFINED;
|
|
|
|
}
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
static int base64_encode_base(const void *data_in, size_t data_in_size,
|
2020-06-30 16:26:50 +02:00
|
|
|
void *base64_out, size_t *base64_out_size,
|
2020-03-30 15:09:02 +02:00
|
|
|
bool urlsafe)
|
2014-08-12 18:14:11 +02:00
|
|
|
{
|
2020-06-30 16:26:50 +02:00
|
|
|
const uint8_t *in = data_in;
|
|
|
|
uint8_t *out = base64_out;
|
2019-01-17 10:56:24 +01:00
|
|
|
size_t required_size = base64_estimate_encode_size(data_in_size);
|
2014-08-12 18:14:11 +02:00
|
|
|
|
|
|
|
if (data_in == NULL) {
|
|
|
|
return BASE64_ERROR_DATA_IN;
|
|
|
|
}
|
|
|
|
|
2019-10-08 13:01:32 +02:00
|
|
|
if (data_in_size == 0) {
|
|
|
|
*base64_out_size = 0;
|
|
|
|
return BASE64_SUCCESS;
|
2014-08-12 18:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*base64_out_size < required_size) {
|
|
|
|
*base64_out_size = required_size;
|
|
|
|
return BASE64_ERROR_BUFFER_OUT_SIZE;
|
|
|
|
}
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
if (out == NULL) {
|
2014-08-12 18:14:11 +02:00
|
|
|
return BASE64_ERROR_BUFFER_OUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int iterate_base64_buffer = 0;
|
2020-06-30 16:26:50 +02:00
|
|
|
uint8_t n_num = 0;
|
2014-08-12 18:14:11 +02:00
|
|
|
int nLst = 0;
|
|
|
|
int njump = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)(data_in_size); ++i) {
|
2020-06-30 16:26:50 +02:00
|
|
|
uint8_t tmpval;
|
2014-08-12 18:14:11 +02:00
|
|
|
njump++;
|
2018-09-26 18:24:21 +02:00
|
|
|
tmpval = *(in + i);
|
2014-08-12 18:14:11 +02:00
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
n_num = (tmpval >> (2 * njump));
|
2014-08-12 18:14:11 +02:00
|
|
|
|
|
|
|
if (njump == 4) {
|
2020-06-30 16:26:50 +02:00
|
|
|
n_num = nLst << (8 - 2 * njump);
|
2014-08-12 18:14:11 +02:00
|
|
|
njump = 0;
|
|
|
|
nLst = 0;
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
else {
|
2020-06-30 16:26:50 +02:00
|
|
|
n_num += nLst << (8 - 2 * njump);
|
2014-08-12 18:14:11 +02:00
|
|
|
nLst = tmpval & ((1 << njump * 2) - 1);
|
|
|
|
}
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
out[iterate_base64_buffer++] = getsymbol(n_num, urlsafe);
|
2014-08-12 18:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The last character is not finished yet */
|
|
|
|
njump++;
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
n_num = nLst << (8 - 2 * njump);
|
|
|
|
out[iterate_base64_buffer++] = getsymbol(n_num, urlsafe);
|
2014-08-12 18:14:11 +02:00
|
|
|
|
|
|
|
/* if required we append '=' for the required dividability */
|
|
|
|
while (iterate_base64_buffer % 4) {
|
2020-06-30 16:26:50 +02:00
|
|
|
out[iterate_base64_buffer++] = '=';
|
2014-08-12 18:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*base64_out_size = iterate_base64_buffer;
|
|
|
|
|
|
|
|
return BASE64_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
int base64_encode(const void *data_in, size_t data_in_size,
|
2020-06-30 16:26:50 +02:00
|
|
|
void *base64_out, size_t *base64_out_size)
|
2020-03-30 15:09:02 +02:00
|
|
|
{
|
|
|
|
return base64_encode_base(data_in, data_in_size, base64_out, base64_out_size, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if IS_ACTIVE(MODULE_BASE64URL)
|
|
|
|
int base64url_encode(const void *data_in, size_t data_in_size,
|
2020-06-30 16:26:50 +02:00
|
|
|
void *base64_out, size_t *base64_out_size)
|
2020-03-30 15:09:02 +02:00
|
|
|
{
|
|
|
|
return base64_encode_base(data_in, data_in_size, base64_out, base64_out_size, true);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-08-12 18:14:11 +02:00
|
|
|
/*
|
|
|
|
* returns the corresponding base64 code for the given ascii symbol
|
|
|
|
*/
|
2020-06-30 16:26:50 +02:00
|
|
|
static uint8_t getcode(char symbol)
|
2014-08-12 18:14:11 +02:00
|
|
|
{
|
|
|
|
if (symbol == '/') {
|
|
|
|
return BASE64_SLASH;
|
|
|
|
}
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
if (symbol == '_') {
|
|
|
|
return BASE64_UNDERLINE;
|
|
|
|
}
|
|
|
|
|
2014-08-12 18:14:11 +02:00
|
|
|
if (symbol == '+') {
|
|
|
|
return BASE64_PLUS;
|
|
|
|
}
|
|
|
|
|
2020-03-30 15:09:02 +02:00
|
|
|
if (symbol == '-') {
|
|
|
|
return BASE64_MINUS;
|
|
|
|
}
|
|
|
|
|
2014-08-12 18:14:11 +02:00
|
|
|
if (symbol == '=') {
|
|
|
|
/* indicates a padded base64 end */
|
|
|
|
return BASE64_EQUALS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol < '0') {
|
|
|
|
/* indicates that the given symbol is not base64 and should be ignored */
|
|
|
|
return BASE64_NOT_DEFINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol <= '9' && symbol >= '0') {
|
|
|
|
return (symbol + (BASE64_NUMBER_UPPER_BOUND - '9'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol <= 'Z' && symbol >= 'A') {
|
|
|
|
return (symbol - 'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol <= 'z' && symbol >= 'a') {
|
|
|
|
return (symbol + (BASE64_SMALL_UPPER_BOUND - 'z'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* indicates that the given symbol is not base64 and should be ignored */
|
|
|
|
return BASE64_NOT_DEFINED;
|
|
|
|
}
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
int base64_decode(const void *base64_in, size_t base64_in_size,
|
2018-09-26 18:24:21 +02:00
|
|
|
void *data_out, size_t *data_out_size)
|
2014-08-12 18:14:11 +02:00
|
|
|
{
|
2020-06-30 16:26:50 +02:00
|
|
|
uint8_t *out = data_out;
|
|
|
|
const uint8_t *in = base64_in;
|
2019-01-17 10:56:24 +01:00
|
|
|
size_t required_size = base64_estimate_decode_size(base64_in_size);
|
2014-08-12 18:14:11 +02:00
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
if (in == NULL) {
|
2014-08-12 18:14:11 +02:00
|
|
|
return BASE64_ERROR_DATA_IN;
|
|
|
|
}
|
|
|
|
|
2019-10-08 13:01:32 +02:00
|
|
|
if (base64_in_size == 0) {
|
|
|
|
*data_out_size = 0;
|
|
|
|
return BASE64_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-08-12 18:14:11 +02:00
|
|
|
if (base64_in_size < 4) {
|
|
|
|
return BASE64_ERROR_DATA_IN_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*data_out_size < required_size) {
|
|
|
|
*data_out_size = required_size;
|
|
|
|
return BASE64_ERROR_BUFFER_OUT_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data_out == NULL) {
|
|
|
|
return BASE64_ERROR_BUFFER_OUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int iterate_data_buffer = 0;
|
2020-06-30 16:26:50 +02:00
|
|
|
uint8_t n_num = 0;
|
|
|
|
int nLst = getcode(in[0]) << 2;
|
2014-08-12 18:14:11 +02:00
|
|
|
int code = 0;
|
|
|
|
|
|
|
|
int mask = 2;
|
|
|
|
|
|
|
|
for (int i = 1; i < (int)(base64_in_size); i++) {
|
2020-06-30 16:26:50 +02:00
|
|
|
code = getcode(in[i]);
|
2014-08-12 18:14:11 +02:00
|
|
|
|
|
|
|
if (code == BASE64_NOT_DEFINED || code == BASE64_EQUALS) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nm = (0xFF << (2 * mask));
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
n_num = nLst + ((code & (0xFF & nm)) >> (2 * mask));
|
2014-08-12 18:14:11 +02:00
|
|
|
nLst = (code & (0xFF & ~nm)) << (8 - (2 * mask));
|
|
|
|
|
2020-06-30 16:26:50 +02:00
|
|
|
(mask != 3) ? out[iterate_data_buffer++] = n_num : n_num;
|
2014-08-12 18:14:11 +02:00
|
|
|
(mask == 0) ? mask = 3 : mask--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code == BASE64_EQUALS) {
|
|
|
|
/* add the last character to the data_out buffer */
|
2020-06-30 16:26:50 +02:00
|
|
|
out[iterate_data_buffer] = n_num;
|
2014-08-12 18:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*data_out_size = iterate_data_buffer;
|
|
|
|
return BASE64_SUCCESS;
|
|
|
|
}
|