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