mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			236 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
    This file is part of TON Blockchain Library.
 | 
						|
 | 
						|
    TON Blockchain Library is free software: you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU Lesser General Public License as published by
 | 
						|
    the Free Software Foundation, either version 2 of the License, or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    TON Blockchain Library is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU Lesser General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU Lesser General Public License
 | 
						|
    along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    Copyright 2017-2020 Telegram Systems LLP
 | 
						|
*/
 | 
						|
#include "util.h"
 | 
						|
 | 
						|
#include <limits>
 | 
						|
 | 
						|
#include "td/utils/crypto.h"
 | 
						|
#include "td/utils/base64.h"
 | 
						|
 | 
						|
namespace td {
 | 
						|
 | 
						|
std::size_t compute_base64_encoded_size(size_t bindata_size) {
 | 
						|
  return ((bindata_size + 2) / 3) << 2;
 | 
						|
}
 | 
						|
 | 
						|
const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | 
						|
const char base64_url_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 | 
						|
const unsigned char base64_dec_table[256] = {
 | 
						|
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
 | 
						|
    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
 | 
						|
    0,    0,    0,    0,    0,    0x7e, 0,    0xbe, 0,    0x7f, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
 | 
						|
    0xfd, 0,    0,    0,    1,    0,    0,    0,    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
 | 
						|
    0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0,    0,    0,    0,
 | 
						|
    0xbf, 0,    0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
 | 
						|
    0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0,    0,    0,    0,    0};
 | 
						|
 | 
						|
std::size_t buff_base64_encode(td::MutableSlice buffer, td::Slice raw, bool base64_url) {
 | 
						|
  std::size_t orig_size = raw.size(), res_size = compute_base64_encoded_size(orig_size);
 | 
						|
  if (res_size > buffer.size()) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  const char *table = base64_url ? base64_url_table : base64_table;
 | 
						|
  char *wptr = buffer.data();
 | 
						|
  unsigned x;
 | 
						|
  std::size_t i;
 | 
						|
  for (i = 0; i < orig_size - 2; i += 3) {
 | 
						|
    x = (((unsigned)(unsigned char)raw[i]) << 16) | (((unsigned)(unsigned char)raw[i + 1]) << 8) |
 | 
						|
        ((unsigned)(unsigned char)raw[i + 2]);
 | 
						|
    *wptr++ = table[(x >> 18) & 0x3f];
 | 
						|
    *wptr++ = table[(x >> 12) & 0x3f];
 | 
						|
    *wptr++ = table[(x >> 6) & 0x3f];
 | 
						|
    *wptr++ = table[x & 0x3f];
 | 
						|
  }
 | 
						|
  switch (orig_size - i) {
 | 
						|
    case 1:
 | 
						|
      x = (((unsigned)(unsigned char)raw[i]) << 16);
 | 
						|
      *wptr++ = table[(x >> 18) & 0x3f];
 | 
						|
      *wptr++ = table[(x >> 12) & 0x3f];
 | 
						|
      *wptr++ = '=';
 | 
						|
      *wptr++ = '=';
 | 
						|
      break;
 | 
						|
    case 2:
 | 
						|
      x = (((unsigned)(unsigned char)raw[i]) << 16) | (((unsigned)(unsigned char)raw[i + 1]) << 8);
 | 
						|
      *wptr++ = table[(x >> 18) & 0x3f];
 | 
						|
      *wptr++ = table[(x >> 12) & 0x3f];
 | 
						|
      *wptr++ = table[(x >> 6) & 0x3f];
 | 
						|
      *wptr++ = '=';
 | 
						|
  }
 | 
						|
  CHECK(wptr == buffer.data() + res_size);
 | 
						|
  return res_size;
 | 
						|
}
 | 
						|
 | 
						|
std::string str_base64_encode(td::Slice raw, bool base64_url) {
 | 
						|
  std::size_t res_size = compute_base64_encoded_size(raw.size());
 | 
						|
  std::string s;
 | 
						|
  s.resize(res_size);
 | 
						|
  if (res_size) {
 | 
						|
    buff_base64_encode(td::MutableSlice{const_cast<char *>(s.data()), s.size()}, raw, base64_url);
 | 
						|
  }
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
bool is_valid_base64(td::Slice encoded, bool allow_base64_url) {
 | 
						|
  const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
 | 
						|
  if (encoded.size() & 3) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  unsigned mode = (allow_base64_url ? 0xc0 : 0x40);
 | 
						|
  while (ptr < end && (base64_dec_table[*ptr] & mode)) {
 | 
						|
    ptr++;
 | 
						|
  }
 | 
						|
  std::size_t d = end - ptr;
 | 
						|
  if (d > 2) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  while (ptr < end && *ptr == '=') {
 | 
						|
    ptr++;
 | 
						|
  }
 | 
						|
  return ptr == end;
 | 
						|
}
 | 
						|
 | 
						|
td::int32 decoded_base64_size(td::Slice encoded, bool allow_base64_url) {
 | 
						|
  const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
 | 
						|
  if (encoded.size() & 3) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  if (encoded.size() > static_cast<size_t>(std::numeric_limits<td::int32>::max())) {
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  if (end == ptr) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  auto s = static_cast<td::int32>((encoded.size() >> 2) * 3);
 | 
						|
  if (end[-1] == '=') {
 | 
						|
    s--;
 | 
						|
    if (end[-2] == '=') {
 | 
						|
      s--;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
std::size_t buff_base64_decode(td::MutableSlice buffer, td::Slice encoded, bool allow_base64_url) {
 | 
						|
  if ((encoded.size() & 3) || !encoded.size()) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  std::size_t n = (encoded.size() >> 2);
 | 
						|
  const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
 | 
						|
  unsigned q = (end[-1] == '=' ? (end[-2] == '=' ? 2 : 1) : 0);
 | 
						|
  if (buffer.size() + q < n * 3) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  unsigned char *wptr = (unsigned char *)buffer.data(), *wend = wptr + buffer.size();
 | 
						|
  unsigned mode = (allow_base64_url ? 0xc0 : 0x40);
 | 
						|
  for (std::size_t i = 0; i < n; i++) {
 | 
						|
    unsigned x = 0;
 | 
						|
    for (std::size_t j = 0; j < 4; j++) {
 | 
						|
      unsigned z = base64_dec_table[ptr[4 * i + j]];
 | 
						|
      if (!(z & mode) && z != 1 && (i < n - 1 || j + q < 4)) {
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
      x = (x << 6) | (z & 0x3f);
 | 
						|
    }
 | 
						|
    if (i < n - 1) {
 | 
						|
      *wptr++ = (unsigned char)(x >> 16);
 | 
						|
      *wptr++ = (unsigned char)(x >> 8);
 | 
						|
      *wptr++ = (unsigned char)x;
 | 
						|
    } else {
 | 
						|
      for (; q < 3; q++) {
 | 
						|
        *wptr++ = (unsigned char)(x >> 16);
 | 
						|
        x <<= 8;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CHECK(wptr <= wend);
 | 
						|
  return wptr - (unsigned char *)buffer.data();
 | 
						|
}
 | 
						|
 | 
						|
td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) {
 | 
						|
  auto s = decoded_base64_size(encoded, allow_base64_url);
 | 
						|
  if (s <= 0) {
 | 
						|
    return td::BufferSlice{};
 | 
						|
  }
 | 
						|
  td::BufferSlice res{static_cast<std::size_t>(s)};
 | 
						|
  auto r = buff_base64_decode(res.as_slice(), encoded, allow_base64_url);
 | 
						|
  if (!r) {
 | 
						|
    return td::BufferSlice{};
 | 
						|
  }
 | 
						|
  CHECK(r == static_cast<std::size_t>(s));
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) {
 | 
						|
  auto s = decoded_base64_size(encoded, allow_base64_url);
 | 
						|
  if (s <= 0) {
 | 
						|
    return std::string{};
 | 
						|
  }
 | 
						|
  std::string res;
 | 
						|
  res.resize(static_cast<std::size_t>(s));
 | 
						|
  auto r = buff_base64_decode(td::MutableSlice{const_cast<char *>(res.data()), res.size()}, encoded, allow_base64_url);
 | 
						|
  if (!r) {
 | 
						|
    return std::string{};
 | 
						|
  }
 | 
						|
  CHECK(r == static_cast<std::size_t>(s));
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
td::Result<std::string> adnl_id_encode(td::Slice id, bool upper_case) {
 | 
						|
  if (id.size() != 32) {
 | 
						|
    return td::Status::Error("Wrong andl id size");
 | 
						|
  }
 | 
						|
  td::uint8 buf[35];
 | 
						|
  td::MutableSlice buf_slice(buf, 35);
 | 
						|
  buf_slice[0] = 0x2d;
 | 
						|
  buf_slice.substr(1).copy_from(id);
 | 
						|
  auto hash = td::crc16(buf_slice.substr(0, 33));
 | 
						|
  buf[33] = static_cast<td::uint8>((hash >> 8) & 255);
 | 
						|
  buf[34] = static_cast<td::uint8>(hash & 255);
 | 
						|
  return td::base32_encode(buf_slice, upper_case).substr(1);
 | 
						|
}
 | 
						|
 | 
						|
std::string adnl_id_encode(td::Bits256 adnl_addr, bool upper_case) {
 | 
						|
  return adnl_id_encode(adnl_addr.as_slice(), upper_case).move_as_ok();
 | 
						|
}
 | 
						|
 | 
						|
td::Result<Bits256> adnl_id_decode(td::Slice id) {
 | 
						|
  if (id.size() != 55) {
 | 
						|
    return td::Status::Error("Wrong length of adnl id");
 | 
						|
  }
 | 
						|
  td::uint8 buf[56];
 | 
						|
  buf[0] = 'f';
 | 
						|
  td::MutableSlice buf_slice(buf, 56);
 | 
						|
  buf_slice.substr(1).copy_from(id);
 | 
						|
  TRY_RESULT(decoded_str, td::base32_decode(buf_slice));
 | 
						|
  auto decoded = td::Slice(decoded_str);
 | 
						|
  if (decoded[0] != 0x2d) {
 | 
						|
    return td::Status::Error("Invalid first byte");
 | 
						|
  }
 | 
						|
  auto got_hash = (decoded.ubegin()[33] << 8) | decoded.ubegin()[34];
 | 
						|
  auto hash = td::crc16(decoded.substr(0, 33));
 | 
						|
  if (hash != got_hash) {
 | 
						|
    return td::Status::Error("Hash mismatch");
 | 
						|
  }
 | 
						|
  Bits256 res;
 | 
						|
  res.as_slice().copy_from(decoded.substr(1, 32));
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace td
 |