/* base64 encoder/decoder based on public domain implementation * by Chris Venter */ #include #include #include "base64.h" static char base64_encode_value(char value_in) { static const char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (value_in > 63) return '='; return encoding[(int)value_in]; } static char *base64_encode_block(const char *plaintext_in, int length_in, char *codechar) { const char *plainchar = plaintext_in; const char *const plaintextend = plaintext_in + length_in; char result; char fragment; while (1) { if (plainchar == plaintextend) { return codechar; } fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; if (plainchar == plaintextend) { *codechar++ = base64_encode_value(result); *codechar++ = '='; *codechar++ = '='; return codechar; } fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; if (plainchar == plaintextend) { *codechar++ = base64_encode_value(result); *codechar++ = '='; return codechar; } fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); } /* control should not reach here */ return codechar; } #define BASE64_DEFAULT_LINE_LENGTH 64 char *b64encode(const void *data, size_t len, int linelen) { size_t encodedlen; const char *dataptr = data; char *output; char *outptr; if (data == NULL) return NULL; if (linelen < 0) linelen = BASE64_DEFAULT_LINE_LENGTH; linelen /= 4; encodedlen = ((len + 2) / 3) * 4; if (linelen > 0) { encodedlen += encodedlen/(linelen * 4) + 1; } ++encodedlen; /* for zero termination */ output = malloc(encodedlen); if (output == NULL) return NULL; outptr = output; while (len > 0) { if (linelen > 0 && len > linelen * 3) { outptr = base64_encode_block(dataptr, linelen * 3, outptr); len -= linelen * 3; dataptr += linelen * 3; } else { outptr = base64_encode_block(dataptr, len, outptr); len = 0; } if (linelen > 0) { *outptr++ = '\n'; } } *outptr = '\0'; return output; } static int base64_decode_value(char value_in) { static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,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,-1,-1,-1,-1,-1,-1,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}; static const char decoding_size = sizeof(decoding); value_in -= 43; if (value_in < 0 || value_in > decoding_size) return -1; return decoding[(int)value_in]; } static size_t base64_decode_block(const char *code_in, const size_t length_in, char *plaintext_out) { const char *codechar = code_in; char *plainchar = plaintext_out; char fragment; *plainchar = 0; while (1) { do { if (codechar == code_in+length_in) { return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar = (fragment & 0x03f) << 2; do { if (codechar == code_in+length_in) { return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x030) >> 4; *plainchar = (fragment & 0x00f) << 4; do { if (codechar == code_in+length_in) { return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x03c) >> 2; *plainchar = (fragment & 0x003) << 6; do { if (codechar == code_in+length_in) { return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x03f); } /* control should not reach here */ return plainchar - plaintext_out; } int b64decode(const char *in, void **out, size_t *outlen) { size_t outcnt = 0; const char *inptr = in; *out = NULL; if (in == NULL) { return 1; } while (*inptr != '\0') { /* assume all ASCII control chars as whitespace */ if (*inptr > 32) { if (base64_decode_value(*inptr) != -1) { ++outcnt; } else { return 3; } } ++inptr; } if (outcnt % 4 != 0) return 2; outcnt = (outcnt / 4) * 3; *out = malloc(outcnt + 1); /* base64_decode_block can write one extra character */ if (*out == NULL) return 4; *outlen = base64_decode_block(in, inptr - in, *out); return 0; } #define CRC24_INIT 0xb704ce #define CRC24_POLY 0x1864cfb char *b64crc(const unsigned char *data, size_t len) { uint32_t crc = CRC24_INIT; int i; while (len--) { crc ^= (*data++) << 16; for (i = 0; i < 8; i++) { crc <<= 1; if (crc & 0x1000000) crc ^= CRC24_POLY; } } crc = htonl(crc & 0xffffff); data = (unsigned char *)&crc; ++data; return b64encode(data, 3, 0); } #ifdef BASE64_TEST #include #include int main(int argc, char *argv[]) { static char tst[]="wtrt8122čLýáj\x20s ~ýhž\t4\x02šjjmBvž^%$RTš#á.íěj\x1hčýčŤc+"; char *encoded; void *decoded; size_t size; int err; printf("Original: %lu\n%s\n", sizeof(tst)-1, tst); encoded = b64encode(tst, sizeof(tst)-1, 64); printf("Encoded: %lu\n%s\n", strlen(encoded), encoded); if ((err = b64decode(encoded, &decoded, &size)) != 0) { fprintf(stderr, "Error in decode: %d\n", err); return 1; } printf("Decoded:\n%.*s\n", (int)size, (char *)decoded); if (size != sizeof(tst)-1) { fprintf(stderr, "Size differs orig: %lu new: %lu\n", sizeof(tst)-1, size); return 1; } if (memcmp(tst, decoded, size) != 0) { fprintf(stderr, "Decoded data differs.\n"); return 1; } fprintf(stderr, "OK\n"); return 0; } #endif