/*
 *
 *  base32 encoding/decoding per RFC 4648
 *
 *   Copyright (C) 2013  Milan Kupcevic
 *   All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#include "basez.h"

static const unsigned char
code32u[]    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

static const unsigned char
code32l[]    = "abcdefghijklmnopqrstuvwxyz234567";

static const unsigned char
code32hexu[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";

static const unsigned char
code32hexl[] = "0123456789abcdefghijklmnopqrstuv";

static const unsigned char c32d[] =
{
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 00 - 07 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 08 - 0f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 10 - 17 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 18 - 1f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 20 - 27 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 28 - 2f */
  0xff, 0xff, 0x1a, 0x1b,    0x1c, 0x1d, 0x1e, 0x1f, /* 30 - 37 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xfe, 0xff, 0xff, /* 38 - 3f */
  0xff, 0x00, 0x01, 0x02,    0x03, 0x04, 0x05, 0x06, /* 40 - 47 */
  0x07, 0x08, 0x09, 0x0a,    0x0b, 0x0c, 0x0d, 0x0e, /* 48 - 4f */
  0x0f, 0x10, 0x11, 0x12,    0x13, 0x14, 0x15, 0x16, /* 50 - 57 */
  0x17, 0x18, 0x19, 0xff,    0xff, 0xff, 0xff, 0xff, /* 58 - 5f */
  0xff, 0x00, 0x01, 0x02,    0x03, 0x04, 0x05, 0x06, /* 60 - 67 */
  0x07, 0x08, 0x09, 0x0a,    0x0b, 0x0c, 0x0d, 0x0e, /* 68 - 6f */
  0x0f, 0x10, 0x11, 0x12,    0x13, 0x14, 0x15, 0x16, /* 70 - 77 */
  0x17, 0x18, 0x19, 0xff,    0xff, 0xff, 0xff, 0xff, /* 78 - 7f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 80 - 87 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 88 - 8f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 90 - 97 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 98 - 9f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* a0 - a7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* a8 - af */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* b0 - b7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* b8 - bf */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* c0 - c7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* c8 - cf */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* d0 - d7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* d8 - df */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* e0 - e7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* e8 - ef */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* f0 - f7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* f8 - ff */
};

static const unsigned char c32hexd[] =
{
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 00 - 07 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 08 - 0f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 10 - 17 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 18 - 1f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 20 - 27 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 28 - 2f */
  0x00, 0x01, 0x02, 0x03,    0x04, 0x05, 0x06, 0x07, /* 30 - 37 */
  0x08, 0x09, 0xff, 0xff,    0xff, 0xfe, 0xff, 0xff, /* 38 - 3f */
  0xff, 0x0a, 0x0b, 0x0c,    0x0d, 0x0e, 0x0f, 0x10, /* 40 - 47 */
  0x11, 0x12, 0x13, 0x14,    0x15, 0x16, 0x17, 0x18, /* 48 - 4f */
  0x19, 0x1a, 0x1b, 0x1c,    0x1d, 0x1e, 0x1f, 0xff, /* 50 - 57 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 58 - 5f */
  0xff, 0x0a, 0x0b, 0x0c,    0x0d, 0x0e, 0x0f, 0x10, /* 60 - 67 */
  0x11, 0x12, 0x13, 0x14,    0x15, 0x16, 0x17, 0x18, /* 68 - 6f */
  0x19, 0x1a, 0x1b, 0x1c,    0x1d, 0x1e, 0x1f, 0xff, /* 70 - 77 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 78 - 7f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 80 - 87 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 88 - 8f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 90 - 97 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* 98 - 9f */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* a0 - a7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* a8 - af */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* b0 - b7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* b8 - bf */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* c0 - c7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* c8 - cf */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* d0 - d7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* d8 - df */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* e0 - e7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* e8 - ef */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* f0 - f7 */
  0xff, 0xff, 0xff, 0xff,    0xff, 0xff, 0xff, 0xff, /* f8 - ff */
};

static
void
enc_b32(
  const unsigned char *bin,
  const int benc,
  unsigned char *bout,
  const int code)
{
  const unsigned char *c;

  switch(code)
  {
    case 3:
      c = code32hexl;
      break;
    case 2:
      c = code32hexu;
      break;
    case 1:
      c = code32l;
      break;
    default:
      c = code32u;
  }
 
  switch(benc) 
  {
    case 5:
      bout[7] = c[bin[4] & 0x1f];
      bout[6] = c[(bin[3]<<3 & 0x1f) | bin[4]>>5];
    case 4:
      bout[5] = c[bin[3]>>2 & 0x1f];
      bout[4] = c[(bin[2]<<1 & 0x1f) | bin[3]>>7];
    case 3:
      bout[3] = c[(bin[1]<<4 & 0x1f) | bin[2]>>4];
    case 2:
      bout[2] = c[bin[1]>>1 & 0x1f];
      bout[1] = c[(bin[0]<<2 & 0x1f) | bin[1]>>6];
    case 1:
      bout[0] = c[bin[0]>>3];
  }

  switch(benc) 
  {
    case 1:
      bout[1] = c[bin[0]<<2 & 0x1f];
      bout[2] = '=';
      bout[3] = '=';
      bout[4] = '=';
      bout[5] = '=';
      bout[6] = '=';
      bout[7] = '=';
      break;
    case 2:
      bout[3] = c[bin[1]<<4 & 0x1f];
      bout[4] = '=';
      bout[5] = '=';
      bout[6] = '=';
      bout[7] = '=';
      break;
    case 3:
      bout[4] = c[bin[2]<<1 & 0x1f];
      bout[5] = '=';
      bout[6] = '=';
      bout[7] = '=';
      break;
    case 4:
      bout[6] = c[bin[3]<<3 & 0x1f];
      bout[7] = '=';
      break;
  }
}

static
int
dec_b32(
  const unsigned char *bin,
  unsigned char *bout,
  const int code)
{
  const unsigned char* c;

  switch(code)
  {
    case 2:
      c = c32hexd;
      break;
    default:
      c = c32d;
  }

  switch(c[bin[0]])
  {
    case 0xfe:
    case 0xff:
      return 0;
    default:
      bout[0] = c[bin[0]] << 3;
  }

  switch(c[bin[1]])
  {
    case 0xfe:
    case 0xff:
      return 0;
    default:
      bout[0] = (c[bin[1]] >> 2) | bout[0];
  }

  switch(c[bin[2]])
  {
    case 0xfe:
      if((bin[3] == '=') &&
         (bin[4] == '=') &&
         (bin[5] == '=') &&
         (bin[6] == '=') &&
         (bin[7] == '='))
         return 1;
    case 0xff:
      return 0;
    default:
      bout[1] = (c[bin[1]] << 6) | c[bin[2]] << 1;
  }

  switch(c[bin[3]])
  {
    case 0xfe:
    case 0xff:
      return 0;
    default:
      bout[1] = (c[bin[3]] >> 4) | bout[1];
  }

  switch(c[bin[4]])
  {
    case 0xfe:
      if((bin[5] == '=') &&
         (bin[6] == '=') &&
         (bin[7] == '='))
         return 2;
    case 0xff:
      return 0;
    default:
      bout[2] = (c[bin[3]] << 4) | c[bin[4]] >> 1;
  }

  switch(c[bin[5]])
  {
    case 0xfe:
      if((bin[6] == '=') &&
         (bin[7] == '='))
         return 3;
    case 0xff:
      return 0;
    default:
      bout[3] = (c[bin[4]] << 7) | c[bin[5]] << 2;
  }

  switch(c[bin[6]])
  {
    case 0xfe:
    case 0xff:
      return 0;
    default:
      bout[3] = (c[bin[6]] >> 3) | bout[3];
  }

  switch(c[bin[7]])
  {
    case 0xfe:
      return 4;
    case 0xff:
      return 0;
    default:
      bout[4] = (c[bin[6]] << 5) | c[bin[7]];
  }
  return 5;
}

void
encode_base32u(
  const unsigned char *bin,
  const int benc,
  unsigned char *bout)
{
  enc_b32(bin, benc, bout, 0);
}

void
encode_base32l(
  const unsigned char *bin,
  const int benc,
  unsigned char *bout)
{
  enc_b32(bin, benc, bout, 1);
}

int
decode_base32(
  const unsigned char *bin,
  unsigned char *bout)
{
  return dec_b32(bin, bout, 0);
}

void
encode_base32hexu(
  const unsigned char *bin,
  const int benc,
  unsigned char *bout)
{
  enc_b32(bin, benc, bout, 2);
}

void
encode_base32hexl(
  const unsigned char *bin,
  const int benc,
  unsigned char *bout)
{
  enc_b32(bin, benc, bout, 3);
}

int
decode_base32hex(
  const unsigned char *bin,
  unsigned char *bout)
{
  return dec_b32(bin, bout, 2);
}
