113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci/* Base64 encoding/decoding */
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#include "curl_setup.h"
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
3013498266Sopenharmony_ci  !defined(CURL_DISABLE_LDAP) || \
3113498266Sopenharmony_ci  !defined(CURL_DISABLE_SMTP) || \
3213498266Sopenharmony_ci  !defined(CURL_DISABLE_POP3) || \
3313498266Sopenharmony_ci  !defined(CURL_DISABLE_IMAP) || \
3413498266Sopenharmony_ci  !defined(CURL_DISABLE_DIGEST_AUTH) || \
3513498266Sopenharmony_ci  !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
3613498266Sopenharmony_ci#include "curl/curl.h"
3713498266Sopenharmony_ci#include "warnless.h"
3813498266Sopenharmony_ci#include "curl_base64.h"
3913498266Sopenharmony_ci
4013498266Sopenharmony_ci/* The last 2 #include files should be in this order */
4113498266Sopenharmony_ci#ifdef BUILDING_LIBCURL
4213498266Sopenharmony_ci#include "curl_memory.h"
4313498266Sopenharmony_ci#endif
4413498266Sopenharmony_ci#include "memdebug.h"
4513498266Sopenharmony_ci
4613498266Sopenharmony_ci/* ---- Base64 Encoding/Decoding Table --- */
4713498266Sopenharmony_ci/* Padding character string starts at offset 64. */
4813498266Sopenharmony_cistatic const char base64encdec[]=
4913498266Sopenharmony_ci  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
5013498266Sopenharmony_ci
5113498266Sopenharmony_ci/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
5213498266Sopenharmony_ci   section 5 */
5313498266Sopenharmony_cistatic const char base64url[]=
5413498266Sopenharmony_ci  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
5513498266Sopenharmony_ci
5613498266Sopenharmony_cistatic const unsigned char decodetable[] =
5713498266Sopenharmony_ci{ 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255,
5813498266Sopenharmony_ci  255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
5913498266Sopenharmony_ci  17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28,
6013498266Sopenharmony_ci  29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
6113498266Sopenharmony_ci  48, 49, 50, 51 };
6213498266Sopenharmony_ci/*
6313498266Sopenharmony_ci * Curl_base64_decode()
6413498266Sopenharmony_ci *
6513498266Sopenharmony_ci * Given a base64 NUL-terminated string at src, decode it and return a
6613498266Sopenharmony_ci * pointer in *outptr to a newly allocated memory area holding decoded
6713498266Sopenharmony_ci * data. Size of decoded data is returned in variable pointed by outlen.
6813498266Sopenharmony_ci *
6913498266Sopenharmony_ci * Returns CURLE_OK on success, otherwise specific error code. Function
7013498266Sopenharmony_ci * output shall not be considered valid unless CURLE_OK is returned.
7113498266Sopenharmony_ci *
7213498266Sopenharmony_ci * When decoded data length is 0, returns NULL in *outptr.
7313498266Sopenharmony_ci *
7413498266Sopenharmony_ci * @unittest: 1302
7513498266Sopenharmony_ci */
7613498266Sopenharmony_ciCURLcode Curl_base64_decode(const char *src,
7713498266Sopenharmony_ci                            unsigned char **outptr, size_t *outlen)
7813498266Sopenharmony_ci{
7913498266Sopenharmony_ci  size_t srclen = 0;
8013498266Sopenharmony_ci  size_t padding = 0;
8113498266Sopenharmony_ci  size_t i;
8213498266Sopenharmony_ci  size_t numQuantums;
8313498266Sopenharmony_ci  size_t fullQuantums;
8413498266Sopenharmony_ci  size_t rawlen = 0;
8513498266Sopenharmony_ci  unsigned char *pos;
8613498266Sopenharmony_ci  unsigned char *newstr;
8713498266Sopenharmony_ci  unsigned char lookup[256];
8813498266Sopenharmony_ci
8913498266Sopenharmony_ci  *outptr = NULL;
9013498266Sopenharmony_ci  *outlen = 0;
9113498266Sopenharmony_ci  srclen = strlen(src);
9213498266Sopenharmony_ci
9313498266Sopenharmony_ci  /* Check the length of the input string is valid */
9413498266Sopenharmony_ci  if(!srclen || srclen % 4)
9513498266Sopenharmony_ci    return CURLE_BAD_CONTENT_ENCODING;
9613498266Sopenharmony_ci
9713498266Sopenharmony_ci  /* srclen is at least 4 here */
9813498266Sopenharmony_ci  while(src[srclen - 1 - padding] == '=') {
9913498266Sopenharmony_ci    /* count padding characters */
10013498266Sopenharmony_ci    padding++;
10113498266Sopenharmony_ci    /* A maximum of two = padding characters is allowed */
10213498266Sopenharmony_ci    if(padding > 2)
10313498266Sopenharmony_ci      return CURLE_BAD_CONTENT_ENCODING;
10413498266Sopenharmony_ci  }
10513498266Sopenharmony_ci
10613498266Sopenharmony_ci  /* Calculate the number of quantums */
10713498266Sopenharmony_ci  numQuantums = srclen / 4;
10813498266Sopenharmony_ci  fullQuantums = numQuantums - (padding ? 1 : 0);
10913498266Sopenharmony_ci
11013498266Sopenharmony_ci  /* Calculate the size of the decoded string */
11113498266Sopenharmony_ci  rawlen = (numQuantums * 3) - padding;
11213498266Sopenharmony_ci
11313498266Sopenharmony_ci  /* Allocate our buffer including room for a null-terminator */
11413498266Sopenharmony_ci  newstr = malloc(rawlen + 1);
11513498266Sopenharmony_ci  if(!newstr)
11613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
11713498266Sopenharmony_ci
11813498266Sopenharmony_ci  pos = newstr;
11913498266Sopenharmony_ci
12013498266Sopenharmony_ci  memset(lookup, 0xff, sizeof(lookup));
12113498266Sopenharmony_ci  memcpy(&lookup['+'], decodetable, sizeof(decodetable));
12213498266Sopenharmony_ci  /* replaces
12313498266Sopenharmony_ci  {
12413498266Sopenharmony_ci    unsigned char c;
12513498266Sopenharmony_ci    const unsigned char *p = (const unsigned char *)base64encdec;
12613498266Sopenharmony_ci    for(c = 0; *p; c++, p++)
12713498266Sopenharmony_ci      lookup[*p] = c;
12813498266Sopenharmony_ci  }
12913498266Sopenharmony_ci  */
13013498266Sopenharmony_ci
13113498266Sopenharmony_ci  /* Decode the complete quantums first */
13213498266Sopenharmony_ci  for(i = 0; i < fullQuantums; i++) {
13313498266Sopenharmony_ci    unsigned char val;
13413498266Sopenharmony_ci    unsigned int x = 0;
13513498266Sopenharmony_ci    int j;
13613498266Sopenharmony_ci
13713498266Sopenharmony_ci    for(j = 0; j < 4; j++) {
13813498266Sopenharmony_ci      val = lookup[(unsigned char)*src++];
13913498266Sopenharmony_ci      if(val == 0xff) /* bad symbol */
14013498266Sopenharmony_ci        goto bad;
14113498266Sopenharmony_ci      x = (x << 6) | val;
14213498266Sopenharmony_ci    }
14313498266Sopenharmony_ci    pos[2] = x & 0xff;
14413498266Sopenharmony_ci    pos[1] = (x >> 8) & 0xff;
14513498266Sopenharmony_ci    pos[0] = (x >> 16) & 0xff;
14613498266Sopenharmony_ci    pos += 3;
14713498266Sopenharmony_ci  }
14813498266Sopenharmony_ci  if(padding) {
14913498266Sopenharmony_ci    /* this means either 8 or 16 bits output */
15013498266Sopenharmony_ci    unsigned char val;
15113498266Sopenharmony_ci    unsigned int x = 0;
15213498266Sopenharmony_ci    int j;
15313498266Sopenharmony_ci    size_t padc = 0;
15413498266Sopenharmony_ci    for(j = 0; j < 4; j++) {
15513498266Sopenharmony_ci      if(*src == '=') {
15613498266Sopenharmony_ci        x <<= 6;
15713498266Sopenharmony_ci        src++;
15813498266Sopenharmony_ci        if(++padc > padding)
15913498266Sopenharmony_ci          /* this is a badly placed '=' symbol! */
16013498266Sopenharmony_ci          goto bad;
16113498266Sopenharmony_ci      }
16213498266Sopenharmony_ci      else {
16313498266Sopenharmony_ci        val = lookup[(unsigned char)*src++];
16413498266Sopenharmony_ci        if(val == 0xff) /* bad symbol */
16513498266Sopenharmony_ci          goto bad;
16613498266Sopenharmony_ci        x = (x << 6) | val;
16713498266Sopenharmony_ci      }
16813498266Sopenharmony_ci    }
16913498266Sopenharmony_ci    if(padding == 1)
17013498266Sopenharmony_ci      pos[1] = (x >> 8) & 0xff;
17113498266Sopenharmony_ci    pos[0] = (x >> 16) & 0xff;
17213498266Sopenharmony_ci    pos += 3 - padding;
17313498266Sopenharmony_ci  }
17413498266Sopenharmony_ci
17513498266Sopenharmony_ci  /* Zero terminate */
17613498266Sopenharmony_ci  *pos = '\0';
17713498266Sopenharmony_ci
17813498266Sopenharmony_ci  /* Return the decoded data */
17913498266Sopenharmony_ci  *outptr = newstr;
18013498266Sopenharmony_ci  *outlen = rawlen;
18113498266Sopenharmony_ci
18213498266Sopenharmony_ci  return CURLE_OK;
18313498266Sopenharmony_cibad:
18413498266Sopenharmony_ci  free(newstr);
18513498266Sopenharmony_ci  return CURLE_BAD_CONTENT_ENCODING;
18613498266Sopenharmony_ci}
18713498266Sopenharmony_ci
18813498266Sopenharmony_cistatic CURLcode base64_encode(const char *table64,
18913498266Sopenharmony_ci                              const char *inputbuff, size_t insize,
19013498266Sopenharmony_ci                              char **outptr, size_t *outlen)
19113498266Sopenharmony_ci{
19213498266Sopenharmony_ci  char *output;
19313498266Sopenharmony_ci  char *base64data;
19413498266Sopenharmony_ci  const unsigned char *in = (unsigned char *)inputbuff;
19513498266Sopenharmony_ci  const char *padstr = &table64[64];    /* Point to padding string. */
19613498266Sopenharmony_ci
19713498266Sopenharmony_ci  *outptr = NULL;
19813498266Sopenharmony_ci  *outlen = 0;
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci  if(!insize)
20113498266Sopenharmony_ci    insize = strlen(inputbuff);
20213498266Sopenharmony_ci
20313498266Sopenharmony_ci#if SIZEOF_SIZE_T == 4
20413498266Sopenharmony_ci  if(insize > UINT_MAX/4)
20513498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
20613498266Sopenharmony_ci#endif
20713498266Sopenharmony_ci
20813498266Sopenharmony_ci  base64data = output = malloc((insize + 2) / 3 * 4 + 1);
20913498266Sopenharmony_ci  if(!output)
21013498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
21113498266Sopenharmony_ci
21213498266Sopenharmony_ci  while(insize >= 3) {
21313498266Sopenharmony_ci    *output++ = table64[ in[0] >> 2 ];
21413498266Sopenharmony_ci    *output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ];
21513498266Sopenharmony_ci    *output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ];
21613498266Sopenharmony_ci    *output++ = table64[ in[2] & 0x3F ];
21713498266Sopenharmony_ci    insize -= 3;
21813498266Sopenharmony_ci    in += 3;
21913498266Sopenharmony_ci  }
22013498266Sopenharmony_ci  if(insize) {
22113498266Sopenharmony_ci    /* this is only one or two bytes now */
22213498266Sopenharmony_ci    *output++ = table64[ in[0] >> 2 ];
22313498266Sopenharmony_ci    if(insize == 1) {
22413498266Sopenharmony_ci      *output++ = table64[ ((in[0] & 0x03) << 4) ];
22513498266Sopenharmony_ci      if(*padstr) {
22613498266Sopenharmony_ci        *output++ = *padstr;
22713498266Sopenharmony_ci        *output++ = *padstr;
22813498266Sopenharmony_ci      }
22913498266Sopenharmony_ci    }
23013498266Sopenharmony_ci    else {
23113498266Sopenharmony_ci      /* insize == 2 */
23213498266Sopenharmony_ci      *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
23313498266Sopenharmony_ci      *output++ = table64[ ((in[1] & 0x0F) << 2) ];
23413498266Sopenharmony_ci      if(*padstr)
23513498266Sopenharmony_ci        *output++ = *padstr;
23613498266Sopenharmony_ci    }
23713498266Sopenharmony_ci  }
23813498266Sopenharmony_ci
23913498266Sopenharmony_ci  /* Zero terminate */
24013498266Sopenharmony_ci  *output = '\0';
24113498266Sopenharmony_ci
24213498266Sopenharmony_ci  /* Return the pointer to the new data (allocated memory) */
24313498266Sopenharmony_ci  *outptr = base64data;
24413498266Sopenharmony_ci
24513498266Sopenharmony_ci  /* Return the length of the new data */
24613498266Sopenharmony_ci  *outlen = output - base64data;
24713498266Sopenharmony_ci
24813498266Sopenharmony_ci  return CURLE_OK;
24913498266Sopenharmony_ci}
25013498266Sopenharmony_ci
25113498266Sopenharmony_ci/*
25213498266Sopenharmony_ci * Curl_base64_encode()
25313498266Sopenharmony_ci *
25413498266Sopenharmony_ci * Given a pointer to an input buffer and an input size, encode it and
25513498266Sopenharmony_ci * return a pointer in *outptr to a newly allocated memory area holding
25613498266Sopenharmony_ci * encoded data. Size of encoded data is returned in variable pointed by
25713498266Sopenharmony_ci * outlen.
25813498266Sopenharmony_ci *
25913498266Sopenharmony_ci * Input length of 0 indicates input buffer holds a NUL-terminated string.
26013498266Sopenharmony_ci *
26113498266Sopenharmony_ci * Returns CURLE_OK on success, otherwise specific error code. Function
26213498266Sopenharmony_ci * output shall not be considered valid unless CURLE_OK is returned.
26313498266Sopenharmony_ci *
26413498266Sopenharmony_ci * @unittest: 1302
26513498266Sopenharmony_ci */
26613498266Sopenharmony_ciCURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
26713498266Sopenharmony_ci                            char **outptr, size_t *outlen)
26813498266Sopenharmony_ci{
26913498266Sopenharmony_ci  return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
27013498266Sopenharmony_ci}
27113498266Sopenharmony_ci
27213498266Sopenharmony_ci/*
27313498266Sopenharmony_ci * Curl_base64url_encode()
27413498266Sopenharmony_ci *
27513498266Sopenharmony_ci * Given a pointer to an input buffer and an input size, encode it and
27613498266Sopenharmony_ci * return a pointer in *outptr to a newly allocated memory area holding
27713498266Sopenharmony_ci * encoded data. Size of encoded data is returned in variable pointed by
27813498266Sopenharmony_ci * outlen.
27913498266Sopenharmony_ci *
28013498266Sopenharmony_ci * Input length of 0 indicates input buffer holds a NUL-terminated string.
28113498266Sopenharmony_ci *
28213498266Sopenharmony_ci * Returns CURLE_OK on success, otherwise specific error code. Function
28313498266Sopenharmony_ci * output shall not be considered valid unless CURLE_OK is returned.
28413498266Sopenharmony_ci *
28513498266Sopenharmony_ci * @unittest: 1302
28613498266Sopenharmony_ci */
28713498266Sopenharmony_ciCURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
28813498266Sopenharmony_ci                               char **outptr, size_t *outlen)
28913498266Sopenharmony_ci{
29013498266Sopenharmony_ci  return base64_encode(base64url, inputbuff, insize, outptr, outlen);
29113498266Sopenharmony_ci}
29213498266Sopenharmony_ci
29313498266Sopenharmony_ci#endif /* no users so disabled */
294