Blame src/b64dec.c

Packit Service 672cf4
/* b64dec.c - Simple Base64 decoder.
Packit Service 672cf4
 * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
Packit Service 672cf4
 * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
Packit Service 672cf4
 *
Packit Service 672cf4
 * This file is part of GnuPG.
Packit Service 672cf4
 *
Packit Service 672cf4
 * This file is free software; you can redistribute it and/or modify
Packit Service 672cf4
 * it under the terms of the GNU Lesser General Public License as
Packit Service 672cf4
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 672cf4
 * the License, or (at your option) any later version.
Packit Service 672cf4
 *
Packit Service 672cf4
 * This file is distributed in the hope that it will be useful,
Packit Service 672cf4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 672cf4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 672cf4
 * GNU Lesser General Public License for more details.
Packit Service 672cf4
 *
Packit Service 672cf4
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 6c01f9
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit Service 672cf4
 */
Packit Service 672cf4
Packit Service 672cf4
#include <config.h>
Packit Service 672cf4
#include <stdio.h>
Packit Service 672cf4
#include <stdlib.h>
Packit Service 672cf4
#include <string.h>
Packit Service 672cf4
#include <assert.h>
Packit Service 672cf4
Packit Service 672cf4
#include "gpgme.h"
Packit Service 672cf4
#include "util.h"
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* The reverse base-64 list used for base-64 decoding. */
Packit Service 672cf4
static unsigned char const asctobin[128] =
Packit Service 672cf4
  {
Packit Service 672cf4
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
Packit Service 672cf4
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
Packit Service 672cf4
    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
Packit Service 672cf4
    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
Packit Service 672cf4
    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
Packit Service 672cf4
    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit Service 672cf4
    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
Packit Service 672cf4
    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
Packit Service 672cf4
    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
Packit Service 672cf4
    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
Packit Service 672cf4
  };
Packit Service 672cf4
Packit Service 672cf4
enum decoder_states
Packit Service 672cf4
  {
Packit Service 672cf4
    s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
Packit Service 672cf4
    s_b64_0, s_b64_1, s_b64_2, s_b64_3,
Packit Service 672cf4
    s_waitendtitle, s_waitend
Packit Service 672cf4
  };
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* Initialize the context for the base64 decoder.  If TITLE is NULL a
Packit Service 672cf4
   plain base64 decoding is done.  If it is the empty string the
Packit Service 672cf4
   decoder will skip everything until a "-----BEGIN " line has been
Packit Service 672cf4
   seen, decoding ends at a "----END " line.  */
Packit Service 672cf4
gpg_error_t
Packit Service 672cf4
_gpgme_b64dec_start (struct b64state *state, const char *title)
Packit Service 672cf4
{
Packit Service 672cf4
  memset (state, 0, sizeof *state);
Packit Service 672cf4
  if (title)
Packit Service 672cf4
    {
Packit Service 672cf4
      state->title = strdup (title);
Packit Service 672cf4
      if (!state->title)
Packit Service 672cf4
        state->lasterr = gpg_error_from_syserror ();
Packit Service 672cf4
      else
Packit Service 672cf4
        state->idx = s_init;
Packit Service 672cf4
    }
Packit Service 672cf4
  else
Packit Service 672cf4
    state->idx = s_b64_0;
Packit Service 672cf4
  return state->lasterr;
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Stores the
Packit Service 672cf4
   new length of the buffer at R_NBYTES. */
Packit Service 672cf4
gpg_error_t
Packit Service 672cf4
_gpgme_b64dec_proc (struct b64state *state, void *buffer, size_t length,
Packit Service 672cf4
                    size_t *r_nbytes)
Packit Service 672cf4
{
Packit Service 672cf4
  enum decoder_states ds = state->idx;
Packit Service 672cf4
  unsigned char val = state->radbuf[0];
Packit Service 672cf4
  int pos = state->quad_count;
Packit Service 672cf4
  char *d, *s;
Packit Service 672cf4
Packit Service 672cf4
  if (state->lasterr)
Packit Service 672cf4
    return state->lasterr;
Packit Service 672cf4
Packit Service 672cf4
  if (state->stop_seen)
Packit Service 672cf4
    {
Packit Service 672cf4
      *r_nbytes = 0;
Packit Service 672cf4
      state->lasterr = gpg_error (GPG_ERR_EOF);
Packit Service 672cf4
      free (state->title);
Packit Service 672cf4
      state->title = NULL;
Packit Service 672cf4
      return state->lasterr;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  for (s=d=buffer; length && !state->stop_seen; length--, s++)
Packit Service 672cf4
    {
Packit Service 672cf4
    again:
Packit Service 672cf4
      switch (ds)
Packit Service 672cf4
        {
Packit Service 672cf4
        case s_idle:
Packit Service 672cf4
          if (*s == '\n')
Packit Service 672cf4
            {
Packit Service 672cf4
              ds = s_lfseen;
Packit Service 672cf4
              pos = 0;
Packit Service 672cf4
            }
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_init:
Packit Service 672cf4
          ds = s_lfseen;
Packit Service 672cf4
        case s_lfseen:
Packit Service 672cf4
          if (*s != "-----BEGIN "[pos])
Packit Service 672cf4
            {
Packit Service 672cf4
              ds = s_idle;
Packit Service 672cf4
              goto again;
Packit Service 672cf4
            }
Packit Service 672cf4
          else if (pos == 10)
Packit Service 672cf4
            {
Packit Service 672cf4
              pos = 0;
Packit Service 672cf4
              ds = s_beginseen;
Packit Service 672cf4
            }
Packit Service 672cf4
          else
Packit Service 672cf4
            pos++;
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_beginseen:
Packit Service 672cf4
          if (*s != "PGP "[pos])
Packit Service 672cf4
            ds = s_begin; /* Not a PGP armor.  */
Packit Service 672cf4
          else if (pos == 3)
Packit Service 672cf4
            ds = s_waitheader;
Packit Service 672cf4
          else
Packit Service 672cf4
            pos++;
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_waitheader:
Packit Service 672cf4
          if (*s == '\n')
Packit Service 672cf4
            ds = s_waitblank;
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_waitblank:
Packit Service 672cf4
          if (*s == '\n')
Packit Service 672cf4
            ds = s_b64_0; /* blank line found.  */
Packit Service 672cf4
          else if (*s == ' ' || *s == '\r' || *s == '\t')
Packit Service 672cf4
            ; /* Ignore spaces. */
Packit Service 672cf4
          else
Packit Service 672cf4
            {
Packit Service 672cf4
              /* Armor header line.  Note that we don't care that our
Packit Service 672cf4
               * FSM accepts a header prefixed with spaces.  */
Packit Service 672cf4
              ds = s_waitheader; /* Wait for next header.  */
Packit Service 672cf4
            }
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_begin:
Packit Service 672cf4
          if (*s == '\n')
Packit Service 672cf4
            ds = s_b64_0;
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_b64_0:
Packit Service 672cf4
        case s_b64_1:
Packit Service 672cf4
        case s_b64_2:
Packit Service 672cf4
        case s_b64_3:
Packit Service 672cf4
          {
Packit Service 672cf4
            int c;
Packit Service 672cf4
Packit Service 672cf4
            if (*s == '-' && state->title)
Packit Service 672cf4
              {
Packit Service 672cf4
                /* Not a valid Base64 character: assume end
Packit Service 672cf4
                   header.  */
Packit Service 672cf4
                ds = s_waitend;
Packit Service 672cf4
              }
Packit Service 672cf4
            else if (*s == '=')
Packit Service 672cf4
              {
Packit Service 672cf4
                /* Pad character: stop */
Packit Service 672cf4
                if (ds == s_b64_1)
Packit Service 672cf4
                  *d++ = val;
Packit Service 672cf4
                ds = state->title? s_waitendtitle : s_waitend;
Packit Service 672cf4
              }
Packit Service 672cf4
            else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
Packit Service 672cf4
              ; /* Skip white spaces. */
Packit Service 672cf4
            else if ( (*s & 0x80)
Packit Service 672cf4
                      || (c = asctobin[*(unsigned char *)s]) == 255)
Packit Service 672cf4
              {
Packit Service 672cf4
                /* Skip invalid encodings.  */
Packit Service 672cf4
                state->invalid_encoding = 1;
Packit Service 672cf4
              }
Packit Service 672cf4
            else if (ds == s_b64_0)
Packit Service 672cf4
              {
Packit Service 672cf4
                val = c << 2;
Packit Service 672cf4
                ds = s_b64_1;
Packit Service 672cf4
              }
Packit Service 672cf4
            else if (ds == s_b64_1)
Packit Service 672cf4
              {
Packit Service 672cf4
                val |= (c>>4)&3;
Packit Service 672cf4
                *d++ = val;
Packit Service 672cf4
                val = (c<<4)&0xf0;
Packit Service 672cf4
                ds = s_b64_2;
Packit Service 672cf4
              }
Packit Service 672cf4
            else if (ds == s_b64_2)
Packit Service 672cf4
              {
Packit Service 672cf4
                val |= (c>>2)&1;;
Packit Service 672cf4
                *d++ = val;
Packit Service 672cf4
                val = (c<<6)&0xc0;
Packit Service 672cf4
                ds = s_b64_3;
Packit Service 672cf4
              }
Packit Service 672cf4
            else
Packit Service 672cf4
              {
Packit Service 672cf4
                val |= c&0x3f;
Packit Service 672cf4
                *d++ = val;
Packit Service 672cf4
                ds = s_b64_0;
Packit Service 672cf4
              }
Packit Service 672cf4
          }
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_waitendtitle:
Packit Service 672cf4
          if (*s == '-')
Packit Service 672cf4
            ds = s_waitend;
Packit Service 672cf4
          break;
Packit Service 672cf4
        case s_waitend:
Packit Service 672cf4
          if ( *s == '\n')
Packit Service 672cf4
            state->stop_seen = 1;
Packit Service 672cf4
          break;
Packit Service 672cf4
        default:
Packit Service 672cf4
          assert (!"invalid state");
Packit Service 672cf4
        }
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
  state->idx = ds;
Packit Service 672cf4
  state->radbuf[0] = val;
Packit Service 672cf4
  state->quad_count = pos;
Packit Service 672cf4
  *r_nbytes = (d -(char*) buffer);
Packit Service 672cf4
  return 0;
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* This function needs to be called before releasing the decoder
Packit Service 672cf4
   state.  It may return an error code in case an encoding error has
Packit Service 672cf4
   been found during decoding. */
Packit Service 672cf4
gpg_error_t
Packit Service 672cf4
_gpgme_b64dec_finish (struct b64state *state)
Packit Service 672cf4
{
Packit Service 672cf4
  if (state->lasterr)
Packit Service 672cf4
    return state->lasterr;
Packit Service 672cf4
Packit Service 672cf4
  free (state->title);
Packit Service 672cf4
  state->title = NULL;
Packit Service 672cf4
  return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
Packit Service 672cf4
}