Home | History | Annotate | Download | only in brillo
      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