damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

pbase64.h (15876B)


      1 #ifndef PBASE64_H
      2 #define PBASE64_H
      3 
      4 #ifdef __cplusplus
      5 extern "C" {
      6 #endif
      7 
      8 #include <stdlib.h>
      9 
     10 /* Guarded to allow inclusion of pstdint.h first, if stdint.h is not supported. */
     11 #ifndef UINT8_MAX
     12 #include <stdint.h>
     13 #endif
     14 
     15 #define BASE64_EOK    0
     16 /* 0 or mure full blocks decoded, remaining content may be parsed with fresh buffer. */
     17 #define BASE64_EMORE  1
     18 /* The `src_len` argument is required when encoding. */
     19 #define BASE64_EARGS  2
     20 /* Unsupported mode, or modifier not supported by mode when encoding. */
     21 #define BASE64_EMODE  3
     22 /* Decoding ends at invalid tail length - either by source length or by non-alphabet symbol. */
     23 #define BASE64_ETAIL  4
     24 /* Decoding ends at valid tail length but last byte has non-zero bits where it shouldn't have. */
     25 #define BASE64_EDIRTY 5
     26 
     27 static inline const char *base64_strerror(int err);
     28 
     29 /* All codecs are URL safe. Only Crockford allow for non-canocical decoding. */
     30 enum {
     31     /* Most common base64 codec, but not url friendly. */
     32     base64_mode_rfc4648 = 0,
     33 
     34     /*  URL safe version, '+' -> '-', '/' -> '_'. */
     35     base64_mode_url = 1,
     36 
     37     /*
     38      * Skip ' ', '\r', and '\n' - we do not allow tab because common
     39      * uses of base64 such as PEM do not allow tab.
     40      */
     41     base64_dec_modifier_skipspace = 32,
     42 
     43     /* Padding is excluded by default. Not allowed for zbase64. */
     44     base64_enc_modifier_padding = 128,
     45 
     46     /* For internal use or to decide codec of mode. */
     47     base64_modifier_mask = 32 + 64 + 128,
     48 };
     49 
     50 /* Encoded size with or without padding. */
     51 static inline size_t base64_encoded_size(size_t len, int mode);
     52 
     53 /*
     54  * Decoded size assuming no padding.
     55  * If `len` does include padding, the actual size may be less
     56  * when decoding, but never more.
     57  */
     58 static inline size_t base64_decoded_size(size_t len);
     59 
     60 /*
     61  * `dst` must hold ceil(len * 4 / 3) bytes.
     62  * `src_len` points to length of source and is updated with length of
     63  * parse on both success and failure. If `dst_len` is not null
     64  * it is used to store resulting output lengt withh length of decoded
     65  * output on both success and failure.
     66  * If `hyphen` is non-zero a hyphen is encoded every `hyphen` output bytes.
     67  * `mode` selects encoding alphabet defaulting to Crockfords base64.
     68  * Returns 0 on success.
     69  *
     70  * A terminal space can be added with `dst[dst_len++] = ' '` after the
     71  * encode call. All non-alphabet can be used as terminators except the
     72  * padding character '='. The following characters will work as
     73  * terminator for all modes: { '\0', '\n', ' ', '\t' }. A terminator is
     74  * optional when the source length is given to the decoder. Note that
     75  * crockford also reserves a few extra characters for checksum but the
     76  * checksum must be separate from the main buffer and is not supported
     77  * by this library.
     78  */
     79 static inline int base64_encode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode);
     80 
     81 /*
     82  * Decodes according to mode while ignoring encoding modifiers.
     83  * `src_len` and `dst_len` are optional pointers. If `src_len` is set it
     84  * must contain the length of the input, otherwise the input must be
     85  * terminated with a non-alphabet character or valid padding (a single
     86  * padding character is accepted) - if the src_len output is needed but
     87  * not the input due to guaranteed termination, then set it to
     88  * (size_t)-1. `dst_len` must contain length of output buffer if present
     89  * and parse will fail with BASE64_EMORE after decoding a block multiple
     90  * if dst_len is exhausted - the parse can thus be resumed after
     91  * draining destination. `src_len` and `dst_len` are updated with parsed
     92  * and decoded length, when present, on both success and failure.
     93  * Returns 0 on success. Invalid characters are not considered errors -
     94  * they simply terminate the parse, however, if the termination is not
     95  * at a block multiple or a valid partial block length then BASE64_ETAIL
     96  * without output holding the last full block, if any. BASE64_ETAIL is also
     97  * returned if the a valid length holds non-zero unused tail bits.
     98  */
     99 static inline int base64_decode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode);
    100 
    101 static inline const char *base64_strerror(int err)
    102 {
    103     switch (err) {
    104     case BASE64_EOK: return "ok";
    105     case BASE64_EARGS: return "invalid argument";
    106     case BASE64_EMODE: return "invalid mode";
    107     case BASE64_EMORE: return "destination full";
    108     case BASE64_ETAIL: return "invalid tail length";
    109     case BASE64_EDIRTY: return "invalid tail content";
    110     default: return "unknown error";
    111     }
    112 }
    113 
    114 static inline size_t base64_encoded_size(size_t len, int mode)
    115 {
    116     size_t k = len % 3;
    117     size_t n = (len * 4 / 3 + 3) & ~(size_t)3;
    118     int pad = mode & base64_enc_modifier_padding;
    119 
    120     if (!pad) {
    121         switch (k) {
    122         case 2:
    123             n -= 1;
    124             break;
    125         case 1:
    126             n -= 2;
    127             break;
    128         default:
    129             break;
    130         }
    131     }
    132     return n;
    133 }
    134 
    135 static inline size_t base64_decoded_size(size_t len)
    136 {
    137     size_t k = len % 4;
    138     size_t n = len / 4 * 3;
    139 
    140     switch (k) {
    141     case 3:
    142         return n + 2;
    143     case 2:
    144         return n + 1;
    145     case 1: /* Not valid without padding. */
    146     case 0:
    147     default:
    148         return n;
    149     }
    150 }
    151 
    152 static inline int base64_encode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode)
    153 {
    154     const uint8_t *rfc4648_alphabet            = (const uint8_t *)
    155         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    156     const uint8_t *url_alphabet                = (const uint8_t *)
    157         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    158 
    159     const uint8_t *T;
    160     uint8_t *dst_base = dst;
    161     int pad = mode & base64_enc_modifier_padding;
    162     size_t len = 0;
    163     int ret = BASE64_EMODE;
    164 
    165     if (!src_len) {
    166         ret = BASE64_EARGS;
    167         goto done;
    168     }
    169     len = *src_len;
    170     mode = mode & ~base64_modifier_mask;
    171     switch (mode) {
    172     case base64_mode_rfc4648:
    173         T = rfc4648_alphabet;
    174         break;
    175     case base64_mode_url:
    176         T = url_alphabet;
    177         break;
    178     default:
    179         /* Invalid mode. */
    180         goto done;
    181     }
    182 
    183     ret = BASE64_EOK;
    184 
    185     /* Encodes 4 destination bytes from 3 source bytes. */
    186     while (len >= 3) {
    187         dst[0] = T[((src[0] >> 2))];
    188         dst[1] = T[((src[0] << 4) & 0x30) | (src[1] >> 4)];
    189         dst[2] = T[((src[1] << 2) & 0x3c) | (src[2] >> 6)];
    190         dst[3] = T[((src[2] & 0x3f))];
    191         len -= 3;
    192         dst += 4;
    193         src += 3;
    194     }
    195     /* Encodes 8 destination bytes from 1 to 4 source bytes, if any. */
    196     switch(len) {
    197     case 2:
    198         dst[0] = T[((src[0] >> 2))];
    199         dst[1] = T[((src[0] << 4) & 0x30) | (src[1] >> 4)];
    200         dst[2] = T[((src[1] << 2) & 0x3c)];
    201         dst += 3;
    202         if (pad) {
    203             *dst++ = '=';
    204         }
    205         break;
    206     case 1:
    207         dst[0] = T[((src[0] >> 2))];
    208         dst[1] = T[((src[0] << 4) & 0x30)];
    209         dst += 2;
    210         if (pad) {
    211             *dst++ = '=';
    212             *dst++ = '=';
    213         }
    214         break;
    215     default:
    216         pad = 0;
    217         break;
    218     }
    219     len = 0;
    220 done:
    221     if (dst_len) {
    222         *dst_len = (size_t)(dst - dst_base);
    223     }
    224     if (src_len) {
    225         *src_len -= len;
    226     }
    227     return ret;
    228 }
    229 
    230 static inline int base64_decode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode)
    231 {
    232     static const uint8_t cinvalid = 64;
    233     static const uint8_t cignore = 65;
    234     static const uint8_t cpadding = 66;
    235 
    236     /*
    237      * 0..63: 6-bit encoded value.
    238      * 64: flags non-alphabet symbols.
    239      * 65: codes for ignored symbols.
    240      * 66: codes for pad symbol '='.
    241      * All codecs consider padding an optional terminator and if present
    242      * consumes as many pad bytes as possible up to block termination,
    243      * but does not fail if a block is not full.
    244      *
    245      * We do not currently have any ignored characters but we might
    246      * add spaces as per MIME spec, but  assuming spaces only happen
    247      * at block boundaries this is probalby better handled by repeated
    248      * parsing.
    249      */
    250     static const uint8_t base64rfc4648_decode[256] = {
    251         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    252         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    253         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    254         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64,
    255         64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    256         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    257         64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    258         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    259         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    260         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    261         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    262         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    263         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    264         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    265         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    266         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
    267     };
    268 
    269     static const uint8_t base64url_decode[256] = {
    270         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    271         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    272         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64,
    273         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64,
    274         64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    275         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
    276         64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    277         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    278         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    279         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    280         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    281         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    282         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    283         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    284         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    285         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
    286     };
    287 
    288     static const uint8_t base64rfc4648_decode_skipspace[256] = {
    289         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 64, 64, 65, 64, 64,
    290         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    291         65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    292         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64,
    293         64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    294         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    295         64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    296         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    297         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    298         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    299         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    300         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    301         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    302         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    303         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    304         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
    305     };
    306 
    307     static const uint8_t base64url_decode_skipspace[256] = {
    308         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 64, 64, 65, 64, 64,
    309         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    310         65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64,
    311         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64,
    312         64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    313         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
    314         64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    315         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
    316         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    317         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    318         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    319         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    320         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    321         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    322         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    323         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
    324     };
    325 
    326     int ret = BASE64_EOK;
    327     size_t i, k;
    328     uint8_t hold[4];
    329     uint8_t *dst_base = dst;
    330     size_t limit = (size_t)-1;
    331     size_t len = (size_t)-1, mark;
    332     const uint8_t *T = base64rfc4648_decode;
    333     int skipspace = mode & base64_dec_modifier_skipspace;
    334 
    335     if (src_len) {
    336         len = *src_len;
    337     }
    338     mark = len;
    339     mode = mode & ~base64_modifier_mask;
    340     switch (mode) {
    341     case base64_mode_rfc4648:
    342         T = skipspace ? base64rfc4648_decode_skipspace : base64rfc4648_decode;
    343         break;
    344     case base64_mode_url:
    345         T = skipspace ?  base64url_decode_skipspace : base64url_decode;
    346         break;
    347     default:
    348         ret = BASE64_EMODE;
    349         goto done;
    350     }
    351 
    352     if (dst_len && *dst_len > 0) {
    353         limit = *dst_len;
    354     }
    355     while(limit > 0) {
    356         for (i = 0; i < 4; ++i) {
    357             if (len == i) {
    358                 k = i;
    359                 len -= i;
    360                 goto tail;
    361             }
    362             if ((hold[i] = T[src[i]]) >= cinvalid) {
    363                 if (hold[i] == cignore) {
    364                     ++src;
    365                     --len;
    366                     --i;
    367                     continue;
    368                 }
    369                 k = i;
    370                 /* Strip padding and ignore hyphen in padding, if present. */
    371                 if (hold[i] == cpadding) {
    372                     ++i;
    373                     while (i < len && i < 8) {
    374                         if (T[src[i]] != cpadding && T[src[i]] != cignore) {
    375                             break;
    376                         }
    377                         ++i;
    378                     }
    379                 }
    380                 len -= i;
    381                 goto tail;
    382             }
    383         }
    384         if (limit < 3) {
    385             goto more;
    386         }
    387         dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4));
    388         dst[1] = (uint8_t)((hold[1] << 4) | (hold[2] >> 2));
    389         dst[2] = (uint8_t)((hold[2] << 6) | (hold[3]));
    390         dst += 3;
    391         src += 4;
    392         limit -= 3;
    393         len -= 4;
    394         mark = len;
    395     }
    396 done:
    397     if (dst_len) {
    398         *dst_len = (size_t)(dst - dst_base);
    399     }
    400     if (src_len) {
    401         *src_len -= mark;
    402     }
    403     return ret;
    404 
    405 tail:
    406     switch (k) {
    407     case 0:
    408         break;
    409     case 2:
    410         if ((hold[1] << 4) & 0xff) {
    411             goto dirty;
    412         }
    413         if (limit < 1) {
    414             goto more;
    415         }
    416         dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4));
    417         dst += 1;
    418         break;
    419     case 3:
    420         if ((hold[2] << 6) & 0xff) {
    421             goto dirty;
    422         }
    423         if (limit < 2) {
    424             goto more;
    425         }
    426         dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4));
    427         dst[1] = (uint8_t)((hold[1] << 4) | (hold[2] >> 2));
    428         dst += 2;
    429         break;
    430     default:
    431         ret = BASE64_ETAIL;
    432         goto done;
    433     }
    434     mark = len;
    435     goto done;
    436 dirty:
    437     ret = BASE64_EDIRTY;
    438     goto done;
    439 more:
    440     ret = BASE64_EMORE;
    441     goto done;
    442 }
    443 
    444 #ifdef __cplusplus
    445 }
    446 #endif
    447 
    448 #endif /* PBASE64_H */