/src/mozilla-central/xpcom/io/Base64.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "Base64.h" |
8 | | |
9 | | #include "mozilla/ArrayUtils.h" |
10 | | #include "mozilla/ScopeExit.h" |
11 | | #include "mozilla/UniquePtrExtensions.h" |
12 | | #include "nsIInputStream.h" |
13 | | #include "nsString.h" |
14 | | #include "nsTArray.h" |
15 | | |
16 | | #include "plbase64.h" |
17 | | |
18 | | namespace { |
19 | | |
20 | | // BEGIN base64 encode code copied and modified from NSPR |
21 | | const unsigned char* base = |
22 | | (unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
23 | | "abcdefghijklmnopqrstuvwxyz" |
24 | | "0123456789+/"; |
25 | | |
26 | | // The Base64 encoder assumes all characters are less than 256; for 16-bit |
27 | | // strings, that means assuming that all characters are within range, and |
28 | | // masking off high bits if necessary. |
29 | | template<typename T> |
30 | | uint8_t |
31 | | CharTo8Bit(T aChar) |
32 | 0 | { |
33 | 0 | return uint8_t(aChar); |
34 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:unsigned char (anonymous namespace)::CharTo8Bit<unsigned char>(unsigned char) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:unsigned char (anonymous namespace)::CharTo8Bit<char>(char) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:unsigned char (anonymous namespace)::CharTo8Bit<char16_t>(char16_t) |
35 | | |
36 | | template<typename SrcT, typename DestT> |
37 | | static void |
38 | | Encode3to4(const SrcT* aSrc, DestT* aDest) |
39 | 0 | { |
40 | 0 | uint32_t b32 = (uint32_t)0; |
41 | 0 | int i, j = 18; |
42 | 0 |
|
43 | 0 | for (i = 0; i < 3; ++i) { |
44 | 0 | b32 <<= 8; |
45 | 0 | b32 |= CharTo8Bit(aSrc[i]); |
46 | 0 | } |
47 | 0 |
|
48 | 0 | for (i = 0; i < 4; ++i) { |
49 | 0 | aDest[i] = base[(uint32_t)((b32 >> j) & 0x3F)]; |
50 | 0 | j -= 6; |
51 | 0 | } |
52 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode3to4<unsigned char, char>(unsigned char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode3to4<unsigned char, char16_t>(unsigned char const*, char16_t*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode3to4<char, char>(char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode3to4<char16_t, char16_t>(char16_t const*, char16_t*) |
53 | | |
54 | | template<typename SrcT, typename DestT> |
55 | | static void |
56 | | Encode2to4(const SrcT* aSrc, DestT* aDest) |
57 | 0 | { |
58 | 0 | uint8_t src0 = CharTo8Bit(aSrc[0]); |
59 | 0 | uint8_t src1 = CharTo8Bit(aSrc[1]); |
60 | 0 | aDest[0] = base[(uint32_t)((src0 >> 2) & 0x3F)]; |
61 | 0 | aDest[1] = base[(uint32_t)(((src0 & 0x03) << 4) | ((src1 >> 4) & 0x0F))]; |
62 | 0 | aDest[2] = base[(uint32_t)((src1 & 0x0F) << 2)]; |
63 | 0 | aDest[3] = DestT('='); |
64 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode2to4<unsigned char, char>(unsigned char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode2to4<unsigned char, char16_t>(unsigned char const*, char16_t*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode2to4<char, char>(char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode2to4<char16_t, char16_t>(char16_t const*, char16_t*) |
65 | | |
66 | | template<typename SrcT, typename DestT> |
67 | | static void |
68 | | Encode1to4(const SrcT* aSrc, DestT* aDest) |
69 | 0 | { |
70 | 0 | uint8_t src0 = CharTo8Bit(aSrc[0]); |
71 | 0 | aDest[0] = base[(uint32_t)((src0 >> 2) & 0x3F)]; |
72 | 0 | aDest[1] = base[(uint32_t)((src0 & 0x03) << 4)]; |
73 | 0 | aDest[2] = DestT('='); |
74 | 0 | aDest[3] = DestT('='); |
75 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode1to4<unsigned char, char>(unsigned char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode1to4<unsigned char, char16_t>(unsigned char const*, char16_t*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode1to4<char, char>(char const*, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode1to4<char16_t, char16_t>(char16_t const*, char16_t*) |
76 | | |
77 | | template<typename SrcT, typename DestT> |
78 | | static void |
79 | | Encode(const SrcT* aSrc, uint32_t aSrcLen, DestT* aDest) |
80 | 0 | { |
81 | 0 | while (aSrcLen >= 3) { |
82 | 0 | Encode3to4(aSrc, aDest); |
83 | 0 | aSrc += 3; |
84 | 0 | aDest += 4; |
85 | 0 | aSrcLen -= 3; |
86 | 0 | } |
87 | 0 |
|
88 | 0 | switch (aSrcLen) { |
89 | 0 | case 2: |
90 | 0 | Encode2to4(aSrc, aDest); |
91 | 0 | break; |
92 | 0 | case 1: |
93 | 0 | Encode1to4(aSrc, aDest); |
94 | 0 | break; |
95 | 0 | case 0: |
96 | 0 | break; |
97 | 0 | default: |
98 | 0 | MOZ_ASSERT_UNREACHABLE("coding error"); |
99 | 0 | } |
100 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode<unsigned char, char>(unsigned char const*, unsigned int, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode<unsigned char, char16_t>(unsigned char const*, unsigned int, char16_t*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode<char, char>(char const*, unsigned int, char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:void (anonymous namespace)::Encode<char16_t, char16_t>(char16_t const*, unsigned int, char16_t*) |
101 | | |
102 | | // END base64 encode code copied and modified from NSPR. |
103 | | |
104 | | template<typename T> |
105 | | struct EncodeInputStream_State |
106 | | { |
107 | | unsigned char c[3]; |
108 | | uint8_t charsOnStack; |
109 | | typename T::char_type* buffer; |
110 | | }; |
111 | | |
112 | | template<typename T> |
113 | | nsresult |
114 | | EncodeInputStream_Encoder(nsIInputStream* aStream, |
115 | | void* aClosure, |
116 | | const char* aFromSegment, |
117 | | uint32_t aToOffset, |
118 | | uint32_t aCount, |
119 | | uint32_t* aWriteCount) |
120 | 0 | { |
121 | 0 | NS_ASSERTION(aCount > 0, "Er, what?"); |
122 | 0 |
|
123 | 0 | EncodeInputStream_State<T>* state = |
124 | 0 | static_cast<EncodeInputStream_State<T>*>(aClosure); |
125 | 0 |
|
126 | 0 | // If we have any data left from last time, encode it now. |
127 | 0 | uint32_t countRemaining = aCount; |
128 | 0 | const unsigned char* src = (const unsigned char*)aFromSegment; |
129 | 0 | if (state->charsOnStack) { |
130 | 0 | unsigned char firstSet[4]; |
131 | 0 | if (state->charsOnStack == 1) { |
132 | 0 | firstSet[0] = state->c[0]; |
133 | 0 | firstSet[1] = src[0]; |
134 | 0 | firstSet[2] = (countRemaining > 1) ? src[1] : '\0'; |
135 | 0 | firstSet[3] = '\0'; |
136 | 0 | } else /* state->charsOnStack == 2 */ { |
137 | 0 | firstSet[0] = state->c[0]; |
138 | 0 | firstSet[1] = state->c[1]; |
139 | 0 | firstSet[2] = src[0]; |
140 | 0 | firstSet[3] = '\0'; |
141 | 0 | } |
142 | 0 | Encode(firstSet, 3, state->buffer); |
143 | 0 | state->buffer += 4; |
144 | 0 | countRemaining -= (3 - state->charsOnStack); |
145 | 0 | src += (3 - state->charsOnStack); |
146 | 0 | state->charsOnStack = 0; |
147 | 0 | } |
148 | 0 |
|
149 | 0 | // Encode the bulk of the |
150 | 0 | uint32_t encodeLength = countRemaining - countRemaining % 3; |
151 | 0 | MOZ_ASSERT(encodeLength % 3 == 0, |
152 | 0 | "Should have an exact number of triplets!"); |
153 | 0 | Encode(src, encodeLength, state->buffer); |
154 | 0 | state->buffer += (encodeLength / 3) * 4; |
155 | 0 | src += encodeLength; |
156 | 0 | countRemaining -= encodeLength; |
157 | 0 |
|
158 | 0 | // We must consume all data, so if there's some data left stash it |
159 | 0 | *aWriteCount = aCount; |
160 | 0 |
|
161 | 0 | if (countRemaining) { |
162 | 0 | // We should never have a full triplet left at this point. |
163 | 0 | MOZ_ASSERT(countRemaining < 3, "We should have encoded more!"); |
164 | 0 | state->c[0] = src[0]; |
165 | 0 | state->c[1] = (countRemaining == 2) ? src[1] : '\0'; |
166 | 0 | state->charsOnStack = countRemaining; |
167 | 0 | } |
168 | 0 |
|
169 | 0 | return NS_OK; |
170 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult (anonymous namespace)::EncodeInputStream_Encoder<nsTSubstring<char> >(nsIInputStream*, void*, char const*, unsigned int, unsigned int, unsigned int*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult (anonymous namespace)::EncodeInputStream_Encoder<nsTSubstring<char16_t> >(nsIInputStream*, void*, char const*, unsigned int, unsigned int, unsigned int*) |
171 | | |
172 | | template<typename T> |
173 | | nsresult |
174 | | EncodeInputStream(nsIInputStream* aInputStream, |
175 | | T& aDest, |
176 | | uint32_t aCount, |
177 | | uint32_t aOffset) |
178 | 0 | { |
179 | 0 | nsresult rv; |
180 | 0 | uint64_t count64 = aCount; |
181 | 0 |
|
182 | 0 | if (!aCount) { |
183 | 0 | rv = aInputStream->Available(&count64); |
184 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
185 | 0 | return rv; |
186 | 0 | } |
187 | 0 | // if count64 is over 4GB, it will be failed at the below condition, |
188 | 0 | // then will return NS_ERROR_OUT_OF_MEMORY |
189 | 0 | aCount = (uint32_t)count64; |
190 | 0 | } |
191 | 0 |
|
192 | 0 | uint64_t countlong = |
193 | 0 | (count64 + 2) / 3 * 4; // +2 due to integer math. |
194 | 0 | if (countlong + aOffset > UINT32_MAX) { |
195 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
196 | 0 | } |
197 | 0 | |
198 | 0 | uint32_t count = uint32_t(countlong); |
199 | 0 |
|
200 | 0 | if (!aDest.SetLength(count + aOffset, mozilla::fallible)) { |
201 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
202 | 0 | } |
203 | 0 | |
204 | 0 | EncodeInputStream_State<T> state; |
205 | 0 | state.charsOnStack = 0; |
206 | 0 | state.c[2] = '\0'; |
207 | 0 | state.buffer = aOffset + aDest.BeginWriting(); |
208 | 0 |
|
209 | 0 | while (1) { |
210 | 0 | uint32_t read = 0; |
211 | 0 |
|
212 | 0 | rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>, |
213 | 0 | (void*)&state, |
214 | 0 | aCount, |
215 | 0 | &read); |
216 | 0 | if (NS_FAILED(rv)) { |
217 | 0 | if (rv == NS_BASE_STREAM_WOULD_BLOCK) { |
218 | 0 | MOZ_CRASH("Not implemented for async streams!"); |
219 | 0 | } |
220 | 0 | if (rv == NS_ERROR_NOT_IMPLEMENTED) { |
221 | 0 | MOZ_CRASH("Requires a stream that implements ReadSegments!"); |
222 | 0 | } |
223 | 0 | return rv; |
224 | 0 | } |
225 | 0 | |
226 | 0 | if (!read) { |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | } |
230 | 0 |
|
231 | 0 | // Finish encoding if anything is left |
232 | 0 | if (state.charsOnStack) { |
233 | 0 | Encode(state.c, state.charsOnStack, state.buffer); |
234 | 0 | } |
235 | 0 |
|
236 | 0 | if (aDest.Length()) { |
237 | 0 | // May belong to an nsCString with an unallocated buffer, so only null |
238 | 0 | // terminate if there is a need to. |
239 | 0 | *aDest.EndWriting() = '\0'; |
240 | 0 | } |
241 | 0 |
|
242 | 0 | return NS_OK; |
243 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult (anonymous namespace)::EncodeInputStream<nsTSubstring<char> >(nsIInputStream*, nsTSubstring<char>&, unsigned int, unsigned int) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult (anonymous namespace)::EncodeInputStream<nsTSubstring<char16_t> >(nsIInputStream*, nsTSubstring<char16_t>&, unsigned int, unsigned int) |
244 | | |
245 | | // Maps an encoded character to a value in the Base64 alphabet, per |
246 | | // RFC 4648, Table 1. Invalid input characters map to UINT8_MAX. |
247 | | |
248 | | static const uint8_t kBase64DecodeTable[] = { |
249 | | // clang-format off |
250 | | /* 0 */ 255, 255, 255, 255, 255, 255, 255, 255, |
251 | | /* 8 */ 255, 255, 255, 255, 255, 255, 255, 255, |
252 | | /* 16 */ 255, 255, 255, 255, 255, 255, 255, 255, |
253 | | /* 24 */ 255, 255, 255, 255, 255, 255, 255, 255, |
254 | | /* 32 */ 255, 255, 255, 255, 255, 255, 255, 255, |
255 | | /* 40 */ 255, 255, 255, |
256 | | 62 /* + */, |
257 | | 255, 255, 255, |
258 | | 63 /* / */, |
259 | | |
260 | | /* 48 */ /* 0 - 9 */ 52, 53, 54, 55, 56, 57, 58, 59, |
261 | | /* 56 */ 60, 61, 255, 255, 255, 255, 255, 255, |
262 | | |
263 | | /* 64 */ 255, /* A - Z */ 0, 1, 2, 3, 4, 5, 6, |
264 | | /* 72 */ 7, 8, 9, 10, 11, 12, 13, 14, |
265 | | /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, |
266 | | /* 88 */ 23, 24, 25, 255, 255, 255, 255, 255, |
267 | | /* 96 */ 255, /* a - z */ 26, 27, 28, 29, 30, 31, 32, |
268 | | /* 104 */ 33, 34, 35, 36, 37, 38, 39, 40, |
269 | | /* 112 */ 41, 42, 43, 44, 45, 46, 47, 48, |
270 | | /* 120 */ 49, 50, 51, 255, 255, 255, 255, 255, |
271 | | }; |
272 | | // clang-format on |
273 | | |
274 | | template<typename T> |
275 | | MOZ_MUST_USE bool |
276 | | Base64CharToValue(T aChar, uint8_t* aValue) |
277 | 0 | { |
278 | 0 | static const size_t mask = 0x7f; |
279 | 0 | static_assert((mask + 1) == sizeof(kBase64DecodeTable)/sizeof(kBase64DecodeTable[0]), |
280 | 0 | "wrong mask"); |
281 | 0 | size_t index = static_cast<uint8_t>(aChar); |
282 | 0 |
|
283 | 0 | if (index & ~mask) { |
284 | 0 | return false; |
285 | 0 | } |
286 | 0 | *aValue = kBase64DecodeTable[index & mask]; |
287 | 0 |
|
288 | 0 | return *aValue != 255; |
289 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool (anonymous namespace)::Base64CharToValue<char>(char, unsigned char*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool (anonymous namespace)::Base64CharToValue<char16_t>(char16_t, unsigned char*) |
290 | | |
291 | | static const char kBase64URLAlphabet[] = |
292 | | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
293 | | |
294 | | // Maps an encoded character to a value in the Base64 URL alphabet, per |
295 | | // RFC 4648, Table 2. Invalid input characters map to UINT8_MAX. |
296 | | static const uint8_t kBase64URLDecodeTable[] = { |
297 | | // clang-format off |
298 | | 255, 255, 255, 255, 255, 255, 255, 255, |
299 | | 255, 255, 255, 255, 255, 255, 255, 255, |
300 | | 255, 255, 255, 255, 255, 255, 255, 255, |
301 | | 255, 255, 255, 255, 255, 255, 255, 255, |
302 | | 255, 255, 255, 255, 255, 255, 255, 255, |
303 | | 255, 255, 255, 255, 255, |
304 | | 62 /* - */, |
305 | | 255, 255, |
306 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */ |
307 | | 255, 255, 255, 255, 255, 255, 255, |
308 | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
309 | | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* A - Z */ |
310 | | 255, 255, 255, 255, |
311 | | 63 /* _ */, |
312 | | 255, |
313 | | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, |
314 | | 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* a - z */ |
315 | | 255, 255, 255, 255, |
316 | | }; |
317 | | // clang-format on |
318 | | |
319 | | bool |
320 | 0 | Base64URLCharToValue(char aChar, uint8_t* aValue) { |
321 | 0 | uint8_t index = static_cast<uint8_t>(aChar); |
322 | 0 | *aValue = kBase64URLDecodeTable[index & 0x7f]; |
323 | 0 | return (*aValue != 255) && !(index & ~0x7f); |
324 | 0 | } |
325 | | |
326 | | } // namespace |
327 | | |
328 | | namespace mozilla { |
329 | | |
330 | | nsresult |
331 | | Base64EncodeInputStream(nsIInputStream* aInputStream, |
332 | | nsACString& aDest, |
333 | | uint32_t aCount, |
334 | | uint32_t aOffset) |
335 | 0 | { |
336 | 0 | return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset); |
337 | 0 | } |
338 | | |
339 | | nsresult |
340 | | Base64EncodeInputStream(nsIInputStream* aInputStream, |
341 | | nsAString& aDest, |
342 | | uint32_t aCount, |
343 | | uint32_t aOffset) |
344 | 0 | { |
345 | 0 | return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset); |
346 | 0 | } |
347 | | |
348 | | nsresult |
349 | | Base64Encode(const char* aBinary, uint32_t aBinaryLen, char** aBase64) |
350 | 0 | { |
351 | 0 | // Check for overflow. |
352 | 0 | if (aBinaryLen > (UINT32_MAX / 4) * 3) { |
353 | 0 | return NS_ERROR_FAILURE; |
354 | 0 | } |
355 | 0 | |
356 | 0 | if (aBinaryLen == 0) { |
357 | 0 | *aBase64 = (char*)moz_xmalloc(1); |
358 | 0 | (*aBase64)[0] = '\0'; |
359 | 0 | return NS_OK; |
360 | 0 | } |
361 | 0 | |
362 | 0 | *aBase64 = nullptr; |
363 | 0 | uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4; |
364 | 0 |
|
365 | 0 | // Add one byte for null termination. |
366 | 0 | UniqueFreePtr<char[]> base64((char*)malloc(base64Len + 1)); |
367 | 0 | if (!base64) { |
368 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
369 | 0 | } |
370 | 0 | |
371 | 0 | Encode(aBinary, aBinaryLen, base64.get()); |
372 | 0 | base64[base64Len] = '\0'; |
373 | 0 |
|
374 | 0 | *aBase64 = base64.release(); |
375 | 0 | return NS_OK; |
376 | 0 | } |
377 | | |
378 | | template<typename T> |
379 | | static nsresult |
380 | | Base64EncodeHelper(const T& aBinary, T& aBase64) |
381 | 0 | { |
382 | 0 | // Check for overflow. |
383 | 0 | if (aBinary.Length() > (UINT32_MAX / 4) * 3) { |
384 | 0 | return NS_ERROR_FAILURE; |
385 | 0 | } |
386 | 0 | |
387 | 0 | if (aBinary.IsEmpty()) { |
388 | 0 | aBase64.Truncate(); |
389 | 0 | return NS_OK; |
390 | 0 | } |
391 | 0 | |
392 | 0 | uint32_t base64Len = ((aBinary.Length() + 2) / 3) * 4; |
393 | 0 |
|
394 | 0 | nsresult rv; |
395 | 0 | auto handle = aBase64.BulkWrite(base64Len, 0, false, rv); |
396 | 0 | if (NS_FAILED(rv)) { |
397 | 0 | return rv; |
398 | 0 | } |
399 | 0 | |
400 | 0 | Encode(aBinary.BeginReading(), aBinary.Length(), handle.Elements()); |
401 | 0 | handle.Finish(base64Len, false); |
402 | 0 | return NS_OK; |
403 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64EncodeHelper<nsTSubstring<char> >(nsTSubstring<char> const&, nsTSubstring<char>&) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64EncodeHelper<nsTSubstring<char16_t> >(nsTSubstring<char16_t> const&, nsTSubstring<char16_t>&) |
404 | | |
405 | | nsresult |
406 | | Base64Encode(const nsACString& aBinary, nsACString& aBase64) |
407 | 0 | { |
408 | 0 | return Base64EncodeHelper(aBinary, aBase64); |
409 | 0 | } |
410 | | |
411 | | nsresult |
412 | | Base64Encode(const nsAString& aBinary, nsAString& aBase64) |
413 | 0 | { |
414 | 0 | return Base64EncodeHelper(aBinary, aBase64); |
415 | 0 | } |
416 | | |
417 | | template<typename T, typename U, typename Decoder> |
418 | | static bool |
419 | | Decode4to3(const T* aSrc, U* aDest, Decoder aToVal) |
420 | 0 | { |
421 | 0 | uint8_t w, x, y, z; |
422 | 0 | if (!aToVal(aSrc[0], &w) || |
423 | 0 | !aToVal(aSrc[1], &x) || |
424 | 0 | !aToVal(aSrc[2], &y) || |
425 | 0 | !aToVal(aSrc[3], &z)) { |
426 | 0 | return false; |
427 | 0 | } |
428 | 0 | aDest[0] = U(uint8_t(w << 2 | x >> 4)); |
429 | 0 | aDest[1] = U(uint8_t(x << 4 | y >> 2)); |
430 | 0 | aDest[2] = U(uint8_t(y << 6 | z)); |
431 | 0 | return true; |
432 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode4to3<char, char, bool (*)(char, unsigned char*)>(char const*, char*, bool (*)(char, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode4to3<char16_t, char16_t, bool (*)(char16_t, unsigned char*)>(char16_t const*, char16_t*, bool (*)(char16_t, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode4to3<char, unsigned char, bool (*)(char, unsigned char*)>(char const*, unsigned char*, bool (*)(char, unsigned char*)) |
433 | | |
434 | | template<typename T, typename U, typename Decoder> |
435 | | static bool |
436 | | Decode3to2(const T* aSrc, U* aDest, Decoder aToVal) |
437 | 0 | { |
438 | 0 | uint8_t w, x, y; |
439 | 0 | if (!aToVal(aSrc[0], &w) || |
440 | 0 | !aToVal(aSrc[1], &x) || |
441 | 0 | !aToVal(aSrc[2], &y)) { |
442 | 0 | return false; |
443 | 0 | } |
444 | 0 | aDest[0] = U(uint8_t(w << 2 | x >> 4)); |
445 | 0 | aDest[1] = U(uint8_t(x << 4 | y >> 2)); |
446 | 0 | return true; |
447 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode3to2<char, char, bool (*)(char, unsigned char*)>(char const*, char*, bool (*)(char, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode3to2<char16_t, char16_t, bool (*)(char16_t, unsigned char*)>(char16_t const*, char16_t*, bool (*)(char16_t, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode3to2<char, unsigned char, bool (*)(char, unsigned char*)>(char const*, unsigned char*, bool (*)(char, unsigned char*)) |
448 | | |
449 | | template<typename T, typename U, typename Decoder> |
450 | | static bool |
451 | | Decode2to1(const T* aSrc, U* aDest, Decoder aToVal) |
452 | 0 | { |
453 | 0 | uint8_t w, x; |
454 | 0 | if (!aToVal(aSrc[0], &w) || |
455 | 0 | !aToVal(aSrc[1], &x)) { |
456 | 0 | return false; |
457 | 0 | } |
458 | 0 | aDest[0] = U(uint8_t(w << 2 | x >> 4)); |
459 | 0 | return true; |
460 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode2to1<char, char, bool (*)(char, unsigned char*)>(char const*, char*, bool (*)(char, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode2to1<char16_t, char16_t, bool (*)(char16_t, unsigned char*)>(char16_t const*, char16_t*, bool (*)(char16_t, unsigned char*)) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:bool mozilla::Decode2to1<char, unsigned char, bool (*)(char, unsigned char*)>(char const*, unsigned char*, bool (*)(char, unsigned char*)) |
461 | | |
462 | | template<typename SrcT, typename DestT> |
463 | | static nsresult |
464 | | Base64DecodeHelper(const SrcT* aBase64, uint32_t aBase64Len, DestT* aBinary, |
465 | | uint32_t* aBinaryLen) |
466 | 0 | { |
467 | 0 | MOZ_ASSERT(aBinary); |
468 | 0 |
|
469 | 0 | const SrcT* input = aBase64; |
470 | 0 | uint32_t inputLength = aBase64Len; |
471 | 0 | DestT* binary = aBinary; |
472 | 0 | uint32_t binaryLength = 0; |
473 | 0 |
|
474 | 0 | // Handle trailing '=' characters. |
475 | 0 | if (inputLength && (inputLength % 4 == 0)) { |
476 | 0 | if (aBase64[inputLength - 1] == SrcT('=')) { |
477 | 0 | if (aBase64[inputLength - 2] == SrcT('=')) { |
478 | 0 | inputLength -= 2; |
479 | 0 | } else { |
480 | 0 | inputLength -= 1; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | } |
484 | 0 |
|
485 | 0 | while (inputLength >= 4) { |
486 | 0 | if (!Decode4to3(input, binary, Base64CharToValue<SrcT>)) { |
487 | 0 | return NS_ERROR_INVALID_ARG; |
488 | 0 | } |
489 | 0 | |
490 | 0 | input += 4; |
491 | 0 | inputLength -= 4; |
492 | 0 | binary += 3; |
493 | 0 | binaryLength += 3; |
494 | 0 | } |
495 | 0 |
|
496 | 0 | switch (inputLength) { |
497 | 0 | case 3: |
498 | 0 | if (!Decode3to2(input, binary, Base64CharToValue<SrcT>)) { |
499 | 0 | return NS_ERROR_INVALID_ARG; |
500 | 0 | } |
501 | 0 | binaryLength += 2; |
502 | 0 | break; |
503 | 0 | case 2: |
504 | 0 | if (!Decode2to1(input, binary, Base64CharToValue<SrcT>)) { |
505 | 0 | return NS_ERROR_INVALID_ARG; |
506 | 0 | } |
507 | 0 | binaryLength += 1; |
508 | 0 | break; |
509 | 0 | case 1: |
510 | 0 | return NS_ERROR_INVALID_ARG; |
511 | 0 | case 0: |
512 | 0 | break; |
513 | 0 | default: |
514 | 0 | MOZ_CRASH("Too many characters leftover"); |
515 | 0 | } |
516 | 0 |
|
517 | 0 | aBinary[binaryLength] = DestT('\0'); |
518 | 0 | *aBinaryLen = binaryLength; |
519 | 0 |
|
520 | 0 | return NS_OK; |
521 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64DecodeHelper<char, char>(char const*, unsigned int, char*, unsigned int*) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64DecodeHelper<char16_t, char16_t>(char16_t const*, unsigned int, char16_t*, unsigned int*) |
522 | | |
523 | | nsresult |
524 | | Base64Decode(const char* aBase64, uint32_t aBase64Len, char** aBinary, |
525 | | uint32_t* aBinaryLen) |
526 | 0 | { |
527 | 0 | // Check for overflow. |
528 | 0 | if (aBase64Len > UINT32_MAX / 3) { |
529 | 0 | return NS_ERROR_FAILURE; |
530 | 0 | } |
531 | 0 | |
532 | 0 | // Don't ask PR_Base64Decode to decode the empty string. |
533 | 0 | if (aBase64Len == 0) { |
534 | 0 | *aBinary = (char*)moz_xmalloc(1); |
535 | 0 | (*aBinary)[0] = '\0'; |
536 | 0 | *aBinaryLen = 0; |
537 | 0 | return NS_OK; |
538 | 0 | } |
539 | 0 | |
540 | 0 | *aBinary = nullptr; |
541 | 0 | *aBinaryLen = (aBase64Len * 3) / 4; |
542 | 0 |
|
543 | 0 | // Add one byte for null termination. |
544 | 0 | UniqueFreePtr<char[]> binary((char*)malloc(*aBinaryLen + 1)); |
545 | 0 | if (!binary) { |
546 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
547 | 0 | } |
548 | 0 | |
549 | 0 | nsresult rv = |
550 | 0 | Base64DecodeHelper(aBase64, aBase64Len, binary.get(), aBinaryLen); |
551 | 0 | if (NS_FAILED(rv)) { |
552 | 0 | return rv; |
553 | 0 | } |
554 | 0 | |
555 | 0 | *aBinary = binary.release(); |
556 | 0 | return NS_OK; |
557 | 0 | } |
558 | | |
559 | | template<typename T> |
560 | | static nsresult |
561 | | Base64DecodeString(const T& aBase64, T& aBinary) |
562 | 0 | { |
563 | 0 | aBinary.Truncate(); |
564 | 0 |
|
565 | 0 | // Check for overflow. |
566 | 0 | if (aBase64.Length() > UINT32_MAX / 3) { |
567 | 0 | return NS_ERROR_FAILURE; |
568 | 0 | } |
569 | 0 | |
570 | 0 | // Don't decode the empty string |
571 | 0 | if (aBase64.IsEmpty()) { |
572 | 0 | return NS_OK; |
573 | 0 | } |
574 | 0 | |
575 | 0 | uint32_t binaryLen = ((aBase64.Length() * 3) / 4); |
576 | 0 |
|
577 | 0 | nsresult rv; |
578 | 0 | auto handle = aBinary.BulkWrite(binaryLen, 0, false, rv); |
579 | 0 | if(NS_FAILED(rv)) { |
580 | 0 | // Must not touch the handle if failing here, but we |
581 | 0 | // already truncated the string at the top, so it's |
582 | 0 | // unchanged. |
583 | 0 | return rv; |
584 | 0 | } |
585 | 0 | |
586 | 0 | rv = Base64DecodeHelper(aBase64.BeginReading(), aBase64.Length(), |
587 | 0 | handle.Elements(), &binaryLen); |
588 | 0 | if (NS_FAILED(rv)) { |
589 | 0 | // Retruncate to match old semantics of this method. |
590 | 0 | handle.Finish(0, true); |
591 | 0 | return rv; |
592 | 0 | } |
593 | 0 | |
594 | 0 | handle.Finish(binaryLen, true); |
595 | 0 | return NS_OK; |
596 | 0 | } Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64DecodeString<nsTSubstring<char> >(nsTSubstring<char> const&, nsTSubstring<char>&) Unexecuted instantiation: Unified_cpp_xpcom_io0.cpp:nsresult mozilla::Base64DecodeString<nsTSubstring<char16_t> >(nsTSubstring<char16_t> const&, nsTSubstring<char16_t>&) |
597 | | |
598 | | nsresult |
599 | | Base64Decode(const nsACString& aBase64, nsACString& aBinary) |
600 | 0 | { |
601 | 0 | return Base64DecodeString(aBase64, aBinary); |
602 | 0 | } |
603 | | |
604 | | nsresult |
605 | | Base64Decode(const nsAString& aBase64, nsAString& aBinary) |
606 | 0 | { |
607 | 0 | return Base64DecodeString(aBase64, aBinary); |
608 | 0 | } |
609 | | |
610 | | nsresult |
611 | | Base64URLDecode(const nsACString& aBase64, |
612 | | Base64URLDecodePaddingPolicy aPaddingPolicy, |
613 | | FallibleTArray<uint8_t>& aBinary) |
614 | 0 | { |
615 | 0 | // Don't decode empty strings. |
616 | 0 | if (aBase64.IsEmpty()) { |
617 | 0 | aBinary.Clear(); |
618 | 0 | return NS_OK; |
619 | 0 | } |
620 | 0 | |
621 | 0 | // Check for overflow. |
622 | 0 | uint32_t base64Len = aBase64.Length(); |
623 | 0 | if (base64Len > UINT32_MAX / 3) { |
624 | 0 | return NS_ERROR_FAILURE; |
625 | 0 | } |
626 | 0 | const char* base64 = aBase64.BeginReading(); |
627 | 0 |
|
628 | 0 | // The decoded length may be 1-2 bytes over, depending on the final quantum. |
629 | 0 | uint32_t binaryLen = (base64Len * 3) / 4; |
630 | 0 |
|
631 | 0 | // Determine whether to check for and ignore trailing padding. |
632 | 0 | bool maybePadded = false; |
633 | 0 | switch (aPaddingPolicy) { |
634 | 0 | case Base64URLDecodePaddingPolicy::Require: |
635 | 0 | if (base64Len % 4) { |
636 | 0 | // Padded input length must be a multiple of 4. |
637 | 0 | return NS_ERROR_INVALID_ARG; |
638 | 0 | } |
639 | 0 | maybePadded = true; |
640 | 0 | break; |
641 | 0 |
|
642 | 0 | case Base64URLDecodePaddingPolicy::Ignore: |
643 | 0 | // Check for padding only if the length is a multiple of 4. |
644 | 0 | maybePadded = !(base64Len % 4); |
645 | 0 | break; |
646 | 0 |
|
647 | 0 | // If we're expecting unpadded input, no need for additional checks. |
648 | 0 | // `=` isn't in the decode table, so padded strings will fail to decode. |
649 | 0 | default: |
650 | 0 | MOZ_FALLTHROUGH_ASSERT("Invalid decode padding policy"); |
651 | 0 | case Base64URLDecodePaddingPolicy::Reject: |
652 | 0 | break; |
653 | 0 | } |
654 | 0 | if (maybePadded && base64[base64Len - 1] == '=') { |
655 | 0 | if (base64[base64Len - 2] == '=') { |
656 | 0 | base64Len -= 2; |
657 | 0 | } else { |
658 | 0 | base64Len -= 1; |
659 | 0 | } |
660 | 0 | } |
661 | 0 |
|
662 | 0 | if (NS_WARN_IF(!aBinary.SetCapacity(binaryLen, mozilla::fallible))) { |
663 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
664 | 0 | } |
665 | 0 | aBinary.SetLengthAndRetainStorage(binaryLen); |
666 | 0 | uint8_t* binary = aBinary.Elements(); |
667 | 0 |
|
668 | 0 | for (; base64Len >= 4; base64Len -= 4) { |
669 | 0 | if (!Decode4to3(base64, binary, Base64URLCharToValue)) { |
670 | 0 | return NS_ERROR_INVALID_ARG; |
671 | 0 | } |
672 | 0 | base64 += 4; |
673 | 0 | binary += 3; |
674 | 0 | } |
675 | 0 |
|
676 | 0 | if (base64Len == 3) { |
677 | 0 | if (!Decode3to2(base64, binary, Base64URLCharToValue)) { |
678 | 0 | return NS_ERROR_INVALID_ARG; |
679 | 0 | } |
680 | 0 | binary += 2; |
681 | 0 | } else if (base64Len == 2) { |
682 | 0 | if (!Decode2to1(base64, binary, Base64URLCharToValue)) { |
683 | 0 | return NS_ERROR_INVALID_ARG; |
684 | 0 | } |
685 | 0 | binary += 1; |
686 | 0 | } else if (base64Len) { |
687 | 0 | return NS_ERROR_INVALID_ARG; |
688 | 0 | } |
689 | 0 | |
690 | 0 | // Set the length to the actual number of decoded bytes. |
691 | 0 | aBinary.TruncateLength(binary - aBinary.Elements()); |
692 | 0 | return NS_OK; |
693 | 0 | } |
694 | | |
695 | | nsresult |
696 | | Base64URLEncode(uint32_t aBinaryLen, const uint8_t* aBinary, |
697 | | Base64URLEncodePaddingPolicy aPaddingPolicy, |
698 | | nsACString& aBase64) |
699 | 0 | { |
700 | 0 | aBase64.Truncate(); |
701 | 0 | // Don't encode empty strings. |
702 | 0 | if (aBinaryLen == 0) { |
703 | 0 | return NS_OK; |
704 | 0 | } |
705 | 0 | |
706 | 0 | // Check for overflow. |
707 | 0 | if (aBinaryLen > (UINT32_MAX / 4) * 3) { |
708 | 0 | return NS_ERROR_FAILURE; |
709 | 0 | } |
710 | 0 | |
711 | 0 | // Allocate a buffer large enough to hold the encoded string with padding. |
712 | 0 | uint32_t base64Len = ((aBinaryLen + 2) / 3) * 4; |
713 | 0 |
|
714 | 0 | nsresult rv; |
715 | 0 | auto handle = aBase64.BulkWrite(base64Len, 0, false, rv); |
716 | 0 | if (NS_FAILED(rv)) { |
717 | 0 | return rv; |
718 | 0 | } |
719 | 0 | |
720 | 0 | char* base64 = handle.Elements(); |
721 | 0 |
|
722 | 0 | uint32_t index = 0; |
723 | 0 | for (; index + 3 <= aBinaryLen; index += 3) { |
724 | 0 | *base64++ = kBase64URLAlphabet[aBinary[index] >> 2]; |
725 | 0 | *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) | |
726 | 0 | (aBinary[index + 1] >> 4)]; |
727 | 0 | *base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2) | |
728 | 0 | (aBinary[index + 2] >> 6)]; |
729 | 0 | *base64++ = kBase64URLAlphabet[aBinary[index + 2] & 0x3f]; |
730 | 0 | } |
731 | 0 |
|
732 | 0 | uint32_t remaining = aBinaryLen - index; |
733 | 0 | if (remaining == 1) { |
734 | 0 | *base64++ = kBase64URLAlphabet[aBinary[index] >> 2]; |
735 | 0 | *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4)]; |
736 | 0 | } else if (remaining == 2) { |
737 | 0 | *base64++ = kBase64URLAlphabet[aBinary[index] >> 2]; |
738 | 0 | *base64++ = kBase64URLAlphabet[((aBinary[index] & 0x3) << 4) | |
739 | 0 | (aBinary[index + 1] >> 4)]; |
740 | 0 | *base64++ = kBase64URLAlphabet[((aBinary[index + 1] & 0xf) << 2)]; |
741 | 0 | } |
742 | 0 |
|
743 | 0 | uint32_t length = base64 - handle.Elements(); |
744 | 0 | if (aPaddingPolicy == Base64URLEncodePaddingPolicy::Include) { |
745 | 0 | if (length % 4 == 2) { |
746 | 0 | *base64++ = '='; |
747 | 0 | *base64++ = '='; |
748 | 0 | length += 2; |
749 | 0 | } else if (length % 4 == 3) { |
750 | 0 | *base64++ = '='; |
751 | 0 | length += 1; |
752 | 0 | } |
753 | 0 | } else { |
754 | 0 | MOZ_ASSERT(aPaddingPolicy == Base64URLEncodePaddingPolicy::Omit, |
755 | 0 | "Invalid encode padding policy"); |
756 | 0 | } |
757 | 0 |
|
758 | 0 | handle.Finish(length, false); |
759 | 0 | return NS_OK; |
760 | 0 | } |
761 | | |
762 | | } // namespace mozilla |