/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "base64.h" #include "buffer.h" /* * Low-level Base64 encoder */ uoff_t base64_get_full_encoded_size(struct base64_encoder *enc, uoff_t src_size) { bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); bool no_padding = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING); uoff_t out_size; uoff_t newlines; if (src_size == 0) return 0; /* calculate size of encoded data */ out_size = MAX_BASE64_ENCODED_SIZE(src_size); if (no_padding) { switch (src_size % 3) { case 0: break; case 1: i_assert(out_size > 2); out_size -= 2; break; case 2: i_assert(out_size > 1); out_size -= 1; break; } } if (out_size > enc->max_line_len) { /* newline between each full line */ i_assert(enc->max_line_len > 0); newlines = (out_size / enc->max_line_len) - 1; /* an extra newline to separate the partial last line from the previous full line */ if ((out_size % enc->max_line_len) != 0) newlines++; /* update size with added newlines */ out_size += newlines * (crlf ? 2 : 1); } return out_size; } static size_t base64_encode_get_out_size(struct base64_encoder *enc, size_t src_size) { size_t res_size = enc->w_buf_len; i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); if (src_size == 0) return res_size; /* Handle sub-position */ switch (enc->sub_pos) { case 0: break; case 1: res_size++; src_size--; if (src_size == 0) return res_size; /* fall through */ case 2: res_size += 2; src_size--; break; default: i_unreached(); } /* We're now at a 3-byte boundary */ if (src_size == 0) return res_size; /* Calculate size we can append to the output from remaining input */ res_size += ((src_size) / 3) * 4; switch (src_size % 3) { case 0: break; case 1: res_size += 1; break; case 2: res_size += 2; break; } return res_size; } size_t base64_encode_get_size(struct base64_encoder *enc, size_t src_size) { bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); size_t out_size = base64_encode_get_out_size(enc, src_size); if (src_size == 0) { /* last block */ switch (enc->sub_pos) { case 0: break; case 1: out_size += 3; break; case 2: out_size += 2; break; default: i_unreached(); } } if (enc->max_line_len < SIZE_MAX) { size_t line_part, lines; /* Calculate how many line endings must be added */ i_assert(enc->max_line_len > 0); lines = out_size / enc->max_line_len; line_part = out_size % enc->max_line_len; if (enc->cur_line_len > (enc->max_line_len - line_part)) lines++; out_size += lines * (crlf ? 2 : 1); } if (enc->pending_lf) out_size++; return out_size; } size_t base64_encode_get_full_space(struct base64_encoder *enc, size_t dst_space) { bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); bool no_padding = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING); size_t src_space = 0; i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); if (enc->max_line_len < SIZE_MAX) { size_t max_line_space, lines, nl_space; /* Calculate how many line endings must be added if all space were used. */ i_assert(enc->max_line_len < SIZE_MAX-2); max_line_space = enc->max_line_len + (crlf ? 2 : 1); lines = dst_space / max_line_space; /* Calculate how much space is used by newline characters and subtract this from the available space. */ nl_space = lines * (crlf ? 2 : 1); if (dst_space <= nl_space) return 0; dst_space -= nl_space; } if (dst_space <= enc->w_buf_len) return 0; dst_space -= enc->w_buf_len; if (enc->pending_lf) dst_space--; if (dst_space == 0) return 0; /* Handle sub-position */ switch (enc->sub_pos) { case 0: break; case 1: dst_space--; src_space++; /* fall through */ case 2: if (dst_space < 2) return src_space; dst_space -= 2; src_space++; break; default: i_unreached(); } if (dst_space == 0) return src_space; src_space += dst_space / 4 * 3; if (no_padding) { switch (dst_space % 4) { case 0: case 1: break; case 2: src_space += 1; break; case 3: src_space += 2; break; } } return src_space; } static void base64_encode_more_data(struct base64_encoder *enc, const unsigned char *src_c, size_t src_size, size_t *src_pos_r, size_t dst_avail, buffer_t *dest) { const struct base64_scheme *b64 = enc->b64; const char *b64enc = b64->encmap; size_t res_size; unsigned char *start, *ptr, *end; size_t src_pos; i_assert(!enc->pending_lf); /* determine how much we can write in destination buffer */ if (dst_avail == 0) { *src_pos_r = 0; return; } /* pre-allocate space in the destination buffer */ res_size = base64_encode_get_out_size(enc, src_size); if (res_size > dst_avail) res_size = dst_avail; start = buffer_append_space_unsafe(dest, res_size); end = start + res_size; ptr = start; /* write bytes not written in previous call */ i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); if (enc->w_buf_len > res_size) { memcpy(ptr, enc->w_buf, res_size); ptr += res_size; enc->w_buf_len -= res_size; memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len); } else if (enc->w_buf_len > 0) { memcpy(ptr, enc->w_buf, enc->w_buf_len); ptr += enc->w_buf_len; enc->w_buf_len = 0; } if (ptr == end) { *src_pos_r = 0; return; } i_assert(enc->w_buf_len == 0); i_assert(src_size != 0); /* Handle sub-position */ src_pos = 0; switch (enc->sub_pos) { case 0: break; case 1: i_assert(ptr < end); ptr[0] = b64enc[enc->buf | (src_c[src_pos] >> 4)]; ptr++; enc->buf = (src_c[src_pos] & 0x0f) << 2; src_pos++; if (src_pos == src_size || ptr == end) { enc->sub_pos = 2; *src_pos_r = src_pos; return; } /* fall through */ case 2: ptr[0] = b64enc[enc->buf | ((src_c[src_pos] & 0xc0) >> 6)]; enc->w_buf[0] = b64enc[src_c[src_pos] & 0x3f]; ptr++; src_pos++; if (ptr < end) { ptr[0] = enc->w_buf[0]; ptr++; enc->w_buf_len = 0; } else { enc->sub_pos = 0; enc->w_buf_len = 1; *src_pos_r = src_pos; return; } break; default: i_unreached(); } enc->sub_pos = 0; /* We're now at a 3-byte boundary */ if (src_pos == src_size) { i_assert(ptr == end); *src_pos_r = src_pos; return; } /* Convert the bulk */ for (; src_size - src_pos > 2 && &ptr[3] < end; src_pos += 3, ptr += 4) { ptr[0] = b64enc[src_c[src_pos] >> 2]; ptr[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | (src_c[src_pos+1] >> 4)]; ptr[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) | ((src_c[src_pos+2] & 0xc0) >> 6)]; ptr[3] = b64enc[src_c[src_pos+2] & 0x3f]; } /* Convert the bytes beyond the last 3-byte boundary and update state for next call */ switch (src_size - src_pos) { case 0: enc->sub_pos = 0; enc->buf = 0; break; case 1: enc->sub_pos = 1; enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; enc->w_buf_len = 1; enc->buf = (src_c[src_pos] & 0x03) << 4; src_pos++; break; case 2: enc->sub_pos = 2; enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | (src_c[src_pos+1] >> 4)]; enc->w_buf_len = 2; enc->buf = (src_c[src_pos+1] & 0x0f) << 2; src_pos += 2; break; default: /* hit the end of the destination buffer */ enc->sub_pos = 0; enc->w_buf[0] = b64enc[src_c[src_pos] >> 2]; enc->w_buf[1] = b64enc[((src_c[src_pos] & 0x03) << 4) | (src_c[src_pos+1] >> 4)]; enc->w_buf[2] = b64enc[((src_c[src_pos+1] & 0x0f) << 2) | ((src_c[src_pos+2] & 0xc0) >> 6)]; enc->w_buf[3] = b64enc[src_c[src_pos+2] & 0x3f]; enc->w_buf_len = 4; enc->buf = 0; src_pos += 3; } /* fill the remaining allocated space */ i_assert(ptr <= end); res_size = end - ptr; i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); if (enc->w_buf_len > res_size) { memcpy(ptr, enc->w_buf, res_size); ptr += res_size; enc->w_buf_len -= res_size; memmove(enc->w_buf, enc->w_buf + res_size, enc->w_buf_len); } else if (enc->w_buf_len > 0) { memcpy(ptr, enc->w_buf, enc->w_buf_len); ptr += enc->w_buf_len; enc->w_buf_len = 0; } i_assert(ptr == end); *src_pos_r = src_pos; } bool base64_encode_more(struct base64_encoder *enc, const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); const unsigned char *src_c, *src_p; size_t src_pos, src_left; i_assert(!enc->finishing); i_assert(!enc->finished); src_p = src_c = src; src_left = src_size; while (src_left > 0) { size_t dst_avail, dst_pos, line_avail, written; /* determine how much we can write in destination buffer */ dst_avail = buffer_get_avail_size(dest); if (dst_avail == 0) break; /* Emit pending newline immediately */ if (enc->pending_lf) { i_assert(crlf); buffer_append_c(dest, '\n'); enc->pending_lf = FALSE; dst_avail--; if (dst_avail == 0) break; } i_assert(enc->max_line_len > 0); i_assert(enc->cur_line_len <= enc->max_line_len); line_avail = I_MIN(enc->max_line_len - enc->cur_line_len, dst_avail); if (line_avail > 0) { dst_pos = dest->used; base64_encode_more_data(enc, src_p, src_left, &src_pos, line_avail, dest); i_assert(src_pos <= src_left); src_p += src_pos; src_left -= src_pos; i_assert(dest->used >= dst_pos); written = dest->used - dst_pos; i_assert(written <= line_avail); i_assert(written <= enc->max_line_len); i_assert(enc->cur_line_len <= (enc->max_line_len - written)); enc->cur_line_len += written; dst_avail -= written; } if (dst_avail == 0) break; if (src_left > 0 && enc->cur_line_len == enc->max_line_len) { if (crlf) { if (dst_avail >= 2) { /* emit the full CRLF sequence */ buffer_append(dest, "\r\n", 2); } else { /* emit CR */ buffer_append_c(dest, '\r'); /* remember the LF */ enc->pending_lf = TRUE; } } else { buffer_append_c(dest, '\n'); } enc->cur_line_len = 0; } } i_assert(src_p >= src_c); src_pos = (src_p - src_c); if (src_pos_r != NULL) *src_pos_r = src_pos; return (src_pos == src_size); } bool base64_encode_finish(struct base64_encoder *enc, buffer_t *dest) { const struct base64_scheme *b64 = enc->b64; const char *b64enc = b64->encmap; bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); bool padding = HAS_NO_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING); unsigned char *ptr, *end; size_t dst_avail, line_avail, write_full, write; unsigned int w_buf_pos = 0; i_assert(!enc->finished); enc->finishing = TRUE; dst_avail = 0; if (dest != NULL) dst_avail = buffer_get_avail_size(dest); if (enc->w_buf_len > 0 || enc->pending_lf) { if (dst_avail == 0) return FALSE; i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); } i_assert(enc->max_line_len > 0); i_assert(enc->cur_line_len <= enc->max_line_len); line_avail = enc->max_line_len - enc->cur_line_len; switch (enc->sub_pos) { case 0: break; case 1: i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 3)); enc->w_buf[enc->w_buf_len] = b64enc[enc->buf]; enc->w_buf_len++; if (padding) { enc->w_buf[enc->w_buf_len + 0] = '='; enc->w_buf[enc->w_buf_len + 1] = '='; enc->w_buf_len += 2; } break; case 2: i_assert(enc->w_buf_len <= (sizeof(enc->w_buf) - 2)); enc->w_buf[enc->w_buf_len] = b64enc[enc->buf]; enc->w_buf_len++; if (padding) { enc->w_buf[enc->w_buf_len + 0] = '='; enc->w_buf_len++; } break; default: i_unreached(); } enc->sub_pos = 0; write_full = write = enc->w_buf_len; if (enc->pending_lf) write_full++; if (enc->max_line_len < SIZE_MAX && line_avail < write) { unsigned int lines; lines = I_MAX((write - line_avail) / enc->max_line_len, 1); write_full += lines * (crlf ? 2 : 1); } else { line_avail = write; } if (write_full == 0) { enc->finished = TRUE; return TRUE; } i_assert(dest != NULL); if (write_full > dst_avail) write_full = dst_avail; ptr = buffer_append_space_unsafe(dest, write_full); end = ptr + write_full; if (enc->pending_lf) { ptr[0] = '\n'; dst_avail--; ptr++; enc->pending_lf = FALSE; } if (line_avail > dst_avail) line_avail = dst_avail; if (line_avail > 0) { memcpy(ptr, enc->w_buf, line_avail); ptr += line_avail; w_buf_pos += line_avail; } while (ptr < end && w_buf_pos < enc->w_buf_len) { enc->cur_line_len = 0; if (crlf) { ptr[0] = '\r'; ptr++; if (ptr == end) { enc->pending_lf = TRUE; break; } } ptr[0] = '\n'; ptr++; if (ptr == end) break; write = I_MIN(enc->w_buf_len - w_buf_pos, enc->max_line_len); write = I_MIN(write, (size_t)(end - ptr)); memcpy(ptr, &enc->w_buf[w_buf_pos], write); ptr += write; w_buf_pos += write; enc->cur_line_len += write; i_assert(ptr <= end); } i_assert(ptr == end); if (w_buf_pos < enc->w_buf_len) { enc->w_buf_len -= w_buf_pos; memmove(enc->w_buf, enc->w_buf + w_buf_pos, enc->w_buf_len); return FALSE; } if (enc->pending_lf) return FALSE; enc->finished = TRUE; return TRUE; } /* * Low-level Base64 decoder */ #define IS_EMPTY(c) \ ((c) == '\n' || (c) == '\r' || (c) == ' ' || (c) == '\t') static inline void base64_skip_whitespace(struct base64_decoder *dec, const unsigned char *src_c, size_t src_size, size_t *src_pos) { if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE)) return; /* skip any whitespace in the padding */ while ((*src_pos) < src_size && IS_EMPTY(src_c[(*src_pos)])) (*src_pos)++; } int base64_decode_more(struct base64_decoder *dec, const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { const struct base64_scheme *b64 = dec->b64; const unsigned char *src_c = src; bool expect_boundary = HAS_ALL_BITS( dec->flags, BASE64_DECODE_FLAG_EXPECT_BOUNDARY); bool no_whitespace = HAS_ALL_BITS( dec->flags, BASE64_DECODE_FLAG_NO_WHITESPACE); bool no_padding = HAS_ALL_BITS( dec->flags, BASE64_DECODE_FLAG_NO_PADDING); size_t src_pos, dst_avail; int ret = 1; i_assert(!dec->finished); i_assert(!dec->failed); if (dec->seen_boundary) { /* already seen the boundary/end of base64 data */ if (src_pos_r != NULL) *src_pos_r = 0; dec->failed = TRUE; return -1; } src_pos = 0; if (dec->seen_end) { /* skip any whitespace at the end */ base64_skip_whitespace(dec, src_c, src_size, &src_pos); if (src_pos_r != NULL) *src_pos_r = src_pos; if (src_pos < src_size) { if (!expect_boundary) { dec->failed = TRUE; return -1; } dec->seen_boundary = TRUE; return 0; } if (no_whitespace) { dec->seen_boundary = TRUE; return 0; } /* more whitespace may follow */ return 1; } if (src_size == 0) { if (src_pos_r != NULL) *src_pos_r = 0; return 1; } dst_avail = buffer_get_avail_size(dest); if (dst_avail == 0) { i_assert(src_pos_r != NULL); *src_pos_r = 0; return 1; } for (; !dec->seen_padding && src_pos < src_size; src_pos++) { unsigned char in = src_c[src_pos]; unsigned char dm = b64->decmap[in]; if (dm == 0xff) { if (no_whitespace) { ret = -1; break; } if (unlikely(!IS_EMPTY(in))) { ret = -1; break; } continue; } if (dst_avail == 0) { i_assert(src_pos_r != NULL); *src_pos_r = src_pos; return 1; } switch (dec->sub_pos) { case 0: dec->buf = dm; dec->sub_pos++; break; case 1: dec->buf = (dec->buf << 2) | (dm >> 4); buffer_append_c(dest, dec->buf); dst_avail--; dec->buf = dm; dec->sub_pos++; break; case 2: dec->buf = ((dec->buf << 4) & 0xff) | (dm >> 2); buffer_append_c(dest, dec->buf); dst_avail--; dec->buf = dm; dec->sub_pos++; break; case 3: dec->buf = ((dec->buf << 6) & 0xc0) | dm; buffer_append_c(dest, dec->buf); dst_avail--; dec->buf = 0; dec->sub_pos = 0; break; default: i_unreached(); } } if (dec->seen_padding) { /* skip any whitespace in or after the padding */ base64_skip_whitespace(dec, src_c, src_size, &src_pos); if (src_pos == src_size) { if (src_pos_r != NULL) *src_pos_r = src_pos; return 1; } } if (dec->seen_padding || ret < 0) { /* try to parse the end (padding) of the base64 input */ i_assert(src_pos < src_size); if (no_padding) { /* no padding allowed */ i_assert(!dec->seen_padding); ret = -1; } else { switch (dec->sub_pos) { case 0: case 1: /* no padding expected */ ret = -1; break; case 2: if (unlikely(src_c[src_pos] != '=')) { /* invalid character */ ret = -1; break; } dec->seen_padding = TRUE; dec->sub_pos++; src_pos++; if (src_pos == src_size) { ret = 1; break; } /* skip any whitespace in the padding */ base64_skip_whitespace(dec, src_c, src_size, &src_pos); if (src_pos == src_size) { ret = 1; break; } /* fall through */ case 3: if (unlikely(src_c[src_pos] != '=')) { /* invalid character */ ret = -1; break; } dec->seen_padding = TRUE; dec->seen_end = TRUE; dec->sub_pos = 0; src_pos++; /* skip any trailing whitespace */ base64_skip_whitespace(dec, src_c, src_size, &src_pos); if (src_pos < src_size) { ret = -1; break; } if (no_whitespace) { dec->seen_boundary = TRUE; ret = 0; } else { /* more whitespace may follow */ ret = 1; } break; } } } if (ret < 0) { if (!expect_boundary) { dec->failed = TRUE; } else { dec->seen_boundary = TRUE; ret = 0; } } if (src_pos_r != NULL) *src_pos_r = src_pos; return ret; } int base64_decode_finish(struct base64_decoder *dec) { i_assert(!dec->finished); dec->finished = TRUE; if (dec->failed) return -1; if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_NO_PADDING)) { i_assert(!dec->seen_padding); return 0; } if (HAS_ALL_BITS(dec->flags, BASE64_DECODE_FLAG_IGNORE_PADDING)) return 0; return (dec->sub_pos == 0 ? 0 : -1); } /* * Generic Base64 API */ buffer_t *t_base64_scheme_encode(const struct base64_scheme *b64, enum base64_encode_flags flags, size_t max_line_len, const void *src, size_t src_size) { buffer_t *buf; buf = t_buffer_create(MAX_BASE64_ENCODED_SIZE(src_size)); base64_scheme_encode(b64, flags, max_line_len, src, src_size, buf); return buf; } int base64_scheme_decode(const struct base64_scheme *b64, enum base64_decode_flags flags, const void *src, size_t src_size, buffer_t *dest) { struct base64_decoder dec; int ret; base64_decode_init(&dec, b64, flags); ret = base64_decode_more(&dec, src, src_size, NULL, dest); if (ret >= 0) ret = base64_decode_finish(&dec); return ret; } buffer_t *t_base64_scheme_decode(const struct base64_scheme *b64, enum base64_decode_flags flags, const void *src, size_t src_size) { buffer_t *buf; buf = t_buffer_create(MAX_BASE64_DECODED_SIZE(src_size)); (void)base64_scheme_decode(b64, flags, src, src_size, buf); return buf; } /* * "base64" encoding scheme (RFC 4648, Section 4) */ struct base64_scheme base64_scheme = { .encmap = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', }, .decmap = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, /* 40-47 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, /* 88-95 */ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, }; /* * "base64url" encoding scheme (RFC 4648, Section 5) */ struct base64_scheme base64url_scheme = { .encmap = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', }, .decmap = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0-7 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 8-15 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16-23 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 24-31 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32-39 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, /* 40-47 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, /* 48-55 */ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56-63 */ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* 64-71 */ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, /* 72-79 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* 80-87 */ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0x3f, /* 88-95 */ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* 96-103 */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 104-111 */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, /* 112-119 */ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, /* 120-127 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128-255 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, };