Patchwork [v2,08/15] Add base64 encoder/decoder

login
register
mail settings
Submitter Jan Kiszka
Date May 22, 2010, 8:18 a.m.
Message ID <ce09322623e5f1feb82feb6c09fe1786a594223b.1274516288.git.jan.kiszka@web.de>
Download mbox | patch
Permalink /patch/53253/
State New
Headers show

Comments

Jan Kiszka - May 22, 2010, 8:18 a.m.
From: Jan Kiszka <jan.kiszka@siemens.com>

Will be used by QBuffer.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 Makefile.objs |    2 +-
 base64.c      |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 base64.h      |   18 +++++
 3 files changed, 221 insertions(+), 1 deletions(-)
 create mode 100644 base64.c
 create mode 100644 base64.h
Blue Swirl - May 22, 2010, 1:59 p.m.
On Sat, May 22, 2010 at 8:18 AM, Jan Kiszka <jan.kiszka@web.de> wrote:
> From: Jan Kiszka <jan.kiszka@siemens.com>
>
> Will be used by QBuffer.
>
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> ---
>  Makefile.objs |    2 +-
>  base64.c      |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  base64.h      |   18 +++++
>  3 files changed, 221 insertions(+), 1 deletions(-)
>  create mode 100644 base64.c
>  create mode 100644 base64.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index acbaf22..2c603b2 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -2,7 +2,7 @@
>  # QObject
>  qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
>  qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
> -qobject-obj-y += qerror.o
> +qobject-obj-y += qerror.o base64.o
>
>  #######################################################################
>  # block-obj-y is code used by both qemu system emulation and qemu-img
> diff --git a/base64.c b/base64.c
> new file mode 100644
> index 0000000..543e8c6
> --- /dev/null
> +++ b/base64.c
> @@ -0,0 +1,202 @@
> +/*
> + * Base64 encoder/decoder conforming to RFC 4648
> + * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
> + *
> + * Copyright (C) 2010 Siemens AG
> + *
> + * Authors:
> + *  Jan Kiszka <jan.kiszka@siemens.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include "inttypes.h"

Why not <inttypes.h>?

> +#include "base64.h"
> +
> +static const char base[] =
> +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
> +
> +static void encode3to4(const char *src, char *dest)
> +{
> +    uint32_t b32 = 0;
> +    int i, j = 18;
> +
> +    for (i = 0; i < 3; i++) {
> +        b32 <<= 8;
> +        b32 |= src[i];
> +    }
> +    for (i = 0; i < 4; i++) {
> +        dest[i] = base[(b32 >> j) & 0x3F];
> +        j -= 6;
> +    }
> +}
> +
> +static void encode2to4(const char *src, char *dest)
> +{
> +    dest[0] = base[(src[0] >> 2) & 0x3F];
> +    dest[1] = base[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)];
> +    dest[2] = base[(src[1] & 0x0F) << 2];
> +    dest[3] = '=';
> +}
> +
> +static void encode1to4(const char *src, char *dest)
> +{
> +    dest[0] = base[(src[0] >> 2) & 0x3F];
> +    dest[1] = base[(src[0] & 0x03) << 4];
> +    dest[2] = '=';
> +    dest[3] = '=';
> +}
> +
> +/*
> + * Encode data in 'src' of length 'srclen' to a base64 string, saving the
> + * null-terminated result in 'dest'. The size of the destition buffer must be
> + * at least ((srclen + 2) / 3) * 4 + 1.
> + */
> +void base64_encode(const void *src, size_t srclen, char *dest)
> +{
> +    while (srclen >= 3) {
> +        encode3to4(src, dest);
> +        src += 3;
> +        dest += 4;
> +        srclen -= 3;
> +    }
> +    switch (srclen) {
> +    case 2:
> +        encode2to4(src, dest);
> +        dest += 4;
> +        break;
> +    case 1:
> +        encode1to4(src, dest);
> +        dest += 4;
> +        break;
> +    case 0:
> +        break;
> +    }
> +    dest[0] = 0;
> +}
> +
> +static int32_t codetovalue(char c)
> +{
> +    if (c >= 'A' && c <= 'Z') {
> +        return c - 'A';
> +    } else if (c >= 'a' && c <= 'z') {
> +        return c - 'a' + 26;
> +    } else if (c >= '0' && c <= '9') {
> +        return c - '0' + 52;
> +    } else if (c == '+') {
> +        return 62;
> +    } else if ( c == '/') {
> +        return 63;
> +    } else {
> +        return -1;
> +    }
> +}
> +
> +static int decode4to3 (const char *src, char *dest)
> +{
> +    uint32_t b32 = 0;
> +    int32_t bits;
> +    int i;
> +
> +    for (i = 0; i < 4; i++) {
> +        bits = codetovalue(src[i]);
> +        if (bits < 0) {
> +            return bits;
> +        }
> +        b32 <<= 6;
> +        b32 |= bits;
> +    }
> +    dest[0] = (b32 >> 16) & 0xFF;
> +    dest[1] = (b32 >> 8) & 0xFF;
> +    dest[2] = b32 & 0xFF;
> +
> +    return 0;
> +}
> +
> +static int decode3to2(const char *src, char *dest)
> +{
> +    uint32_t b32 = 0;
> +    int32_t bits;
> +
> +    bits = codetovalue(src[0]);
> +    if (bits < 0) {
> +        return bits;
> +    }
> +    b32 = (uint32_t)bits;
> +    b32 <<= 6;
> +
> +    bits = codetovalue(src[1]);
> +    if (bits < 0) {
> +        return bits;
> +    }
> +    b32 |= (uint32_t)bits;
> +    b32 <<= 4;
> +
> +    bits = codetovalue(src[2]);
> +    if (bits < 0) {
> +        return bits;
> +    }
> +    b32 |= ((uint32_t)bits) >> 2;
> +
> +    dest[0] = (b32 >> 8) & 0xFF;
> +    dest[1] = b32 & 0xFF;
> +
> +    return 0;
> +}
> +
> +static int decode2to1(const char *src, char *dest)
> +{
> +    uint32_t b32;
> +    int32_t bits;
> +
> +    bits = codetovalue(src[0]);
> +    if (bits < 0) {
> +        return bits;
> +    }
> +    b32 = (uint32_t)bits << 2;
> +
> +    bits = codetovalue(src[1]);
> +    if (bits < 0) {
> +        return bits;
> +    }
> +    b32 |= ((uint32_t)bits) >> 4;
> +
> +    dest[0] = b32;
> +
> +    return 0;
> +}
> +
> +/*
> + * Convert string 'src' of length 'srclen' from base64 to binary form,
> + * saving the result in 'dest'. The size of the destination buffer must be at
> + * least srclen * 3 / 4.
> + *
> + * Returns 0 on success, -1 on conversion error.
> + */
> +int base64_decode(const char *src, size_t srclen, void *dest)

I think dest should be char *, like all the functions where dest is passed to.

> +{
> +    int ret;
> +
> +    while (srclen >= 4) {
> +        ret = decode4to3(src, dest);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +        src += 4;
> +        dest += 3;
> +        srclen -= 4;
> +    }
> +
> +    switch (srclen) {
> +    case 3:
> +        return decode3to2(src, dest);
> +    case 2:
> +        return decode2to1(src, dest);
> +    case 1:
> +        return -1;
> +    default: /* 0 */
> +        return 0;
> +    }
> +}
> diff --git a/base64.h b/base64.h
> new file mode 100644
> index 0000000..9a0e03a
> --- /dev/null
> +++ b/base64.h
> @@ -0,0 +1,18 @@
> +/*
> + * Base64 encoder/decoder conforming to RFC 4648
> + * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
> + *
> + * Copyright (C) 2010 Siemens AG
> + *
> + * Authors:
> + *  Jan Kiszka <jan.kiszka@siemens.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include <unistd.h>

Maybe <stddef.h> instead, it's only for size_t?

> +
> +void base64_encode(const void *src, size_t srclen, char *dest);
> +int base64_decode(const char *src, size_t srclen, void *dest);
> --
> 1.6.0.2
>
>
>
Jan Kiszka - May 23, 2010, 7:55 a.m.
Blue Swirl wrote:
> On Sat, May 22, 2010 at 8:18 AM, Jan Kiszka <jan.kiszka@web.de> wrote:
>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>
>> Will be used by QBuffer.
>>
>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>> ---
>>  Makefile.objs |    2 +-
>>  base64.c      |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  base64.h      |   18 +++++
>>  3 files changed, 221 insertions(+), 1 deletions(-)
>>  create mode 100644 base64.c
>>  create mode 100644 base64.h
>>
>> diff --git a/Makefile.objs b/Makefile.objs
>> index acbaf22..2c603b2 100644
>> --- a/Makefile.objs
>> +++ b/Makefile.objs
>> @@ -2,7 +2,7 @@
>>  # QObject
>>  qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
>>  qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
>> -qobject-obj-y += qerror.o
>> +qobject-obj-y += qerror.o base64.o
>>
>>  #######################################################################
>>  # block-obj-y is code used by both qemu system emulation and qemu-img
>> diff --git a/base64.c b/base64.c
>> new file mode 100644
>> index 0000000..543e8c6
>> --- /dev/null
>> +++ b/base64.c
>> @@ -0,0 +1,202 @@
>> +/*
>> + * Base64 encoder/decoder conforming to RFC 4648
>> + * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
>> + *
>> + * Copyright (C) 2010 Siemens AG
>> + *
>> + * Authors:
>> + *  Jan Kiszka <jan.kiszka@siemens.com>
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + *
>> + */
>> +
>> +#include "inttypes.h"
> 
> Why not <inttypes.h>?

Oops, no intention.

> 
>> +#include "base64.h"
>> +
>> +static const char base[] =
>> +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
>> +
>> +static void encode3to4(const char *src, char *dest)
>> +{
>> +    uint32_t b32 = 0;
>> +    int i, j = 18;
>> +
>> +    for (i = 0; i < 3; i++) {
>> +        b32 <<= 8;
>> +        b32 |= src[i];
>> +    }
>> +    for (i = 0; i < 4; i++) {
>> +        dest[i] = base[(b32 >> j) & 0x3F];
>> +        j -= 6;
>> +    }
>> +}
>> +
>> +static void encode2to4(const char *src, char *dest)
>> +{
>> +    dest[0] = base[(src[0] >> 2) & 0x3F];
>> +    dest[1] = base[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)];
>> +    dest[2] = base[(src[1] & 0x0F) << 2];
>> +    dest[3] = '=';
>> +}
>> +
>> +static void encode1to4(const char *src, char *dest)
>> +{
>> +    dest[0] = base[(src[0] >> 2) & 0x3F];
>> +    dest[1] = base[(src[0] & 0x03) << 4];
>> +    dest[2] = '=';
>> +    dest[3] = '=';
>> +}
>> +
>> +/*
>> + * Encode data in 'src' of length 'srclen' to a base64 string, saving the
>> + * null-terminated result in 'dest'. The size of the destition buffer must be
>> + * at least ((srclen + 2) / 3) * 4 + 1.
>> + */
>> +void base64_encode(const void *src, size_t srclen, char *dest)
>> +{
>> +    while (srclen >= 3) {
>> +        encode3to4(src, dest);
>> +        src += 3;
>> +        dest += 4;
>> +        srclen -= 3;
>> +    }
>> +    switch (srclen) {
>> +    case 2:
>> +        encode2to4(src, dest);
>> +        dest += 4;
>> +        break;
>> +    case 1:
>> +        encode1to4(src, dest);
>> +        dest += 4;
>> +        break;
>> +    case 0:
>> +        break;
>> +    }
>> +    dest[0] = 0;
>> +}
>> +
>> +static int32_t codetovalue(char c)
>> +{
>> +    if (c >= 'A' && c <= 'Z') {
>> +        return c - 'A';
>> +    } else if (c >= 'a' && c <= 'z') {
>> +        return c - 'a' + 26;
>> +    } else if (c >= '0' && c <= '9') {
>> +        return c - '0' + 52;
>> +    } else if (c == '+') {
>> +        return 62;
>> +    } else if ( c == '/') {
>> +        return 63;
>> +    } else {
>> +        return -1;
>> +    }
>> +}
>> +
>> +static int decode4to3 (const char *src, char *dest)
>> +{
>> +    uint32_t b32 = 0;
>> +    int32_t bits;
>> +    int i;
>> +
>> +    for (i = 0; i < 4; i++) {
>> +        bits = codetovalue(src[i]);
>> +        if (bits < 0) {
>> +            return bits;
>> +        }
>> +        b32 <<= 6;
>> +        b32 |= bits;
>> +    }
>> +    dest[0] = (b32 >> 16) & 0xFF;
>> +    dest[1] = (b32 >> 8) & 0xFF;
>> +    dest[2] = b32 & 0xFF;
>> +
>> +    return 0;
>> +}
>> +
>> +static int decode3to2(const char *src, char *dest)
>> +{
>> +    uint32_t b32 = 0;
>> +    int32_t bits;
>> +
>> +    bits = codetovalue(src[0]);
>> +    if (bits < 0) {
>> +        return bits;
>> +    }
>> +    b32 = (uint32_t)bits;
>> +    b32 <<= 6;
>> +
>> +    bits = codetovalue(src[1]);
>> +    if (bits < 0) {
>> +        return bits;
>> +    }
>> +    b32 |= (uint32_t)bits;
>> +    b32 <<= 4;
>> +
>> +    bits = codetovalue(src[2]);
>> +    if (bits < 0) {
>> +        return bits;
>> +    }
>> +    b32 |= ((uint32_t)bits) >> 2;
>> +
>> +    dest[0] = (b32 >> 8) & 0xFF;
>> +    dest[1] = b32 & 0xFF;
>> +
>> +    return 0;
>> +}
>> +
>> +static int decode2to1(const char *src, char *dest)
>> +{
>> +    uint32_t b32;
>> +    int32_t bits;
>> +
>> +    bits = codetovalue(src[0]);
>> +    if (bits < 0) {
>> +        return bits;
>> +    }
>> +    b32 = (uint32_t)bits << 2;
>> +
>> +    bits = codetovalue(src[1]);
>> +    if (bits < 0) {
>> +        return bits;
>> +    }
>> +    b32 |= ((uint32_t)bits) >> 4;
>> +
>> +    dest[0] = b32;
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Convert string 'src' of length 'srclen' from base64 to binary form,
>> + * saving the result in 'dest'. The size of the destination buffer must be at
>> + * least srclen * 3 / 4.
>> + *
>> + * Returns 0 on success, -1 on conversion error.
>> + */
>> +int base64_decode(const char *src, size_t srclen, void *dest)
> 
> I think dest should be char *, like all the functions where dest is passed to.

The output may but need not be a string, it's binary data. And to avoid
needless warnings about signedness mismatches if unsigned char or
uint8_t buffers are passed, I chose void *.

> 
>> +{
>> +    int ret;
>> +
>> +    while (srclen >= 4) {
>> +        ret = decode4to3(src, dest);
>> +        if (ret < 0) {
>> +            return ret;
>> +        }
>> +        src += 4;
>> +        dest += 3;
>> +        srclen -= 4;
>> +    }
>> +
>> +    switch (srclen) {
>> +    case 3:
>> +        return decode3to2(src, dest);
>> +    case 2:
>> +        return decode2to1(src, dest);
>> +    case 1:
>> +        return -1;
>> +    default: /* 0 */
>> +        return 0;
>> +    }
>> +}
>> diff --git a/base64.h b/base64.h
>> new file mode 100644
>> index 0000000..9a0e03a
>> --- /dev/null
>> +++ b/base64.h
>> @@ -0,0 +1,18 @@
>> +/*
>> + * Base64 encoder/decoder conforming to RFC 4648
>> + * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
>> + *
>> + * Copyright (C) 2010 Siemens AG
>> + *
>> + * Authors:
>> + *  Jan Kiszka <jan.kiszka@siemens.com>
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + *
>> + */
>> +
>> +#include <unistd.h>
> 
> Maybe <stddef.h> instead, it's only for size_t?

Makes sense.

> 
>> +
>> +void base64_encode(const void *src, size_t srclen, char *dest);
>> +int base64_decode(const char *src, size_t srclen, void *dest);
>> --
>> 1.6.0.2
>>
>>
>>

Thanks,
Jan
Avi Kivity - May 23, 2010, 8:48 a.m.
On 05/23/2010 10:55 AM, Jan Kiszka wrote:
>>> +/*
>>> + * Convert string 'src' of length 'srclen' from base64 to binary form,
>>> + * saving the result in 'dest'. The size of the destination buffer must be at
>>> + * least srclen * 3 / 4.
>>> + *
>>> + * Returns 0 on success, -1 on conversion error.
>>> + */
>>> +int base64_decode(const char *src, size_t srclen, void *dest)
>>>        
>> I think dest should be char *, like all the functions where dest is passed to.
>>      
> The output may but need not be a string, it's binary data. And to avoid
> needless warnings about signedness mismatches if unsigned char or
> uint8_t buffers are passed, I chose void *.
>    

I think qemu is pretty consistent in using uint8_t for binary, and void 
* is a little dangerous as it allows passing any kind of data (anything 
above a byte is subject to endianness issues for example).

But I don't feel strongly about this.
Jan Kiszka - May 23, 2010, 10:04 a.m.
Avi Kivity wrote:
> On 05/23/2010 10:55 AM, Jan Kiszka wrote:
>>>> +/*
>>>> + * Convert string 'src' of length 'srclen' from base64 to binary form,
>>>> + * saving the result in 'dest'. The size of the destination buffer
>>>> must be at
>>>> + * least srclen * 3 / 4.
>>>> + *
>>>> + * Returns 0 on success, -1 on conversion error.
>>>> + */
>>>> +int base64_decode(const char *src, size_t srclen, void *dest)
>>>>        
>>> I think dest should be char *, like all the functions where dest is
>>> passed to.
>>>      
>> The output may but need not be a string, it's binary data. And to avoid
>> needless warnings about signedness mismatches if unsigned char or
>> uint8_t buffers are passed, I chose void *.
>>    
> 
> I think qemu is pretty consistent in using uint8_t for binary, and void
> * is a little dangerous as it allows passing any kind of data (anything
> above a byte is subject to endianness issues for example).
> 
> But I don't feel strongly about this.
> 

Let's go for consistency: I switched to uint8_t for the binary input/output.

Jan

Patch

diff --git a/Makefile.objs b/Makefile.objs
index acbaf22..2c603b2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,7 @@ 
 # QObject
 qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
 qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
-qobject-obj-y += qerror.o
+qobject-obj-y += qerror.o base64.o
 
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..543e8c6
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,202 @@ 
+/*
+ * Base64 encoder/decoder conforming to RFC 4648
+ * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "inttypes.h"
+#include "base64.h"
+
+static const char base[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void encode3to4(const char *src, char *dest)
+{
+    uint32_t b32 = 0;
+    int i, j = 18;
+
+    for (i = 0; i < 3; i++) {
+        b32 <<= 8;
+        b32 |= src[i];
+    }
+    for (i = 0; i < 4; i++) {
+        dest[i] = base[(b32 >> j) & 0x3F];
+        j -= 6;
+    }
+}
+
+static void encode2to4(const char *src, char *dest)
+{
+    dest[0] = base[(src[0] >> 2) & 0x3F];
+    dest[1] = base[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)];
+    dest[2] = base[(src[1] & 0x0F) << 2];
+    dest[3] = '=';
+}
+
+static void encode1to4(const char *src, char *dest)
+{
+    dest[0] = base[(src[0] >> 2) & 0x3F];
+    dest[1] = base[(src[0] & 0x03) << 4];
+    dest[2] = '=';
+    dest[3] = '=';
+}
+
+/*
+ * Encode data in 'src' of length 'srclen' to a base64 string, saving the
+ * null-terminated result in 'dest'. The size of the destition buffer must be
+ * at least ((srclen + 2) / 3) * 4 + 1.
+ */
+void base64_encode(const void *src, size_t srclen, char *dest)
+{
+    while (srclen >= 3) {
+        encode3to4(src, dest);
+        src += 3;
+        dest += 4;
+        srclen -= 3;
+    }
+    switch (srclen) {
+    case 2:
+        encode2to4(src, dest);
+        dest += 4;
+        break;
+    case 1:
+        encode1to4(src, dest);
+        dest += 4;
+        break;
+    case 0:
+        break;
+    }
+    dest[0] = 0;
+}
+
+static int32_t codetovalue(char c)
+{
+    if (c >= 'A' && c <= 'Z') {
+        return c - 'A';
+    } else if (c >= 'a' && c <= 'z') {
+        return c - 'a' + 26;
+    } else if (c >= '0' && c <= '9') {
+        return c - '0' + 52;
+    } else if (c == '+') {
+        return 62;
+    } else if ( c == '/') {
+        return 63;
+    } else {
+        return -1;
+    }
+}
+
+static int decode4to3 (const char *src, char *dest)
+{
+    uint32_t b32 = 0;
+    int32_t bits;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        bits = codetovalue(src[i]);
+        if (bits < 0) {
+            return bits;
+        }
+        b32 <<= 6;
+        b32 |= bits;
+    }
+    dest[0] = (b32 >> 16) & 0xFF;
+    dest[1] = (b32 >> 8) & 0xFF;
+    dest[2] = b32 & 0xFF;
+
+    return 0;
+}
+
+static int decode3to2(const char *src, char *dest)
+{
+    uint32_t b32 = 0;
+    int32_t bits;
+
+    bits = codetovalue(src[0]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 = (uint32_t)bits;
+    b32 <<= 6;
+
+    bits = codetovalue(src[1]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= (uint32_t)bits;
+    b32 <<= 4;
+
+    bits = codetovalue(src[2]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= ((uint32_t)bits) >> 2;
+
+    dest[0] = (b32 >> 8) & 0xFF;
+    dest[1] = b32 & 0xFF;
+
+    return 0;
+}
+
+static int decode2to1(const char *src, char *dest)
+{
+    uint32_t b32;
+    int32_t bits;
+
+    bits = codetovalue(src[0]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 = (uint32_t)bits << 2;
+
+    bits = codetovalue(src[1]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= ((uint32_t)bits) >> 4;
+
+    dest[0] = b32;
+
+    return 0;
+}
+
+/*
+ * Convert string 'src' of length 'srclen' from base64 to binary form,
+ * saving the result in 'dest'. The size of the destination buffer must be at
+ * least srclen * 3 / 4.
+ *
+ * Returns 0 on success, -1 on conversion error.
+ */
+int base64_decode(const char *src, size_t srclen, void *dest)
+{
+    int ret;
+
+    while (srclen >= 4) {
+        ret = decode4to3(src, dest);
+        if (ret < 0) {
+            return ret;
+        }
+        src += 4;
+        dest += 3;
+        srclen -= 4;
+    }
+
+    switch (srclen) {
+    case 3:
+        return decode3to2(src, dest);
+    case 2:
+        return decode2to1(src, dest);
+    case 1:
+        return -1;
+    default: /* 0 */
+        return 0;
+    }
+}
diff --git a/base64.h b/base64.h
new file mode 100644
index 0000000..9a0e03a
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,18 @@ 
+/*
+ * Base64 encoder/decoder conforming to RFC 4648
+ * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <unistd.h>
+
+void base64_encode(const void *src, size_t srclen, char *dest);
+int base64_decode(const char *src, size_t srclen, void *dest);