diff options
author | NIIBE Yutaka <[email protected]> | 2017-02-01 10:45:39 +0000 |
---|---|---|
committer | NIIBE Yutaka <[email protected]> | 2017-02-01 10:45:39 +0000 |
commit | 4bfc2117b70415a5c5d3f0a0ac9086e168350d83 (patch) | |
tree | f2c59bddd20f3bb89ffc298c2167fc2d966852e9 | |
parent | estream: Correctly set ERRNO to EOPNOTSUPP. (diff) | |
download | libgpg-error-4bfc2117b70415a5c5d3f0a0ac9086e168350d83.tar.gz libgpg-error-4bfc2117b70415a5c5d3f0a0ac9086e168350d83.zip |
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 <[email protected]>
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/b64dec.c | 279 | ||||
-rw-r--r-- | src/gpg-error.def.in | 4 | ||||
-rw-r--r-- | src/gpg-error.h.in | 10 | ||||
-rw-r--r-- | src/gpg-error.vers | 4 | ||||
-rw-r--r-- | src/gpgrt-int.h | 6 | ||||
-rw-r--r-- | src/visibility.c | 19 | ||||
-rw-r--r-- | src/visibility.h | 7 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/t-b64dec.c | 123 |
11 files changed, 461 insertions, 2 deletions
@@ -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; +} |