Blame src/b64enc.c

Packit fc043f
/* b64enc.c - Simple Base64 encoder.
Packit fc043f
 * Copyright (C) 2001, 2003, 2004, 2008, 2010,
Packit fc043f
 *               2011 Free Software Foundation, Inc.
Packit fc043f
 * Copyright (C) 2001, 2003, 2004, 2008, 2010,
Packit fc043f
 *               2011, 2018 g10 Code GmbH
Packit fc043f
 *
Packit fc043f
 * This file is part of Libgpg-error.
Packit fc043f
 *
Packit fc043f
 * This file is free software; you can redistribute it and/or modify
Packit fc043f
 * it under the terms of the GNU Lesser General Public License as
Packit fc043f
 * published by the Free Software Foundation; either version 2.1 of
Packit fc043f
 * the License, or (at your option) any later version.
Packit fc043f
 *
Packit fc043f
 * This file is distributed in the hope that it will be useful,
Packit fc043f
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit fc043f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit fc043f
 * GNU General Public License for more details.
Packit fc043f
 *
Packit fc043f
 * You should have received a copy of the GNU Lesser General Public License
Packit fc043f
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit fc043f
 * SPDX-License-Identifier: LGPL-2.1-or-later
Packit fc043f
 *
Packit fc043f
 * This file was originally a part of GnuPG.
Packit fc043f
 */
Packit fc043f
Packit fc043f
#include <config.h>
Packit fc043f
#include <stdio.h>
Packit fc043f
#include <stdint.h>
Packit fc043f
#include <stdlib.h>
Packit fc043f
#include <string.h>
Packit fc043f
#include <errno.h>
Packit fc043f
Packit fc043f
#include "gpgrt-int.h"
Packit fc043f
Packit fc043f
Packit fc043f
#define B64ENC_DID_HEADER   1
Packit fc043f
#define B64ENC_DID_TRAILER  2
Packit fc043f
#define B64ENC_NO_LINEFEEDS 16
Packit fc043f
#define B64ENC_USE_PGPCRC   32
Packit fc043f
Packit fc043f
/* The base-64 character list */
Packit fc043f
static unsigned char const bintoasc[64] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Packit fc043f
                                           "abcdefghijklmnopqrstuvwxyz"
Packit fc043f
                                           "0123456789+/");
Packit fc043f
Packit fc043f
/* Stuff required to create the OpenPGP CRC.  This crc_table has been
Packit fc043f
   created using this code:
Packit fc043f
Packit fc043f
   #include <stdio.h>
Packit fc043f
   #include <stdint.h>
Packit fc043f
Packit fc043f
   #define CRCPOLY 0x864CFB
Packit fc043f
Packit fc043f
   int
Packit fc043f
   main (void)
Packit fc043f
   {
Packit fc043f
     int i, j;
Packit fc043f
     uint32_t t;
Packit fc043f
     uint32_t crc_table[256];
Packit fc043f
Packit fc043f
     crc_table[0] = 0;
Packit fc043f
     for (i=j=0; j < 128; j++ )
Packit fc043f
       {
Packit fc043f
         t = crc_table[j];
Packit fc043f
         if ( (t & 0x00800000) )
Packit fc043f
           {
Packit fc043f
             t <<= 1;
Packit fc043f
             crc_table[i++] = t ^ CRCPOLY;
Packit fc043f
             crc_table[i++] = t;
Packit fc043f
       }
Packit fc043f
         else
Packit fc043f
           {
Packit fc043f
             t <<= 1;
Packit fc043f
             crc_table[i++] = t;
Packit fc043f
             crc_table[i++] = t ^ CRCPOLY;
Packit fc043f
           }
Packit fc043f
       }
Packit fc043f
Packit fc043f
     puts ("static const u32 crc_table[256] = {");
Packit fc043f
     for (i=j=0; i < 256; i++)
Packit fc043f
       {
Packit fc043f
         printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]);
Packit fc043f
         if (i != 255)
Packit fc043f
           {
Packit fc043f
             putchar (',');
Packit fc043f
             if ( ++j > 5)
Packit fc043f
               {
Packit fc043f
                 j = 0;
Packit fc043f
                 putchar ('\n');
Packit fc043f
               }
Packit fc043f
           }
Packit fc043f
       }
Packit fc043f
     puts ("\n};");
Packit fc043f
     return 0;
Packit fc043f
   }
Packit fc043f
*/
Packit fc043f
#define CRCINIT 0xB704CE
Packit fc043f
static const uint32_t crc_table[256] = {
Packit fc043f
  0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a,
Packit fc043f
  0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf,
Packit fc043f
  0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272,
Packit fc043f
  0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e,
Packit fc043f
  0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa,
Packit fc043f
  0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f,
Packit fc043f
  0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b,
Packit fc043f
  0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7,
Packit fc043f
  0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a,
Packit fc043f
  0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af,
Packit fc043f
  0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29,
Packit fc043f
  0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5,
Packit fc043f
  0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1,
Packit fc043f
  0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad,
Packit fc043f
  0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099,
Packit fc043f
  0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375,
Packit fc043f
  0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821,
Packit fc043f
  0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4,
Packit fc043f
  0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049,
Packit fc043f
  0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5,
Packit fc043f
  0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791,
Packit fc043f
  0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52,
Packit fc043f
  0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66,
Packit fc043f
  0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a,
Packit fc043f
  0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337,
Packit fc043f
  0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2,
Packit fc043f
  0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6,
Packit fc043f
  0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a,
Packit fc043f
  0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e,
Packit fc043f
  0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132,
Packit fc043f
  0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506,
Packit fc043f
  0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea,
Packit fc043f
  0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c,
Packit fc043f
  0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9,
Packit fc043f
  0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604,
Packit fc043f
  0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8,
Packit fc043f
  0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc,
Packit fc043f
  0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69,
Packit fc043f
  0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d,
Packit fc043f
  0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1,
Packit fc043f
  0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c,
Packit fc043f
  0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9,
Packit fc043f
  0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538
Packit fc043f
};
Packit fc043f
Packit fc043f
Packit fc043f
/* Prepare for Base-64 writing to STREAM.  If TITLE is not NULL and
Packit fc043f
 * not an empty string, that string will be used as the title for the
Packit fc043f
 * armor lines, with TITLE being an empty string, we don't write the
Packit fc043f
 * header lines and furthermore even don't write any linefeeds.  If
Packit fc043f
 * TITLE starts with "PGP " the OpenPGP CRC checksum will be written
Packit fc043f
 * as well.  With TITLE being NULL, we merely don't write header but
Packit fc043f
 * make sure that lines are not too long.  Note, that we don't write
Packit fc043f
 * anything unless at least one byte is written using b64enc_write.
Packit fc043f
 * On success an enoder object is returned which needs to be released
Packit fc043f
 * using _gpgrt_b64dec_finish.  On error NULL is returned an ERRNO is
Packit fc043f
 * set.
Packit fc043f
 */
Packit fc043f
gpgrt_b64state_t
Packit fc043f
_gpgrt_b64enc_start (estream_t stream, const char *title)
Packit fc043f
{
Packit fc043f
  gpgrt_b64state_t state;
Packit fc043f
Packit fc043f
  state = xtrycalloc (1, sizeof *state);
Packit fc043f
  if (!state)
Packit fc043f
    return NULL;
Packit fc043f
Packit fc043f
  state->stream = stream;
Packit fc043f
  if (title && !*title)
Packit fc043f
    state->flags |= B64ENC_NO_LINEFEEDS;
Packit fc043f
  else if (title)
Packit fc043f
    {
Packit fc043f
      if (!strncmp (title, "PGP ", 4))
Packit fc043f
        {
Packit fc043f
          state->flags |= B64ENC_USE_PGPCRC;
Packit fc043f
          state->crc = CRCINIT;
Packit fc043f
        }
Packit fc043f
      state->title = xtrystrdup (title);
Packit fc043f
      if (!state->title)
Packit fc043f
        {
Packit fc043f
          xfree (state);
Packit fc043f
          return NULL;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  return state;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Write NBYTES from BUFFER to the Base 64 stream identified by STATE.
Packit fc043f
 * With BUFFER and NBYTES being 0, merely do a fflush on the stream.
Packit fc043f
 */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_b64enc_write (gpgrt_b64state_t state, const void *buffer, size_t nbytes)
Packit fc043f
{
Packit fc043f
  unsigned char radbuf[4];
Packit fc043f
  int idx, quad_count;
Packit fc043f
  const unsigned char *p;
Packit fc043f
Packit fc043f
  if (state->lasterr)
Packit fc043f
    return state->lasterr;
Packit fc043f
Packit fc043f
  if (!nbytes)
Packit fc043f
    {
Packit fc043f
      if (buffer)
Packit fc043f
        if (_gpgrt_fflush (state->stream))
Packit fc043f
          goto write_error;
Packit fc043f
      return 0;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (!(state->flags & B64ENC_DID_HEADER))
Packit fc043f
    {
Packit fc043f
      if (state->title)
Packit fc043f
        {
Packit fc043f
          if ( _gpgrt_fputs ("-----BEGIN ", state->stream) == EOF
Packit fc043f
               || _gpgrt_fputs (state->title, state->stream) == EOF
Packit fc043f
               || _gpgrt_fputs ("-----\n", state->stream) == EOF)
Packit fc043f
            goto write_error;
Packit fc043f
          if ( (state->flags & B64ENC_USE_PGPCRC)
Packit fc043f
               && _gpgrt_fputs ("\n", state->stream) == EOF)
Packit fc043f
            goto write_error;
Packit fc043f
        }
Packit fc043f
Packit fc043f
      state->flags |= B64ENC_DID_HEADER;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  idx = state->idx;
Packit fc043f
  quad_count = state->quad_count;
Packit fc043f
  gpgrt_assert (idx < 4);
Packit fc043f
  memcpy (radbuf, state->radbuf, idx);
Packit fc043f
Packit fc043f
  if ( (state->flags & B64ENC_USE_PGPCRC) )
Packit fc043f
    {
Packit fc043f
      size_t n;
Packit fc043f
      uint32_t crc = state->crc;
Packit fc043f
Packit fc043f
      for (p=buffer, n=nbytes; n; p++, n-- )
Packit fc043f
        crc = ((uint32_t)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p];
Packit fc043f
      state->crc = (crc & 0x00ffffff);
Packit fc043f
    }
Packit fc043f
Packit fc043f
  for (p=buffer; nbytes; p++, nbytes--)
Packit fc043f
    {
Packit fc043f
      radbuf[idx++] = *p;
Packit fc043f
      if (idx > 2)
Packit fc043f
        {
Packit fc043f
          char tmp[4];
Packit fc043f
Packit fc043f
          tmp[0] = bintoasc[(*radbuf >> 2) & 077];
Packit fc043f
          tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
Packit fc043f
          tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
Packit fc043f
          tmp[3] = bintoasc[radbuf[2]&077];
Packit fc043f
          for (idx=0; idx < 4; idx++)
Packit fc043f
            _gpgrt_fputc (tmp[idx], state->stream);
Packit fc043f
          idx = 0;
Packit fc043f
          if (_gpgrt_ferror (state->stream))
Packit fc043f
            goto write_error;
Packit fc043f
Packit fc043f
          if (++quad_count >= (64/4))
Packit fc043f
            {
Packit fc043f
              quad_count = 0;
Packit fc043f
              if (!(state->flags & B64ENC_NO_LINEFEEDS)
Packit fc043f
                  && _gpgrt_fputs ("\n", state->stream) == EOF)
Packit fc043f
                goto write_error;
Packit fc043f
            }
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
  memcpy (state->radbuf, radbuf, idx);
Packit fc043f
  state->idx = idx;
Packit fc043f
  state->quad_count = quad_count;
Packit fc043f
  return 0;
Packit fc043f
Packit fc043f
 write_error:
Packit fc043f
  state->lasterr = _gpg_err_code_from_syserror ();
Packit fc043f
  if (state->title)
Packit fc043f
    {
Packit fc043f
      xfree (state->title);
Packit fc043f
      state->title = NULL;
Packit fc043f
    }
Packit fc043f
  return state->lasterr;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_b64enc_finish (gpgrt_b64state_t state)
Packit fc043f
{
Packit fc043f
  gpg_err_code_t err = 0;
Packit fc043f
  unsigned char radbuf[4];
Packit fc043f
  int idx, quad_count;
Packit fc043f
  char tmp[4];
Packit fc043f
Packit fc043f
  if (!state)
Packit fc043f
    return 0;  /* Already released.  */
Packit fc043f
Packit fc043f
  if (state->using_decoder)
Packit fc043f
    {
Packit fc043f
      err = GPG_ERR_CONFLICT;  /* State was created for the decoder.  */
Packit fc043f
      goto cleanup;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (state->lasterr)
Packit fc043f
    {
Packit fc043f
      err = state->lasterr;
Packit fc043f
      goto cleanup;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (!(state->flags & B64ENC_DID_HEADER))
Packit fc043f
    goto cleanup;
Packit fc043f
Packit fc043f
  /* Flush the base64 encoding */
Packit fc043f
  idx = state->idx;
Packit fc043f
  quad_count = state->quad_count;
Packit fc043f
  gpgrt_assert (idx < 4);
Packit fc043f
  memcpy (radbuf, state->radbuf, idx);
Packit fc043f
Packit fc043f
  if (idx)
Packit fc043f
    {
Packit fc043f
      tmp[0] = bintoasc[(*radbuf>>2)&077];
Packit fc043f
      if (idx == 1)
Packit fc043f
        {
Packit fc043f
          tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
Packit fc043f
          tmp[2] = '=';
Packit fc043f
          tmp[3] = '=';
Packit fc043f
        }
Packit fc043f
      else
Packit fc043f
        {
Packit fc043f
          tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
Packit fc043f
          tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
Packit fc043f
          tmp[3] = '=';
Packit fc043f
        }
Packit fc043f
      for (idx=0; idx < 4; idx++)
Packit fc043f
        _gpgrt_fputc (tmp[idx], state->stream);
Packit fc043f
      if (_gpgrt_ferror (state->stream))
Packit fc043f
        goto write_error;
Packit fc043f
Packit fc043f
      if (++quad_count >= (64/4))
Packit fc043f
        {
Packit fc043f
          quad_count = 0;
Packit fc043f
          if (!(state->flags & B64ENC_NO_LINEFEEDS)
Packit fc043f
              && _gpgrt_fputs ("\n", state->stream) == EOF)
Packit fc043f
            goto write_error;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  /* Finish the last line and write the trailer. */
Packit fc043f
  if (quad_count
Packit fc043f
      && !(state->flags & B64ENC_NO_LINEFEEDS)
Packit fc043f
      && _gpgrt_fputs ("\n", state->stream) == EOF)
Packit fc043f
    goto write_error;
Packit fc043f
Packit fc043f
  if ( (state->flags & B64ENC_USE_PGPCRC) )
Packit fc043f
    {
Packit fc043f
      /* Write the CRC.  */
Packit fc043f
      _gpgrt_fputs ("=", state->stream);
Packit fc043f
      radbuf[0] = state->crc >>16;
Packit fc043f
      radbuf[1] = state->crc >> 8;
Packit fc043f
      radbuf[2] = state->crc;
Packit fc043f
      tmp[0] = bintoasc[(*radbuf>>2)&077];
Packit fc043f
      tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
Packit fc043f
      tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
Packit fc043f
      tmp[3] = bintoasc[radbuf[2]&077];
Packit fc043f
      for (idx=0; idx < 4; idx++)
Packit fc043f
        _gpgrt_fputc (tmp[idx], state->stream);
Packit fc043f
      if (_gpgrt_ferror (state->stream))
Packit fc043f
        goto write_error;
Packit fc043f
Packit fc043f
      if (!(state->flags & B64ENC_NO_LINEFEEDS)
Packit fc043f
          && _gpgrt_fputs ("\n", state->stream) == EOF)
Packit fc043f
        goto write_error;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (state->title)
Packit fc043f
    {
Packit fc043f
      if ( _gpgrt_fputs ("-----END ", state->stream) == EOF
Packit fc043f
           || _gpgrt_fputs (state->title, state->stream) == EOF
Packit fc043f
           || _gpgrt_fputs ("-----\n", state->stream) == EOF)
Packit fc043f
        goto write_error;
Packit fc043f
    }
Packit fc043f
Packit fc043f
 cleanup:
Packit fc043f
  xfree (state->title);
Packit fc043f
  xfree (state);
Packit fc043f
  return err;
Packit fc043f
Packit fc043f
 write_error:
Packit fc043f
  err = gpg_error_from_syserror ();
Packit fc043f
  goto cleanup;
Packit fc043f
}