xref: /spdk/lib/util/base64.c (revision 9544fe07aad355262fcaa65dc27f9965a8ea4617)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) Intel Corporation. All rights reserved.
5  *   Copyright(c) ARM Limited. 2021 All rights reserved.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "spdk/stdinc.h"
36 #include "spdk/endian.h"
37 #include "spdk/base64.h"
38 
39 #ifdef __aarch64__
40 #ifdef __ARM_FEATURE_SVE
41 #include "base64_sve.c"
42 #else
43 #include "base64_neon.c"
44 #endif
45 #endif
46 
47 
48 #define BASE64_ENC_BITMASK 0x3FUL
49 #define BASE64_PADDING_CHAR '='
50 
51 static const char base64_enc_table[] =
52 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
53 	"abcdefghijklmnopqrstuvwxyz"
54 	"0123456789+/";
55 
56 static const char base64_urfsafe_enc_table[] =
57 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
58 	"abcdefghijklmnopqrstuvwxyz"
59 	"0123456789-_";
60 
61 static const uint8_t
62 base64_dec_table[] = {
63 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
64 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
65 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
66 	52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255,
67 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
68 	15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
69 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
70 	41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
71 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
72 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
73 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
74 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
75 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
76 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
77 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
78 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
79 };
80 
81 static const uint8_t
82 base64_urlsafe_dec_table[] = {
83 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
84 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
85 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255,
86 	52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255,
87 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
88 	15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255,  63,
89 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
90 	41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
91 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
92 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
93 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
94 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
95 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
96 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
97 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
98 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
99 };
100 
101 static int
102 base64_encode(char *dst, const char *enc_table, const void *src, size_t src_len)
103 {
104 	uint32_t raw_u32;
105 
106 	if (!dst || !src || src_len <= 0) {
107 		return -EINVAL;
108 	}
109 
110 #ifdef __aarch64__
111 #ifdef __ARM_FEATURE_SVE
112 	base64_encode_sve(&dst, enc_table, &src, &src_len);
113 #else
114 	base64_encode_neon64(&dst, enc_table, &src, &src_len);
115 #endif
116 #endif
117 
118 
119 	while (src_len >= 4) {
120 		raw_u32 = from_be32(src);
121 
122 		*dst++ = enc_table[(raw_u32 >> 26) & BASE64_ENC_BITMASK];
123 		*dst++ = enc_table[(raw_u32 >> 20) & BASE64_ENC_BITMASK];
124 		*dst++ = enc_table[(raw_u32 >> 14) & BASE64_ENC_BITMASK];
125 		*dst++ = enc_table[(raw_u32 >> 8) & BASE64_ENC_BITMASK];
126 
127 		src_len -= 3;
128 		src += 3;
129 	}
130 
131 	if (src_len == 0) {
132 		goto out;
133 	}
134 
135 	raw_u32 = 0;
136 	memcpy(&raw_u32, src, src_len);
137 	raw_u32 = from_be32(&raw_u32);
138 
139 	*dst++ = enc_table[(raw_u32 >> 26) & BASE64_ENC_BITMASK];
140 	*dst++ = enc_table[(raw_u32 >> 20) & BASE64_ENC_BITMASK];
141 	*dst++ = (src_len >= 2) ? enc_table[(raw_u32 >> 14) & BASE64_ENC_BITMASK] : BASE64_PADDING_CHAR;
142 	*dst++ = (src_len == 3) ? enc_table[(raw_u32 >> 8) & BASE64_ENC_BITMASK] : BASE64_PADDING_CHAR;
143 
144 out:
145 	*dst = '\0';
146 
147 	return 0;
148 }
149 
150 int
151 spdk_base64_encode(char *dst, const void *src, size_t src_len)
152 {
153 	return base64_encode(dst, base64_enc_table, src, src_len);
154 }
155 
156 int
157 spdk_base64_urlsafe_encode(char *dst, const void *src, size_t src_len)
158 {
159 	return base64_encode(dst, base64_urfsafe_enc_table, src, src_len);
160 }
161 
162 #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
163 static int
164 base64_decode(void *dst, size_t *_dst_len, const uint8_t *dec_table,
165 	      const uint8_t *dec_table_opt, const char *src)
166 #else
167 static int
168 base64_decode(void *dst, size_t *_dst_len, const uint8_t *dec_table, const char *src)
169 #endif
170 {
171 	size_t src_strlen;
172 	size_t tail_len = 0;
173 	const uint8_t *src_in;
174 	uint32_t tmp[4];
175 	int i;
176 
177 	if (!src) {
178 		return -EINVAL;
179 	}
180 
181 	src_strlen = strlen(src);
182 
183 	/* strlen of src should be 4n */
184 	if (src_strlen == 0 || src_strlen % 4 != 0) {
185 		return -EINVAL;
186 	}
187 
188 	/* Consider Base64 padding, it at most has 2 padding characters. */
189 	for (i = 0; i < 2; i++) {
190 		if (src[src_strlen - 1] != BASE64_PADDING_CHAR) {
191 			break;
192 		}
193 		src_strlen--;
194 	}
195 
196 	/* strlen of src without padding shouldn't be 4n+1 */
197 	if (src_strlen == 0 || src_strlen % 4 == 1) {
198 		return -EINVAL;
199 	}
200 
201 	if (_dst_len) {
202 		*_dst_len = spdk_base64_get_decoded_len(src_strlen);
203 	}
204 
205 	/* If dst is NULL, the client is only concerned w/ _dst_len, return */
206 	if (!dst) {
207 		return 0;
208 	}
209 
210 	src_in = (const uint8_t *) src;
211 
212 #ifdef __aarch64__
213 #ifdef __ARM_FEATURE_SVE
214 	base64_decode_sve(&dst, dec_table, &src_in, &src_strlen);
215 #else
216 	base64_decode_neon64(&dst, dec_table_opt, &src_in, &src_strlen);
217 #endif
218 
219 	if (src_strlen == 0) {
220 		return 0;
221 	}
222 #endif
223 
224 
225 	/* space of dst can be used by to_be32 */
226 	while (src_strlen > 4) {
227 		tmp[0] = dec_table[*src_in++];
228 		tmp[1] = dec_table[*src_in++];
229 		tmp[2] = dec_table[*src_in++];
230 		tmp[3] = dec_table[*src_in++];
231 
232 		if (tmp[0] == 255 || tmp[1] == 255 || tmp[2] == 255 || tmp[3] == 255) {
233 			return -EINVAL;
234 		}
235 
236 		to_be32(dst, tmp[3] << 8 | tmp[2] << 14 | tmp[1] << 20 | tmp[0] << 26);
237 
238 		dst += 3;
239 		src_strlen -= 4;
240 	}
241 
242 	/* space of dst is not enough to be used by to_be32 */
243 	tmp[0] = dec_table[src_in[0]];
244 	tmp[1] = dec_table[src_in[1]];
245 	tmp[2] = (src_strlen >= 3) ? dec_table[src_in[2]] : 0;
246 	tmp[3] = (src_strlen == 4) ? dec_table[src_in[3]] : 0;
247 	tail_len = src_strlen - 1;
248 
249 	if (tmp[0] == 255 || tmp[1] == 255 || tmp[2] == 255 || tmp[3] == 255) {
250 		return -EINVAL;
251 	}
252 
253 	to_be32(&tmp[3], tmp[3] << 8 | tmp[2] << 14 | tmp[1] << 20 | tmp[0] << 26);
254 	memcpy(dst, (uint8_t *)&tmp[3], tail_len);
255 
256 	return 0;
257 }
258 
259 int
260 spdk_base64_decode(void *dst, size_t *dst_len, const char *src)
261 {
262 #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
263 	return base64_decode(dst, dst_len, base64_dec_table, base64_dec_table_neon64, src);
264 #else
265 	return base64_decode(dst, dst_len, base64_dec_table, src);
266 #endif
267 }
268 
269 int
270 spdk_base64_urlsafe_decode(void *dst, size_t *dst_len, const char *src)
271 {
272 #if defined(__aarch64__) && !defined(__ARM_FEATURE_SVE)
273 	return base64_decode(dst, dst_len, base64_urlsafe_dec_table, base64_urlsafe_dec_table_neon64,
274 			     src);
275 #else
276 	return base64_decode(dst, dst_len, base64_urlsafe_dec_table, src);
277 #endif
278 }
279