/* 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 . Copyright 2017-2019 Telegram Systems LLP */ #include "util.h" #include 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(std::string raw, bool base64_url) { return str_base64_encode(td::Slice{raw}, base64_url); } 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(s.data()), s.size()}, raw, base64_url); } return s; } bool is_valid_base64(std::string encoded, bool allow_base64_url) { return is_valid_base64(td::Slice{encoded}, allow_base64_url); } 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(std::string encoded, bool allow_base64_url) { return decoded_base64_size(td::Slice{encoded}, allow_base64_url); } 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(std::numeric_limits::max())) { return -1; } if (end == ptr) { return 0; } auto s = static_cast((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(std::string encoded, bool allow_base64_url) { return base64_decode(td::Slice{encoded}, allow_base64_url); } 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(s)}; auto r = buff_base64_decode(res.as_slice(), encoded, allow_base64_url); if (!r) { return td::BufferSlice{}; } CHECK(r == static_cast(s)); return res; } std::string str_base64_decode(std::string encoded, bool allow_base64_url) { return str_base64_decode(td::Slice{encoded}, allow_base64_url); } 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(s)); auto r = buff_base64_decode(td::MutableSlice{const_cast(res.data()), res.size()}, encoded, allow_base64_url); if (!r) { return std::string{}; } CHECK(r == static_cast(s)); return res; } } // namespace td