diff mbox

[OpenWrt-Devel,libubox,v4] b64: add base64 support

Message ID 1429226039-13576-1-git-send-email-luka@openwrt.org
State Superseded
Headers show

Commit Message

Luka Perkov April 16, 2015, 11:13 p.m. UTC
The base code has been taken from zstream project which was
written by Steven Barth.

Signed-off-by: Luka Perkov <luka@openwrt.org>
CC: Steven Barth <steven@midlink.org>
---
=> changes in v2:

Use new API:

size_t b64decode(void **out, const char *in, size_t len);
size_t b64encode(char **out, const void *in, size_t len);

=> changes in v3:

Use new API:

static inline int b64_decode_size(const void *in, size_t len);
static inline int b64_encode_size(size_t len);

size_t b64decode(void *out, const void *in, size_t len);
size_t b64encode(void *out, const void *in, size_t len);

In this set a few corner cases of invalid writes in b64decode were fixed.
Furthermore, b64decode() works fine when in == out.

=> changes in v4:

Use new API:

static inline int b64_decode_size(size_t len)
static inline int b64_encode_size(size_t len)

size_t b64decode(void *out, const void *in, size_t len);
size_t b64encode(void *out, const void *in, size_t len);

Add terminating NULL byte and make changes reported by Jonas, John and Felix.
---
 CMakeLists.txt |   2 +-
 b64.c          | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 b64.h          |  53 ++++++++++++++++++++++++++
 3 files changed, 171 insertions(+), 1 deletion(-)
 create mode 100644 b64.c
 create mode 100644 b64.h

Comments

Felix Fietkau April 17, 2015, 12:14 a.m. UTC | #1
On 2015-04-17 01:13, Luka Perkov wrote:
> The base code has been taken from zstream project which was
> written by Steven Barth.
> 
> Signed-off-by: Luka Perkov <luka@openwrt.org>
> CC: Steven Barth <steven@midlink.org>
> ---
> --- /dev/null
> +++ b/b64.c
> @@ -0,0 +1,117 @@
> +/*
> + * Copyright (C) 2011 Steven Barth <steven@midlink.org>
> + * Copyright (C) 2015 Luka Perkov <luka@openwrt.org>
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#include "b64.h"
> +
> +static const uint8_t b64decode_tbl[] = {
> +	0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
> +	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
> +	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01,
> +	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
> +	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
> +	0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
> +	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
> +	0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
> +	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
> +	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
> +};
> +
> +size_t b64decode(void *out, const void *in, size_t len)
> +{
> +	uint8_t *o = (uint8_t *) out;
> +	const uint8_t *data = (const uint8_t *) in;
> +	size_t lenout, i, j;
> +	uint32_t cv = 0;
> +
> +	lenout = b64_decode_size(len);
> +	if (!lenout)
> +		return 0;
> +
> +	o[--lenout] = '\0';
> +
> +	for (i = 0; i < len; i += 4) {
> +		cv = 0;
> +		for (j = 0; j < 4; j++) {
> +			uint8_t c = data[i + j] - 43;
> +			if (c > 79 || (c = b64decode_tbl[c]) == 0xff)
> +				return 0;
> +
> +			cv |= c;
> +			if (j != 3)
> +				cv <<= 6;
> +		}
> +
> +		o[2] = (uint8_t)(cv & 0xff);
> +		o[1] = (uint8_t)((cv >> 8) & 0xff);
> +		o[0] = (uint8_t)((cv >> 16) & 0xff);
> +		o += 3;
> +	}
> +
> +	for (i = 1; i <= 2; i++) {
> +		if (data[len - i] == '=') {
> +			o[-i] = '\0';
> +			lenout--;
> +		} else
> +			break;
> +	}
I think this function should match the capabilities and return code of
b64_pton from BSD.
It should return an int (or ssize_t) instead of size_t, and return -1 on
errors. It should also be able to skip whitespaces.

> +
> +	return lenout;
> +}
> +
> +static const uint8_t b64encode_tbl[] =
> +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
> +
> +size_t b64encode(void *out, const void *in, size_t len)
> +{
> +	uint8_t *o = (uint8_t *) out;
> +	const uint8_t *data = (const uint8_t *) in;
> +	size_t lenout, pad, i;
> +	uint32_t cv;
> +
> +	lenout = b64_encode_size(len);
> +	if (!lenout)
> +		return 0;
Why call b64_encode_size instead of just setting *o = 0 at the end of
the function?

- Felix
Luka Perkov April 19, 2015, 12:32 a.m. UTC | #2
On Fri, Apr 17, 2015 at 02:14:14AM +0200, Felix Fietkau wrote:
> > +size_t b64decode(void *out, const void *in, size_t len)
> > +{
> > +	uint8_t *o = (uint8_t *) out;
> > +	const uint8_t *data = (const uint8_t *) in;
> > +	size_t lenout, i, j;
> > +	uint32_t cv = 0;
> > +
> > +	lenout = b64_decode_size(len);
> > +	if (!lenout)
> > +		return 0;
> > +
> > +	o[--lenout] = '\0';
> > +
> > +	for (i = 0; i < len; i += 4) {
> > +		cv = 0;
> > +		for (j = 0; j < 4; j++) {
> > +			uint8_t c = data[i + j] - 43;
> > +			if (c > 79 || (c = b64decode_tbl[c]) == 0xff)
> > +				return 0;
> > +
> > +			cv |= c;
> > +			if (j != 3)
> > +				cv <<= 6;
> > +		}
> > +
> > +		o[2] = (uint8_t)(cv & 0xff);
> > +		o[1] = (uint8_t)((cv >> 8) & 0xff);
> > +		o[0] = (uint8_t)((cv >> 16) & 0xff);
> > +		o += 3;
> > +	}
> > +
> > +	for (i = 1; i <= 2; i++) {
> > +		if (data[len - i] == '=') {
> > +			o[-i] = '\0';
> > +			lenout--;
> > +		} else
> > +			break;
> > +	}
> I think this function should match the capabilities and return code of
> b64_pton from BSD.
> It should return an int (or ssize_t) instead of size_t, and return -1 on
> errors. It should also be able to skip whitespaces.

I've chaned API as requested but there is no easy way of adding support
for whitespaces current b64decode() function. So if you really want
support for whitespaces as well as the same capabilities and return
codes I am proposing that we take in function from BSD instead of this
one. Please let me know.

> > +static const uint8_t b64encode_tbl[] =
> > +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
> > +
> > +size_t b64encode(void *out, const void *in, size_t len)
> > +{
> > +	uint8_t *o = (uint8_t *) out;
> > +	const uint8_t *data = (const uint8_t *) in;
> > +	size_t lenout, pad, i;
> > +	uint32_t cv;
> > +
> > +	lenout = b64_encode_size(len);
> > +	if (!lenout)
> > +		return 0;
> Why call b64_encode_size instead of just setting *o = 0 at the end of
> the function?

Fixed in v5 which I've sent few moments ago.

Luka
Felix Fietkau May 8, 2015, 12:33 p.m. UTC | #3
On 2015-04-19 02:32, Luka Perkov wrote:
> On Fri, Apr 17, 2015 at 02:14:14AM +0200, Felix Fietkau wrote:
>> > +size_t b64decode(void *out, const void *in, size_t len)
>> > +{
>> > +	uint8_t *o = (uint8_t *) out;
>> > +	const uint8_t *data = (const uint8_t *) in;
>> > +	size_t lenout, i, j;
>> > +	uint32_t cv = 0;
>> > +
>> > +	lenout = b64_decode_size(len);
>> > +	if (!lenout)
>> > +		return 0;
>> > +
>> > +	o[--lenout] = '\0';
>> > +
>> > +	for (i = 0; i < len; i += 4) {
>> > +		cv = 0;
>> > +		for (j = 0; j < 4; j++) {
>> > +			uint8_t c = data[i + j] - 43;
>> > +			if (c > 79 || (c = b64decode_tbl[c]) == 0xff)
>> > +				return 0;
>> > +
>> > +			cv |= c;
>> > +			if (j != 3)
>> > +				cv <<= 6;
>> > +		}
>> > +
>> > +		o[2] = (uint8_t)(cv & 0xff);
>> > +		o[1] = (uint8_t)((cv >> 8) & 0xff);
>> > +		o[0] = (uint8_t)((cv >> 16) & 0xff);
>> > +		o += 3;
>> > +	}
>> > +
>> > +	for (i = 1; i <= 2; i++) {
>> > +		if (data[len - i] == '=') {
>> > +			o[-i] = '\0';
>> > +			lenout--;
>> > +		} else
>> > +			break;
>> > +	}
>> I think this function should match the capabilities and return code of
>> b64_pton from BSD.
>> It should return an int (or ssize_t) instead of size_t, and return -1 on
>> errors. It should also be able to skip whitespaces.
> 
> I've chaned API as requested but there is no easy way of adding support
> for whitespaces current b64decode() function. So if you really want
> support for whitespaces as well as the same capabilities and return
> codes I am proposing that we take in function from BSD instead of this
> one. Please let me know.
I merged an implementation based on the BSD code, and I will use it in
usign.

- Felix
diff mbox

Patch

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 58381da..77f4842 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,7 @@  IF(JSONC_FOUND)
   INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS})
 ENDIF()
 
-SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c kvlist.c ulog.c)
+SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c kvlist.c ulog.c b64.c)
 
 ADD_LIBRARY(ubox SHARED ${SOURCES})
 ADD_LIBRARY(ubox-static STATIC ${SOURCES})
diff --git a/b64.c b/b64.c
new file mode 100644
index 0000000..b16b42d
--- /dev/null
+++ b/b64.c
@@ -0,0 +1,117 @@ 
+/*
+ * Copyright (C) 2011 Steven Barth <steven@midlink.org>
+ * Copyright (C) 2015 Luka Perkov <luka@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "b64.h"
+
+static const uint8_t b64decode_tbl[] = {
+	0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
+	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
+	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+	0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
+	0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
+};
+
+size_t b64decode(void *out, const void *in, size_t len)
+{
+	uint8_t *o = (uint8_t *) out;
+	const uint8_t *data = (const uint8_t *) in;
+	size_t lenout, i, j;
+	uint32_t cv = 0;
+
+	lenout = b64_decode_size(len);
+	if (!lenout)
+		return 0;
+
+	o[--lenout] = '\0';
+
+	for (i = 0; i < len; i += 4) {
+		cv = 0;
+		for (j = 0; j < 4; j++) {
+			uint8_t c = data[i + j] - 43;
+			if (c > 79 || (c = b64decode_tbl[c]) == 0xff)
+				return 0;
+
+			cv |= c;
+			if (j != 3)
+				cv <<= 6;
+		}
+
+		o[2] = (uint8_t)(cv & 0xff);
+		o[1] = (uint8_t)((cv >> 8) & 0xff);
+		o[0] = (uint8_t)((cv >> 16) & 0xff);
+		o += 3;
+	}
+
+	for (i = 1; i <= 2; i++) {
+		if (data[len - i] == '=') {
+			o[-i] = '\0';
+			lenout--;
+		} else
+			break;
+	}
+
+	return lenout;
+}
+
+static const uint8_t b64encode_tbl[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+size_t b64encode(void *out, const void *in, size_t len)
+{
+	uint8_t *o = (uint8_t *) out;
+	const uint8_t *data = (const uint8_t *) in;
+	size_t lenout, pad, i;
+	uint32_t cv;
+
+	lenout = b64_encode_size(len);
+	if (!lenout)
+		return 0;
+
+	o[--lenout] = '\0';
+
+	for (i = 0; i < len; i += 3) {
+		cv = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];
+		o[3] = b64encode_tbl[cv & 0x3f];
+		o[2] = b64encode_tbl[(cv >> 6) & 0x3f];
+		o[1] = b64encode_tbl[(cv >> 12) & 0x3f];
+		o[0] = b64encode_tbl[(cv >> 18) & 0x3f];
+		o += 4;
+	}
+
+	pad = len % 3;
+	if (pad) {
+		cv = data[len - pad] << 16;
+		o[-1] = '=';
+		o[-2] = '=';
+		if (pad == 2) {
+			cv |= data[len - pad + 1] << 8;
+			o[-2] = b64encode_tbl[(cv >> 6) & 0x3f];
+		}
+		o[-3] = b64encode_tbl[(cv >> 12) & 0x3f];
+		o[-4] = b64encode_tbl[(cv >> 18) & 0x3f];
+	}
+
+	return lenout;
+}
diff --git a/b64.h b/b64.h
new file mode 100644
index 0000000..0c41919
--- /dev/null
+++ b/b64.h
@@ -0,0 +1,53 @@ 
+/*
+ * Copyright (C) 2011 Steven Barth <steven@midlink.org>
+ * Copyright (C) 2015 Luka Perkov <luka@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __LIBUBOX_B64_H
+#define __LIBUBOX_B64_H
+
+#include <stddef.h>
+
+static inline int b64_decode_size(size_t len)
+{
+	int ret;
+
+	if ((len == 0) || (len % 4))
+		return 0;
+
+	ret = (len / 4) * 3;
+
+	return ret + 1;
+}
+
+static inline int b64_encode_size(size_t len)
+{
+	int ret;
+
+	if (len == 0)
+		return 0;
+
+	ret = (len / 3) * 4;
+
+	if (len % 3)
+		ret += 4;
+
+	return ret + 1;
+}
+
+size_t b64decode(void *out, const void *in, size_t len);
+size_t b64encode(void *out, const void *in, size_t len);
+
+#endif