bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-gnulib] Re: base64


From: Simon Josefsson
Subject: [bug-gnulib] Re: base64
Date: Thu, 25 Nov 2004 14:40:01 +0100
User-agent: Gnus/5.110003 (No Gnus v0.3) Emacs/21.3.50 (gnu/linux)

Bruno Haible <address@hidden> writes:

>> +#define BASE64_LENGTH(inlen) (((4 - (inlen) % 3) % 4) + (4 * (inlen) / 3))
>
> This is overly complex. You can also write it as
>    ((inlen) + 2) / 3) * 4

I don't think that give the same result, try tabulating the two macros
for, say, inlen = 0...15.

>> +  while (inlen && outlen)
>> +    {
>> +      if (outlen--)
>> +    *optr++ = b64[iptr[0] >> 2];
>> +      if (outlen--)
>> +    *optr++ = b64[((iptr[0] << 4) +
>> +                   (--inlen ? (iptr[1] >> 4) : 0)) & 0x3f];
>> +      if (outlen--)
>> +    *optr++ = inlen ? b64[((iptr[1] << 2) +
>> +                           (--inlen ? (iptr[2] >> 6) : 0)) & 0x3f] : '=';
>> +      if (outlen--)
>> +    *optr++ = inlen ? b64[iptr[2] & 0x3f] : '=';
>> +      if (inlen)
>> +    inlen--;
>> +      iptr += 3;
>> +    }
>> +
>> +  if (outlen)
>> +    *optr = '\0';
>
> If outlen becomes zero in the loop, it continues to be decremented
> nevertheless, so that at the end of the loop it has the value
> 0xffffffff or 0xfffffffe or 0xfffffffd, and the final '\0' is stored
> where it shouldn't.

Ah, thanks, adding those checks was the last thing I did, and it seems
they were broken.  New version below.

Index: MODULES.html.sh
===================================================================
RCS file: /cvsroot/gnulib/gnulib/MODULES.html.sh,v
retrieving revision 1.68
diff -u -p -r1.68 MODULES.html.sh
--- MODULES.html.sh     12 Nov 2004 00:00:47 -0000      1.68
+++ MODULES.html.sh     25 Nov 2004 13:40:31 -0000
@@ -1605,6 +1605,7 @@ func_all_modules ()
   func_echo "$element"
 
   func_begin_table
+  func_module base64
   func_module diacrit
   func_module getline
   func_module getnline
Index: lib/base64.c
===================================================================
RCS file: lib/base64.c
diff -N lib/base64.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/base64.c        25 Nov 2004 13:40:31 -0000
@@ -0,0 +1,197 @@
+/* base64.c -- Encode binary data using printable characters.
+   Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Portions adapted from GNU MailUtils, by Simon Josefsson.  For more
+   information, see RFC 3548 <http://www.ietf.org/rfc/rfc3548.txt>. */
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+   If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+   possible.  If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+   terminate the output buffer. */
+void
+base64_encode (const char *in, size_t inlen, char *out, size_t outlen)
+{
+  const char *b64 =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  const unsigned char *iptr = (const unsigned char *) in;
+  unsigned char *optr = (unsigned char *) out;
+
+  while (inlen && outlen)
+    {
+      *optr++ = b64[iptr[0] >> 2];
+      if (!--outlen)
+       break;
+      *optr++ = b64[((iptr[0] << 4) + (--inlen ? (iptr[1] >> 4) : 0)) & 0x3f];
+      if (!--outlen)
+       break;
+      *optr++ = inlen ? b64[((iptr[1] << 2) +
+                            (--inlen ? (iptr[2] >> 6) : 0)) & 0x3f] : '=';
+      if (!--outlen)
+       break;
+      *optr++ = inlen ? b64[iptr[2] & 0x3f] : '=';
+      if (!--outlen)
+       break;
+      if (inlen)
+       inlen--;
+      iptr += 3;
+    }
+
+  if (outlen)
+    *optr = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+   from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+   the length of the encoded data, excluding the terminating zero.  On
+   return, the OUT variable will hold a pointer to newly allocated
+   memory that must be deallocated by the caller, or NULL on memory
+   allocation failure.  */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+  size_t outlen;
+
+  outlen = BASE64_LENGTH (inlen);
+  *out = malloc (outlen + 1);
+  if (!*out)
+    return outlen;
+
+  base64_encode (in, inlen, *out, outlen + 1);
+
+  return outlen;
+}
+
+/* Decode base64 encoded input array IN of length INLEN to output
+   array OUT that can hold *OUTLEN bytes.  Return true if decoding was
+   successful, false otherwise.  If *OUTLEN is too small, as many
+   bytes as possible will be written to OUT.  On return, *OUTLEN holds
+   the length of decode bytes in OUT.  Note that if any non-alphabet
+   characters are encountered, decoding is stopped and false is
+   returned. */
+bool
+base64_decode (const char *in, size_t inlen, char *out, size_t * outlen)
+{
+  static const signed char b64[0x100] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1,
+    -1, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+  };
+  const unsigned char *iptr = (const unsigned char *) in;
+  unsigned char *optr = (unsigned char *) out;
+  size_t len = *outlen;
+
+  *outlen = 0;
+
+  while (inlen >= 2)
+    {
+      if (!len--)
+       return true;
+
+      if (b64[iptr[0]] < 0 || b64[iptr[1]] < 0)
+       return false;
+
+      *optr++ = (b64[iptr[0]] << 2) | (b64[iptr[1]] >> 4);
+      (*outlen)++;
+
+      if (inlen == 2)
+       return false;
+
+      if (iptr[2] == '=')
+       {
+         if (iptr[3] != '=')
+           return false;
+
+         if (inlen != 4)
+           return false;
+       }
+      else
+       {
+         if (!len--)
+           return true;
+
+         if (b64[iptr[2]] < 0)
+           return false;
+
+         *optr++ = ((b64[iptr[1]] << 4) & 0xf0) | (b64[iptr[2]] >> 2);
+         (*outlen)++;
+
+         if (inlen == 3)
+           return false;
+
+         if (iptr[3] == '=')
+           {
+             if (inlen != 4)
+               return false;
+           }
+         else
+           {
+             if (!len--)
+               return true;
+
+             if (b64[iptr[3]] < 0)
+               return false;
+
+             *optr++ = ((b64[iptr[2]] << 6) & 0xc0) | b64[iptr[3]];
+             (*outlen)++;
+           }
+       }
+      iptr += 4;
+      inlen -= 4;
+    }
+
+  if (inlen != 0)
+    return false;
+
+  return true;
+
+}
+
+/* Allocate an output buffer OUT, and decode the base64 encoded data
+   stored in IN of size INLEN.  On return, the actual size of the
+   decoded data is stored in *OUTLEN.  The function return true if
+   decoding was successful, or false on memory allocation or decoding
+   errors.  */
+bool
+base64_decode_alloc (const char *in, size_t inlen, char **out,
+                    size_t * outlen)
+{
+  *outlen = 3 * inlen / 4;     /* FIXME: May allocate one 1 or 2 bytes too
+                                  much, depending on input. */
+  *out = malloc (*outlen);
+  if (!*out)
+    return false;
+  return base64_decode (in, inlen, *out, outlen);
+}
Index: lib/base64.h
===================================================================
RCS file: lib/base64.h
diff -N lib/base64.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/base64.h        25 Nov 2004 13:40:31 -0000
@@ -0,0 +1,41 @@
+/* base64.h -- Encode binary data using printable characters.
+   Copyright (C) 2004 Free Software Foundation, Inc.
+   Written by Simon Josefsson.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get bool. */
+#include <stdbool.h>
+
+#define BASE64_LENGTH(inlen) (((4 - (inlen) % 3) % 4) + (4 * (inlen) / 3))
+
+extern void base64_encode (const char *in, size_t inlen,
+                          char *out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern bool base64_decode (const char *in, size_t inlen,
+                          char *out, size_t * outlen);
+
+extern bool base64_decode_alloc (const char *in, size_t inlen,
+                                char **out, size_t * outlen);
+
+#endif /* BASE64_H */
Index: modules/base64
===================================================================
RCS file: modules/base64
diff -N modules/base64
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ modules/base64      25 Nov 2004 13:40:31 -0000
@@ -0,0 +1,23 @@
+Description:
+Encode binary data using printable characters (base64).
+
+Files:
+lib/base64.h
+lib/base64.c
+
+Depends-on:
+stdbool
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += base64.h base64.c
+
+Include:
+"base64.h"
+
+License:
+LGPL
+
+Maintainer:
+Simon Josefsson
Index: tests/test-base64.c
===================================================================
RCS file: tests/test-base64.c
diff -N tests/test-base64.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/test-base64.c 25 Nov 2004 13:40:31 -0000
@@ -0,0 +1,158 @@
+#include <stdio.h>
+
+#include "base64.h"
+
+int
+main (int argc, char *argv[])
+{
+  const char *in = "abcdefghijklmnop";
+  const char *b64in = "YWJjZGVmZw==";
+  char out[255];
+  char *p;
+  size_t len;
+  bool ok;
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 0, out, 0);
+  if (out[0] != '\x42')
+    fprintf (stderr, "failure\n");
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 1);
+  if (memcmp (out, "YQ==", 1) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 2);
+  if (memcmp (out, "YQ==", 2) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 3);
+  if (memcmp (out, "YQ==", 3) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 4);
+  if (memcmp (out, "YQ==", 4) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 1, out, 8);
+  if (memcmp (out, "YQ==", 4) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 2, out, 4);
+  if (memcmp (out, "YWI=", 4) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 3, out, 4);
+  if (memcmp (out, "YWJj", 4) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 4, out, 5);
+  if (memcmp (out, "YWJjZA==", 5) != 0)
+    {
+      out[5] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  base64_encode (in, 4, out, 100);
+  if (memcmp (out, "YWJjZA==", 6) != 0)
+    {
+      out[6] = '\0';
+      fprintf (stderr, "failure (%s)\n", out);
+    }
+
+  /* Decode. */
+
+  memset (out, 0x42, sizeof (out));
+  len = 0;
+  ok = base64_decode (b64in, 4, out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 0)
+    fprintf (stderr, "failure (%d)\n", len);
+
+  memset (out, 0x42, sizeof (out));
+  len = 1;
+  ok = base64_decode (b64in, 4, out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 1 || memcmp (out, "abcdefg", 1) != 0)
+    {
+      out[2] = '\0';
+      fprintf (stderr, "failure (%d: %s)\n", len, out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  len = 2;
+  ok = base64_decode (b64in, 4, out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 2 || memcmp (out, "abcdefg", 2) != 0)
+    {
+      out[3] = '\0';
+      fprintf (stderr, "failure (%d: %s)\n", len, out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  len = 3;
+  ok = base64_decode (b64in, 4, out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 3 || memcmp (out, "abcdefg", 3) != 0)
+    {
+      out[4] = '\0';
+      fprintf (stderr, "failure (%d: %s)\n", len, out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  len = 4;
+  ok = base64_decode (b64in, 4, out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 3 || memcmp (out, "abcdefg", 3) != 0)
+    {
+      out[3] = '\0';
+      fprintf (stderr, "failure (%d: %s)\n", len, out);
+    }
+
+  memset (out, 0x42, sizeof (out));
+  len = 100;
+  ok = base64_decode (b64in, strlen (b64in), out, &len);
+  if (!ok)
+    fprintf (stderr, "decode failed\n");
+  if (len != 7 || memcmp (out, "abcdefg", 7) != 0)
+    {
+      out[7] = '\0';
+      fprintf (stderr, "failure (%d: %s)\n", len, out);
+    }
+
+  return 0;
+}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]