From 78158c87e37ce3b41cd5145e29baea38fd002374 Mon Sep 17 00:00:00 2001 From: Peter Barker Date: Mon, 9 Mar 2015 15:59:48 +1100 Subject: [PATCH] base64: implements rfc4648, the base64 encoding Encode buffers into base64 according to rfc4648. Decode base64-encoded buffers according to the same standard. Signed-off-by: Signed-off-by: Rusty Russell --- Makefile-ccan | 1 + ccan/base64/LICENSE | 1 + ccan/base64/_info | 41 +++++ ccan/base64/base64.c | 254 ++++++++++++++++++++++++++ ccan/base64/base64.h | 241 +++++++++++++++++++++++++ ccan/base64/test/moretap.h | 96 ++++++++++ ccan/base64/test/run.c | 359 +++++++++++++++++++++++++++++++++++++ 7 files changed, 993 insertions(+) create mode 120000 ccan/base64/LICENSE create mode 100644 ccan/base64/_info create mode 100644 ccan/base64/base64.c create mode 100644 ccan/base64/base64.h create mode 100644 ccan/base64/test/moretap.h create mode 100644 ccan/base64/test/run.c diff --git a/Makefile-ccan b/Makefile-ccan index 54430c03..cfaa0a9a 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -35,6 +35,7 @@ MODS_WITH_SRC := antithread \ asprintf \ autodata \ avl \ + base64 \ bdelta \ block_pool \ breakpoint \ diff --git a/ccan/base64/LICENSE b/ccan/base64/LICENSE new file mode 120000 index 00000000..2354d129 --- /dev/null +++ b/ccan/base64/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/base64/_info b/ccan/base64/_info new file mode 100644 index 00000000..22981990 --- /dev/null +++ b/ccan/base64/_info @@ -0,0 +1,41 @@ +#include "config.h" + +/** + * base64 - base64 encoding and decoding (rfc4648). + * + * base64 encoding is used to encode data in a 7-bit clean manner. + * Commonly used for escaping data before encapsulation or transfer + * + * Example: + * #include + * #include + * #include + * + * int main(int argc, char *argv[]) + * { + * char *base64_encoded_string; + * int i; + * + * // print the base64-encoded form of the program arguments + * for(i=1;i +#include +#include +#include + +/** + * sixbit_to_b64 - maps a 6-bit value to the base64 alphabet + * @param map A base 64 map (see base64_init_map) + * @param sixbit Six-bit value to map + * @return a base 64 character + */ +static char sixbit_to_b64(const base64_maps_t *maps, const uint8_t sixbit) +{ + assert(sixbit >= 0); + assert(sixbit <= 63); + + return maps->encode_map[(unsigned char)sixbit]; +} + +/** + * sixbit_from_b64 - maps a base64-alphabet character to its 6-bit value + * @param maps A base 64 maps structure (see base64_init_maps) + * @param sixbit Six-bit value to map + * @return a six-bit value + */ +static int8_t sixbit_from_b64(const base64_maps_t *maps, + const unsigned char b64letter) +{ + int8_t ret; + + ret = maps->decode_map[(unsigned char)b64letter]; + if (ret == (char)0xff) { + errno = EDOM; + return -1; + } + + return ret; +} + +bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char) +{ + return (maps->decode_map[(const unsigned char)b64char] != (char)0xff); +} + +void base64_init_maps(base64_maps_t *dest, const char src[64]) +{ + unsigned char i; + + memcpy(dest->encode_map,src,64); + memset(dest->decode_map,0xff,256); + for (i=0; i<64; i++) { + dest->decode_map[(unsigned char)src[i]] = i; + } +} + +size_t base64_encoded_length(size_t srclen) +{ + return ((srclen + 2) / 3) * 4; +} + +void base64_encode_triplet_using_maps(const base64_maps_t *maps, + char dest[4], const char src[3]) +{ + char a = src[0]; + char b = src[1]; + char c = src[2]; + + dest[0] = sixbit_to_b64(maps, (a & 0xfc) >> 2); + dest[1] = sixbit_to_b64(maps, ((a & 0x3) << 4) | ((b & 0xf0) >> 4)); + dest[2] = sixbit_to_b64(maps, ((c & 0xc0) >> 6) | ((b & 0xf) << 2)); + dest[3] = sixbit_to_b64(maps, c & 0x3f); +} + +void base64_encode_tail_using_maps(const base64_maps_t *maps, char dest[4], + const char *src, const size_t srclen) +{ + char longsrc[3] = { 0 }; + + assert(srclen <= 3); + + memcpy(longsrc, src, srclen); + base64_encode_triplet_using_maps(maps, dest, longsrc); + memset(dest+1+srclen, '=', 3-srclen); +} + +ssize_t base64_encode_using_maps(const base64_maps_t *maps, + char *dest, const size_t destlen, + const char *src, const size_t srclen) +{ + size_t src_offset = 0; + size_t dest_offset = 0; + + if (destlen < base64_encoded_length(srclen)) { + errno = EOVERFLOW; + return -1; + } + + while (srclen - src_offset >= 3) { + base64_encode_triplet_using_maps(maps, &dest[dest_offset], &src[src_offset]); + src_offset += 3; + dest_offset += 4; + } + + if (src_offset < srclen) { + base64_encode_tail_using_maps(maps, &dest[dest_offset], &src[src_offset], srclen-src_offset); + dest_offset += 4; + } + + memset(&dest[dest_offset], '\0', destlen-dest_offset); + + return dest_offset; +} + +size_t base64_decoded_length(size_t srclen) +{ + return ((srclen+3)/4*3); +} + +int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3], + const char src[4]) +{ + signed char a; + signed char b; + signed char c; + signed char d; + + a = sixbit_from_b64(maps, src[0]); + b = sixbit_from_b64(maps, src[1]); + c = sixbit_from_b64(maps, src[2]); + d = sixbit_from_b64(maps, src[3]); + + if ((a == -1) || (b == -1) || (c == -1) || (d == -1)) { + return -1; + } + + dest[0] = (a << 2) | (b >> 4); + dest[1] = ((b & 0xf) << 4) | (c >> 2); + dest[2] = ((c & 0x3) << 6) | d; + + return 0; +} + + +int base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3], + const char * src, const size_t srclen) +{ + char longsrc[4]; + int quartet_result; + size_t insize = srclen; + + while (insize != 0 && + src[insize-1] == '=') { /* throw away padding symbols */ + insize--; + } + if (insize == 0) { + return 0; + } + if (insize == 1) { + /* the input is malformed.... */ + errno = EINVAL; + return -1; + } + memcpy(longsrc, src, insize); + memset(longsrc+insize, 'A', 4-insize); + quartet_result = base64_decode_quartet_using_maps(maps, dest, longsrc); + if (quartet_result == -1) { + return -1; + } + + return insize - 1; +} + +ssize_t base64_decode_using_maps(const base64_maps_t *maps, + char *dest, const size_t destlen, + const char *src, const size_t srclen) +{ + ssize_t dest_offset = 0; + ssize_t i; + size_t more; + + if (destlen < base64_decoded_length(srclen)) { + errno = EOVERFLOW; + return -1; + } + + for(i=0; srclen - i > 4; i+=4) { + if (base64_decode_quartet_using_maps(maps, &dest[dest_offset], &src[i]) == -1) { + return -1; + } + dest_offset += 3; + } + + more = base64_decode_tail_using_maps(maps, &dest[dest_offset], &src[i], srclen - i); + if (more == -1) { + return -1; + } + dest_offset += more; + + memset(&dest[dest_offset], '\0', destlen-dest_offset); + + return dest_offset; +} + + + + +/** + * base64_maps_rfc4648 - pregenerated maps struct for rfc4648 + */ +static const base64_maps_t base64_maps_rfc4648 = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", + + "\xff\xff\xff\xff\xff" /* 0 */ \ + "\xff\xff\xff\xff\xff" /* 5 */ \ + "\xff\xff\xff\xff\xff" /* 10 */ \ + "\xff\xff\xff\xff\xff" /* 15 */ \ + "\xff\xff\xff\xff\xff" /* 20 */ \ + "\xff\xff\xff\xff\xff" /* 25 */ \ + "\xff\xff\xff\xff\xff" /* 30 */ \ + "\xff\xff\xff\xff\xff" /* 35 */ \ + "\xff\xff\xff\x3e\xff" /* 40 */ \ + "\xff\xff\x3f\x34\x35" /* 45 */ \ + "\x36\x37\x38\x39\x3a" /* 50 */ \ + "\x3b\x3c\x3d\xff\xff" /* 55 */ \ + "\xff\xff\xff\xff\xff" /* 60 */ \ + "\x00\x01\x02\x03\x04" /* 65 A */ \ + "\x05\x06\x07\x08\x09" /* 70 */ \ + "\x0a\x0b\x0c\x0d\x0e" /* 75 */ \ + "\x0f\x10\x11\x12\x13" /* 80 */ \ + "\x14\x15\x16\x17\x18" /* 85 */ \ + "\x19\xff\xff\xff\xff" /* 90 */ \ + "\xff\xff\x1a\x1b\x1c" /* 95 */ \ + "\x1d\x1e\x1f\x20\x21" /* 100 */ \ + "\x22\x23\x24\x25\x26" /* 105 */ \ + "\x27\x28\x29\x2a\x2b" /* 110 */ \ + "\x2c\x2d\x2e\x2f\x30" /* 115 */ \ + "\x31\x32\x33\xff\xff" /* 120 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */ \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */ +}; diff --git a/ccan/base64/base64.h b/ccan/base64/base64.h new file mode 100644 index 00000000..283c6e44 --- /dev/null +++ b/ccan/base64/base64.h @@ -0,0 +1,241 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#ifndef CCAN_BASE64_H +#define CCAN_BASE64_H + +#include +#include +#include + +/** + * base64_maps_t - structure to hold maps for encode/decode + */ +typedef struct { + char encode_map[64]; + signed char decode_map[256]; +} base64_maps_t; + +/** + * base64_encoded_length - Calculate encode buffer length + * @param srclen the size of the data to be encoded + * @note add 1 to this to get null-termination + * @return Buffer length required for encode + */ +size_t base64_encoded_length(size_t srclen); + +/** + * base64_decoded_length - Calculate decode buffer length + * @param srclen Length of the data to be decoded + * @note This does not return the size of the decoded data! see base64_decode + * @return Minimum buffer length for safe decode + */ +size_t base64_decoded_length(size_t srclen); + +/** + * base64_init_maps - populate a base64_maps_t based on a supplied alphabet + * @param dest A base64 maps object + * @param src Alphabet to populate the maps from (e.g. base64_alphabet_rfc4648) + */ +void base64_init_maps(base64_maps_t *dest, const char src[64]); + + +/** + * base64_encode_triplet_using_maps - encode 3 bytes into base64 using a specific alphabet + * @param maps Maps to use for encoding (see base64_init_maps) + * @param dest Buffer containing 3 bytes + * @param src Buffer containing 4 characters + */ +void base64_encode_triplet_using_maps(const base64_maps_t *maps, + char dest[4], const char src[3]); + +/** + * base64_encode_tail_using_maps - encode the final bytes of a source using a specific alphabet + * @param maps Maps to use for encoding (see base64_init_maps) + * @param dest Buffer containing 4 bytes + * @param src Buffer containing srclen bytes + * @param srclen Number of bytes (<= 3) to encode in src + */ +void base64_encode_tail_using_maps(const base64_maps_t *maps, char dest[4], + const char *src, size_t srclen); + +/** + * base64_encode_using_maps - encode a buffer into base64 using a specific alphabet + * @param maps Maps to use for encoding (see base64_init_maps) + * @param dest Buffer to encode into + * @param destlen Length of dest + * @param src Buffer to encode + * @param srclen Length of the data to encode + * @return Number of encoded bytes set in dest. -1 on error (and errno set) + * @note dest will be nul-padded to destlen (past any required padding) + * @note sets errno = EOVERFLOW if destlen is too small + */ +ssize_t base64_encode_using_maps(const base64_maps_t *maps, + char *dest, size_t destlen, + const char *src, size_t srclen); + +/* + * base64_char_in_alphabet - returns true if character can be part of an encoded string + * @param maps A base64 maps object (see base64_init_maps) + * @param b64char Character to check + */ +bool base64_char_in_alphabet(const base64_maps_t *maps, char b64char); + +/** + * base64_decode_using_maps - decode a base64-encoded string using a specific alphabet + * @param maps A base64 maps object (see base64_init_maps) + * @param dest Buffer to decode into + * @param destlen length of dest + * @param src the buffer to decode + * @param srclen the length of the data to decode + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note dest will be nul-padded to destlen + * @note sets errno = EOVERFLOW if destlen is too small + * @note sets errno = EDOM if src contains invalid characters + */ +ssize_t base64_decode_using_maps(const base64_maps_t *maps, + char *dest, size_t destlen, + const char *src, size_t srclen); + +/** + * base64_decode_quartet_using_maps - decode 4 bytes from base64 using a specific alphabet + * @param maps A base64 maps object (see base64_init_maps) + * @param dest Buffer containing 3 bytes + * @param src Buffer containing 4 bytes + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note sets errno = EDOM if src contains invalid characters + */ +int base64_decode_quartet_using_maps(const base64_maps_t *maps, + char dest[3], const char src[4]); + +/** + * base64_decode_tail_using_maps - decode the final bytes of a base64 string using a specific alphabet + * @param maps A base64 maps object (see base64_init_maps) + * @param dest Buffer containing 3 bytes + * @param src Buffer containing 4 bytes - padded with '=' as required + * @param srclen Number of bytes to decode in src + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note sets errno = EDOM if src contains invalid characters + * @note sets errno = EINVAL if src is an invalid base64 tail + */ +int base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest, + const char *src, size_t srclen); + + +/* the rfc4648 functions: */ + +static const base64_maps_t base64_maps_rfc4648; + +/** + * base64_encode - Encode a buffer into base64 according to rfc4648 + * @param dest Buffer to encode into + * @param destlen Length of the destination buffer + * @param src Buffer to encode + * @param srclen Length of the data to encode + * @return Number of encoded bytes set in dest. -1 on error (and errno set) + * @note dest will be nul-padded to destlen (past any required padding) + * @note sets errno = EOVERFLOW if destlen is too small + * + * This function encodes src according to http://tools.ietf.org/html/rfc4648 + * + * Example: + * size_t encoded_length; + * char dest[100]; + * const char *src = "This string gets encoded"; + * encoded_length = base64_encode(dest, sizeof(dest), src, strlen(src)); + * printf("Returned data of length %zd @%p\n", encoded_length, &dest); + */ +static inline +ssize_t base64_encode(char *dest, size_t destlen, + const char *src, size_t srclen) +{ + return base64_encode_using_maps(&base64_maps_rfc4648, + dest, destlen, src, srclen); +} + +/** + * base64_encode_triplet - encode 3 bytes into base64 according to rfc4648 + * @param dest Buffer containing 4 bytes + * @param src Buffer containing 3 bytes + */ +static inline +void base64_encode_triplet(char dest[4], const char src[3]) +{ + base64_encode_triplet_using_maps(&base64_maps_rfc4648, dest, src); +} + +/** + * base64_encode_tail - encode the final bytes of a source according to rfc4648 + * @param dest Buffer containing 4 bytes + * @param src Buffer containing srclen bytes + * @param srclen Number of bytes (<= 3) to encode in src + */ +static inline +void base64_encode_tail(char dest[4], const char *src, size_t srclen) +{ + base64_encode_tail_using_maps(&base64_maps_rfc4648, dest, src, srclen); +} + + +/** + * base64_decode - decode An rfc4648 base64-encoded string + * @param dest Buffer to decode into + * @param destlen Length of the destination buffer + * @param src Buffer to decode + * @param srclen Length of the data to decode + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note dest will be nul-padded to destlen + * @note sets errno = EOVERFLOW if destlen is too small + * @note sets errno = EDOM if src contains invalid characters + * + * This function decodes the buffer according to + * http://tools.ietf.org/html/rfc4648 + * + * Example: + * size_t decoded_length; + * char ret[100]; + * const char *src = "Zm9vYmFyYmF6"; + * decoded_length = base64_decode(ret, sizeof(ret), src, strlen(src)); + * printf("Returned data of length %zd @%p\n", decoded_length, &ret); + */ +static inline +ssize_t base64_decode(char *dest, size_t destlen, + const char *src, size_t srclen) +{ + return base64_decode_using_maps(&base64_maps_rfc4648, + dest, destlen, src, srclen); +} + +/** + * base64_decode_quartet - decode the first 4 characters in src into dest + * @param dest Buffer containing 3 bytes + * @param src Buffer containing 4 characters + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note sets errno = EDOM if src contains invalid characters + */ +static inline +int base64_decode_quartet(char dest[3], const char src[4]) +{ + return base64_decode_quartet_using_maps(&base64_maps_rfc4648, + dest, src); +} + +/** + * @brief decode the final bytes of a base64 string from src into dest + * @param dest Buffer containing 3 bytes + * @param src Buffer containing 4 bytes - padded with '=' as required + * @param srclen Number of bytes to decode in src + * @return Number of decoded bytes set in dest. -1 on error (and errno set) + * @note sets errno = EDOM if src contains invalid characters + * @note sets errno = EINVAL if src is an invalid base64 tail + */ +static inline +ssize_t base64_decode_tail(char dest[3], const char *src, size_t srclen) +{ + return base64_decode_tail_using_maps(&base64_maps_rfc4648, + dest, src, srclen); +} + +/* end rfc4648 functions */ + + + +#endif /* CCAN_BASE64_H */ diff --git a/ccan/base64/test/moretap.h b/ccan/base64/test/moretap.h new file mode 100644 index 00000000..114445c3 --- /dev/null +++ b/ccan/base64/test/moretap.h @@ -0,0 +1,96 @@ +#ifndef _BASE64_MORETAP_H +#define _BASE64_MORETAP_H + +#include + +/** + * is_str - OK if strings are equal + * @e1: expression for the variable string + * @e2: expression for the expected string + * + * If the strings are equal, the test passes. + * + * Example: + * is_str(give_me_a_fred(),"fred"); + */ +static void _is_str(char *got,const char *expected, const char *got_string, const char *expected_string, const char *func, const char *file, int line) { + if (streq(expected,got)) { + _gen_result(1, func, file, line,"%s eq %s", + got_string,expected_string); + } else { + _gen_result(0, func, file, line,"%s eq %s", + got_string,expected_string); + diag("Expected: %s",expected); + diag(" Got: %s",got); + } +} +# define is_str(got,expected) _is_str(got,expected,#got,#expected,__func__, __FILE__, __LINE__) + + +/** + * is_int - OK if arguments are equal when cast to integers + * @e1: expression for the number + * @e2: expression for the expected number + * + * If the numbers are equal, the test passes. + * + * Example: + * is_int(give_me_17(),17); + */ +# define is_int(e1,e2 ...) \ + (((int)e1)==((int)e2) ? \ + _gen_result(1, __func__, __FILE__, __LINE__,"%s == %s",#e1,#e2) : \ + (_gen_result(0, __func__, __FILE__, __LINE__,"%s == %s",#e1,#e2)) || (diag("Expected: %d",e2),diag(" Got: %d",e1),0)) /* diag is void; note commas. */ + + + +/** + * is_mem - OK if arguments are identical up to length @e3 + * @e1: expression for the buffer + * @e2: expression for the expected buffer + * @e2: length to compare in buffers + * + * If the buffers are equal up to @e2, the test passes. + * + * Example: + * is_mem(give_me_foo(),"foo",3); + */ +static void _is_mem(const char *got, const char *expected, const size_t len, + const char *got_string, const char *expected_string, const char *len_string, + const char *func, const char *file, int line) { + size_t offset = 0; + + for (offset=0; offset +#include +#include + +#include +#include + +#include +#include "moretap.h" + +static void * xmalloc(size_t size); + +/* not defined in terms of test_encode_using_maps so we cross + appropriate paths in library */ +#define test_encode(src,srclen,expected) \ + do { \ + size_t destlen; \ + char * dest; \ + destlen = base64_encoded_length(srclen); \ + destlen++; /* null termination */ \ + dest = xmalloc(destlen); \ + ok1(base64_encode(dest,destlen,src,srclen) != -1); \ + is_str(dest,expected); \ + free(dest); \ + } while (0) + +#define test_encode_using_alphabet(alphastring,src,srclen,expected) \ + do { \ + size_t destlen; \ + char * dest; \ + base64_maps_t maps; \ + base64_init_maps(&maps,alphastring); \ + destlen = base64_encoded_length(srclen); \ + destlen++; /* null termination */ \ + dest = xmalloc(destlen); \ + ok1(base64_encode_using_maps(&maps,dest,destlen,src,srclen) != -1); \ + is_str(dest,expected); \ + free(dest); \ + } while (0) + +/* not defined in terms of test_decode_using_alphabet so we cross + appropriate paths in library */ +#define test_decode(src,srclen,expected,expectedlen) \ + do { \ + size_t destlen; \ + size_t bytes_used; \ + char * dest; \ + destlen = base64_decoded_length(srclen); \ + dest = xmalloc(destlen); \ + ok1((bytes_used = base64_decode(dest,destlen,src,srclen)) != -1); \ + is_size_t(bytes_used,expectedlen); \ + is_mem(dest,expected,bytes_used); \ + free(dest); \ + } while (0) + +#define test_decode_using_alphabet(alphastring,src,srclen,expected,expectedlen) \ + do { \ + size_t destlen; \ + size_t bytes_used; \ + char * dest; \ + base64_maps_t maps; \ + \ + base64_init_maps(&maps,alphastring); \ + destlen = base64_decoded_length(srclen); \ + dest = xmalloc(destlen); \ + ok1((bytes_used = base64_decode_using_maps(&maps,dest,destlen,src,srclen)) != -1); \ + is_size_t(bytes_used,expectedlen); \ + is_mem(dest,expected,bytes_used); \ + free(dest); \ + } while (0) + +#define check_bad_range_decode(stuff_to_test,stufflen) \ +do { \ + char dest[10]; \ + errno = 0; \ + is_size_t(base64_decode(dest,sizeof(dest),stuff_to_test,(size_t)stufflen), \ + (size_t)-1); \ + is_int(errno,EDOM); \ +} while (0) + +int +main(int argc, char *argv[]) +{ + plan_tests(131); + + is_size_t(base64_encoded_length(0),(size_t)0); + is_size_t(base64_encoded_length(1),(size_t)4); + is_size_t(base64_encoded_length(2),(size_t)4); + is_size_t(base64_encoded_length(3),(size_t)4); + is_size_t(base64_encoded_length(512),(size_t)684); + + /* straight from page 11 of http://tools.ietf.org/html/rfc4648 */ + test_encode("",0,""); + test_encode("f",1,"Zg=="); + test_encode("fo",2,"Zm8="); + + test_encode("foo",3,"Zm9v"); + test_encode("foob",4,"Zm9vYg=="); + test_encode("fooba",5,"Zm9vYmE="); + test_encode("foobar",6,"Zm9vYmFy"); + + /* a few more */ + test_encode("foobarb",7,"Zm9vYmFyYg=="); + test_encode("foobarba",8,"Zm9vYmFyYmE="); + test_encode("foobarbaz",9,"Zm9vYmFyYmF6"); + + test_encode("foobart",7,"Zm9vYmFydA=="); + + test_encode("abcdefghijklmnopqrstuvwxyz",26,"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="); + test_encode("\x05\x05\x01\x00\x07",5,"BQUBAAc="); + + test_encode("FOO",3,"Rk9P"); + test_encode("Z",1,"Wg=="); + + /* decode testing */ + + test_decode("",0,"",0); + test_decode("Zg==",4,"f",1); + test_decode("Zm8=",4,"fo",2); + test_decode("Zm9v",4,"foo",3); + test_decode("Zm9vYg==",8,"foob",4); + test_decode("Zm9vYmE=",8,"fooba",5); + test_decode("Zm9vYmFy",8,"foobar",6); + test_decode("Zm9vYmFyYg==",12,"foobarb",7); + test_decode("Zm9vYmFyYmE=",12,"foobarba",8); + test_decode("Zm9vYmFyYmF6",12,"foobarbaz",9); + + test_decode("Rk9P",4,"FOO",3); + + test_decode("Wg==",4,"Z",1); + test_decode("AA==",4,"\0",1); + test_decode("AAA=",4,"\0\0",2); + + { + const char *binary = "\x01\x00\x03"; + const size_t binarylen = 3; + + char * decoded; + char * encoded; + size_t encoded_len; + size_t decoded_len; + size_t decoded_space_required; + + size_t encoded_space_required = base64_encoded_length(binarylen); + encoded_space_required++; /* null termination */ + encoded = xmalloc(encoded_space_required); + encoded_len = base64_encode(encoded,encoded_space_required,binary,binarylen); + is_mem(encoded,"AQAD",encoded_len); + + decoded_space_required = base64_decoded_length(encoded_len); + decoded = xmalloc(decoded_space_required); + decoded_len = base64_decode(decoded,decoded_space_required,encoded,encoded_len); + is_size_t(decoded_len,binarylen); + is_mem(binary,decoded,decoded_len); + } + + /* some expected encode failures: */ + { + size_t destlen = 1; + char dest[destlen]; + errno = 0; + is_size_t(base64_encode(dest,destlen,"A",1),(size_t)-1); + is_int(errno,EOVERFLOW); + } + + /* some expected decode failures: */ + { + base64_maps_t maps; + const char * src = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + base64_init_maps(&maps,src); + + is_int(sixbit_from_b64(&maps,'\xfe'),(signed char)-1); + is_int(errno,EDOM); + } + { + size_t destlen = 10; + char dest[destlen]; + errno = 0; + is_size_t(base64_decode(dest,destlen,"A",1),(size_t)-1); + is_int(errno,EINVAL); + } + { + size_t destlen = 1; + char dest[destlen]; + errno = 0; + is_size_t(base64_decode(dest,destlen,"A",1),(size_t)-1); + is_int(errno,EOVERFLOW); + } + { + /* (char)1 is not a valid base64 character: */ + check_bad_range_decode("A\x01",2); + /* (char)255 is not a valid base64 character: (char is signed on most platforms, so this is actually < 0 */ + check_bad_range_decode("\xff""A",2); + check_bad_range_decode("A\xff",2); + check_bad_range_decode("AA\xff",3); + check_bad_range_decode("A\xff""A",3); + check_bad_range_decode("\xff""AA",3); + check_bad_range_decode("AAA\xff",4); + check_bad_range_decode("\xff\x41\x41\x41\x41",5); + check_bad_range_decode("A\xff\x41\x41\x41\x41",6); + check_bad_range_decode("AA\xff\x41\x41\x41\x41",7); + check_bad_range_decode("AAA\xff\x41\x41\x41\x41",8); + } + /* trigger some failures in the sixbit-to-b64 encoder: */ + /* this function now aborts rather than returning -1/setting errno */ + /* { */ + /* is_int(sixbit_to_b64(base64_maps_rfc4648,'\x70'),(char)-1); */ + /* is_int(sixbit_to_b64(base64_maps_rfc4648,'\xff'),(char)-1); */ + /* } */ + /* following tests all of the mapping from b64 chars to 6-bit values: */ + test_decode("//+FwHRSRIsFU2IhAEGD+AMPhOA=",28,"\xff\xff\x85\xc0\x74\x52\x44\x8b\x05\x53\x62\x21\x00\x41\x83\xf8\x03\x0f\x84\xe0",20); + test_encode("\xff\xff\x85\xc0\x74\x52\x44\x8b\x05\x53\x62\x21\x00\x41\x83\xf8\x03\x0f\x84\xe0",20,"//+FwHRSRIsFU2IhAEGD+AMPhOA="); + + + /* check the null-padding stuff */ + { + size_t destlen = 8; + char dest[destlen]; + memset(dest,'\1',sizeof(dest)); + is_size_t(base64_encode(dest,destlen,"A",1),(size_t)4); + is_mem(&dest[4],"\0\0\0\0",4); + } + { + size_t destlen = 3; + char dest[destlen]; + memset(dest,'\1',sizeof(dest)); + is_size_t(base64_decode(dest,destlen,"Wg==",4), 1); + is_mem(&dest[1],"\0",2); + } + + /* test encoding using different alphabets */ + { + char alphabet_fs_safe[64]; + memcpy(alphabet_fs_safe,base64_maps_rfc4648.encode_map,sizeof(alphabet_fs_safe)); + alphabet_fs_safe[62] = '-'; + alphabet_fs_safe[63] = '_'; + test_encode_using_alphabet(alphabet_fs_safe,"\xff\xff\x85\xc0\x74\x52\x44\x8b\x05\x53\x62\x21\x00\x41\x83\xf8\x03\x0f\x84\xe0",20,"__-FwHRSRIsFU2IhAEGD-AMPhOA="); + } + + /* test decoding using different alphabets */ + { + char alphabet_fs_safe[64]; + #define src "__-FwHRSRIsFU2IhAEGD-AMPhOA=" + #define expected "\xff\xff\x85\xc0\x74\x52\x44\x8b\x05\x53\x62\x21\x00\x41\x83\xf8\x03\x0f\x84\xe0" + + memcpy(alphabet_fs_safe,base64_maps_rfc4648.encode_map,sizeof(alphabet_fs_safe)); + alphabet_fs_safe[62] = '-'; + alphabet_fs_safe[63] = '_'; + + test_decode_using_alphabet(alphabet_fs_safe,src,strlen(src),expected,20); + #undef src + #undef expected + } + + /* explicitly test the non-maps encode_triplet and + encode_tail functions */ + { + size_t destlen = 4; + char dest[destlen]; + const char *src = "AB\04"; + memset(dest,'\1',sizeof(dest)); + base64_encode_triplet(dest,src); + is_mem(dest,"QUIE",sizeof(dest)); + } + { + size_t destlen = 4; + char dest[destlen]; + const char *src = "A"; + memset(dest,'\1',sizeof(dest)); + base64_encode_tail(dest,src,strlen(src)); + is_mem(dest,"QQ==",sizeof(dest)); + } + + /* test the alphabet inversion */ + { + base64_maps_t dest; + const char expected_inverse[] = + "\xff\xff\xff\xff\xff" /* 0 */ + "\xff\xff\xff\xff\xff" /* 5 */ + "\xff\xff\xff\xff\xff" /* 10 */ + "\xff\xff\xff\xff\xff" /* 15 */ + "\xff\xff\xff\xff\xff" /* 20 */ + "\xff\xff\xff\xff\xff" /* 25 */ + "\xff\xff\xff\xff\xff" /* 30 */ + "\xff\xff\xff\xff\xff" /* 35 */ + "\xff\xff\xff\x3e\xff" /* 40 */ + "\xff\xff\x3f\x34\x35" /* 45 - */ + "\x36\x37\x38\x39\x3a" /* 50 */ + "\x3b\x3c\x3d\xff\xff" /* 55 */ + "\xff\xff\xff\xff\xff" /* 60 */ + "\x00\x01\x02\x03\x04" /* 65 A */ + "\x05\x06\x07\x08\x09" /* 70 */ + "\x0a\x0b\x0c\x0d\x0e" /* 75 */ + "\x0f\x10\x11\x12\x13" /* 80 */ + "\x14\x15\x16\x17\x18" /* 85 */ + "\x19\xff\xff\xff\xff" /* 90 */ + "\xff\xff\x1a\x1b\x1c" /* 95 _ */ + "\x1d\x1e\x1f\x20\x21" /* 100 */ + "\x22\x23\x24\x25\x26" /* 105 */ + "\x27\x28\x29\x2a\x2b" /* 110 */ + "\x2c\x2d\x2e\x2f\x30" /* 115 */ + "\x31\x32\x33\xff\xff" /* 120 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */ + ; + const char * src = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + base64_init_maps(&dest, src); + is_mem((const char *)dest.decode_map, expected_inverse, 256); + ok1(base64_char_in_alphabet(&dest,'A')); + ok1(!base64_char_in_alphabet(&dest,'\n')); + } + + /* explicitly test the non-alpha decode_tail and decode_quartet */ + { + char dest[4]; + const char *src = "QQ=="; + const char * expected = "A"; + memset(dest, '%', sizeof(dest)); + base64_decode_tail(dest,src,4); + is_mem(dest, expected, 1); + } + { + char dest[4]; + const char *src = "Zm9v"; + const char * expected = "foo"; + memset(dest, '%', sizeof(dest)); + base64_decode_quartet(dest,src); + is_mem(dest, expected, 1); + } + + exit(exit_status()); +} + +static void * xmalloc(size_t size) +{ + char * ret; + ret = malloc(size); + if (ret == NULL) { + perror("malloc"); + abort(); + } + return ret; +} + +/* End of run.c test */ -- 2.39.2