void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { // The blockchain block at height 202612 http://monerochain.info/block/bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698 // contained 514 transactions, that triggered bad calculation of variable "cnt" in the original version of this function // as from CryptoNote code. // // This bug applies to all CN altcoins. // // Mathematical bug here was first published on 14:45:34 (GMT+2) 2014-09-04 by Rafal Freeman <rfree> // https://github.com/rfree2monero/bitmonero/commit/b417abfb7a297d09f1bbb6de29030f8de9952ac8 // and soon also applied to CryptoNote (15:10 GMT+2), and BoolBerry used not fully correct work around: // the work around of sizeof(size_t)*8 or <<3 as used before in 2 coins and in BBL later was blocking // exploitation on normal platforms, how ever we strongly recommend the following fix because it removes // mistake in mathematical formula. assert(count > 0); if (count == 1) { memcpy(root_hash, hashes, HASH_SIZE); } else if (count == 2) { cn_fast_hash(hashes, 2 * HASH_SIZE, root_hash); } else { size_t i, j; size_t cnt = tree_hash_cnt( count ); size_t max_size_t = (size_t) -1; // max allowed value of size_t assert( cnt < max_size_t/2 ); // reasonable size to avoid any overflows. /2 is extra; Anyway should be limited much stronger by logical code // as we have sane limits on transactions counts in blockchain rules char (*ints)[HASH_SIZE]; size_t ints_size = cnt * HASH_SIZE; ints = alloca(ints_size); memset( ints , 0 , ints_size); // allocate, and zero out as extra protection for using uninitialized mem memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { cn_fast_hash(hashes[i], 64, ints[j]); } assert(i == count); while (cnt > 2) { cnt >>= 1; for (i = 0, j = 0; j < cnt; i += 2, ++j) { cn_fast_hash(ints[i], 64, ints[j]); } } cn_fast_hash(ints[0], 64, root_hash); } }
//cn_fast_hash for a key-vector of arbitrary length //this is useful since you take a number of keys //put them in the key vector and it concatenates them //and then hashes them key cn_fast_hash(const keyV &keys) { if (keys.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0)); key rv; cn_fast_hash(rv, &keys[0], keys.size() * sizeof(keys[0])); //dp(rv); return rv; }
bool tx_extra_message::decrypt(size_t index, const Crypto::PublicKey &txkey, const Crypto::SecretKey *recepient_secret_key, std::string &message) const { size_t mlen = data.size(); if (mlen < TX_EXTRA_MESSAGE_CHECKSUM_SIZE) { return false; } const char *buf; std::unique_ptr<char[]> ptr; if (recepient_secret_key != nullptr) { ptr.reset(new char[mlen]); assert(ptr); message_key_data key_data; if (!generate_key_derivation(txkey, *recepient_secret_key, key_data.derivation)) { return false; } key_data.magic1 = 0x80; key_data.magic2 = 0; Hash h = cn_fast_hash(&key_data, sizeof(message_key_data)); uint64_t nonce = SWAP64LE(index); chacha(10, data.data(), mlen, reinterpret_cast<uint8_t *>(&h), reinterpret_cast<uint8_t *>(&nonce), ptr.get()); buf = ptr.get(); } else { buf = data.data(); } mlen -= TX_EXTRA_MESSAGE_CHECKSUM_SIZE; for (size_t i = 0; i < TX_EXTRA_MESSAGE_CHECKSUM_SIZE; i++) { if (buf[mlen + i] != 0) { return false; } } message.assign(buf, mlen); return true; }
static void hash_to_ec(const public_key &key, ge_p3 &res) { hash h; ge_p2 point; ge_p1p1 point2; cn_fast_hash(std::addressof(key), sizeof(public_key), h); ge_fromfe_frombytes_vartime(&point, reinterpret_cast<const unsigned char *>(&h)); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); }
bool parseAndValidateTransactionFromBinaryArray(const BinaryArray& tx_blob, Transaction& tx, Hash& tx_hash, Hash& tx_prefix_hash) { if (!fromBinaryArray(tx, tx_blob)) { return false; } //TODO: validate tx cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); getObjectHash(*static_cast<TransactionPrefix*>(&tx), tx_prefix_hash); return true; }
void hashToPoint(key & pointk, const key & hh) { ge_p2 point; ge_p1p1 point2; ge_p3 res; key h = cn_fast_hash(hh); ge_fromfe_frombytes_vartime(&point, h.bytes); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); ge_p3_tobytes(pointk.bytes, &res); }
//cn_fast_hash for multisig purpose //This takes the outputs and commitments //and hashes them into a 32 byte sized key key cn_fast_hash(ctkeyV PC) { key rv = identity(); std::size_t l = (std::size_t)PC.size(); size_t i = 0, j = 0; vector<char> m(l * 64); for (i = 0 ; i < l ; i++) { memcpy(&m[i * 64], &PC[i].dest, 32); memcpy(&m[i * 64 + 32], &PC[i].mask, 32); } cn_fast_hash(rv, &m[0], 64*l); return rv; }
//cn_fast_hash for a key-vector of arbitrary length //this is useful since you take a number of keys //put them in the key vector and it concatenates them //and then hashes them key cn_fast_hash(const keyV &keys) { size_t l = keys.size(); vector<unsigned char> m(l * 32); size_t i; for (i = 0 ; i < l ; i++) { memcpy(&m[i * 32], keys[i].bytes, 32); } key rv; cn_fast_hash(rv, &m[0], 32 * l); //dp(rv); return rv; }
key hashToPointSimple(const key & hh) { key pointk; ge_p1p1 point2; ge_p2 point; ge_p3 res; key h = cn_fast_hash(hh); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_p3_to_p2(&point, &res); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); ge_p3_tobytes(pointk.bytes, &res); return pointk; }
bool tx_extra_message::encrypt(size_t index, const std::string &message, const AccountPublicAddress *recipient, const KeyPair &txkey) { size_t mlen = message.size(); std::unique_ptr<char[]> buf(new char[mlen + TX_EXTRA_MESSAGE_CHECKSUM_SIZE]); memcpy(buf.get(), message.data(), mlen); memset(buf.get() + mlen, 0, TX_EXTRA_MESSAGE_CHECKSUM_SIZE); mlen += TX_EXTRA_MESSAGE_CHECKSUM_SIZE; if (recipient) { message_key_data key_data; if (!generate_key_derivation(recipient->spendPublicKey, txkey.secretKey, key_data.derivation)) { return false; } key_data.magic1 = 0x80; key_data.magic2 = 0; Hash h = cn_fast_hash(&key_data, sizeof(message_key_data)); uint64_t nonce = SWAP64LE(index); chacha(10, buf.get(), mlen, reinterpret_cast<uint8_t *>(&h), reinterpret_cast<uint8_t *>(&nonce), buf.get()); } data.assign(buf.get(), mlen); return true; }
void hash_to_scalar(key & hash, const key & in) { cn_fast_hash(hash, in); sc_reduce32(hash.bytes); }
key hash_to_scalar(const keyV &keys) { key rv = cn_fast_hash(keys); sc_reduce32(rv.bytes); return rv; }
key hash_to_scalar(ctkeyV PC) { key rv = cn_fast_hash(PC); sc_reduce32(rv.bytes); return rv; }
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { cn_fast_hash(data, length, reinterpret_cast<hash &>(res)); sc_reduce32(&res); }
void hash_to_scalar(key &hash, const void * data, const std::size_t l) { cn_fast_hash(hash, data, l); sc_reduce32(hash.bytes); }
key cn_fast_hash(const key64 keys) { key rv; cn_fast_hash(rv, &keys[0], 64 * sizeof(keys[0])); //dp(rv); return rv; }
void getBinaryArrayHash(const BinaryArray& binaryArray, Crypto::Hash& hash) { cn_fast_hash(binaryArray.data(), binaryArray.size(), hash); }
//cn_fast_hash for multisig purpose //This takes the outputs and commitments //and hashes them into a 32 byte sized key key cn_fast_hash(const ctkeyV &PC) { if (PC.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0)); key rv; cn_fast_hash(rv, &PC[0], 64*PC.size()); return rv; }
key hash_to_scalar(const key & in) { key hash = cn_fast_hash(in); sc_reduce32(hash.bytes); return hash; }
void get_blob_hash(const blobdata& blob, crypto::hash& res) { cn_fast_hash(blob.data(), blob.size(), res); }