Skip to content

refactor: validates key type and sig type for TSS Endpoints #112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: feat/override-keytype-sigtype
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/fetch-node-details/src/nodeDetailManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
WEB3AUTH_KEY_TYPE,
WEB3AUTH_SIG_TYPE,
} from "@toruslabs/constants";
import { fetchLocalConfig } from "@toruslabs/fnd-base";
import { fetchLocalConfig, validateKeyTypeAndSigTypeForTSS } from "@toruslabs/fnd-base";
import { get } from "@toruslabs/http-helpers";
import logger from "loglevel";

Expand Down Expand Up @@ -100,6 +100,9 @@ class NodeDetailManager {
const finalKeyType = keyType ?? this._keyType;
const finalSigType = sigType ?? this._sigType;

// validate key type and sig type to fetch TSS endpoints
validateKeyTypeAndSigTypeForTSS(finalKeyType, finalSigType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should do validation on server side only specifically to maintain forward compatibility specially for fnd sdk. Its a good practice to fail fast in general but in this case we want server to be source of truth to ensure older client sdks will remain compatible to future changes in endpoints. I dont know any specific reason to do that for now but its just from past experience we want this sdk to remain future compatible

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok.
But in the same method, https://github.com/torusresearch/fetch-node-details/blob/feat/key-sig-type-validation/packages/fetch-node-details/src/nodeDetailManager.ts#L117, we are also directly calling the fetchLocalConfig from fnd-base when it's failed to fetch from the server.
I think it'll still do validation on the client side.


try {
const { nodeDetails } = await get<{ nodeDetails: INodeDetails }>(
`${this.fndServerEndpoint}?network=${this.network}&verifier=${verifier}&verifierId=${verifierId}&keyType=${finalKeyType}&sigType=${finalSigType}`
Expand Down
25 changes: 23 additions & 2 deletions packages/fetch-node-details/test/nodeDetail.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { METADATA_MAP, TORUS_LEGACY_NETWORK, TORUS_NETWORK_TYPE, TORUS_SAPPHIRE_NETWORK } from "@toruslabs/constants";
import { METADATA_MAP, SIG_TYPE, TORUS_LEGACY_NETWORK, TORUS_NETWORK_TYPE, TORUS_SAPPHIRE_NETWORK } from "@toruslabs/constants";
import { getSapphireNodeDetails } from "@toruslabs/fnd-base";
import { deepStrictEqual, strictEqual, throws } from "assert";
import { deepStrictEqual, rejects, strictEqual, throws } from "assert";

import NodeDetailManager from "../src/nodeDetailManager";

Expand Down Expand Up @@ -115,6 +115,27 @@ describe("Fetch Node Details", function () {
);
});

it("#should throw error for invalid key type and sig type", async function () {
const nodeDetailManager = new NodeDetailManager({
network: TORUS_SAPPHIRE_NETWORK.SAPPHIRE_MAINNET,
fndServerEndpoint,
enableLogging: true,
});

const sigType = SIG_TYPE.ED25519;

await rejects(
async () => {
await nodeDetailManager.getNodeDetails({ verifier: "google", verifierId: "[email protected]", keyType: "secp256k1", sigType });
},
(err) => {
const error = err as Error;
strictEqual(error.message, `Invalid key type for ${sigType}`);
return true;
}
);
});

it("#should use default network if not provided", async function () {
const nodeDetailManager = new NodeDetailManager({
fndServerEndpoint,
Expand Down
33 changes: 4 additions & 29 deletions packages/fnd-base/src/endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {
KEY_TYPE,
LEGACY_NETWORKS_ROUTE_MAP,
SIG_TYPE,
TORUS_LEGACY_NETWORK_TYPE,
TORUS_SAPPHIRE_NETWORK,
TORUS_SAPPHIRE_NETWORK_TYPE,
WEB3AUTH_KEY_TYPE,
WEB3AUTH_SIG_TYPE,
} from "@toruslabs/constants";

import { validateSigTypeAndGetTSSPath } from "./utils";

export const SAPPHIRE_NETWORK_URLS: Record<TORUS_SAPPHIRE_NETWORK_TYPE, string[]> = {
[TORUS_SAPPHIRE_NETWORK.SAPPHIRE_DEVNET]: [
"https://node-1.dev-node.web3auth.io",
Expand Down Expand Up @@ -66,34 +67,8 @@ export const getTSSEndpoints = (
throw new Error(`Unsupported network: ${sapphireNetwork}`);
}

const tssPath = (() => {
const dklsPath = "tss";
const frostPath = "tss-frost";
if (sigType) {
if (sigType === SIG_TYPE.ECDSA_SECP256K1) {
if (keyType !== KEY_TYPE.SECP256K1) {
throw new Error("Invalid key type for ecdsa-secp256k1");
}
return dklsPath;
} else if (sigType === SIG_TYPE.ED25519) {
if (keyType !== KEY_TYPE.ED25519) {
throw new Error("Invalid key type for ed25519");
}
return frostPath;
} else if (sigType === SIG_TYPE.BIP340) {
if (keyType !== KEY_TYPE.SECP256K1) {
throw new Error("Invalid key type for bip340");
}
return frostPath;
}
throw new Error("Invalid sig type");
} else if (keyType === KEY_TYPE.SECP256K1) {
return dklsPath;
} else if (keyType === KEY_TYPE.ED25519) {
return frostPath;
}
throw new Error("Invalid key type");
})();
// validate the keyType and sigType and get the relevant tssPath
const tssPath = validateSigTypeAndGetTSSPath(keyType, sigType);

const routeIdentifier = LEGACY_NETWORKS_ROUTE_MAP[legacyNetwork as TORUS_LEGACY_NETWORK_TYPE];
return endpoints.map((e) => {
Expand Down
27 changes: 27 additions & 0 deletions packages/fnd-base/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
import {
INodeDetails,
LEGACY_NETWORKS_ROUTE_MAP,
TORUS_LEGACY_NETWORK,
TORUS_LEGACY_NETWORK_TYPE,
TORUS_NETWORK_TYPE,
TORUS_SAPPHIRE_NETWORK,
TORUS_SAPPHIRE_NETWORK_TYPE,
WEB3AUTH_KEY_TYPE,
WEB3AUTH_SIG_TYPE,
} from "@toruslabs/constants";

import { getSapphireNodeDetails } from "./sapphireNetworkConfig";

export * from "./endpoints";
export * from "./sapphireNetworkConfig";
export * from "./utils";

export function fetchLocalConfig(network: TORUS_NETWORK_TYPE, keyType: WEB3AUTH_KEY_TYPE, sigType?: WEB3AUTH_SIG_TYPE): INodeDetails | undefined {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to move this function here from utils file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I moved the function, fetchLocalConfig, to prevent circular import, because ~
fetchLocalConfig needs import from /endpoints and getTSSEndpoints function in /endpoints also needs to import /utils to use validation function.

if (Object.values(TORUS_SAPPHIRE_NETWORK).includes(network as TORUS_SAPPHIRE_NETWORK_TYPE)) {
return getSapphireNodeDetails(network as TORUS_SAPPHIRE_NETWORK_TYPE, undefined, keyType, sigType);
}

if (Object.values(TORUS_LEGACY_NETWORK).includes(network as TORUS_LEGACY_NETWORK_TYPE)) {
const legacyMap = LEGACY_NETWORKS_ROUTE_MAP[network as TORUS_LEGACY_NETWORK_TYPE];
if (legacyMap.migrationCompleted) return getSapphireNodeDetails(legacyMap.networkMigratedTo, network as TORUS_LEGACY_NETWORK_TYPE, keyType);
}

return undefined;
}
56 changes: 35 additions & 21 deletions packages/fnd-base/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import {
INodeDetails,
LEGACY_NETWORKS_ROUTE_MAP,
TORUS_LEGACY_NETWORK,
TORUS_LEGACY_NETWORK_TYPE,
TORUS_NETWORK_TYPE,
TORUS_SAPPHIRE_NETWORK,
TORUS_SAPPHIRE_NETWORK_TYPE,
WEB3AUTH_KEY_TYPE,
WEB3AUTH_SIG_TYPE,
} from "@toruslabs/constants";
import { KEY_TYPE, SIG_TYPE, WEB3AUTH_KEY_TYPE, WEB3AUTH_SIG_TYPE } from "@toruslabs/constants";

import { getSapphireNodeDetails } from "./sapphireNetworkConfig";

export function fetchLocalConfig(network: TORUS_NETWORK_TYPE, keyType: WEB3AUTH_KEY_TYPE, sigType?: WEB3AUTH_SIG_TYPE): INodeDetails | undefined {
if (Object.values(TORUS_SAPPHIRE_NETWORK).includes(network as TORUS_SAPPHIRE_NETWORK_TYPE)) {
return getSapphireNodeDetails(network as TORUS_SAPPHIRE_NETWORK_TYPE, undefined, keyType, sigType);
export function validateKeyTypeAndSigTypeForTSS(keyType: WEB3AUTH_KEY_TYPE, sigType?: WEB3AUTH_SIG_TYPE) {
if (sigType === SIG_TYPE.ECDSA_SECP256K1) {
if (keyType !== KEY_TYPE.SECP256K1) {
throw new Error("Invalid key type for ecdsa-secp256k1");
}
} else if (sigType === SIG_TYPE.BIP340) {
if (keyType !== KEY_TYPE.SECP256K1) {
throw new Error("Invalid key type for bip340");
}
} else if (sigType === SIG_TYPE.ED25519) {
if (keyType !== KEY_TYPE.ED25519) {
throw new Error("Invalid key type for ed25519");
}
}
}

if (Object.values(TORUS_LEGACY_NETWORK).includes(network as TORUS_LEGACY_NETWORK_TYPE)) {
const legacyMap = LEGACY_NETWORKS_ROUTE_MAP[network as TORUS_LEGACY_NETWORK_TYPE];
if (legacyMap.migrationCompleted) return getSapphireNodeDetails(legacyMap.networkMigratedTo, network as TORUS_LEGACY_NETWORK_TYPE, keyType);
}
export function validateSigTypeAndGetTSSPath(keyType: WEB3AUTH_KEY_TYPE, sigType?: WEB3AUTH_SIG_TYPE): "tss" | "tss-frost" {
validateKeyTypeAndSigTypeForTSS(keyType, sigType);

return undefined;
let tssPath: "tss" | "tss-frost";
if (!sigType) {
// if sigType is not provided, we will determine the tssPath based on the keyType
if (keyType === KEY_TYPE.SECP256K1) {
tssPath = "tss";
} else if (keyType === KEY_TYPE.ED25519) {
tssPath = "tss-frost";
}
} else if (sigType === SIG_TYPE.ECDSA_SECP256K1) {
// we will use dkls for ECDSA Sigs
tssPath = "tss";
} else if (sigType === SIG_TYPE.ED25519 || sigType === SIG_TYPE.BIP340) {
// we will use frost for Ed25519 and BIP340 Sigs
tssPath = "tss-frost";
} else {
throw new Error(`Unsupported signature type: ${sigType} for key: ${keyType}`);
}
return tssPath;
}