From fcaba4b8ab76280279071bd947fa300c8b277a77 Mon Sep 17 00:00:00 2001 From: Chaitanya Potti Date: Tue, 30 Jul 2024 19:51:23 +0800 Subject: [PATCH] minor refactor --- package-lock.json | 58 ++++++++++---------- package.json | 12 ++--- src/Polynomial.ts | 4 +- src/helpers/common.ts | 11 +++- src/helpers/keyUtils.ts | 72 ++++++------------------- src/helpers/langrangeInterpolatePoly.ts | 28 +++++----- src/helpers/metadataUtils.ts | 50 ++++++++++++----- src/helpers/nodeUtils.ts | 52 +++++++++--------- src/interfaces.ts | 3 ++ src/torus.ts | 2 +- 10 files changed, 146 insertions(+), 146 deletions(-) diff --git a/package-lock.json b/package-lock.json index c97ff03..a3f80fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,15 +13,15 @@ "@toruslabs/eccrypto": "^5.0.4", "@toruslabs/http-helpers": "^7.0.0", "bn.js": "^5.2.1", - "bs58": "^5.0.0", - "elliptic": "^6.5.5", + "bs58": "^6.0.0", + "elliptic": "^6.5.6", "ethereum-cryptography": "^2.2.1", "json-stable-stringify": "^1.1.1", "loglevel": "^1.9.1" }, "devDependencies": { "@babel/register": "^7.24.6", - "@babel/runtime": "^7.24.8", + "@babel/runtime": "^7.25.0", "@toruslabs/config": "^2.1.0", "@toruslabs/eslint-config-typescript": "^3.3.1", "@toruslabs/fetch-node-details": "^14.0.1", @@ -38,15 +38,15 @@ "dotenv": "^16.4.5", "eslint": "^8.57.0", "faker": "^5.5.3", - "husky": "^9.0.11", + "husky": "^9.1.4", "jsonwebtoken": "^9.0.2", "lint-staged": "^15.2.7", - "mocha": "^10.6.0", + "mocha": "^10.7.0", "prettier": "^3.3.3", "rimraf": "^6.0.1", "sinon": "^18.0.0", "ts-node": "^10.9.2", - "typescript": "^5.5.3" + "typescript": "^5.5.4" }, "engines": { "node": ">=18.x", @@ -1815,9 +1815,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -4451,9 +4451,9 @@ "dev": true }, "node_modules/base-x": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", - "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -4850,11 +4850,11 @@ } }, "node_modules/bs58": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", - "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", "dependencies": { - "base-x": "^4.0.0" + "base-x": "^5.0.0" } }, "node_modules/buffer": { @@ -5888,9 +5888,9 @@ "dev": true }, "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz", + "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -7890,12 +7890,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", + "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -10015,9 +10015,9 @@ } }, "node_modules/mocha": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", - "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -13025,9 +13025,9 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index b38c46c..a408260 100644 --- a/package.json +++ b/package.json @@ -28,15 +28,15 @@ "@toruslabs/eccrypto": "^5.0.4", "@toruslabs/http-helpers": "^7.0.0", "bn.js": "^5.2.1", - "elliptic": "^6.5.5", - "bs58": "^5.0.0", + "elliptic": "^6.5.6", + "bs58": "^6.0.0", "ethereum-cryptography": "^2.2.1", "json-stable-stringify": "^1.1.1", "loglevel": "^1.9.1" }, "devDependencies": { "@babel/register": "^7.24.6", - "@babel/runtime": "^7.24.8", + "@babel/runtime": "^7.25.0", "@toruslabs/config": "^2.1.0", "@toruslabs/eslint-config-typescript": "^3.3.1", "@toruslabs/fetch-node-details": "^14.0.1", @@ -53,15 +53,15 @@ "dotenv": "^16.4.5", "eslint": "^8.57.0", "faker": "^5.5.3", - "husky": "^9.0.11", + "husky": "^9.1.4", "jsonwebtoken": "^9.0.2", "lint-staged": "^15.2.7", - "mocha": "^10.6.0", + "mocha": "^10.7.0", "prettier": "^3.3.3", "rimraf": "^6.0.1", "sinon": "^18.0.0", "ts-node": "^10.9.2", - "typescript": "^5.5.3" + "typescript": "^5.5.4" }, "repository": { "type": "git", diff --git a/src/Polynomial.ts b/src/Polynomial.ts index b5237f7..c86ab4f 100644 --- a/src/Polynomial.ts +++ b/src/Polynomial.ts @@ -30,9 +30,9 @@ class Polynomial { for (let i = 1; i < this.polynomial.length; i += 1) { const tmp = xi.mul(this.polynomial[i]); sum = sum.add(tmp); - sum = sum.umod(this.ecCurve.curve.n); + sum = sum.umod(this.ecCurve.n); xi = xi.mul(new BN(tmpX)); - xi = xi.umod(this.ecCurve.curve.n); + xi = xi.umod(this.ecCurve.n); } return sum; } diff --git a/src/helpers/common.ts b/src/helpers/common.ts index 1ecb58c..8d37f0a 100644 --- a/src/helpers/common.ts +++ b/src/helpers/common.ts @@ -2,10 +2,19 @@ import { JRPCResponse, KEY_TYPE } from "@toruslabs/constants"; import { Ecies } from "@toruslabs/eccrypto"; import { BN } from "bn.js"; import { ec as EC } from "elliptic"; +import { keccak256 as keccakHash } from "ethereum-cryptography/keccak"; import JsonStringify from "json-stable-stringify"; import { CommitmentRequestResult, EciesHex, KeyType, VerifierLookupResponse } from "../interfaces"; -import { keccak256 } from "./keyUtils"; + +export function keccak256(a: Buffer): string { + const hash = Buffer.from(keccakHash(a)).toString("hex"); + return `0x${hash}`; +} + +export const generatePrivateKey = (ecCurve: EC, buf: typeof Buffer): Buffer => { + return ecCurve.genKeyPair().getPrivate().toArrayLike(buf); +}; export const getKeyCurve = (keyType: KeyType) => { if (keyType === KEY_TYPE.ED25519) { diff --git a/src/helpers/keyUtils.ts b/src/helpers/keyUtils.ts index e506bec..12810c6 100644 --- a/src/helpers/keyUtils.ts +++ b/src/helpers/keyUtils.ts @@ -9,18 +9,9 @@ import stringify from "json-stable-stringify"; import log from "loglevel"; import { EncryptedSeed, ImportedShare, KeyType, PrivateKeyData } from "../interfaces"; -import { encParamsBufToHex, getKeyCurve } from "./common"; +import { encParamsBufToHex, generatePrivateKey, getKeyCurve, keccak256 } from "./common"; import { generateRandomPolynomial } from "./langrangeInterpolatePoly"; -import { generateNonceMetadataParams } from "./metadataUtils"; - -export function keccak256(a: Buffer): string { - const hash = Buffer.from(keccakHash(a)).toString("hex"); - return `0x${hash}`; -} - -export const generatePrivateKey = (ecCurve: EC, buf: typeof Buffer): Buffer => { - return ecCurve.genKeyPair().getPrivate().toArrayLike(buf); -}; +import { generateNonceMetadataParams, getSecpKeyFromEd25519 } from "./metadataUtils"; export function stripHexPrefix(str: string): string { return str.startsWith("0x") ? str.slice(2) : str; @@ -81,30 +72,6 @@ export function getEd25519ExtendedPublicKey(keyBuffer: Buffer): { return { scalar, point }; } -export const getSecpKeyFromEd25519 = ( - ed25519Scalar: BN -): { - scalar: BN; - point: curve.base.BasePoint; -} => { - const secp256k1Curve = getKeyCurve(KEY_TYPE.SECP256K1); - - const ed25519Key = ed25519Scalar.toString("hex", 64); - const keyHash = keccakHash(Buffer.from(ed25519Key, "hex")); - const secpKey = new BN(keyHash).umod(secp256k1Curve.curve.n).toString("hex", 64); - const bufferKey = Buffer.from(secpKey, "hex"); - - const secpKeyPair = secp256k1Curve.keyFromPrivate(bufferKey); - - if (bufferKey.length < 32) { - throw new Error("Invalid key length, please try again"); - } - return { - scalar: secpKeyPair.getPrivate(), - point: secpKeyPair.getPublic(), - }; -}; - export function encodeEd25519Point(point: curve.base.BasePoint) { const ed25519Curve = getKeyCurve(KEY_TYPE.ED25519); @@ -149,11 +116,11 @@ export const generateSecp256k1KeyData = async (scalarBuffer: Buffer): Promise { @@ -230,7 +192,7 @@ export const generateShares = async ( for (const nodeIndex of nodeIndexes) { nodeIndexesBn.push(new BN(nodeIndex)); } - const oAuthPubKey = ecCurve.keyFromPrivate(oAuthKey.toString("hex").padStart(64, "0")).getPublic(); + const oAuthPubKey = ecCurve.keyFromPrivate(oAuthKey.toString("hex", 64), "hex").getPublic(); const poly = generateRandomPolynomial(ecCurve, degree, oAuthKey); const shares = poly.generateShares(nodeIndexesBn); const nonceParams = generateNonceMetadataParams(serverTimeOffset, "getOrSetNonce", metadataSigningKey, keyType, metadataNonce, encryptedSeed); @@ -248,7 +210,7 @@ export const generateShares = async ( ); } const encShares = await Promise.all(encPromises); - for (let i = 0; i < nodeIndexesBn.length; i++) { + for (let i = 0; i < nodeIndexesBn.length; i += 1) { const shareJson = shares[nodeIndexesBn[i].toString("hex", 64)].toJSON() as Record; const encParams = encShares[i]; const encParamsMetadata = encParamsBufToHex(encParams); diff --git a/src/helpers/langrangeInterpolatePoly.ts b/src/helpers/langrangeInterpolatePoly.ts index 28290e6..568f342 100644 --- a/src/helpers/langrangeInterpolatePoly.ts +++ b/src/helpers/langrangeInterpolatePoly.ts @@ -4,7 +4,7 @@ import { ec as EC } from "elliptic"; import Point from "../Point"; import Polynomial from "../Polynomial"; import Share from "../Share"; -import { generatePrivateKey } from "."; +import { generatePrivateKey } from "./common"; function generatePrivateExcludingIndexes(shareIndexes: BN[], ecCurve: EC): BN { const key = new BN(generatePrivateKey(ecCurve, Buffer)); @@ -22,9 +22,9 @@ const denominator = (ecCurve: EC, i: number, innerPoints: Point[]) => { if (i !== j) { let tmp = new BN(xi); tmp = tmp.sub(innerPoints[j].x); - tmp = tmp.umod(ecCurve.curve.n); + tmp = tmp.umod(ecCurve.n); result = result.mul(tmp); - result = result.umod(ecCurve.curve.n); + result = result.umod(ecCurve.n); } } return result; @@ -36,7 +36,7 @@ const interpolationPoly = (ecCurve: EC, i: number, innerPoints: Point[]): BN[] = if (d.cmp(new BN(0)) === 0) { throw new Error("Denominator for interpolationPoly is 0"); } - coefficients[0] = d.invm(ecCurve.curve.n); + coefficients[0] = d.invm(ecCurve.n); for (let k = 0; k < innerPoints.length; k += 1) { const newCoefficients = generateEmptyBNArray(innerPoints.length); if (k !== i) { @@ -48,10 +48,10 @@ const interpolationPoly = (ecCurve: EC, i: number, innerPoints: Point[]): BN[] = } j -= 1; for (; j >= 0; j -= 1) { - newCoefficients[j + 1] = newCoefficients[j + 1].add(coefficients[j]).umod(ecCurve.curve.n); + newCoefficients[j + 1] = newCoefficients[j + 1].add(coefficients[j]).umod(ecCurve.n); let tmp = new BN(innerPoints[k].x); - tmp = tmp.mul(coefficients[j]).umod(ecCurve.curve.n); - newCoefficients[j] = newCoefficients[j].sub(tmp).umod(ecCurve.curve.n); + tmp = tmp.mul(coefficients[j]).umod(ecCurve.n); + newCoefficients[j] = newCoefficients[j].sub(tmp).umod(ecCurve.n); } coefficients = newCoefficients; } @@ -73,7 +73,7 @@ const lagrange = (ecCurve: EC, unsortedPoints: Point[]) => { for (let k = 0; k < sortedPoints.length; k += 1) { let tmp = new BN(sortedPoints[i].y); tmp = tmp.mul(coefficients[k]); - polynomial[k] = polynomial[k].add(tmp).umod(ecCurve.curve.n); + polynomial[k] = polynomial[k].add(tmp).umod(ecCurve.n); } } return new Polynomial(polynomial, ecCurve); @@ -94,17 +94,17 @@ export function lagrangeInterpolation(ecCurve: EC, shares: BN[], nodeIndex: BN[] for (let j = 0; j < shares.length; j += 1) { if (i !== j) { upper = upper.mul(nodeIndex[j].neg()); - upper = upper.umod(ecCurve.curve.n); + upper = upper.umod(ecCurve.n); let temp = nodeIndex[i].sub(nodeIndex[j]); - temp = temp.umod(ecCurve.curve.n); - lower = lower.mul(temp).umod(ecCurve.curve.n); + temp = temp.umod(ecCurve.n); + lower = lower.mul(temp).umod(ecCurve.n); } } - let delta = upper.mul(lower.invm(ecCurve.curve.n)).umod(ecCurve.curve.n); - delta = delta.mul(shares[i]).umod(ecCurve.curve.n); + let delta = upper.mul(lower.invm(ecCurve.n)).umod(ecCurve.n); + delta = delta.mul(shares[i]).umod(ecCurve.n); secret = secret.add(delta); } - return secret.umod(ecCurve.curve.n); + return secret.umod(ecCurve.n); } // generateRandomPolynomial - determinisiticShares are assumed random diff --git a/src/helpers/metadataUtils.ts b/src/helpers/metadataUtils.ts index 2e959fd..ed992c1 100644 --- a/src/helpers/metadataUtils.ts +++ b/src/helpers/metadataUtils.ts @@ -2,7 +2,8 @@ import { KEY_TYPE, LEGACY_NETWORKS_ROUTE_MAP, TORUS_LEGACY_NETWORK_TYPE, TORUS_N import { decrypt } from "@toruslabs/eccrypto"; import { Data, post } from "@toruslabs/http-helpers"; import BN from "bn.js"; -import { ec as EC } from "elliptic"; +import { curve, ec as EC } from "elliptic"; +import { keccak256 as keccakHash } from "ethereum-cryptography/keccak"; import stringify from "json-stable-stringify"; import log from "loglevel"; @@ -17,8 +18,32 @@ import { SapphireMetadataParams, SetNonceData, } from "../interfaces"; -import { encParamsHexToBuf, getKeyCurve } from "./common"; -import { getSecpKeyFromEd25519, keccak256 } from "./keyUtils"; +import { encParamsHexToBuf, getKeyCurve, keccak256 } from "./common"; + +export const getSecpKeyFromEd25519 = ( + ed25519Scalar: BN +): { + scalar: BN; + point: curve.base.BasePoint; +} => { + const secp256k1Curve = getKeyCurve(KEY_TYPE.SECP256K1); + + const ed25519Key = ed25519Scalar.toString("hex", 64); + const keyHash = keccakHash(Buffer.from(ed25519Key, "hex")); + const secpKey = new BN(keyHash).umod(secp256k1Curve.n).toString("hex", 64); + const bufferKey = Buffer.from(secpKey, "hex"); + + const secpKeyPair = secp256k1Curve.keyFromPrivate(bufferKey); + + if (bufferKey.length < 32) { + throw new Error(`Key length must be less than 32. got ${bufferKey.length}`); + } + return { + scalar: secpKeyPair.getPrivate(), + point: secpKeyPair.getPublic(), + }; +}; + export function convertMetadataToNonce(params: { message?: string }) { if (!params || !params.message) { return new BN(0); @@ -44,6 +69,7 @@ export async function decryptNodeDataWithPadding(eciesData: EciesHex, ciphertext }); return decryptedSigBuffer; } catch (error) { + // ciphertext can be any length. not just 64. depends on input. we have this for legacy reason const ciphertextHexPadding = ciphertextHex.padStart(64, "0"); log.warn("Failed to decrypt padded share cipher", error); @@ -53,7 +79,7 @@ export async function decryptNodeDataWithPadding(eciesData: EciesHex, ciphertext } export function generateMetadataParams(ecCurve: EC, serverTimeOffset: number, message: string, privateKey: BN): MetadataParams { - const key = ecCurve.keyFromPrivate(privateKey.toString("hex", 64)); + const key = ecCurve.keyFromPrivate(privateKey.toString("hex", 64), "hex"); const setData = { data: message, timestamp: new BN(~~(serverTimeOffset + Date.now() / 1000)).toString(16), @@ -93,7 +119,7 @@ export function generateNonceMetadataParams( seed?: string ): NonceMetadataParams { // metadata only uses secp for sig validation - const key = getKeyCurve(KEY_TYPE.SECP256K1).keyFromPrivate(privateKey.toString("hex", 64)); + const key = getKeyCurve(KEY_TYPE.SECP256K1).keyFromPrivate(privateKey.toString("hex", 64), "hex"); const setData: Partial = { operation, timestamp: new BN(~~(serverTimeOffset + Date.now() / 1000)).toString(16), @@ -167,7 +193,7 @@ export async function getOrSetNonce( const data = { pub_key_X: X, pub_key_Y: Y, - set_data: { operation: "getNonce" }, + set_data: { operation }, key_type: keyType, }; return post(`${metadataHost}/get_or_set_nonce`, data, undefined, { useAPIKey: true }); @@ -187,13 +213,8 @@ export const decryptSeedData = async (seedBase64: string, finalUserKey: BN) => { const decryptionKey = getSecpKeyFromEd25519(finalUserKey); const seedUtf8 = Buffer.from(seedBase64, "base64").toString("utf-8"); const seedJson = JSON.parse(seedUtf8) as EncryptedSeed; - const bufferMetadata = { - ephemPublicKey: Buffer.from(seedJson.metadata.ephemPublicKey, "hex"), - iv: Buffer.from(seedJson.metadata.iv, "hex"), - mac: Buffer.from(seedJson.metadata.mac, "hex"), - mode: "AES256", - }; - const bufferKey = Buffer.from(decryptionKey.scalar.toString("hex", 64), "hex"); + const bufferMetadata = { ...encParamsHexToBuf(seedJson.metadata), mode: "AES256" }; + const bufferKey = decryptionKey.scalar.toArrayLike(Buffer); const decText = await decrypt(bufferKey, { ...bufferMetadata, ciphertext: Buffer.from(seedJson.enc_text, "hex"), @@ -201,6 +222,7 @@ export const decryptSeedData = async (seedBase64: string, finalUserKey: BN) => { return decText; }; + export async function getOrSetSapphireMetadataNonce( network: TORUS_NETWORK_TYPE, X: string, @@ -218,7 +240,7 @@ export async function getOrSetSapphireMetadataNonce( set_data: { operation: "getOrSetNonce" }, }; if (privKey) { - const key = getKeyCurve(KEY_TYPE.SECP256K1).keyFromPrivate(privKey.toString("hex", 64)); + const key = getKeyCurve(KEY_TYPE.SECP256K1).keyFromPrivate(privKey.toString("hex", 64), "hex"); const setData = { operation: "getOrSetNonce", diff --git a/src/helpers/nodeUtils.ts b/src/helpers/nodeUtils.ts index 08112e4..ffc7cc3 100644 --- a/src/helpers/nodeUtils.ts +++ b/src/helpers/nodeUtils.ts @@ -27,16 +27,17 @@ import { import log from "../loglevel"; import { Some } from "../some"; import { TorusUtilsExtraParams } from "../TorusUtilsExtraParams"; -import { calculateMedian, getProxyCoordinatorEndpointIndex, kCombinations, normalizeKeysResult, retryCommitment, thresholdSame } from "./common"; import { - derivePubKey, - generateAddressFromPrivKey, - generateAddressFromPubKey, + calculateMedian, generatePrivateKey, - generateShares, - getSecpKeyFromEd25519, + getProxyCoordinatorEndpointIndex, + kCombinations, keccak256, -} from "./keyUtils"; + normalizeKeysResult, + retryCommitment, + thresholdSame, +} from "./common"; +import { derivePubKey, generateAddressFromPrivKey, generateAddressFromPubKey, generateShares } from "./keyUtils"; import { lagrangeInterpolation } from "./langrangeInterpolatePoly"; import { decryptNodeData, @@ -45,6 +46,7 @@ import { getMetadata, getOrSetNonce, getOrSetSapphireMetadataNonce, + getSecpKeyFromEd25519, } from "./metadataUtils"; export const GetPubKeyOrKeyAssign = async (params: { @@ -218,13 +220,16 @@ export async function retrieveOrImportShare(params: { const pubKeyX = pubKey.slice(2, 66); const pubKeyY = pubKey.slice(66); let finalImportedShares: ImportedShare[] = []; + const threeFourthsThreshold = ~~((endpoints.length * 3) / 4) + 1; + const halfThreshold = ~~(endpoints.length / 2) + 1; - if (newImportedShares.length > 0) { + if (newImportedShares?.length > 0) { if (newImportedShares.length !== endpoints.length) { throw new Error("Invalid imported shares length"); } finalImportedShares = newImportedShares; } else if (!useDkg) { + // TODO: why use getrandombytes here? const bufferKey = keyType === KEY_TYPE.SECP256K1 ? generatePrivateKey(ecCurve, Buffer) : await getRandomBytes(32); const generatedShares = await generateShares(ecCurve, keyType, serverTimeOffset, indexes, nodePubkeys, Buffer.from(bufferKey)); finalImportedShares = [...finalImportedShares, ...generatedShares]; @@ -289,7 +294,7 @@ export async function retrieveOrImportShare(params: { if (requiredNodeResult) { return Promise.resolve(resultArr); } - } else if (!overrideExistingKey && completedRequests.length >= ~~((endpoints.length * 3) / 4) + 1) { + } else if (!overrideExistingKey && completedRequests.length >= threeFourthsThreshold) { const nodeSigs: CommitmentRequestResult[] = []; for (let i = 0; i < completedRequests.length; i += 1) { const x = completedRequests[i]; @@ -300,7 +305,7 @@ export async function retrieveOrImportShare(params: { } const existingPubKey = thresholdSame( nodeSigs.map((x) => x && x.pub_key_x), - ~~(endpoints.length / 2) + 1 + halfThreshold ); const proxyEndpointNum = getProxyCoordinatorEndpointIndex(endpoints, verifier, verifierParams.verifier_id); // for import shares, proxy node response is required. @@ -321,7 +326,7 @@ export async function retrieveOrImportShare(params: { } } } - } else if (completedRequests.length >= ~~((endpoints.length * 3) / 4) + 1) { + } else if (completedRequests.length >= threeFourthsThreshold) { // this case is for dkg keys const requiredNodeResult = completedRequests.find((resp: void | JRPCResponse) => { if (resp) { @@ -350,7 +355,7 @@ export async function retrieveOrImportShare(params: { // if user's account already const existingPubKey = thresholdSame( nodeSigs.map((x) => x && x.pub_key_x), - ~~(endpoints.length / 2) + 1 + halfThreshold ); // can only import shares if override existing key is allowed or for new non dkg registration @@ -475,7 +480,7 @@ export async function retrieveOrImportShare(params: { return undefined; }); - const thresholdPublicKey = thresholdSame(pubkeys, ~~(endpoints.length / 2) + 1); + const thresholdPublicKey = thresholdSame(pubkeys, halfThreshold); if (!thresholdPublicKey) { throw new Error("invalid result from nodes, threshold number of public key results are not matching"); @@ -492,7 +497,7 @@ export async function retrieveOrImportShare(params: { } }); - const thresholdReqCount = canImportedShares ? endpoints.length : ~~(endpoints.length / 2) + 1; + const thresholdReqCount = canImportedShares ? endpoints.length : halfThreshold; // optimistically run lagrange interpolation once threshold number of shares have been received // this is matched against the user public key to ensure that shares are consistent // Note: no need of thresholdMetadataNonce for extended_verifier_id key @@ -578,9 +583,8 @@ export async function retrieveOrImportShare(params: { return false; }); - const minThresholdRequired = ~~(endpoints.length / 2) + 1; - if (!verifierParams.extended_verifier_id && validSigs.length < minThresholdRequired) { - throw new Error(`Insufficient number of signatures from nodes, required: ${minThresholdRequired}, found: ${validSigs.length}`); + if (!verifierParams.extended_verifier_id && validSigs.length < halfThreshold) { + throw new Error(`Insufficient number of signatures from nodes, required: ${halfThreshold}, found: ${validSigs.length}`); } const validTokens = sessionTokensResolved.filter((token) => { @@ -590,8 +594,8 @@ export async function retrieveOrImportShare(params: { return false; }); - if (!verifierParams.extended_verifier_id && validTokens.length < minThresholdRequired) { - throw new Error(`Insufficient number of session tokens from nodes, required: ${minThresholdRequired}, found: ${validTokens.length}`); + if (!verifierParams.extended_verifier_id && validTokens.length < halfThreshold) { + throw new Error(`Insufficient number of session tokens from nodes, required: ${halfThreshold}, found: ${validTokens.length}`); } sessionTokensResolved.forEach((x, index) => { if (!x || !sessionSigsResolved[index]) sessionTokenData.push(undefined); @@ -616,7 +620,7 @@ export async function retrieveOrImportShare(params: { [] as { index: BN; value: BN }[] ); // run lagrange interpolation on all subsets, faster in the optimistic scenario than berlekamp-welch due to early exit - const allCombis = kCombinations(decryptedShares.length, ~~(endpoints.length / 2) + 1); + const allCombis = kCombinations(decryptedShares.length, halfThreshold); let privateKey: BN | null = null; for (let j = 0; j < allCombis.length; j += 1) { @@ -640,7 +644,7 @@ export async function retrieveOrImportShare(params: { throw new Error("could not derive private key"); } - const thresholdIsNewKey = thresholdSame(isNewKeyResponses, ~~(endpoints.length / 2) + 1); + const thresholdIsNewKey = thresholdSame(isNewKeyResponses, halfThreshold); // Convert each string timestamp to a number const serverOffsetTimes = serverTimeOffsetResponses.map((timestamp) => Number.parseInt(timestamp, 10)); @@ -716,14 +720,14 @@ export async function retrieveOrImportShare(params: { typeOfUser = "v1"; // for imported keys in legacy networks metadataNonce = await getMetadata(legacyMetadataHost, { pub_key_X: oAuthPubkeyX, pub_key_Y: oAuthPubkeyY }); - const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.curve.n); + const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.n); finalPubKey = ecCurve.keyFromPrivate(privateKeyWithNonce.toString(16, 64), "hex").getPublic(); } } else { typeOfUser = "v1"; // for imported keys in legacy networks metadataNonce = await getMetadata(legacyMetadataHost, { pub_key_X: oAuthPubkeyX, pub_key_Y: oAuthPubkeyY }); - const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.curve.n); + const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.n); finalPubKey = ecCurve.keyFromPrivate(privateKeyWithNonce.toString(16, 64), "hex").getPublic(); } } else { @@ -755,7 +759,7 @@ export async function retrieveOrImportShare(params: { } if (typeOfUser === "v1" || (typeOfUser === "v2" && metadataNonce.gt(new BN(0)))) { - const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.curve.n); + const privateKeyWithNonce = oAuthKey.add(metadataNonce).umod(ecCurve.n); keyWithNonce = privateKeyWithNonce.toString("hex", 64); } if (keyType === KEY_TYPE.SECP256K1) { diff --git a/src/interfaces.ts b/src/interfaces.ts index 02b8e13..b55a5e8 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -156,11 +156,13 @@ export interface SessionToken { node_puby: string; } export interface TorusPublicKey { + // based on curve type finalKeyData: { walletAddress: string; // format depends on key type X: string; // this is final pub x user before and after updating to 2/n Y: string; // this is final pub y user before and after updating to 2/n }; + // based on curve type oAuthKeyData: { walletAddress: string; // format depends on key type X: string; @@ -185,6 +187,7 @@ export interface TorusKey { oAuthKeyData: TorusPublicKey["oAuthKeyData"] & { privKey: string; }; + // always secp key postboxKeyData: { X: string; Y: string; diff --git a/src/torus.ts b/src/torus.ts index 3e4780a..404410c 100644 --- a/src/torus.ts +++ b/src/torus.ts @@ -193,7 +193,7 @@ class Torus { } } if (this.keyType === KEY_TYPE.ED25519) { - privKeyBuffer = Buffer.from(newPrivateKey, "hex"); + privKeyBuffer = Buffer.from(newPrivateKey.padStart(64, "0"), "hex"); if (privKeyBuffer.length !== 32) { throw new Error("Invalid private key length for given ed25519 key"); }