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/tokens.h>
22 #include <ripple/protocol/digest.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
43 typename Hasher::result_type
44 digest (void const* data, std::size_t size) noexcept
45 {
46  Hasher h;
47  h(data, size);
48  return static_cast<
49  typename Hasher::result_type>(h);
50 }
51 
52 template <class Hasher, class T, std::size_t N,
53  class = std::enable_if_t<sizeof(T) == 1>>
54 static
55 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
64 typename Hasher::result_type
65 digest2 (Args const&... args)
66 {
67  return digest<Hasher>(
68  digest<Hasher>(args...));
69 }
70 
71 /* Calculate a 4-byte checksum of the data
72 
73  The checksum is calculated as the first 4 bytes
74  of the SHA256 digest of the message. This is added
75  to the base58 encoding of identifiers to detect
76  user error in data entry.
77 
78  @note This checksum algorithm is part of the client API
79 */
80 void
81 checksum (void* out,
82  void const* message,
83  std::size_t size)
84 {
85  auto const h =
86  digest2<sha256_hasher>(message, size);
87  std::memcpy(out, h.data(), 4);
88 }
89 
90 //------------------------------------------------------------------------------
91 
92 // Code from Bitcoin: https://github.com/bitcoin/bitcoin
93 // Copyright (c) 2014 The Bitcoin Core developers
94 // Distributed under the MIT software license, see the accompanying
95 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
96 //
97 // Modified from the original
98 //
99 // WARNING Do not call this directly, use
100 // encodeBase58Token instead since it
101 // calculates the size of buffer needed.
102 static
105  void const* message, std::size_t size,
106  void *temp, std::size_t temp_size,
107  char const* const alphabet)
108 {
109  auto pbegin = reinterpret_cast<unsigned char const*>(message);
110  auto const pend = pbegin + size;
111 
112  // Skip & count leading zeroes.
113  int zeroes = 0;
114  while (pbegin != pend && *pbegin == 0)
115  {
116  pbegin++;
117  zeroes++;
118  }
119 
120  auto const b58begin = reinterpret_cast<unsigned char*>(temp);
121  auto const b58end = b58begin + temp_size;
122 
123  std::fill(b58begin, b58end, 0);
124 
125  while (pbegin != pend)
126  {
127  int carry = *pbegin;
128  // Apply "b58 = b58 * 256 + ch".
129  for (auto iter = b58end; iter != b58begin; --iter)
130  {
131  carry += 256 * (iter[-1]);
132  iter[-1] = carry % 58;
133  carry /= 58;
134  }
135  assert(carry == 0);
136  pbegin++;
137  }
138 
139  // Skip leading zeroes in base58 result.
140  auto iter = b58begin;
141  while (iter != b58end && *iter == 0)
142  ++iter;
143 
144  // Translate the result into a string.
145  std::string str;
146  str.reserve(zeroes + (b58end - iter));
147  str.assign(zeroes, alphabet[0]);
148  while (iter != b58end)
149  str += alphabet[*(iter++)];
150  return str;
151 }
152 
153 static
156  void const* token, std::size_t size, char const* const alphabet)
157 {
158  // expanded token includes type + 4 byte checksum
159  auto const expanded = 1 + size + 4;
160 
161  // We need expanded + expanded * (log(256) / log(58)) which is
162  // bounded by expanded + expanded * (138 / 100 + 1) which works
163  // out to expanded * 3:
164  auto const bufsize = expanded * 3;
165 
166  boost::container::small_vector<std::uint8_t, 1024> buf (bufsize);
167 
168  // Lay the data out as
169  // <type><token><checksum>
170  buf[0] = safe_cast<std::underlying_type_t <TokenType>>(type);
171  if (size)
172  std::memcpy(buf.data() + 1, token, size);
173  checksum(buf.data() + 1 + size, buf.data(), 1 + size);
174 
175  return encodeBase58(
176  buf.data(), expanded,
177  buf.data() + expanded, bufsize - expanded,
178  alphabet);
179 }
180 
183  void const* token, std::size_t size)
184 {
185  return encodeToken(type, token, size, rippleAlphabet);
186 }
187 
190  void const* token, std::size_t size)
191 {
192  return encodeToken(type, token, size, bitcoinAlphabet);
193 }
194 
195 //------------------------------------------------------------------------------
196 
197 // Code from Bitcoin: https://github.com/bitcoin/bitcoin
198 // Copyright (c) 2014 The Bitcoin Core developers
199 // Distributed under the MIT software license, see the accompanying
200 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
201 //
202 // Modified from the original
203 template <class InverseArray>
204 static
207  InverseArray const& inv)
208 {
209  auto psz = s.c_str();
210  auto remain = s.size();
211  // Skip and count leading zeroes
212  int zeroes = 0;
213  while (remain > 0 && inv[*psz] == 0)
214  {
215  ++zeroes;
216  ++psz;
217  --remain;
218  }
219 
220  if (remain > 64)
221  return {};
222 
223  // Allocate enough space in big-endian base256 representation.
224  // log(58) / log(256), rounded up.
226  remain * 733 / 1000 + 1);
227  while (remain > 0)
228  {
229  auto carry = inv[*psz];
230  if (carry == -1)
231  return {};
232  // Apply "b256 = b256 * 58 + carry".
233  for (auto iter = b256.rbegin();
234  iter != b256.rend(); ++iter)
235  {
236  carry += 58 * *iter;
237  *iter = carry % 256;
238  carry /= 256;
239  }
240  assert(carry == 0);
241  ++psz;
242  --remain;
243  }
244  // Skip leading zeroes in b256.
245  auto iter = std::find_if(
246  b256.begin(), b256.end(),[](unsigned char c)
247  { return c != 0; });
248  std::string result;
249  result.reserve (zeroes + (b256.end() - iter));
250  result.assign (zeroes, 0x00);
251  while (iter != b256.end())
252  result.push_back(*(iter++));
253  return result;
254 }
255 
256 /* Base58 decode a Ripple token
257 
258  The type and checksum are are checked
259  and removed from the returned result.
260 */
261 template <class InverseArray>
262 static
265  TokenType type, InverseArray const& inv)
266 {
267  std::string const ret = decodeBase58(s, inv);
268 
269  // Reject zero length tokens
270  if (ret.size() < 6)
271  return {};
272 
273  // The type must match.
274  if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
275  return {};
276 
277  // And the checksum must as well.
278  std::array<char, 4> guard;
279  checksum(guard.data(), ret.data(), ret.size() - guard.size());
280  if (!std::equal (guard.rbegin(), guard.rend(), ret.rbegin()))
281  return {};
282 
283  // Skip the leading type byte and the trailing checksum.
284  return ret.substr(1, ret.size() - 1 - guard.size());
285 }
286 
287 //------------------------------------------------------------------------------
288 
289 // Maps characters to their base58 digit
291 {
292 private:
294 
295 public:
296  explicit
298  {
299  map_.fill(-1);
300  int i = 0;
301  for(auto const c : digits)
302  map_[static_cast<
303  unsigned char>(c)] = i++;
304  }
305 
306  int
307  operator[](char c) const
308  {
309  return map_[static_cast<
310  unsigned char>(c)];
311  }
312 };
313 
314 static InverseAlphabet rippleInverse(rippleAlphabet);
315 
316 static InverseAlphabet bitcoinInverse(bitcoinAlphabet);
317 
320  std::string const& s, TokenType type)
321 {
322  return decodeBase58Token(s, type, rippleInverse);
323 }
324 
327  std::string const& s, TokenType type)
328 {
329  return decodeBase58Token(s, type, bitcoinInverse);
330 }
331 
332 } // ripple
ripple::decodeBase58
static std::string decodeBase58(std::string const &s, InverseArray const &inv)
Definition: tokens.cpp:206
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:297
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:44
ripple::InverseAlphabet
Definition: tokens.cpp:290
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:189
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:65
ripple::InverseAlphabet::map_
std::array< int, 256 > map_
Definition: tokens.cpp:293
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:307
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:104
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:264
cassert
ripple::checksum
void checksum(void *out, void const *message, std::size_t size)
Definition: tokens.cpp:81
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:326
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:155
std::vector::rbegin
T rbegin(T... args)