[Pkg-gnupg-commit] [libgpg-error] 09/29: Add Base64 decoder.

Daniel Kahn Gillmor dkg at fifthhorseman.net
Sun Mar 5 00:41:33 UTC 2017


This is an automated email from the git hooks/post-receive script.

dkg pushed a commit to branch master
in repository libgpg-error.

commit 4bfc2117b70415a5c5d3f0a0ac9086e168350d83
Author: NIIBE Yutaka <gniibe at fsij.org>
Date:   Wed Feb 1 19:45:39 2017 +0900

    Add Base64 decoder.
    
    * NEWS: Add interface changes.
    * src/Makefile.am (libgpg_error_la_SOURCES): Add b64dec.c.
    * src/b64dec.c: New.  Taken from gpgme.  Prefix function names with
    _gpgrt_.  Change API a bit, not exposing the structure.
    * src/gpg-error.def.in: Export Base64 functions.
    * src/gpg-error.vers: Likewise.
    * src/visibility.c, src/visibility.h: Likewise.
    * src/gpg-error.h.in: Add Base64 struct and functions.
    * src/gpgrt-int.h: Add Base64 internal functions.
    * tests/Makefile.am (TESTS): Add t-b64dec.
    * tests/t-b64dec.c: New.
    
    Signed-off-by: NIIBE Yutaka <gniibe at fsij.org>
---
 NEWS                 |   7 ++
 src/Makefile.am      |   2 +-
 src/b64dec.c         | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gpg-error.def.in |   4 +
 src/gpg-error.h.in   |  10 ++
 src/gpg-error.vers   |   4 +
 src/gpgrt-int.h      |   6 ++
 src/visibility.c     |  19 ++++
 src/visibility.h     |   7 ++
 tests/Makefile.am    |   2 +-
 tests/t-b64dec.c     | 123 +++++++++++++++++++++++
 11 files changed, 461 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 572385f..912dd0c 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,13 @@ Noteworthy changes in version 1.27 (unreleased) [C2_/A2_/R_]
 
  * Fixed macro GPGRT_GCC_VERSION.
 
+ * Interface changes relative to the 1.26 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgrt_b64state_t                 NEW type.
+ gpgrt_b64dec_start               NEW.
+ gpgrt_b64dec_proc                NEW.
+ gpgrt_b64dec_finish              NEW.
+
 
 Noteworthy changes in version 1.26 (2016-12-21) [C21/A21/R0]
 -----------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 1eb8287..d849c42 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -179,7 +179,7 @@ libgpg_error_la_SOURCES = gettext.h $(arch_sources) \
 	gpgrt-int.h init.c init.h version.c lock.h thread.h \
 	estream.c estream-printf.c estream-printf.h \
 	strsource.c strerror.c code-to-errno.c code-from-errno.c \
-	visibility.c visibility.h
+	visibility.c visibility.h b64dec.c
 nodist_libgpg_error_la_SOURCES = gpg-error.h
 
 # libgpg_error_la_DEPENDENCIES = \
diff --git a/src/b64dec.c b/src/b64dec.c
new file mode 100644
index 0000000..d846a6a
--- /dev/null
+++ b/src/b64dec.c
@@ -0,0 +1,279 @@
+/* b64dec.c - Simple Base64 decoder.
+ * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "gpgrt-int.h"
+
+struct _gpgrt_b64state
+{
+  int idx;
+  int quad_count;
+  char *title;
+  unsigned char radbuf[4];
+  int stop_seen:1;
+  int invalid_encoding:1;
+  gpg_error_t lasterr;
+};
+
+/* The reverse base-64 list used for base-64 decoding. */
+static unsigned char const asctobin[128] =
+  {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 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, 0xff, 0xff, 0xff, 0xff, 0xff
+  };
+
+enum decoder_states
+  {
+    s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
+    s_b64_0, s_b64_1, s_b64_2, s_b64_3,
+    s_waitendtitle, s_waitend
+  };
+
+
+
+/* Allocate and initialize the context for the base64 decoder.  If
+   TITLE is NULL a plain base64 decoding is done.  If it is the empty
+   string the decoder will skip everything until a "-----BEGIN " line
+   has been seen, decoding ends at a "----END " line.  */
+gpgrt_b64state_t
+_gpgrt_b64dec_start (const char *title)
+{
+  gpgrt_b64state_t state;
+  char *t = NULL;
+
+  if (title)
+    {
+      t = strdup (title);
+      if (!t)
+        return NULL;
+    }
+
+  state = calloc (1, sizeof (struct _gpgrt_b64state));
+  if (!state)
+    {
+      free (t);
+      return NULL;
+    }
+
+  if (t)
+    {
+      state->title = t;
+      state->idx = s_init;
+    }
+  else
+    state->idx = s_b64_0;
+
+  return state;
+}
+
+
+/* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Stores the
+   new length of the buffer at R_NBYTES. */
+gpg_error_t
+_gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer, size_t length,
+                    size_t *r_nbytes)
+{
+  enum decoder_states ds = state->idx;
+  unsigned char val = state->radbuf[0];
+  int pos = state->quad_count;
+  char *d, *s;
+
+  if (state->lasterr)
+    return state->lasterr;
+
+  if (state->stop_seen)
+    {
+      *r_nbytes = 0;
+      state->lasterr = gpg_error (GPG_ERR_EOF);
+      free (state->title);
+      state->title = NULL;
+      return state->lasterr;
+    }
+
+  for (s=d=buffer; length && !state->stop_seen; length--, s++)
+    {
+    again:
+      switch (ds)
+        {
+        case s_idle:
+          if (*s == '\n')
+            {
+              ds = s_lfseen;
+              pos = 0;
+            }
+          break;
+        case s_init:
+          ds = s_lfseen;
+        case s_lfseen:
+          if (*s != "-----BEGIN "[pos])
+            {
+              ds = s_idle;
+              goto again;
+            }
+          else if (pos == 10)
+            {
+              pos = 0;
+              ds = s_beginseen;
+            }
+          else
+            pos++;
+          break;
+        case s_beginseen:
+          if (*s != "PGP "[pos])
+            ds = s_begin; /* Not a PGP armor.  */
+          else if (pos == 3)
+            ds = s_waitheader;
+          else
+            pos++;
+          break;
+        case s_waitheader:
+          if (*s == '\n')
+            ds = s_waitblank;
+          break;
+        case s_waitblank:
+          if (*s == '\n')
+            ds = s_b64_0; /* blank line found.  */
+          else if (*s == ' ' || *s == '\r' || *s == '\t')
+            ; /* Ignore spaces. */
+          else
+            {
+              /* Armor header line.  Note that we don't care that our
+               * FSM accepts a header prefixed with spaces.  */
+              ds = s_waitheader; /* Wait for next header.  */
+            }
+          break;
+        case s_begin:
+          if (*s == '\n')
+            ds = s_b64_0;
+          break;
+        case s_b64_0:
+        case s_b64_1:
+        case s_b64_2:
+        case s_b64_3:
+          {
+            int c;
+
+            if (*s == '-' && state->title)
+              {
+                /* Not a valid Base64 character: assume end
+                   header.  */
+                ds = s_waitend;
+              }
+            else if (*s == '=')
+              {
+                /* Pad character: stop */
+                if (ds == s_b64_1)
+                  *d++ = val;
+                ds = state->title? s_waitendtitle : s_waitend;
+              }
+            else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
+              ; /* Skip white spaces. */
+            else if ( (*s & 0x80)
+                      || (c = asctobin[*(unsigned char *)s]) == 255)
+              {
+                /* Skip invalid encodings.  */
+                state->invalid_encoding = 1;
+              }
+            else if (ds == s_b64_0)
+              {
+                val = c << 2;
+                ds = s_b64_1;
+              }
+            else if (ds == s_b64_1)
+              {
+                val |= (c>>4)&3;
+                *d++ = val;
+                val = (c<<4)&0xf0;
+                ds = s_b64_2;
+              }
+            else if (ds == s_b64_2)
+              {
+                val |= (c>>2)&15;
+                *d++ = val;
+                val = (c<<6)&0xc0;
+                ds = s_b64_3;
+              }
+            else
+              {
+                val |= c&0x3f;
+                *d++ = val;
+                ds = s_b64_0;
+              }
+          }
+          break;
+        case s_waitendtitle:
+          if (*s == '-')
+            ds = s_waitend;
+          break;
+        case s_waitend:
+          if ( *s == '\n')
+            state->stop_seen = 1;
+          break;
+        default:
+          assert (!"invalid state");
+        }
+    }
+
+
+  state->idx = ds;
+  state->radbuf[0] = val;
+  state->quad_count = pos;
+  *r_nbytes = (d -(char*) buffer);
+  return 0;
+}
+
+
+/* Return an error code in case an encoding error has been found
+   during decoding. */
+gpg_error_t
+_gpgrt_b64dec_finish (gpgrt_b64state_t state)
+{
+  gpg_error_t err;
+
+  if (state->lasterr)
+    err = state->lasterr;
+  else
+    {
+      free (state->title);
+      err = state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
+    }
+  free (state);
+
+  return err;
+}
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index 19e87fa..ad65f8c 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -152,4 +152,8 @@ EXPORTS
 
  gpgrt_get_syscall_clamp      @112
 
+ gpgrt_b64dec_start           @113
+ gpgrt_b64dec_proc            @114
+ gpgrt_b64dec_finish          @115
+
 ;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index c603314..19bdeed 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -853,6 +853,16 @@ int gpgrt_vsnprintf (char *buf,size_t bufsize,
 # define es_bsprintf          gpgrt_bsprintf
 # define es_vbsprintf         gpgrt_vbsprintf
 #endif /*GPGRT_ENABLE_ES_MACROS*/
+

+/* Base64 decode functions.  */
+
+struct _gpgrt_b64state;
+typedef struct _gpgrt_b64state *gpgrt_b64state_t;
+
+gpgrt_b64state_t gpgrt_b64dec_start (const char *title);
+gpg_error_t gpgrt_b64dec_proc (gpgrt_b64state_t state,
+                               void *buffer, size_t length, size_t *r_nbytes);
+gpg_error_t gpgrt_b64dec_finish (gpgrt_b64state_t state);
 
 #ifdef __cplusplus
 }
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index 802ff3d..e44128c 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -126,6 +126,10 @@ GPG_ERROR_1.0 {
     gpg_err_deinit;
     gpgrt_set_alloc_func;
 
+    gpgrt_b64dec_start;
+    gpgrt_b64dec_proc;
+    gpgrt_b64dec_finish;
+
   local:
     *;
 };
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index fba2585..d624e84 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -312,4 +312,10 @@ int _gpgrt_w32_pollable_create (void *_GPGRT__RESTRICT *_GPGRT__RESTRICT cookie,
 int _gpgrt_w32_poll (gpgrt_poll_t *fds, size_t nfds, int timeout);
 #endif
 
+gpgrt_b64state_t _gpgrt_b64dec_start (const char *title);
+gpg_error_t _gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer,
+                                size_t length, size_t *r_nbytes);
+gpg_error_t _gpgrt_b64dec_finish (gpgrt_b64state_t state);
+
+
 #endif /*_GPGRT_GPGRT_INT_H*/
diff --git a/src/visibility.c b/src/visibility.c
index 89b5623..b637e7a 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -711,3 +711,22 @@ gpgrt_vsnprintf (char *buf, size_t bufsize,
 {
   return _gpgrt_estream_vsnprintf (buf, bufsize, format, arg_ptr);
 }
+
+gpgrt_b64state_t
+gpgrt_b64dec_start (const char *title)
+{
+  return _gpgrt_b64dec_start (title);
+}
+
+gpg_error_t
+gpgrt_b64dec_proc (gpgrt_b64state_t state, void *buffer,
+                   size_t length, size_t *r_nbytes)
+{
+  return _gpgrt_b64dec_proc (state, buffer, length, r_nbytes);
+}
+
+gpg_error_t
+gpgrt_b64dec_finish (gpgrt_b64state_t state)
+{
+  return _gpgrt_b64dec_finish (state);
+}
diff --git a/src/visibility.h b/src/visibility.h
index 479186f..da8e228 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -145,6 +145,10 @@ MARK_VISIBLE (gpgrt_set_syscall_clamp)
 MARK_VISIBLE (gpgrt_get_syscall_clamp)
 MARK_VISIBLE (gpgrt_set_alloc_func)
 
+MARK_VISIBLE (gpgrt_b64dec_start)
+MARK_VISIBLE (gpgrt_b64dec_proc)
+MARK_VISIBLE (gpgrt_b64dec_finish)
+
 #undef MARK_VISIBLE
 
 #else /*!_GPGRT_INCL_BY_VISIBILITY_C*/
@@ -255,6 +259,9 @@ MARK_VISIBLE (gpgrt_set_alloc_func)
 #define gpgrt_get_syscall_clamp     _gpgrt_USE_UNDERSCORED_FUNCTION
 #define gpgrt_set_alloc_func        _gpgrt_USE_UNDERSCORED_FUNCTION
 
+#define gpgrt_b64dec_start          _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_b64dec_proc           _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_b64dec_finish         _gpgrt_USE_UNDERSCORED_FUNCTION
 
 #endif /*!_GPGRT_INCL_BY_VISIBILITY_C*/
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 92b97f2..a3c6cbd 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -27,7 +27,7 @@ endif
 
 gpg_error_lib = ../src/libgpg-error.la
 
-TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll
+TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64dec
 
 AM_CPPFLAGS = -I$(top_builddir)/src $(extra_includes)
 
diff --git a/tests/t-b64dec.c b/tests/t-b64dec.c
new file mode 100644
index 0000000..aae208b
--- /dev/null
+++ b/tests/t-b64dec.c
@@ -0,0 +1,123 @@
+/* t-b64dec.c - b64dec test.
+   Copyright (C) 2017 g10 Code GmbH
+
+   This file is part of libgpg-error.
+
+   libgpg-error is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   libgpg-error 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with libgpgme-error; if not, write to the Free
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <gpg-error.h>
+
+static const char *test_b64_string = "bGliZ3BnLWVycm9yIGlzIGZyZWUgc29"
+  "mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkgaXQgd"
+  "W5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIEx"
+  "pY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb"
+  "247IGVpdGhlciB2ZXJzaW9uIDIuMSBvZiB0aGUgTGljZW5zZSwgb3IgKGF0IHlvdXI"
+  "gb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4=";
+
+static const char *test_string = "libgpg-error is free software; "
+  "you can redistribute it and/or modify it under the terms of "
+  "the GNU Lesser General Public License as published by the Free "
+  "Software Foundation; either version 2.1 of the License, or "
+  "(at your option) any later version.";
+
+#define fail(a)  do { fprintf (stderr, "%s:%d: test %d failed\n",\
+                               __FILE__,__LINE__, (a));          \
+                     errcount++;                                 \
+                   } while(0)
+
+static int errcount;
+
+static gpg_error_t
+test_b64dec_string (const char *string, const char *expected)
+{
+  gpg_error_t err;
+  gpgrt_b64state_t state;
+  char *buffer;
+  size_t len;
+
+  len = strlen (string);
+  buffer = malloc (strlen (string) + 1);
+  if (!buffer)
+    {
+      err = gpg_error_from_syserror ();
+      return err;
+    }
+
+  state = gpgrt_b64dec_start ("");
+  if (!state)
+    {
+      err = gpg_error_from_syserror ();
+      free (buffer);
+      return err;
+    }
+
+  err = gpgrt_b64dec_proc (state, buffer, len, &len);
+  if (err)
+    {
+      if (gpg_err_code (err) != GPG_ERR_EOF)
+        {
+          free (buffer);
+          free (state);
+          return err;
+        }
+    }
+
+  err = gpgrt_b64dec_finish (state);
+  if (err)
+    {
+      free (buffer);
+      return err;
+    }
+
+  if (strncmp (buffer, expected, len) == 0)
+    err = 0;
+  else
+    err = GPG_ERR_INTERNAL;
+
+  free (buffer);
+  return err;
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+  gpg_error_t err;
+
+  (void)argc;
+  (void)argv;
+
+  err = test_b64dec_string (test_b64_string, test_string);
+
+  if (err)
+    {
+      fail (1);
+      return 1;
+    }
+  else
+    return 0;
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/libgpg-error.git



More information about the Pkg-gnupg-commit mailing list