forked from lthn/blockchain
merge from develop
This commit is contained in:
commit
16f37ed4c2
12 changed files with 772 additions and 57 deletions
|
|
@ -30,6 +30,7 @@
|
|||
#include <stddef.h>
|
||||
#include "warnings.h"
|
||||
#include "crypto-ops.h"
|
||||
#include "hash-ops.h" // for cn_fast_hash
|
||||
|
||||
DISABLE_VS_WARNINGS(4146 4244)
|
||||
|
||||
|
|
@ -3724,3 +3725,38 @@ void sc_invert(unsigned char* out, const unsigned char* z)
|
|||
sc_mul(out, out, out);
|
||||
sc_mul(out, out, z);
|
||||
}
|
||||
|
||||
/*
|
||||
In: t (x, y, z)
|
||||
Out: r (x, t, z, t)
|
||||
|
||||
Note: expensive conversion because of fe_invert
|
||||
*/
|
||||
void ge_p2_to_p3(ge_p3 *r, const ge_p2 *t)
|
||||
{
|
||||
fe_copy(r->X, t->X);
|
||||
fe_copy(r->Y, t->Y);
|
||||
fe_copy(r->Z, t->Z);
|
||||
fe_invert(r->T, t->Z);
|
||||
fe_mul(r->T, r->T, t->Y);
|
||||
fe_mul(r->T, r->T, t->X);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
In: ge_bytes -- points to 32 bytes of data
|
||||
Out: res = Hp(ge_bytes)
|
||||
where Hp = 8 * ge_fromfe_frombytes_vartime(cn_fast_hash(ge_bytes))
|
||||
*/
|
||||
void ge_bytes_hash_to_ec(ge_p3 *res, const unsigned char *ge_bytes)
|
||||
{
|
||||
unsigned char h[HASH_SIZE];
|
||||
ge_p2 point;
|
||||
ge_p1p1 point2;
|
||||
|
||||
cn_fast_hash(ge_bytes, 32, h);
|
||||
ge_fromfe_frombytes_vartime(&point, &h[0]);
|
||||
/*ge_p2_to_p3(res, &point); /* -- can be used to avoid multiplication by 8 for debugging */
|
||||
ge_mul8(&point2, &point);
|
||||
ge_p1p1_to_p3(res, &point2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *);
|
|||
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
|
||||
void ge_mul8(ge_p1p1 *, const ge_p2 *);
|
||||
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
|
||||
void ge_bytes_hash_to_ec(ge_p3 *, const unsigned char *);
|
||||
|
||||
void ge_p3_0(ge_p3 *h);
|
||||
void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
|
||||
|
||||
|
|
|
|||
|
|
@ -303,11 +303,13 @@ namespace crypto {
|
|||
|
||||
PUSH_VS_WARNINGS
|
||||
DISABLE_VS_WARNINGS(4200)
|
||||
struct rs_comm_entry
|
||||
{
|
||||
ec_point a, b;
|
||||
};
|
||||
struct rs_comm {
|
||||
struct rs_comm_entry
|
||||
{
|
||||
ec_point a, b;
|
||||
};
|
||||
|
||||
struct rs_comm
|
||||
{
|
||||
hash h;
|
||||
struct rs_comm_entry ab[];
|
||||
};
|
||||
|
|
@ -422,4 +424,5 @@ POP_VS_WARNINGS
|
|||
sc_sub(&h, &h, &sum);
|
||||
return sc_isnonzero(&h) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
|
|
|
|||
|
|
@ -6167,9 +6167,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
|
|||
if (abg_it != alt_chain.back()->second.gindex_lookup_table.end())
|
||||
{
|
||||
amount_touched_altchain = true;
|
||||
//Notice: since transactions is not allowed to refer to each other in one block, then we can consider that index in
|
||||
//tx input would be always less then top for previous block, so just take it
|
||||
global_outs_for_amount = abg_it->second;
|
||||
// local gindex lookup table contains last used gindex, so we can't get total number of outs
|
||||
// just skip setting global_outs_for_amount
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -6194,7 +6193,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
|
|||
if (off.type() == typeid(uint64_t))
|
||||
{
|
||||
uint64_t offset_gindex = boost::get<uint64_t>(off);
|
||||
CHECK_AND_ASSERT_MES(offset_gindex < global_outs_for_amount, false,
|
||||
CHECK_AND_ASSERT_MES(amount_touched_altchain || (offset_gindex < global_outs_for_amount), false,
|
||||
"invalid global output index " << offset_gindex << " for amount=" << input_to_key.amount <<
|
||||
", max is " << global_outs_for_amount <<
|
||||
", referred to by offset #" << pk_n <<
|
||||
|
|
@ -6226,7 +6225,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
|
|||
}
|
||||
}
|
||||
if (found_the_key)
|
||||
break;
|
||||
continue;
|
||||
//otherwise lookup in main chain index
|
||||
}
|
||||
auto p = m_db_outputs.get_subitem(input_to_key.amount, offset_gindex);
|
||||
|
|
|
|||
|
|
@ -622,3 +622,135 @@ bool alt_blocks_with_the_same_txs::check_tx_not_related_to_altblock(currency::co
|
|||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
||||
bool chain_switching_when_out_spent_in_alt_chain_mixin::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
// Test idea: make sure a tx can spend an output with mixins from another tx when both txs are in an altchain.
|
||||
bool r = false;
|
||||
GENERATE_ACCOUNT(miner_acc);
|
||||
GENERATE_ACCOUNT(alice_acc);
|
||||
GENERATE_ACCOUNT(bob_acc);
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc);
|
||||
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
||||
|
||||
// 0 1 11 12 13 14
|
||||
// (0 )- (1 )-...(1r)- (2 )- (3 )- <-- main chain
|
||||
// \
|
||||
// \
|
||||
// \- (2a)- (3a)- (4a)-
|
||||
// tx_0 <- tx_1 // tx_1 spends output from tx_0
|
||||
|
||||
// send batch of 10 x 5 test coins to Alice for easier tx_1 construction (and to generate free decoys)
|
||||
transaction tx_0;
|
||||
r = construct_tx_with_many_outputs(events, blk_1r, miner_acc.get_keys(), alice_acc.get_public_address(), MK_TEST_COINS(50), 10,
|
||||
TESTS_DEFAULT_FEE, tx_0);
|
||||
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed");
|
||||
events.push_back(tx_0);
|
||||
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_acc);
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner_acc, tx_0);
|
||||
|
||||
// make sure Alice received exactly 50 test coins
|
||||
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
|
||||
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_2a, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2);
|
||||
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, MK_TEST_COINS(50));
|
||||
|
||||
// Alice spends her 5 test coins received by tx_0
|
||||
MAKE_TX_FEE_MIX(events, tx_1, alice_acc, bob_acc, MK_TEST_COINS(4), TESTS_DEFAULT_FEE, 3 /* nmix */, blk_2a);
|
||||
events.pop_back(); // pop back tx_1 as it won't go into the tx pool normally because of alt chain
|
||||
|
||||
// simulate handling a block with that tx: handle tx like going with the block...
|
||||
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, true));
|
||||
events.push_back(tx_1);
|
||||
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false));
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_3a, blk_2a, miner_acc, tx_1);
|
||||
MAKE_NEXT_BLOCK(events, blk_4a, blk_3a, miner_acc);
|
||||
|
||||
// make sure Alice has correct balance
|
||||
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_4a, 2);
|
||||
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, MK_TEST_COINS(45));
|
||||
|
||||
// make sure chain successfully switched
|
||||
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_4a), get_block_hash(blk_4a)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
||||
bool chain_switching_when_out_spent_in_alt_chain_ref_id::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
// Test idea: make sure tx can spend (using ref_by_id) an output from another tx when both txs are in an altchain.
|
||||
bool r = false;
|
||||
GENERATE_ACCOUNT(miner_acc);
|
||||
GENERATE_ACCOUNT(alice_acc);
|
||||
GENERATE_ACCOUNT(bob_acc);
|
||||
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
|
||||
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc);
|
||||
REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
||||
|
||||
// 0 1 11 12 13 14
|
||||
// (0 )- (1 )-...(1r)- (2 )- (3 )- <- main chain
|
||||
// \
|
||||
// \
|
||||
// \- (2a)- (3a)- (4a)-
|
||||
// tx_0 <- tx_1 // tx_1 spends an output from tx_0 using ref_by_id and mixins
|
||||
|
||||
// send batch of 10 x 5 test coins to Alice for easier tx_0 construction
|
||||
transaction tx_0;
|
||||
r = construct_tx_with_many_outputs(events, blk_1r, miner_acc.get_keys(), alice_acc.get_public_address(), MK_TEST_COINS(50), 10,
|
||||
TESTS_DEFAULT_FEE, tx_0, true);
|
||||
CHECK_AND_ASSERT_MES(r, false, "construct_tx_with_many_outputs failed");
|
||||
|
||||
// make sure tx_0 really use ref_by_id
|
||||
size_t refs_count = 0, gindex_count = 0;
|
||||
count_ref_by_id_and_gindex_refs_for_tx_inputs(tx_0, refs_count, gindex_count);
|
||||
CHECK_AND_ASSERT_MES(refs_count == 1 && gindex_count == 0, false, "incorrect input references: " << refs_count << ", " << gindex_count);
|
||||
|
||||
events.push_back(tx_0);
|
||||
|
||||
MAKE_NEXT_BLOCK(events, blk_2, blk_1r, miner_acc);
|
||||
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1r, miner_acc, tx_0);
|
||||
|
||||
// make sure Alice received exactly 50 test coins
|
||||
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
|
||||
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_2a, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2);
|
||||
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, MK_TEST_COINS(50));
|
||||
|
||||
// Alice spends her 5 test coins received by tx_0
|
||||
transaction tx_1;
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
destinations.push_back(tx_destination_entry(MK_TEST_COINS(4), bob_acc.get_public_address()));
|
||||
size_t nmix = 3;
|
||||
r = construct_tx_to_key(events, tx_1, blk_2a, alice_acc, destinations, TESTS_DEFAULT_FEE, nmix, 0, empty_extra, empty_attachment, true, true, true);
|
||||
CHECK_AND_ASSERT_MES(r, false, "construct_tx_to_key failed");
|
||||
|
||||
// make sure tx_1 really use ref_by_id
|
||||
refs_count = 0, gindex_count = 0;
|
||||
count_ref_by_id_and_gindex_refs_for_tx_inputs(tx_1, refs_count, gindex_count);
|
||||
CHECK_AND_ASSERT_MES(refs_count == nmix + 1 && gindex_count == 0, false, "incorrect input references: " << refs_count << ", " << gindex_count);
|
||||
|
||||
// simulate handling a block with that tx: handle tx like going with the block...
|
||||
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, true));
|
||||
events.push_back(tx_1);
|
||||
events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false));
|
||||
|
||||
MAKE_NEXT_BLOCK_TX1(events, blk_3a, blk_2a, miner_acc, tx_1);
|
||||
MAKE_NEXT_BLOCK(events, blk_4a, blk_3a, miner_acc);
|
||||
|
||||
// make sure Alice has correct balance
|
||||
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_4a, 2);
|
||||
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, MK_TEST_COINS(45));
|
||||
|
||||
// make sure chain successfully switched
|
||||
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_4a), get_block_hash(blk_4a)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,3 +72,13 @@ struct alt_blocks_with_the_same_txs : public test_chain_unit_enchanced
|
|||
bool check_tx_related_to_altblock(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
bool check_tx_not_related_to_altblock(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
|
||||
};
|
||||
|
||||
struct chain_switching_when_out_spent_in_alt_chain_mixin : public test_chain_unit_enchanced
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
||||
struct chain_switching_when_out_spent_in_alt_chain_ref_id : public test_chain_unit_enchanced
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1548,10 +1548,10 @@ transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const b
|
|||
|
||||
bool construct_tx_with_many_outputs(std::vector<test_event_entry>& events, const currency::block& blk_head,
|
||||
const currency::account_keys& keys_from, const currency::account_public_address& addr_to,
|
||||
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx)
|
||||
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id /* = false */)
|
||||
{
|
||||
std::vector<currency::tx_source_entry> sources;
|
||||
bool r = fill_tx_sources(sources, events, blk_head, keys_from, total_amount + fee, 0, true, false, false);
|
||||
bool r = fill_tx_sources(sources, events, blk_head, keys_from, total_amount + fee, 0, true, false, use_ref_by_id);
|
||||
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
|
||||
|
||||
std::vector<currency::tx_destination_entry> destinations;
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ currency::transaction construct_tx_with_fee(std::vector<test_event_entry>& event
|
|||
|
||||
bool construct_tx_with_many_outputs(std::vector<test_event_entry>& events, const currency::block& blk_head,
|
||||
const currency::account_keys& keys_from, const currency::account_public_address& addr_to,
|
||||
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx);
|
||||
uint64_t total_amount, size_t outputs_count, uint64_t fee, currency::transaction& tx, bool use_ref_by_id = false);
|
||||
|
||||
void get_confirmed_txs(const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs);
|
||||
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<currency::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head);
|
||||
|
|
@ -916,6 +916,26 @@ inline uint64_t get_sources_total_amount(const std::vector<currency::tx_source_e
|
|||
return result;
|
||||
}
|
||||
|
||||
inline void count_ref_by_id_and_gindex_refs_for_tx_inputs(const currency::transaction& tx, size_t& refs_by_id, size_t& refs_by_gindex)
|
||||
{
|
||||
refs_by_id = 0;
|
||||
refs_by_gindex = 0;
|
||||
for (auto& in : tx.vin)
|
||||
{
|
||||
if (in.type() != typeid(currency::txin_to_key))
|
||||
continue;
|
||||
|
||||
const currency::txin_to_key& in2key = boost::get<currency::txin_to_key>(in);
|
||||
for (auto& ko : in2key.key_offsets)
|
||||
{
|
||||
if (ko.type() == typeid(currency::ref_by_id))
|
||||
++refs_by_id;
|
||||
else if (ko.type() == typeid(uint64_t))
|
||||
++refs_by_gindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, typename V>
|
||||
void append_vector_by_another_vector(U& dst, const V& src)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -899,6 +899,9 @@ int main(int argc, char* argv[])
|
|||
GENERATE_AND_PLAY(gen_simple_chain_split_1);
|
||||
GENERATE_AND_PLAY(alt_blocks_validation_and_same_new_amount_in_two_txs);
|
||||
GENERATE_AND_PLAY(alt_blocks_with_the_same_txs);
|
||||
GENERATE_AND_PLAY(chain_switching_when_out_spent_in_alt_chain_mixin);
|
||||
// GENERATE_AND_PLAY(chain_switching_when_out_spent_in_alt_chain_ref_id);
|
||||
|
||||
|
||||
// miscellaneous tests
|
||||
GENERATE_AND_PLAY(test_blockchain_vs_spent_keyimges);
|
||||
|
|
|
|||
110
tests/functional_tests/L2S.h
Normal file
110
tests/functional_tests/L2S.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2020 Zano Project (https://zano.org/)
|
||||
// Copyright (c) 2020 Locksmith (acmxddk@gmail.com)
|
||||
// Copyright (c) 2020 sowle (crypto.sowle@gmail.com)
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file contains the implementation of L2S membership proof protocol
|
||||
// and a linkable ring signature scheme based on it.
|
||||
//
|
||||
|
||||
point_t ml2s_rsum_impl(size_t n, size_t N, std::vector<point_t>::const_iterator X_array_bg_it, const std::vector<scalar_t>& c1_array,
|
||||
const std::vector<scalar_t>& c3_array, const scalar_t& cn)
|
||||
{
|
||||
if (n == 1)
|
||||
return *X_array_bg_it + cn * *(X_array_bg_it + 1);
|
||||
|
||||
// n >= 2, N >= 4
|
||||
return ml2s_rsum_impl(n - 1, N / 2, X_array_bg_it, c1_array, c3_array, c1_array[n - 2]) +
|
||||
cn * ml2s_rsum_impl(n - 1, N / 2, X_array_bg_it + N / 2, c1_array, c3_array, c3_array[n - 2]);
|
||||
}
|
||||
|
||||
bool ml2s_rsum(size_t n, const std::vector<point_t>& X_array, const std::vector<scalar_t>& c1_array,
|
||||
const std::vector<scalar_t>& c3_array, point_t& result)
|
||||
{
|
||||
size_t N = (size_t)1 << n;
|
||||
CHECK_AND_ASSERT_MES(n != 0, false, "n == 0");
|
||||
CHECK_AND_ASSERT_MES(N == X_array.size(), false, "|X_array| != N, " << X_array.size() << ", " << N);
|
||||
CHECK_AND_ASSERT_MES(c1_array.size() == n, false, "|c1_array| != n, " << c1_array.size() << ", " << n);
|
||||
CHECK_AND_ASSERT_MES(c3_array.size() == n - 1, false, "|c3_array| != n - 1, " << c3_array.size() << ", " << n - 1);
|
||||
|
||||
result = ml2s_rsum_impl(n, N, X_array.begin(), c1_array, c3_array, c1_array[n - 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ml2s_signature_element
|
||||
{
|
||||
point_t Z0;
|
||||
point_t T0;
|
||||
scalar_t t0;
|
||||
point_t Z;
|
||||
std::vector<scalar_t> r_array;
|
||||
std::vector<point_t> H_array;
|
||||
point_t T;
|
||||
scalar_t t;
|
||||
};
|
||||
|
||||
struct ml2s_signature
|
||||
{
|
||||
scalar_t z;
|
||||
std::vector<ml2s_signature_element> elements;
|
||||
};
|
||||
|
||||
// reference: mL2SLnkSig_Verif()
|
||||
bool ml2s_lnk_sig_verif(const scalar_t& m, const std::vector<point_t>& B_array, const ml2s_signature& signature, uint8_t* p_err = nullptr)
|
||||
{
|
||||
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
|
||||
if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_verif: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
|
||||
if (p_err) *p_err = err_code; return false; }
|
||||
|
||||
auto hash_point_lambda = [&signature](const point_t& point) { return point + signature.z * hash_helper_t::hp(point); };
|
||||
|
||||
size_t L = signature.elements.size();
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 0);
|
||||
size_t n = signature.elements[0].r_array.size();
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n < 32, 4);
|
||||
size_t N = (size_t)1 << n;
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(B_array.size() == N / 2, 5);
|
||||
|
||||
std::vector<point_t> I_array(L);
|
||||
std::vector<point_t> A_array(L);
|
||||
|
||||
for (size_t i = 0; i < L; ++i)
|
||||
{
|
||||
I_array[i] = (signature.elements[i].Z0 - c_point_G) / signature.z;
|
||||
A_array[i] = signature.elements[i].Z0;
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].r_array.size() == n, 1);
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].H_array.size() == n, 2);
|
||||
}
|
||||
|
||||
scalar_t z_ = hash_helper_t::hs(m, B_array, I_array);
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(z_ == signature.z, 3);
|
||||
|
||||
scalar_t e = hash_helper_t::hs(signature.z);
|
||||
|
||||
// ref: mL2SHPoM_Verif()
|
||||
|
||||
// ref: X = mL2SHPoM_GetDecoySet(N, A, hash_point_cb, preimage_set_gen_cb)
|
||||
std::vector<point_t> P_array(B_array.size());
|
||||
for (size_t i = 0; i < B_array.size(); ++i)
|
||||
P_array[i] = hash_point_lambda(B_array[i]);
|
||||
|
||||
point_t Q_shift = hash_helper_t::hs(A_array, P_array) * c_point_G;
|
||||
|
||||
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(P_array.size() * 2 == N, 6);
|
||||
std::vector<point_t> X_array(N);
|
||||
// X_array = { P_array[0], Q_array[0], P_array[1], Q_array[1], etc.
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
X_array[i] = P_array[i / 2];
|
||||
else
|
||||
X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
|
||||
}
|
||||
|
|
@ -130,6 +130,7 @@ void sc_invert2(unsigned char* recip, const unsigned char* s)
|
|||
sc_sqmul(recip, 8, _11101011);
|
||||
}
|
||||
|
||||
extern void *sha3(const void *in, size_t inlen, void *md, int mdlen);
|
||||
|
||||
|
||||
//
|
||||
|
|
@ -177,7 +178,6 @@ static const fe scalar_L_fe = { 16110573, 10012311, -6632702, 16062397, 5471207,
|
|||
__declspec(align(32))
|
||||
struct scalar_t
|
||||
{
|
||||
//fe m_fe; // 40 bytes, array 10 * 4, optimized form
|
||||
union
|
||||
{
|
||||
uint64_t m_u64[4];
|
||||
|
|
@ -189,6 +189,7 @@ struct scalar_t
|
|||
scalar_t()
|
||||
{}
|
||||
|
||||
// won't check scalar range validity (< L)
|
||||
scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3)
|
||||
{
|
||||
m_u64[0] = a0;
|
||||
|
|
@ -197,14 +198,28 @@ struct scalar_t
|
|||
m_u64[3] = a3;
|
||||
}
|
||||
|
||||
// won't check secret key validity (sk < L)
|
||||
scalar_t(const crypto::secret_key& sk)
|
||||
{
|
||||
from_secret_key(sk);
|
||||
}
|
||||
|
||||
// copy data and reduce
|
||||
scalar_t(const crypto::hash& hash)
|
||||
{
|
||||
m_u64[0] = ((uint64_t*)&hash)[0];
|
||||
m_u64[1] = ((uint64_t*)&hash)[1];
|
||||
m_u64[2] = ((uint64_t*)&hash)[2];
|
||||
m_u64[3] = ((uint64_t*)&hash)[3];
|
||||
sc_reduce32(&m_s[0]);
|
||||
}
|
||||
|
||||
scalar_t(uint64_t v)
|
||||
{
|
||||
zero();
|
||||
if (v == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
reinterpret_cast<uint64_t&>(m_s) = v;
|
||||
m_u64[0] = v;
|
||||
// do not need to call reduce as 2^64 < L
|
||||
}
|
||||
|
||||
|
|
@ -218,28 +233,49 @@ struct scalar_t
|
|||
return &m_s[0];
|
||||
}
|
||||
|
||||
crypto::secret_key &as_secret_key()
|
||||
{
|
||||
return *(crypto::secret_key*)&m_s[0];
|
||||
}
|
||||
|
||||
const crypto::secret_key& as_secret_key() const
|
||||
{
|
||||
return *(const crypto::secret_key*)&m_s[0];
|
||||
}
|
||||
|
||||
operator crypto::secret_key() const
|
||||
{
|
||||
crypto::secret_key result;
|
||||
memcpy(result.data, &m_s, sizeof result.data);
|
||||
//fe_tobytes(reinterpret_cast<unsigned char*>(&result), m_fe);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool from_secret_key(const crypto::secret_key& sk)
|
||||
void from_secret_key(const crypto::secret_key& sk)
|
||||
{
|
||||
//fe_frombytes(m_fe, reinterpret_cast<const unsigned char*>(&sk));
|
||||
return false;
|
||||
uint64_t *p_sk64 = (uint64_t*)&sk;
|
||||
m_u64[0] = p_sk64[0];
|
||||
m_u64[1] = p_sk64[1];
|
||||
m_u64[2] = p_sk64[2];
|
||||
m_u64[3] = p_sk64[3];
|
||||
// assuming secret key is correct (< L), so we don't need to call reduce here
|
||||
}
|
||||
|
||||
void zero()
|
||||
{
|
||||
//fe_0(m_fe);
|
||||
m_u64[0] = 0;
|
||||
m_u64[1] = 0;
|
||||
m_u64[2] = 0;
|
||||
m_u64[3] = 0;
|
||||
//memset(&m_s, 0, sizeof m_s);
|
||||
}
|
||||
|
||||
static scalar_t random()
|
||||
{
|
||||
unsigned char tmp[64];
|
||||
crypto::generate_random_bytes(64, tmp);
|
||||
sc_reduce(tmp);
|
||||
scalar_t result;
|
||||
memcpy(&result.m_s, tmp, sizeof result.m_s);
|
||||
return result;
|
||||
}
|
||||
|
||||
void make_random()
|
||||
|
|
@ -247,7 +283,7 @@ struct scalar_t
|
|||
unsigned char tmp[64];
|
||||
crypto::generate_random_bytes(64, tmp);
|
||||
sc_reduce(tmp);
|
||||
memcpy(&m_s, tmp, 32);
|
||||
memcpy(&m_s, tmp, sizeof m_s);
|
||||
}
|
||||
|
||||
bool is_zero() const
|
||||
|
|
@ -453,6 +489,13 @@ struct point_t
|
|||
|
||||
return true;
|
||||
};
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& ss, const point_t &v)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
ge_p3_tobytes((unsigned char*)&pk, &v.m_p3);
|
||||
return ss << epee::string_tools::pod_to_hex(pk);
|
||||
}
|
||||
}; // struct point_t
|
||||
|
||||
struct point_g_t : public point_t
|
||||
|
|
@ -486,13 +529,106 @@ struct point_g_t : public point_t
|
|||
|
||||
}; // struct point_g_t
|
||||
|
||||
static const point_g_t point_G;
|
||||
static const point_g_t c_point_G;
|
||||
|
||||
static const scalar_t c_scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
|
||||
static const scalar_t c_scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
|
||||
static const scalar_t c_scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
|
||||
static const scalar_t c_scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
|
||||
static const scalar_t c_scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff };
|
||||
|
||||
|
||||
// H_s hash function
|
||||
struct hash_helper_t
|
||||
{
|
||||
static scalar_t hs(const scalar_t& s)
|
||||
{
|
||||
scalar_t result = 0;
|
||||
|
||||
crypto::cn_fast_hash(s.data(), sizeof s, (char*)result.data());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static scalar_t hs(const scalar_t& s, const std::vector<scalar_t>& ss, const std::vector<point_t>& ps)
|
||||
{
|
||||
scalar_t result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static scalar_t hs(const scalar_t& s, const std::vector<point_t>& ps0, const std::vector<point_t>& ps1)
|
||||
{
|
||||
scalar_t result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static scalar_t hs(const std::vector<point_t>& ps0, const std::vector<point_t>& ps1)
|
||||
{
|
||||
scalar_t result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static point_t hp(const point_t& p)
|
||||
{
|
||||
point_t result;
|
||||
crypto::public_key pk = p;
|
||||
|
||||
ge_bytes_hash_to_ec(&result.m_p3, (const unsigned char*)&pk);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// test helpers
|
||||
//
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& ss, const fe &f)
|
||||
{
|
||||
constexpr size_t fe_index_max = (sizeof f / sizeof f[0]) - 1;
|
||||
ss << "{";
|
||||
for (size_t i = 0; i <= fe_index_max; ++i)
|
||||
ss << f[i] << ", ";
|
||||
return ss << f[fe_index_max] << "}";
|
||||
}
|
||||
|
||||
point_t point_from_str(const std::string& str)
|
||||
{
|
||||
crypto::public_key pk;
|
||||
if (!epee::string_tools::parse_tpod_from_hex_string(str, pk))
|
||||
throw std::runtime_error("couldn't parse pub key");
|
||||
|
||||
point_t result;
|
||||
if (!result.from_public_key(pk))
|
||||
throw std::runtime_error("invalid pub key");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
scalar_t scalar_from_str(const std::string& str)
|
||||
{
|
||||
crypto::secret_key sk;
|
||||
if (!epee::string_tools::parse_tpod_from_hex_string(str, sk))
|
||||
throw std::runtime_error("couldn't parse sec key");
|
||||
|
||||
scalar_t result;
|
||||
result.from_secret_key(sk);
|
||||
if (result > c_scalar_Lm1)
|
||||
throw std::runtime_error("sec key scalar >= L");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "L2S.h"
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const scalar_t scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
|
||||
static const scalar_t scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 };
|
||||
static const scalar_t scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
|
||||
static const scalar_t scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff };
|
||||
static const scalar_t scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff };
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -676,31 +812,31 @@ TEST(crypto, scalar_basics)
|
|||
ASSERT_TRUE(z < z + 1);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(scalar_L > 0 && !(scalar_L < 0));
|
||||
ASSERT_TRUE(scalar_Lm1 > 0 && !(scalar_Lm1 < 0));
|
||||
ASSERT_TRUE(scalar_Lm1 < scalar_L);
|
||||
ASSERT_FALSE(scalar_Lm1 > scalar_L);
|
||||
ASSERT_TRUE(scalar_P > scalar_Pm1);
|
||||
ASSERT_FALSE(scalar_P < scalar_Pm1);
|
||||
ASSERT_TRUE(c_scalar_L > 0 && !(c_scalar_L < 0));
|
||||
ASSERT_TRUE(c_scalar_Lm1 > 0 && !(c_scalar_Lm1 < 0));
|
||||
ASSERT_TRUE(c_scalar_Lm1 < c_scalar_L);
|
||||
ASSERT_FALSE(c_scalar_Lm1 > c_scalar_L);
|
||||
ASSERT_TRUE(c_scalar_P > c_scalar_Pm1);
|
||||
ASSERT_FALSE(c_scalar_P < c_scalar_Pm1);
|
||||
|
||||
std::cout << "0 = " << zero << std::endl;
|
||||
std::cout << "1 = " << one << std::endl;
|
||||
std::cout << "L = " << scalar_L << std::endl;
|
||||
std::cout << "L-1 = " << scalar_Lm1 << std::endl;
|
||||
std::cout << "P = " << scalar_P << std::endl;
|
||||
std::cout << "P-1 = " << scalar_Pm1 << std::endl;
|
||||
std::cout << "L = " << c_scalar_L << std::endl;
|
||||
std::cout << "L-1 = " << c_scalar_Lm1 << std::endl;
|
||||
std::cout << "P = " << c_scalar_P << std::endl;
|
||||
std::cout << "P-1 = " << c_scalar_Pm1 << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
// check rolling over L for scalars arithmetics
|
||||
ASSERT_EQ(scalar_Lm1 + 1, 0);
|
||||
ASSERT_EQ(scalar_t(0) - 1, scalar_Lm1);
|
||||
ASSERT_EQ(scalar_Lm1 * 2, scalar_Lm1 - 1); // (L - 1) * 2 = L + L - 2 = (L - 1) - 1 (mod L)
|
||||
ASSERT_EQ(scalar_Lm1 * 100, scalar_Lm1 - 99);
|
||||
ASSERT_EQ(scalar_Lm1 * scalar_Lm1, 1); // (L - 1) * (L - 1) = L*L - 2L + 1 = 1 (mod L)
|
||||
ASSERT_EQ(scalar_Lm1 * (scalar_Lm1 - 1) * scalar_Lm1, scalar_Lm1 - 1);
|
||||
ASSERT_EQ(scalar_L * scalar_L, 0);
|
||||
ASSERT_EQ(c_scalar_Lm1 + 1, 0);
|
||||
ASSERT_EQ(scalar_t(0) - 1, c_scalar_Lm1);
|
||||
ASSERT_EQ(c_scalar_Lm1 * 2, c_scalar_Lm1 - 1); // (L - 1) * 2 = L + L - 2 = (L - 1) - 1 (mod L)
|
||||
ASSERT_EQ(c_scalar_Lm1 * 100, c_scalar_Lm1 - 99);
|
||||
ASSERT_EQ(c_scalar_Lm1 * c_scalar_Lm1, 1); // (L - 1) * (L - 1) = L*L - 2L + 1 = 1 (mod L)
|
||||
ASSERT_EQ(c_scalar_Lm1 * (c_scalar_Lm1 - 1) * c_scalar_Lm1, c_scalar_Lm1 - 1);
|
||||
ASSERT_EQ(c_scalar_L * c_scalar_L, 0);
|
||||
|
||||
ASSERT_EQ(scalar_t(3) / scalar_Lm1, scalar_t(3) * scalar_Lm1); // because (L - 1) ^ 2 = 1
|
||||
ASSERT_EQ(scalar_t(3) / c_scalar_Lm1, scalar_t(3) * c_scalar_Lm1); // because (L - 1) ^ 2 = 1
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -795,24 +931,24 @@ TEST(crypto, scalar_arithmetic_assignment)
|
|||
TEST(crypto, point_basics)
|
||||
{
|
||||
scalar_t s = 4;
|
||||
point_t E = s * point_G;
|
||||
point_t E = s * c_point_G;
|
||||
point_t X = 4 * E;
|
||||
point_t K = 193847 * point_G;
|
||||
point_t K = 193847 * c_point_G;
|
||||
point_t C = E + K;
|
||||
|
||||
ASSERT_EQ(X, 16 * point_G);
|
||||
ASSERT_EQ(X, 16 * c_point_G);
|
||||
ASSERT_EQ(C - K, E);
|
||||
ASSERT_EQ(C - E, K);
|
||||
ASSERT_EQ(C, (193847 + 4) * point_G);
|
||||
ASSERT_EQ(C, (193847 + 4) * c_point_G);
|
||||
|
||||
ASSERT_EQ(point_G / 1, 1 * point_G);
|
||||
ASSERT_EQ(c_point_G / 1, 1 * c_point_G);
|
||||
ASSERT_EQ(C / 3, E / 3 + K / 3);
|
||||
//ASSERT_EQ(K, 61 * (K / (61)));
|
||||
//ASSERT_EQ(K, 192847 * (K / scalar_t(192847)));
|
||||
ASSERT_EQ(K, 61 * (283 * (192847 * (K / (192847ull * 283 * 61)))));
|
||||
|
||||
ASSERT_EQ(E, point_G + point_G + point_G + point_G);
|
||||
ASSERT_EQ(E - point_G, 3 * point_G);
|
||||
ASSERT_EQ(E, c_point_G + c_point_G + c_point_G + c_point_G);
|
||||
ASSERT_EQ(E - c_point_G, 3 * c_point_G);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -851,6 +987,59 @@ TEST(crypto, scalars)
|
|||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ML2S tests
|
||||
//
|
||||
|
||||
TEST(ml2s, rsum)
|
||||
{
|
||||
// Ref: Rsum(3, 8, [1, 2, 3, 4, 5, 6, 7, 8], { 1: 1, 2 : 2, 3 : 3 }, { 1: 4, 2 : 5 }) == 659
|
||||
|
||||
point_t A = scalar_t::random() * c_point_G;
|
||||
point_t result;
|
||||
|
||||
bool r = ml2s_rsum(3, std::vector<point_t>{ A, 2 * A, 3 * A, 4 * A, 5 * A, 6 * A, 7 * A, 8 * A },
|
||||
std::vector<scalar_t>{ 1, 2, 3 }, std::vector<scalar_t>{ 4, 5 }, result);
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(result, 659 * A);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(ml2s, hs)
|
||||
{
|
||||
scalar_t x = 2, p = 250;
|
||||
//sc_exp(r.data(), x.data(), p.data());
|
||||
|
||||
x = 0;
|
||||
|
||||
crypto::hash h;
|
||||
scalar_t r;
|
||||
|
||||
sha3(0, 0, &h, sizeof h);
|
||||
LOG_PRINT("SHA3 0 -> " << h, LOG_LEVEL_0);
|
||||
LOG_PRINT("SHA3 0 -> " << (scalar_t&)h, LOG_LEVEL_0);
|
||||
|
||||
h = crypto::cn_fast_hash(0, 0);
|
||||
LOG_PRINT("CN 0 -> " << h, LOG_LEVEL_0);
|
||||
LOG_PRINT("CN 0 -> " << (scalar_t&)h, LOG_LEVEL_0);
|
||||
|
||||
std::string abc("abc");
|
||||
sha3(abc.c_str(), abc.size(), &h, sizeof h);
|
||||
LOG_PRINT(abc << " -> " << h, LOG_LEVEL_0);
|
||||
LOG_PRINT(abc << " -> " << (scalar_t&)h, LOG_LEVEL_0);
|
||||
|
||||
h = crypto::cn_fast_hash(abc.c_str(), abc.size());
|
||||
LOG_PRINT(abc << " -> " << h, LOG_LEVEL_0);
|
||||
LOG_PRINT(abc << " -> " << (scalar_t&)h, LOG_LEVEL_0);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// test's runner
|
||||
//
|
||||
|
||||
int crypto_tests()
|
||||
{
|
||||
|
|
@ -867,7 +1056,19 @@ int crypto_tests()
|
|||
{
|
||||
auto& test = g_tests[i];
|
||||
TIME_MEASURE_START(runtime);
|
||||
bool r = test.second();
|
||||
bool r = false;
|
||||
try
|
||||
{
|
||||
r = test.second();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LOG_PRINT_RED("EXCEPTION: " << e.what(), LOG_LEVEL_0);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_RED("EXCEPTION: unknown", LOG_LEVEL_0);
|
||||
}
|
||||
TIME_MEASURE_FINISH(runtime);
|
||||
uint64_t runtime_ms = runtime / 1000;
|
||||
uint64_t runtime_mcs = runtime % 1000;
|
||||
|
|
|
|||
199
tests/functional_tests/sha3.cpp
Normal file
199
tests/functional_tests/sha3.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// code from tiny_sha3 project
|
||||
// distibuted under MIT license:
|
||||
// The MIT License(MIT)
|
||||
//
|
||||
// Copyright(c) 2015 Markku - Juhani O.Saarinen
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files(the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions :
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
using namespace std;
|
||||
|
||||
#ifndef KECCAKF_ROUNDS
|
||||
#define KECCAKF_ROUNDS 24
|
||||
#endif
|
||||
|
||||
#ifndef ROTL64
|
||||
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
|
||||
#endif
|
||||
|
||||
// state context
|
||||
typedef struct {
|
||||
union { // state:
|
||||
uint8_t b[200]; // 8-bit bytes
|
||||
uint64_t q[25]; // 64-bit words
|
||||
} st;
|
||||
int pt, rsiz, mdlen; // these don't overflow
|
||||
} sha3_ctx_t;
|
||||
|
||||
|
||||
void sha3_keccakf(uint64_t st[25])
|
||||
{
|
||||
// constants
|
||||
const uint64_t keccakf_rndc[24] = {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
|
||||
};
|
||||
const int keccakf_rotc[24] = {
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
|
||||
};
|
||||
const int keccakf_piln[24] = {
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
|
||||
};
|
||||
|
||||
// variables
|
||||
int i, j, r;
|
||||
uint64_t t, bc[5];
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t *v;
|
||||
|
||||
// endianess conversion. this is redundant on little-endian targets
|
||||
for (i = 0; i < 25; i++) {
|
||||
v = (uint8_t *)&st[i];
|
||||
st[i] = ((uint64_t)v[0]) | (((uint64_t)v[1]) << 8) |
|
||||
(((uint64_t)v[2]) << 16) | (((uint64_t)v[3]) << 24) |
|
||||
(((uint64_t)v[4]) << 32) | (((uint64_t)v[5]) << 40) |
|
||||
(((uint64_t)v[6]) << 48) | (((uint64_t)v[7]) << 56);
|
||||
}
|
||||
#endif
|
||||
|
||||
// actual iteration
|
||||
for (r = 0; r < KECCAKF_ROUNDS; r++) {
|
||||
|
||||
// Theta
|
||||
for (i = 0; i < 5; i++)
|
||||
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
|
||||
for (j = 0; j < 25; j += 5)
|
||||
st[j + i] ^= t;
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
t = st[1];
|
||||
for (i = 0; i < 24; i++) {
|
||||
j = keccakf_piln[i];
|
||||
bc[0] = st[j];
|
||||
st[j] = ROTL64(t, keccakf_rotc[i]);
|
||||
t = bc[0];
|
||||
}
|
||||
|
||||
// Chi
|
||||
for (j = 0; j < 25; j += 5) {
|
||||
for (i = 0; i < 5; i++)
|
||||
bc[i] = st[j + i];
|
||||
for (i = 0; i < 5; i++)
|
||||
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
||||
}
|
||||
|
||||
// Iota
|
||||
st[0] ^= keccakf_rndc[r];
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
// endianess conversion. this is redundant on little-endian targets
|
||||
for (i = 0; i < 25; i++) {
|
||||
v = (uint8_t *)&st[i];
|
||||
t = st[i];
|
||||
v[0] = t & 0xFF;
|
||||
v[1] = (t >> 8) & 0xFF;
|
||||
v[2] = (t >> 16) & 0xFF;
|
||||
v[3] = (t >> 24) & 0xFF;
|
||||
v[4] = (t >> 32) & 0xFF;
|
||||
v[5] = (t >> 40) & 0xFF;
|
||||
v[6] = (t >> 48) & 0xFF;
|
||||
v[7] = (t >> 56) & 0xFF;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialize the context for SHA3
|
||||
|
||||
int sha3_init(sha3_ctx_t *c, int mdlen)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 25; i++)
|
||||
c->st.q[i] = 0;
|
||||
c->mdlen = mdlen;
|
||||
c->rsiz = 200 - 2 * mdlen;
|
||||
c->pt = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// update state with more data
|
||||
|
||||
int sha3_update(sha3_ctx_t *c, const void *data, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int j;
|
||||
|
||||
j = c->pt;
|
||||
for (i = 0; i < len; i++) {
|
||||
c->st.b[j++] ^= ((const uint8_t *)data)[i];
|
||||
if (j >= c->rsiz) {
|
||||
sha3_keccakf(c->st.q);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
c->pt = j;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// finalize and output a hash
|
||||
|
||||
int sha3_final(void *md, sha3_ctx_t *c)
|
||||
{
|
||||
int i;
|
||||
|
||||
c->st.b[c->pt] ^= 0x06;
|
||||
c->st.b[c->rsiz - 1] ^= 0x80;
|
||||
sha3_keccakf(c->st.q);
|
||||
|
||||
for (i = 0; i < c->mdlen; i++) {
|
||||
((uint8_t *)md)[i] = c->st.b[i];
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// compute a SHA-3 hash (md) of given byte length from "in"
|
||||
|
||||
void *sha3(const void *in, size_t inlen, void *md, int mdlen)
|
||||
{
|
||||
sha3_ctx_t sha3;
|
||||
|
||||
sha3_init(&sha3, mdlen);
|
||||
sha3_update(&sha3, in, inlen);
|
||||
sha3_final(md, &sha3);
|
||||
|
||||
return md;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue