1 // Copyright 2014 The Chromium OS 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 <brillo/data_encoding.h>
6 #include <modp_b64/modp_b64.h>
7
8 #include <memory>
9
10 #include <base/logging.h>
11 #include <base/strings/string_util.h>
12 #include <base/strings/stringprintf.h>
13 #include <brillo/strings/string_utils.h>
14
15 namespace {
16
HexToDec(int hex)17 inline int HexToDec(int hex) {
18 int dec = -1;
19 if (hex >= '0' && hex <= '9') {
20 dec = hex - '0';
21 } else if (hex >= 'A' && hex <= 'F') {
22 dec = hex - 'A' + 10;
23 } else if (hex >= 'a' && hex <= 'f') {
24 dec = hex - 'a' + 10;
25 }
26 return dec;
27 }
28
29 // Helper for Base64Encode() and Base64EncodeWrapLines().
Base64EncodeHelper(const void * data,size_t size)30 std::string Base64EncodeHelper(const void* data, size_t size) {
31 std::vector<char> buffer;
32 buffer.resize(modp_b64_encode_len(size));
33 size_t out_size = modp_b64_encode(buffer.data(),
34 static_cast<const char*>(data),
35 size);
36 return std::string{buffer.begin(), buffer.begin() + out_size};
37 }
38
39 } // namespace
40
41 /////////////////////////////////////////////////////////////////////////
42 namespace brillo {
43 namespace data_encoding {
44
UrlEncode(const char * data,bool encodeSpaceAsPlus)45 std::string UrlEncode(const char* data, bool encodeSpaceAsPlus) {
46 std::string result;
47
48 while (*data) {
49 char c = *data++;
50 // According to RFC3986 (http://www.faqs.org/rfcs/rfc3986.html),
51 // section 2.3. - Unreserved Characters
52 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
53 (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' ||
54 c == '~') {
55 result += c;
56 } else if (c == ' ' && encodeSpaceAsPlus) {
57 // For historical reasons, some URLs have spaces encoded as '+',
58 // this also applies to form data encoded as
59 // 'application/x-www-form-urlencoded'
60 result += '+';
61 } else {
62 base::StringAppendF(&result,
63 "%%%02X",
64 static_cast<unsigned char>(c)); // Encode as %NN
65 }
66 }
67 return result;
68 }
69
UrlDecode(const char * data)70 std::string UrlDecode(const char* data) {
71 std::string result;
72 while (*data) {
73 char c = *data++;
74 int part1 = 0, part2 = 0;
75 // HexToDec would return -1 even for character 0 (end of string),
76 // so it is safe to access data[0] and data[1] without overrunning the buf.
77 if (c == '%' && (part1 = HexToDec(data[0])) >= 0 &&
78 (part2 = HexToDec(data[1])) >= 0) {
79 c = static_cast<char>((part1 << 4) | part2);
80 data += 2;
81 } else if (c == '+') {
82 c = ' ';
83 }
84 result += c;
85 }
86 return result;
87 }
88
WebParamsEncode(const WebParamList & params,bool encodeSpaceAsPlus)89 std::string WebParamsEncode(const WebParamList& params,
90 bool encodeSpaceAsPlus) {
91 std::vector<std::string> pairs;
92 pairs.reserve(params.size());
93 for (const auto& p : params) {
94 std::string key = UrlEncode(p.first.c_str(), encodeSpaceAsPlus);
95 std::string value = UrlEncode(p.second.c_str(), encodeSpaceAsPlus);
96 pairs.push_back(brillo::string_utils::Join("=", key, value));
97 }
98
99 return brillo::string_utils::Join("&", pairs);
100 }
101
WebParamsDecode(const std::string & data)102 WebParamList WebParamsDecode(const std::string& data) {
103 WebParamList result;
104 std::vector<std::string> params = brillo::string_utils::Split(data, "&");
105 for (const auto& p : params) {
106 auto pair = brillo::string_utils::SplitAtFirst(p, "=");
107 result.emplace_back(UrlDecode(pair.first.c_str()),
108 UrlDecode(pair.second.c_str()));
109 }
110 return result;
111 }
112
Base64Encode(const void * data,size_t size)113 std::string Base64Encode(const void* data, size_t size) {
114 return Base64EncodeHelper(data, size);
115 }
116
Base64EncodeWrapLines(const void * data,size_t size)117 std::string Base64EncodeWrapLines(const void* data, size_t size) {
118 std::string unwrapped = Base64EncodeHelper(data, size);
119 std::string wrapped;
120
121 for (size_t i = 0; i < unwrapped.size(); i += 64) {
122 wrapped.append(unwrapped, i, 64);
123 wrapped.append("\n");
124 }
125 return wrapped;
126 }
127
Base64Decode(const std::string & input,brillo::Blob * output)128 bool Base64Decode(const std::string& input, brillo::Blob* output) {
129 std::string temp_buffer;
130 const std::string* data = &input;
131 if (input.find_first_of("\r\n") != std::string::npos) {
132 base::ReplaceChars(input, "\n", "", &temp_buffer);
133 base::ReplaceChars(temp_buffer, "\r", "", &temp_buffer);
134 data = &temp_buffer;
135 }
136 // base64 decoded data has 25% fewer bytes than the original (since every
137 // 3 source octets are encoded as 4 characters in base64).
138 // modp_b64_decode_len provides an upper estimate of the size of the output
139 // data.
140 output->resize(modp_b64_decode_len(data->size()));
141
142 size_t size_read = modp_b64_decode(reinterpret_cast<char*>(output->data()),
143 data->data(), data->size());
144 if (size_read == MODP_B64_ERROR) {
145 output->resize(0);
146 return false;
147 }
148 output->resize(size_read);
149
150 return true;
151 }
152
153 } // namespace data_encoding
154 } // namespace brillo
155