Blame src/base64.c

Packit 6c0a39
/*
Packit 6c0a39
 * base64.c - support for base64 alphabet system, described in RFC1521
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2005-2005 by Aris Adamantiadis
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
/* just the dirtiest part of code i ever made */
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/buffer.h"
Packit 6c0a39
Packit 6c0a39
static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Packit 6c0a39
                         "abcdefghijklmnopqrstuvwxyz"
Packit 6c0a39
                         "0123456789+/";
Packit 6c0a39
Packit 6c0a39
/* Transformations */
Packit 6c0a39
#define SET_A(n, i) do { (n) |= ((i) & 63) <<18; } while (0)
Packit 6c0a39
#define SET_B(n, i) do { (n) |= ((i) & 63) <<12; } while (0)
Packit 6c0a39
#define SET_C(n, i) do { (n) |= ((i) & 63) << 6; } while (0)
Packit 6c0a39
#define SET_D(n, i) do { (n) |= ((i) & 63); } while (0)
Packit 6c0a39
Packit 6c0a39
#define GET_A(n) (unsigned char) (((n) & 0xff0000) >> 16)
Packit 6c0a39
#define GET_B(n) (unsigned char) (((n) & 0xff00) >> 8)
Packit 6c0a39
#define GET_C(n) (unsigned char) ((n) & 0xff)
Packit 6c0a39
Packit 6c0a39
static int _base64_to_bin(unsigned char dest[3], const char *source, int num);
Packit 6c0a39
static int get_equals(char *string);
Packit 6c0a39
Packit 6c0a39
/* First part: base64 to binary */
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Translates a base64 string into a binary one.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns A buffer containing the decoded string, NULL if something went
Packit 6c0a39
 *          wrong (e.g. incorrect char).
Packit 6c0a39
 */
Packit 6c0a39
ssh_buffer base64_to_bin(const char *source) {
Packit 6c0a39
  ssh_buffer buffer = NULL;
Packit 6c0a39
  unsigned char block[3];
Packit 6c0a39
  char *base64;
Packit 6c0a39
  char *ptr;
Packit 6c0a39
  size_t len;
Packit 6c0a39
  int equals;
Packit 6c0a39
Packit 6c0a39
  base64 = strdup(source);
Packit 6c0a39
  if (base64 == NULL) {
Packit 6c0a39
    return NULL;
Packit 6c0a39
  }
Packit 6c0a39
  ptr = base64;
Packit 6c0a39
Packit 6c0a39
  /* Get the number of equals signs, which mirrors the padding */
Packit 6c0a39
  equals = get_equals(ptr);
Packit 6c0a39
  if (equals > 2) {
Packit 6c0a39
    SAFE_FREE(base64);
Packit 6c0a39
    return NULL;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  buffer = ssh_buffer_new();
Packit 6c0a39
  if (buffer == NULL) {
Packit 6c0a39
    SAFE_FREE(base64);
Packit 6c0a39
    return NULL;
Packit 6c0a39
  }
Packit 6c0a39
  /*
Packit 6c0a39
   * The base64 buffer often contains sensitive data. Make sure we don't leak
Packit 6c0a39
   * sensitive data
Packit 6c0a39
   */
Packit 6c0a39
  ssh_buffer_set_secure(buffer);
Packit 6c0a39
Packit 6c0a39
  len = strlen(ptr);
Packit 6c0a39
  while (len > 4) {
Packit 6c0a39
    if (_base64_to_bin(block, ptr, 3) < 0) {
Packit 6c0a39
      goto error;
Packit 6c0a39
    }
Packit 6c0a39
    if (ssh_buffer_add_data(buffer, block, 3) < 0) {
Packit 6c0a39
      goto error;
Packit 6c0a39
    }
Packit 6c0a39
    len -= 4;
Packit 6c0a39
    ptr += 4;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  /*
Packit 6c0a39
   * Depending on the number of bytes resting, there are 3 possibilities
Packit 6c0a39
   * from the RFC.
Packit 6c0a39
   */
Packit 6c0a39
  switch (len) {
Packit 6c0a39
    /*
Packit 6c0a39
     * (1) The final quantum of encoding input is an integral multiple of
Packit 6c0a39
     *     24 bits. Here, the final unit of encoded output will be an integral
Packit 6c0a39
     *     multiple of 4 characters with no "=" padding
Packit 6c0a39
     */
Packit 6c0a39
    case 4:
Packit 6c0a39
      if (equals != 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      if (_base64_to_bin(block, ptr, 3) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      if (ssh_buffer_add_data(buffer, block, 3) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      SAFE_FREE(base64);
Packit 6c0a39
Packit 6c0a39
      return buffer;
Packit 6c0a39
    /*
Packit 6c0a39
     * (2) The final quantum of encoding input is exactly 8 bits; here, the
Packit 6c0a39
     *     final unit of encoded output will be two characters followed by
Packit 6c0a39
     *     two "=" padding characters.
Packit 6c0a39
     */
Packit 6c0a39
    case 2:
Packit 6c0a39
      if (equals != 2){
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
Packit 6c0a39
      if (_base64_to_bin(block, ptr, 1) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      if (ssh_buffer_add_data(buffer, block, 1) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      SAFE_FREE(base64);
Packit 6c0a39
Packit 6c0a39
      return buffer;
Packit 6c0a39
    /*
Packit 6c0a39
     * The final quantum of encoding input is exactly 16 bits. Here, the final
Packit 6c0a39
     * unit of encoded output will be three characters followed by one "="
Packit 6c0a39
     * padding character.
Packit 6c0a39
     */
Packit 6c0a39
    case 3:
Packit 6c0a39
      if (equals != 1) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      if (_base64_to_bin(block, ptr, 2) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      if (ssh_buffer_add_data(buffer,block,2) < 0) {
Packit 6c0a39
        goto error;
Packit 6c0a39
      }
Packit 6c0a39
      SAFE_FREE(base64);
Packit 6c0a39
Packit 6c0a39
      return buffer;
Packit 6c0a39
    default:
Packit 6c0a39
      /* 4,3,2 are the only padding size allowed */
Packit 6c0a39
      goto error;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
  SAFE_FREE(base64);
Packit 6c0a39
  ssh_buffer_free(buffer);
Packit 6c0a39
  return NULL;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#define BLOCK(letter, n) do {ptr = strchr(alphabet, source[n]); \
Packit 6c0a39
                             if(!ptr) return -1; \
Packit 6c0a39
                             i = ptr - alphabet; \
Packit 6c0a39
                             SET_##letter(*block, i); \
Packit 6c0a39
                         } while(0)
Packit 6c0a39
Packit 6c0a39
/* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */
Packit 6c0a39
static int to_block4(unsigned long *block, const char *source, int num) {
Packit 6c0a39
  char *ptr;
Packit 6c0a39
  unsigned int i;
Packit 6c0a39
Packit 6c0a39
  *block = 0;
Packit 6c0a39
  if (num < 1) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  BLOCK(A, 0); /* 6 bit */
Packit 6c0a39
  BLOCK(B,1); /* 12 bit */
Packit 6c0a39
Packit 6c0a39
  if (num < 2) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  BLOCK(C, 2); /* 18 bit */
Packit 6c0a39
Packit 6c0a39
  if (num < 3) {
Packit 6c0a39
    return 0;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  BLOCK(D, 3); /* 24 bit */
Packit 6c0a39
Packit 6c0a39
  return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* num = numbers of final bytes to be decoded */
Packit 6c0a39
static int _base64_to_bin(unsigned char dest[3], const char *source, int num) {
Packit 6c0a39
  unsigned long block;
Packit 6c0a39
Packit 6c0a39
  if (to_block4(&block, source, num) < 0) {
Packit 6c0a39
    return -1;
Packit 6c0a39
  }
Packit 6c0a39
  dest[0] = GET_A(block);
Packit 6c0a39
  dest[1] = GET_B(block);
Packit 6c0a39
  dest[2] = GET_C(block);
Packit 6c0a39
Packit 6c0a39
  return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* Count the number of "=" signs and replace them by zeroes */
Packit 6c0a39
static int get_equals(char *string) {
Packit 6c0a39
  char *ptr = string;
Packit 6c0a39
  int num = 0;
Packit 6c0a39
Packit 6c0a39
  while ((ptr=strchr(ptr,'=')) != NULL) {
Packit 6c0a39
    num++;
Packit 6c0a39
    *ptr = '\0';
Packit 6c0a39
    ptr++;
Packit 6c0a39
  }
Packit 6c0a39
Packit 6c0a39
  return num;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* thanks sysk for debugging my mess :) */
Packit 6c0a39
#define BITS(n) ((1 << (n)) - 1)
Packit 6c0a39
static void _bin_to_base64(unsigned char *dest, const unsigned char source[3],
Packit 6c0a39
    int len) {
Packit 6c0a39
  switch (len) {
Packit 6c0a39
    case 1:
Packit 6c0a39
      dest[0] = alphabet[(source[0] >> 2)];
Packit 6c0a39
      dest[1] = alphabet[((source[0] & BITS(2)) << 4)];
Packit 6c0a39
      dest[2] = '=';
Packit 6c0a39
      dest[3] = '=';
Packit 6c0a39
      break;
Packit 6c0a39
    case 2:
Packit 6c0a39
      dest[0] = alphabet[source[0] >> 2];
Packit 6c0a39
      dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)];
Packit 6c0a39
      dest[2] = alphabet[(source[1] & BITS(4)) << 2];
Packit 6c0a39
      dest[3] = '=';
Packit 6c0a39
      break;
Packit 6c0a39
    case 3:
Packit 6c0a39
      dest[0] = alphabet[(source[0] >> 2)];
Packit 6c0a39
      dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)];
Packit 6c0a39
      dest[2] = alphabet[ (source[2] >> 6) | (source[1] & BITS(4)) << 2];
Packit 6c0a39
      dest[3] = alphabet[source[2] & BITS(6)];
Packit 6c0a39
      break;
Packit 6c0a39
  }
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Converts binary data to a base64 string.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns the converted string
Packit 6c0a39
 */
Packit 6c0a39
unsigned char *bin_to_base64(const unsigned char *source, int len) {
Packit 6c0a39
  unsigned char *base64;
Packit 6c0a39
  unsigned char *ptr;
Packit 6c0a39
  int flen = len + (3 - (len % 3)); /* round to upper 3 multiple */
Packit 6c0a39
  flen = (4 * flen) / 3 + 1;
Packit 6c0a39
Packit 6c0a39
  base64 = malloc(flen);
Packit 6c0a39
  if (base64 == NULL) {
Packit 6c0a39
    return NULL;
Packit 6c0a39
  }
Packit 6c0a39
  ptr = base64;
Packit 6c0a39
Packit 6c0a39
  while(len > 0){
Packit 6c0a39
    _bin_to_base64(ptr, source, len > 3 ? 3 : len);
Packit 6c0a39
    ptr += 4;
Packit 6c0a39
    source += 3;
Packit 6c0a39
    len -= 3;
Packit 6c0a39
  }
Packit 6c0a39
  ptr[0] = '\0';
Packit 6c0a39
Packit 6c0a39
  return base64;
Packit 6c0a39
}