diff --git a/src/helpers/metadataUtils.ts b/src/helpers/metadataUtils.ts index efea217..0f5da9b 100644 --- a/src/helpers/metadataUtils.ts +++ b/src/helpers/metadataUtils.ts @@ -1,15 +1,16 @@ import { decrypt } from "@toruslabs/eccrypto"; import { Data, post } from "@toruslabs/http-helpers"; import BN from "bn.js"; -import { ec } from "elliptic"; +import { ec as EC } from "elliptic"; import stringify from "json-stable-stringify"; import log from "loglevel"; import { SAPPHIRE_METADATA_URL } from "../constants"; -import { EciesHex, GetOrSetNonceResult, MetadataParams } from "../interfaces"; +import { EciesHex, GetOrSetNonceResult, MetadataParams, SapphireMetadataParams } from "../interfaces"; import { encParamsHexToBuf } from "./common"; import { keccak256 } from "./keyUtils"; +const secp256k1Curve = new EC("secp256k1"); export function convertMetadataToNonce(params: { message?: string }) { if (!params || !params.message) { return new BN(0); @@ -26,7 +27,7 @@ export async function decryptNodeData(eciesData: EciesHex, ciphertextHex: string return decryptedSigBuffer; } -export function generateMetadataParams(ecCurve: ec, serverTimeOffset: number, message: string, privateKey: BN): MetadataParams { +export function generateMetadataParams(ecCurve: EC, serverTimeOffset: number, message: string, privateKey: BN): MetadataParams { const key = ecCurve.keyFromPrivate(privateKey.toString("hex", 64)); const setData = { data: message, @@ -60,7 +61,7 @@ export async function getMetadata( export async function getOrSetNonce( legacyMetadataHost: string, - ecCurve: ec, + ecCurve: EC, serverTimeOffset: number, X: string, Y: string, @@ -83,7 +84,7 @@ export async function getOrSetNonce( export async function getNonce( legacyMetadataHost: string, - ecCurve: ec, + ecCurve: EC, serverTimeOffset: number, X: string, Y: string, @@ -91,13 +92,27 @@ export async function getNonce( ): Promise { return getOrSetNonce(legacyMetadataHost, ecCurve, serverTimeOffset, X, Y, privKey, true); } - -export async function getOrSetSapphireMetadataNonce(X: string, Y: string): Promise { - const data = { +export async function getOrSetSapphireMetadataNonce(X: string, Y: string, serverTimeOffset?: number, privKey?: BN): Promise { + let data: SapphireMetadataParams = { pub_key_X: X, pub_key_Y: Y, key_type: "secp256k1", set_data: { operation: "getOrSetNonce" }, }; + if (privKey) { + const key = secp256k1Curve.keyFromPrivate(privKey.toString("hex", 64)); + + const setData = { + operation: "getOrSetNonce", + timestamp: new BN(~~(serverTimeOffset + Date.now() / 1000)).toString(16), + }; + const sig = key.sign(keccak256(Buffer.from(stringify(setData), "utf8")).slice(2)); + data = { + ...data, + set_data: setData, + signature: Buffer.from(sig.r.toString(16, 64) + sig.s.toString(16, 64) + new BN("").toString(16, 2), "hex").toString("base64"), + }; + } + return post(`${SAPPHIRE_METADATA_URL}/get_or_set_nonce`, data, undefined, { useAPIKey: true }); } diff --git a/src/helpers/nodeUtils.ts b/src/helpers/nodeUtils.ts index 32b8cf5..52607b0 100644 --- a/src/helpers/nodeUtils.ts +++ b/src/helpers/nodeUtils.ts @@ -95,6 +95,9 @@ export const GetPubKeyOrKeyAssign = async (params: { // rechecking nonceResult to avoid promise race condition. if (!nonceResult && metadataNonceResult) { nonceResult = metadataNonceResult; + if (nonceResult.nonce) { + delete nonceResult.nonce; + } } } } @@ -375,29 +378,11 @@ export async function retrieveOrImportShare(params: { } }); - // if both thresholdNonceData and extended_verifier_id are not available - // then we need to throw other wise address would be incorrect. - if (!thresholdNonceData && !verifierParams.extended_verifier_id && !LEGACY_NETWORKS_ROUTE_MAP[network as TORUS_LEGACY_NETWORK_TYPE]) { - const metadataNonceResult = await getOrSetSapphireMetadataNonce(thresholdPublicKey.X, thresholdPublicKey.Y); - // rechecking nonceResult to avoid promise race condition. - if (metadataNonceResult && !thresholdNonceData) { - thresholdNonceData = metadataNonceResult; - } else { - throw new Error( - `invalid metadata result from nodes, nonce metadata is empty for verifier: ${verifier} and verifierId: ${verifierParams.verifier_id}` - ); - } - } - const thresholdReqCount = importedShares.length > 0 ? endpoints.length : minThreshold; // 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 - if ( - completedRequests.length >= thresholdReqCount && - thresholdPublicKey && - (thresholdNonceData || verifierParams.extended_verifier_id || LEGACY_NETWORKS_ROUTE_MAP[network as TORUS_LEGACY_NETWORK_TYPE]) - ) { + if (completedRequests.length >= thresholdReqCount && thresholdPublicKey) { const sharePromises: Promise[] = []; const sessionTokenSigPromises: Promise[] = []; const sessionTokenPromises: Promise[] = []; @@ -564,13 +549,28 @@ export async function retrieveOrImportShare(params: { }); }) .then(async (res) => { - const { privateKey, sessionTokenData, thresholdNonceData, nodeIndexes, isNewKey, serverTimeOffsetResponse } = res; + const { privateKey, sessionTokenData, nodeIndexes, thresholdNonceData, isNewKey, serverTimeOffsetResponse } = res; let nonceResult = thresholdNonceData; if (!privateKey) throw new Error("Invalid private key returned"); + const oAuthKey = privateKey; const oAuthPubKey = getPublic(Buffer.from(oAuthKey.toString(16, 64), "hex")).toString("hex"); const oAuthPubkeyX = oAuthPubKey.slice(2, 66); const oAuthPubkeyY = oAuthPubKey.slice(66); + + // if both thresholdNonceData and extended_verifier_id are not available + // then we need to throw other wise address would be incorrect. + if (!nonceResult && !verifierParams.extended_verifier_id && !LEGACY_NETWORKS_ROUTE_MAP[network as TORUS_LEGACY_NETWORK_TYPE]) { + const metadataNonceResult = await getOrSetSapphireMetadataNonce(oAuthPubkeyX, oAuthPubkeyY, serverTimeOffset, oAuthKey); + // rechecking nonceResult to avoid promise race condition. + if (metadataNonceResult && !thresholdNonceData) { + nonceResult = metadataNonceResult; + } else { + throw new Error( + `invalid metadata result from nodes, nonce metadata is empty for verifier: ${verifier} and verifierId: ${verifierParams.verifier_id}` + ); + } + } let metadataNonce = new BN(nonceResult?.nonce ? nonceResult.nonce.padStart(64, "0") : "0", "hex"); let finalPubKey: curve.base.BasePoint; let pubNonce: { X: string; Y: string } | undefined; diff --git a/src/interfaces.ts b/src/interfaces.ts index d291acd..1ad525e 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -194,3 +194,15 @@ export interface MetadataParams { }; signature: string; } + +export interface SapphireMetadataParams { + namespace?: string; + pub_key_X: string; + pub_key_Y: string; + key_type: "secp256k1" | "ed25519"; + set_data: { + operation: "getNonce" | "getOrSetNonce" | string; + timestamp?: string; + }; + signature?: string; +}