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