Skip to content

Commit a4e2394

Browse files
authored
Merge pull request fancoder#1 from sumlnoether/cryptonight-variants
Cryptonight variants
2 parents bc68ab9 + fe46cc7 commit a4e2394

File tree

6 files changed

+169
-32
lines changed

6 files changed

+169
-32
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ Algorithms
3232
Usage
3333
-----
3434

35-
Install
35+
Install on Ubuntu 16.04
3636

3737
```bash
38-
npm install multi-hashing
38+
sudo apt-get nodejs nodejs-dev node-gyp npm
39+
sudo ln -s /usr/bin/nodejs /usr/bin/node
40+
npm install git+https://github.com/sumlnoether/node-multi-hashing-node8.git
3941
```
4042

4143
So far this native Node.js addon can do the following hashing algos
@@ -79,3 +81,5 @@ Credits
7981
* [bcrypt](http://en.wikipedia.org/wiki/Bcrypt) - Niels Provos and David Mazières
8082
* [X11](http://www.darkcoin.io/), [Hefty1](http://heavycoin.github.io/about.html), [Quark](http://www.qrk.cc/) creators (they just mixed together a bunch of the above algos)
8183
* [PhearZero](https://github.com/PhearZero) Michael J Feher
84+
* [codebling](https://github.com/codebling) CodeBling
85+
* [Monero](https://github.com/monero-project/monero) The Monero Project

cryptonight.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// Copyright (c) 2012-2013 The Cryptonote developers
22
// Distributed under the MIT/X11 software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
// Portions Copyright (c) 2018 The Monero developers
45

6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <unistd.h>
59
#include "crypto/oaes_lib.h"
610
#include "crypto/c_keccak.h"
711
#include "crypto/c_groestl.h"
@@ -17,11 +21,40 @@
1721

1822
#define MEMORY (1 << 21) /* 2 MiB */
1923
#define ITER (1 << 20)
24+
#define MASK 0x1FFFF
25+
26+
#define LITE_MEMORY (1 << 20) /* 2 MiB */
27+
#define LITE_ITER (1 << 19)
28+
#define LITE_MASK 0xFFFF
29+
2030
#define AES_BLOCK_SIZE 16
2131
#define AES_KEY_SIZE 32 /*16*/
2232
#define INIT_SIZE_BLK 8
2333
#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE)
2434

35+
#define VARIANT1_1(p) \
36+
do if (variant > 0) \
37+
{ \
38+
const uint8_t tmp = ((const uint8_t*)(p))[11]; \
39+
static const uint32_t table = 0x75310; \
40+
const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \
41+
((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \
42+
} while(0)
43+
44+
#define VARIANT1_2(p) \
45+
do if (variant > 0) \
46+
{ \
47+
((uint64_t*)p)[1] ^= tweak1_2; \
48+
} while(0)
49+
50+
#define VARIANT1_INIT() \
51+
if (variant > 0 && len < 43) \
52+
{ \
53+
fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \
54+
_exit(1); \
55+
} \
56+
const uint64_t tweak1_2 = variant > 0 ? *(const uint64_t*)(((const uint8_t*)input)+35) ^ ctx->state.hs.w[24] : 0
57+
2558
#pragma pack(push, 1)
2659
union cn_slow_hash_state {
2760
union hash_state hs;
@@ -57,8 +90,8 @@ static void (* const extra_hashes[4])(const void *, size_t, char *) = {
5790
extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey);
5891
extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
5992

60-
static inline size_t e2i(const uint8_t* a) {
61-
return (*((uint64_t*) a) / AES_BLOCK_SIZE) & (MEMORY / AES_BLOCK_SIZE - 1);
93+
static inline size_t e2i(const uint8_t* a, size_t mask) {
94+
return (*((uint64_t*) a) / AES_BLOCK_SIZE) & mask;
6295
}
6396

6497
static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) {
@@ -124,16 +157,21 @@ struct cryptonight_ctx {
124157
oaes_ctx* aes_ctx;
125158
};
126159

127-
void cryptonight_hash(const char* input, char* output, uint32_t len) {
160+
void cryptonight_hash(const char* input, char* output, uint32_t len, int variant, int lite) {
128161
struct cryptonight_ctx *ctx = alloca(sizeof(struct cryptonight_ctx));
129162
hash_process(&ctx->state.hs, (const uint8_t*) input, len);
130163
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
131164
memcpy(ctx->aes_key, ctx->state.hs.b, AES_KEY_SIZE);
132165
ctx->aes_ctx = (oaes_ctx*) oaes_alloc();
133166
size_t i, j;
167+
size_t memory = lite ? LITE_MEMORY : MEMORY;
168+
size_t iterations = lite ? LITE_ITER : ITER;
169+
size_t mask = lite ? LITE_MASK : MASK;
170+
171+
VARIANT1_INIT();
134172

135173
oaes_key_import_data(ctx->aes_ctx, ctx->aes_key, AES_KEY_SIZE);
136-
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
174+
for (i = 0; i < memory / INIT_SIZE_BYTE; i++) {
137175
for (j = 0; j < INIT_SIZE_BLK; j++) {
138176
aesb_pseudo_round(&ctx->text[AES_BLOCK_SIZE * j],
139177
&ctx->text[AES_BLOCK_SIZE * j],
@@ -147,24 +185,27 @@ void cryptonight_hash(const char* input, char* output, uint32_t len) {
147185
ctx->b[i] = ctx->state.k[16 + i] ^ ctx->state.k[48 + i];
148186
}
149187

150-
for (i = 0; i < ITER / 2; i++) {
188+
for (i = 0; i < iterations / 2; i++) {
151189
/* Dependency chain: address -> read value ------+
152190
* written value <-+ hard function (AES or MUL) <+
153191
* next address <-+
154192
*/
155193
/* Iteration 1 */
156-
j = e2i(ctx->a);
194+
j = e2i(ctx->a, mask);
157195
aesb_single_round(&ctx->long_state[j * AES_BLOCK_SIZE], ctx->c, ctx->a);
158196
xor_blocks_dst(ctx->c, ctx->b, &ctx->long_state[j * AES_BLOCK_SIZE]);
197+
VARIANT1_1((uint8_t*)&ctx->long_state[j * AES_BLOCK_SIZE]);
159198
/* Iteration 2 */
160199
mul_sum_xor_dst(ctx->c, ctx->a,
161-
&ctx->long_state[e2i(ctx->c) * AES_BLOCK_SIZE]);
200+
&ctx->long_state[e2i(ctx->c, mask) * AES_BLOCK_SIZE]);
162201
copy_block(ctx->b, ctx->c);
202+
VARIANT1_2((uint8_t*)
203+
&ctx->long_state[e2i(ctx->c, mask) * AES_BLOCK_SIZE]);
163204
}
164205

165206
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
166207
oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE);
167-
for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) {
208+
for (i = 0; i < memory / INIT_SIZE_BYTE; i++) {
168209
for (j = 0; j < INIT_SIZE_BLK; j++) {
169210
xor_blocks(&ctx->text[j * AES_BLOCK_SIZE],
170211
&ctx->long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]);

cryptonight.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extern "C" {
77

88
#include <stdint.h>
99

10-
void cryptonight_hash(const char* input, char* output, uint32_t len);
10+
void cryptonight_hash(const char* input, char* output, uint32_t len, int variant, int lite);
1111
void cryptonight_fast_hash(const char* input, char* output, uint32_t len);
1212

1313
#ifdef __cplusplus

multihashing.cpp

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extern "C" {
2121
#include "cryptonight.h"
2222
#include "x13.h"
2323
#include "nist5.h"
24-
#include "sha1.h",
24+
#include "sha1.h"
2525
#include "x15.h"
2626
#include "fresh.h"
2727
}
@@ -49,7 +49,7 @@ NAN_METHOD(quark) {
4949
char * input = node::Buffer::Data(target);
5050
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
5151
char* output = node::Buffer::Data(dest.ToLocalChecked());
52-
52+
5353
uint32_t input_len = node::Buffer::Length(target);
5454

5555
quark_hash(input, output, input_len);
@@ -86,18 +86,18 @@ NAN_METHOD(scrypt) {
8686

8787
if(!node::Buffer::HasInstance(target))
8888
return except("Argument should be a buffer object.");
89-
89+
9090
Local<Number> numn = info[1]->ToNumber();
9191
unsigned int nValue = numn->Value();
9292
Local<Number> numr = info[2]->ToNumber();
9393
unsigned int rValue = numr->Value();
94-
94+
9595
char * input = node::Buffer::Data(target);
9696
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
9797
char* output = node::Buffer::Data(dest.ToLocalChecked());
9898

9999
uint32_t input_len = node::Buffer::Length(target);
100-
100+
101101
scrypt_N_R_1_256(input, output, nValue, rValue, input_len);
102102

103103
info.GetReturnValue().Set(dest.ToLocalChecked());
@@ -216,7 +216,7 @@ NAN_METHOD(skein) {
216216
char* output = node::Buffer::Data(dest.ToLocalChecked());
217217

218218
uint32_t input_len = node::Buffer::Length(target);
219-
219+
220220
skein_hash(input, output, input_len);
221221

222222
info.GetReturnValue().Set(dest.ToLocalChecked());
@@ -235,7 +235,7 @@ NAN_METHOD(groestl) {
235235
char * input = node::Buffer::Data(target);
236236
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
237237
char* output = node::Buffer::Data(dest.ToLocalChecked());
238-
238+
239239
uint32_t input_len = node::Buffer::Length(target);
240240

241241
groestl_hash(input, output, input_len);
@@ -256,7 +256,7 @@ NAN_METHOD(groestlmyriad) {
256256
char * input = node::Buffer::Data(target);
257257
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
258258
char* output = node::Buffer::Data(dest.ToLocalChecked());
259-
259+
260260
uint32_t input_len = node::Buffer::Length(target);
261261

262262
groestlmyriad_hash(input, output, input_len);
@@ -277,7 +277,7 @@ NAN_METHOD(blake) {
277277
char * input = node::Buffer::Data(target);
278278
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
279279
char* output = node::Buffer::Data(dest.ToLocalChecked());
280-
280+
281281
uint32_t input_len = node::Buffer::Length(target);
282282

283283
blake_hash(input, output, input_len);
@@ -298,7 +298,7 @@ NAN_METHOD(fugue) {
298298
char * input = node::Buffer::Data(target);
299299
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
300300
char* output = node::Buffer::Data(dest.ToLocalChecked());
301-
301+
302302
uint32_t input_len = node::Buffer::Length(target);
303303

304304
fugue_hash(input, output, input_len);
@@ -319,7 +319,7 @@ NAN_METHOD(qubit) {
319319
char * input = node::Buffer::Data(target);
320320
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
321321
char* output = node::Buffer::Data(dest.ToLocalChecked());
322-
322+
323323
uint32_t input_len = node::Buffer::Length(target);
324324

325325
qubit_hash(input, output, input_len);
@@ -340,7 +340,7 @@ NAN_METHOD(hefty1) {
340340
char * input = node::Buffer::Data(target);
341341
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
342342
char* output = node::Buffer::Data(dest.ToLocalChecked());
343-
343+
344344
uint32_t input_len = node::Buffer::Length(target);
345345

346346
hefty1_hash(input, output, input_len);
@@ -361,7 +361,7 @@ NAN_METHOD(shavite3) {
361361
char * input = node::Buffer::Data(target);
362362
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
363363
char* output = node::Buffer::Data(dest.ToLocalChecked());
364-
364+
365365
uint32_t input_len = node::Buffer::Length(target);
366366

367367
shavite3_hash(input, output, input_len);
@@ -371,14 +371,60 @@ NAN_METHOD(shavite3) {
371371

372372
NAN_METHOD(cryptonight) {
373373
bool fast = false;
374+
uint32_t cn_variant = 0;
375+
376+
if (info.Length() < 1)
377+
return except("You must provide one argument.");
378+
379+
if (info.Length() >= 2) {
380+
if (info.Length() >= 2) {
381+
if(info[1]->IsBoolean())
382+
fast = info[1]->ToBoolean()->BooleanValue();
383+
else if(info[1]->IsUint32())
384+
cn_variant = info[1]->ToUint32()->Uint32Value();
385+
else
386+
return except("Argument 2 should be a boolean or uint32_t");
387+
}
388+
}
389+
390+
Local<Object> target = info[0]->ToObject();
391+
392+
if(!node::Buffer::HasInstance(target))
393+
return except("Argument should be a buffer object.");
394+
395+
char * input = node::Buffer::Data(target);
396+
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
397+
char* output = node::Buffer::Data(dest.ToLocalChecked());
398+
399+
uint32_t input_len = node::Buffer::Length(target);
400+
401+
if(fast)
402+
cryptonight_fast_hash(input, output, input_len);
403+
else {
404+
if (cn_variant > 0 && input_len < 43)
405+
return except("Argument must be 43 bytes for monero variant 1+");
406+
cryptonight_hash(input, output, input_len, cn_variant, 0);
407+
}
408+
409+
info.GetReturnValue().Set(dest.ToLocalChecked());
410+
}
411+
412+
NAN_METHOD(cryptonightlite) {
413+
bool fast = false;
414+
uint32_t cn_variant = 0;
374415

375416
if (info.Length() < 1)
376417
return except("You must provide one argument.");
377-
418+
378419
if (info.Length() >= 2) {
379-
if(!info[1]->IsBoolean())
380-
return except("Argument 2 should be a boolean");
381-
fast = info[1]->ToBoolean()->BooleanValue();
420+
if (info.Length() >= 2) {
421+
if(info[1]->IsBoolean())
422+
fast = info[1]->ToBoolean()->BooleanValue();
423+
else if(info[1]->IsUint32())
424+
cn_variant = info[1]->ToUint32()->Uint32Value();
425+
else
426+
return except("Argument 2 should be a boolean or uint32_t");
427+
}
382428
}
383429

384430
Local<Object> target = info[0]->ToObject();
@@ -389,13 +435,16 @@ NAN_METHOD(cryptonight) {
389435
char * input = node::Buffer::Data(target);
390436
Nan::MaybeLocal<v8::Object> dest = Nan::NewBuffer(32);
391437
char* output = node::Buffer::Data(dest.ToLocalChecked());
392-
438+
393439
uint32_t input_len = node::Buffer::Length(target);
394440

395441
if(fast)
396442
cryptonight_fast_hash(input, output, input_len);
397-
else
398-
cryptonight_hash(input, output, input_len);
443+
else {
444+
if (cn_variant > 0 && input_len < 43)
445+
return except("Argument must be 43 bytes for aeon variant 1+");
446+
cryptonight_hash(input, output, input_len, cn_variant, 1);
447+
}
399448

400449
info.GetReturnValue().Set(dest.ToLocalChecked());
401450
}
@@ -566,6 +615,8 @@ NAN_MODULE_INIT(Init) {
566615
GetFunction(New<FunctionTemplate>(shavite3)).ToLocalChecked());
567616
Nan::Set(target, New<String>("cryptonight").ToLocalChecked(),
568617
GetFunction(New<FunctionTemplate>(cryptonight)).ToLocalChecked());
618+
Nan::Set(target, New<String>("cryptonight-lite").ToLocalChecked(),
619+
GetFunction(New<FunctionTemplate>(cryptonightlite)).ToLocalChecked());
569620
Nan::Set(target, New<String>("x13").ToLocalChecked(),
570621
GetFunction(New<FunctionTemplate>(x13)).ToLocalChecked());
571622
Nan::Set(target, New<String>("boolberry").ToLocalChecked(),
@@ -581,4 +632,4 @@ NAN_MODULE_INIT(Init) {
581632
}
582633

583634
NODE_MODULE(multihashing, Init)
584-
}
635+
}

tests/cryptonight-tests.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const multiHashing = require('../build/Release/multihashing');
2+
const assert = require('assert');
3+
4+
var cn_data = new Buffer("6465206f6d6e69627573206475626974616e64756d", "hex");
5+
var xmrig_data = new Buffer("0100fb8e8ac805899323371bb790db19218afd8db8e3755d8b90f39b3d5506a9abce4fa912244500000000ee8146d49fa93ee724deb57d12cbc6c6f3b924d946127c7a97418f9348828f0f02", "hex");
6+
var cn_hash = new Buffer("2f8e3df40bd11f9ac90c743ca8e32bb391da4fb98612aa3b6cdc639ee00b31f5", "hex");
7+
var xmrig_cnvariant1_hash = new Buffer("c9fae8425d8688dc236bcdbc42fdb42d376c6ec190501aa84b04a4b4cf1ee122", "hex");
8+
var xmrig_cnlite_hash = new Buffer("28a22bad3f93d1408fca472eb5ad1cbe75f21d053c8ce5b3af105a57713e21dd", "hex");
9+
var xmrig_cnlitevariant1_hash = new Buffer("87c4e570653eb4c2b42b7a0d546559452dfab573b82ec52f152b7ff98e79446f", "hex");
10+
11+
hashedData = multiHashing['cryptonight'](cn_data);
12+
cn_variant1Data = multiHashing['cryptonight'](xmrig_data, 1);
13+
cnlite_data = multiHashing['cryptonight-lite'](xmrig_data, 0);
14+
cnlite_variant1Data = multiHashing['cryptonight-lite'](xmrig_data, 1);
15+
16+
console.log(hashedData);
17+
console.log(cn_variant1Data);
18+
console.log(cnlite_data);
19+
console.log(cnlite_variant1Data);
20+
21+
assert.deepEqual(hashedData, cn_hash);
22+
assert.deepEqual(cn_variant1Data, xmrig_cnvariant1_hash);
23+
assert.deepEqual(cnlite_data, xmrig_cnlite_hash);
24+
assert.deepEqual(cnlite_variant1Data, xmrig_cnlitevariant1_hash);

0 commit comments

Comments
 (0)