Coverage Report

Created: 2018-09-25 14:53

/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