arnaucube
arnaucube/circomlibPublic Self-hosted repository
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