@sismo-core/crypto
Version:
Snark friendly crypto functions
356 lines (340 loc) • 15.6 kB
JavaScript
import { BigNumber, ethers } from 'ethers';
import { getCurveFromName, Scalar } from 'ffjavascript';
import createBlakeHash from 'blake-hash';
import { buildPoseidon as buildPoseidon$1 } from 'circomlibjs';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.fromArray = function (arr) {
return new Point(arr[0], arr[1]);
};
return Point;
}());
var babyJub;
function buildBabyjub() {
return __awaiter(this, void 0, void 0, function () {
var bn128;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!babyJub) return [3 /*break*/, 2];
return [4 /*yield*/, getCurveFromName("bn128", true)];
case 1:
bn128 = _a.sent();
babyJub = new BabyJub(bn128.Fr);
_a.label = 2;
case 2: return [2 /*return*/, babyJub];
}
});
});
}
// Highly inspired from https://github.com/iden3/circomlibjs/blob/main/src/babyjub.js
var BabyJub = /** @class */ (function () {
function BabyJub(F) {
this.F = F;
this.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
this.pm1d2 = Scalar.div(Scalar.sub(this.p, Scalar.e(1)), Scalar.e(2));
this.Generator = new Point(F.e("995203441582195749578291179787384436505546430278305826713579947235728471134"), F.e("5472060717959818805561601436314318772137091100104008585924551046643952123905"));
this.Base8 = new Point(F.e("5299619240641551281634865583518297030282874472190772894086521144482721001553"), F.e("16950150798460657717958625567821834550301663161624707787222815936182638968203"));
this.order = Scalar.fromString("21888242871839275222246405745257275088614511777268538073601725287587578984328");
this.subOrder = Scalar.shiftRight(this.order, 3);
this.A = F.e("168700");
this.D = F.e("168696");
}
BabyJub.prototype.addPoint = function (a, b) {
var F = this.F;
/* does the equivalent of:
res[0] = bigInt((a[0]*b[1] + b[0]*a[1]) * bigInt(bigInt("1") + d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
res[1] = bigInt((a[1]*b[1] - cta*a[0]*b[0]) * bigInt(bigInt("1") - d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
*/
var beta = F.mul(a.x, b.y);
var gamma = F.mul(a.y, b.x);
var delta = F.mul(F.sub(a.y, F.mul(this.A, a.x)), F.add(b.x, b.y));
var tau = F.mul(beta, gamma);
var dtau = F.mul(this.D, tau);
var xCoord = F.div(F.add(beta, gamma), F.add(F.one, dtau));
var yCoord = F.div(F.add(delta, F.sub(F.mul(this.A, beta), gamma)), F.sub(F.one, dtau));
return new Point(xCoord, yCoord);
};
BabyJub.prototype.mulPointEscalar = function (base, e) {
var F = this.F;
var res = new Point(F.e("0"), F.e("1"));
var rem = e;
var exp = base;
while (!Scalar.isZero(rem)) {
if (Scalar.isOdd(rem)) {
res = this.addPoint(res, exp);
}
exp = this.addPoint(exp, exp);
rem = Scalar.shiftRight(rem, 1);
}
return res;
};
BabyJub.prototype.inCurve = function (P) {
var F = this.F;
var x2 = F.square(P.x);
var y2 = F.square(P.y);
if (!F.eq(F.add(F.mul(this.A, x2), y2), F.add(F.one, F.mul(F.mul(x2, y2), this.D))))
return false;
return true;
};
return BabyJub;
}());
var poseidon;
function buildPoseidon() {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!poseidon) return [3 /*break*/, 2];
return [4 /*yield*/, buildPoseidon$1()];
case 1:
poseidon = _a.sent();
_a.label = 2;
case 2: return [2 /*return*/, function (inputs) {
var hash = poseidon(inputs.map(function (x) { return BigNumber.from(x); }));
var hashStr = poseidon.F.toString(hash);
return BigNumber.from(hashStr);
}];
}
});
});
}
function getPoseidonFiniteField() {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!poseidon) return [3 /*break*/, 2];
return [4 /*yield*/, buildPoseidon()];
case 1:
poseidon = _a.sent();
_a.label = 2;
case 2: return [2 /*return*/, poseidon.F];
}
});
});
}
var eddsa;
function buildEddsa() {
return __awaiter(this, void 0, void 0, function () {
var poseidon, field, babyJub;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, buildPoseidon()];
case 1:
poseidon = _a.sent();
return [4 /*yield*/, getPoseidonFiniteField()];
case 2:
field = _a.sent();
babyJub = new BabyJub(field);
if (!eddsa)
eddsa = new EdDSA(babyJub, poseidon);
return [2 /*return*/, eddsa];
}
});
});
}
// Highly inspired from https://github.com/iden3/circomlibjs/blob/main/src/eddsa.js
var EdDSA = /** @class */ (function () {
function EdDSA(babyJub, poseidon) {
this.babyJub = babyJub;
this.poseidon = poseidon;
}
EdDSA.prototype.pruneBuffer = function (buff) {
buff[0] = buff[0] & 0xf8;
buff[31] = buff[31] & 0x7f;
buff[31] = buff[31] | 0x40;
return buff;
};
EdDSA.prototype.prv2pub = function (prv) {
this.babyJub.F;
var sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
var s = Scalar.fromRprLE(sBuff, 0, 32);
var A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
return A;
};
EdDSA.prototype.signPoseidon = function (prv, msg) {
var _this = this;
var F = this.babyJub.F;
var sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
var s = Scalar.fromRprLE(sBuff, 0, 32);
var A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
var composeBuff = new Uint8Array(32 + msg.length);
composeBuff.set(sBuff.slice(32), 0);
F.toRprLE(composeBuff, 32, msg);
var rBuff = createBlakeHash("blake512")
.update(Buffer.from(composeBuff))
.digest();
var r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder);
var R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
var hm = this.poseidon([R8.x, R8.y, A.x, A.y, msg].map(function (x) {
return Scalar.e(_this.babyJub.F.toObject(x));
}));
var hms = Scalar.e(this.babyJub.F.toObject(hm));
var S = Scalar.mod(Scalar.add(r, Scalar.mul(hms, s)), this.babyJub.subOrder);
return {
R8: R8,
S: S,
};
};
EdDSA.prototype.verifyPoseidon = function (msg, sig, A) {
var _this = this;
var R8 = new Point(sig.R8[0], sig.R8[1]);
// Check parameters
if (typeof sig != "object")
return false;
if (!Array.isArray(sig.R8))
return false;
if (sig.R8.length != 2)
return false;
if (!this.babyJub.inCurve(R8))
return false;
if (!Array.isArray(A))
return false;
if (A.length != 2)
return false;
if (!this.babyJub.inCurve(Point.fromArray(A)))
return false;
if (sig.S >= this.babyJub.subOrder)
return false;
var hm = this.poseidon([sig.R8[0], sig.R8[1], A[0], A[1], msg].map(function (x) {
return Scalar.e(_this.babyJub.F.toObject(x));
}));
var hms = Scalar.e(this.babyJub.F.toObject(hm));
var Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
var Pright = this.babyJub.mulPointEscalar(Point.fromArray(A), Scalar.mul(hms, 8));
Pright = this.babyJub.addPoint(R8, Pright);
if (!this.babyJub.F.eq(Pleft.x, Pright.x))
return false;
if (!this.babyJub.F.eq(Pleft.y, Pright.y))
return false;
return true;
};
return EdDSA;
}());
var EddsaAccount = /** @class */ (function () {
function EddsaAccount(pubKey, privKey, eddsa, babyjub) {
this.pubKey = pubKey;
this.privKey = privKey;
this.eddsa = eddsa;
this.babyJub = babyjub;
}
EddsaAccount.generateFromSeed = function (seed) {
return __awaiter(this, void 0, void 0, function () {
var eddsa, babyjub, privKey, pubKey, pubKeyBigNumber;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, buildEddsa()];
case 1:
eddsa = _a.sent();
babyjub = eddsa.babyJub;
privKey = Buffer.from(BigNumber.from(seed).toHexString().slice(2), "hex");
pubKey = eddsa.prv2pub(privKey);
pubKeyBigNumber = [
BigNumber.from(babyjub.F.toObject(pubKey.x).toString()),
BigNumber.from(babyjub.F.toObject(pubKey.y).toString()),
];
return [2 /*return*/, new EddsaAccount(pubKeyBigNumber, privKey, eddsa, babyjub)];
}
});
});
};
EddsaAccount.prototype.sign = function (msg) {
var msgF = this.babyJub.F.e(BigNumber.from(msg));
var signature = this.eddsa.signPoseidon(this.privKey, msgF);
return [
BigNumber.from(this.babyJub.F.toObject(signature.R8.x)),
BigNumber.from(this.babyJub.F.toObject(signature.R8.y)),
BigNumber.from(signature.S),
];
};
EddsaAccount.verify = function (msg, signature, pubKey) {
return __awaiter(this, void 0, void 0, function () {
var babyJub, eddsa, msgF, sigPoseidon, pubKeyPoseidon;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, buildBabyjub()];
case 1:
babyJub = _a.sent();
return [4 /*yield*/, buildEddsa()];
case 2:
eddsa = _a.sent();
msgF = babyJub.F.e(BigNumber.from(msg));
sigPoseidon = {
R8: [
babyJub.F.e(BigNumber.from(signature[0])),
babyJub.F.e(BigNumber.from(signature[1])),
],
S: BigNumber.from(signature[2]).toBigInt(),
};
pubKeyPoseidon = [
babyJub.F.e(BigNumber.from(pubKey[0])),
babyJub.F.e(BigNumber.from(pubKey[1])),
];
return [2 /*return*/, eddsa.verifyPoseidon(msgF, sigPoseidon, pubKeyPoseidon)];
}
});
});
};
EddsaAccount.prototype.getPubKey = function () {
return this.pubKey;
};
return EddsaAccount;
}());
var SNARK_FIELD = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617");
function keccak256(inputs) {
return BigNumber.from(ethers.utils.keccak256(ethers.utils.solidityPack(["uint256", "uint256"], [inputs[0], inputs[1]])));
}
export { EddsaAccount, SNARK_FIELD, buildPoseidon, getPoseidonFiniteField, keccak256 };
//# sourceMappingURL=index.js.map