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

ed25519 keys support and import keys new flow #126

Merged
merged 26 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": true
"source.fixAll": "explicit"
},
"cSpell.words": ["Mutex", "Mutexes", "toruslabs"]
}
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const JRPC_METHODS = {
GET_OR_SET_KEY: "GetPubKeyOrKeyAssign",
COMMITMENT_REQUEST: "CommitmentRequest",
IMPORT_SHARE: "ImportShare",
IMPORT_SHARES: "ImportShares",
GET_SHARE_OR_KEY_ASSIGN: "GetShareOrKeyAssign",
};
15 changes: 8 additions & 7 deletions src/helpers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ export const normalizeKeysResult = (result: VerifierLookupResponse) => {
is_new_key: result.is_new_key,
};
if (result && result.keys && result.keys.length > 0) {
finalResult.keys = result.keys.map((key) => {
return {
pub_key_X: key.pub_key_X,
pub_key_Y: key.pub_key_Y,
address: key.address,
};
});
const finalKey = result.keys[0];
Copy link
Member

Choose a reason for hiding this comment

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

why are we using only the first key?

Copy link
Member Author

Choose a reason for hiding this comment

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

So there are cases where it is possible that a user can have multiple keys but all the nodes might not have all those shares/pub keys for ex i have seen these cases when a node is temp down but key was assigned successfully by other threshold number of nodes.

and as a result we should only check threshold for first key rather than checking older keys which are not even being used on frontend and can have mismatch in threshold when some nodes have more keys assigned for a user than others.

finalResult.keys = [
{
pub_key_X: finalKey.pub_key_X,
pub_key_Y: finalKey.pub_key_Y,
address: finalKey.address,
},
];
}
return finalResult;
};
Expand Down
15 changes: 11 additions & 4 deletions src/helpers/keyUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BN from "bn.js";
import { ec } from "elliptic";
import { curve, ec as EC } from "elliptic";
import { keccak256 as keccakHash } from "ethereum-cryptography/keccak";

import log from "../loglevel";
Expand Down Expand Up @@ -31,24 +31,31 @@ export function toChecksumAddress(hexAddress: string): string {
return ret;
}

export function generateAddressFromPrivKey(ecCurve: ec, privateKey: BN): string {
export function generateAddressFromPrivKey(ecCurve: EC, privateKey: BN): string {
const key = ecCurve.keyFromPrivate(privateKey.toString("hex", 64), "hex");
const publicKey = key.getPublic().encode("hex", false).slice(2);
log.info(publicKey, "public key");
const evmAddressLower = `0x${keccak256(Buffer.from(publicKey, "hex")).slice(64 - 38)}`;
return toChecksumAddress(evmAddressLower);
}

export function generateAddressFromPubKey(ecCurve: ec, publicKeyX: BN, publicKeyY: BN): string {
export function generateAddressFromPubKey(ecCurve: EC, publicKeyX: BN, publicKeyY: BN): string {
const key = ecCurve.keyFromPublic({ x: publicKeyX.toString("hex", 64), y: publicKeyY.toString("hex", 64) });
const publicKey = key.getPublic().encode("hex", false).slice(2);
log.info(key.getPublic().encode("hex", false), "public key");
const evmAddressLower = `0x${keccak256(Buffer.from(publicKey, "hex")).slice(64 - 38)}`;
return toChecksumAddress(evmAddressLower);
}

export function getPostboxKeyFrom1OutOf1(ecCurve: ec, privKey: string, nonce: string): string {
export function getPostboxKeyFrom1OutOf1(ecCurve: EC, privKey: string, nonce: string): string {
const privKeyBN = new BN(privKey, 16);
const nonceBN = new BN(nonce, 16);
return privKeyBN.sub(nonceBN).umod(ecCurve.curve.n).toString("hex");
Copy link
Member

Choose a reason for hiding this comment

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

some users / libraries may expect the hex to be 32 bytes long (64 characters). ideally should use a global function to encode to bytes / hex, so that we don't have to do and check this manually every time. (even better might be to create a package that can be used across libraries.)

Copy link
Member Author

Choose a reason for hiding this comment

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

will address these suggestions to make test cases modular in other PRs, thanks for suggesting these improvement

}

export function derivePubKey(ecCurve: EC, sk: BN): curve.base.BasePoint {
const skHex = sk.toString(16, 64);
return ecCurve.keyFromPrivate(skHex).getPublic();
}

export const encryptionEC = new EC("secp256k1");
2 changes: 1 addition & 1 deletion src/helpers/langrangeInterpolatePoly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export function generateRandomPolynomial(ecCurve: EC, degree: number, secret?: B
while (points[shareIndex.toString("hex", 64)] !== undefined) {
shareIndex = generatePrivateExcludingIndexes([new BN(0)]);
}
points[shareIndex.toString("hex", 64)] = new Point(shareIndex, new BN(generatePrivate()), ecCurve);
points[shareIndex.toString("hex", 64)] = new Point(shareIndex, new BN(generatePrivate()).umod(ecCurve.curve.n), ecCurve);
}
points["0"] = new Point(new BN(0), actualS, ecCurve);
return lagrangeInterpolatePolynomial(ecCurve, Object.values(points));
Expand Down
11 changes: 7 additions & 4 deletions src/helpers/metadataUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ec } from "elliptic";
import stringify from "json-stable-stringify";
import log from "loglevel";

import { EciesHex, GetOrSetNonceResult, MetadataParams } from "../interfaces";
import { EciesHex, GetOrSetNonceResult, KeyType, MetadataParams } from "../interfaces";
import { encParamsHexToBuf } from "./common";
import { keccak256 } from "./keyUtils";

Expand All @@ -25,7 +25,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, keyType: KeyType): MetadataParams {
const key = ecCurve.keyFromPrivate(privateKey.toString("hex", 64));
const setData = {
data: message,
Expand All @@ -36,6 +36,7 @@ export function generateMetadataParams(ecCurve: ec, serverTimeOffset: number, me
pub_key_X: key.getPublic().getX().toString("hex"), // DO NOT PAD THIS. BACKEND DOESN'T
pub_key_Y: key.getPublic().getY().toString("hex"), // DO NOT PAD THIS. BACKEND DOESN'T
set_data: setData,
key_type: keyType,
signature: Buffer.from(sig.r.toString(16, 64) + sig.s.toString(16, 64) + new BN("").toString(16, 2), "hex").toString("base64"),
};
}
Expand All @@ -60,6 +61,7 @@ export async function getMetadata(
export async function getOrSetNonce(
legacyMetadataHost: string,
ecCurve: ec,
keyType: KeyType,
serverTimeOffset: number,
X: string,
Y: string,
Expand All @@ -69,7 +71,7 @@ export async function getOrSetNonce(
let data: Data;
const msg = getOnly ? "getNonce" : "getOrSetNonce";
if (privKey) {
data = generateMetadataParams(ecCurve, serverTimeOffset, msg, privKey);
data = generateMetadataParams(ecCurve, serverTimeOffset, msg, privKey, keyType);
} else {
data = {
pub_key_X: X,
Expand All @@ -83,10 +85,11 @@ export async function getOrSetNonce(
export async function getNonce(
legacyMetadataHost: string,
ecCurve: ec,
keyType: KeyType,
serverTimeOffset: number,
X: string,
Y: string,
privKey?: BN
): Promise<GetOrSetNonceResult> {
return getOrSetNonce(legacyMetadataHost, ecCurve, serverTimeOffset, X, Y, privKey, true);
return getOrSetNonce(legacyMetadataHost, ecCurve, keyType, serverTimeOffset, X, Y, privKey, true);
}
Loading