Skip to content

Commit

Permalink
Merge pull request #176 from KeystoneHQ/feat/support_sign_message_hash
Browse files Browse the repository at this point in the history
Feat/support sign message hash
  • Loading branch information
soralit authored Nov 13, 2024
2 parents edb042b + b17045d commit b31f312
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 77 deletions.
126 changes: 64 additions & 62 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,64 +1,66 @@
{
"name": "keystone-airgaped-base",
"publishConfig": {
"access": "public"
},
"packageManager": "[email protected]",
"scripts": {
"clean": "./scripts/clean.sh",
"build": "pnpm -r build",
"bootstrap": "pnpm clean && pnpm install",
"lint": "eslint '*/**/*.ts' --quiet --fix",
"prettier:check": "pnpm run prettier --check",
"prettier:fix": "pnpm run prettier --write",
"prettier": "prettier '*/**/*.{js,jsx,ts,tsx}'",
"test": "pnpm -r test",
"prepack": "pnpm -r build"
},
"jest": {
"modulePathIgnorePatterns": [
"__data__"
],
"testEnvironment": "node"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
"name": "keystone-airgaped-base",
"publishConfig": {
"access": "public"
},
"packageManager": "[email protected]",
"scripts": {
"clean": "./scripts/clean.sh",
"build": "pnpm -r build",
"bootstrap": "pnpm clean && pnpm install",
"lint": "eslint '*/**/*.ts' --quiet --fix",
"prettier:check": "pnpm run prettier --check",
"prettier:fix": "pnpm run prettier --write",
"prettier": "prettier '*/**/*.{js,jsx,ts,tsx}'",
"test": "pnpm -r test",
"prepack": "pnpm -r build"
},
"jest": {
"modulePathIgnorePatterns": [
"__data__"
],
"testEnvironment": "node"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint "
],
"*.json": [
"prettier --write"
]
},
"pnpm": {
"neverBuiltDependencies": [
"node-hid"
]
},
"devDependencies": {
"@babel/cli": "^7.21.5",
"@babel/core": "^7.22.1",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@types/jest": "^27.4.1",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"git-commit-msg-linter": "^4.1.2",
"husky": "^7.0.4",
"jest": "^27.5.1",
"lint-staged": "^12.3.5",
"prettier": "^2.5.1",
"tsdx": "^0.14.1",
"typescript": "^4.6.2"
},
"dependencies": {
"uuid": "^8.3.2"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint "
],
"*.json": [
"prettier --write"
]
},
"pnpm": {
"neverBuiltDependencies": ["node-hid"]
},
"devDependencies": {
"@babel/cli": "^7.21.5",
"@babel/core": "^7.22.1",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@types/jest": "^27.4.1",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"git-commit-msg-linter": "^4.1.2",
"husky": "^7.0.4",
"jest": "^27.5.1",
"lint-staged": "^12.3.5",
"prettier": "^2.5.1",
"tsdx": "^0.14.1",
"typescript": "^4.6.2"
},
"dependencies": {
"uuid": "^8.3.2"
}
}
}
88 changes: 88 additions & 0 deletions packages/ur-registry-sui/__tests__/SuiSignHashRequest.test.ts

Large diffs are not rendered by default.

43 changes: 36 additions & 7 deletions packages/ur-registry-sui/__tests__/SuiSignature.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@ describe("sui-signature", () => {
"47e7b510784406dfa14d9fd13c3834128b49c56ddfc28edb02c5047219779adeed12017e2f9f116e83762e86f805c7311ea88fb403ff21900e069142b1fb310e",
"hex"
);
const requestId = Buffer.from(uuid.parse("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"));
const requestId = Buffer.from(
uuid.parse("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d")
);
const pubKey = Buffer.from(
"8e53e7b10656816de70824e3016fc1a277e77825e12825dc4f239f418ab2e04e",
"hex"
);

const suiSignature = new SuiSignature(
signature,
requestId,
pubKey
);
const suiSignature = new SuiSignature(signature, requestId, pubKey);

const cborHex = suiSignature.toCBOR().toString("hex");
const ur = suiSignature.toUREncoder(1000).nextPart();
Expand All @@ -29,9 +27,40 @@ describe("sui-signature", () => {
const suiSignatureDecoded = SuiSignature.fromCBOR(
Buffer.from(cborHex, "hex")
);
expect(uuid.stringify(suiSignatureDecoded.getRequestId())).toBe(uuid.stringify(requestId));
expect(uuid.stringify(suiSignatureDecoded.getRequestId())).toBe(
uuid.stringify(requestId)
);
expect(suiSignatureDecoded.getSignature().toString("hex")).toEqual(
"47e7b510784406dfa14d9fd13c3834128b49c56ddfc28edb02c5047219779adeed12017e2f9f116e83762e86f805c7311ea88fb403ff21900e069142b1fb310e"
);
});

it("should sign messageHash result is equal to the request", () => {
const signature = Buffer.from(
"2b2596eeb2bf21c0444ee4a85960447dbf5144eab6f855594fa836ed1e100bc6e32c04ad15e58d04ac669623f2125fe7154fe3caa7baaaf0243d64f88cd9e903",
"hex"
);
const requestId = Buffer.from("c267e8c4f363464e9f24c53aabf84fe0", "hex");
const requestIdHex = "c267e8c4f363464e9f24c53aabf84fe0";
const formattedUUID = [
requestIdHex.slice(0, 8),
requestIdHex.slice(8, 12),
requestIdHex.slice(12, 16),
requestIdHex.slice(16, 20),
requestIdHex.slice(20, 32),
].join("-");
const receiveRequestId = Buffer.from(uuid.parse(formattedUUID));
expect(requestId.toString("hex")).toBe(receiveRequestId.toString("hex"));
const pubKey = Buffer.from(
"edbe1b9b3b040ff88fbfa4ccda6f5f8d404ae7ffe35f9b220dec08679d5c336f",
"hex"
);

const suiSignature = new SuiSignature(signature, requestId, pubKey);

const ur = suiSignature.toUREncoder(1000).nextPart().toLocaleUpperCase();
expect(ur).toBe(
"UR:SUI-SIGNATURE/OTADTPDAGDSAIOVSSSWFIAFGGLNEDKSKFTPYYAGWVTAOHDFZDNDAMTWYPRRSCLRTFYGLVEPDHKHNFYKIRSGYFYWDRPYAGOHKGWPDENWECKBEBDSWVLDWAAPMBZVWLGAAPSIYMTCNWZBGHEVDBZGWVLSGOSRDPKWTDKFSIEYALKTAWLAXAXHDCXWERNCWNDFRAABSYAMYRSOXSFTNJLHELGFZGEVDZMVLHENDCPBTWPAYIONTHHEOJLDSLULEFW"
);
});
});
3 changes: 2 additions & 1 deletion packages/ur-registry-sui/src/RegistryType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import { RegistryType } from "@keystonehq/bc-ur-registry";

export const ExtendedRegistryTypes = {
SUI_SIGN_REQUEST: new RegistryType("sui-sign-request", 7101),
SUI_SIGNATURE: new RegistryType("sui-signature", 7102)
SUI_SIGNATURE: new RegistryType("sui-signature", 7102),
SUI_SIGN_HASH_REQUEST: new RegistryType("sui-sign-hash-request", 7103),
};
133 changes: 133 additions & 0 deletions packages/ur-registry-sui/src/SuiSignHashRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import {
CryptoKeypath,
extend,
DataItem,
PathComponent,
RegistryItem,
DataItemMap,
} from "@keystonehq/bc-ur-registry";
import { ExtendedRegistryTypes } from "./RegistryType";
import * as uuid from "uuid";

const { decodeToDataItem, RegistryTypes } = extend;

enum Keys {
requestId = 1,
messageHash,
derivationPaths,
addresses,
origin,
}

type signHashRequestProps = {
requestId?: Buffer;
messageHash: string;
derivationPaths: CryptoKeypath[];
addresses?: Buffer[];
origin?: string;
};

export class SuiSignHashRequest extends RegistryItem {
private requestId?: Buffer;
private messageHash: string;
private derivationPaths: CryptoKeypath[];
private addresses?: Buffer[];
private origin?: string;

getRegistryType = () => ExtendedRegistryTypes.SUI_SIGN_HASH_REQUEST;

constructor(args: signHashRequestProps) {
super();
this.requestId = args.requestId;
this.messageHash = args.messageHash;
this.derivationPaths = args.derivationPaths;
this.addresses = args.addresses;
this.origin = args.origin;
}

public getRequestId = () => this.requestId;
public getMessageHash = () => this.messageHash;
public getDerivationPaths = () =>
this.derivationPaths.map((key) => key.getPath());
public getAddresses = () => this.addresses;
public getOrigin = () => this.origin;

public toDataItem = () => {
const map: DataItemMap = {};
if (this.requestId) {
map[Keys.requestId] = new DataItem(
this.requestId,
RegistryTypes.UUID.getTag()
);
}
if (this.addresses) {
map[Keys.addresses] = this.addresses;
}
if (this.origin) {
map[Keys.origin] = this.origin;
}

map[Keys.messageHash] = this.messageHash;
map[Keys.derivationPaths] = this.derivationPaths.map((item) => {
const dataItem = item.toDataItem();
dataItem.setTag(item.getRegistryType().getTag());
return dataItem;
});
return new DataItem(map);
};

public static fromDataItem = (dataItem: DataItem) => {
const map = dataItem.getData();
const messageHash = map[Keys.messageHash];
const derivationPaths = map[Keys.derivationPaths].map((item: DataItem) =>
CryptoKeypath.fromDataItem(item)
);
const addresses = map[Keys.addresses] ? map[Keys.addresses] : undefined;
const requestId = map[Keys.requestId]
? map[Keys.requestId].getData()
: undefined;
const origin = map[Keys.origin] ? map[Keys.origin] : undefined;
return new SuiSignHashRequest({
requestId,
messageHash,
derivationPaths,
addresses,
origin,
});
};

public static fromCBOR = (_cborPayload: Buffer) => {
const dataItem = decodeToDataItem(_cborPayload);
return SuiSignHashRequest.fromDataItem(dataItem);
};

public static constructSuiRequest(
messageHash: string,
publicKeyHdPath: string[],
xfps: string[],
uuidString: string,
addresses?: Buffer[],
origin?: string
) {
const publicKeyHdPathObjects = publicKeyHdPath.map((path, index) => {
const paths = path.replace(/[m|M]\//, "").split("/");
const pathComponent = paths.map((path) => {
const index = parseInt(path.replace("'", ""));
let isHardened = false;
if (path.endsWith("'")) {
isHardened = true;
}
return new PathComponent({ index, hardened: isHardened });
});
return new CryptoKeypath(pathComponent, Buffer.from(xfps[index], "hex"));
});

return new SuiSignHashRequest({
requestId: Buffer.from(uuid.parse(uuidString)),
messageHash,
derivationPaths: publicKeyHdPathObjects,
addresses: addresses || undefined,
origin: origin || undefined,
});
}
}
10 changes: 3 additions & 7 deletions packages/ur-registry-sui/src/SuiSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
extend,
DataItem,
RegistryItem,
DataItemMap
DataItemMap,
} from "@keystonehq/bc-ur-registry";
import { ExtendedRegistryTypes } from "./RegistryType";

Expand All @@ -11,7 +11,7 @@ const { RegistryTypes, decodeToDataItem } = extend;
enum Keys {
requestId = 1,
signature,
publicKey
publicKey,
}

export class SuiSignature extends RegistryItem {
Expand All @@ -21,11 +21,7 @@ export class SuiSignature extends RegistryItem {

getRegistryType = () => ExtendedRegistryTypes.SUI_SIGNATURE;

constructor(
signature: Buffer,
requestId?: Buffer,
publicKey?: Buffer
) {
constructor(signature: Buffer, requestId?: Buffer, publicKey?: Buffer) {
super();
this.signature = signature;
this.requestId = requestId;
Expand Down
1 change: 1 addition & 0 deletions packages/ur-registry-sui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ patchTags(

export { SuiSignRequest } from "./SuiSignRequest";
export { SuiSignature } from "./SuiSignature";
export { SuiSignHashRequest } from "./SuiSignHashRequest";

0 comments on commit b31f312

Please sign in to comment.