From 3f1b6097777183c81826c8e8b06000221cf4fd76 Mon Sep 17 00:00:00 2001 From: zy0n Date: Fri, 24 Jul 2026 14:32:13 +0000 Subject: [PATCH] fix(transport): send P1=0x01 when fetching the spending public key RAILGUN firmware 1.6.1 requires P1=0x01 (display + confirm) on the spending-public-key command and rejects P1=0x00 with SW_WRONG_P1P2, so the device now shows the account index and pubkey for on-device approval before returning the key. Update buildGetPublicKey accordingly. The viewing-private-key command keeps P1=0x00 (it is gated by its own confirmation); document that so it is not changed by mistake. Lock both P1 values in the public-API smoke test. --- src/core/transport/apdu.ts | 14 ++++++++++++-- test/unit/public-api-smoke.test.ts | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/core/transport/apdu.ts b/src/core/transport/apdu.ts index 7e42bc6..f3003b5 100644 --- a/src/core/transport/apdu.ts +++ b/src/core/transport/apdu.ts @@ -214,6 +214,11 @@ function assertBytes(value: Uint8Array, length: number, label: string): void { /** * Build GET_PUBLIC_KEY APDU. * Returns the BabyJubjub spending public key (x, y). + * + * P1 is `0x01` (display + confirm): the RAILGUN app shows the account index and + * pubkey hex and returns the 64-byte key only on Approve (Reject → `0x6985`). + * Production firmware rejects `P1 = 0x00` (`SW_WRONG_P1P2`), so this always + * requires an on-device tap. * @param account - Account index (default 0). * @param profile - APDU profile (default RAILGUN_PROFILE). */ @@ -224,7 +229,7 @@ export function buildGetPublicKey( return { cla: profile.cla, ins: profile.commands.getPublicKey.ins, - p1: 0, + p1: 0x01, p2: 0, data: encodeAccountIndex(account), }; @@ -262,9 +267,14 @@ export function buildSignHash( } /** - * Build GET_VIEWING_KEY APDU. + * Build GET_VIEWING_KEY APDU (VIEWING_PRIVKEY, INS 0x13). * Returns the viewing private key — 32 bytes. * Device displays a confirmation prompt. + * + * P1 stays `0x00` here: unlike the *public* key commands (spending pubkey 0x01, + * viewing pubkey 0x10), the viewing-privkey export uses `P1 = 0x00` — the app + * already gates it behind an on-device confirmation. `P1 = 0x01` would return + * `SW_WRONG_P1P2`. * @param account - Account index (default 0). * @param profile - APDU profile (default RAILGUN_PROFILE). */ diff --git a/test/unit/public-api-smoke.test.ts b/test/unit/public-api-smoke.test.ts index 6b21d33..abc0bff 100644 --- a/test/unit/public-api-smoke.test.ts +++ b/test/unit/public-api-smoke.test.ts @@ -57,6 +57,8 @@ describe('public API smoke tests', () => { const cmd = buildGetPublicKey(); expect(cmd.cla).toBe(RAILGUN_CLA); expect(cmd.ins).toBe(RailgunAppINS.GET_PUBLIC_KEY); + // P1 = 0x01 (display + confirm); production firmware rejects P1 = 0x00. + expect(cmd.p1).toBe(0x01); // Default account 0 → 4 bytes big-endian expect(cmd.data).toEqual(new Uint8Array([0, 0, 0, 0])); }); @@ -77,6 +79,8 @@ describe('public API smoke tests', () => { const cmd = buildGetViewingKey(); expect(cmd.cla).toBe(RAILGUN_CLA); expect(cmd.ins).toBe(RailgunAppINS.GET_VIEWING_KEY); + // Viewing-privkey export stays P1 = 0x00 (unlike the public-key commands). + expect(cmd.p1).toBe(0x00); expect(cmd.data).toEqual(new Uint8Array([0, 0, 0, 0])); });