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 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(). 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 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 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 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 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 113 std::string Base64Encode(const void* data, size_t size) { 114 return Base64EncodeHelper(data, size); 115 } 116 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 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