rippled
tokens.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/safe_cast.h>
21 #include <ripple/protocol/digest.h>
22 #include <ripple/protocol/tokens.h>
23 #include <boost/container/small_vector.hpp>
24 #include <cassert>
25 #include <cstring>
26 #include <memory>
27 #include <type_traits>
28 #include <utility>
29 #include <vector>
30 
31 namespace ripple {
32 
33 static constexpr char const* alphabetForward =
34  "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
35 
36 static constexpr std::array<int, 256> const alphabetReverse = []() {
38  for (auto& m : map)
39  m = -1;
40  for (int i = 0, j = 0; alphabetForward[i] != 0; ++i)
41  map[static_cast<unsigned char>(alphabetForward[i])] = j++;
42  return map;
43 }();
44 
45 template <class Hasher>
46 static typename Hasher::result_type
47 digest(void const* data, std::size_t size) noexcept
48 {
49  Hasher h;
50  h(data, size);
51  return static_cast<typename Hasher::result_type>(h);
52 }
53 
54 template <
55  class Hasher,
56  class T,
57  std::size_t N,
58  class = std::enable_if_t<sizeof(T) == 1>>
59 static typename Hasher::result_type
61 {
62  return digest<Hasher>(v.data(), v.size());
63 }
64 
65 // Computes a double digest (e.g. digest of the digest)
66 template <class Hasher, class... Args>
67 static typename Hasher::result_type
68 digest2(Args const&... args)
69 {
70  return digest<Hasher>(digest<Hasher>(args...));
71 }
72 
82 static void
83 checksum(void* out, void const* message, std::size_t size)
84 {
85  auto const h = digest2<sha256_hasher>(message, size);
86  std::memcpy(out, h.data(), 4);
87 }
88 
89 namespace detail {
90 
91 /* The base58 encoding & decoding routines in this namespace are taken from
92  * Bitcoin but have been modified from the original.
93  *
94  * Copyright (c) 2014 The Bitcoin Core developers
95  * Distributed under the MIT software license, see the accompanying
96  * file COPYING or http://www.opensource.org/licenses/mit-license.php.
97  */
98 static std::string
100  void const* message,
101  std::size_t size,
102  void* temp,
103  std::size_t temp_size)
104 {
105  auto pbegin = reinterpret_cast<unsigned char const*>(message);
106  auto const pend = pbegin + size;
107 
108  // Skip & count leading zeroes.
109  int zeroes = 0;
110  while (pbegin != pend && *pbegin == 0)
111  {
112  pbegin++;
113  zeroes++;
114  }
115 
116  auto const b58begin = reinterpret_cast<unsigned char*>(temp);
117  auto const b58end = b58begin + temp_size;
118 
119  std::fill(b58begin, b58end, 0);
120 
121  while (pbegin != pend)
122  {
123  int carry = *pbegin;
124  // Apply "b58 = b58 * 256 + ch".
125  for (auto iter = b58end; iter != b58begin; --iter)
126  {
127  carry += 256 * (iter[-1]);
128  iter[-1] = carry % 58;
129  carry /= 58;
130  }
131  assert(carry == 0);
132  pbegin++;
133  }
134 
135  // Skip leading zeroes in base58 result.
136  auto iter = b58begin;
137  while (iter != b58end && *iter == 0)
138  ++iter;
139 
140  // Translate the result into a string.
141  std::string str;
142  str.reserve(zeroes + (b58end - iter));
143  str.assign(zeroes, alphabetForward[0]);
144  while (iter != b58end)
145  str += alphabetForward[*(iter++)];
146  return str;
147 }
148 
149 static std::string
151 {
152  auto psz = reinterpret_cast<unsigned char const*>(s.c_str());
153  auto remain = s.size();
154  // Skip and count leading zeroes
155  int zeroes = 0;
156  while (remain > 0 && alphabetReverse[*psz] == 0)
157  {
158  ++zeroes;
159  ++psz;
160  --remain;
161  }
162 
163  if (remain > 64)
164  return {};
165 
166  // Allocate enough space in big-endian base256 representation.
167  // log(58) / log(256), rounded up.
168  std::vector<unsigned char> b256(remain * 733 / 1000 + 1);
169  while (remain > 0)
170  {
171  auto carry = alphabetReverse[*psz];
172  if (carry == -1)
173  return {};
174  // Apply "b256 = b256 * 58 + carry".
175  for (auto iter = b256.rbegin(); iter != b256.rend(); ++iter)
176  {
177  carry += 58 * *iter;
178  *iter = carry % 256;
179  carry /= 256;
180  }
181  assert(carry == 0);
182  ++psz;
183  --remain;
184  }
185  // Skip leading zeroes in b256.
186  auto iter = std::find_if(
187  b256.begin(), b256.end(), [](unsigned char c) { return c != 0; });
188  std::string result;
189  result.reserve(zeroes + (b256.end() - iter));
190  result.assign(zeroes, 0x00);
191  while (iter != b256.end())
192  result.push_back(*(iter++));
193  return result;
194 }
195 
196 } // namespace detail
197 
199 encodeBase58Token(TokenType type, void const* token, std::size_t size)
200 {
201  // expanded token includes type + 4 byte checksum
202  auto const expanded = 1 + size + 4;
203 
204  // We need expanded + expanded * (log(256) / log(58)) which is
205  // bounded by expanded + expanded * (138 / 100 + 1) which works
206  // out to expanded * 3:
207  auto const bufsize = expanded * 3;
208 
209  boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
210 
211  // Lay the data out as
212  // <type><token><checksum>
213  buf[0] = safe_cast<std::underlying_type_t<TokenType>>(type);
214  if (size)
215  std::memcpy(buf.data() + 1, token, size);
216  checksum(buf.data() + 1 + size, buf.data(), 1 + size);
217 
218  return detail::encodeBase58(
219  buf.data(), expanded, buf.data() + expanded, bufsize - expanded);
220 }
221 
224 {
225  std::string const ret = detail::decodeBase58(s);
226 
227  // Reject zero length tokens
228  if (ret.size() < 6)
229  return {};
230 
231  // The type must match.
232  if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
233  return {};
234 
235  // And the checksum must as well.
236  std::array<char, 4> guard;
237  checksum(guard.data(), ret.data(), ret.size() - guard.size());
238  if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
239  return {};
240 
241  // Skip the leading type byte and the trailing checksum.
242  return ret.substr(1, ret.size() - 1 - guard.size());
243 }
244 
245 } // namespace ripple
std::string
STL class.
std::equal
T equal(T... args)
utility
cstring
ripple::detail::decodeBase58
static std::string decodeBase58(std::string const &s)
Definition: tokens.cpp:150
ripple::alphabetForward
static constexpr char const * alphabetForward
Definition: tokens.cpp:33
std::string::reserve
T reserve(T... args)
vector
std::find_if
T find_if(T... args)
std::array::size
T size(T... args)
ripple::checksum
static void checksum(void *out, void const *message, std::size_t size)
Calculate a 4-byte checksum of the data.
Definition: tokens.cpp:83
ripple::decodeBase58Token
std::string decodeBase58Token(std::string const &s, TokenType type)
Decode a token of given type encoded using Base58Check and the XRPL alphabet.
Definition: tokens.cpp:223
std::fill
T fill(T... args)
std::string::push_back
T push_back(T... args)
ripple::digest
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition: tokens.cpp:47
ripple::QualityDirection::out
@ out
ripple::TokenType
TokenType
Definition: tokens.h:29
std::enable_if_t
std::string::c_str
T c_str(T... args)
std::array
STL class.
std::uint8_t
memory
std::vector::rend
T rend(T... args)
std::string::substr
T substr(T... args)
ripple::digest2
static Hasher::result_type digest2(Args const &... args)
Definition: tokens.cpp:68
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::encodeBase58Token
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Encode data in Base58Check format using XRPL alphabet.
Definition: tokens.cpp:199
std::vector::begin
T begin(T... args)
cassert
std::string::assign
T assign(T... args)
std::size_t
std::memcpy
T memcpy(T... args)
std::vector::end
T end(T... args)
std::array::data
T data(T... args)
type_traits
std::vector::rbegin
T rbegin(T... args)
ripple::detail::encodeBase58
static std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition: tokens.cpp:99
ripple::alphabetReverse
static constexpr const std::array< int, 256 > alphabetReverse
Definition: tokens.cpp:36