• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
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 <string_view>
10 
11 #include "base/base64.h"
12 #include "base/numerics/safe_math.h"
13 #include "base/strings/string_util.h"
14 #include "third_party/modp_b64/modp_b64.h"
15 
16 namespace base {
17 
18 namespace {
19 
20 const char kPaddingChar = '=';
21 
22 // Base64url maps {+, /} to {-, _} in order for the encoded content to be safe
23 // to use in a URL. These characters will be translated by this implementation.
24 const char kBase64Chars[] = "+/";
25 const char kBase64UrlSafeChars[] = "-_";
26 
27 class StringViewOrString {
28  public:
StringViewOrString(std::string_view piece)29   explicit StringViewOrString(std::string_view piece) : piece_(piece) {}
StringViewOrString(std::string str)30   explicit StringViewOrString(std::string str) : str_(std::move(str)) {}
31 
get() const32   std::string_view get() const {
33     if (str_) {
34       return *str_;
35     }
36     return piece_;
37   }
38 
39  private:
40   const std::optional<std::string> str_;
41   const std::string_view piece_;
42 };
43 
44 // Converts the base64url `input` into a plain base64 string.
Base64UrlToBase64(std::string_view input,Base64UrlDecodePolicy policy)45 std::optional<StringViewOrString> Base64UrlToBase64(
46     std::string_view input,
47     Base64UrlDecodePolicy policy) {
48   // Characters outside of the base64url alphabet are disallowed, which includes
49   // the {+, /} characters found in the conventional base64 alphabet.
50   if (input.find_first_of(kBase64Chars) != std::string::npos)
51     return std::nullopt;
52 
53   const size_t required_padding_characters = input.size() % 4;
54   const bool needs_replacement =
55       input.find_first_of(kBase64UrlSafeChars) != std::string::npos;
56 
57   switch (policy) {
58     case Base64UrlDecodePolicy::REQUIRE_PADDING:
59       // Fail if the required padding is not included in |input|.
60       if (required_padding_characters > 0)
61         return std::nullopt;
62       break;
63     case Base64UrlDecodePolicy::IGNORE_PADDING:
64       // Missing padding will be silently appended.
65       break;
66     case Base64UrlDecodePolicy::DISALLOW_PADDING:
67       // Fail if padding characters are included in |input|.
68       if (input.find_first_of(kPaddingChar) != std::string::npos)
69         return std::nullopt;
70       break;
71   }
72 
73   if (required_padding_characters == 0 && !needs_replacement) {
74     return StringViewOrString(input);
75   }
76 
77   // If the string either needs replacement of URL-safe characters to normal
78   // base64 ones, or additional padding, a copy of |input| needs to be made in
79   // order to make these adjustments without side effects.
80   CheckedNumeric<size_t> out_size = input.size();
81   if (required_padding_characters > 0) {
82     out_size += 4 - required_padding_characters;
83   }
84 
85   std::string base64_input;
86   base64_input.reserve(out_size.ValueOrDie());
87   base64_input.append(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(out_size.ValueOrDie(), '=');
95 
96   return StringViewOrString(std::move(base64_input));
97 }
98 
99 }  // namespace
100 
Base64UrlEncode(span<const uint8_t> input,Base64UrlEncodePolicy policy,std::string * output)101 void Base64UrlEncode(span<const uint8_t> input,
102                      Base64UrlEncodePolicy policy,
103                      std::string* output) {
104   *output = Base64Encode(input);
105 
106   ReplaceChars(*output, "+", "-", output);
107   ReplaceChars(*output, "/", "_", output);
108 
109   switch (policy) {
110     case Base64UrlEncodePolicy::INCLUDE_PADDING:
111       // The padding included in |*output| will not be amended.
112       break;
113     case Base64UrlEncodePolicy::OMIT_PADDING:
114       // The padding included in |*output| will be removed.
115       const size_t last_non_padding_pos =
116           output->find_last_not_of(kPaddingChar);
117       if (last_non_padding_pos != std::string::npos) {
118         output->resize(last_non_padding_pos + 1);
119       }
120 
121       break;
122   }
123 }
124 
Base64UrlEncode(std::string_view input,Base64UrlEncodePolicy policy,std::string * output)125 void Base64UrlEncode(std::string_view input,
126                      Base64UrlEncodePolicy policy,
127                      std::string* output) {
128   Base64UrlEncode(base::as_byte_span(input), policy, output);
129 }
130 
Base64UrlDecode(std::string_view input,Base64UrlDecodePolicy policy,std::string * output)131 bool Base64UrlDecode(std::string_view input,
132                      Base64UrlDecodePolicy policy,
133                      std::string* output) {
134   std::optional<StringViewOrString> base64_input =
135       Base64UrlToBase64(input, policy);
136   if (!base64_input) {
137     return false;
138   }
139   return Base64Decode(base64_input->get(), output);
140 }
141 
Base64UrlDecode(std::string_view input,Base64UrlDecodePolicy policy)142 std::optional<std::vector<uint8_t>> Base64UrlDecode(
143     std::string_view input,
144     Base64UrlDecodePolicy policy) {
145   std::optional<StringViewOrString> base64_input =
146       Base64UrlToBase64(input, policy);
147   if (!base64_input) {
148     return std::nullopt;
149   }
150   return Base64Decode(base64_input->get());
151 }
152 
153 }  // namespace base
154