Home | History | Annotate | Download | only in base
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/base64url.h"
      6 
      7 #include <stddef.h>
      8 
      9 #include "base/base64.h"
     10 #include "base/macros.h"
     11 #include "base/numerics/safe_math.h"
     12 #include "base/strings/string_util.h"
     13 
     14 #include <modp_b64/modp_b64.h>
     15 
     16 namespace base {
     17 
     18 const char kPaddingChar = '=';
     19 
     20 // Base64url maps {+, /} to {-, _} in order for the encoded content to be safe
     21 // to use in a URL. These characters will be translated by this implementation.
     22 const char kBase64Chars[] = "+/";
     23 const char kBase64UrlSafeChars[] = "-_";
     24 
     25 void Base64UrlEncode(const StringPiece& input,
     26                      Base64UrlEncodePolicy policy,
     27                      std::string* output) {
     28   Base64Encode(input, output);
     29 
     30   ReplaceChars(*output, "+", "-", output);
     31   ReplaceChars(*output, "/", "_", output);
     32 
     33   switch (policy) {
     34     case Base64UrlEncodePolicy::INCLUDE_PADDING:
     35       // The padding included in |*output| will not be amended.
     36       break;
     37     case Base64UrlEncodePolicy::OMIT_PADDING:
     38       // The padding included in |*output| will be removed.
     39       const size_t last_non_padding_pos =
     40           output->find_last_not_of(kPaddingChar);
     41       if (last_non_padding_pos != std::string::npos)
     42         output->resize(last_non_padding_pos + 1);
     43 
     44       break;
     45   }
     46 }
     47 
     48 bool Base64UrlDecode(const StringPiece& input,
     49                      Base64UrlDecodePolicy policy,
     50                      std::string* output) {
     51   // Characters outside of the base64url alphabet are disallowed, which includes
     52   // the {+, /} characters found in the conventional base64 alphabet.
     53   if (input.find_first_of(kBase64Chars) != std::string::npos)
     54     return false;
     55 
     56   const size_t required_padding_characters = input.size() % 4;
     57   const bool needs_replacement =
     58       input.find_first_of(kBase64UrlSafeChars) != std::string::npos;
     59 
     60   switch (policy) {
     61     case Base64UrlDecodePolicy::REQUIRE_PADDING:
     62       // Fail if the required padding is not included in |input|.
     63       if (required_padding_characters > 0)
     64         return false;
     65       break;
     66     case Base64UrlDecodePolicy::IGNORE_PADDING:
     67       // Missing padding will be silently appended.
     68       break;
     69     case Base64UrlDecodePolicy::DISALLOW_PADDING:
     70       // Fail if padding characters are included in |input|.
     71       if (input.find_first_of(kPaddingChar) != std::string::npos)
     72         return false;
     73       break;
     74   }
     75 
     76   // If the string either needs replacement of URL-safe characters to normal
     77   // base64 ones, or additional padding, a copy of |input| needs to be made in
     78   // order to make these adjustments without side effects.
     79   if (required_padding_characters > 0 || needs_replacement) {
     80     std::string base64_input;
     81 
     82     CheckedNumeric<size_t> base64_input_size = input.size();
     83     if (required_padding_characters > 0)
     84       base64_input_size += 4 - required_padding_characters;
     85 
     86     base64_input.reserve(base64_input_size.ValueOrDie());
     87     input.AppendToString(&base64_input);
     88 
     89     // Substitute the base64url URL-safe characters to their base64 equivalents.
     90     ReplaceChars(base64_input, "-", "+", &base64_input);
     91     ReplaceChars(base64_input, "_", "/", &base64_input);
     92 
     93     // Append the necessary padding characters.
     94     base64_input.resize(base64_input_size.ValueOrDie(), '=');
     95 
     96     return Base64Decode(base64_input, output);
     97   }
     98 
     99   return Base64Decode(input, output);
    100 }
    101 
    102 }  // namespace base
    103