diff options
Diffstat (limited to 'src/b64dec.c')
| -rw-r--r-- | src/b64dec.c | 251 | 
1 files changed, 251 insertions, 0 deletions
| diff --git a/src/b64dec.c b/src/b64dec.c new file mode 100644 index 00000000..7965a305 --- /dev/null +++ b/src/b64dec.c @@ -0,0 +1,251 @@ +/* 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 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "gpgme.h" +#include "util.h" + + +/* 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 +  }; + + + +/* 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.  */ +gpg_error_t +_gpgme_b64dec_start (struct b64state *state, const char *title) +{ +  memset (state, 0, sizeof *state); +  if (title) +    { +      state->title = strdup (title); +      if (!state->title) +        state->lasterr = gpg_error_from_syserror (); +      else +        state->idx = s_init; +    } +  else +    state->idx = s_b64_0; +  return state->lasterr; +} + + +/* 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 +_gpgme_b64dec_proc (struct b64state *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; +} + + +/* This function needs to be called before releasing the decoder +   state.  It may return an error code in case an encoding error has +   been found during decoding. */ +gpg_error_t +_gpgme_b64dec_finish (struct b64state *state) +{ +  if (state->lasterr) +    return state->lasterr; + +  free (state->title); +  state->title = NULL; +  return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; +} | 
