Skip to content

Commit 8dd8668

Browse files
authored
Merge pull request #31 from HenryNguyen5/test/add-tests
[Test/add tests][RE-MERGE->DEVELOP] Add tests for genBorromean and MLSAG_gen
2 parents 7d3b742 + ad4fe49 commit 8dd8668

11 files changed

+612
-4
lines changed

cryptonote_utils/cryptonote_utils.js

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,13 @@ var cnUtil = function(currencyConfig) {
8787
var l = JSBigInt(
8888
"7237005577332262213973186563042994240857116359379907606001950938285454250989",
8989
); //curve order (not RCT specific)
90+
9091
var I = "0100000000000000000000000000000000000000000000000000000000000000"; //identity element
92+
this.I = I;
93+
this.identity = function() {
94+
return I;
95+
};
96+
9197
var Z = "0000000000000000000000000000000000000000000000000000000000000000"; //zero scalar
9298
//H2 object to speed up some operations
9399
var H2 = [
@@ -157,6 +163,8 @@ var cnUtil = function(currencyConfig) {
157163
"f8fef05a3fa5c9f3eba41638b247b711a99f960fe73aa2f90136aeb20329b888",
158164
];
159165

166+
this.H2 = H2;
167+
160168
//begin rct new functions
161169
//creates a Pedersen commitment from an amount (in scalar form) and a mask
162170
//C = bG + aH where b = mask, a = amount
@@ -222,6 +230,7 @@ var cnUtil = function(currencyConfig) {
222230

223231
//for most uses you'll also want to swapEndian after conversion
224232
//mainly to convert integer "scalars" to usable hexadecimal strings
233+
//uint long long to 32 byte key
225234
function d2h(integer) {
226235
if (typeof integer !== "string" && integer.toString().length > 15) {
227236
throw "integer should be entered as a string for precision";
@@ -237,12 +246,14 @@ var cnUtil = function(currencyConfig) {
237246
.toLowerCase()
238247
).slice(-64);
239248
}
249+
this.d2h = d2h;
240250

241251
//integer (string) to scalar
242252
function d2s(integer) {
243253
return swapEndian(d2h(integer));
244254
}
245255

256+
this.d2s = d2s;
246257
//scalar to integer (string)
247258
function s2d(scalar) {
248259
return JSBigInt.parse(swapEndian(scalar), 16).toString();
@@ -314,6 +325,7 @@ var cnUtil = function(currencyConfig) {
314325
}
315326
return res;
316327
}
328+
this.hextobin = hextobin;
317329

318330
function bintohex(bin) {
319331
var out = [];
@@ -514,6 +526,8 @@ var cnUtil = function(currencyConfig) {
514526
return this.sc_reduce32(this.rand_32());
515527
};
516528

529+
// alias
530+
this.skGen = random_scalar;
517531
/* no longer used
518532
this.keccak = function(hex, inlen, outlen) {
519533
var input = hextobin(hex);
@@ -869,6 +883,7 @@ var cnUtil = function(currencyConfig) {
869883
CNCrypto._free(res2_m);
870884
return bintohex(res);
871885
};
886+
this.hashToPoint = hash_to_ec_2;
872887

873888
this.generate_key_image_2 = function(pub, sec) {
874889
if (!pub || !sec || pub.length !== 64 || sec.length !== 64) {
@@ -1248,8 +1263,8 @@ var cnUtil = function(currencyConfig) {
12481263
//xv: vector of secret keys, 1 per ring (nrings)
12491264
//pm: matrix of pubkeys, indexed by size first
12501265
//iv: vector of indexes, 1 per ring (nrings), can be a string
1251-
//size: ring size
1252-
//nrings: number of rings
1266+
//size: ring size, default 2
1267+
//nrings: number of rings, default 64
12531268
//extensible borromean signatures
12541269
this.genBorromean = function(xv, pm, iv, size, nrings) {
12551270
if (xv.length !== nrings) {
@@ -1272,6 +1287,8 @@ var cnUtil = function(currencyConfig) {
12721287
}
12731288
}
12741289
//signature struct
1290+
// in the case of size 2 and nrings 64
1291+
// bb.s = [[64], [64]]
12751292
var bb = {
12761293
s: [],
12771294
ee: "",
@@ -1323,6 +1340,37 @@ var cnUtil = function(currencyConfig) {
13231340
return bb;
13241341
};
13251342

1343+
this.verifyBorromean = function(bb, P1, P2) {
1344+
let Lv1 = [];
1345+
let chash;
1346+
let LL;
1347+
1348+
let p2 = "";
1349+
for (let ii = 0; ii < 64; ii++) {
1350+
p2 = this.ge_double_scalarmult_base_vartime(
1351+
bb.ee,
1352+
P1[ii],
1353+
bb.s[0][ii],
1354+
);
1355+
LL = p2;
1356+
chash = this.hash_to_scalar(LL);
1357+
1358+
p2 = this.ge_double_scalarmult_base_vartime(
1359+
chash,
1360+
P2[ii],
1361+
bb.s[1][ii],
1362+
);
1363+
Lv1[ii] = p2;
1364+
}
1365+
const eeComputed = this.array_hash_to_scalar(Lv1);
1366+
const equalKeys = eeComputed === bb.ee;
1367+
console.log(`Keys equal? ${equalKeys}
1368+
${eeComputed}
1369+
${bb.ee}`);
1370+
1371+
return equalKeys;
1372+
};
1373+
13261374
//proveRange
13271375
//proveRange gives C, and mask such that \sumCi = C
13281376
// c.f. http://eprint.iacr.org/2015/1098 section 5.1
@@ -1406,6 +1454,7 @@ var cnUtil = function(currencyConfig) {
14061454
}
14071455
return hash_to_scalar(buf);
14081456
}
1457+
this.array_hash_to_scalar = array_hash_to_scalar;
14091458

14101459
// Gen creates a signature which proves that for some column in the keymatrix "pk"
14111460
// the signer knows a secret key for each row in that column
@@ -1414,13 +1463,16 @@ var cnUtil = function(currencyConfig) {
14141463
// because we don't want to force same secret column for all inputs
14151464
this.MLSAG_Gen = function(message, pk, xx, kimg, index) {
14161465
var cols = pk.length; //ring size
1466+
// secret index
14171467
if (index >= cols) {
14181468
throw "index out of range";
14191469
}
14201470
var rows = pk[0].length; //number of signature rows (always 2)
1471+
// [pub, com] = 2
14211472
if (rows !== 2) {
14221473
throw "wrong row count";
14231474
}
1475+
// check all are len 2
14241476
for (var i = 0; i < cols; i++) {
14251477
if (pk[i].length !== rows) {
14261478
throw "pk is not rectangular";
@@ -1444,9 +1496,14 @@ var cnUtil = function(currencyConfig) {
14441496
toHash[0] = message;
14451497

14461498
//secret index (pubkey section)
1499+
14471500
alpha[0] = random_scalar(); //need to save alphas for later
14481501
toHash[1] = pk[index][0]; //secret index pubkey
1449-
toHash[2] = ge_scalarmult_base(alpha[0]); //dsRow L
1502+
1503+
// this is the keyimg anyway const H1 = this.hashToPoint(pk[index][0]) // Hp(K_in)
1504+
// rv.II[0] = this.ge_scalarmult(H1, xx[0]) // k_in.Hp(K_in)
1505+
1506+
toHash[2] = ge_scalarmult_base(alpha[0]); //dsRow L, a.G
14501507
toHash[3] = generate_key_image_2(pk[index][0], alpha[0]); //dsRow R (key image check)
14511508
//secret index (commitment section)
14521509
alpha[1] = random_scalar();
@@ -1495,6 +1552,51 @@ var cnUtil = function(currencyConfig) {
14951552
return rv;
14961553
};
14971554

1555+
this.MLSAG_ver = function(message, pk, rv, kimg) {
1556+
// we assume that col, row, rectangular checks are already done correctly
1557+
// in MLSAG_gen
1558+
const cols = pk.length;
1559+
let c_old = rv.cc;
1560+
console.log(`cols ${cols}`);
1561+
let i = 0;
1562+
let toHash = [];
1563+
toHash[0] = message;
1564+
while (i < cols) {
1565+
//!secret index (pubkey section)
1566+
toHash[1] = pk[i][0];
1567+
toHash[2] = ge_double_scalarmult_base_vartime(
1568+
c_old,
1569+
pk[i][0],
1570+
rv.ss[i][0],
1571+
);
1572+
toHash[3] = ge_double_scalarmult_postcomp_vartime(
1573+
rv.ss[i][0],
1574+
pk[i][0],
1575+
c_old,
1576+
kimg,
1577+
);
1578+
1579+
//!secret index (commitment section)
1580+
toHash[4] = pk[i][1];
1581+
toHash[5] = ge_double_scalarmult_base_vartime(
1582+
c_old,
1583+
pk[i][1],
1584+
rv.ss[i][1],
1585+
);
1586+
1587+
c_old = array_hash_to_scalar(toHash);
1588+
1589+
i = i + 1;
1590+
}
1591+
1592+
const c = this.sc_sub(c_old, rv.cc);
1593+
console.log(`
1594+
c_old: ${c_old}
1595+
rc.cc: ${rv.cc}
1596+
c: ${c}`);
1597+
return c;
1598+
};
1599+
14981600
//prepares for MLSAG_Gen
14991601
this.proveRctMG = function(message, pubs, inSk, kimg, mask, Cout, index) {
15001602
var cols = pubs.length;
@@ -2483,6 +2585,8 @@ var cnUtil = function(currencyConfig) {
24832585
return str;
24842586
}
24852587

2588+
this.padLeft = padLeft;
2589+
24862590
this.printDsts = function(dsts) {
24872591
for (var i = 0; i < dsts.length; i++) {
24882592
console.log(

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
"jest": "^23.1.0"
3939
},
4040
"jest" : {
41+
"testEnvironment":"node",
4142
"coveragePathIgnorePatterns": [
4243
"node_modules",
4344
"cryptonote_utils/biginteger.js",
45+
"cryptonote_utils/nacl-fast-cn.js",
4446
"cryptonote_utils/sha3.js",
4547
"cryptonote_utils/cryptonote_crypto_EMSCRIPTEN.js"]
4648
}

tests/MG_sigs.spec.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2014-2018, MyMonero.com
2+
//
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without modification, are
6+
// permitted provided that the following conditions are met:
7+
//
8+
// 1. Redistributions of source code must retain the above copyright notice, this list of
9+
// conditions and the following disclaimer.
10+
//
11+
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12+
// of conditions and the following disclaimer in the documentation and/or other
13+
// materials provided with the distribution.
14+
//
15+
// 3. Neither the name of the copyright holder nor the names of its contributors may be
16+
// used to endorse or promote products derived from this software without specific
17+
// prior written permission.
18+
//
19+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20+
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21+
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22+
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24+
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26+
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27+
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
const monero_utils = require("../").monero_utils;
30+
31+
it("MG_sigs", () => {
32+
function skvGen(len) {
33+
let skVec = [];
34+
for (let i = 0; i < len; i++) {
35+
skVec.push(monero_utils.skGen());
36+
}
37+
return skVec;
38+
}
39+
//initializes a key matrix;
40+
//first parameter is rows,
41+
//second is columns
42+
function keyMInit(rows, cols) {
43+
let rv = [];
44+
for (let i = 0; i < cols; i++) {
45+
rv.push([]);
46+
}
47+
return rv;
48+
}
49+
let j = 0;
50+
51+
//Tests for MG Sigs
52+
//#MG sig: true one
53+
let N = 3; // cols
54+
let R = 2; // rows
55+
56+
let xtmp = skvGen(R);
57+
let xm = keyMInit(R, N); // = [[None]*N] #just used to generate test public keys
58+
let sk = skvGen(R);
59+
60+
// [
61+
// [pubkey1, commitment1],
62+
// [pubkey2, commitment2],
63+
// ...
64+
// [pubkeyn, commitmentn]]
65+
// // Gen creates a signature which proves that for some column in the keymatrix "pk"
66+
// the signer knows a secret key for each row in that column
67+
let P = keyMInit(R, N); // = keyM[[None]*N] #stores the public keys;
68+
69+
let ind = 2;
70+
let i = 0;
71+
72+
for (j = 0; j < R; j++) {
73+
for (i = 0; i < N; i++) {
74+
xm[i][j] = monero_utils.skGen();
75+
P[i][j] = monero_utils.ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
76+
}
77+
}
78+
79+
for (j = 0; j < R; j++) {
80+
// our secret vector of [onetimesec, z]
81+
sk[j] = xm[ind][j];
82+
}
83+
84+
let message = monero_utils.identity();
85+
let kimg = monero_utils.ge_scalarmult(
86+
monero_utils.hashToPoint(P[ind][0]),
87+
sk[0],
88+
);
89+
let rv = monero_utils.MLSAG_Gen(message, P, sk, kimg, ind);
90+
let c = monero_utils.MLSAG_ver(message, P, rv, kimg);
91+
92+
expect(Number(c)).toEqual(0);
93+
94+
xtmp = skvGen(R);
95+
xm = keyMInit(R, N); // = [[None]*N] #just used to generate test public keys
96+
sk = skvGen(R);
97+
98+
for (j = 0; j < R; j++) {
99+
for (i = 0; i < N; i++) {
100+
xm[i][j] = monero_utils.skGen();
101+
P[i][j] = monero_utils.ge_scalarmult_base(xm[i][j]); // generate fake [pubkey, commit]
102+
}
103+
}
104+
105+
sk[1] = skGen(); //assume we don't know one of the private keys..
106+
kimg = monero_utils.ge_scalarmult(
107+
monero_utils.hashToPoint(P[ind][0]),
108+
sk[0],
109+
);
110+
rv = monero_utils.MLSAG_Gen(message, P, sk, kimg, ind);
111+
c = monero_utils.MLSAG_ver(message, P, rv, kimg);
112+
113+
expect(Number(c)).toBeFalsy();
114+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2014-2018, MyMonero.com
2+
//
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without modification, are
6+
// permitted provided that the following conditions are met:
7+
//
8+
// 1. Redistributions of source code must retain the above copyright notice, this list of
9+
// conditions and the following disclaimer.
10+
//
11+
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12+
// of conditions and the following disclaimer in the documentation and/or other
13+
// materials provided with the distribution.
14+
//
15+
// 3. Neither the name of the copyright holder nor the names of its contributors may be
16+
// used to endorse or promote products derived from this software without specific
17+
// prior written permission.
18+
//
19+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20+
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21+
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22+
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24+
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26+
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27+
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
const monero_utils = require("../../").monero_utils;
30+
const { generate_parameters } = require("./test_parameters");
31+
const { indi, P1v, P2v, xv, N } = generate_parameters();
32+
33+
it("borromean_3", () => {
34+
// #true one
35+
const bb = monero_utils.genBorromean(xv, [P1v, P2v], indi, 2, N); /*?.*/
36+
const valid = monero_utils.verifyBorromean(bb, P1v, P2v); /*?.*/
37+
expect(valid).toBe(true);
38+
});

0 commit comments

Comments
 (0)