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