1
0
mirror of https://github.com/danog/ton.git synced 2024-11-30 04:29:19 +01:00
ton/crypto/common/bitstring.cpp
2019-09-07 14:33:36 +04:00

669 lines
19 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-2019 Telegram Systems LLP
*/
#include "common/bitstring.h"
#include <cstring>
#include <limits>
#include "td/utils/as.h"
#include "td/utils/bits.h"
#include "td/utils/misc.h"
#include "crypto/openssl/digest.h"
namespace td {
template class Ref<BitString>;
BitString::BitString(const BitSlice& bs, unsigned reserve_bits) {
if (!bs.size() && !reserve_bits) {
ptr = 0;
offs = len = bytes_alloc = 0;
} else {
offs = bs.get_offs();
len = bs.size();
bytes_alloc = (bs.get_offs() + bs.size() + reserve_bits + 7) >> 3;
ptr = static_cast<unsigned char*>(std::malloc(bytes_alloc));
CHECK(ptr);
if (bs.size()) {
std::memcpy(ptr, bs.get_ptr(), bs.byte_size());
}
}
}
BitString::BitString(unsigned reserve_bits) {
if (!reserve_bits) {
ptr = 0;
offs = len = bytes_alloc = 0;
} else {
bytes_alloc = (reserve_bits + 7) >> 3;
ptr = static_cast<unsigned char*>(std::malloc(bytes_alloc));
CHECK(ptr);
offs = len = 0;
}
}
BitString::operator BitSlice() const {
return BitSlice(BitStringRef{this}, ptr, offs, len);
}
BitString* BitString::make_copy() const {
if (!ptr) {
return new BitString(64); // reserve 64 bits
} else {
return new BitString(operator BitSlice(), 64);
}
}
BitString& BitString::reserve_bits(unsigned req_bits) {
req_bits += offs + len;
if (req_bits > bytes_alloc * 8) {
bytes_alloc = (req_bits + 7) >> 3;
ptr = (unsigned char*)std::realloc(ptr, bytes_alloc);
CHECK(ptr);
}
return *this;
}
BitSliceWrite BitString::reserve_bitslice(unsigned req_bits) {
reserve_bits(req_bits);
unsigned pos = offs + len;
len += req_bits;
return BitSliceWrite(Ref<BitString>(this), ptr, pos, req_bits);
}
BitString& BitString::append(const BitSlice& bs) {
reserve_bitslice(bs.size()) = bs;
return *this;
}
BitSlice BitString::subslice(unsigned from, unsigned bits) const {
return BitSlice{BitStringRef{this}, ptr, static_cast<int>(offs + from), bits};
}
BitSliceWrite BitString::subslice_write(unsigned from, unsigned bits) {
return BitSliceWrite{BitStringRef{this}, ptr, offs + from, bits};
}
const BitSliceWrite& BitSliceWrite::operator=(const BitSlice& bs) const {
if (size() != bs.size()) {
throw LengthMismatch();
}
bitstring::bits_memcpy(get_ptr(), get_offs(), bs.get_ptr(), bs.get_offs(), size());
return *this;
}
const BitSliceWrite& BitSliceWrite::operator=(bool val) const {
bitstring::bits_memset(get_ptr(), get_offs(), val, size());
return *this;
}
std::ostream& operator<<(std::ostream& os, const BitString& bs) {
return os << bs.to_hex();
}
std::ostream& operator<<(std::ostream& os, Ref<BitString> bs_ref) {
return os << (bs_ref.is_null() ? "(null-bs)" : bs_ref->to_hex());
}
namespace bitstring {
void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int from_offs, std::size_t bit_count) {
if (bit_count <= 0) {
return;
}
from += (from_offs >> 3);
to += (to_offs >> 3);
from_offs &= 7;
to_offs &= 7;
//fprintf(stderr, "bits_memcpy: from=%p (%02x) to=%p (%02x) from_offs=%d to_offs=%d count=%lu\n", from, *from, to, *to, from_offs, to_offs, bit_count);
int sz = (int)bit_count;
bit_count += from_offs;
if (from_offs == to_offs) {
if (bit_count < 8) {
int mask = (-0x100 >> bit_count) & (0xff >> to_offs);
*to = (unsigned char)((*to & ~mask) | (*from & mask));
return;
}
std::size_t l = (bit_count >> 3);
if (!to_offs) {
std::memcpy(to, from, l);
} else {
int mask = (0xff >> to_offs);
*to = (unsigned char)((*to & ~mask) | (*from & mask));
std::memcpy(to + 1, from + 1, l - 1);
}
if ((bit_count &= 7) != 0) {
int mask = (-0x100 >> bit_count);
to[l] = (unsigned char)((to[l] & ~mask) | (from[l] & mask));
}
} else {
int b = (int)to_offs;
unsigned long long acc = (b ? *to >> (8 - b) : 0);
if (bit_count < 8) {
acc <<= sz;
acc |= ((*from & (0xff >> from_offs)) >> (8 - bit_count));
b += sz;
} else {
unsigned ld = 8 - from_offs;
acc <<= ld;
acc |= (*from++ & (0xff >> from_offs));
b += ld;
bit_count -= 8;
// b <= 15 here
while (bit_count >= 32) {
acc <<= 32;
acc |= td::bswap32(as<unsigned>(from));
from += 4;
as<unsigned>(to) = td::bswap32((unsigned)(acc >> b));
to += 4;
bit_count -= 32;
}
// bit_count <= 31, b <= 15
while (bit_count >= 8) {
acc <<= 8;
acc |= *from++;
bit_count -= 8;
b += 8;
}
// b + bit_count = const <= 46
if (bit_count > 0) {
acc <<= bit_count;
acc |= (*from >> (8 - bit_count));
b += (int)bit_count;
}
}
while (b >= 8) {
b -= 8;
*to++ = (unsigned char)(acc >> b);
}
if (b > 0) {
*to = (unsigned char)((*to & (0xff >> b)) | ((int)acc << (8 - b)));
}
}
}
void bits_memcpy(BitPtr to, ConstBitPtr from, std::size_t bit_count) {
bits_memcpy(to.ptr, to.offs, from.ptr, from.offs, bit_count);
}
void bits_memset(unsigned char* to, int to_offs, bool val, std::size_t bit_count) {
if (bit_count <= 0) {
return;
}
to += (to_offs >> 3);
to_offs &= 7;
int sz = (int)bit_count;
bit_count += to_offs;
int c = *to;
if (bit_count <= 8) {
int mask = (((-0x100 >> sz) & 0xff) >> to_offs);
if (val) {
*to = (unsigned char)(c | mask);
} else {
*to = (unsigned char)(c & ~mask);
}
return;
}
if (val) {
*to = (unsigned char)(c | (0xff >> to_offs));
} else {
*to = (unsigned char)(c & (-0x100 >> to_offs));
}
std::size_t l = (bit_count >> 3);
std::memset(to + 1, val ? 0xff : 0, l - 1);
if ((bit_count &= 7) != 0) {
if (val) {
to[l] = (unsigned char)(to[l] | (-0x100 >> bit_count));
} else {
to[l] = (unsigned char)(to[l] & (0xff >> bit_count));
}
}
}
void bits_memset(BitPtr to, bool val, std::size_t bit_count) {
bits_memset(to.ptr, to.offs, val, bit_count);
}
std::size_t bits_memscan_rev(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to) {
if (!bit_count) {
return 0;
}
int xor_val = (cmp_to ? -1 : 0);
ptr += ((offs + bit_count) >> 3);
offs = (int)((offs + bit_count) & 7);
std::size_t res = offs;
if (offs) {
unsigned v = (*ptr >> (8 - offs)) ^ xor_val;
unsigned c = td::count_trailing_zeroes32(v);
if (c < (unsigned)offs || res >= bit_count) {
return std::min(c, (unsigned)bit_count);
}
}
bit_count -= res;
while (bit_count >= 32) {
ptr -= 4;
unsigned v = td::bswap32(as<unsigned>(ptr)) ^ xor_val;
if (v) {
return td::count_trailing_zeroes_non_zero32(v) + res;
}
res += 32;
bit_count -= 32;
}
xor_val &= 0xff;
while (bit_count >= 8) {
unsigned v = *--ptr ^ xor_val;
if (v) {
return td::count_trailing_zeroes_non_zero32(v) + res;
}
res += 8;
bit_count -= 8;
}
if (bit_count > 0) {
unsigned v = *--ptr ^ xor_val;
return std::min((unsigned)td::count_trailing_zeroes32(v), (unsigned)bit_count) + res;
} else {
return res;
}
}
std::size_t bits_memscan(const unsigned char* ptr, int offs, std::size_t bit_count, bool cmp_to) {
if (!bit_count) {
return 0;
}
int xor_val = -static_cast<int>(cmp_to);
ptr += (offs >> 3);
offs &= 7;
std::size_t rem = bit_count;
unsigned v, c;
if (offs) {
v = ((unsigned)(ptr[0] ^ xor_val) << (24 + offs));
// std::cerr << "[A] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
c = td::count_leading_zeroes32(v);
unsigned l = (unsigned)(8 - offs);
if (c < l || bit_count <= l) {
return std::min<std::size_t>(c, bit_count);
}
rem -= l;
ptr++;
}
while (rem >= 8 && !td::is_aligned_pointer<8>(ptr)) {
v = ((*ptr++ ^ xor_val) << 24);
// std::cerr << "[B] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
if (v) {
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
}
rem -= 8;
}
td::uint64 xor_val_l = (cmp_to ? ~0LL : 0LL);
while (rem >= 64) {
td::uint64 z = td::bswap64(as<td::uint64>(ptr)) ^ xor_val_l;
// std::cerr << "[C] rem=" << rem << " ptr=" << (const void*)ptr << " z=" << std::hex << z << std::dec << std::endl;
if (z) {
return bit_count - rem + td::count_leading_zeroes_non_zero64(z);
}
ptr += 8;
rem -= 64;
}
while (rem >= 8) {
v = ((*ptr++ ^ xor_val) << 24);
// std::cerr << "[D] rem=" << rem << " ptr=" << (const void*)(ptr - 1) << " v=" << std::hex << v << std::dec << std::endl;
if (v) {
return bit_count - rem + td::count_leading_zeroes_non_zero32(v);
}
rem -= 8;
}
if (rem > 0) {
v = ((*ptr ^ xor_val) << 24);
// std::cerr << "[E] rem=" << rem << " ptr=" << (const void*)ptr << " v=" << std::hex << v << std::dec << std::endl;
c = td::count_leading_zeroes32(v);
return c < rem ? bit_count - rem + c : bit_count;
} else {
return bit_count;
}
}
std::size_t bits_memscan(ConstBitPtr bs, std::size_t bit_count, bool cmp_to) {
return bits_memscan(bs.ptr, bs.offs, bit_count, cmp_to);
}
std::size_t bits_memscan_rev(ConstBitPtr bs, std::size_t bit_count, bool cmp_to) {
return bits_memscan_rev(bs.ptr, bs.offs, bit_count, cmp_to);
}
int bits_memcmp(const unsigned char* bs1, int bs1_offs, const unsigned char* bs2, int bs2_offs, std::size_t bit_count,
std::size_t* same_upto) {
if (!bit_count) {
return 0;
}
bs1 += (bs1_offs >> 3);
bs2 += (bs2_offs >> 3);
bs1_offs &= 7;
bs2_offs &= 7;
//fprintf(stderr, "bits_memcmp: bs1=%02x%02x offs=%d bs2=%02x%02x offs=%d cnt=%lu\n", bs1[0], bs1[1], bs1_offs, bs2[0], bs2[1], bs2_offs, bit_count);
unsigned long long acc1 = (((unsigned long long)*bs1++) << (56 + bs1_offs));
int z1 = 8 - bs1_offs;
unsigned long long acc2 = (((unsigned long long)*bs2++) << (56 + bs2_offs));
int z2 = 8 - bs2_offs;
std::size_t processed = 0;
while (bit_count >= 40) {
acc1 |= ((unsigned long long)td::bswap32(as<unsigned>(bs1)) << (32 - z1));
bs1 += 4;
acc2 |= ((unsigned long long)td::bswap32(as<unsigned>(bs2)) << (32 - z2));
bs2 += 4;
if ((acc1 ^ acc2) & (~0ULL << 32)) {
if (same_upto) {
*same_upto = processed + td::count_leading_zeroes64(acc1 ^ acc2);
}
return acc1 < acc2 ? -1 : 1;
}
acc1 <<= 32;
acc2 <<= 32;
processed += 32;
bit_count -= 32;
}
// now 0 <= bit_count <= 39
bs1_offs += (int)bit_count - 8; // = bit_count - z1, bits to load from bs1
while (bs1_offs >= 8) {
acc1 |= ((unsigned long long)(*bs1++) << (56 - z1));
z1 += 8;
bs1_offs -= 8;
}
if (bs1_offs > 0) {
acc1 |= ((unsigned long long)(*bs1) << (56 - z1));
}
z1 += bs1_offs; // NB: bs1_offs may be negative
bs2_offs += (int)bit_count - 8; // bits to load from bs2
while (bs2_offs >= 8) {
acc2 |= ((unsigned long long)(*bs2++) << (56 - z2));
z2 += 8;
bs2_offs -= 8;
}
if (bs2_offs > 0) {
acc2 |= ((unsigned long long)(*bs2) << (56 - z2));
}
z2 += bs2_offs;
CHECK(z1 == z2);
CHECK(z1 < 64);
//fprintf(stderr, "acc1=%016llx acc2=%016llx z1=z2=%d\n", acc1, acc2, z1);
if (z1) {
if ((acc1 ^ acc2) & (~0ULL << (64 - z1))) {
if (same_upto) {
*same_upto = processed + td::count_leading_zeroes64(acc1 ^ acc2);
}
return acc1 < acc2 ? -1 : 1;
}
}
if (same_upto) {
*same_upto = processed + bit_count;
}
return 0;
}
int bits_memcmp(ConstBitPtr bs1, ConstBitPtr bs2, std::size_t bit_count, std::size_t* same_upto) {
return bits_memcmp(bs1.ptr, bs1.offs, bs2.ptr, bs2.offs, bit_count, same_upto);
}
int bits_lexcmp(const unsigned char* bs1, int bs1_offs, std::size_t bs1_bit_count, const unsigned char* bs2,
int bs2_offs, std::size_t bs2_bit_count) {
int res = bits_memcmp(bs1, bs1_offs, bs2, bs2_offs, std::min(bs1_bit_count, bs2_bit_count), 0);
if (res || bs1_bit_count == bs2_bit_count) {
return res;
}
return bs1_bit_count < bs2_bit_count ? -1 : 1;
}
int bits_lexcmp(ConstBitPtr bs1, std::size_t bs1_bit_count, ConstBitPtr bs2, std::size_t bs2_bit_count) {
return bits_lexcmp(bs1.ptr, bs1.offs, bs1_bit_count, bs2.ptr, bs2.offs, bs2_bit_count);
}
void bits_store_long_top(unsigned char* to, int to_offs, unsigned long long val, unsigned top_bits) {
CHECK(top_bits <= 64);
if (top_bits <= 0) {
return;
}
to += (to_offs >> 3);
to_offs &= 7;
if (!to_offs && !(top_bits & 7)) {
// good only on little-endian machines!
unsigned long long tmp = td::bswap64(val);
std::memcpy(to, &tmp, top_bits >> 3);
return;
}
unsigned long long z = (unsigned long long)(*to & (-0x100 >> to_offs)) << 56;
z |= (val >> to_offs);
top_bits += to_offs;
if (top_bits > 64) {
as<unsigned long long>(to) = td::bswap64(z);
z = (val << (8 - to_offs));
int mask = (0xff >> (top_bits - 64));
to[8] = (unsigned char)((to[8] & mask) | ((int)z & ~mask));
} else {
int p = 56, q = 64 - top_bits;
if (q <= 32) {
as<unsigned>(to) = td::bswap32((unsigned)(z >> 32));
to += 4;
p -= 32;
}
while (p >= q) {
*to++ = (unsigned char)(z >> p);
p -= 8;
}
top_bits = p + 8 - q;
if (top_bits > 0) {
int mask = (0xff >> top_bits);
*to = (unsigned char)((*to & mask) | ((z >> p) & ~mask));
}
}
}
void bits_store_long_top(BitPtr to, unsigned long long val, unsigned top_bits) {
bits_store_long_top(to.ptr, to.offs, val, top_bits);
}
void bits_store_long(BitPtr to, unsigned long long val, unsigned bits) {
bits_store_long_top(to, val << (64 - bits), bits);
}
unsigned long long bits_load_long_top(const unsigned char* from, int from_offs, unsigned top_bits) {
CHECK(top_bits <= 64);
if (!top_bits) {
return 0;
}
from += (from_offs >> 3);
from_offs &= 7;
if ((unsigned)from_offs + top_bits <= 64) {
unsigned long long tmp;
std::memcpy(&tmp, from, (from_offs + top_bits + 7) >> 3);
return (td::bswap64(tmp) << from_offs) & (std::numeric_limits<td::uint64>::max() << (64 - top_bits));
} else {
unsigned long long z = td::bswap64(as<unsigned long long>(from));
z <<= from_offs;
z |= (from[8] >> (8 - from_offs));
return z & (std::numeric_limits<td::uint64>::max() << (64 - top_bits));
}
}
unsigned long long bits_load_long_top(ConstBitPtr from, unsigned top_bits) {
return bits_load_long_top(from.ptr, from.offs, top_bits);
}
unsigned long long bits_load_ulong(ConstBitPtr from, unsigned bits) {
return bits_load_long_top(from, bits) >> (64 - bits);
}
long long bits_load_long(ConstBitPtr from, unsigned bits) {
return (long long)bits_load_long_top(from, bits) >> (64 - bits);
}
std::string bits_to_binary(const unsigned char* ptr, int offs, std::size_t len) {
if (!len) {
return "";
}
std::string s;
s.reserve(len);
ptr += (offs >> 3);
unsigned mask = (0x80 >> (offs & 7));
unsigned value = *ptr++;
do {
s.push_back(value & mask ? '1' : '0');
if (!(mask >>= 1)) {
value = *ptr++;
mask = 0x80;
}
} while (--len > 0);
return s;
}
std::string bits_to_binary(ConstBitPtr bs, std::size_t len) {
return bits_to_binary(bs.ptr, bs.offs, len);
}
static const char hex_digits[] = "0123456789ABCDEF";
std::string bits_to_hex(const unsigned char* ptr, int offs, std::size_t len) {
if (!len) {
return "";
}
std::string s;
s.reserve((len + 7) >> 2);
ptr += (offs >> 3);
offs &= 7;
unsigned long long acc = *ptr++ & (0xff >> offs);
unsigned bits = 8 - offs;
if (bits > len) {
acc >>= bits - (unsigned)len;
bits = (unsigned)len;
} else {
len -= bits;
while (len >= 8) {
while (len >= 8 && bits <= 56) {
acc <<= 8;
acc |= *ptr++;
bits += 8;
len -= 8;
}
while (bits >= 4) {
bits -= 4;
s.push_back(hex_digits[(acc >> bits) & 15]);
}
}
if (len > 0) {
acc <<= len;
acc |= (*ptr >> (8 - len));
bits += (unsigned)len;
}
}
int f = bits & 3;
if (f) {
acc = (2 * acc + 1) << (3 - f);
bits += 4 - f;
}
while (bits >= 4) {
bits -= 4;
s.push_back(hex_digits[(acc >> bits) & 15]);
}
CHECK(!bits);
if (f) {
s.push_back('_');
}
return s;
}
std::string bits_to_hex(ConstBitPtr bs, std::size_t len) {
return bits_to_hex(bs.ptr, bs.offs, len);
}
long parse_bitstring_hex_literal(unsigned char* buff, std::size_t buff_size, const char* str, const char* str_end) {
std::size_t hex_digits_count = 0;
bool cmpl = false;
unsigned char* ptr = buff;
const char* rptr = str;
while (rptr < str_end) {
int c = *rptr++;
if (c == ' ' || c == '\t') {
continue;
}
if (cmpl) {
return td::narrow_cast<long>(str - rptr);
}
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
int val = (c <= '9') ? c - '0' : ((c | 0x20) - 'a' + 10);
if (hex_digits_count >= 2 * buff_size) {
return td::narrow_cast<long>(str - rptr);
}
if (!(hex_digits_count & 1)) {
*ptr = (unsigned char)(val << 4);
} else {
*ptr = (unsigned char)(*ptr | val);
ptr++;
}
hex_digits_count++;
continue;
}
if (c == '_') {
cmpl = true;
} else {
return td::narrow_cast<long>(str - rptr);
}
}
std::size_t bits = 4 * hex_digits_count;
if (cmpl && bits) {
int t = (hex_digits_count & 1) ? (0x100 + *ptr) >> 4 : (0x100 + *--ptr);
while (bits > 0) {
--bits;
if (t & 1) {
break;
}
t >>= 1;
if (t == 1) {
t = 0x100 + *--ptr;
}
}
}
return bits;
}
long parse_bitstring_binary_literal(BitPtr buff, std::size_t buff_size, const char* str, const char* str_end) {
const char* ptr = str;
while (ptr < str_end && buff_size && (*ptr == '0' || *ptr == '1')) {
*buff++ = (bool)(*ptr++ & 1);
--buff_size;
}
return td::narrow_cast<long>(ptr == str_end ? ptr - str : str - ptr - 1);
}
void bits_sha256(BitPtr to, ConstBitPtr from, std::size_t size) {
if (from.byte_aligned() && !(size & 7)) {
if (to.byte_aligned()) {
digest::hash_str<digest::SHA256>(to.get_byte_ptr(), from.get_byte_ptr(), size >> 3);
} else {
unsigned char buffer[32];
digest::hash_str<digest::SHA256>(buffer, from.get_byte_ptr(), size >> 3);
to.copy_from(BitPtr{buffer}, 256);
}
} else {
throw BitstringError{};
}
}
} // namespace bitstring
} // namespace td