Add a base64 codec, due to popular request
I still think that base64 is awful, but users have spoken.
This commit is contained in:
parent
308684790f
commit
3f272cbbfc
@ -61,6 +61,22 @@ int sodium_hex2bin(unsigned char * const bin, const size_t bin_maxlen,
|
||||
const char * const ignore, size_t * const bin_len,
|
||||
const char ** const hex_end);
|
||||
|
||||
#define sodium_bin2b64_VARIANT_ORIGINAL 1
|
||||
#define sodium_bin2b64_VARIANT_ORIGINAL_NO_PADDING 3
|
||||
#define sodium_bin2b64_VARIANT_URLSAFE 5
|
||||
#define sodium_bin2b64_VARIANT_URLSAFE_NO_PADDING 7
|
||||
|
||||
SODIUM_EXPORT
|
||||
char *sodium_bin2base64(char * const b64, const size_t b64_maxlen,
|
||||
const unsigned char * const bin, const size_t bin_len,
|
||||
const int variant);
|
||||
|
||||
SODIUM_EXPORT
|
||||
int sodium_base642bin(unsigned char * const bin, const size_t bin_maxlen,
|
||||
const char * const b64, const size_t b64_len,
|
||||
const char * const ignore, size_t * const bin_len,
|
||||
const char ** const b64_end, const int variant);
|
||||
|
||||
SODIUM_EXPORT
|
||||
int sodium_mlock(void * const addr, const size_t len);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "utils.h"
|
||||
@ -89,3 +90,218 @@ sodium_hex2bin(unsigned char *const bin, const size_t bin_maxlen,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some macros for constant-time comparisons. These work over values in
|
||||
* the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
|
||||
*
|
||||
* Original code by Thomas Pornin.
|
||||
*/
|
||||
#define EQ(x, y) \
|
||||
((((0U - ((unsigned int) (x) ^ (unsigned int) (y))) >> 8) & 0xFF) ^ 0xFF)
|
||||
#define GT(x, y) ((((unsigned int) (y) - (unsigned int) (x)) >> 8) & 0xFF)
|
||||
#define GE(x, y) (GT(y, x) ^ 0xFF)
|
||||
#define LT(x, y) GT(y, x)
|
||||
#define LE(x, y) GE(y, x)
|
||||
|
||||
static int
|
||||
b64_byte_to_char(unsigned int x)
|
||||
{
|
||||
return (LT(x, 26) & (x + 'A')) |
|
||||
(GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
|
||||
(GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') |
|
||||
(EQ(x, 63) & '/');
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
b64_char_to_byte(int c)
|
||||
{
|
||||
const unsigned int x =
|
||||
(GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
|
||||
(GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
|
||||
(GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
|
||||
(EQ(c, '/') & 63);
|
||||
|
||||
return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
|
||||
}
|
||||
|
||||
static int
|
||||
b64_byte_to_urlsafe_char(unsigned int x)
|
||||
{
|
||||
return (LT(x, 26) & (x + 'A')) |
|
||||
(GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
|
||||
(GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '-') |
|
||||
(EQ(x, 63) & '_');
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
b64_urlsafe_char_to_byte(int c)
|
||||
{
|
||||
const unsigned x =
|
||||
(GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
|
||||
(GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
|
||||
(GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '-') & 62) |
|
||||
(EQ(c, '_') & 63);
|
||||
|
||||
return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
|
||||
}
|
||||
|
||||
|
||||
#define VARIANT_NO_PADDING_MASK 0x2U
|
||||
#define VARIANT_URLSAFE_MASK 0x4U
|
||||
|
||||
char *
|
||||
sodium_bin2base64(char * const b64, const size_t b64_maxlen,
|
||||
const unsigned char * const bin, const size_t bin_len,
|
||||
const int variant)
|
||||
{
|
||||
size_t acc_len = (size_t) 0;
|
||||
size_t b64_len;
|
||||
size_t b64_pos = (size_t) 0;
|
||||
size_t bin_pos = (size_t) 0;
|
||||
size_t nibbles;
|
||||
size_t remainder;
|
||||
unsigned int acc = 0U;
|
||||
|
||||
if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) {
|
||||
sodium_misuse();
|
||||
}
|
||||
nibbles = bin_len / 3;
|
||||
remainder = bin_len - 3 * nibbles;
|
||||
b64_len = nibbles * 4;
|
||||
if (remainder != 0) {
|
||||
if ((((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
|
||||
b64_len += 4;
|
||||
} else {
|
||||
b64_len += 2;
|
||||
if (remainder == 2) {
|
||||
b64_len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b64_maxlen <= b64_len) {
|
||||
sodium_misuse();
|
||||
}
|
||||
if ((((unsigned int) variant) & VARIANT_URLSAFE_MASK) != 0U) {
|
||||
while (bin_pos < bin_len) {
|
||||
acc = (acc << 8) + bin[bin_pos++];
|
||||
acc_len += 8;
|
||||
while (acc_len >= 6) {
|
||||
acc_len -= 6;
|
||||
b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc >> acc_len) & 0x3F);
|
||||
}
|
||||
}
|
||||
if (acc_len > 0) {
|
||||
b64[b64_pos++] = (char) b64_byte_to_urlsafe_char((acc << (6 - acc_len)) & 0x3F);
|
||||
}
|
||||
} else {
|
||||
while (bin_pos < bin_len) {
|
||||
acc = (acc << 8) + bin[bin_pos++];
|
||||
acc_len += 8;
|
||||
while (acc_len >= 6) {
|
||||
acc_len -= 6;
|
||||
b64[b64_pos++] = (char) b64_byte_to_char((acc >> acc_len) & 0x3F);
|
||||
}
|
||||
}
|
||||
if (acc_len > 0) {
|
||||
b64[b64_pos++] = (char) b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
|
||||
}
|
||||
}
|
||||
while (b64_pos < b64_len) {
|
||||
b64[b64_pos++] = '=';
|
||||
}
|
||||
b64[b64_pos++] = 0;
|
||||
|
||||
return b64;
|
||||
}
|
||||
|
||||
static int
|
||||
_sodium_base642bin_skip_padding(const char * const b64, const size_t b64_len,
|
||||
size_t * const b64_pos_p,
|
||||
const char * const ignore, int padding_len)
|
||||
{
|
||||
int c;
|
||||
|
||||
while (padding_len > 0) {
|
||||
if (*b64_pos_p >= b64_len) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
c = b64[*b64_pos_p];
|
||||
if (c == '=') {
|
||||
padding_len--;
|
||||
} else if (ignore == NULL || strchr(ignore, c) == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
(*b64_pos_p)++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sodium_base642bin(unsigned char * const bin, const size_t bin_maxlen,
|
||||
const char * const b64, const size_t b64_len,
|
||||
const char * const ignore, size_t * const bin_len,
|
||||
const char ** const b64_end, const int variant)
|
||||
{
|
||||
size_t acc_len = (size_t) 0;
|
||||
size_t b64_pos = (size_t) 0;
|
||||
size_t bin_pos = (size_t) 0;
|
||||
int is_urlsafe;
|
||||
int ret = 0;
|
||||
unsigned int acc = 0U;
|
||||
unsigned int d;
|
||||
char c;
|
||||
|
||||
if ((((unsigned int) variant) & ~ 0x6U) != 0x1U) {
|
||||
sodium_misuse();
|
||||
}
|
||||
is_urlsafe = ((unsigned int) variant) & VARIANT_URLSAFE_MASK;
|
||||
while (b64_pos < b64_len) {
|
||||
c = b64[b64_pos];
|
||||
if (is_urlsafe) {
|
||||
d = b64_urlsafe_char_to_byte(c);
|
||||
} else {
|
||||
d = b64_char_to_byte(c);
|
||||
}
|
||||
if (d == 0xFF) {
|
||||
if (ignore != NULL && strchr(ignore, c) != NULL) {
|
||||
if (b64_pos >= b64_len) {
|
||||
errno = ERANGE;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
b64_pos++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
acc = (acc << 6) + d;
|
||||
acc_len += 6;
|
||||
if (acc_len >= 8) {
|
||||
acc_len -= 8;
|
||||
if (bin_pos >= bin_maxlen) {
|
||||
errno = ERANGE;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
bin[bin_pos++] = (acc >> acc_len) & 0xFF;
|
||||
}
|
||||
b64_pos++;
|
||||
}
|
||||
if (acc_len > 4U || (acc & ((1U << acc_len) - 1U)) != 0U) {
|
||||
ret = -1;
|
||||
} else if (ret == 0 &&
|
||||
(((unsigned int) variant) & VARIANT_NO_PADDING_MASK) == 0U) {
|
||||
ret = _sodium_base642bin_skip_padding(b64, b64_len, &b64_pos, ignore,
|
||||
acc_len / 2);
|
||||
}
|
||||
if (bin_len != NULL) {
|
||||
*bin_len = bin_pos;
|
||||
}
|
||||
if (b64_end != NULL) {
|
||||
*b64_end = &b64[b64_pos];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user