Commit 78158c87 authored by Peter Barker's avatar Peter Barker Committed by Rusty Russell

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: <pb-ccan@barker.dropbear.id.au>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 94e7bbe8
......@@ -35,6 +35,7 @@ MODS_WITH_SRC := antithread \
asprintf \
autodata \
avl \
base64 \
bdelta \
block_pool \
breakpoint \
......
../../licenses/BSD-MIT
\ No newline at end of file
#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 <stdio.h>
* #include <string.h>
* #include <ccan/base64/base64.h>
*
* 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<argc;i++) {
* size_t unencoded_length = strlen(argv[i]);
* size_t encoded_length = base64_encoded_length(unencoded_length);
* base64_encoded_string = malloc(encoded_length);
* base64_encode(base64_encoded_string, encoded_length,
* argv[i], unencoded_length);
* printf("%s\n", base64_encoded_string);
* free(base64_encoded_string);
* }
*
* return 0;
* }
*
* License: BSD-MIT
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
return 1;
}
/* Licensed under BSD-MIT - see LICENSE file for details */
#include "base64.h"
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
/**
* 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 */
};
/* Licensed under BSD-MIT - see LICENSE file for details */
#ifndef CCAN_BASE64_H
#define CCAN_BASE64_H
#include <stddef.h>
#include <stdbool.h>
#include <sys/types.h>
/**
* 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 */
#ifndef _BASE64_MORETAP_H
#define _BASE64_MORETAP_H
#include <ccan/str/str.h>
/**
* 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<len; offset++) {
if (got[offset] != expected[offset]) {
_gen_result(0, func, file, line,"%s eq %s",got_string,expected_string);
/* diag("Expected: %s",e2); */
/* diag(" Got: %s",e1); */
diag("Buffers differ at offset %zd (got=0x%02x expected=0x%02x)",
offset,got[offset],expected[offset]);
return;
}
}
_gen_result(1, __func__, __FILE__, __LINE__,"%s eq %s",
expected_string,got_string);
}
# define is_mem(got,expected,len) \
_is_mem(got,expected,len,#got,#expected,#len,__func__, __FILE__, __LINE__)
/**
* is_size_t - OK if arguments are equal when cast to size_t
* @e1: expression for the number
* @e2: expression for the expected number
*
* If the numbers are equal, the test passes.
*
* Example:
* is_size_t(give_me_17(),17);
*/
# define is_size_t(e1,e2 ...) \
((size_t)(e1)==((size_t)e2) ? \
_gen_result(1, __func__, __FILE__, __LINE__,"%s == %s",#e1,#e2) : \
(_gen_result(0, __func__, __FILE__, __LINE__, \
"%s == %s",#e1,#e2)) || (diag("Expected: %zd",(size_t)e2),diag(" Got: %zd",(size_t)e1),0)) /* diag is void; note commas. */
#endif
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment