circomlib/src/pedersenHash.js
src/pedersenHash.jsRaw
1const babyJub = require("./babyjub");
2const createBlakeHash = require("blake-hash");
3const blake2b = require("blake2b");
4const Scalar = require("ffjavascript").Scalar;
5
6const GENPOINT_PREFIX = "PedersenGenerator";
7const windowSize = 4;
8const nWindowsPerSegment = 50;
9
10exports.hash = pedersenHash;
11exports.getBasePoint = getBasePoint;
12
13function baseHash(type, S) {
14 if (type == "blake") {
15 return createBlakeHash("blake256").update(S).digest();
16 } else if (type == "blake2b") {
17 return Buffer.from(blake2b(32).update(Buffer.from(S)).digest());
18 }
19}
20
21function pedersenHash(msg, options) {
22 options = options || {};
23 options.baseHash = options.baseHash || "blake";
24 const bitsPerSegment = windowSize*nWindowsPerSegment;
25 const bits = buffer2bits(msg);
26
27 const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1;
28
29 let accP = [babyJub.F.zero,babyJub.F.one];
30
31 for (let s=0; s<nSegments; s++) {
32 let nWindows;
33 if (s == nSegments-1) {
34 nWindows = Math.floor(((bits.length - (nSegments - 1)*bitsPerSegment) - 1) / windowSize) +1;
35 } else {
36 nWindows = nWindowsPerSegment;
37 }
38 let escalar = Scalar.e(0);
39 let exp = Scalar.e(1);
40 for (let w=0; w<nWindows; w++) {
41 let o = s*bitsPerSegment + w*windowSize;
42 let acc = Scalar.e(1);
43 for (let b=0; ((b<windowSize-1)&&(o<bits.length)) ; b++) {
44 if (bits[o]) {
45 acc = Scalar.add(acc, Scalar.shl(Scalar.e(1), b) );
46 }
47 o++;
48 }
49 if (o<bits.length) {
50 if (bits[o]) {
51 acc = Scalar.neg(acc);
52 }
53 o++;
54 }
55 escalar = Scalar.add(escalar, Scalar.mul(acc, exp));
56 exp = Scalar.shl(exp, windowSize+1);
57 }
58
59 if (Scalar.lt(escalar, 0)) {
60 escalar = Scalar.add( escalar, babyJub.subOrder);
61 }
62
63 accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(getBasePoint(options.baseHash, s), escalar));
64 }
65
66 return babyJub.packPoint(accP);
67}
68
69let bases = [];
70
71function getBasePoint(baseHashType, pointIdx) {
72 if (pointIdx<bases.length) return bases[pointIdx];
73 let p= null;
74 let tryIdx = 0;
75 while (p==null) {
76 const S = GENPOINT_PREFIX + "_" + padLeftZeros(pointIdx, 32) + "_" + padLeftZeros(tryIdx, 32);
77 const h = baseHash(baseHashType, S);
78 h[31] = h[31] & 0xBF; // Set 255th bit to 0 (256th is the signal and 254th is the last possible bit to 1)
79 p = babyJub.unpackPoint(h);
80 tryIdx++;
81 }
82
83 const p8 = babyJub.mulPointEscalar(p, 8);
84
85 if (!babyJub.inSubgroup(p8)) {
86 throw new Error("Point not in curve");
87 }
88
89 bases[pointIdx] = p8;
90 return p8;
91}
92
93function padLeftZeros(idx, n) {
94 let sidx = "" + idx;
95 while (sidx.length<n) sidx = "0"+sidx;
96 return sidx;
97}
98
99/*
100Input a buffer
101Returns an array of booleans. 0 is LSB of first byte and so on.
102 */
103function buffer2bits(buff) {
104 const res = new Array(buff.length*8);
105 for (let i=0; i<buff.length; i++) {
106 const b = buff[i];
107 res[i*8] = b & 0x01;
108 res[i*8+1] = b & 0x02;
109 res[i*8+2] = b & 0x04;
110 res[i*8+3] = b & 0x08;
111 res[i*8+4] = b & 0x10;
112 res[i*8+5] = b & 0x20;
113 res[i*8+6] = b & 0x40;
114 res[i*8+7] = b & 0x80;
115 }
116 return res;
117}
118
119
120
121