nostril

A C cli tool for creating nostr events
git clone git://jb55.com/nostril
Log | Files | Refs | Submodules | README | LICENSE

base64.c (6567B)


      1 /* Licensed under BSD-MIT - see LICENSE file for details */
      2 #include "base64.h"
      3 
      4 #include <errno.h>
      5 #include <string.h>
      6 #include <assert.h>
      7 #include <stdint.h>
      8 
      9 /**
     10  * sixbit_to_b64 - maps a 6-bit value to the base64 alphabet
     11  * @param map A base 64 map (see base64_init_map)
     12  * @param sixbit Six-bit value to map
     13  * @return a base 64 character
     14  */
     15 static char sixbit_to_b64(const base64_maps_t *maps, const uint8_t sixbit)
     16 {
     17 	assert(sixbit <= 63);
     18 
     19 	return maps->encode_map[(unsigned char)sixbit];
     20 }
     21 
     22 /**
     23  * sixbit_from_b64 - maps a base64-alphabet character to its 6-bit value
     24  * @param maps A base 64 maps structure (see base64_init_maps)
     25  * @param sixbit Six-bit value to map
     26  * @return a six-bit value
     27  */
     28 static int8_t sixbit_from_b64(const base64_maps_t *maps,
     29 			      const unsigned char b64letter)
     30 {
     31 	int8_t ret;
     32 
     33 	ret = maps->decode_map[(unsigned char)b64letter];
     34 	if (ret == (char)0xff) {
     35 		errno = EDOM;
     36 		return -1;
     37 	}
     38 
     39 	return ret;
     40 }
     41 
     42 bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
     43 {
     44 	return (maps->decode_map[(const unsigned char)b64char] != (char)0xff);
     45 }
     46 
     47 void base64_init_maps(base64_maps_t *dest, const char src[64])
     48 {
     49 	unsigned char i;
     50 
     51 	memcpy(dest->encode_map,src,64);
     52 	memset(dest->decode_map,0xff,256);
     53 	for (i=0; i<64; i++) {
     54 		dest->decode_map[(unsigned char)src[i]] = i;
     55 	}
     56 }
     57 
     58 size_t base64_encoded_length(size_t srclen)
     59 {
     60 	return ((srclen + 2) / 3) * 4;
     61 }
     62 
     63 void base64_encode_triplet_using_maps(const base64_maps_t *maps,
     64 				      char dest[4], const char src[3])
     65 {
     66 	char a = src[0];
     67 	char b = src[1];
     68 	char c = src[2];
     69 
     70 	dest[0] = sixbit_to_b64(maps, (a & 0xfc) >> 2);
     71 	dest[1] = sixbit_to_b64(maps, ((a & 0x3) << 4) | ((b & 0xf0) >> 4));
     72 	dest[2] = sixbit_to_b64(maps, ((c & 0xc0) >> 6) | ((b & 0xf) << 2));
     73 	dest[3] = sixbit_to_b64(maps, c & 0x3f);
     74 }
     75 
     76 void base64_encode_tail_using_maps(const base64_maps_t *maps, char dest[4],
     77 				   const char *src, const size_t srclen)
     78 {
     79 	char longsrc[3] = { 0 };
     80 
     81 	assert(srclen <= 3);
     82 
     83 	memcpy(longsrc, src, srclen);
     84 	base64_encode_triplet_using_maps(maps, dest, longsrc);
     85 	memset(dest+1+srclen, '=', 3-srclen);
     86 }
     87 
     88 ssize_t base64_encode_using_maps(const base64_maps_t *maps,
     89 				 char *dest, const size_t destlen,
     90 				 const char *src, const size_t srclen)
     91 {
     92 	size_t src_offset = 0;
     93 	size_t dest_offset = 0;
     94 
     95 	if (destlen < base64_encoded_length(srclen)) {
     96 		errno = EOVERFLOW;
     97 		return -1;
     98 	}
     99 
    100 	while (srclen - src_offset >= 3) {
    101 		base64_encode_triplet_using_maps(maps, &dest[dest_offset], &src[src_offset]);
    102 		src_offset += 3;
    103 		dest_offset += 4;
    104 	}
    105 
    106 	if (src_offset < srclen) {
    107 		base64_encode_tail_using_maps(maps, &dest[dest_offset], &src[src_offset], srclen-src_offset);
    108 		dest_offset += 4;
    109 	}
    110 
    111 	memset(&dest[dest_offset], '\0', destlen-dest_offset);
    112 
    113 	return dest_offset;
    114 }
    115 
    116 size_t base64_decoded_length(size_t srclen)
    117 {
    118 	return ((srclen+3)/4*3);
    119 }
    120 
    121 ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3],
    122 				     const char src[4])
    123 {
    124 	signed char a;
    125 	signed char b;
    126 	signed char c;
    127 	signed char d;
    128 
    129 	a = sixbit_from_b64(maps, src[0]);
    130 	b = sixbit_from_b64(maps, src[1]);
    131 	c = sixbit_from_b64(maps, src[2]);
    132 	d = sixbit_from_b64(maps, src[3]);
    133 
    134 	if ((a == -1) || (b == -1) || (c == -1) || (d == -1)) {
    135 		return -1;
    136 	}
    137 
    138 	dest[0] = (a << 2) | (b >> 4);
    139 	dest[1] = ((b & 0xf) << 4) | (c >> 2);
    140 	dest[2] = ((c & 0x3) << 6) | d;
    141 
    142 	return 0;
    143 }
    144 
    145 
    146 ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
    147 				  const char * src, const size_t srclen)
    148 {
    149 	char longsrc[4];
    150 	int quartet_result;
    151 	size_t insize = srclen;
    152 
    153 	while (insize != 0 &&
    154 	       src[insize-1] == '=') { /* throw away padding symbols */
    155 		insize--;
    156 	}
    157 	if (insize == 0) {
    158 		return 0;
    159 	}
    160 	if (insize == 1) {
    161 		/* the input is malformed.... */
    162 		errno = EINVAL;
    163 		return -1;
    164 	}
    165 	memcpy(longsrc, src, insize);
    166 	memset(longsrc+insize, 'A', 4-insize);
    167 	quartet_result = base64_decode_quartet_using_maps(maps, dest, longsrc);
    168 	if (quartet_result == -1) {
    169 		return -1;
    170 	}
    171 
    172 	return insize - 1;
    173 }
    174 
    175 ssize_t base64_decode_using_maps(const base64_maps_t *maps,
    176 				 char *dest, const size_t destlen,
    177 				 const char *src, const size_t srclen)
    178 {
    179 	ssize_t dest_offset = 0;
    180 	ssize_t i;
    181 	ssize_t more;
    182 
    183 	if (destlen < base64_decoded_length(srclen)) {
    184 		errno = EOVERFLOW;
    185 		return -1;
    186 	}
    187 
    188 	for(i=0; srclen - i > 4; i+=4) {
    189 		if (base64_decode_quartet_using_maps(maps, &dest[dest_offset], &src[i]) == -1) {
    190 			return -1;
    191 		}
    192 		dest_offset += 3;
    193 	}
    194 
    195 	more = base64_decode_tail_using_maps(maps, &dest[dest_offset], &src[i], srclen - i);
    196 	if (more == -1) {
    197 		return -1;
    198 	}
    199 	dest_offset += more;
    200 
    201 	memset(&dest[dest_offset], '\0', destlen-dest_offset);
    202 
    203 	return dest_offset;
    204 }
    205 
    206 
    207 
    208 
    209 /**
    210  * base64_maps_rfc4648 - pregenerated maps struct for rfc4648
    211  */
    212 const base64_maps_t base64_maps_rfc4648 = {
    213   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    214 
    215   "\xff\xff\xff\xff\xff" /* 0 */					\
    216   "\xff\xff\xff\xff\xff" /* 5 */					\
    217   "\xff\xff\xff\xff\xff" /* 10 */					\
    218   "\xff\xff\xff\xff\xff" /* 15 */					\
    219   "\xff\xff\xff\xff\xff" /* 20 */					\
    220   "\xff\xff\xff\xff\xff" /* 25 */					\
    221   "\xff\xff\xff\xff\xff" /* 30 */					\
    222   "\xff\xff\xff\xff\xff" /* 35 */					\
    223   "\xff\xff\xff\x3e\xff" /* 40 */					\
    224   "\xff\xff\x3f\x34\x35" /* 45 */					\
    225   "\x36\x37\x38\x39\x3a" /* 50 */					\
    226   "\x3b\x3c\x3d\xff\xff" /* 55 */					\
    227   "\xff\xff\xff\xff\xff" /* 60 */					\
    228   "\x00\x01\x02\x03\x04" /* 65 A */					\
    229   "\x05\x06\x07\x08\x09" /* 70 */					\
    230   "\x0a\x0b\x0c\x0d\x0e" /* 75 */					\
    231   "\x0f\x10\x11\x12\x13" /* 80 */					\
    232   "\x14\x15\x16\x17\x18" /* 85 */					\
    233   "\x19\xff\xff\xff\xff" /* 90 */					\
    234   "\xff\xff\x1a\x1b\x1c" /* 95 */					\
    235   "\x1d\x1e\x1f\x20\x21" /* 100 */					\
    236   "\x22\x23\x24\x25\x26" /* 105 */					\
    237   "\x27\x28\x29\x2a\x2b" /* 110 */					\
    238   "\x2c\x2d\x2e\x2f\x30" /* 115 */					\
    239   "\x31\x32\x33\xff\xff" /* 120 */					\
    240   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */			\
    241   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    242   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    243   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */			\
    244   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    245   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    246   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */			\
    247   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    248   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    249   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */			\
    250   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    251   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"				\
    252   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */
    253 };
    254