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(hw-app-trx): support #signTIP712HashedMessage() for Tron #6779

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silly-dolphins-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/hw-app-trx": patch
---

Support #signTIP712HashedMessage() for Tron
30 changes: 28 additions & 2 deletions libs/ledgerjs/packages/hw-app-trx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ For a smooth and quick integration:
* [signPersonalMessage](#signpersonalmessage)
* [Parameters](#parameters-4)
* [Examples](#examples-5)
* [getECDHPairKey](#getecdhpairkey)
* [signTIP712HashedMessage](#signtip712hashedmessage)
* [Parameters](#parameters-5)
* [Examples](#examples-6)
* [Examples](#examples-6)
* [getECDHPairKey](#getecdhpairkey)
* [Parameters](#parameters-6)
* [Examples](#examples-7)

### Trx

Expand Down Expand Up @@ -155,6 +158,29 @@ const signature = await tron.signPersonalMessage("44'/195'/0'/0/0", "43727970746

Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** a signature as hex string

#### signTIP712HashedMessage

Sign a typed data. The host computes the domain separator and hashStruct(message)

##### Parameters

- `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `domainSeparatorHex` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `hashStructMessageHex` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**

##### Examples

```javascript
const signature = await tron
.signTIP712HashedMessage(
"44'/195'/0'/0/0",
Buffer.from("0101010101010101010101010101010101010101010101010101010101010101").toString("hex"),
Buffer.from("0202020202020202020202020202020202020202020202020202020202020202").toString("hex"),
);
```

Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** a signature as hex string

#### getECDHPairKey

get Tron address for a given BIP 32 path.
Expand Down
31 changes: 31 additions & 0 deletions libs/ledgerjs/packages/hw-app-trx/src/TIP712/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Transport from "@ledgerhq/hw-transport";
import { hexBuffer, splitPath } from "../utils";

const CLA = 0xe0;

export const signTIP712HashedMessage = (
transport: Transport,
path: string,
domainSeparatorHex: string,
hashStructMessageHex: string,
): Promise<string> => {
const domainSeparator = hexBuffer(domainSeparatorHex);
const hashStruct = hexBuffer(hashStructMessageHex);
const paths = splitPath(path);
const buffer = Buffer.alloc(1 + paths.length * 4 + 32 + 32, 0);

let offset = 0;
buffer[0] = paths.length;
paths.forEach((element, index) => {
buffer.writeUint32BE(element, 1 + 4 * index);
});

offset = 1 + 4 * paths.length;
domainSeparator.copy(buffer, offset);
offset += 32;
hashStruct.copy(buffer, offset);

return transport.send(CLA, 0x0c, 0x00, 0x00, buffer).then(response => {
return response.slice(0, 65).toString("hex");
});
};
11 changes: 11 additions & 0 deletions libs/ledgerjs/packages/hw-app-trx/src/Trx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// FIXME drop:
import { splitPath, foreach, decodeVarint } from "./utils";
import type Transport from "@ledgerhq/hw-transport";
import { signTIP712HashedMessage } from "./TIP712";

const remapTransactionRelatedErrors = e => {
if (e && e.statusCode === 0x6a80) {
Expand Down Expand Up @@ -57,6 +58,7 @@ export default class Trx {
"signTransaction",
"signTransactionHash",
"signPersonalMessage",
"signTIP712HashedMessage",
"getAppConfiguration",
],
scrambleKey,
Expand Down Expand Up @@ -319,6 +321,15 @@ export default class Trx {
});
}

/**
* Sign a typed data. The host computes the domain separator and hashStruct(message)
* @example
const signature = await tronApp.signTIP712HashedMessage("44'/195'/0'/0/0",Buffer.from( "0101010101010101010101010101010101010101010101010101010101010101").toString("hex"), Buffer.from("0202020202020202020202020202020202020202020202020202020202020202").toString("hex"));
*/
signTIP712HashedMessage(path: string, domainSeparatorHex: string, hashStructMessageHex: string) {
return signTIP712HashedMessage(this.transport, path, domainSeparatorHex, hashStructMessageHex);
}

/**
* get Tron address for a given BIP 32 path.
* @param path a path in BIP 32 format
Expand Down
9 changes: 9 additions & 0 deletions libs/ledgerjs/packages/hw-app-trx/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,12 @@ export function decodeVarint(stream: Buffer, index: number): DecodeResult {

throw new Error("Too many bytes when decoding varint.");
}

export const padHexString = (str: string) => {
return str.length % 2 ? "0" + str : str;
};

export function hexBuffer(str: string): Buffer {
const strWithoutPrefix = str.startsWith("0x") ? str.slice(2) : str;
return Buffer.from(padHexString(strWithoutPrefix), "hex");
}
22 changes: 22 additions & 0 deletions libs/ledgerjs/packages/hw-app-trx/tests/Trx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,25 @@ test("getSharedKey", async () => {
"04f3087b3d8f99fff119458a5e66f47a391af594e06e4f23e7849347125648a4c93369c0e4a5cce4aabec92f0abf90c94ca33cdeef905d848dfba5e12a8d77137a"
);
});

test("signTIP712HashedMessage", async () => {
const transport = await openTransportReplayer(
RecordStore.fromString(`
=> e00c000055058000002c800000c380000000000000000000000001010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202
<= 1c9b03dd6de5285ac5a648d7288f111e8aafc6ae36338e000130011f1eb68fbbe9760513d08cb2a582d96af3559e5c1185235e3a35b14f223751254659108a5f1a9000
`)
);
const trx = new Trx(transport);
const result = await trx.signTIP712HashedMessage(
"44'/195'/0'/0/0",
Buffer.from("0101010101010101010101010101010101010101010101010101010101010101", "hex").toString(
"hex"
),
Buffer.from("0202020202020202020202020202020202020202020202020202020202020202", "hex").toString(
"hex"
)
);
expect(result).toEqual(
"1c9b03dd6de5285ac5a648d7288f111e8aafc6ae36338e000130011f1eb68fbbe9760513d08cb2a582d96af3559e5c1185235e3a35b14f223751254659108a5f1a"
);
});