Skip to content

Commit 18d2cbc

Browse files
frozennovazone117x
authored andcommitted
Add Cryptonight Fast support (zone117x#60)
1 parent 8693954 commit 18d2cbc

File tree

4 files changed

+272
-1
lines changed

4 files changed

+272
-1
lines changed

binding.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"boolberry.cc",
1010
"c11.c",
1111
"cryptonight.c",
12+
"cryptonight_fast.c",
1213
"fresh.c",
1314
"fugue.c",
1415
"groestl.c",

cryptonight_fast.c

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Copyright (c) 2012-2013 The Cryptonote developers
2+
// Distributed under the MIT/X11 software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
// Portions Copyright (c) 2018 The Monero developers
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <unistd.h>
9+
#include "crypto/oaes_lib.h"
10+
#include "crypto/c_keccak.h"
11+
#include "crypto/c_groestl.h"
12+
#include "crypto/c_blake256.h"
13+
#include "crypto/c_jh.h"
14+
#include "crypto/c_skein.h"
15+
#include "crypto/int-util.h"
16+
#include "crypto/hash-ops.h"
17+
18+
#define MEMORY (1 << 21) /* 2 MiB */
19+
#define ITER (1 << 19)
20+
#define AES_BLOCK_SIZE 16
21+
#define AES_KEY_SIZE 32 /*16*/
22+
#define INIT_SIZE_BLK 8
23+
#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE)
24+
25+
#define VARIANT1_1(p) \
26+
do if (variant > 0) \
27+
{ \
28+
const uint8_t tmp = ((const uint8_t*)(p))[11]; \
29+
static const uint32_t table = 0x75310; \
30+
const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \
31+
((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \
32+
} while(0)
33+
34+
#define VARIANT1_2(p) \
35+
do if (variant > 0) \
36+
{ \
37+
((uint64_t*)p)[1] ^= tweak1_2; \
38+
} while(0)
39+
40+
#define VARIANT1_INIT() \
41+
if (variant > 0 && len < 43) \
42+
{ \
43+
fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \
44+
_exit(1); \
45+
} \
46+
const uint64_t tweak1_2 = variant > 0 ? *(const uint64_t*)(((const uint8_t*)input)+35) ^ ctx->state.hs.w[24] : 0
47+
48+
#pragma pack(push, 1)
49+
union cn_slow_hash_state {
50+
union hash_state hs;
51+
struct {
52+
uint8_t k[64];
53+
uint8_t init[INIT_SIZE_BYTE];
54+
};
55+
};
56+
#pragma pack(pop)
57+
58+
static void do_fast_blake_hash(const void* input, size_t len, char* output) {
59+
blake256_hash((uint8_t*)output, input, len);
60+
}
61+
62+
void do_fast_groestl_hash(const void* input, size_t len, char* output) {
63+
groestl(input, len * 8, (uint8_t*)output);
64+
}
65+
66+
static void do_fast_jh_hash(const void* input, size_t len, char* output) {
67+
int r = jh_hash(HASH_SIZE * 8, input, 8 * len, (uint8_t*)output);
68+
assert(SUCCESS == r);
69+
}
70+
71+
static void do_fast_skein_hash(const void* input, size_t len, char* output) {
72+
int r = c_skein_hash(8 * HASH_SIZE, input, 8 * len, (uint8_t*)output);
73+
assert(SKEIN_SUCCESS == r);
74+
}
75+
76+
static void (* const extra_hashes[4])(const void *, size_t, char *) = {
77+
do_fast_blake_hash, do_fast_groestl_hash, do_fast_jh_hash, do_fast_skein_hash
78+
};
79+
80+
extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey);
81+
extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
82+
83+
static inline size_t e2i(const uint8_t* a) {
84+
return (*((uint64_t*) a) / AES_BLOCK_SIZE) & (MEMORY / AES_BLOCK_SIZE - 1);
85+
}
86+
87+
static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) {
88+
((uint64_t*) res)[1] = mul128(((uint64_t*) a)[0], ((uint64_t*) b)[0], (uint64_t*) res);
89+
}
90+
91+
static void mul_sum_xor_dst(const uint8_t* a, uint8_t* c, uint8_t* dst) {
92+
uint64_t hi, lo = mul128(((uint64_t*) a)[0], ((uint64_t*) dst)[0], &hi) + ((uint64_t*) c)[1];
93+
hi += ((uint64_t*) c)[0];
94+
95+
((uint64_t*) c)[0] = ((uint64_t*) dst)[0] ^ hi;
96+
((uint64_t*) c)[1] = ((uint64_t*) dst)[1] ^ lo;
97+
((uint64_t*) dst)[0] = hi;
98+
((uint64_t*) dst)[1] = lo;
99+
}
100+
101+
static void sum_half_blocks(uint8_t* a, const uint8_t* b) {
102+
uint64_t a0, a1, b0, b1;
103+
104+
a0 = SWAP64LE(((uint64_t*) a)[0]);
105+
a1 = SWAP64LE(((uint64_t*) a)[1]);
106+
b0 = SWAP64LE(((uint64_t*) b)[0]);
107+
b1 = SWAP64LE(((uint64_t*) b)[1]);
108+
a0 += b0;
109+
a1 += b1;
110+
((uint64_t*) a)[0] = SWAP64LE(a0);
111+
((uint64_t*) a)[1] = SWAP64LE(a1);
112+
}
113+
114+
static inline void copy_block(uint8_t* dst, const uint8_t* src) {
115+
((uint64_t*) dst)[0] = ((uint64_t*) src)[0];
116+
((uint64_t*) dst)[1] = ((uint64_t*) src)[1];
117+
}
118+
119+
static void swap_blocks(uint8_t* a, uint8_t* b) {
120+
size_t i;
121+
uint8_t t;
122+
for (i = 0; i < AES_BLOCK_SIZE; i++) {
123+
t = a[i];
124+
a[i] = b[i];
125+
b[i] = t;
126+
}
127+
}
128+
129+
static inline void xor_blocks(uint8_t* a, const uint8_t* b) {
130+
((uint64_t*) a)[0] ^= ((uint64_t*) b)[0];
131+
((uint64_t*) a)[1] ^= ((uint64_t*) b)[1];
132+
}
133+
134+
static inline void xor_blocks_dst(const uint8_t* a, const uint8_t* b, uint8_t* dst) {
135+
((uint64_t*) dst)[0] = ((uint64_t*) a)[0] ^ ((uint64_t*) b)[0];
136+
((uint64_t*) dst)[1] = ((uint64_t*) a)[1] ^ ((uint64_t*) b)[1];
137+
}
138+
139+
struct cryptonightfast_ctx {
140+
uint8_t long_state[MEMORY];
141+
union cn_slow_hash_state state;
142+
uint8_t text[INIT_SIZE_BYTE];
143+
uint8_t a[AES_BLOCK_SIZE];
144+
uint8_t b[AES_BLOCK_SIZE];
145+
uint8_t c[AES_BLOCK_SIZE];
146+
uint8_t aes_key[AES_KEY_SIZE];
147+
oaes_ctx* aes_ctx;
148+
};
149+
150+
void cryptonightfast_hash(const char* input, char* output, uint32_t len, int variant) {
151+
struct cryptonightfast_ctx *ctx = alloca(sizeof(struct cryptonightfast_ctx));
152+
hash_process(&ctx->state.hs, (const uint8_t*) input, len);
153+
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
154+
memcpy(ctx->aes_key, ctx->state.hs.b, AES_KEY_SIZE);
155+
ctx->aes_ctx = (oaes_ctx*) oaes_alloc();
156+
size_t i, j;
157+
158+
VARIANT1_INIT();
159+
160+
oaes_key_import_data(ctx->aes_ctx, ctx->aes_key, AES_KEY_SIZE);
161+
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
162+
for (j = 0; j < INIT_SIZE_BLK; j++) {
163+
aesb_pseudo_round(&ctx->text[AES_BLOCK_SIZE * j],
164+
&ctx->text[AES_BLOCK_SIZE * j],
165+
ctx->aes_ctx->key->exp_data);
166+
}
167+
memcpy(&ctx->long_state[i * INIT_SIZE_BYTE], ctx->text, INIT_SIZE_BYTE);
168+
}
169+
170+
for (i = 0; i < 16; i++) {
171+
ctx->a[i] = ctx->state.k[i] ^ ctx->state.k[32 + i];
172+
ctx->b[i] = ctx->state.k[16 + i] ^ ctx->state.k[48 + i];
173+
}
174+
175+
for (i = 0; i < ITER / 2; i++) {
176+
/* Dependency chain: address -> read value ------+
177+
* written value <-+ hard function (AES or MUL) <+
178+
* next address <-+
179+
*/
180+
/* Iteration 1 */
181+
j = e2i(ctx->a);
182+
aesb_single_round(&ctx->long_state[j * AES_BLOCK_SIZE], ctx->c, ctx->a);
183+
xor_blocks_dst(ctx->c, ctx->b, &ctx->long_state[j * AES_BLOCK_SIZE]);
184+
VARIANT1_1((uint8_t*)&ctx->long_state[j * AES_BLOCK_SIZE]);
185+
/* Iteration 2 */
186+
mul_sum_xor_dst(ctx->c, ctx->a,
187+
&ctx->long_state[e2i(ctx->c) * AES_BLOCK_SIZE]);
188+
copy_block(ctx->b, ctx->c);
189+
VARIANT1_2((uint8_t*)
190+
&ctx->long_state[e2i(ctx->c) * AES_BLOCK_SIZE]);
191+
}
192+
193+
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
194+
oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE);
195+
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
196+
for (j = 0; j < INIT_SIZE_BLK; j++) {
197+
xor_blocks(&ctx->text[j * AES_BLOCK_SIZE],
198+
&ctx->long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]);
199+
aesb_pseudo_round(&ctx->text[j * AES_BLOCK_SIZE],
200+
&ctx->text[j * AES_BLOCK_SIZE],
201+
ctx->aes_ctx->key->exp_data);
202+
}
203+
}
204+
memcpy(ctx->state.init, ctx->text, INIT_SIZE_BYTE);
205+
hash_permutation(&ctx->state.hs);
206+
/*memcpy(hash, &state, 32);*/
207+
extra_hashes[ctx->state.hs.b[0] & 3](&ctx->state, 200, output);
208+
oaes_free((OAES_CTX **) &ctx->aes_ctx);
209+
}
210+
211+
void cryptonightfast_fast_hash(const char* input, char* output, uint32_t len) {
212+
union hash_state state;
213+
hash_process(&state, (const uint8_t*) input, len);
214+
memcpy(output, &state, HASH_SIZE);
215+
}

cryptonight_fast.h

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef CRYPTONIGHTFAST_H
2+
#define CRYPTONIGHTFAST_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include <stdint.h>
9+
10+
void cryptonightfast_hash(const char* input, char* output, uint32_t len, int variant);
11+
void cryptonightfast_fast_hash(const char* input, char* output, uint32_t len);
12+
13+
#ifdef __cplusplus
14+
}
15+
#endif
16+
17+
#endif

multihashing.cc

+39-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88
#include "blake.h"
99
#include "c11.h"
1010
#include "cryptonight.h"
11+
#include "cryptonight_fast.h"
1112
#include "fresh.h"
1213
#include "fugue.h"
1314
#include "groestl.h"
@@ -234,7 +235,43 @@ DECLARE_FUNC(cryptonight) {
234235
}
235236
SET_BUFFER_RETURN(output, 32);
236237
}
238+
DECLARE_FUNC(cryptonightfast) {
239+
DECLARE_SCOPE;
237240

241+
bool fast = false;
242+
uint32_t cn_variant = 0;
243+
244+
if (args.Length() < 1)
245+
RETURN_EXCEPT("You must provide one argument.");
246+
247+
if (args.Length() >= 2) {
248+
if(args[1]->IsBoolean())
249+
fast = args[1]->BooleanValue();
250+
else if(args[1]->IsUint32())
251+
cn_variant = args[1]->Uint32Value();
252+
else
253+
RETURN_EXCEPT("Argument 2 should be a boolean or uint32_t");
254+
}
255+
256+
Local<Object> target = args[0]->ToObject();
257+
258+
if(!Buffer::HasInstance(target))
259+
RETURN_EXCEPT("Argument should be a buffer object.");
260+
261+
char * input = Buffer::Data(target);
262+
char output[32];
263+
264+
uint32_t input_len = Buffer::Length(target);
265+
266+
if(fast)
267+
cryptonightfast_fast_hash(input, output, input_len);
268+
else {
269+
if (cn_variant > 0 && input_len < 43)
270+
RETURN_EXCEPT("Argument must be 43 bytes for monero variant 1+");
271+
cryptonightfast_hash(input, output, input_len, cn_variant);
272+
}
273+
SET_BUFFER_RETURN(output, 32);
274+
}
238275
DECLARE_FUNC(boolberry) {
239276
DECLARE_SCOPE;
240277

@@ -276,6 +313,7 @@ DECLARE_INIT(init) {
276313
NODE_SET_METHOD(exports, "boolberry", boolberry);
277314
NODE_SET_METHOD(exports, "c11", c11);
278315
NODE_SET_METHOD(exports, "cryptonight", cryptonight);
316+
NODE_SET_METHOD(exports, "cryptonightfast", cryptonightfast);
279317
NODE_SET_METHOD(exports, "fresh", fresh);
280318
NODE_SET_METHOD(exports, "fugue", fugue);
281319
NODE_SET_METHOD(exports, "groestl", groestl);
@@ -296,4 +334,4 @@ DECLARE_INIT(init) {
296334
NODE_SET_METHOD(exports, "x15", x15);
297335
}
298336

299-
NODE_MODULE(multihashing, init)
337+
NODE_MODULE(multihashing, init)

0 commit comments

Comments
 (0)