/* * 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. * * vim: ts=2 sw=2 et cindent */ /* just the dirtiest part of code i ever made */ #include #include #include #include "libssh/priv.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], 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). */ BUFFER *base64_to_bin(const char *source) { 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 = 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); 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, char *source,int num){ char *ptr; unsigned int i; *block=0; if(num<1) return 0; BLOCK(A,0); /* 6 bits */ BLOCK(B,1); /* 12 */ if(num<2) return 0; BLOCK(C,2); /* 18 */ if(num < 3) return 0; BLOCK(D,3); /* 24 */ return 0; } /* num = numbers of final bytes to be decoded */ static int _base64_to_bin(unsigned char dest[3], char *source,int num){ unsigned long block; if(to_block4(&block,source,num)) return -1; dest[0]=GET_A(block); dest[1]=GET_B(block); dest[2]=GET_C(block); return 0; } /* counts 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,'='))){ num++; *ptr=0; ptr++; } return num; } /* thanks sysk for debugging my mess :) */ #define BITS(n) ((1<>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; } } /** \brief Converts binary data to a base64 string * \returns the converted string * \internal */ unsigned char *bin_to_base64(unsigned char *source, int len){ int flen=len + (3 - (len %3)); /* round to upper 3 multiple */ unsigned char *buffer; unsigned char *ptr; flen=(4 * flen)/3 + 1 ; ptr=buffer=malloc(flen); while(len>0){ _bin_to_base64(ptr,source,len>3?3:len); ptr+=4; source +=3; len -=3; } ptr[0]=0; return buffer; }