diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h index 7e0d881f..e1ceafa9 100644 --- a/src/crypto/crypto-sugar.h +++ b/src/crypto/crypto-sugar.h @@ -517,6 +517,24 @@ namespace crypto m_u64[bit_index >> 6] &= ~(1ull << (bit_index & 63)); } + // the result is guaranteed to be within [ 0; 2 ^ bits_count ) + uint64_t get_bits(uint8_t bit_index_first, uint8_t bits_count) const + { + if (bits_count == 0 || bits_count > 64) + return 0; + uint8_t bits_count_m_1 = bits_count - 1; + unsigned int bit_index_last = bit_index_first + bits_count_m_1; + if (bit_index_last > 255) + bit_index_last = 255; + + uint64_t result = m_u64[bit_index_first >> 6] >> (bit_index_first & 63); + if (bits_count_m_1 > (bit_index_last & 63)) + result |= m_u64[bit_index_last >> 6] << (bits_count_m_1 - (bit_index_last & 63)); + + uint64_t result_mask = ((1ull << bits_count_m_1) - 1) << 1 | 1; // (just because 1ull << 64 in undefined behaviour, not a 0 as one would expect) + return result & result_mask; + } + // does not reduce static scalar_t power_of_2(uint8_t exponent) { diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index efa4aab0..3916e355 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1651,6 +1651,49 @@ TEST(crypto, point_negation) } +TEST(crypto, scalar_get_bits) +{ + scalar_t x = scalar_t::random(); + for(size_t i = 0; i < 256; ++i) + ASSERT_EQ(x.get_bits(i, 0), 0); + for(size_t i = 0; i < 256; ++i) + ASSERT_EQ(x.get_bits(i, std::min(255ull, i + 65)), 0); + + ASSERT_EQ(x.get_bits(0, 64), x.m_u64[0]); + ASSERT_EQ(x.get_bits(64, 64), x.m_u64[1]); + ASSERT_EQ(x.get_bits(128, 64), x.m_u64[2]); + ASSERT_EQ(x.get_bits(192, 64), x.m_u64[3]); + + uint64_t high_32_bits = x.m_u64[3] >> 32; + ASSERT_EQ(x.get_bits(192+32, 32), high_32_bits); + + for(size_t i = 33; i <= 64; ++i) + ASSERT_EQ(x.get_bits(192+32, i), high_32_bits); + + for(size_t i = 0; i < 10000; ++i) + { + scalar_t b = scalar_t::random(); + scalar_t x = scalar_t::random(); + size_t bit_index_from = b.m_s[5]; + size_t bits_count = b.m_s[6] % 65; // [0; 64] are allowed + + uint64_t extracted_bits = 0; + for(size_t j = 0; j < bits_count; ++j) + { + if (bit_index_from + j <= 255 && x.get_bit(bit_index_from + j)) + extracted_bits |= 1ull << j; + } + + if (extracted_bits != x.get_bits(bit_index_from, bits_count)) + { + std::cout << "i: " << i << ", bit_index_from: " << bit_index_from << ", bits_count: " << bits_count << ENDL + << "extracted_bits: " << extracted_bits << ", get_bits(): " << x.get_bits(bit_index_from, bits_count); + ASSERT_TRUE(false); + } + } + return true; +} + // // test's runner // diff --git a/tests/functional_tests/crypto_tests_performance.h b/tests/functional_tests/crypto_tests_performance.h index b686bbad..68a8a7e7 100644 --- a/tests/functional_tests/crypto_tests_performance.h +++ b/tests/functional_tests/crypto_tests_performance.h @@ -3,7 +3,45 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once -TEST(crypto, primitives) + +uint64_t get_bits_v1(const scalar_t& s, uint8_t bit_index_first, uint8_t bits_count) +{ + if (bits_count == 0 || bits_count > 64) + return 0; + unsigned int bit_index_last = bit_index_first + bits_count - 1; + if (bit_index_last > 255) + bit_index_last = 255; + uint64_t result_mask = ((1ull << (bits_count - 1)) - 1) << 1 | 1; // (just because 1ull << 64 in undefined behaviour, not a 0 as one would expect) + + uint64_t result = s.m_u64[bit_index_first >> 6] >> (bit_index_first & 63); + if (bits_count > (bit_index_last & 63) + 1) + result |= s.m_u64[bit_index_last >> 6] << (bits_count - (bit_index_last & 63) - 1); + return result & result_mask; +} + + +inline std::ostream &operator <<(std::ostream &o, const crypto::ge_precomp v) +{ + o << "{{"; + + for(size_t i = 0; i < 9; ++i) + o << v.yplusx[i] << ", "; + + o << v.yplusx[9] << "},\n {"; + + for(size_t i = 0; i < 9; ++i) + o << v.yminusx[i] << ", "; + + o << v.yminusx[9] << "},\n {"; + + for(size_t i = 0; i < 9; ++i) + o << v.xy2d[i] << ", "; + + o << v.xy2d[9] << "}}\n"; + return o; +} + +TEST(perf, primitives) { struct helper { @@ -46,6 +84,44 @@ TEST(crypto, primitives) #define HASH_64_VEC(vec_var_name) hash_64(vec_var_name.data(), vec_var_name.size() * sizeof(vec_var_name[0])) + LOG_PRINT_L0(ENDL << "hash functions:"); + + struct run_cn_fash_hash + { + static uint64_t run(timer_t& t, size_t rounds, size_t data_size) + { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + struct bytes64 + { + unsigned char b[64]; + }; + + std::vector scalars_64(rounds); + for (size_t i = 0; i < scalars_64.size(); ++i) + crypto::generate_random_bytes(sizeof(bytes64), scalars_64[i].b); + + std::vector results(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + results[i] = cn_fast_hash(scalars_64[rnd_indecies[i]].b, 64); + } + t.stop(); + + return HASH_64_VEC(results); + }; + }; + + run("cn_fast_hash(64 bytes)", 1000, [](timer_t& t, size_t rounds) { + return run_cn_fash_hash::run(t, rounds, 64ull); + }); + + run("cn_fast_hash(2048 bytes)", 1000, [](timer_t& t, size_t rounds) { + return run_cn_fash_hash::run(t, rounds, 2048ull); + }); + LOG_PRINT_L0(ENDL << "native crypto primitives:"); run("sc_reduce", 30000, [](timer_t& t, size_t rounds) {