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