LCOV - code coverage report
Current view: top level - xpcom/io - Base64.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 53 306 17.3 %
Date: 2017-07-14 16:53:18 Functions: 4 26 15.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13