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 char rippleAlphabet[] =
34  "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
35 
36 static char bitcoinAlphabet[] =
37  "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
38 
39 //------------------------------------------------------------------------------
40 
41 template <class Hasher>
42 static typename Hasher::result_type
43 digest(void const* data, std::size_t size) noexcept
44 {
45  Hasher h;
46  h(data, size);
47  return static_cast<typename Hasher::result_type>(h);
48 }
49 
50 template <
51  class Hasher,
52  class T,
53  std::size_t N,
54  class = std::enable_if_t<sizeof(T) == 1>>
55 static typename Hasher::result_type
57 {
58  return digest<Hasher>(v.data(), v.size());
59 }
60 
61 // Computes a double digest (e.g. digest of the digest)
62 template <class Hasher, class... Args>
63 static typename Hasher::result_type
64 digest2(Args const&... args)
65 {
66  return digest<Hasher>(digest<Hasher>(args...));
67 }
68 
69 /* Calculate a 4-byte checksum of the data
70 
71  The checksum is calculated as the first 4 bytes
72  of the SHA256 digest of the message. This is added
73  to the base58 encoding of identifiers to detect
74  user error in data entry.
75 
76  @note This checksum algorithm is part of the client API
77 */
78 void
79 checksum(void* out, void const* message, std::size_t size)
80 {
81  auto const h = digest2<sha256_hasher>(message, size);
82  std::memcpy(out, h.data(), 4);
83 }
84 
85 //------------------------------------------------------------------------------
86 
87 // Code from Bitcoin: https://github.com/bitcoin/bitcoin
88 // Copyright (c) 2014 The Bitcoin Core developers
89 // Distributed under the MIT software license, see the accompanying
90 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
91 //
92 // Modified from the original
93 //
94 // WARNING Do not call this directly, use
95 // encodeBase58Token instead since it
96 // calculates the size of buffer needed.
97 static std::string
99  void const* message,
100  std::size_t size,
101  void* temp,
102  std::size_t temp_size,
103  char const* const alphabet)
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, alphabet[0]);
144  while (iter != b58end)
145  str += alphabet[*(iter++)];
146  return str;
147 }
148 
149 static std::string
151  TokenType type,
152  void const* token,
153  std::size_t size,
154  char const* const alphabet)
155 {
156  // expanded token includes type + 4 byte checksum
157  auto const expanded = 1 + size + 4;
158 
159  // We need expanded + expanded * (log(256) / log(58)) which is
160  // bounded by expanded + expanded * (138 / 100 + 1) which works
161  // out to expanded * 3:
162  auto const bufsize = expanded * 3;
163 
164  boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
165 
166  // Lay the data out as
167  // <type><token><checksum>
168  buf[0] = safe_cast<std::underlying_type_t<TokenType>>(type);
169  if (size)
170  std::memcpy(buf.data() + 1, token, size);
171  checksum(buf.data() + 1 + size, buf.data(), 1 + size);
172 
173  return encodeBase58(
174  buf.data(),
175  expanded,
176  buf.data() + expanded,
177  bufsize - expanded,
178  alphabet);
179 }
180 
182 base58EncodeToken(TokenType type, void const* token, std::size_t size)
183 {
184  return encodeToken(type, token, size, rippleAlphabet);
185 }
186 
188 base58EncodeTokenBitcoin(TokenType type, void const* token, std::size_t size)
189 {
190  return encodeToken(type, token, size, bitcoinAlphabet);
191 }
192 
193 //------------------------------------------------------------------------------
194 
195 // Code from Bitcoin: https://github.com/bitcoin/bitcoin
196 // Copyright (c) 2014 The Bitcoin Core developers
197 // Distributed under the MIT software license, see the accompanying
198 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
199 //
200 // Modified from the original
201 template <class InverseArray>
202 static std::string
203 decodeBase58(std::string const& s, InverseArray const& inv)
204 {
205  auto psz = s.c_str();
206  auto remain = s.size();
207  // Skip and count leading zeroes
208  int zeroes = 0;
209  while (remain > 0 && inv[*psz] == 0)
210  {
211  ++zeroes;
212  ++psz;
213  --remain;
214  }
215 
216  if (remain > 64)
217  return {};
218 
219  // Allocate enough space in big-endian base256 representation.
220  // log(58) / log(256), rounded up.
221  std::vector<unsigned char> b256(remain * 733 / 1000 + 1);
222  while (remain > 0)
223  {
224  auto carry = inv[*psz];
225  if (carry == -1)
226  return {};
227  // Apply "b256 = b256 * 58 + carry".
228  for (auto iter = b256.rbegin(); iter != b256.rend(); ++iter)
229  {
230  carry += 58 * *iter;
231  *iter = carry % 256;
232  carry /= 256;
233  }
234  assert(carry == 0);
235  ++psz;
236  --remain;
237  }
238  // Skip leading zeroes in b256.
239  auto iter = std::find_if(
240  b256.begin(), b256.end(), [](unsigned char c) { return c != 0; });
241  std::string result;
242  result.reserve(zeroes + (b256.end() - iter));
243  result.assign(zeroes, 0x00);
244  while (iter != b256.end())
245  result.push_back(*(iter++));
246  return result;
247 }
248 
249 /* Base58 decode a Ripple token
250 
251  The type and checksum are are checked
252  and removed from the returned result.
253 */
254 template <class InverseArray>
255 static std::string
256 decodeBase58Token(std::string const& s, TokenType type, InverseArray const& inv)
257 {
258  std::string const ret = decodeBase58(s, inv);
259 
260  // Reject zero length tokens
261  if (ret.size() < 6)
262  return {};
263 
264  // The type must match.
265  if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
266  return {};
267 
268  // And the checksum must as well.
269  std::array<char, 4> guard;
270  checksum(guard.data(), ret.data(), ret.size() - guard.size());
271  if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
272  return {};
273 
274  // Skip the leading type byte and the trailing checksum.
275  return ret.substr(1, ret.size() - 1 - guard.size());
276 }
277 
278 //------------------------------------------------------------------------------
279 
280 // Maps characters to their base58 digit
282 {
283 private:
285 
286 public:
287  explicit InverseAlphabet(std::string const& digits)
288  {
289  map_.fill(-1);
290  int i = 0;
291  for (auto const c : digits)
292  map_[static_cast<unsigned char>(c)] = i++;
293  }
294 
295  int
296  operator[](char c) const
297  {
298  return map_[static_cast<unsigned char>(c)];
299  }
300 };
301 
302 static InverseAlphabet rippleInverse(rippleAlphabet);
303 
304 static InverseAlphabet bitcoinInverse(bitcoinAlphabet);
305 
308 {
309  return decodeBase58Token(s, type, rippleInverse);
310 }
311 
314 {
315  return decodeBase58Token(s, type, bitcoinInverse);
316 }
317 
318 } // namespace ripple
ripple::decodeBase58
static std::string decodeBase58(std::string const &s, InverseArray const &inv)
Definition: tokens.cpp:203
ripple::bitcoinInverse
static InverseAlphabet bitcoinInverse(bitcoinAlphabet)
std::string
STL class.
std::equal
T equal(T... args)
ripple::base58EncodeToken
std::string base58EncodeToken(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:182
ripple::rippleInverse
static InverseAlphabet rippleInverse(rippleAlphabet)
utility
cstring
std::string::reserve
T reserve(T... args)
vector
std::find_if
T find_if(T... args)
std::array::size
T size(T... args)
ripple::InverseAlphabet::InverseAlphabet
InverseAlphabet(std::string const &digits)
Definition: tokens.cpp:287
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:43
ripple::InverseAlphabet
Definition: tokens.cpp:281
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
ripple::bitcoinAlphabet
static char bitcoinAlphabet[]
Definition: tokens.cpp:36
memory
ripple::base58EncodeTokenBitcoin
std::string base58EncodeTokenBitcoin(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:188
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:64
ripple::InverseAlphabet::map_
std::array< int, 256 > map_
Definition: tokens.cpp:284
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::rippleAlphabet
static char rippleAlphabet[]
Definition: tokens.cpp:33
ripple::InverseAlphabet::operator[]
int operator[](char c) const
Definition: tokens.cpp:296
ripple::encodeBase58
static std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size, char const *const alphabet)
Definition: tokens.cpp:98
std::vector::begin
T begin(T... args)
ripple::decodeBase58Token
static std::string decodeBase58Token(std::string const &s, TokenType type, InverseArray const &inv)
Definition: tokens.cpp:256
cassert
ripple::checksum
void checksum(void *out, void const *message, std::size_t size)
Definition: tokens.cpp:79
std::string::assign
T assign(T... args)
std::size_t
std::memcpy
T memcpy(T... args)
std::vector::end
T end(T... args)
ripple::decodeBase58TokenBitcoin
std::string decodeBase58TokenBitcoin(std::string const &s, TokenType type)
Decode a Base58 token using Bitcoin alphabet.
Definition: tokens.cpp:313
std::array::data
T data(T... args)
type_traits
ripple::encodeToken
static std::string encodeToken(TokenType type, void const *token, std::size_t size, char const *const alphabet)
Definition: tokens.cpp:150
std::vector::rbegin
T rbegin(T... args)