Blame src/b64dec.c

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