xpcom/io/Base64.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "Base64.h"
     8 #include "nsIInputStream.h"
     9 #include "nsString.h"
    11 #include "plbase64.h"
    13 namespace {
    15 // BEGIN base64 encode code copied and modified from NSPR
    16 const unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    18 template <typename T>
    19 static void
    20 Encode3to4(const unsigned char *src, T *dest)
    21 {
    22     uint32_t b32 = (uint32_t)0;
    23     int i, j = 18;
    25     for( i = 0; i < 3; i++ )
    26     {
    27         b32 <<= 8;
    28         b32 |= (uint32_t)src[i];
    29     }
    31     for( i = 0; i < 4; i++ )
    32     {
    33         dest[i] = base[ (uint32_t)((b32>>j) & 0x3F) ];
    34         j -= 6;
    35     }
    36 }
    38 template <typename T>
    39 static void
    40 Encode2to4(const unsigned char *src, T *dest)
    41 {
    42     dest[0] = base[ (uint32_t)((src[0]>>2) & 0x3F) ];
    43     dest[1] = base[ (uint32_t)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
    44     dest[2] = base[ (uint32_t)((src[1] & 0x0F) << 2) ];
    45     dest[3] = (unsigned char)'=';
    46 }
    48 template <typename T>
    49 static void
    50 Encode1to4(const unsigned char *src, T *dest)
    51 {
    52     dest[0] = base[ (uint32_t)((src[0]>>2) & 0x3F) ];
    53     dest[1] = base[ (uint32_t)((src[0] & 0x03) << 4) ];
    54     dest[2] = (unsigned char)'=';
    55     dest[3] = (unsigned char)'=';
    56 }
    58 template <typename T>
    59 static void
    60 Encode(const unsigned char *src, uint32_t srclen, T *dest)
    61 {
    62     while( srclen >= 3 )
    63     {
    64         Encode3to4(src, dest);
    65         src += 3;
    66         dest += 4;
    67         srclen -= 3;
    68     }
    70     switch( srclen )
    71     {
    72         case 2:
    73             Encode2to4(src, dest);
    74             break;
    75         case 1:
    76             Encode1to4(src, dest);
    77             break;
    78         case 0:
    79             break;
    80         default:
    81             NS_NOTREACHED("coding error");
    82     }
    83 }
    85 // END base64 encode code copied and modified from NSPR.
    87 template <typename T>
    88 struct EncodeInputStream_State {
    89   unsigned char c[3];
    90   uint8_t charsOnStack;
    91   typename T::char_type* buffer;
    92 };
    94 template <typename T>
    95 NS_METHOD
    96 EncodeInputStream_Encoder(nsIInputStream *aStream,
    97                           void *aClosure,
    98                           const char *aFromSegment,
    99                           uint32_t aToOffset,
   100                           uint32_t aCount,
   101                           uint32_t *aWriteCount)
   102 {
   103   NS_ASSERTION(aCount > 0, "Er, what?");
   105   EncodeInputStream_State<T>* state =
   106     static_cast<EncodeInputStream_State<T>*>(aClosure);
   108   // If we have any data left from last time, encode it now.
   109   uint32_t countRemaining = aCount;
   110   const unsigned char *src = (const unsigned char*)aFromSegment;
   111   if (state->charsOnStack) {
   112     unsigned char firstSet[4];
   113     if (state->charsOnStack == 1) {
   114       firstSet[0] = state->c[0];
   115       firstSet[1] = src[0];
   116       firstSet[2] = (countRemaining > 1) ? src[1] : '\0';
   117       firstSet[3] = '\0';
   118     } else /* state->charsOnStack == 2 */ {
   119       firstSet[0] = state->c[0];
   120       firstSet[1] = state->c[1];
   121       firstSet[2] = src[0];
   122       firstSet[3] = '\0';
   123     }
   124     Encode(firstSet, 3, state->buffer);
   125     state->buffer += 4;
   126     countRemaining -= (3 - state->charsOnStack);
   127     src += (3 - state->charsOnStack);
   128     state->charsOnStack = 0;
   129   }
   131   // Encode the bulk of the 
   132   uint32_t encodeLength = countRemaining - countRemaining % 3;
   133   NS_ABORT_IF_FALSE(encodeLength % 3 == 0,
   134                     "Should have an exact number of triplets!");
   135   Encode(src, encodeLength, state->buffer);
   136   state->buffer += (encodeLength / 3) * 4;
   137   src += encodeLength;
   138   countRemaining -= encodeLength;
   140   // We must consume all data, so if there's some data left stash it
   141   *aWriteCount = aCount;
   143   if (countRemaining) {
   144     // We should never have a full triplet left at this point.
   145     NS_ABORT_IF_FALSE(countRemaining < 3, "We should have encoded more!");
   146     state->c[0] = src[0];
   147     state->c[1] = (countRemaining == 2) ? src[1] : '\0';
   148     state->charsOnStack = countRemaining;
   149   }
   151   return NS_OK;
   152 }
   154 template <typename T>
   155 nsresult
   156 EncodeInputStream(nsIInputStream *aInputStream, 
   157                   T &aDest,
   158                   uint32_t aCount,
   159                   uint32_t aOffset)
   160 {
   161   nsresult rv;
   162   uint64_t count64 = aCount;
   164   if (!aCount) {
   165     rv = aInputStream->Available(&count64);
   166     if (NS_WARN_IF(NS_FAILED(rv)))
   167       return rv;
   168     // if count64 is over 4GB, it will be failed at the below condition,
   169     // then will return NS_ERROR_OUT_OF_MEMORY
   170     aCount = (uint32_t)count64;
   171   }
   173   uint64_t countlong =
   174     (count64 + 2) / 3 * 4; // +2 due to integer math.
   175   if (countlong + aOffset > UINT32_MAX)
   176     return NS_ERROR_OUT_OF_MEMORY;
   178   uint32_t count = uint32_t(countlong);
   180   aDest.SetLength(count + aOffset);
   181   if (aDest.Length() != count + aOffset)
   182     return NS_ERROR_OUT_OF_MEMORY;
   184   EncodeInputStream_State<T> state;
   185   state.charsOnStack = 0;
   186   state.c[2] = '\0';
   187   state.buffer = aOffset + aDest.BeginWriting();
   189   while (1) {
   190     uint32_t read = 0;
   192     rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>,
   193                                     (void*)&state,
   194                                     aCount,
   195                                     &read);
   196     if (NS_FAILED(rv)) {
   197       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
   198         NS_RUNTIMEABORT("Not implemented for async streams!");
   199       if (rv == NS_ERROR_NOT_IMPLEMENTED)
   200         NS_RUNTIMEABORT("Requires a stream that implements ReadSegments!");
   201       return rv;
   202     }
   204     if (!read)
   205       break;
   206   }
   208   // Finish encoding if anything is left
   209   if (state.charsOnStack)
   210     Encode(state.c, state.charsOnStack, state.buffer);
   212   if (aDest.Length())
   213     // May belong to an nsCString with an unallocated buffer, so only null
   214     // terminate if there is a need to.
   215     *aDest.EndWriting() = '\0';
   217   return NS_OK;
   218 }
   220 } // namespace (anonymous)
   222 namespace mozilla {
   224 nsresult
   225 Base64EncodeInputStream(nsIInputStream *aInputStream, 
   226                         nsACString &aDest,
   227                         uint32_t aCount,
   228                         uint32_t aOffset)
   229 {
   230   return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset);
   231 }
   233 nsresult
   234 Base64EncodeInputStream(nsIInputStream *aInputStream, 
   235                         nsAString &aDest,
   236                         uint32_t aCount,
   237                         uint32_t aOffset)
   238 {
   239   return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset);
   240 }
   242 nsresult
   243 Base64Encode(const nsACString &aBinaryData, nsACString &aString)
   244 {
   245   // Check for overflow.
   246   if (aBinaryData.Length() > (UINT32_MAX / 4) * 3) {
   247     return NS_ERROR_FAILURE;
   248   }
   250   // Don't ask PR_Base64Encode to encode empty strings
   251   if (aBinaryData.IsEmpty()) {
   252     aString.Truncate();
   253     return NS_OK;
   254   }
   256   uint32_t stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
   258   char *buffer;
   260   // Add one byte for null termination.
   261   if (aString.SetCapacity(stringLen + 1, fallible_t()) &&
   262     (buffer = aString.BeginWriting()) &&
   263     PL_Base64Encode(aBinaryData.BeginReading(), aBinaryData.Length(), buffer)) {
   264     // PL_Base64Encode doesn't null terminate the buffer for us when we pass
   265     // the buffer in. Do that manually.
   266     buffer[stringLen] = '\0';
   268     aString.SetLength(stringLen);
   269     return NS_OK;
   270   }
   272   aString.Truncate();
   273   return NS_ERROR_INVALID_ARG;
   274 }
   276 nsresult
   277 Base64Encode(const nsAString &aString, nsAString &aBinaryData)
   278 {
   279   NS_LossyConvertUTF16toASCII string(aString);
   280   nsAutoCString binaryData;
   282   nsresult rv = Base64Encode(string, binaryData);
   283   if (NS_SUCCEEDED(rv)) {
   284     CopyASCIItoUTF16(binaryData, aBinaryData);
   285   } else {
   286     aBinaryData.Truncate();
   287   }
   289   return rv;
   290 }
   292 nsresult
   293 Base64Decode(const nsACString &aString, nsACString &aBinaryData)
   294 {
   295   // Check for overflow.
   296   if (aString.Length() > UINT32_MAX / 3) {
   297     return NS_ERROR_FAILURE;
   298   }
   300   // Don't ask PR_Base64Decode to decode the empty string
   301   if (aString.IsEmpty()) {
   302     aBinaryData.Truncate();
   303     return NS_OK;
   304   }
   306   uint32_t binaryDataLen = ((aString.Length() * 3) / 4);
   308   char *buffer;
   310   // Add one byte for null termination.
   311   if (aBinaryData.SetCapacity(binaryDataLen + 1, fallible_t()) &&
   312     (buffer = aBinaryData.BeginWriting()) &&
   313     PL_Base64Decode(aString.BeginReading(), aString.Length(), buffer)) {
   314     // PL_Base64Decode doesn't null terminate the buffer for us when we pass
   315     // the buffer in. Do that manually, taking into account the number of '='
   316     // characters we were passed.
   317     if (!aString.IsEmpty() && aString[aString.Length() - 1] == '=') {
   318       if (aString.Length() > 1 && aString[aString.Length() - 2] == '=') {
   319         binaryDataLen -= 2;
   320       } else {
   321         binaryDataLen -= 1;
   322       }
   323     }
   324     buffer[binaryDataLen] = '\0';
   326     aBinaryData.SetLength(binaryDataLen);
   327     return NS_OK;
   328   }
   330   aBinaryData.Truncate();
   331   return NS_ERROR_INVALID_ARG;
   332 }
   334 nsresult
   335 Base64Decode(const nsAString &aBinaryData, nsAString &aString)
   336 {
   337   NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
   338   nsAutoCString string;
   340   nsresult rv = Base64Decode(binaryData, string);
   341   if (NS_SUCCEEDED(rv)) {
   342     CopyASCIItoUTF16(string, aString);
   343   } else {
   344     aString.Truncate();
   345   }
   347   return rv;
   348 }
   350 } // namespace mozilla

mercurial