From 0ffdc8b196a074f6bdbd6f4bbe8a170b45932985 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 15 Jun 2017 21:17:54 +0300 Subject: [PATCH] Add base64url encoding/decoding per RFC 4648 This adds functionality needed for parsing and generating JSON Web Key data structures in DPP. Signed-off-by: Jouni Malinen --- src/utils/base64.c | 139 +++++++++++++++++++++++++++++---------------- src/utils/base64.h | 4 ++ 2 files changed, 95 insertions(+), 48 deletions(-) diff --git a/src/utils/base64.c b/src/utils/base64.c index d44f290e5..22647d5e3 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -13,21 +13,14 @@ static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const unsigned char base64_url_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -/** - * base64_encode - Base64 encode - * @src: Data to be encoded - * @len: Length of the data to be encoded - * @out_len: Pointer to output length variable, or %NULL if not used - * Returns: Allocated buffer of out_len bytes of encoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. Returned buffer is - * nul terminated to make it easier to use as a C string. The nul terminator is - * not included in out_len. - */ -unsigned char * base64_encode(const unsigned char *src, size_t len, - size_t *out_len) + +static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table, + int add_pad) { unsigned char *out, *pos; const unsigned char *end, *in; @@ -35,7 +28,8 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, int line_len; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ - olen += olen / 72; /* line feeds */ + if (add_pad) + olen += olen / 72; /* line feeds */ olen++; /* nul termination */ if (olen < len) return NULL; /* integer overflow */ @@ -48,35 +42,35 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, pos = out; line_len = 0; while (end - in >= 3) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[(((in[1] & 0x0f) << 2) | - (in[2] >> 6)) & 0x3f]; - *pos++ = base64_table[in[2] & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f]; + *pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f]; + *pos++ = table[in[2] & 0x3f]; in += 3; line_len += 4; - if (line_len >= 72) { + if (add_pad && line_len >= 72) { *pos++ = '\n'; line_len = 0; } } if (end - in) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; if (end - in == 1) { - *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; - *pos++ = '='; + *pos++ = table[((in[0] & 0x03) << 4) & 0x3f]; + if (add_pad) + *pos++ = '='; } else { - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = table[((in[1] & 0x0f) << 2) & 0x3f]; } - *pos++ = '='; + if (add_pad) + *pos++ = '='; line_len += 4; } - if (line_len) + if (add_pad && line_len) *pos++ = '\n'; *pos = '\0'; @@ -86,26 +80,18 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, } -/** - * base64_decode - Base64 decode - * @src: Data to be decoded - * @len: Length of the data to be decoded - * @out_len: Pointer to output length variable - * Returns: Allocated buffer of out_len bytes of decoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. - */ -unsigned char * base64_decode(const unsigned char *src, size_t len, - size_t *out_len) +static unsigned char * base64_gen_decode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; + size_t extra_pad; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) - dtable[base64_table[i]] = (unsigned char) i; + dtable[table[i]] = (unsigned char) i; dtable['='] = 0; count = 0; @@ -114,21 +100,28 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, count++; } - if (count == 0 || count % 4) + if (count == 0) return NULL; + extra_pad = (4 - count % 4) % 4; - olen = count / 4 * 3; + olen = (count + extra_pad) / 4 * 3; pos = out = os_malloc(olen); if (out == NULL) return NULL; count = 0; - for (i = 0; i < len; i++) { - tmp = dtable[src[i]]; + for (i = 0; i < len + extra_pad; i++) { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; if (tmp == 0x80) continue; - if (src[i] == '=') + if (val == '=') pad++; block[count] = tmp; count++; @@ -155,3 +148,53 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *out_len = pos - out; return out; } + + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_encode(src, len, out_len, base64_table, 0); +} + + +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad) +{ + return base64_gen_encode(src, len, out_len, base64_url_table, add_pad); +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_table); +} + + +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_url_table); +} diff --git a/src/utils/base64.h b/src/utils/base64.h index aa21fd0fc..5a72c3ebf 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -13,5 +13,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len); +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad); +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len); #endif /* BASE64_H */