Merge pull request #566 from teg/util-base64-2

util: add base64 handling
This commit is contained in:
Daniel Mack
2015-07-12 14:10:39 -04:00
5 changed files with 338 additions and 19 deletions
+191 -5
View File
@@ -916,32 +916,218 @@ char *hexmem(const void *p, size_t l) {
return r;
}
void *unhexmem(const char *p, size_t l) {
uint8_t *r, *z;
int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
_cleanup_free_ uint8_t *r = NULL;
uint8_t *z;
const char *x;
assert(mem);
assert(len);
assert(p);
z = r = malloc((l + 1) / 2 + 1);
if (!r)
return NULL;
return -ENOMEM;
for (x = p; x < p + l; x += 2) {
int a, b;
a = unhexchar(x[0]);
if (x+1 < p + l)
if (a < 0)
return a;
else if (x+1 < p + l) {
b = unhexchar(x[1]);
else
if (b < 0)
return b;
} else
b = 0;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
*z = 0;
*mem = r;
r = NULL;
*len = (l + 1) / 2;
return 0;
}
/* https://tools.ietf.org/html/rfc4648#section-4 */
char base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return table[x & 63];
}
int unbase64char(char c) {
unsigned offset;
if (c >= 'A' && c <= 'Z')
return c - 'A';
offset = 'Z' - 'A' + 1;
if (c >= 'a' && c <= 'z')
return c - 'a' + offset;
offset += 'z' - 'a' + 1;
if (c >= '0' && c <= '9')
return c - '0' + offset;
offset += '9' - '0' + 1;
if (c == '+')
return offset;
offset ++;
if (c == '/')
return offset;
return -EINVAL;
}
char *base64mem(const void *p, size_t l) {
char *r, *z;
const uint8_t *x;
/* three input bytes makes four output bytes, padding is added so we must round up */
z = r = malloc(4 * (l + 2) / 3 + 1);
if (!r)
return NULL;
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
}
switch (l % 3) {
case 2:
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
*(z++) = '=';
break;
case 1:
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
*(z++) = '=';
*(z++) = '=';
break;
}
*z = 0;
return r;
}
int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
_cleanup_free_ uint8_t *t = NULL;
int a, b, c, d;
uint8_t *r, *z;
const char *x;
size_t len;
assert(p);
/* padding ensures any base63 input has input divisible by 4 */
if (l % 4 != 0)
return -EINVAL;
/* strip the padding */
if (l > 0 && p[l - 1] == '=')
l --;
if (l > 0 && p[l - 1] == '=')
l --;
/* a group of four input bytes needs three output bytes, in case of
padding we need to add two or three extra bytes */
len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
z = r = malloc(len + 1);
if (!r)
return -ENOMEM;
for (x = p; x < p + (l / 4) * 4; x += 4) {
/* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
a = unbase64char(x[0]);
if (a < 0)
return -EINVAL;
b = unbase64char(x[1]);
if (b < 0)
return -EINVAL;
c = unbase64char(x[2]);
if (c < 0)
return -EINVAL;
d = unbase64char(x[3]);
if (d < 0)
return -EINVAL;
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
*(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
}
switch (l % 4) {
case 3:
a = unbase64char(x[0]);
if (a < 0)
return -EINVAL;
b = unbase64char(x[1]);
if (b < 0)
return -EINVAL;
c = unbase64char(x[2]);
if (c < 0)
return -EINVAL;
/* c == 00ZZZZ00 */
if (c & 3)
return -EINVAL;
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
break;
case 2:
a = unbase64char(x[0]);
if (a < 0)
return -EINVAL;
b = unbase64char(x[1]);
if (b < 0)
return -EINVAL;
/* b == 00YY0000 */
if (b & 15)
return -EINVAL;
*(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
break;
}
*z = 0;
*mem = r;
r = NULL;
*_len = len;
return 0;
}
char octchar(int x) {
return '0' + (x & 7);
}
+6 -1
View File
@@ -240,6 +240,8 @@ char octchar(int x) _const_;
int unoctchar(char c) _const_;
char decchar(int x) _const_;
int undecchar(char c) _const_;
char base64char(int x) _const_;
int unbase64char(char c) _const_;
char *cescape(const char *s);
size_t cescape_char(char c, char *buf);
@@ -614,7 +616,10 @@ static inline void *mempset(void *s, int c, size_t n) {
}
char *hexmem(const void *p, size_t l);
void *unhexmem(const char *p, size_t l);
int unhexmem(const char *p, size_t l, void **mem, size_t *len);
char *base64mem(const void *p, size_t l);
int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
char *strextend(char **x, ...) _sentinel_;
char *strrep(const char *s, unsigned n);
+4 -5
View File
@@ -811,13 +811,12 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (client_id_hex) {
if (strlen (client_id_hex) % 2)
if (strlen(client_id_hex) % 2)
return -EINVAL;
lease->client_id = unhexmem (client_id_hex, strlen (client_id_hex));
if (!lease->client_id)
return -ENOMEM;
lease->client_id_len = strlen (client_id_hex) / 2;
r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len);
if (r < 0)
return r;
}
*ret = lease;
+12 -8
View File
@@ -264,6 +264,8 @@ static bool line_begins(const char *s, size_t m, const char *word) {
static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
_cleanup_free_ char *token = NULL;
size_t len;
int r;
if (!b->anonymous_auth)
return 0;
@@ -276,11 +278,12 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
if (l % 2 != 0)
return 0;
token = unhexmem(p, l);
if (!token)
return -ENOMEM;
if (memchr(token, 0, l/2))
r = unhexmem(p, l, (void **) &token, &len);
if (r < 0)
return 0;
if (memchr(token, 0, len))
return 0;
return !!utf8_is_valid(token);
@@ -288,6 +291,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
static int verify_external_token(sd_bus *b, const char *p, size_t l) {
_cleanup_free_ char *token = NULL;
size_t len;
uid_t u;
int r;
@@ -307,11 +311,11 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
if (l % 2 != 0)
return 0;
token = unhexmem(p, l);
if (!token)
return -ENOMEM;
r = unhexmem(p, l, (void**) &token, &len);
if (r < 0)
return 0;
if (memchr(token, 0, l/2))
if (memchr(token, 0, len))
return 0;
r = parse_uid(token, &u);
+125
View File
@@ -390,6 +390,24 @@ static void test_unhexchar(void) {
assert_se(unhexchar('0') == 0x0);
}
static void test_base64char(void) {
assert_se(base64char(0) == 'A');
assert_se(base64char(26) == 'a');
assert_se(base64char(63) == '/');
}
static void test_unbase64char(void) {
assert_se(unbase64char('A') == 0);
assert_se(unbase64char('Z') == 25);
assert_se(unbase64char('a') == 26);
assert_se(unbase64char('z') == 51);
assert_se(unbase64char('0') == 52);
assert_se(unbase64char('9') == 61);
assert_se(unbase64char('+') == 62);
assert_se(unbase64char('/') == 63);
assert_se(unbase64char('=') == -EINVAL);
}
static void test_octchar(void) {
assert_se(octchar(00) == '0');
assert_se(octchar(07) == '7');
@@ -410,6 +428,108 @@ static void test_undecchar(void) {
assert_se(undecchar('9') == 9);
}
static void test_unhexmem(void) {
const char *hex = "efa214921";
const char *hex_invalid = "efa214921o";
_cleanup_free_ char *hex2 = NULL;
_cleanup_free_ void *mem = NULL;
size_t len;
assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
assert_se((hex2 = hexmem(mem, len)));
free(mem);
assert_se(memcmp(hex, hex2, strlen(hex)) == 0);
free(hex2);
assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0);
assert_se((hex2 = hexmem(mem, len)));
assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0);
}
/* https://tools.ietf.org/html/rfc4648#section-10 */
static void test_base64mem(void) {
char *b64;
b64 = base64mem("", strlen(""));
assert_se(b64);
assert_se(streq(b64, ""));
free(b64);
b64 = base64mem("f", strlen("f"));
assert_se(b64);
assert_se(streq(b64, "Zg=="));
free(b64);
b64 = base64mem("fo", strlen("fo"));
assert_se(b64);
assert_se(streq(b64, "Zm8="));
free(b64);
b64 = base64mem("foo", strlen("foo"));
assert_se(b64);
assert_se(streq(b64, "Zm9v"));
free(b64);
b64 = base64mem("foob", strlen("foob"));
assert_se(b64);
assert_se(streq(b64, "Zm9vYg=="));
free(b64);
b64 = base64mem("fooba", strlen("fooba"));
assert_se(b64);
assert_se(streq(b64, "Zm9vYmE="));
free(b64);
b64 = base64mem("foobar", strlen("foobar"));
assert_se(b64);
assert_se(streq(b64, "Zm9vYmFy"));
free(b64);
}
static void test_unbase64mem(void) {
void *mem;
size_t len;
assert_se(unbase64mem("", strlen(""), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), ""));
free(mem);
assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "f"));
free(mem);
assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fo"));
free(mem);
assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foo"));
free(mem);
assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foob"));
free(mem);
assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fooba"));
free(mem);
assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foobar"));
free(mem);
assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL);
assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL);
assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL);
assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL);
}
static void test_cescape(void) {
_cleanup_free_ char *escaped;
@@ -1804,10 +1924,15 @@ int main(int argc, char *argv[]) {
test_in_charset();
test_hexchar();
test_unhexchar();
test_base64char();
test_unbase64char();
test_octchar();
test_unoctchar();
test_decchar();
test_undecchar();
test_unhexmem();
test_base64mem();
test_unbase64mem();
test_cescape();
test_cunescape();
test_foreach_word();