Pedersen2 and BitPoints MulFix and MulAny

This commit is contained in:
Jordi Baylina
2018-11-27 16:03:57 +01:00
parent fcc61f9237
commit 55e9a60c37
40 changed files with 2272 additions and 80 deletions

View File

@@ -4,6 +4,17 @@ const bigInt = require("snarkjs").bigInt;
exports.addPoint = addPoint;
exports.mulPointEscalar = mulPointEscalar;
exports.inCurve = inCurve;
exports.inSubgroup = inSubgroup;
exports.packPoint = packPoint;
exports.unpackPoint = unpackPoint;
exports.Base8 = [
bigInt("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
bigInt("2626589144620713026669568689430873010625803728049924121243784502389097019475")
];
exports.order = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328");
exports.subOrder = exports.order.shr(3);
exports.p = bn128.r;
function addPoint(a,b) {
const q = bn128.r;
@@ -32,26 +43,63 @@ function mulPointEscalar(base, e) {
return res;
}
function isLowGrade(p) {
const r = bigInt("21888242871839275222246405745257275088614511777268538073601725287587578984328").shr(3);
const res= mulPointEscalar(p, r);
function inSubgroup(P) {
if (!inCurve(P)) return false;
const res= mulPointEscalar(P, exports.subOrder);
return (res[0].equals(bigInt(0))) && (res[1].equals(bigInt(1)));
}
function inCurve(p) {
function inCurve(P) {
const F = bn128.Fr;
const a = bigInt("168700");
const d = bigInt("168696");
const x2 = F.square(p[0]);
const y2 = F.square(p[1]);
const x2 = F.square(P[0]);
const y2 = F.square(P[1]);
if (!F.equals(
F.add(F.mul(a, x2), y2),
F.add(F.one, F.mul(F.mul(x2, y2), d)))) return false;
if (!isLowGrade(p)) return false;
return true;
}
function packPoint(P) {
const buff = bigInt.leInt2Buff(P[1], 32);
if (P[0].greater(exports.p.shr(1))) {
buff[31] = buff[31] | 0x80;
}
return buff;
}
function unpackPoint(_buff) {
const F = bn128.Fr;
const buff = Buffer.from(_buff);
let sign = false;
const P = new Array(2);
if (buff[31] & 0x80) {
sign = true;
buff[31] = buff[31] & 0x7F;
}
P[1] = bigInt.leBuff2int(buff);
if (P[1].greaterOrEquals(exports.p)) return null;
const a = bigInt("168700");
const d = bigInt("168696");
const y2 = F.square(P[1]);
let x = F.sqrt(F.div(
F.sub(F.one, y2),
F.sub(a, F.mul(d, y2))));
if (x == null) return null;
if (sign) x = F.neg(x);
P[0] = F.affine(x);
return P;
}

92
src/eddsa.js Normal file
View File

@@ -0,0 +1,92 @@
const createBlakeHash = require("blake-hash");
const bigInt = require("snarkjs").bigInt;
const babyJub = require("./babyjub");
const pedersenHash = require("./pedersenHash").hash;
const crypto = require("crypto");
exports.cratePrvKey = cratePrvKey;
exports.prv2pub= prv2pub;
exports.sign = sign;
exports.verify = verify;
exports.packSignature = packSignature;
exports.unpackSignature = unpackSignature;
function cratePrvKey() {
return crypto.randomBytes(32);
}
function pruneBuffer(_buff) {
const buff = Buffer.from(_buff);
buff[0] = buff[0] & 0xF8;
buff[31] = buff[31] & 0x7F;
buff[31] = buff[31] | 0x40;
}
function prv2pub(prv) {
const sBuff = pruneBuffer(createBlakeHash("blake512").update(prv).digest().slice(0,32));
let s = bigInt.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3));
return A;
}
function sign(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = bigInt.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, s.shr(3));
const rBuff = createBlakeHash("blake512").update(Buffer.concat(h1.slice(32,64), msg)).digest();
let r = bigInt.leBuff2int(rBuff);
r = r.mod(babyJub.subOrder);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const R8p = babyJub.packPoint(R8);
const Ap = babyJub.packPoint(A);
const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg));
const hm = bigInt.leBuff2int(hmBuff);
const S = r.add(hm.mul(s)).mod(babyJub.subOrder);
return {
R8: R8,
S: S
};
}
function verify(msg, sig, A) {
// Check parameters
if (typeof sig != "object") return false;
if (!Array.isArray(sig.R8)) return false;
if (sig.R8.length!= 2) return false;
if (!babyJub.inCurve(sig.R8)) return false;
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!babyJub.inCurve(sig.A)) return false;
if (sig.S>= babyJub.subOrder) return false;
const R8p = babyJub.packPoint(sig.R8);
const Ap = babyJub.packPoint(A);
const hmBuff = pedersenHash(Buffer.concat(R8p, Ap, msg));
const hm = bigInt.leBuff2int(hmBuff);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S);
let Pright = babyJub.mulPointEscalar(A, hm.mul(8));
Pright = babyJub.addaddPoint(sig.R8, Pright);
if (!Pleft[0].equals(Pright[0])) return false;
if (!Pleft[1].equals(Pright[1])) return false;
return true;
}
function packSignature(sig) {
const R8p = babyJub.packPoint(sig.R8);
const Sp = bigInt.leInt2Buff(sig.S, 32);
return Buffer.concat(R8p, Sp);
}
function unpackSignature(sigBuff) {
return {
R8: babyJub.unpackPoint(sigBuff.slice(0,32)),
S: bigInt.leBuff2int(sigBuff.slice(32,64))
};
}

110
src/pedersenHash.js Normal file
View File

@@ -0,0 +1,110 @@
const bn128 = require("snarkjs").bn128;
const bigInt = require("snarkjs").bigInt;
const babyJub = require("./babyjub");
const assert = require("assert");
const createBlakeHash = require("blake-hash");
const GENPOINT_PREFIX = "Iden3_PedersenGenerator";
const windowSize = 4;
const nWindowsPerSegment = 50;
exports.hash = pedersenHash;
exports.getBasePoint = getBasePoint;
function pedersenHash(msg) {
const bitsPerSegment = windowSize*nWindowsPerSegment;
const bits = buffer2bits(msg);
const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1;
let accP = [bigInt.zero,bigInt.one];
for (let s=0; s<nSegments; s++) {
let nWindows;
if (s == nSegments-1) {
nWindows = Math.floor(((bits.length - (nSegments - 1)*bitsPerSegment) - 1) / windowSize) +1;
} else {
nWindows = nWindowsPerSegment;
}
let escalar = bigInt.zero;
let exp = bigInt.one;
for (let w=0; w<nWindows; w++) {
let o = s*bitsPerSegment + w*windowSize;
let acc = bigInt.one;
for (let b=0; ((b<windowSize-1)&&(o<bits.length)) ; b++) {
if (bits[o]) {
acc = acc.add( bigInt.one.shl(b) );
}
o++;
}
if (o<bits.length) {
if (bits[o]) {
acc = acc.neg();
}
o++;
}
escalar = escalar.add(acc.mul(exp));
exp = exp.shl(windowSize+1);
}
if (escalar.lesser(bigInt.zero)) {
escalar = babyJub.subOrder.add(escalar);
}
accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(getBasePoint(s), escalar));
}
return babyJub.packPoint(accP);
}
let bases = [];
function getBasePoint(pointIdx) {
if (pointIdx<bases.length) return bases[pointIdx];
let p= null;
let tryIdx = 0;
while (p==null) {
const S = GENPOINT_PREFIX + "_" + padLeftZeros(pointIdx, 32) + "_" + padLeftZeros(tryIdx, 32);
const h = createBlakeHash("blake256").update(S).digest();
h[31] = h[31] & 0xBF; // Set 255th bit to 0 (256th is the signal and 254th is the last possible bit to 1)
p = babyJub.unpackPoint(h);
tryIdx++;
}
const p8 = babyJub.mulPointEscalar(p, 8);
assert(babyJub.inSubgroup(p8), "Point not in curve");
bases[pointIdx] = p8;
return p8;
}
function padLeftZeros(idx, n) {
let sidx = "" + idx;
while (sidx.length<n) sidx = "0"+sidx;
return sidx;
}
/*
Input a buffer
Returns an array of booleans. 0 is LSB of first byte and so on.
*/
function buffer2bits(buff) {
const res = new Array(buff.length*8);
for (let i=0; i<buff.length; i++) {
const b = buff[i];
res[i*8] = b & 0x01;
res[i*8+1] = b & 0x02;
res[i*8+2] = b & 0x04;
res[i*8+3] = b & 0x08;
res[i*8+4] = b & 0x10;
res[i*8+5] = b & 0x20;
res[i*8+6] = b & 0x40;
res[i*8+7] = b & 0x80;
}
return res;
}

13
src/printBases.js Normal file
View File

@@ -0,0 +1,13 @@
const pedersenHash = require("./pedersenHash.js");
let nBases;
if (typeof process.argv[2] != "undefined") {
nBases = parseInt(process.argv[2]);
} else {
nBases = 5;
}
for (let i=0; i < nBases; i++) {
const p = pedersenHash.getBasePoint(i);
console.log(`[${p[0]},${p[1]}]`);
}