Merge pull request #3253

e136bc6b tweaks to the monerov1 cryptonight algorithm (Lee Clagett)
d58c9ec9 slow-hash: optimized version (SChernykh)
608fd6f1 Monero Cryptonight variants, and add one for v7 (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2018-03-06 08:41:02 +02:00
commit c102c49da5
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
9 changed files with 107 additions and 17 deletions

View File

@ -69,10 +69,10 @@ namespace crypto {
chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher); chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
} }
inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, bool prehashed=false) { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, int cn_variant = 0, bool prehashed=false) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash; tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash_pre(data, size, pwd_hash.data(), prehashed); crypto::cn_slow_hash_pre(data, size, pwd_hash.data(), cn_variant, prehashed);
memcpy(&key, pwd_hash.data(), sizeof(key)); memcpy(&key, pwd_hash.data(), sizeof(key));
} }

View File

@ -79,8 +79,8 @@ enum {
}; };
void cn_fast_hash(const void *data, size_t length, char *hash); void cn_fast_hash(const void *data, size_t length, char *hash);
void cn_slow_hash(const void *data, size_t length, char *hash); void cn_slow_hash(const void *data, size_t length, char *hash, int variant);
void cn_slow_hash_pre(const void *data, size_t length, char *hash, bool pre); void cn_slow_hash_pre(const void *data, size_t length, char *hash, int variant, bool pre);
void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_blake(const void *data, size_t length, char *hash);
void hash_extra_groestl(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash);

View File

@ -71,8 +71,8 @@ namespace crypto {
return h; return h;
} }
inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) {
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash)); cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant);
} }
inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) {

View File

@ -32,6 +32,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "common/int-util.h" #include "common/int-util.h"
#include "hash-ops.h" #include "hash-ops.h"
@ -47,6 +49,46 @@
extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey);
extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
#define VARIANT1_1(p) \
do if (variant > 0) \
{ \
const uint8_t tmp = ((const uint8_t*)(p))[11]; \
static const uint32_t table = 0x75310; \
const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \
((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \
} while(0)
#define VARIANT1_2(p) \
do if (variant > 0) \
{ \
xor64(p, tweak1_2); \
} while(0)
#define VARIANT1_CHECK() \
do if (length < 43) \
{ \
fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \
_exit(1); \
} while(0)
#define NONCE_POINTER (((const uint8_t*)data)+35)
#define VARIANT1_PORTABLE_INIT() \
uint8_t tweak1_2[8]; \
do if (variant > 0) \
{ \
VARIANT1_CHECK(); \
memcpy(&tweak1_2, &state.hs.b[192], sizeof(tweak1_2)); \
xor64(tweak1_2, NONCE_POINTER); \
} while(0)
#define VARIANT1_INIT64() \
if (variant > 0) \
{ \
VARIANT1_CHECK(); \
} \
const uint64_t tweak1_2 = variant > 0 ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0
#if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)))
// Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI
// Fall back to more portable code is down at the bottom // Fall back to more portable code is down at the bottom
@ -125,6 +167,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
_mm_store_si128(R128(c), _c); \ _mm_store_si128(R128(c), _c); \
_b = _mm_xor_si128(_b, _c); \ _b = _mm_xor_si128(_b, _c); \
_mm_store_si128(R128(&hp_state[j]), _b); \ _mm_store_si128(R128(&hp_state[j]), _b); \
VARIANT1_1(&hp_state[j]); \
j = state_index(c); \ j = state_index(c); \
p = U64(&hp_state[j]); \ p = U64(&hp_state[j]); \
b[0] = p[0]; b[1] = p[1]; \ b[0] = p[0]; b[1] = p[1]; \
@ -133,6 +176,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
p = U64(&hp_state[j]); \ p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \ p[0] = a[0]; p[1] = a[1]; \
a[0] ^= b[0]; a[1] ^= b[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \
VARIANT1_2(p + 1); \
_b = _c; \ _b = _c; \
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -183,6 +227,11 @@ STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b)
U64(a)[1] ^= U64(b)[1]; U64(a)[1] ^= U64(b)[1];
} }
STATIC INLINE void xor64(uint64_t *a, const uint64_t b)
{
*a ^= b;
}
/** /**
* @brief uses cpuid to determine if the CPU supports the AES instructions * @brief uses cpuid to determine if the CPU supports the AES instructions
* @return true if the CPU supports AES, false otherwise * @return true if the CPU supports AES, false otherwise
@ -515,11 +564,11 @@ void slow_hash_free_state(void)
* @param length the length in bytes of the data * @param length the length in bytes of the data
* @param hash a pointer to a buffer in which the final 256 bit hash will be stored * @param hash a pointer to a buffer in which the final 256 bit hash will be stored
*/ */
void cn_slow_hash(const void *data, size_t length, char *hash) { void cn_slow_hash(const void *data, size_t length, char *hash, int variant) {
cn_slow_hash_pre(data,length,hash,false); cn_slow_hash_pre(data,length,hash,variant,false);
} }
void cn_slow_hash_pre(const void *data, size_t length, char *hash, bool prehashed) void cn_slow_hash_pre(const void *data, size_t length, char *hash, int variant, bool prehashed)
{ {
RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */
@ -553,6 +602,8 @@ void cn_slow_hash_pre(const void *data, size_t length, char *hash, bool prehashe
} }
memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
/* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill
* the 2MB large random access buffer. * the 2MB large random access buffer.
*/ */
@ -676,6 +727,11 @@ void slow_hash_free_state(void)
#define U64(x) ((uint64_t *) (x)) #define U64(x) ((uint64_t *) (x))
STATIC INLINE void xor64(uint64 *a, const uint64 b)
{
*a ^= b;
}
#pragma pack(push, 1) #pragma pack(push, 1)
union cn_slow_hash_state union cn_slow_hash_state
{ {
@ -712,6 +768,7 @@ union cn_slow_hash_state
vst1q_u8((uint8_t *)c, _c); \ vst1q_u8((uint8_t *)c, _c); \
_b = veorq_u8(_b, _c); \ _b = veorq_u8(_b, _c); \
vst1q_u8(&hp_state[j], _b); \ vst1q_u8(&hp_state[j], _b); \
VARIANT1_1(&hp_state[j]); \
j = state_index(c); \ j = state_index(c); \
p = U64(&hp_state[j]); \ p = U64(&hp_state[j]); \
b[0] = p[0]; b[1] = p[1]; \ b[0] = p[0]; b[1] = p[1]; \
@ -720,6 +777,7 @@ union cn_slow_hash_state
p = U64(&hp_state[j]); \ p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \ p[0] = a[0]; p[1] = a[1]; \
a[0] ^= b[0]; a[1] ^= b[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \
VARIANT1_2(p + 1); \
_b = _c; \ _b = _c; \
@ -851,7 +909,7 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u
} }
} }
void cn_slow_hash(const void *data, size_t length, char *hash) void cn_slow_hash(const void *data, size_t length, char *hash, int variant)
{ {
RDATA_ALIGN16 uint8_t expandedKey[240]; RDATA_ALIGN16 uint8_t expandedKey[240];
RDATA_ALIGN16 uint8_t hp_state[MEMORY]; RDATA_ALIGN16 uint8_t hp_state[MEMORY];
@ -877,6 +935,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
hash_process(&state.hs, data, length); hash_process(&state.hs, data, length);
memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
/* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill
* the 2MB large random access buffer. * the 2MB large random access buffer.
*/ */
@ -1045,7 +1105,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b)
U64(a)[1] ^= U64(b)[1]; U64(a)[1] ^= U64(b)[1];
} }
void cn_slow_hash(const void *data, size_t length, char *hash) void cn_slow_hash(const void *data, size_t length, char *hash, int variant)
{ {
uint8_t text[INIT_SIZE_BYTE]; uint8_t text[INIT_SIZE_BYTE];
uint8_t a[AES_BLOCK_SIZE]; uint8_t a[AES_BLOCK_SIZE];
@ -1074,6 +1134,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
hash_process(&state.hs, data, length); hash_process(&state.hs, data, length);
memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(text, state.init, INIT_SIZE_BYTE);
VARIANT1_INIT64();
aes_ctx = (oaes_ctx *) oaes_alloc(); aes_ctx = (oaes_ctx *) oaes_alloc();
oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE);
@ -1103,6 +1165,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
xor_blocks(b, p); xor_blocks(b, p);
swap_blocks(b, p); swap_blocks(b, p);
swap_blocks(a, b); swap_blocks(a, b);
VARIANT1_1(p);
// Iteration 2 // Iteration 2
p = &long_state[state_index(a)]; p = &long_state[state_index(a)];
@ -1112,6 +1175,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
swap_blocks(b, p); swap_blocks(b, p);
xor_blocks(b, p); xor_blocks(b, p);
swap_blocks(a, b); swap_blocks(a, b);
VARIANT1_2(U64(p) + 1);
} }
memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(text, state.init, INIT_SIZE_BYTE);
@ -1206,6 +1270,15 @@ static void xor_blocks(uint8_t* a, const uint8_t* b) {
} }
} }
static void xor64(uint8_t* left, const uint8_t* right)
{
size_t i;
for (i = 0; i < 8; ++i)
{
left[i] ^= right[i];
}
}
#pragma pack(push, 1) #pragma pack(push, 1)
union cn_slow_hash_state { union cn_slow_hash_state {
union hash_state hs; union hash_state hs;
@ -1216,7 +1289,7 @@ union cn_slow_hash_state {
}; };
#pragma pack(pop) #pragma pack(pop)
void cn_slow_hash(const void *data, size_t length, char *hash) { void cn_slow_hash(const void *data, size_t length, char *hash, int variant) {
uint8_t long_state[MEMORY]; uint8_t long_state[MEMORY];
union cn_slow_hash_state state; union cn_slow_hash_state state;
uint8_t text[INIT_SIZE_BYTE]; uint8_t text[INIT_SIZE_BYTE];
@ -1233,6 +1306,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) {
memcpy(aes_key, state.hs.b, AES_KEY_SIZE); memcpy(aes_key, state.hs.b, AES_KEY_SIZE);
aes_ctx = (oaes_ctx *) oaes_alloc(); aes_ctx = (oaes_ctx *) oaes_alloc();
VARIANT1_PORTABLE_INIT();
oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE);
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
for (j = 0; j < INIT_SIZE_BLK; j++) { for (j = 0; j < INIT_SIZE_BLK; j++) {
@ -1260,6 +1335,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) {
copy_block(&long_state[j * AES_BLOCK_SIZE], c); copy_block(&long_state[j * AES_BLOCK_SIZE], c);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE));
swap_blocks(a, b); swap_blocks(a, b);
VARIANT1_1(&long_state[j * AES_BLOCK_SIZE]);
/* Iteration 2 */ /* Iteration 2 */
j = e2i(a, MEMORY / AES_BLOCK_SIZE); j = e2i(a, MEMORY / AES_BLOCK_SIZE);
copy_block(c, &long_state[j * AES_BLOCK_SIZE]); copy_block(c, &long_state[j * AES_BLOCK_SIZE]);
@ -1267,6 +1343,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) {
sum_half_blocks(b, d); sum_half_blocks(b, d);
swap_blocks(b, c); swap_blocks(b, c);
xor_blocks(b, c); xor_blocks(b, c);
VARIANT1_2(c + 8);
copy_block(&long_state[j * AES_BLOCK_SIZE], c); copy_block(&long_state[j * AES_BLOCK_SIZE], c);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE));
swap_blocks(a, b); swap_blocks(a, b);

View File

@ -1022,7 +1022,8 @@ namespace cryptonote
return true; return true;
} }
blobdata bd = get_block_hashing_blob(b); blobdata bd = get_block_hashing_blob(b);
crypto::cn_slow_hash(bd.data(), bd.size(), res); const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0;
crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant);
return true; return true;
} }
//--------------------------------------------------------------- //---------------------------------------------------------------

View File

@ -511,7 +511,7 @@ namespace hw {
char prekey[200]; char prekey[200];
memmove(prekey, &this->buffer_recv[0], 200); memmove(prekey, &this->buffer_recv[0], 200);
crypto::generate_chacha_key(&prekey[0], sizeof(prekey), key, true); crypto::generate_chacha_key(&prekey[0], sizeof(prekey), key, 0, true);
#ifdef DEBUG_HWDEVICE #ifdef DEBUG_HWDEVICE
hw::ledger::check32("generate_chacha_key", "key", (char*)key_x.data(), (char*)key.data()); hw::ledger::check32("generate_chacha_key", "key", (char*)key_x.data(), (char*)key.data());

View File

@ -43,7 +43,7 @@ set_property(TARGET hash-tests
PROPERTY PROPERTY
FOLDER "tests") FOLDER "tests")
foreach (hash IN ITEMS fast slow tree extra-blake extra-groestl extra-jh extra-skein) foreach (hash IN ITEMS fast slow slow-1 tree extra-blake extra-groestl extra-jh extra-skein)
add_test( add_test(
NAME "hash-${hash}" NAME "hash-${hash}"
COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt")

View File

@ -51,6 +51,12 @@ extern "C" {
} }
tree_hash((const char (*)[32]) data, length >> 5, hash); tree_hash((const char (*)[32]) data, length >> 5, hash);
} }
static void cn_slow_hash_0(const void *data, size_t length, char *hash) {
return cn_slow_hash(data, length, hash, 0);
}
static void cn_slow_hash_1(const void *data, size_t length, char *hash) {
return cn_slow_hash(data, length, hash, 1);
}
} }
POP_WARNINGS POP_WARNINGS
@ -58,9 +64,10 @@ extern "C" typedef void hash_f(const void *, size_t, char *);
struct hash_func { struct hash_func {
const string name; const string name;
hash_f &f; hash_f &f;
} hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash}, {"tree", hash_tree}, } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree},
{"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl},
{"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}}; {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein},
{"slow-1", cn_slow_hash_1}};
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
hash_f *f; hash_f *f;

View File

@ -0,0 +1,5 @@
b5a7f63abb94d07d1a6445c36c07c7e8327fe61b1647e391b4c7edae5de57a3d 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000
80563c40ed46575a9e44820d93ee095e2851aa22483fd67837118c6cd951ba61 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
5bb40c5880cef2f739bdb6aaaf16161eaae55530e7b10d7ea996b751a299e949 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589
613e638505ba1fd05f428d5c9f8e08f8165614342dac419adc6a47dce257eb3e 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300
ed082e49dbd5bbe34a3726a0d1dad981146062b39d36d62c71eb1ed8ab49459b 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb