xref: /spdk/lib/util/base64.c (revision 075d422f3480d3db11013734f833304606867da4)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2018 Intel Corporation. All rights reserved.
3d483d8a4SRui Chang  *   Copyright(c) ARM Limited. 2021 All rights reserved.
48aaa7079SLiu Xiaodong  *   All rights reserved.
58aaa7079SLiu Xiaodong  */
68aaa7079SLiu Xiaodong 
78aaa7079SLiu Xiaodong #include "spdk/stdinc.h"
88aaa7079SLiu Xiaodong #include "spdk/endian.h"
98aaa7079SLiu Xiaodong #include "spdk/base64.h"
108aaa7079SLiu Xiaodong 
11a41fb6e6SRichael Zhuang #ifdef __aarch64__
12d483d8a4SRui Chang #ifdef __ARM_FEATURE_SVE
13d483d8a4SRui Chang #include "base64_sve.c"
14d483d8a4SRui Chang #else
15a41fb6e6SRichael Zhuang #include "base64_neon.c"
16a41fb6e6SRichael Zhuang #endif
17d483d8a4SRui Chang #endif
18d483d8a4SRui Chang 
19a41fb6e6SRichael Zhuang 
208aaa7079SLiu Xiaodong #define BASE64_ENC_BITMASK 0x3FUL
218aaa7079SLiu Xiaodong #define BASE64_PADDING_CHAR '='
228aaa7079SLiu Xiaodong 
238aaa7079SLiu Xiaodong static const char base64_enc_table[] =
248aaa7079SLiu Xiaodong 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
258aaa7079SLiu Xiaodong 	"abcdefghijklmnopqrstuvwxyz"
268aaa7079SLiu Xiaodong 	"0123456789+/";
278aaa7079SLiu Xiaodong 
28cc6920a4SJosh Soref static const char base64_urlsafe_enc_table[] =
298aaa7079SLiu Xiaodong 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
308aaa7079SLiu Xiaodong 	"abcdefghijklmnopqrstuvwxyz"
318aaa7079SLiu Xiaodong 	"0123456789-_";
328aaa7079SLiu Xiaodong 
338aaa7079SLiu Xiaodong static const uint8_t
348aaa7079SLiu Xiaodong base64_dec_table[] = {
358aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
368aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
378aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
388aaa7079SLiu Xiaodong 	52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255,
398aaa7079SLiu Xiaodong 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
408aaa7079SLiu Xiaodong 	15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
418aaa7079SLiu Xiaodong 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
428aaa7079SLiu Xiaodong 	41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
438aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
448aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
458aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
468aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
478aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
488aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
498aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
508aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
518aaa7079SLiu Xiaodong };
528aaa7079SLiu Xiaodong 
538aaa7079SLiu Xiaodong static const uint8_t
548aaa7079SLiu Xiaodong base64_urlsafe_dec_table[] = {
558aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
568aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
578aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255,
588aaa7079SLiu Xiaodong 	52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255,
598aaa7079SLiu Xiaodong 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
608aaa7079SLiu Xiaodong 	15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255,  63,
618aaa7079SLiu Xiaodong 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
628aaa7079SLiu Xiaodong 	41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
638aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
648aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
658aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
668aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
678aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
688aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
698aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
708aaa7079SLiu Xiaodong 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
718aaa7079SLiu Xiaodong };
728aaa7079SLiu Xiaodong 
738aaa7079SLiu Xiaodong static int
base64_encode(char * dst,const char * enc_table,const void * src,size_t src_len)7457c2b0c5SSeth Howell base64_encode(char *dst, const char *enc_table, const void *src, size_t src_len)
758aaa7079SLiu Xiaodong {
768aaa7079SLiu Xiaodong 	uint32_t raw_u32;
778aaa7079SLiu Xiaodong 
788aaa7079SLiu Xiaodong 	if (!dst || !src || src_len <= 0) {
798aaa7079SLiu Xiaodong 		return -EINVAL;
808aaa7079SLiu Xiaodong 	}
818aaa7079SLiu Xiaodong 
82a41fb6e6SRichael Zhuang #ifdef __aarch64__
83d483d8a4SRui Chang #ifdef __ARM_FEATURE_SVE
84d483d8a4SRui Chang 	base64_encode_sve(&dst, enc_table, &src, &src_len);
85d483d8a4SRui Chang #else
8657c2b0c5SSeth Howell 	base64_encode_neon64(&dst, enc_table, &src, &src_len);
87a41fb6e6SRichael Zhuang #endif
88d483d8a4SRui Chang #endif
89d483d8a4SRui Chang 
90a41fb6e6SRichael Zhuang 
918aaa7079SLiu Xiaodong 	while (src_len >= 4) {
928aaa7079SLiu Xiaodong 		raw_u32 = from_be32(src);
938aaa7079SLiu Xiaodong 
948aaa7079SLiu Xiaodong 		*dst++ = enc_table[(raw_u32 >> 26) & BASE64_ENC_BITMASK];
958aaa7079SLiu Xiaodong 		*dst++ = enc_table[(raw_u32 >> 20) & BASE64_ENC_BITMASK];
968aaa7079SLiu Xiaodong 		*dst++ = enc_table[(raw_u32 >> 14) & BASE64_ENC_BITMASK];
978aaa7079SLiu Xiaodong 		*dst++ = enc_table[(raw_u32 >> 8) & BASE64_ENC_BITMASK];
988aaa7079SLiu Xiaodong 
998aaa7079SLiu Xiaodong 		src_len -= 3;
100*075d422fSKonrad Sztyber 		src = (uint8_t *)src + 3;
1018aaa7079SLiu Xiaodong 	}
1028aaa7079SLiu Xiaodong 
1038aaa7079SLiu Xiaodong 	if (src_len == 0) {
1048aaa7079SLiu Xiaodong 		goto out;
1058aaa7079SLiu Xiaodong 	}
1068aaa7079SLiu Xiaodong 
1078aaa7079SLiu Xiaodong 	raw_u32 = 0;
1088aaa7079SLiu Xiaodong 	memcpy(&raw_u32, src, src_len);
1098aaa7079SLiu Xiaodong 	raw_u32 = from_be32(&raw_u32);
1108aaa7079SLiu Xiaodong 
1118aaa7079SLiu Xiaodong 	*dst++ = enc_table[(raw_u32 >> 26) & BASE64_ENC_BITMASK];
1128aaa7079SLiu Xiaodong 	*dst++ = enc_table[(raw_u32 >> 20) & BASE64_ENC_BITMASK];
1138aaa7079SLiu Xiaodong 	*dst++ = (src_len >= 2) ? enc_table[(raw_u32 >> 14) & BASE64_ENC_BITMASK] : BASE64_PADDING_CHAR;
1148aaa7079SLiu Xiaodong 	*dst++ = (src_len == 3) ? enc_table[(raw_u32 >> 8) & BASE64_ENC_BITMASK] : BASE64_PADDING_CHAR;
1158aaa7079SLiu Xiaodong 
1168aaa7079SLiu Xiaodong out:
1178aaa7079SLiu Xiaodong 	*dst = '\0';
1188aaa7079SLiu Xiaodong 
1198aaa7079SLiu Xiaodong 	return 0;
1208aaa7079SLiu Xiaodong }
1218aaa7079SLiu Xiaodong 
1228aaa7079SLiu Xiaodong int
spdk_base64_encode(char * dst,const void * src,size_t src_len)1238aaa7079SLiu Xiaodong spdk_base64_encode(char *dst, const void *src, size_t src_len)
1248aaa7079SLiu Xiaodong {
12557c2b0c5SSeth Howell 	return base64_encode(dst, base64_enc_table, src, src_len);
1268aaa7079SLiu Xiaodong }
1278aaa7079SLiu Xiaodong 
1288aaa7079SLiu Xiaodong int
spdk_base64_urlsafe_encode(char * dst,const void * src,size_t src_len)1298aaa7079SLiu Xiaodong spdk_base64_urlsafe_encode(char *dst, const void *src, size_t src_len)
1308aaa7079SLiu Xiaodong {
131cc6920a4SJosh Soref 	return base64_encode(dst, base64_urlsafe_enc_table, src, src_len);
1328aaa7079SLiu Xiaodong }
1338aaa7079SLiu Xiaodong 
134d483d8a4SRui Chang #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
135a41fb6e6SRichael Zhuang static int
base64_decode(void * dst,size_t * _dst_len,const uint8_t * dec_table,const uint8_t * dec_table_opt,const char * src)13657c2b0c5SSeth Howell base64_decode(void *dst, size_t *_dst_len, const uint8_t *dec_table,
137a41fb6e6SRichael Zhuang 	      const uint8_t *dec_table_opt, const char *src)
138a41fb6e6SRichael Zhuang #else
1398aaa7079SLiu Xiaodong static int
14057c2b0c5SSeth Howell base64_decode(void *dst, size_t *_dst_len, const uint8_t *dec_table, const char *src)
141a41fb6e6SRichael Zhuang #endif
1428aaa7079SLiu Xiaodong {
143a41fb6e6SRichael Zhuang 	size_t src_strlen;
1448aaa7079SLiu Xiaodong 	size_t tail_len = 0;
1458aaa7079SLiu Xiaodong 	const uint8_t *src_in;
1468aaa7079SLiu Xiaodong 	uint32_t tmp[4];
1478aaa7079SLiu Xiaodong 	int i;
1488aaa7079SLiu Xiaodong 
14906fc4cadSMike Carlin 	if (!src) {
1508aaa7079SLiu Xiaodong 		return -EINVAL;
1518aaa7079SLiu Xiaodong 	}
1528aaa7079SLiu Xiaodong 
1538aaa7079SLiu Xiaodong 	src_strlen = strlen(src);
1548aaa7079SLiu Xiaodong 
1558aaa7079SLiu Xiaodong 	/* strlen of src should be 4n */
1568aaa7079SLiu Xiaodong 	if (src_strlen == 0 || src_strlen % 4 != 0) {
1578aaa7079SLiu Xiaodong 		return -EINVAL;
1588aaa7079SLiu Xiaodong 	}
1598aaa7079SLiu Xiaodong 
1608aaa7079SLiu Xiaodong 	/* Consider Base64 padding, it at most has 2 padding characters. */
1618aaa7079SLiu Xiaodong 	for (i = 0; i < 2; i++) {
1628aaa7079SLiu Xiaodong 		if (src[src_strlen - 1] != BASE64_PADDING_CHAR) {
1638aaa7079SLiu Xiaodong 			break;
1648aaa7079SLiu Xiaodong 		}
1658aaa7079SLiu Xiaodong 		src_strlen--;
1668aaa7079SLiu Xiaodong 	}
1678aaa7079SLiu Xiaodong 
1688aaa7079SLiu Xiaodong 	/* strlen of src without padding shouldn't be 4n+1 */
1698aaa7079SLiu Xiaodong 	if (src_strlen == 0 || src_strlen % 4 == 1) {
1708aaa7079SLiu Xiaodong 		return -EINVAL;
1718aaa7079SLiu Xiaodong 	}
1728aaa7079SLiu Xiaodong 
173a41fb6e6SRichael Zhuang 	if (_dst_len) {
174a41fb6e6SRichael Zhuang 		*_dst_len = spdk_base64_get_decoded_len(src_strlen);
175a41fb6e6SRichael Zhuang 	}
17606fc4cadSMike Carlin 
17706fc4cadSMike Carlin 	/* If dst is NULL, the client is only concerned w/ _dst_len, return */
17806fc4cadSMike Carlin 	if (!dst) {
17906fc4cadSMike Carlin 		return 0;
18006fc4cadSMike Carlin 	}
18106fc4cadSMike Carlin 
1828aaa7079SLiu Xiaodong 	src_in = (const uint8_t *) src;
1838aaa7079SLiu Xiaodong 
184a41fb6e6SRichael Zhuang #ifdef __aarch64__
185d483d8a4SRui Chang #ifdef __ARM_FEATURE_SVE
186d483d8a4SRui Chang 	base64_decode_sve(&dst, dec_table, &src_in, &src_strlen);
187d483d8a4SRui Chang #else
18857c2b0c5SSeth Howell 	base64_decode_neon64(&dst, dec_table_opt, &src_in, &src_strlen);
189d483d8a4SRui Chang #endif
190a41fb6e6SRichael Zhuang 
191a41fb6e6SRichael Zhuang 	if (src_strlen == 0) {
192a41fb6e6SRichael Zhuang 		return 0;
193a41fb6e6SRichael Zhuang 	}
194a41fb6e6SRichael Zhuang #endif
195a41fb6e6SRichael Zhuang 
196d483d8a4SRui Chang 
1978aaa7079SLiu Xiaodong 	/* space of dst can be used by to_be32 */
1988aaa7079SLiu Xiaodong 	while (src_strlen > 4) {
1998aaa7079SLiu Xiaodong 		tmp[0] = dec_table[*src_in++];
2008aaa7079SLiu Xiaodong 		tmp[1] = dec_table[*src_in++];
2018aaa7079SLiu Xiaodong 		tmp[2] = dec_table[*src_in++];
2028aaa7079SLiu Xiaodong 		tmp[3] = dec_table[*src_in++];
2038aaa7079SLiu Xiaodong 
2048aaa7079SLiu Xiaodong 		if (tmp[0] == 255 || tmp[1] == 255 || tmp[2] == 255 || tmp[3] == 255) {
2058aaa7079SLiu Xiaodong 			return -EINVAL;
2068aaa7079SLiu Xiaodong 		}
2078aaa7079SLiu Xiaodong 
2088aaa7079SLiu Xiaodong 		to_be32(dst, tmp[3] << 8 | tmp[2] << 14 | tmp[1] << 20 | tmp[0] << 26);
2098aaa7079SLiu Xiaodong 
210*075d422fSKonrad Sztyber 		dst = (uint8_t *)dst + 3;
2118aaa7079SLiu Xiaodong 		src_strlen -= 4;
2128aaa7079SLiu Xiaodong 	}
2138aaa7079SLiu Xiaodong 
2148aaa7079SLiu Xiaodong 	/* space of dst is not enough to be used by to_be32 */
2158aaa7079SLiu Xiaodong 	tmp[0] = dec_table[src_in[0]];
2168aaa7079SLiu Xiaodong 	tmp[1] = dec_table[src_in[1]];
2178aaa7079SLiu Xiaodong 	tmp[2] = (src_strlen >= 3) ? dec_table[src_in[2]] : 0;
2188aaa7079SLiu Xiaodong 	tmp[3] = (src_strlen == 4) ? dec_table[src_in[3]] : 0;
2198aaa7079SLiu Xiaodong 	tail_len = src_strlen - 1;
2208aaa7079SLiu Xiaodong 
2218aaa7079SLiu Xiaodong 	if (tmp[0] == 255 || tmp[1] == 255 || tmp[2] == 255 || tmp[3] == 255) {
2228aaa7079SLiu Xiaodong 		return -EINVAL;
2238aaa7079SLiu Xiaodong 	}
2248aaa7079SLiu Xiaodong 
2258aaa7079SLiu Xiaodong 	to_be32(&tmp[3], tmp[3] << 8 | tmp[2] << 14 | tmp[1] << 20 | tmp[0] << 26);
2268aaa7079SLiu Xiaodong 	memcpy(dst, (uint8_t *)&tmp[3], tail_len);
2278aaa7079SLiu Xiaodong 
2288aaa7079SLiu Xiaodong 	return 0;
2298aaa7079SLiu Xiaodong }
2308aaa7079SLiu Xiaodong 
2318aaa7079SLiu Xiaodong int
spdk_base64_decode(void * dst,size_t * dst_len,const char * src)2328aaa7079SLiu Xiaodong spdk_base64_decode(void *dst, size_t *dst_len, const char *src)
2338aaa7079SLiu Xiaodong {
234d483d8a4SRui Chang #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
23557c2b0c5SSeth Howell 	return base64_decode(dst, dst_len, base64_dec_table, base64_dec_table_neon64, src);
236a41fb6e6SRichael Zhuang #else
23757c2b0c5SSeth Howell 	return base64_decode(dst, dst_len, base64_dec_table, src);
238a41fb6e6SRichael Zhuang #endif
2398aaa7079SLiu Xiaodong }
2408aaa7079SLiu Xiaodong 
2418aaa7079SLiu Xiaodong int
spdk_base64_urlsafe_decode(void * dst,size_t * dst_len,const char * src)2428aaa7079SLiu Xiaodong spdk_base64_urlsafe_decode(void *dst, size_t *dst_len, const char *src)
2438aaa7079SLiu Xiaodong {
244d483d8a4SRui Chang #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
24557c2b0c5SSeth Howell 	return base64_decode(dst, dst_len, base64_urlsafe_dec_table, base64_urlsafe_dec_table_neon64,
246a41fb6e6SRichael Zhuang 			     src);
247a41fb6e6SRichael Zhuang #else
24857c2b0c5SSeth Howell 	return base64_decode(dst, dst_len, base64_urlsafe_dec_table, src);
249a41fb6e6SRichael Zhuang #endif
2508aaa7079SLiu Xiaodong }
251