diff --git a/src/helpers/common.ts b/src/helpers/common.ts index ae2b36e..5ad34ba 100644 --- a/src/helpers/common.ts +++ b/src/helpers/common.ts @@ -16,11 +16,16 @@ export const generatePrivateKey = (ecCurve: EC, buf: typeof Buffer): Buffer => { return ecCurve.genKeyPair().getPrivate().toArrayLike(buf); }; +let secp256k1EC: EC; +let ed25519EC: EC; + export const getKeyCurve = (keyType: KeyType) => { - if (keyType === KEY_TYPE.ED25519) { - return new EC(KEY_TYPE.ED25519); - } else if (keyType === KEY_TYPE.SECP256K1) { - return new EC(KEY_TYPE.SECP256K1); + if (keyType === KEY_TYPE.SECP256K1) { + if (!secp256k1EC) secp256k1EC = new EC("secp256k1"); + return secp256k1EC; + } else if (keyType === KEY_TYPE.ED25519) { + if (!ed25519EC) ed25519EC = new EC("ed25519"); + return ed25519EC; } throw new Error(`Invalid keyType: ${keyType}`); }; diff --git a/src/torus.ts b/src/torus.ts index 11e54f3..62095a5 100644 --- a/src/torus.ts +++ b/src/torus.ts @@ -17,6 +17,7 @@ import { generateAddressFromPubKey, generateShares, getEd25519ExtendedPublicKey, + getKeyCurve, getMetadata, getOrSetNonce, GetOrSetNonceError, @@ -171,10 +172,11 @@ class Torus { async getPublicAddress( endpoints: string[], torusNodePubs: INodePub[], - { verifier, verifierId, extendedVerifierId }: { verifier: string; verifierId: string; extendedVerifierId?: string } + { verifier, verifierId, extendedVerifierId, keyType }: { verifier: string; verifierId: string; extendedVerifierId?: string; keyType?: KeyType } ): Promise { log.info(torusNodePubs, { verifier, verifierId, extendedVerifierId }); - return this.getNewPublicAddress(endpoints, { verifier, verifierId, extendedVerifierId }, this.enableOneKey); + const localKeyType = keyType ?? this.keyType; + return this.getNewPublicAddress(endpoints, { verifier, verifierId, extendedVerifierId, keyType: localKeyType }, this.enableOneKey); } async importPrivateKey(params: ImportKeyParams): Promise { @@ -264,15 +266,22 @@ class Torus { private async getNewPublicAddress( endpoints: string[], - { verifier, verifierId, extendedVerifierId }: { verifier: string; verifierId: string; extendedVerifierId?: string }, + { verifier, verifierId, extendedVerifierId, keyType }: { verifier: string; verifierId: string; extendedVerifierId?: string; keyType?: KeyType }, enableOneKey: boolean ): Promise { + const localKeyType = keyType ?? this.keyType; + const localEc = getKeyCurve(localKeyType); + + if (localKeyType === KEY_TYPE.ED25519 && LEGACY_NETWORKS_ROUTE_MAP[this.network as TORUS_LEGACY_NETWORK_TYPE]) { + throw new Error(`keyType: ${keyType} is not supported by ${this.network} network`); + } + const keyAssignResult = await GetPubKeyOrKeyAssign({ endpoints, network: this.network, verifier, verifierId, - keyType: this.keyType, + keyType: localKeyType, extendedVerifierId, }); @@ -303,7 +312,7 @@ class Torus { let finalPubKey: curve.base.BasePoint; if (extendedVerifierId) { // for tss key no need to add pub nonce - finalPubKey = this.ec.keyFromPublic({ x: X, y: Y }).getPublic(); + finalPubKey = localEc.keyFromPublic({ x: X, y: Y }).getPublic(); oAuthPubKey = finalPubKey; } else if (LEGACY_NETWORKS_ROUTE_MAP[this.network as TORUS_LEGACY_NETWORK_TYPE]) { return this.formatLegacyPublicKeyData({ @@ -316,11 +325,11 @@ class Torus { }); } else { const v2NonceResult = nonceResult as v2NonceResultType; - oAuthPubKey = this.ec.keyFromPublic({ x: X, y: Y }).getPublic(); - finalPubKey = this.ec + oAuthPubKey = localEc.keyFromPublic({ x: X, y: Y }).getPublic(); + finalPubKey = localEc .keyFromPublic({ x: X, y: Y }) .getPublic() - .add(this.ec.keyFromPublic({ x: v2NonceResult.pubNonce.x, y: v2NonceResult.pubNonce.y }).getPublic()); + .add(localEc.keyFromPublic({ x: v2NonceResult.pubNonce.x, y: v2NonceResult.pubNonce.y }).getPublic()); pubNonce = { X: v2NonceResult.pubNonce.x, Y: v2NonceResult.pubNonce.y }; } @@ -330,14 +339,14 @@ class Torus { } const oAuthX = oAuthPubKey.getX().toString(16, 64); const oAuthY = oAuthPubKey.getY().toString(16, 64); - const oAuthAddress = generateAddressFromPubKey(this.keyType, oAuthPubKey.getX(), oAuthPubKey.getY()); + const oAuthAddress = generateAddressFromPubKey(localKeyType, oAuthPubKey.getX(), oAuthPubKey.getY()); if (!finalPubKey) { throw new Error("Unable to derive finalPubKey"); } const finalX = finalPubKey ? finalPubKey.getX().toString(16, 64) : ""; const finalY = finalPubKey ? finalPubKey.getY().toString(16, 64) : ""; - const finalAddress = finalPubKey ? generateAddressFromPubKey(this.keyType, finalPubKey.getX(), finalPubKey.getY()) : ""; + const finalAddress = finalPubKey ? generateAddressFromPubKey(localKeyType, finalPubKey.getX(), finalPubKey.getY()) : ""; return { oAuthKeyData: { walletAddress: oAuthAddress, @@ -367,8 +376,12 @@ class Torus { enableOneKey: boolean; isNewKey: boolean; serverTimeOffset: number; + keyType?: KeyType; }): Promise { - const { finalKeyResult, enableOneKey, isNewKey, serverTimeOffset } = params; + const { finalKeyResult, enableOneKey, isNewKey, serverTimeOffset, keyType } = params; + const localKeyType = keyType ?? this.keyType; + const localEc = getKeyCurve(localKeyType); + const { pub_key_X: X, pub_key_Y: Y } = finalKeyResult.keys[0]; let nonceResult: GetOrSetNonceResult; let nonce: BN; @@ -376,12 +389,12 @@ class Torus { let typeOfUser: GetOrSetNonceResult["typeOfUser"]; let pubNonce: { X: string; Y: string } | undefined; - const oAuthPubKey = this.ec.keyFromPublic({ x: X, y: Y }).getPublic(); + const oAuthPubKey = localEc.keyFromPublic({ x: X, y: Y }).getPublic(); const finalServerTimeOffset = this.serverTimeOffset || serverTimeOffset; if (enableOneKey) { try { - nonceResult = await getOrSetNonce(this.legacyMetadataHost, this.ec, finalServerTimeOffset, X, Y, undefined, !isNewKey); + nonceResult = await getOrSetNonce(this.legacyMetadataHost, localEc, finalServerTimeOffset, X, Y, undefined, !isNewKey); nonce = new BN(nonceResult.nonce || "0", 16); typeOfUser = nonceResult.typeOfUser; } catch { @@ -389,15 +402,15 @@ class Torus { } if (nonceResult.typeOfUser === "v1") { nonce = await getMetadata(this.legacyMetadataHost, { pub_key_X: X, pub_key_Y: Y }); - finalPubKey = this.ec + finalPubKey = localEc .keyFromPublic({ x: X, y: Y }) .getPublic() - .add(this.ec.keyFromPrivate(nonce.toString(16, 64), "hex").getPublic()); + .add(localEc.keyFromPrivate(nonce.toString(16, 64), "hex").getPublic()); } else if (nonceResult.typeOfUser === "v2") { - finalPubKey = this.ec + finalPubKey = localEc .keyFromPublic({ x: X, y: Y }) .getPublic() - .add(this.ec.keyFromPublic({ x: nonceResult.pubNonce.x, y: nonceResult.pubNonce.y }).getPublic()); + .add(localEc.keyFromPublic({ x: nonceResult.pubNonce.x, y: nonceResult.pubNonce.y }).getPublic()); pubNonce = { X: nonceResult.pubNonce.x, Y: nonceResult.pubNonce.y }; } else { throw new Error("getOrSetNonce should always return typeOfUser."); @@ -405,10 +418,10 @@ class Torus { } else { typeOfUser = "v1"; nonce = await getMetadata(this.legacyMetadataHost, { pub_key_X: X, pub_key_Y: Y }); - finalPubKey = this.ec + finalPubKey = localEc .keyFromPublic({ x: X, y: Y }) .getPublic() - .add(this.ec.keyFromPrivate(nonce.toString(16, 64), "hex").getPublic()); + .add(localEc.keyFromPrivate(nonce.toString(16, 64), "hex").getPublic()); } if (!oAuthPubKey) { @@ -416,14 +429,14 @@ class Torus { } const oAuthX = oAuthPubKey.getX().toString(16, 64); const oAuthY = oAuthPubKey.getY().toString(16, 64); - const oAuthAddress = generateAddressFromPubKey(this.keyType, oAuthPubKey.getX(), oAuthPubKey.getY()); + const oAuthAddress = generateAddressFromPubKey(localKeyType, oAuthPubKey.getX(), oAuthPubKey.getY()); if (typeOfUser === "v2" && !finalPubKey) { throw new Error("Unable to derive finalPubKey"); } const finalX = finalPubKey ? finalPubKey.getX().toString(16, 64) : ""; const finalY = finalPubKey ? finalPubKey.getY().toString(16, 64) : ""; - const finalAddress = finalPubKey ? generateAddressFromPubKey(this.keyType, finalPubKey.getX(), finalPubKey.getY()) : ""; + const finalAddress = finalPubKey ? generateAddressFromPubKey(localKeyType, finalPubKey.getX(), finalPubKey.getY()) : ""; return { oAuthKeyData: { walletAddress: oAuthAddress, diff --git a/test/sapphire_devnet.test.ts b/test/sapphire_devnet.test.ts index 8cc2e8f..003bab2 100644 --- a/test/sapphire_devnet.test.ts +++ b/test/sapphire_devnet.test.ts @@ -240,6 +240,37 @@ describe("torus utils sapphire devnet", function () { }); }); + it("should should fetch public address with keyType", async function () { + const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: "Willa_Funk11@gmail.com" }; + const nodeDetails = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails); + const torusNodeEndpoints = nodeDetails.torusNodeSSSEndpoints; + const result = await torus.getPublicAddress(torusNodeEndpoints, nodeDetails.torusNodePub, { ...verifierDetails, keyType: "ed25519" }); + expect(result.finalKeyData.walletAddress).eql("HHmiJMCAwhyf9ZWNtj7FEKGXeeC2NjUjPobpDKm43yKs"); + delete result.metadata.serverTimeOffset; + expect(result).eql({ + oAuthKeyData: { + walletAddress: "49yLu8yLqpuCXchzjQSt1tpBz8AP2E9EzzP7a8QtxmTE", + X: "5d39eba90fafbce150b33b9a60b41e1cfdf9e2640b55bf96b787173d74f8e415", + Y: "099639b7da35c1f31a44da7399a29d7db8eaa9639582cf7ed80aa4f7216adf2e", + }, + finalKeyData: { + walletAddress: "HHmiJMCAwhyf9ZWNtj7FEKGXeeC2NjUjPobpDKm43yKs", + X: "575203523b34bcfa2c25c428871c421afd69dbcb7375833b52ef264aaa466a81", + Y: "26f0b1f5740088c2ecf676081b8e2fe5254f1cbb693947ae391af13500d706f2", + }, + metadata: { + pubNonce: { + X: "71bf997547c1ac3f0babee87ebac055e8542863ebb1ba66e8092499eacbffd22", + Y: "71a0a70c5ae06d7eeb45673d4081fdfc9f29c4acfbbb57bf52a33dd7630599b1", + }, + nonce: new BN("0", "hex"), + typeOfUser: "v2", + upgraded: false, + }, + nodesData: result.nodesData, + }); + }); + it("should fetch public address of imported user", async function () { const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_IMPORT_EMAIL }; const nodeDetails = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails); diff --git a/test/sapphire_devnet_ed25519.test.ts b/test/sapphire_devnet_ed25519.test.ts index ad77998..52e8343 100644 --- a/test/sapphire_devnet_ed25519.test.ts +++ b/test/sapphire_devnet_ed25519.test.ts @@ -65,6 +65,37 @@ describe("torus utils ed25519 sapphire devnet", function () { }); }); + it("should should fetch public address with keyType", async function () { + const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: "Willa_Funk11@gmail.com" }; + const nodeDetails = await TORUS_NODE_MANAGER.getNodeDetails(verifierDetails); + const torusNodeEndpoints = nodeDetails.torusNodeSSSEndpoints; + const result = await torus.getPublicAddress(torusNodeEndpoints, nodeDetails.torusNodePub, { ...verifierDetails, keyType: "secp256k1" }); + expect(result.finalKeyData.walletAddress).eql("0xc53Df7C3Eb4990CfB8f903e4240dBB3BBa715A96"); + delete result.metadata.serverTimeOffset; + expect(result).eql({ + oAuthKeyData: { + walletAddress: "0x27890B4B87E5a39CA0510B32B2b2621d7D1eF7c0", + X: "d594a7c8368d37b2ca31b55be7db1b6a6bce9a3ddbcc573d5460bc7d630024e3", + Y: "09416f76bdbb88307900f748f0edc1cc345a9ba78c98508c8e29236d98b1d043", + }, + finalKeyData: { + walletAddress: "0xc53Df7C3Eb4990CfB8f903e4240dBB3BBa715A96", + X: "c60e9fbdb820c2ea430769fce86e2fd56ac4a4e5137346d54a914d57c56cab22", + Y: "02df3331a556d429baea94b0da05ec9438ea2ba9912af0fc4b76925531fc4629", + }, + metadata: { + pubNonce: { + X: "d3edb1a89af7db7a078e73cfdb59f9be82512e8121751934122f104b28b92074", + Y: "2a2700c2934c0a0b5cdfaeeca5a4e279fc9d46c6b6837de6f2e2f15ad39c51a3", + }, + nonce: new BN("0", "hex"), + typeOfUser: "v2", + upgraded: false, + }, + nodesData: result.nodesData, + }); + }); + it("should be able to import a key for a new user", async function () { const email = faker.internet.email(); const token = generateIdToken(email, "ES256");