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

[SWAP] Add new functions for trusted service integration #8440

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
121 changes: 87 additions & 34 deletions libs/ledgerjs/packages/hw-app-exchange/src/Exchange.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,22 @@ describe("Contrustructor", () => {

describe("startNewTransaction", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

afterEach(() => {
recordStore.queue = [];
beforeEach(() => {
recordStore.clearStore();
});

it("sends a correct sequence of APDU when Swap", async () => {
// Given
const mockNonceResponse = "2ac38f187c"; // Response of length 10
const mockTransport = new MockTransport(
mockTransport.setNewResponse(
Buffer.concat([
Buffer.from(mockNonceResponse),
Buffer.from([0x90, 0x00]), // StatusCodes.OK
]),
);
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);

// When
Expand All @@ -66,13 +67,12 @@ describe("startNewTransaction", () => {
])("returns a base64 nonce when $name", async ({ name, type }) => {
// Given
const mockNonceResponse = "2ac38f187c2ac38f187c2ac38f187c7c"; // Response of length 10
const mockTransport = new MockTransport(
mockTransport.setNewResponse(
Buffer.concat([
Buffer.from(mockNonceResponse),
Buffer.from([0x90, 0x00]), // StatusCodes.OK
]),
);
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), type);

// When
Expand All @@ -99,13 +99,12 @@ describe("startNewTransaction", () => {
])("returns a 32 bytes nonce when $name", async ({ name, type }) => {
// Given
const mockNonceResponse = Buffer.from("2ac38f187c2ac38f187c2ac38f187c7c"); // Response of 32 bytes
const mockTransport = new MockTransport(
mockTransport.setNewResponse(
Buffer.concat([
mockNonceResponse,
Buffer.from([0x90, 0x00]), // StatusCodes.OK
]),
);
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.SwapNg);

// When
Expand All @@ -123,27 +122,30 @@ describe("startNewTransaction", () => {
expect(result).toEqual(mockNonceResponse.toString("hex"));
});

// TOFIX: TransportError is not recognize as an Error type
xit("throws an error if status is not ok", async () => {
it("throws an error if status is not ok", async () => {
// Given
const mockResponse = "2ac38f187c"; // Response of length 10
const mockTransport = new MockTransport(
mockTransport.setNewResponse(
Buffer.concat([Buffer.from(mockResponse), Buffer.from([0x6a, 0x80])]),
);
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);

// When & Then
expect(async () => await exchange.startNewTransaction()).toThrowError();
await expect(exchange.startNewTransaction()).rejects.toThrowError();
});
});

describe("setPartnerKey", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

beforeEach(() => {
recordStore.clearStore();
});

it("sends legacy info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);
const info = {
name: "LTX",
Expand All @@ -166,9 +168,6 @@ describe("setPartnerKey", () => {

it("sends NG info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = createExchange(new transport(mockTransport), ExchangeTypes.SwapNg);
const info = {
name: "LTX",
Expand All @@ -191,11 +190,16 @@ describe("setPartnerKey", () => {
});

describe("processTransaction", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

beforeEach(() => {
recordStore.clearStore();
});

it("sends legacy info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);

const tx = Buffer.from("1234567890abcdef", "hex");
Expand All @@ -215,9 +219,6 @@ describe("processTransaction", () => {

it("sends NG info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = createExchange(new transport(mockTransport), ExchangeTypes.SwapNg);

const tx = Buffer.from("1234567890abcdef", "hex");
Expand All @@ -237,9 +238,6 @@ describe("processTransaction", () => {

it("sends NG info of larger than 255 bytes", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = createExchange(new transport(mockTransport), ExchangeTypes.SwapNg);

const value = Array(200).fill("abc").join("");
Expand Down Expand Up @@ -278,11 +276,16 @@ describe("processTransaction", () => {
});

describe("checkTransactionSignature", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

beforeEach(() => {
recordStore.clearStore();
});

it("sends legacy info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);

const txSig = Buffer.from("1234567890abcdef", "hex");
Expand All @@ -300,9 +303,6 @@ describe("checkTransactionSignature", () => {

it("sends NG info", async () => {
// Given
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);
const exchange = createExchange(new transport(mockTransport), ExchangeTypes.SwapNg);

const txSig = Buffer.from("1234567890abcdef", "hex");
Expand All @@ -318,3 +318,56 @@ describe("checkTransactionSignature", () => {
expect(recordStore.queue[0][0]).toBe(expectCommand + hexEncodedInfoExpected);
});
});

describe("getChallenge", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

it("returns a new Challenge value", async () => {
// Given
const mockNonceResponse = Buffer.from([0xff, 0xff, 0xff, 0xff]); // Response of 32 bytes
mockTransport.setNewResponse(
Buffer.concat([
mockNonceResponse,
Buffer.from([0x90, 0x00]), // StatusCodes.OK
]),
);
const exchange = createExchange(new transport(mockTransport), ExchangeTypes.SwapNg);

// When
const result = await exchange.getChallenge();

// Then
const expectCommand = Buffer.from([
0xe0,
0x10, // Start Exchance
RateTypes.Fixed,
ExchangeTypes.SwapNg,
0x00, // Data
]).toString("hex");
expect(recordStore.queue[0][0]).toBe(expectCommand);
expect(result).toEqual(4_294_967_295);
});
});

describe("sendTrustedDescriptor", () => {
const recordStore = new RecordStore();
const mockTransport = new MockTransport(Buffer.from([0, 0x90, 0x00]));
const transport = createTransportRecorder(mockTransport, recordStore);

it("sends the expected command", async () => {
// Given
const descriptor = Buffer.from([0x00, 0x0a, 0xff]).toString("hex");
const exchange = new Exchange(new transport(mockTransport), ExchangeTypes.Swap);

// When
await exchange.sendTrustedDescriptor(descriptor);

// Then
const expectCommand = Buffer.from([0xe0, 0x11, RateTypes.Fixed, ExchangeTypes.Swap]).toString(
"hex",
);
expect(recordStore.queue[0][0]).toBe(expectCommand + descriptor);
});
});
29 changes: 29 additions & 0 deletions libs/ledgerjs/packages/hw-app-exchange/src/Exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
// const CHECK_REFUND_ADDRESS_NO_DISPLAY = 0x0c;
const SIGN_COIN_TRANSACTION = 0x0a;
const CHECK_ASSET_IN_AND_DISPLAY = 0x0b;
const SEND_PKI_CERTIFICATE = 0x0e;

Check failure on line 38 in libs/ledgerjs/packages/hw-app-exchange/src/Exchange.ts

View workflow job for this annotation

GitHub Actions / Test Libraries / Codecheck Libraries

'SEND_PKI_CERTIFICATE' is assigned a value but never used
const GET_CHALLENGE = 0x10;
const SEND_TRUSTED_NAME_DESCRIPTOR = 0x11;

// Extension for PROCESS_TRANSACTION_RESPONSE APDU
const P2_NONE = 0x00 << 4;
Expand Down Expand Up @@ -334,6 +337,32 @@
maybeThrowProtocolError(result);
}

async getChallenge(): Promise<number> {
const result: Buffer = await this.transport.send(
0xe0,
GET_CHALLENGE,
this.transactionRate,
this.transactionType,
Buffer.alloc(0),
this.allowedStatuses,
);
maybeThrowProtocolError(result);
return result.slice(0, 4).readUInt32BE();
}

async sendTrustedDescriptor(data: string): Promise<void> {
const buffer = Buffer.from(data, "hex");
const result: Buffer = await this.transport.send(
0xe0,
SEND_TRUSTED_NAME_DESCRIPTOR,
this.transactionRate,
this.transactionType,
buffer,
this.allowedStatuses,
);
maybeThrowProtocolError(result);
}

private getPartnerKeyInfo({ name, curve, publicKey }: PartnerKeyInfo): Buffer {
if (this.isExchangeTypeNg()) {
return Buffer.concat([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export default class MockTransport extends Transport {
exchange(_apdu: Buffer): Promise<Buffer> {
return Promise.resolve(this.preRecordResponse);
}

setNewResponse = (buffer: Buffer) => (this.preRecordResponse = buffer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export class RecordStore {
*/
isEmpty = (): boolean => this.queue.length === 0;

/**
* Clear store history
*/
clearStore = () => (this.queue = []);

/**
* Record an APDU (used by createTransportRecorder)
* @param {Buffer} apdu input
Expand Down
Loading