Skip to content
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

Feat/get public address keytype #170

Merged
merged 7 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 5 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
11 changes: 7 additions & 4 deletions src/helpers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ export const generatePrivateKey = (ecCurve: EC, buf: typeof Buffer): Buffer => {
return ecCurve.genKeyPair().getPrivate().toArrayLike(buf);
};

const secp256k1EC = new EC("secp256k1");
const ed25519EC = new EC("ed25519");
Copy link
Member

Choose a reason for hiding this comment

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

this was intentionally not left as a global variable to reduce the build size

Copy link
Contributor Author

Choose a reason for hiding this comment

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

would create EC object every time increase memory usage and latency?


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) {
return secp256k1EC;
} else if (keyType === KEY_TYPE.ED25519) {
return ed25519EC;
}
throw new Error(`Invalid keyType: ${keyType}`);
};
Expand Down
50 changes: 29 additions & 21 deletions src/torus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
generateAddressFromPubKey,
generateShares,
getEd25519ExtendedPublicKey,
getKeyCurve,
getMetadata,
getOrSetNonce,
GetOrSetNonceError,
Expand Down Expand Up @@ -171,10 +172,10 @@ 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<TorusPublicKey> {
log.info(torusNodePubs, { verifier, verifierId, extendedVerifierId });
return this.getNewPublicAddress(endpoints, { verifier, verifierId, extendedVerifierId }, this.enableOneKey);
return this.getNewPublicAddress(endpoints, { verifier, verifierId, extendedVerifierId, keyType }, this.enableOneKey);
Copy link
Member

Choose a reason for hiding this comment

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

can we pass keyType here as const localKeyType = keyType ?? this.keyType for the sake of consistency with other public functions

}

async importPrivateKey(params: ImportKeyParams): Promise<TorusKey> {
Expand Down Expand Up @@ -264,15 +265,18 @@ 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<TorusPublicKey> {
const localKeyType = keyType ?? this.keyType;
const localEc = getKeyCurve(localKeyType);

const keyAssignResult = await GetPubKeyOrKeyAssign({
endpoints,
network: this.network,
verifier,
verifierId,
keyType: this.keyType,
keyType: localKeyType,
extendedVerifierId,
});

Expand Down Expand Up @@ -303,7 +307,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({
Expand All @@ -316,11 +320,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 };
}
Expand All @@ -330,14 +334,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,
Expand Down Expand Up @@ -367,63 +371,67 @@ class Torus {
enableOneKey: boolean;
isNewKey: boolean;
serverTimeOffset: number;
keyType?: KeyType;
}): Promise<TorusPublicKey> {
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;
let finalPubKey: curve.base.BasePoint;
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 {
throw new GetOrSetNonceError();
}
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.");
}
} 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) {
throw new Error("Unable to derive oAuthPubKey");
}
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,
Expand Down
31 changes: 31 additions & 0 deletions test/sapphire_devnet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,37 @@ describe("torus utils sapphire devnet", function () {
});
});

it("should should fetch public address with keyType", async function () {
Copy link
Member

Choose a reason for hiding this comment

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

can you use existing test case and user for testcase to validate nothing changes on passing keyType for existing test user

Copy link
Contributor Author

Choose a reason for hiding this comment

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

[email protected] is existing testcase's email in the sapphire_devnet_ed25519.test.ts

const verifierDetails = { verifier: TORUS_TEST_VERIFIER, verifierId: "[email protected]" };
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);
Expand Down
31 changes: 31 additions & 0 deletions test/sapphire_devnet_ed25519.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: "[email protected]" };
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");
Expand Down
Loading