From 936669d4408625828ab6c9106aeb369134fc279a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Tue, 10 Sep 2024 17:09:09 +0300 Subject: [PATCH] chore: export ed25519 and blake2b as external dependency --- .../blockannounce/BlockAnnounceEngine.java | 6 +- .../protocol/grandpa/GrandpaEngine.java | 8 +- .../limechain/sync/JustificationVerifier.java | 2 +- .../java/com/limechain/utils/HashUtils.java | 2 +- src/main/webapp/js/blake2b.js | 167 --------- src/main/webapp/js/ed25519.js | 320 ------------------ src/main/webapp/js/fruzhin-lib.js | 6 +- 7 files changed, 11 insertions(+), 500 deletions(-) delete mode 100644 src/main/webapp/js/blake2b.js delete mode 100644 src/main/webapp/js/ed25519.js diff --git a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceEngine.java b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceEngine.java index 057ecf249..49ce13c62 100644 --- a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceEngine.java +++ b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceEngine.java @@ -49,7 +49,7 @@ public static String getHandshake() { @JSBody(params = {"handshake", "protocolId"}, script = "window.fruzhin.libp.getConnections().forEach(async (peer) => {" + " let stream = await ItPbStream.pbStream(await window.fruzhin.libp.dialProtocol(peer.remotePeer, protocolId));" + - " stream.writeLP(window.fruzhin.ED25519.h2b(handshake));" + + " stream.writeLP(Ed25519.h2b(handshake));" + "});") public static native void sendHandshakeToAll(String handshake, String protocolId); @@ -61,9 +61,9 @@ public static String getHandshake() { " let subarr = msg.subarray();" + " if(subarr.length === 69) {" + " let handshake = announceExport.getHandshake();" + - " (await ItPbStream.pbStream(stream)).writeLP(window.fruzhin.ED25519.h2b(handshake));" + + " (await ItPbStream.pbStream(stream)).writeLP(Ed25519.h2b(handshake));" + " } else if (subarr.length > 1) {" + - " announceExport.blockAnnounce(window.fruzhin.ED25519.b2h(subarr.slice(2)), connection.remotePeer.toString());" + + " announceExport.blockAnnounce(Ed25519.b2h(subarr.slice(2)), connection.remotePeer.toString());" + " }" + " }" + " });" + diff --git a/src/main/java/com/limechain/network/protocol/grandpa/GrandpaEngine.java b/src/main/java/com/limechain/network/protocol/grandpa/GrandpaEngine.java index 8d63c0493..916dadf65 100644 --- a/src/main/java/com/limechain/network/protocol/grandpa/GrandpaEngine.java +++ b/src/main/java/com/limechain/network/protocol/grandpa/GrandpaEngine.java @@ -107,7 +107,7 @@ public static String getNeighbourMessage() { @JSBody(params = {"handshake", "protocolId"}, script = "window.fruzhin.libp.getConnections().forEach(async (peer) => {" + " let stream = await ItPbStream.pbStream(await window.fruzhin.libp.dialProtocol(peer.remotePeer, protocolId));" + - " stream.write(window.fruzhin.ED25519.h2b(handshake));" + "});") + " stream.write(Ed25519.h2b(handshake));" + "});") public static native void sendHandshakeToAll(String handshake, String protocolId); @JSBody(params = {"grandpaExport", "protocolId"}, script = @@ -116,13 +116,13 @@ public static String getNeighbourMessage() { " for await (const msg of source) {" + " let subarr = msg.subarray();" + " if(subarr.length === 1) {" + " let handshake = grandpaExport.getHandshake();" + - " (await ItPbStream.pbStream(stream)).writeLP(window.fruzhin.ED25519.h2b(handshake));" + + " (await ItPbStream.pbStream(stream)).writeLP(Ed25519.h2b(handshake));" + " } else if (subarr.length > 1) {" + " if(subarr.slice(1)[0] === 2) {" + " let niehgbourMessage = grandpaExport.getNeighbourMessage();" + - " (await ItPbStream.pbStream(stream)).writeLP(window.fruzhin.ED25519.h2b(niehgbourMessage));" + + " (await ItPbStream.pbStream(stream)).writeLP(Ed25519.h2b(niehgbourMessage));" + " }" + - " grandpaExport.handleMessage(window.fruzhin.ED25519.b2h(subarr.slice(1)), connection.remotePeer.toString());" + + " grandpaExport.handleMessage(Ed25519.b2h(subarr.slice(1)), connection.remotePeer.toString());" + " }" + " }" + " });" + " });") public static native void registerHandler(JSObject grandpaExport, String protocolId); diff --git a/src/main/java/com/limechain/sync/JustificationVerifier.java b/src/main/java/com/limechain/sync/JustificationVerifier.java index 279b05168..f5874d659 100644 --- a/src/main/java/com/limechain/sync/JustificationVerifier.java +++ b/src/main/java/com/limechain/sync/JustificationVerifier.java @@ -134,7 +134,7 @@ private static byte[] getDataToVerify(Precommit precommit, BigInteger authoritie } @JSBody(params = {"publicKeyHex", "signatureHex", - "messageHex"}, script = "return window.fruzhin.ED25519.verifyAsync(signatureHex, messageHex, publicKeyHex);") + "messageHex"}, script = "return Ed25519.verifyAsync(signatureHex, messageHex, publicKeyHex);") public static native JSPromise verifyAsync(String publicKeyHex, String signatureHex, String messageHex); } diff --git a/src/main/java/com/limechain/utils/HashUtils.java b/src/main/java/com/limechain/utils/HashUtils.java index 03a552dfc..1801f1f06 100644 --- a/src/main/java/com/limechain/utils/HashUtils.java +++ b/src/main/java/com/limechain/utils/HashUtils.java @@ -9,6 +9,6 @@ public class HashUtils { @JSBody(params = {"inputHex"}, script = "{" + "let bytes = new Uint8Array([...inputHex.matchAll(/../g)].map(m => parseInt(m[0], 16)));" + - "return window.fruzhin.Blake2b.hash(bytes,undefined,32);" + "}") + "return Blake2B.hash(bytes,undefined,32);" + "}") public static native JSString hashWithBlake2b(String inputHex); } diff --git a/src/main/webapp/js/blake2b.js b/src/main/webapp/js/blake2b.js deleted file mode 100644 index d3a838e02..000000000 --- a/src/main/webapp/js/blake2b.js +++ /dev/null @@ -1,167 +0,0 @@ -export var Blake2b = { - v: new Uint32Array(32), - m: new Uint32Array(32), - BLAKE2B_IV32: new Uint32Array([4089235720, 1779033703, 2227873595, 3144134277, 4271175723, 1013904242, 1595750129, 2773480762, 2917565137, 1359893119, 725511199, 2600822924, 4215389547, 528734635, 327033209, 1541459225]), - parameterBlock: new Uint8Array(64).fill(0), - SIGMA82: new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3].map(function (x) { - return x * 2; - })), - ADD64AA(v2, a, b) { - const o0 = v2[a] + v2[b]; - let o1 = v2[a + 1] + v2[b + 1]; - if (o0 >= 4294967296) o1++; - v2[a] = o0; - v2[a + 1] = o1; - }, - ADD64AC(v2, a, b0, b1) { - let o0 = v2[a] + b0; - if (b0 < 0) o0 += 4294967296; - let o1 = v2[a + 1] + b1; - if (o0 >= 4294967296) o1++; - v2[a] = o0; - v2[a + 1] = o1; - }, - B2B_GET32(arr, i) { - return arr[i] ^ arr[i + 1] << 8 ^ arr[i + 2] << 16 ^ arr[i + 3] << 24; - }, - B2B_G(a, b, c, d, ix, iy) { - const x0 = (this.m)[ix]; - const x1 = (this.m)[ix + 1]; - const y0 = (this.m)[iy]; - const y1 = (this.m)[iy + 1]; - this.ADD64AA(this.v, a, b); - this.ADD64AC(this.v, a, x0, x1); - let xor0 = (this.v)[d] ^ (this.v)[a]; - let xor1 = (this.v)[d + 1] ^ (this.v)[a + 1]; - (this.v)[d] = xor1; - (this.v)[d + 1] = xor0; - this.ADD64AA(this.v, c, d); - xor0 = (this.v)[b] ^ (this.v)[c]; - xor1 = (this.v)[b + 1] ^ (this.v)[c + 1]; - (this.v)[b] = xor0 >>> 24 ^ xor1 << 8; - (this.v)[b + 1] = xor1 >>> 24 ^ xor0 << 8; - this.ADD64AA(this.v, a, b); - this.ADD64AC(this.v, a, y0, y1); - xor0 = (this.v)[d] ^ (this.v)[a]; - xor1 = (this.v)[d + 1] ^ (this.v)[a + 1]; - (this.v)[d] = xor0 >>> 16 ^ xor1 << 16; - (this.v)[d + 1] = xor1 >>> 16 ^ xor0 << 16; - this.ADD64AA(this.v, c, d); - xor0 = (this.v)[b] ^ (this.v)[c]; - xor1 = (this.v)[b + 1] ^ (this.v)[c + 1]; - (this.v)[b] = xor1 >>> 31 ^ xor0 << 1; - (this.v)[b + 1] = xor0 >>> 31 ^ xor1 << 1; - }, - blake2bCompress(ctx, last) { - let i = 0; - for (i = 0; i < 16; i++) { - (this.v)[i] = ctx.h[i]; - (this.v)[i + 16] = (this.BLAKE2B_IV32)[i]; - } - (this.v)[24] = (this.v)[24] ^ ctx.t; - (this.v)[25] = (this.v)[25] ^ ctx.t / 4294967296; - if (last) { - (this.v)[28] = ~(this.v)[28]; - (this.v)[29] = ~(this.v)[29]; - } - for (i = 0; i < 32; i++) { - (this.m)[i] = this.B2B_GET32(ctx.b, 4 * i); - } - for (i = 0; i < 12; i++) { - this.B2B_G(0, 8, 16, 24, (this.SIGMA82)[i * 16 + 0], (this.SIGMA82)[i * 16 + 1]); - this.B2B_G(2, 10, 18, 26, (this.SIGMA82)[i * 16 + 2], (this.SIGMA82)[i * 16 + 3]); - this.B2B_G(4, 12, 20, 28, (this.SIGMA82)[i * 16 + 4], (this.SIGMA82)[i * 16 + 5]); - this.B2B_G(6, 14, 22, 30, (this.SIGMA82)[i * 16 + 6], (this.SIGMA82)[i * 16 + 7]); - this.B2B_G(0, 10, 20, 30, (this.SIGMA82)[i * 16 + 8], (this.SIGMA82)[i * 16 + 9]); - this.B2B_G(2, 12, 22, 24, (this.SIGMA82)[i * 16 + 10], (this.SIGMA82)[i * 16 + 11]); - this.B2B_G(4, 14, 16, 26, (this.SIGMA82)[i * 16 + 12], (this.SIGMA82)[i * 16 + 13]); - this.B2B_G(6, 8, 18, 28, (this.SIGMA82)[i * 16 + 14], (this.SIGMA82)[i * 16 + 15]); - } - for (i = 0; i < 16; i++) { - ctx.h[i] = ctx.h[i] ^ (this.v)[i] ^ (this.v)[i + 16]; - } - }, - blake2bInit(outlen, key, salt, personal) { - if (outlen === 0 || outlen > 64) { - throw new Error("Illegal output length, expected 0 < length <= 64"); - } - if (key && key.length > 64) { - throw new Error("Illegal key, expected Uint8Array with 0 < length <= 64"); - } - if (salt && salt.length !== 16) { - throw new Error("Illegal salt, expected Uint8Array with length is 16"); - } - if (personal && personal.length !== 16) { - throw new Error("Illegal personal, expected Uint8Array with length is 16"); - } - const ctx = { - b: new Uint8Array(128), h: new Uint32Array(16), t: 0, c: 0, outlen - }; - this.parameterBlock.fill(0); - (this.parameterBlock)[0] = outlen; - if (key) (this.parameterBlock)[1] = key.length; - (this.parameterBlock)[2] = 1; - (this.parameterBlock)[3] = 1; - if (salt) this.parameterBlock.set(salt, 32); - if (personal) this.parameterBlock.set(personal, 48); - for (let i = 0; i < 16; i++) { - ctx.h[i] = (this.BLAKE2B_IV32)[i] ^ this.B2B_GET32(this.parameterBlock, i * 4); - } - if (key) { - this.blake2bUpdate(ctx, key); - ctx.c = 128; - } - return ctx; - }, - blake2bUpdate(ctx, input) { - for (let i = 0; i < input.length; i++) { - if (ctx.c === 128) { - ctx.t += ctx.c; - this.blake2bCompress(ctx, false); - ctx.c = 0; - } - ctx.b[ctx.c++] = input[i]; - } - }, - blake2bFinal(ctx) { - ctx.t += ctx.c; - while (ctx.c < 128) { - ctx.b[ctx.c++] = 0; - } - this.blake2bCompress(ctx, true); - const out = new Uint8Array(ctx.outlen); - for (let i = 0; i < ctx.outlen; i++) { - out[i] = ctx.h[i >> 2] >> 8 * (i & 3); - } - return out; - }, - blake2bStart(input, key, outlen, salt, personal) { - outlen = outlen || 64; - const ctx = this.blake2bInit(outlen, key, this.normalizeInput(salt), this.normalizeInput(personal)); - this.blake2bUpdate(ctx, this.normalizeInput(input)); - return this.blake2bFinal(ctx); - }, - normalizeInput(input) { - let ret; - if (input instanceof Uint8Array) { - ret = input; - } else if (typeof input === "string") { - const encoder = new TextEncoder; - ret = encoder.encode(input); - } else { - throw new Error("Input must be an string, Buffer or Uint8Array"); - } - return ret; - }, - toHex(bytes) { - return Array.prototype.map.call(bytes, function (n) { - return (n < 16 ? "0" : "") + n.toString(16); - }).join(""); - }, - hash(message = "", secret = undefined, length = 64, salt = new Uint8Array(16), personal = new Uint8Array(16)) { - if (secret?.length === 0) secret = undefined; - if (typeof secret === "string") secret = new TextEncoder().encode(secret); - const output = this.blake2bStart(message, secret, length, salt, personal); - return this.toHex(output); - } -} \ No newline at end of file diff --git a/src/main/webapp/js/ed25519.js b/src/main/webapp/js/ed25519.js deleted file mode 100644 index 7323aec0b..000000000 --- a/src/main/webapp/js/ed25519.js +++ /dev/null @@ -1,320 +0,0 @@ -const P = 2n ** 255n - 19n; // ed25519 is twisted edwards curve -const N = 2n ** 252n + 27742317777372353535851937790883648493n; // curve's (group) order -const Gx = 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an; // base point x -const Gy = 0x6666666666666666666666666666666666666666666666666666666666666658n; // base point y -const CURVE = { - a: -1n, // where a=-1, d = -(121665/121666) == -(121665 * inv(121666)) mod P - d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n, - p: P, n: N, h: 8, Gx, Gy // field prime, curve (group) order, cofactor -}; -const err = (m = '') => { throw new Error(m); }; // error helper, messes-up stack trace -const str = (s) => typeof s === 'string'; // is string -const isu8 = (a) => (a instanceof Uint8Array || - (a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array')); -const au8 = (a, l) => // is Uint8Array (of specific length) - !isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ? - err('Uint8Array of valid length expected') : a; -const u8n = (data) => new Uint8Array(data); // creates Uint8Array -const toU8 = (a, len) => au8(str(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a -const mod = (a, b = P) => { let r = a % b; return r >= 0n ? r : b + r; }; // mod division -const isPoint = (p) => (p instanceof Point ? p : err('Point expected')); // is xyzt point - -class Point { - constructor(ex, ey, ez, et) { - this.ex = ex; - this.ey = ey; - this.ez = ez; - this.et = et; - } - static fromHex(hex, zip215 = false) { - const { d } = CURVE; - hex = toU8(hex, 32); - const normed = hex.slice(); // copy the array to not mess it up - const lastByte = hex[31]; - normed[31] = lastByte & ~0x80; // adjust first LE byte = last BE byte - const y = b2n_LE(normed); // decode as little-endian, convert to num - if (zip215 && !(0n <= y && y < 2n ** 256n)) - err('bad y coord 1'); // zip215=true [1..2^256-1] - if (!zip215 && !(0n <= y && y < P)) - err('bad y coord 2'); // zip215=false [1..P-1] - const y2 = mod(y * y); // y² - const u = mod(y2 - 1n); // u=y²-1 - const v = mod(d * y2 + 1n); // v=dy²+1 - let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root - if (!isValid) - err('bad y coordinate 3'); // not square root: bad point - const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate - const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit - if (!zip215 && x === 0n && isLastByteOdd) - err('bad y coord 3'); // x=0 and x_0 = 1 - if (isLastByteOdd !== isXOdd) - x = mod(-x); - return new Point(x, y, 1n, mod(x * y)); // Z=1, T=xy - } - equals(other) { - const { ex: X1, ey: Y1, ez: Z1 } = this; - const { ex: X2, ey: Y2, ez: Z2 } = isPoint(other); // isPoint() checks class equality - const X1Z2 = mod(X1 * Z2), X2Z1 = mod(X2 * Z1); - const Y1Z2 = mod(Y1 * Z2), Y2Z1 = mod(Y2 * Z1); - return X1Z2 === X2Z1 && Y1Z2 === Y2Z1; - } - is0() { return this.equals(I); } - negate() { - return new Point(mod(-this.ex), this.ey, this.ez, mod(-this.et)); - } - double() { - const { ex: X1, ey: Y1, ez: Z1 } = this; // Cost: 4M + 4S + 1*a + 6add + 1*2 - const { a } = CURVE; // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd - const A = mod(X1 * X1); - const B = mod(Y1 * Y1); - const C = mod(2n * mod(Z1 * Z1)); - const D = mod(a * A); - const x1y1 = X1 + Y1; - const E = mod(mod(x1y1 * x1y1) - A - B); - const G = D + B; - const F = G - C; - const H = D - B; - const X3 = mod(E * F); - const Y3 = mod(G * H); - const T3 = mod(E * H); - const Z3 = mod(F * G); - return new Point(X3, Y3, Z3, T3); - } - add(other) { - const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this; // Cost: 8M + 1*k + 8add + 1*2. - const { ex: X2, ey: Y2, ez: Z2, et: T2 } = isPoint(other); // doesn't check if other on-curve - const { a, d } = CURVE; // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3 - const A = mod(X1 * X2); - const B = mod(Y1 * Y2); - const C = mod(T1 * d * T2); - const D = mod(Z1 * Z2); - const E = mod((X1 + Y1) * (X2 + Y2) - A - B); - const F = mod(D - C); - const G = mod(D + C); - const H = mod(B - a * A); - const X3 = mod(E * F); - const Y3 = mod(G * H); - const T3 = mod(E * H); - const Z3 = mod(F * G); - return new Point(X3, Y3, Z3, T3); - } - mul(n, safe = true) { - if (n === 0n) - return safe === true ? err('cannot multiply by 0') : I; - if (!(typeof n === 'bigint' && 0n < n && n < N)) - err('invalid scalar, must be < L'); - if (!safe && this.is0() || n === 1n) - return this; // safe=true bans 0. safe=false allows 0. - if (this.equals(G)) - return wNAF(n).p; // use wNAF precomputes for base points - let p = I, f = G; // init result point & fake point - for (let d = this; n > 0n; d = d.double(), n >>= 1n) { // double-and-add ladder - if (n & 1n) - p = p.add(d); // if bit is present, add to point - else if (safe) - f = f.add(d); // if not, add to fake for timing safety - } - return p; - } - clearCofactor() { return this.mul(BigInt(CURVE.h), false); } // multiply by cofactor - isSmallOrder() { return this.clearCofactor().is0(); } // check if P is small order - toAffine() { - const { ex: x, ey: y, ez: z } = this; // (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy) - if (this.equals(I)) - return { x: 0n, y: 1n }; // fast-path for zero point - const iz = invert(z); // z^-1: invert z - if (mod(z * iz) !== 1n) - err('invalid inverse'); // (z * z^-1) must be 1, otherwise bad math - return { x: mod(x * iz), y: mod(y * iz) }; // x = x*z^-1; y = y*z^-1 - } - toRawBytes() { - const { x, y } = this.toAffine(); // convert to affine 2d point - const b = n2b_32LE(y); // encode number to 32 bytes - b[31] |= x & 1n ? 0x80 : 0; // store sign in first LE byte - return b; - } -} -Point.BASE = new Point(Gx, Gy, 1n, mod(Gx * Gy)); // Generator / Base point -Point.ZERO = new Point(0n, 1n, 1n, 0n); // Identity / Zero point -const { BASE: G, ZERO: I } = Point; // Generator, identity points -const padh = (num, pad) => num.toString(16).padStart(pad, '0'); - -const n2b_32LE = (num) => h2b(padh(num, 32 * 2)).reverse(); // number to bytes LE -const b2n_LE = (b) => BigInt('0x' + b2h(u8n(au8(b)).reverse())); // bytes LE to num -const concatB = (...arrs) => { - const r = u8n(arrs.reduce((sum, a) => sum + au8(a).length, 0)); // create u8a of summed length - let pad = 0; // walk through each array, - arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type - return r; -}; -const invert = (num, md = P) => { - if (num === 0n || md <= 0n) - err('no inverse n=' + num + ' mod=' + md); // no neg exponent for now - let a = mod(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n; - while (a !== 0n) { // uses euclidean gcd algorithm - const q = b / a, r = b % a; // not constant-time - const m = x - u * q, n = y - v * q; - b = a, a = r, x = u, y = v, u = m, v = n; - } - return b === 1n ? mod(x, md) : err('no inverse'); // b is gcd at this point -}; -const pow2 = (x, power) => { - let r = x; - while (power-- > 0n) { - r *= r; - r %= P; - } - return r; -}; -const pow_2_252_3 = (x) => { - const x2 = (x * x) % P; // x^2, bits 1 - const b2 = (x2 * x) % P; // x^3, bits 11 - const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111 - const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111 - const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10) - const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20) - const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40) - const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80) - const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160) - const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240) - const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250) - const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x. - return { pow_p_5_8, b2 }; -}; -const RM1 = 19681161376707505956807079304988542015446066515923890162744021073123829784752n; // √-1 -const uvRatio = (u, v) => { - const v3 = mod(v * v * v); // v³ - const v7 = mod(v3 * v3 * v); // v⁷ - const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8 - let x = mod(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8 - const vx2 = mod(v * x * x); // vx² - const root1 = x; // First root candidate - const root2 = mod(x * RM1); // Second root candidate; RM1 is √-1 - const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root - const useRoot2 = vx2 === mod(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4) - const noRoot = vx2 === mod(-u * RM1); // There is no valid root, vx² = -u√-1 - if (useRoot1) - x = root1; - if (useRoot2 || noRoot) - x = root2; // We return root2 anyway, for const-time - if ((mod(x) & 1n) === 1n) - x = mod(-x); // edIsNegative - return { isValid: useRoot1 || useRoot2, value: x }; -}; -const modL_LE = (hash) => mod(b2n_LE(hash), N); // modulo L; but little-endian -let _shaS; -const sha512a = (...m) => etc.sha512Async(...m); // Async SHA512 -const sha512s = (...m) => // Sync SHA512, not set by default - typeof _shaS === 'function' ? _shaS(...m) : err('etc.sha512Sync not set'); - -function hashFinish(asynchronous, res) { - if (asynchronous) - return sha512a(res.hashable).then(res.finish); - return res.finish(sha512s(res.hashable)); -} - -const dvo = { zip215: true }; -const _verify = (sig, msg, pub, opts = dvo) => { - msg = toU8(msg); // Message hex str/Bytes - sig = toU8(sig, 64); // Signature hex str/Bytes, must be 64 bytes - const { zip215 } = opts; // switch between zip215 and rfc8032 verif - let A, R, s, SB, hashable = new Uint8Array(); - try { - A = Point.fromHex(pub, zip215); // public key A decoded - R = Point.fromHex(sig.slice(0, 32), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P - s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S - SB = G.mul(s, false); // in the range 0 <= s < L - hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M) - } - catch (error) { } - const finish = (hashed) => { - if (SB == null) - return false; // false if try-catch catched an error - if (!zip215 && A.isSmallOrder()) - return false; // false for SBS: Strongly Binding Signature - const k = modL_LE(hashed); // decode in little-endian, modulo L - const RkA = R.add(A.mul(k, false)); // [8]R + [8][k]A' - return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A' - }; - return { hashable, finish }; -}; -const cr = () => // We support: 1) browsers 2) node.js 19+ - typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined; -const etc = { - mod, invert, - sha512Async: async (...messages) => { - const crypto = cr(); - if (!crypto || !crypto.subtle) - err('crypto.subtle or etc.sha512Async must be defined'); - const m = concatB(...messages); - return u8n(await crypto.subtle.digest('SHA-512', m.buffer)); - }, -}; -Object.defineProperties(etc, { sha512Sync: { - configurable: false, get() { return _shaS; }, set(f) { if (!_shaS) - _shaS = f; }, - } }); -const W = 8; // Precomputes-related code. W = window size -const precompute = () => { - const points = []; // 10x sign(), 2x verify(). To achieve this, - const windows = 256 / W + 1; // app needs to spend 40ms+ to calculate - let p = G, b = p; // a lot of points related to base point G. - for (let w = 0; w < windows; w++) { // Points are stored in array and used - b = p; // any time Gx multiplication is done. - points.push(b); // They consume 16-32 MiB of RAM. - for (let i = 1; i < 2 ** (W - 1); i++) { - b = b.add(p); - points.push(b); - } - p = b.double(); // Precomputes don't speed-up getSharedKey, - } // which multiplies user point by scalar, - return points; // when precomputes are using base point -}; -let Gpows = undefined; // precomputes for base point G -const wNAF = (n) => { - // Compared to other point mult methods, - const comp = Gpows || (Gpows = precompute()); // stores 2x less points using subtraction - const neg = (cnd, p) => { let n = p.negate(); return cnd ? n : p; }; // negate - let p = I, f = G; // f must be G, or could become I in the end - const windows = 1 + 256 / W; // W=8 17 windows - const wsize = 2 ** (W - 1); // W=8 128 window size - const mask = BigInt(2 ** W - 1); // W=8 will create mask 0b11111111 - const maxNum = 2 ** W; // W=8 256 - const shiftBy = BigInt(W); // W=8 8 - for (let w = 0; w < windows; w++) { - const off = w * wsize; - let wbits = Number(n & mask); // extract W bits. - n >>= shiftBy; // shift number by W bits. - if (wbits > wsize) { - wbits -= maxNum; - n += 1n; - } // split if bits > max: +224 => 256-32 - const off1 = off, off2 = off + Math.abs(wbits) - 1; // offsets, evaluate both - const cnd1 = w % 2 !== 0, cnd2 = wbits < 0; // conditions, evaluate both - if (wbits === 0) { - f = f.add(neg(cnd1, comp[off1])); // bits are 0: add garbage to fake point - } - else { // ^ can't add off2, off2 = I - p = p.add(neg(cnd2, comp[off2])); // bits are 1: add to result point - } - } - return { p, f }; // return both real and fake points for JIT -}; - -export const verifyAsync = async (s, m, p, opts = dvo) => hashFinish(true, _verify(s, m, p, opts)); -export const b2h = (b) => Array.from(b).map(e => padh(e, 2)).join(''); // bytes to hex -export const h2b = (hex) => { - const l = hex.length; // error if not string, - if (!str(hex) || l % 2) - err('hex invalid 1'); // or has odd length like 3, 5. - const arr = u8n(l / 2); // create result array - for (let i = 0; i < arr.length; i++) { - const j = i * 2; - const h = hex.slice(j, j + 2); // hexByte. slice is faster than substr - const b = Number.parseInt(h, 16); // byte, created from string part - if (Number.isNaN(b) || b < 0) - err('hex invalid 2'); // byte must be valid 0 <= byte < 256 - arr[i] = b; - } - return arr; -}; \ No newline at end of file diff --git a/src/main/webapp/js/fruzhin-lib.js b/src/main/webapp/js/fruzhin-lib.js index d921284f8..96e31de6c 100644 --- a/src/main/webapp/js/fruzhin-lib.js +++ b/src/main/webapp/js/fruzhin-lib.js @@ -9,9 +9,9 @@ import 'https://unpkg.com/@chainsafe/libp2p-gossipsub/dist/index.min.js'; import 'https://unpkg.com/libp2p/dist/index.min.js'; import 'https://unpkg.com/it-pipe/dist/index.min.js'; import 'https://unpkg.com/it-pb-stream/dist/index.min.js'; +import 'https://unpkg.com/@muradsenteca/ed25519/dist/index.min.js' +import 'https://unpkg.com/@muradsenteca/blake2b/dist/index.min.js' -import * as Blake2b from './blake2b.js'; -import * as ED25519 from './ed25519.js'; import * as HTTP from './http.js'; import * as Fruzhin from './fruzhin.js' @@ -45,8 +45,6 @@ var startLibp2p = async (bootnodes) => { window.fruzhin = { startLibp2p, - ED25519, HTTP, - ...Blake2b, ...Fruzhin, }