From 0dcccfcb7bdace8d34e5dcdd0d6be82c87e6b3db Mon Sep 17 00:00:00 2001 From: Pierre Aoun Date: Wed, 6 Nov 2024 10:38:29 +0100 Subject: [PATCH] :sparkles: (signer-eth): Allow signing a message as a byte array --- .changeset/lemon-impalas-flash.md | 5 ++++ .../signer/signer-eth/src/api/SignerEth.ts | 2 +- .../SignPersonalMessageDeviceActionTypes.ts | 2 +- .../src/internal/DefaultSignerEth.ts | 2 +- .../src/internal/app-binder/EthAppBinder.ts | 2 +- .../SignPersonalMessageDeviceAction.ts | 4 +-- .../task/SendSignPersonalMessageTask.test.ts | 27 +++++++++++++++++++ .../task/SendSignPersonalMessageTask.ts | 8 ++++-- .../message/use-case/SignMessageUseCase.ts | 2 +- 9 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 .changeset/lemon-impalas-flash.md diff --git a/.changeset/lemon-impalas-flash.md b/.changeset/lemon-impalas-flash.md new file mode 100644 index 000000000..01d169da6 --- /dev/null +++ b/.changeset/lemon-impalas-flash.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/device-signer-kit-ethereum": patch +--- + +Allow signing a message as a byte array diff --git a/packages/signer/signer-eth/src/api/SignerEth.ts b/packages/signer/signer-eth/src/api/SignerEth.ts index 39a85ae92..074bd2b04 100644 --- a/packages/signer/signer-eth/src/api/SignerEth.ts +++ b/packages/signer/signer-eth/src/api/SignerEth.ts @@ -16,7 +16,7 @@ export interface SignerEth { ) => SignTransactionDAReturnType; signMessage: ( derivationPath: string, - message: string, + message: string | Uint8Array, ) => SignPersonalMessageDAReturnType; signTypedData: ( derivationPath: string, diff --git a/packages/signer/signer-eth/src/api/app-binder/SignPersonalMessageDeviceActionTypes.ts b/packages/signer/signer-eth/src/api/app-binder/SignPersonalMessageDeviceActionTypes.ts index a9ed9efc0..43aa3b267 100644 --- a/packages/signer/signer-eth/src/api/app-binder/SignPersonalMessageDeviceActionTypes.ts +++ b/packages/signer/signer-eth/src/api/app-binder/SignPersonalMessageDeviceActionTypes.ts @@ -13,7 +13,7 @@ export type SignPersonalMessageDAOutput = Signature; export type SignPersonalMessageDAInput = { readonly derivationPath: string; - readonly message: string; + readonly message: string | Uint8Array; }; export type SignPersonalMessageDAError = diff --git a/packages/signer/signer-eth/src/internal/DefaultSignerEth.ts b/packages/signer/signer-eth/src/internal/DefaultSignerEth.ts index 8ee4d1400..a6d985c25 100644 --- a/packages/signer/signer-eth/src/internal/DefaultSignerEth.ts +++ b/packages/signer/signer-eth/src/internal/DefaultSignerEth.ts @@ -49,7 +49,7 @@ export class DefaultSignerEth implements SignerEth { signMessage( _derivationPath: string, - _message: string, + _message: string | Uint8Array, ): SignPersonalMessageDAReturnType { return this._container .get(messageTypes.SignMessageUseCase) diff --git a/packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.ts b/packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.ts index ac0cd9fda..a414af45f 100644 --- a/packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.ts +++ b/packages/signer/signer-eth/src/internal/app-binder/EthAppBinder.ts @@ -55,7 +55,7 @@ export class EthAppBinder { signPersonalMessage(args: { derivationPath: string; - message: string; + message: string | Uint8Array; }): SignPersonalMessageDAReturnType { return this.dmk.executeDeviceAction({ sessionId: this.sessionId, diff --git a/packages/signer/signer-eth/src/internal/app-binder/device-action/SignPersonalMessage/SignPersonalMessageDeviceAction.ts b/packages/signer/signer-eth/src/internal/app-binder/device-action/SignPersonalMessage/SignPersonalMessageDeviceAction.ts index b819ee360..3f0e0e2b7 100644 --- a/packages/signer/signer-eth/src/internal/app-binder/device-action/SignPersonalMessage/SignPersonalMessageDeviceAction.ts +++ b/packages/signer/signer-eth/src/internal/app-binder/device-action/SignPersonalMessage/SignPersonalMessageDeviceAction.ts @@ -26,7 +26,7 @@ export type MachineDependencies = { readonly signPersonalMessage: (arg0: { input: { derivationPath: string; - message: string; + message: string | Uint8Array; }; }) => Promise>; }; @@ -215,7 +215,7 @@ export class SignPersonalMessageDeviceAction extends XStateDeviceAction< const signPersonalMessage = async (arg0: { input: { derivationPath: string; - message: string; + message: string | Uint8Array; }; }) => new SendSignPersonalMessageTask(internalApi, arg0.input).run(); diff --git a/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.test.ts b/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.test.ts index 8d9e7b343..ef0bd4428 100644 --- a/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.test.ts +++ b/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.test.ts @@ -8,6 +8,9 @@ import { makeDeviceActionInternalApiMock } from "@internal/app-binder/device-act import { SendSignPersonalMessageTask } from "./SendSignPersonalMessageTask"; const SEND_MESSAGE_HELLO_WORLD = "Hello, World!"; +const SEND_MESSAGE_HELLO_WORLD_BYTES = new Uint8Array([ + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, +]); const SEND_MESSAGE_HELLO_WORLD_DATA = new Uint8Array([ 0x05, 0x80, 0x00, 0x00, 0x2c, 0x80, 0x00, 0x00, 0x3c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x48, @@ -97,6 +100,30 @@ describe("SendSignPersonalMessageTask", () => { expect((result as any).data).toStrictEqual(signature); }); + it("should send the message as byte arrays", async () => { + // GIVEN + const args = { + derivationPath: "44'/60'/0'/0/0", + message: SEND_MESSAGE_HELLO_WORLD_BYTES, + }; + apiMock.sendCommand.mockResolvedValueOnce(resultOk); + apiMock.sendCommand.mockResolvedValueOnce(resultNothing); + + // WHEN + const result = await new SendSignPersonalMessageTask(apiMock, args).run(); + + // THEN + expect(apiMock.sendCommand.mock.calls).toHaveLength(1); + expect(apiMock.sendCommand.mock.calls[0]![0]).toStrictEqual( + new SignPersonalMessageCommand({ + data: new Uint8Array(SEND_MESSAGE_HELLO_WORLD_DATA), + isFirstChunk: true, + }), + ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect((result as any).data).toStrictEqual(signature); + }); + it("should send the long message in chunks", async () => { // GIVEN const args = { diff --git a/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.ts b/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.ts index 2918715ae..fe7de28ae 100644 --- a/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.ts +++ b/packages/signer/signer-eth/src/internal/app-binder/task/SendSignPersonalMessageTask.ts @@ -20,7 +20,7 @@ const PATH_SIZE = 4; type SendSignPersonalMessageTaskArgs = { derivationPath: string; - message: string; + message: string | Uint8Array; }; export class SendSignPersonalMessageTask { @@ -45,7 +45,11 @@ export class SendSignPersonalMessageTask { // add message length builder.add32BitUIntToData(message.length); // add the message - builder.addAsciiStringToData(message); + if (typeof message === "string") { + builder.addAsciiStringToData(message); + } else { + builder.addBufferToData(message); + } const buffer = builder.build(); diff --git a/packages/signer/signer-eth/src/internal/message/use-case/SignMessageUseCase.ts b/packages/signer/signer-eth/src/internal/message/use-case/SignMessageUseCase.ts index 2b0bca96a..f7acd775c 100644 --- a/packages/signer/signer-eth/src/internal/message/use-case/SignMessageUseCase.ts +++ b/packages/signer/signer-eth/src/internal/message/use-case/SignMessageUseCase.ts @@ -17,7 +17,7 @@ export class SignMessageUseCase { execute( derivationPath: string, - message: string, + message: string | Uint8Array, ): SignPersonalMessageDAReturnType { // 1- Sign the transaction using the app binding return this._appBinding.signPersonalMessage({