sys/base64: Implement Base 64 Encoding with URL Safe Alphabet

RFC4648 specifies an alternate alphabet for base64 encoding / decoding
where '+' and '/' are exchanged for '-' and '-' to make the resulting
string safe to use in filenames and URLs.

This adds a base64url_encode() function that uses the alternate alphabet.

The base64_decode() function is extended to accept both alphabets.
This commit is contained in:
Benjamin Valentin 2020-03-30 15:09:02 +02:00 committed by Benjamin Valentin
parent e738508308
commit fc89531c61
4 changed files with 77 additions and 7 deletions

View File

@ -42,6 +42,10 @@ ifneq (,$(filter auto_init_saul,$(USEMODULE)))
USEMODULE += saul_init_devs USEMODULE += saul_init_devs
endif endif
ifneq (,$(filter base64url,$(USEMODULE)))
USEMODULE += base64
endif
ifneq (,$(filter csma_sender,$(USEMODULE))) ifneq (,$(filter csma_sender,$(USEMODULE)))
USEMODULE += random USEMODULE += random
USEMODULE += xtimer USEMODULE += xtimer

View File

@ -1,5 +1,6 @@
PSEUDOMODULES += at_urc PSEUDOMODULES += at_urc
PSEUDOMODULES += at24c% PSEUDOMODULES += at24c%
PSEUDOMODULES += base64url
PSEUDOMODULES += can_mbox PSEUDOMODULES += can_mbox
PSEUDOMODULES += can_pm PSEUDOMODULES += can_pm
PSEUDOMODULES += can_raw PSEUDOMODULES += can_raw

View File

@ -18,26 +18,42 @@
* *
*/ */
#include <stdbool.h>
#include "base64.h" #include "base64.h"
#include "kernel_defines.h"
#define BASE64_CAPITAL_UPPER_BOUND (25) /**< base64 'Z' */ #define BASE64_CAPITAL_UPPER_BOUND (25) /**< base64 'Z' */
#define BASE64_SMALL_UPPER_BOUND (51) /**< base64 'z' */ #define BASE64_SMALL_UPPER_BOUND (51) /**< base64 'z' */
#define BASE64_NUMBER_UPPER_BOUND (61) /**< base64 '9' */ #define BASE64_NUMBER_UPPER_BOUND (61) /**< base64 '9' */
#define BASE64_PLUS (62) /**< base64 '+' */ #define BASE64_PLUS (62) /**< base64 '+' */
#define BASE64_MINUS (62) /**< base64 '-' */
#define BASE64_SLASH (63) /**< base64 '/' */ #define BASE64_SLASH (63) /**< base64 '/' */
#define BASE64_UNDERLINE (63) /**< base64 '_' */
#define BASE64_EQUALS (0xFE) /**< no base64 symbol '=' */ #define BASE64_EQUALS (0xFE) /**< no base64 symbol '=' */
#define BASE64_NOT_DEFINED (0xFF) /**< no base64 symbol */ #define BASE64_NOT_DEFINED (0xFF) /**< no base64 symbol */
/* /*
* returns the corresponding ascii symbol value for the given base64 code * returns the corresponding ascii symbol value for the given base64 code
*/ */
static char getsymbol(unsigned char code) static char getsymbol(unsigned char code, bool urlsafe)
{ {
if (code == BASE64_SLASH) { if (!IS_ACTIVE(MODULE_BASE64URL)) {
urlsafe = false;
}
if (urlsafe && code == BASE64_UNDERLINE) {
return '_';
}
if (urlsafe && code == BASE64_MINUS) {
return '-';
}
if (!urlsafe && code == BASE64_SLASH) {
return '/'; return '/';
} }
if (code == BASE64_PLUS) { if (!urlsafe && code == BASE64_PLUS) {
return '+'; return '+';
} }
@ -56,8 +72,9 @@ static char getsymbol(unsigned char code)
return (char)BASE64_NOT_DEFINED; return (char)BASE64_NOT_DEFINED;
} }
int base64_encode(const void *data_in, size_t data_in_size, static int base64_encode_base(const void *data_in, size_t data_in_size,
unsigned char *base64_out, size_t *base64_out_size) unsigned char *base64_out, size_t *base64_out_size,
bool urlsafe)
{ {
const unsigned char *in = data_in; const unsigned char *in = data_in;
size_t required_size = base64_estimate_encode_size(data_in_size); size_t required_size = base64_estimate_encode_size(data_in_size);
@ -103,14 +120,14 @@ int base64_encode(const void *data_in, size_t data_in_size,
nLst = tmpval & ((1 << njump * 2) - 1); nLst = tmpval & ((1 << njump * 2) - 1);
} }
base64_out[iterate_base64_buffer++] = getsymbol(nNum); base64_out[iterate_base64_buffer++] = getsymbol(nNum, urlsafe);
} }
/* The last character is not finished yet */ /* The last character is not finished yet */
njump++; njump++;
nNum = nLst << (8 - 2 * njump); nNum = nLst << (8 - 2 * njump);
base64_out[iterate_base64_buffer++] = getsymbol(nNum); base64_out[iterate_base64_buffer++] = getsymbol(nNum, urlsafe);
/* if required we append '=' for the required dividability */ /* if required we append '=' for the required dividability */
while (iterate_base64_buffer % 4) { while (iterate_base64_buffer % 4) {
@ -122,6 +139,20 @@ int base64_encode(const void *data_in, size_t data_in_size,
return BASE64_SUCCESS; return BASE64_SUCCESS;
} }
int base64_encode(const void *data_in, size_t data_in_size,
unsigned char *base64_out, size_t *base64_out_size)
{
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,
unsigned char *base64_out, size_t *base64_out_size)
{
return base64_encode_base(data_in, data_in_size, base64_out, base64_out_size, true);
}
#endif
/* /*
* returns the corresponding base64 code for the given ascii symbol * returns the corresponding base64 code for the given ascii symbol
*/ */
@ -131,10 +162,18 @@ static int getcode(char symbol)
return BASE64_SLASH; return BASE64_SLASH;
} }
if (symbol == '_') {
return BASE64_UNDERLINE;
}
if (symbol == '+') { if (symbol == '+') {
return BASE64_PLUS; return BASE64_PLUS;
} }
if (symbol == '-') {
return BASE64_MINUS;
}
if (symbol == '=') { if (symbol == '=') {
/* indicates a padded base64 end */ /* indicates a padded base64 end */
return BASE64_EQUALS; return BASE64_EQUALS;

View File

@ -76,6 +76,32 @@ static inline size_t base64_estimate_encode_size(size_t data_in_size)
int base64_encode(const void *data_in, size_t data_in_size, int base64_encode(const void *data_in, size_t data_in_size,
unsigned char *base64_out, size_t *base64_out_size); unsigned char *base64_out, size_t *base64_out_size);
/**
* @brief Encodes a given datum to base64 with URL and Filename Safe Alphabet
* and save the result to the given destination.
*
* @see [RFC 4648, section 5](https://tools.ietf.org/html/rfc4648#section-5)
*
* @note Requires the use of the `base64url` module.
*
* @param[in] data_in pointer to the datum to encode
* @param[in] data_in_size the size of `data_in`
* @param[out] base64_out pointer to store the encoded base64 string
* @param[in,out] base64_out_size pointer to the variable containing the size of `base64_out.`
This value is overwritten with the estimated size used for
the encoded base64 string on BASE64_ERROR_BUFFER_OUT_SIZE.
This value is overwritten with the actual used size for the
encoded base64 string on BASE64_SUCCESS.
* @returns BASE64_SUCCESS on success,
BASE64_ERROR_BUFFER_OUT_SIZE on insufficient size for encoding to `base64_out`,
BASE64_ERROR_BUFFER_OUT if `base64_out` equals NULL
but the `base64_out_size` is sufficient,
BASE64_ERROR_DATA_IN if `data_in` equals NULL.
*/
int base64url_encode(const void *data_in, size_t data_in_size,
unsigned char *base64_out, size_t *base64_out_size);
/** /**
* @brief Decodes a given base64 string and save the result to the given destination. * @brief Decodes a given base64 string and save the result to the given destination.
* @param[out] base64_in pointer to store the encoded base64 string * @param[out] base64_in pointer to store the encoded base64 string