/*---------------------------------------------------------------------------*/
/* base64 */
/* ====== */
/* */
/* Base64 is a stand-alone C program to encode 7-Bit ASCII strings into */
/* base64 encoded strings and decode base64 encoded strings back into 7 bit */
/* ASCII strings. */
/* */
/* Base64 encoding is sometimes used for simple HTTP authentication. That is */
/* when strong encryption isn't necessary, Base64 encryption is used to */
/* authenticate User-ID's and Passwords. */
/* */
/* Base64 processes a string by octets (3 Byte blocks). For every octet in */
/* the decoded string, four byte blocks are generated in the encoded string. */
/* If the decoded string length is not a multiple of 3, the Base64 algorithm */
/* pads the end of the encoded string with equal signs '='. */
/* */
/* An example taken from RFC 2617 (HTTP Authentication): */
/* */
/* Resource (URL) requires basic authentication (Authorization: Basic) for */
/* access, otherwise a HTTP 401 Unauthorized response is returned. */
/* */
/* User-ID:Password string = "Aladdin:open sesame" */
/* Base64 encoded string = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
/* */
/* Usage: base64 OPTION [STRING] */
/* ------ */
/* OPTION: -h Displays a brief messages. */
/* -e Base64 encode the 7-Bit ASCII STRING. */
/* -d Decode the Base64 STRING to 7-Bit ASCII. */
/* */
/* STRING: Either a 7-Bit ASCII text string for encoding or a Base64 */
/* encoded string for decoding back to 7-Bit ASCII. */
/* */
/* Note: For EBCDIC and other collating sequences, the STRING must first */
/* be converted to 7-Bit ASCII before passing it to this module and */
/* the return string must be converted back to the appropriate */
/* collating sequence. */
/* */
/* Student Exercises: */
/* ------------------ */
/* 1. Modify base64 to accept an additional parameter "Quiet Mode" (-q) to */
/* optionally supress the ending statistics and only display the encoded */
/* or decoded string. */
/* */
/* 2. Make base64 callable from another program as follows: */
/* a. Add an externally callable function to determine and return the */
/* size of the buffer required for encoding or decoding. */
/* b. Make base64 accept three parameters; input and output buffer point- */
/* ers and a flag indicating encoding or decoding. */
/* c. Modify base64 so that a calling program can: */
/* i. Request the size of a buffer required either for encoding or */
/* decoding. */
/* ii. Allocate a buffer based on the result from the previous */
/* call. */
/* iii. Call base64 with the appropriate pointers and flag to encode */
/* or decode a string into the callers buffer. */
/* */
/* Copyright (c) 1994 - 2003 */
/* Marc Niegowski */
/* Connectivity, Inc. */
/* All rights reserved. */
/*---------------------------------------------------------------------------*/
#include <stdlib.h> // calloc and free prototypes.
#include <stdio.h> // printf prototype.
#include <string.h> // str* and memset prototypes.
typedef
unsigned
char uchar; // Define unsigned char as uchar.
typedef
unsigned
int uint; // Define unsigned int as uint.
bool b64help(void); // Displays brief help messages.
bool b64encode(char *); // Encodes a string to Base64.
bool b64decode(char *); // Decodes a string to ASCII.
void b64stats(char *, char *, bool); // Display encoding/decoding stats.
bool b64valid(uchar *); // Tests for a valid Base64 char.
bool b64isnot(char *, char *); // Displays an invalid message.
char *b64buffer(char *, bool); // Alloc. encoding/decoding buffer.
// Macro definitions:
#define b64is7bit(c) ((c) > 0x7f ? 0 : 1) // Valid 7-Bit ASCII character?
#define b64blocks(l) (((l) + 2) / 3 * 4 + 1)// Length rounded to 4 byte block.
#define b64octets(l) ((l) / 4 * 3 + 1) // Length rounded to 3 byte octet.
// Note: Tables are in hex to support different collating sequences
static
const // Base64 Index into encoding
uchar pIndex[] = { // and decoding table.
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f
};
static
const // Base64 encoding and decoding
uchar pBase64[] = { // table.
0x3e, 0x7f, 0x7f, 0x7f, 0x3f, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
};
static
const // Help message.
char *pHelp = "Usage: base64 OPTION [STRING]\n\n"
"OPTION: -d Decode base64 STRING to text string\n"
" -e Encode text STRING to base64 string\n"
" -h Display this help text\n\n"
"STRING: The normal decoded text for option -e\n"
" The encoded string for option -d\n\n"
"Be sure to enclose the STRING in quotes if it\n"
"contains any white space.\n\n";
/*---------------------------------------------------------------------------*/
/* main - Module entry point. */
/* ========================== */
/* */
/* Call with: int - Number of arguments on the stack. */
/* char ** - Pointer to an array of argument arrays. */
/* char ** - Pointer to an array of environment variable arrays.*/
/* */
/* Returns: bool - True (!0) if the operation was successful. */
/* - False (0) if the operation was unsuccessful. */
/*---------------------------------------------------------------------------*/
bool main(int argc, char **argv, char **envp)
{
printf("Base64 Encoder/Decoder\n\n");
if ((argc > 1) && (!strcmp(argv[1], "-h")))
return b64help(); // Operator asked for help.
if (argc != 3)
{
printf("Invalid number of parameters.\n\n");
return b64help(); // Operator doesn't know the parms.
}
if ((strlen(argv[1]) != 2) || (argv[1][0] != '-'))
{
printf("Invalid option: %s\n\n", argv[1]);
return b64help(); // Operator doesn't know the parms.
}
switch (argv[1][1])
{
case 'd': // Decoding?
return b64decode(argv[2]); // Yes, call decoder and...
break; // ...return with result code.
case 'e': // Encoding?
return b64encode(argv[2]); // Yes, call encoder and...
break; // ...return with result code.
default:
printf("Invalid option: %s\n\n", argv[1]);
return b64help(); // Operator doesn't know the parms.
}
return true; // Return to caller with true.
}
/*---------------------------------------------------------------------------*/
/* b64help - Display a brief help message. */
/* ======================================= */
/* */
/* Call with: void - Nothing. */
/* */
/* Returns: bool - False (0) for main if invalid parms were provided. */
/*---------------------------------------------------------------------------*/
bool b64help(void)
{
printf(pHelp); // Display the help message.
return false; // Return to caller with False.
}
/*---------------------------------------------------------------------------*/
/* b64encode - Encode a 7-Bit ASCII string to a Base64 string. */
/* =========================================================== */
/* */
/* Call with: char * - The 7-Bit ASCII string to encode. */
/* */
/* Returns: bool - True (!0) if the operation was successful. */
/* False (0) if the operation was unsuccessful. */
/*---------------------------------------------------------------------------*/
bool b64encode(char *s)
{
int l = strlen(s); // Get the length of the string.
int x = 0; // General purpose integers.
char *b, *p; // Encoded buffer pointers.
while (x < l) // Validate each byte of the string
{ // ...to ensure that it's 7-Bit...
if (!b64is7bit((uchar) *(s + x))) // ...ASCII.
{
printf("\"%s\" is not a 7-Bit ASCII string.\n", s);
return false; // Return false if it's not.
}
x++; // Next byte.
}
if (!(b = b64buffer(s, true))) // Allocate an encoding buffer.
return false; // Can't allocate encoding buffer.
memset(b, 0x3d, b64blocks(l) - 1); // Initialize it to "=".
p = b; // Save the buffer pointer.
x = 0; // Initialize string index.
while (x < (l - (l % 3))) // encode each 3 byte octet.
{
*b++ = pIndex[ s[x] >> 2];
*b++ = pIndex[((s[x] & 0x03) << 4) + (s[x + 1] >> 4)];
*b++ = pIndex[((s[x + 1] & 0x0f) << 2) + (s[x + 2] >> 6)];
*b++ = pIndex[ s[x + 2] & 0x3f];
x += 3; // Next octet.
}
if (l - x) // Partial octet remaining?
{
*b++ = pIndex[s[x] >> 2]; // Yes, encode it.
if (l - x == 1) // End of octet?
*b = pIndex[ (s[x] & 0x03) << 4];
else
{ // No, one more part.
*b++ = pIndex[((s[x] & 0x03) << 4) + (s[x + 1] >> 4)];
*b = pIndex[ (s[x + 1] & 0x0f) << 2];
}
}
b64stats(s, p, true); // Display some encoding stats.
free(p); // De-allocate the encoding buffer.
printf("Base64 encoding complete.\n");
return true; // Return to caller with success.
}
/*---------------------------------------------------------------------------*/
/* b64decode - Decode a Base64 string to a 7-Bit ASCII string. */
/* =========================================================== */
/* */
/* Call with: char * - The Base64 string to decode. */
/* */
/* Returns: bool - True (!0) if the operation was successful. */
/* False (0) if the operation was unsuccessful. */
/*---------------------------------------------------------------------------*/
bool b64decode(char *s)
{
int l = strlen(s); // Get length of Base64 string.
char *b, *p; // Decoding buffer pointers.
uchar c = 0; // Character to decode.
int x = 0; // General purpose integers.
int y = 0;
static // Collating sequence...
const // ...independant "===".
char pPad[] = {0x3d, 0x3d, 0x3d, 0x00};
if (l % 4) // If it's not modulo 4, then it...
return b64isnot(s, NULL); // ...can't be a Base64 string.
if (b = strchr(s, pPad[0])) // Only one, two or three equal...
{ // ...'=' signs are allowed at...
if ((b - s) < (l - 3)) // ...the end of the Base64 string.
return b64isnot(s, NULL); // Any other equal '=' signs are...
else // ...invalid.
if (strncmp(b, (char *) pPad + 3 - (s + l - b), s + l - b))
return b64isnot(s, NULL);
}
if (!(b = b64buffer(s, false))) // Allocate a decoding buffer.
return false; // Can't allocate decoding buffer.
p = s; // Save the encoded string pointer.
x = 0; // Initialize index.
while ((c = *s++)) // Decode every byte of the...
{ // Base64 string.
if (c == pPad[0]) // Ignore "=".
break;
if (!b64valid(&c)) // Valid Base64 Index?
return b64isnot(s, b); // No, return false.
switch(x % 4) // Decode 4 byte words into...
{ // ...3 byte octets.
case 0: // Byte 0 of word.
b[y] = c << 2;
break;
case 1: // Byte 1 of word.
b[y] |= c >> 4;
if (!b64is7bit((uchar) b[y++])) // Is 1st byte of octet valid?
return b64isnot(s, b); // No, return false.
b[y] = (c & 0x0f) << 4;
break;
case 2: // Byte 2 of word.
b[y] |= c >> 2;
if (!b64is7bit((uchar) b[y++])) // Is 2nd byte of octet valid?
return b64isnot(s, b); // No, return false.
b[y] = (c & 0x03) << 6;
break;
case 3: // Byte 3 of word.
b[y] |= c;
if (!b64is7bit((uchar) b[y++])) // Is 3rd byte of octet valid?
return b64isnot(s, b); // No, return false.
}
x++; // Increment word byte.
}
b64stats(p, b, false); // Display some decoding stats.
free(b); // De-allocate decoding buffer.
printf("Base64 decoding complete.\n");
return true; // Return to caller with success.
}
/*---------------------------------------------------------------------------*/
/* b64valid - validate the character to decode. */
/* ============================================ */
/* */
/* Checks whether the character to decode falls within the boundaries of the */
/* Base64 decoding table. */
/* */
/* Call with: char - The Base64 character to decode. */
/* */
/* Returns: bool - True (!0) if the character is valid. */
/* False (0) if the character is not valid. */
/*---------------------------------------------------------------------------*/
bool b64valid(uchar *c)
{
if ((*c < 0x2b) || (*c > 0x7a)) // If not within the range of...
return false; // ...the table, return false.
if ((*c = pBase64[*c - 0x2b]) == 0x7f) // If it falls within one of...
return false; // ...the gaps, return false.
return true; // Otherwise, return true.
}
/*---------------------------------------------------------------------------*/
/* b64isnot - Display an error message and clean up. */
/* ================================================= */
/* */
/* Call this routine to display a message indicating that the string being */
/* decoded is an invalid Base64 string and de-allocate the decoding buffer. */
/* */
/* Call with: char * - Pointer to the Base64 string being decoded. */
/* char * - Pointer to the decoding buffer or NULL if it isn't */
/* allocated and doesn't need to be de-allocated. */
/* */
/* Returns: bool - True (!0) if the character is valid. */
/* False (0) if the character is not valid. */
/*---------------------------------------------------------------------------*/
bool b64isnot(char *p, char *b)
{
printf("\"%s\" is not a Base64 encoded string.\n", p);
if (b) // If the buffer pointer is not...
free(b); // ...NULL, de-allocate it.
return false; // Return false for main.
}
/*---------------------------------------------------------------------------*/
/* b64buffer - Allocate the decoding or encoding buffer. */
/* ===================================================== */
/* */
/* Call this routine to allocate an encoding buffer in 4 byte blocks or a */
/* decoding buffer in 3 byte octets. We use "calloc" to initialize the */
/* buffer to 0x00's for strings. */
/* */
/* Call with: char * - Pointer to the string to be encoded or decoded. */
/* bool - True (!0) to allocate an encoding buffer. */
/* False (0) to allocate a decoding buffer. */
/* */
/* Returns: char * - Pointer to the buffer or NULL if the buffer */
/* could not be allocated. */
/*---------------------------------------------------------------------------*/
char *b64buffer(char *s, bool f)
{
int l = strlen(s); // String size to encode or decode.
char *b; // String pointers.
if (!l) // If the string size is 0...
return NULL; // ...return null.
if (!(b = (char *) calloc((f ? b64blocks(l) : b64octets(l)),
sizeof(char))))
printf("Insufficient real memory to %s \"%s\".\n",
(f ? "encode" : "decode"), s);
return b; // Return the pointer or null.
}
/*---------------------------------------------------------------------------*/
/* b64stats - Display encoding or decoding statistics. */
/* =================================================== */
/* */
/* Call this routine to display some encoding or decoding statistics. */
/* */
/* Call with: char * - Pointer to original input string. */
/* char * - Pointer to the resultant encoded or decoded string.*/
/* bool - True (!0) to display encoding statistics. */
/* False (0) to display decoding statistics. */
/* */
/* Returns: void - Nothing. */
/*---------------------------------------------------------------------------*/
void b64stats(char *s, char *b, bool f)
{
char *e, *d, *p; // General purpose string pointers.
e = "Encoded"; // Point to the "Encoded" string.
d = "Decoded"; // Point to the "Decoded" string.
if (!f) // If we are decoding...
{ // ...switch the pointers.
p = e; // Save, the "Encoded" pointer.
e = d; // Point to the "Decoded" string.
d = p; // Point to the "Encoded" string.
}
// Display the statistics.
printf("%s string length = %d\n", d, strlen(s));
printf("%s string = \"%s\"\n", d, s);
printf("%s buffer size = %d\n", e,
(f ? b64blocks(strlen(s)) : b64octets(strlen(s))));
printf("%s string length = %d\n", e, strlen(b));
printf("%s string = \"%s\"\n", e, b);
}
|