blob: 04810ee1e5b0e642ddf72cda95c300f38134d19d [file] [log] [blame] [raw]
/*
Copyright: Boaz segev, 2016-2017
License: MIT except for any non-public-domain algorithms (none that I'm aware
of), which might be subject to their own licenses.
Feel free to copy, use and enjoy in accordance with to the license(s).
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "base64.h"
/* ***************************************************************************
Base64 encoding
*/
/** the base64 encoding array */
static char base64_encodes[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/**
a base64 decoding array
*/
static unsigned base64_decodes[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
a union used for Base64 parsing
*/
union base64_parser_u {
struct {
unsigned tail : 2;
unsigned data : 6;
} byte1;
struct {
unsigned prev : 8;
unsigned tail : 4;
unsigned head : 4;
} byte2;
struct {
unsigned prev : 16;
unsigned data : 6;
unsigned head : 2;
} byte3;
char bytes[3];
};
/**
This will encode a byte array (data) of a specified length (len) and
place the encoded data into the target byte buffer (target). The target buffer
MUST have enough room for the expected data.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if
the raw data's length isn't devisable by 3.
Always assume the target buffer should have room enough for (len*4/3 + 4)
bytes.
Returns the number of bytes actually written to the target buffer
(including the Base64 required padding and excluding a NULL terminator).
A NULL terminator char is NOT written to the target buffer.
*/
int bscrypt_base64_encode(char *target, const char *data, int len) {
int written = 0;
// // optional implementation: allow a non writing, length computation.
// if (!target)
// return (len % 3) ? (((len + 3) / 3) * 4) : (len / 3);
// use a union to avoid padding issues.
union base64_parser_u *section;
while (len >= 3) {
section = (void *)data;
target[0] = base64_encodes[section->byte1.data];
target[1] =
base64_encodes[(section->byte1.tail << 4) | (section->byte2.head)];
target[2] =
base64_encodes[(section->byte2.tail << 2) | (section->byte3.head)];
target[3] = base64_encodes[section->byte3.data];
target += 4;
data += 3;
len -= 3;
written += 4;
}
section = (void *)data;
if (len == 2) {
target[0] = base64_encodes[section->byte1.data];
target[1] =
base64_encodes[(section->byte1.tail << 4) | (section->byte2.head)];
target[2] = base64_encodes[section->byte2.tail << 2];
target[3] = '=';
target += 4;
written += 4;
} else if (len == 1) {
target[0] = base64_encodes[section->byte1.data];
target[1] = base64_encodes[section->byte1.tail << 4];
target[2] = '=';
target[3] = '=';
target += 4;
written += 4;
}
target[0] = 0; // NULL terminator
return written;
}
/**
This will decode a Base64 encoded string of a specified length (len) and
place the decoded data into the target byte buffer (target).
The target buffer MUST have enough room for the expected data.
A NULL byte will be appended to the target buffer. The function will return
the number of bytes written to the target buffer.
If the target buffer is NULL, the encoded string will be destructively edited
and the decoded data will be placed in the original string's buffer.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if
the raw data's length isn't devisable by 3. Hence, the target buffer should
be, at least, `base64_len/4*3 + 3` long.
Returns the number of bytes actually written to the target buffer (excluding
the NULL terminator byte).
*/
int bscrypt_base64_decode(char *target, char *encoded, int base64_len) {
if (base64_len <= 0)
return -1;
if (!target)
target = encoded;
union base64_parser_u section;
int written = 0;
// base64_encodes
// a struct that will be used to read the data.
while (base64_len >= 4) {
base64_len -= 4; // make sure we don't loop forever.
// copying the data allows us to write destructively to the same buffer
section.byte1.data = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte1.tail = (base64_decodes[(unsigned char)(*encoded)] >> 4);
section.byte2.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte2.tail = (base64_decodes[(unsigned char)(*encoded)] >> 2);
section.byte3.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte3.data = base64_decodes[(unsigned char)(*encoded)];
encoded++;
// write to the target buffer
*(target++) = section.bytes[0];
*(target++) = section.bytes[1];
*(target++) = section.bytes[2];
// count written bytes
written += section.bytes[2] ? 3 : section.bytes[1] ? 2 : 1;
}
// deal with the "tail" of the encoded stream
if (base64_len) {
// zero out data
section.bytes[0] = 0;
section.bytes[1] = 0;
section.bytes[2] = 0;
// byte 1 + 2 (2 might be padding)
section.byte1.data = base64_decodes[(unsigned char)*(encoded++)];
if (--base64_len) {
section.byte1.tail = base64_decodes[(unsigned char)(*encoded)] >> 4;
section.byte2.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
if (--base64_len) {
section.byte2.tail = base64_decodes[(unsigned char)(*encoded)] >> 4;
section.byte3.head = base64_decodes[(unsigned char)(*encoded)];
// --base64_len; // will always be 0 at this point (or it was 4)
}
}
// write to the target buffer
*(target++) = section.bytes[0];
if (section.bytes[1] || section.bytes[2])
*(target++) = section.bytes[1];
if (section.bytes[2])
*(target++) = section.bytes[2];
// count written bytes
written += section.bytes[2] ? 3 : section.bytes[1] ? 2 : 1;
}
*target = 0;
return written;
}
/*******************************************************************************
Base64
*/
#if defined(DEBUG) && DEBUG == 1
void bscrypt_test_base64(void) {
struct {
char *str;
char *base64;
} sets[] = {
// {"Man is distinguished, not only by his reason, but by this singular "
// "passion from other animals, which is a lust of the mind, that by a "
// "perseverance of delight in the continued and indefatigable generation
// "
// "of knowledge, exceeds the short vehemence of any carnal pleasure.",
// "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB"
// "0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
// "x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpb"
// "iB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xl"
// "ZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3V"
// "yZS4="},
{"any carnal pleasure.", "YW55IGNhcm5hbCBwbGVhc3VyZS4="},
{"any carnal pleasure", "YW55IGNhcm5hbCBwbGVhc3VyZQ=="},
{"any carnal pleasur", "YW55IGNhcm5hbCBwbGVhc3Vy"},
{NULL, NULL} // Stop
};
int i = 0;
char buffer[1024];
fprintf(stderr, "+ bscrypt");
while (sets[i].str) {
bscrypt_base64_encode(buffer, sets[i].str, strlen(sets[i].str));
if (strcmp(buffer, sets[i].base64)) {
fprintf(stderr,
":\n--- bscrypt Base64 Test FAILED!\nstring: %s\nlength: %lu\n "
"expected: %s\ngot: %s\n\n",
sets[i].str, strlen(sets[i].str), sets[i].base64, buffer);
break;
}
i++;
}
if (!sets[i].str)
fprintf(stderr, " Base64 encode passed.\n");
i = 0;
fprintf(stderr, "+ bscrypt");
while (sets[i].str) {
bscrypt_base64_decode(buffer, sets[i].base64, strlen(sets[i].base64));
if (strcmp(buffer, sets[i].str)) {
fprintf(stderr,
":\n--- bscrypt Base64 Test FAILED!\nbase64: %s\nexpected: "
"%s\ngot: %s\n\n",
sets[i].base64, sets[i].str, buffer);
return;
}
i++;
}
fprintf(stderr, " Base64 decode passed.\n");
char buff_b64[] = "any carnal pleasure.";
size_t b64_len;
clock_t start = clock();
for (size_t i = 0; i < 100000; i++) {
b64_len = bscrypt_base64_encode(buffer, buff_b64, sizeof(buff_b64) - 1);
bscrypt_base64_decode(buff_b64, buffer, b64_len);
}
fprintf(stderr, "bscrypt 100K Base64: %lf\n",
(double)(clock() - start) / CLOCKS_PER_SEC);
}
#endif