From b7c5c41192abd0052044b496740cefce169b8ca0 Mon Sep 17 00:00:00 2001 From: ZhenQian Date: Thu, 7 Nov 2024 18:32:13 +0800 Subject: [PATCH] feat: support sui sign message hash --- src/__tests__/sui.test.ts | 22 ++++++++++++++++ src/chains/sui.ts | 53 ++++++++++++++++++++++++++++++++++++--- src/types/props.ts | 7 ++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/__tests__/sui.test.ts b/src/__tests__/sui.test.ts index 60587ba..fd058da 100644 --- a/src/__tests__/sui.test.ts +++ b/src/__tests__/sui.test.ts @@ -42,3 +42,25 @@ test('generateSignRequest', () => { expect(keystoneSDK.sui.generateSignRequest({ requestId, intentMessage, accounts, origin })).toStrictEqual(expectResult) }) + + + +test('generateSignHashRequest', () => { + const keystoneSDK = new KeystoneSDK() + const requestId = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' + const messageHash = 'ce035bd8ab6499dcaa01d623aa7c977ec9be13798046ca1f86c9f3ebcd2f4d13' + const messageBuffer = toBuffer(messageHash) + expect(messageBuffer.length === 32).toBe(true) + const accounts = [ + { + path: "m/44'/784'/0'/0'/0'", + xfp: '52744703', + address: '504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc' + } + ] + const origin = 'Suiet' + const cborHex = "a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d027840636530333562643861623634393964636161303164363233616137633937376563396265313337393830343663613166383663396633656263643266346431330381d90130a2018a182cf5190310f500f500f500f5021a5274470304815820504886c9ec43bff70af37f55865094cc3a799cb54479f252d30cd3717f15ecdc05655375696574"; + let expectResult = new UR(toBuffer(cborHex), 'sui-sign-hash-request') + let result = keystoneSDK.sui.generateSignHashRequest({ requestId, messageHash, accounts, origin }) + expect(result).toStrictEqual(expectResult) +}) diff --git a/src/chains/sui.ts b/src/chains/sui.ts index d764483..a3c5431 100644 --- a/src/chains/sui.ts +++ b/src/chains/sui.ts @@ -1,9 +1,9 @@ -import { CryptoKeypath, PathComponent, SuiSignRequest, SuiSignature } from '@keystonehq/bc-ur-registry-sui' +import { CryptoKeypath, PathComponent, SuiSignHashRequest, SuiSignRequest, SuiSignature } from '@keystonehq/bc-ur-registry-sui' import { type SuiSignature as SuiSignatureType } from '../types/signature' import { parsePath, toBuffer, toHex, uuidParse, uuidStringify } from '../utils' import { URType, type UR } from '../types/ur' -import { type SuiSignRequestProps } from '../types/props' - +import { SuiSignHashRequestProps, type SuiSignRequestProps } from '../types/props' +import { blake2b } from '@noble/hashes/blake2b'; export class KeystoneSuiSDK { parseSignature (ur: UR): SuiSignatureType { if (ur.type !== URType.SuiSignature) { @@ -18,6 +18,35 @@ export class KeystoneSuiSDK { } } + + + generateSignHashRequest ({ + requestId, + messageHash, + accounts, + origin + }: SuiSignHashRequestProps): UR { + const derivationPaths: CryptoKeypath[] = [] + const addresses: Buffer[] = [] + accounts.forEach(account => { + derivationPaths.push( + new CryptoKeypath(parsePath(account.path).map(e => new PathComponent(e)), toBuffer(account.xfp)) + ) + account.address !== undefined && addresses.push(toBuffer(account.address.startsWith('0x') ? account.address.substring(2) : account.address)) + }) + if (addresses.length > 0 && addresses.length !== derivationPaths.length) { + throw new Error('address and path count must match') + } + return new SuiSignHashRequest({ + requestId: uuidParse(requestId), + messageHash, + derivationPaths, + addresses, + origin + }).toUR() + } + + generateSignRequest ({ requestId, intentMessage, @@ -35,6 +64,24 @@ export class KeystoneSuiSDK { if (addresses.length > 0 && addresses.length !== derivationPaths.length) { throw new Error('address and path count must match') } + // check intentMessage size + let intentMessageBuffer = toBuffer(intentMessage) + if (intentMessageBuffer.length > 1024) { + // hash intentMessage to 32 bytes + const hash = blake2b(Uint8Array.from(intentMessageBuffer), { dkLen: 32 }); + // assert hash length is 32 bytes + if (hash.length !== 32) { + throw new Error('hash length must be 32') + } + intentMessage = toHex(hash) + return new SuiSignHashRequest({ + requestId: uuidParse(requestId), + messageHash: intentMessage, + derivationPaths, + addresses, + origin + }).toUR() + } return new SuiSignRequest({ requestId: uuidParse(requestId), intentMessage: toBuffer(intentMessage), diff --git a/src/types/props.ts b/src/types/props.ts index 0cc53ee..9c139bd 100644 --- a/src/types/props.ts +++ b/src/types/props.ts @@ -193,6 +193,13 @@ export interface SuiSignRequestProps { origin?: string } +export interface SuiSignHashRequestProps { + requestId: string + messageHash: string + accounts: SuiAccount[] + origin?: string +} + export interface BtcSignRequestProps { requestId: string signData: string