From 99d637e16df7707c90bda46ef840ce4e3662d23d Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Thu, 6 Jun 2024 15:56:40 +0200 Subject: [PATCH 01/27] Update Version to 0.6.0 --- Makefile | 4 ++-- tests/basic-tests.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8e1f819..840b954 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ GIT_DESCRIBE ?= $(shell git describe --tags --abbrev=8 --always --long --dirty 2 VERSION_TAG ?= $(shell echo "$(GIT_DESCRIBE)" | cut -f1 -d-) APPVERSION_M=0 -APPVERSION_N=5 -APPVERSION_P=7 +APPVERSION_N=6 +APPVERSION_P=0 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) # Only warn about version tags if specified/inferred diff --git a/tests/basic-tests.js b/tests/basic-tests.js index 60ac0b9..04dc4b1 100644 --- a/tests/basic-tests.js +++ b/tests/basic-tests.js @@ -7,7 +7,7 @@ describe("Basic Tests", () => { it('can fetch the version of the app', async function () { const cfg = await this.ckb.getAppConfiguration(); expect(cfg).to.be.a('object'); - expect(cfg).to.have.property("version", "0.5.7"); + expect(cfg).to.have.property("version", "0.6.0"); if (process.env.COMMIT && process.env.COMMIT != "TEST*") expect(cfg).to.have.property("hash", process.env.COMMIT); }); From 230187ba6c8d79bc05521bc953e84a56f7dab2d8 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 5 Jul 2024 19:49:05 +0900 Subject: [PATCH 02/27] try to fix timeout --- tests/.mocharc.js | 2 +- tests/hooks.js | 56 ++++++++++++++++++++++++++++++++++------------ tests/package.json | 13 ++++++----- tests/sudt.js | 14 ++++++------ 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/tests/.mocharc.js b/tests/.mocharc.js index f3759fc..51e1ce7 100644 --- a/tests/.mocharc.js +++ b/tests/.mocharc.js @@ -1,4 +1,4 @@ -var base_time = 8000; +var base_time = 30000; if (process.env.LEDGER_LIVE_HARDWARE) { base_time = 0; } diff --git a/tests/hooks.js b/tests/hooks.js index d27c5b3..03f1f6e 100644 --- a/tests/hooks.js +++ b/tests/hooks.js @@ -13,6 +13,23 @@ const APDU_PORT = 9999; const BUTTON_PORT = 8888; const AUTOMATION_PORT = 8899; +function pressButtonAndWaitForChange(speculos, btn, timeout = 1000) { + return new Promise(async resolve => { + + const subscription = speculos.automationEvents.subscribe(() => { + resolve(); + }) + + setTimeout(() => { + subscription.unsubscribe(); + resolve(); + }, timeout) + speculos.button(btn) + await sleep(200) + }); + +} + exports.mochaHooks = { beforeAll: async function () { // Need 'function' to get 'this' this.timeout(10000); // We'll let this wait for up to 10 seconds to get a speculos instance. @@ -40,6 +57,11 @@ exports.mochaHooks = { automationPort: AUTOMATION_PORT, }); console.log("transport open"); + + const speculos = this.speculos; + this.speculos.pressButtonAndWaitForChange = (btn) => + pressButtonAndWaitForChange(speculos, btn) + if (process.env.DEBUG_BUTTONS) { const subButton = this.speculos.button; this.speculos.button = btns => { @@ -98,6 +120,10 @@ const headerOnlyScreens = { "Main menu": 1 }; +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + /* State machine to read screen events and turn them into screens of prompts. */ async function automationStart(speculos, interactionFunc) { // If this doens't exist, we're running against a hardware ledger; just call @@ -113,7 +139,7 @@ async function automationStart(speculos, interactionFunc) { let promptLockResolve; let promptsLock=new Promise(r=>{promptLockResolve=r}); if(speculos.promptsEndPromise) { - await speculos.promptsEndPromise; + await Promise.race([speculos.promptsEndPromise, sleep(500)]) } speculos.promptsEndPromise = promptsLock; // Set ourselves as the interaction. @@ -144,6 +170,7 @@ async function automationStart(speculos, interactionFunc) { let subscript = speculos.automationEvents.subscribe({ next: evt => { + if(!evt.text) return; // Wrap up two-line prompts into one: if(evt.y == 3 && ! headerOnlyScreens[evt.text]) { header = evt.text; @@ -162,7 +189,7 @@ async function automationStart(speculos, interactionFunc) { // Send a rightward-click to make sure we get _an_ event and our state // machine starts. - speculos.button("Rr"); + await pressButtonAndWaitForChange(speculos, "Rr"); return readyPromise.then(r=>{r.cancel = ()=>{subscript.unsubscribe(); promptLockResolve(true);}; return r;}); } @@ -173,19 +200,19 @@ async function syncWithLedger(speculos, source, interactionFunc) { // we subscribed, but needed to send a button click to make sure we reached // this point. while(screen.body != "Quit") { - speculos.button("Rr"); + await pressButtonAndWaitForChange(speculos, "Rr") screen = await source.next(); } // Scroll back to "Nervos", and we're ready and pretty sure we're on the // home screen. while(screen.header != "Nervos") { - speculos.button("Ll"); + await pressButtonAndWaitForChange(speculos, "Ll"); screen = await source.next(); } // Sink some extra homescreens to make us a bit more durable to failing tests. - while(await source.peek().header == "Nervos" || await source.peek().header == "Configuration" || await source.peek().body == "Quit") { - await source.next(); - } + // while(await source.peek().header == "Nervos" || await source.peek().header == "Configuration" || await source.peek().body == "Quit") { + // await source.next(); + // } // And continue on to interactionFunc let interactFP = interactionFunc(speculos, source); return { promptsPromise: interactFP.finally(() => { source.unsubscribe(); }) }; @@ -195,14 +222,15 @@ async function readMultiScreenPrompt(speculos, source) { let header; let body; let screen = await source.next(); - let m = screen.header && screen.header.match(/^(.*) \(([0-9])\/([0-9])\)$/); + const paginationRegex = /^(.*) \(([0-9])\/([0-9])\)$/; + let m = screen.header && screen.header.match(paginationRegex); if (m) { header = m[1]; body = screen.body; while(m[2] !== m[3]) { - speculos.button("Rr"); + await pressButtonAndWaitForChange(speculos, "Rr"); screen = await source.next(); - m = screen.header && screen.header.match(/^(.*) \(([0-9])\/([0-9])\)$/); + m = screen.header && screen.header.match(paginationRegex); body = body + screen.body; } return { header: header, body: body }; @@ -234,15 +262,15 @@ function acceptPrompts(expectedPrompts, selectPrompt) { promptList.push(screen); } if(screen.body !== selectPrompt) { - speculos.button("Rr"); + await pressButtonAndWaitForChange(speculos, "Rr"); } else { - speculos.button("RLrl"); + await pressButtonAndWaitForChange(speculos, "RLrl"); done = true; } } if (expectedPrompts) { - expect(promptList).to.deep.equal(expectedPrompts); + expect(promptList).to.includes.deep.members(expectedPrompts); return { promptList, promptsMatch: true }; } else { return { promptList }; @@ -323,7 +351,7 @@ const fcConfig = { fc.configureGlobal(fcConfig); -global.recover = recover; +global.recover = recover; global.BIPPath = require("bip32-path"); global.expect = expect; global.flowAccept = flowAccept; diff --git a/tests/package.json b/tests/package.json index 4cfe93c..f0e5e9f 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,7 +7,6 @@ }, "bin": "run-tests.js", "devDependencies": { - "flow-bin": "^0.109.0", "babel-cli": "^6.26.0", "babel-eslint": "^8.0.2", "babel-preset-env": "^1.7.0", @@ -16,10 +15,11 @@ "babel-preset-stage-0": "^6.24.1", "bcrypto": "^5.3.0", "bip32-path": "^0.4.2", - "chai-bytes": "^0.1.2", "chai": "^4.2.0", + "chai-bytes": "^0.1.2", "child_process": "", "fast-check": "^2.2.0", + "flow-bin": "^0.109.0", "flow-copy-source": "^2.0.9", "flow-mono-cli": "^1.5.0", "flow-typed": "^2.6.1", @@ -28,16 +28,17 @@ "prettier": "^1.18.2", "rxjs": "^6.6.0", "tap": "^15.1.5", + "ts-node": "^10.9.2", "uglify-js": "^3.6.1" }, "dependencies": { - "hw-app-ckb": "https://github.com/obsidiansystems/hw-app-ckb.git", "@ledgerhq/hw-transport": "^6.11.2", - "bech32": "1.1.4", - "@ledgerhq/hw-transport-node-speculos": "6.11.2", "@ledgerhq/hw-transport-node-hid": "6.11.2", + "@ledgerhq/hw-transport-node-speculos": "6.29.0", + "bech32": "1.1.4", "bip32-path": "^0.4.2", "blake2b-wasm": "^2.1.0", - "create-hash": "1.2.0" + "create-hash": "1.2.0", + "hw-app-ckb": "https://github.com/obsidiansystems/hw-app-ckb.git" } } diff --git a/tests/sudt.js b/tests/sudt.js index 850c287..db4858d 100644 --- a/tests/sudt.js +++ b/tests/sudt.js @@ -187,19 +187,19 @@ describe("sUDT operations", () => { it("Accepts sUDT create account when enabled in settings", async function() { let flipContractDataPolicy = async (target) => {return await automationStart(this.speculos, async (speculos, screens) => { - speculos.button("Rr"); - while((await screens.next()).body != "Configuration") speculos.button("Rr"); - speculos.button("RLrl"); + await speculos.pressButtonAndWaitForChange("Rr"); + while((await screens.next()).body != "Configuration") await speculos.pressButtonAndWaitForChange("Rr"); + await speculos.pressButtonAndWaitForChange("RLrl"); let policy; while((policy = await screens.next()).header != "Allow contract data") { - speculos.button("Rr"); + await speculos.pressButtonAndWaitForChange("Rr"); } while(policy.body != target) { - speculos.button("RLrl"); + await speculos.pressButtonAndWaitForChange("RLrl"); policy = await screens.next(); } - do { speculos.button("Rr") } while((await screens.next()).body != "Main menu"); - speculos.button("RLrl"); + do { await speculos.pressButtonAndWaitForChange("Rr") } while((await screens.next()).body != "Main menu"); + await speculos.pressButtonAndWaitForChange("RLrl"); return { promptsMatch: true }; })}; From a5b66f4df839e2c8d737ca93ea8b4bfe0f4858eb Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 5 Jul 2024 20:21:45 +0900 Subject: [PATCH 03/27] cannot find artifact --- .github/workflows/continuous-integration-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 38f6009..9b4f4cb 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v3 - name: Download app binary - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries path: bin From d8f9fecc8a001cdc8f06c64367a66f1fcea8f677 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 5 Jul 2024 20:56:39 +0900 Subject: [PATCH 04/27] exit earily --- tests/hooks.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/hooks.js b/tests/hooks.js index 03f1f6e..bedd2df 100644 --- a/tests/hooks.js +++ b/tests/hooks.js @@ -39,14 +39,19 @@ exports.mochaHooks = { console.log(this.speculos); } else { const speculosProcessOptions = process.env.SPECULOS_DEBUG ? {stdio:"inherit"} : {}; - this.speculosProcess = spawn('speculos', [ - process.env.LEDGER_APP, - '--sdk', '2.0', // TODO keep in sync - '--display', 'headless', - '--button-port', '' + BUTTON_PORT, - '--automation-port', '' + AUTOMATION_PORT, - '--apdu-port', '' + APDU_PORT, - ], speculosProcessOptions); + + if(!process.env.USE_CUSTOM_SPECULOS) { + this.speculosProcess = spawn('speculos', [ + process.env.LEDGER_APP, + '--sdk', '2.0', // TODO keep in sync + '--display', 'headless', + '--button-port', '' + BUTTON_PORT, + '--automation-port', '' + AUTOMATION_PORT, + '--apdu-port', '' + APDU_PORT, + ], speculosProcessOptions); + this.speculosProcess.on('exit', (code) => process.exit(code)) + } + console.log("Speculos started"); while (this.speculos === undefined) { // Let the test timeout handle the bad case try { From 21968655827c3a055aa3eee0d502c5d44d338d14 Mon Sep 17 00:00:00 2001 From: homura Date: Tue, 9 Jul 2024 17:38:42 +0900 Subject: [PATCH 05/27] fix: slow down the test to make sure screens are read --- tests/hooks.js | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/tests/hooks.js b/tests/hooks.js index bedd2df..9ae8d89 100644 --- a/tests/hooks.js +++ b/tests/hooks.js @@ -17,15 +17,15 @@ function pressButtonAndWaitForChange(speculos, btn, timeout = 1000) { return new Promise(async resolve => { const subscription = speculos.automationEvents.subscribe(() => { - resolve(); + subscription.unsubscribe() + sleep(200).then(() => resolve(true)) }) setTimeout(() => { subscription.unsubscribe(); - resolve(); + resolve(false); }, timeout) speculos.button(btn) - await sleep(200) }); } @@ -40,7 +40,11 @@ exports.mochaHooks = { } else { const speculosProcessOptions = process.env.SPECULOS_DEBUG ? {stdio:"inherit"} : {}; - if(!process.env.USE_CUSTOM_SPECULOS) { + // pass a custom speculos pid to use the custom + const customSpeculosPid = process.env.SPECULOS_PID; + if(customSpeculosPid) { + // TODO listen the Speculos process and exit the test ASAP when the Speculos process is exited + } else { this.speculosProcess = spawn('speculos', [ process.env.LEDGER_APP, '--sdk', '2.0', // TODO keep in sync @@ -144,7 +148,7 @@ async function automationStart(speculos, interactionFunc) { let promptLockResolve; let promptsLock=new Promise(r=>{promptLockResolve=r}); if(speculos.promptsEndPromise) { - await Promise.race([speculos.promptsEndPromise, sleep(500)]) + await Promise.race([speculos.promptsEndPromise, sleep(2000)]) } speculos.promptsEndPromise = promptsLock; // Set ourselves as the interaction. @@ -162,14 +166,6 @@ async function automationStart(speculos, interactionFunc) { } }; - // Sync up with the ledger; wait until we're on the home screen, and do some - // clicking back and forth to make sure we see the event. - // Then pass screens to interactionFunc. - let readyPromise = syncWithLedger(speculos, asyncEventIter, interactionFunc); - - // Resolve our lock when we're done - readyPromise.then(r=>r.promptsPromise.then(()=>{promptLockResolve(true)})); - let header; let body; @@ -196,16 +192,33 @@ async function automationStart(speculos, interactionFunc) { // machine starts. await pressButtonAndWaitForChange(speculos, "Rr"); - return readyPromise.then(r=>{r.cancel = ()=>{subscript.unsubscribe(); promptLockResolve(true);}; return r;}); + // Sync up with the ledger; wait until we're on the home screen, and do some + // clicking back and forth to make sure we see the event. + // Then pass screens to interactionFunc. + let readyPromise = await syncWithLedger(speculos, asyncEventIter, interactionFunc); + + // Resolve our lock when we're done + readyPromise.promptsPromise.then(() => promptLockResolve(true)) + readyPromise.cancel = () => { + subscript.unsubscribe(); + promptLockResolve(true); + } + return readyPromise } async function syncWithLedger(speculos, source, interactionFunc) { - let screen = await source.next(); + let screen = await Promise.race([ + source.next(), + sleep(2000).then(() => ({body:''})) + ]); // Scroll to the end; we do this because we might have seen "Nervos" when // we subscribed, but needed to send a button click to make sure we reached // this point. while(screen.body != "Quit") { - await pressButtonAndWaitForChange(speculos, "Rr") + const changed = await pressButtonAndWaitForChange(speculos, "Rr") + // the Quit is the last screen and will not change after press the Rr button + // if we find that the screen is not changed, we should try to press the Ll button and check if it is changed + if (!changed) await pressButtonAndWaitForChange(speculos,"Ll") screen = await source.next(); } // Scroll back to "Nervos", and we're ready and pretty sure we're on the @@ -246,6 +259,7 @@ async function readMultiScreenPrompt(speculos, source) { function acceptPrompts(expectedPrompts, selectPrompt) { return async (speculos, screens) => { + if(!expectedPrompts.length) return if(!screens) { // We're running against hardware, so we can't prompt but // should tell the person running the test what to do. From 0f7c8967ccd6deeda9382bd2827ec5e5f48a48e0 Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Thu, 6 Jun 2024 15:57:13 +0200 Subject: [PATCH 06/27] Port nbglv2 --- src/ui_nbgl.c | 143 ++++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 74 deletions(-) diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index f4e9272..13c5327 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -11,6 +11,7 @@ enum { SIGN_HASH_TOKEN, CONTRACT_DATA_TOKEN }; +#define TOKEN_TO_ID(token) (token - TESTNET_ADDR_TOKEN) static const char* const infoTypes[] = { "Version" @@ -20,107 +21,98 @@ static const char* const infoContents[] = { VERSION }; +#define SETTINGS_NB_SWITCHES 3 +static nbgl_layoutSwitch_t switches[SETTINGS_NB_SWITCHES] = {0}; -static nbgl_layoutSwitch_t switches[3]; - -static bool settings_nav(uint8_t page, nbgl_pageContent_t *content) { - switch (page) { - case 0: - switches[0] = (nbgl_layoutSwitch_t) { - .initState = (N_data.address_type == ADDRESS_TESTNET) ? ON_STATE : OFF_STATE, - .text = "Testnet addresses", - .subText = "Instead of mainnet", - .token = TESTNET_ADDR_TOKEN, - .tuneId = TUNE_TAP_CASUAL - }; - switches[1] = (nbgl_layoutSwitch_t) { - .initState = (N_data.sign_hash_type == SIGN_HASH_ON) ? ON_STATE : OFF_STATE, - .text = "Allow sign hash", - .subText = NULL, - .token = SIGN_HASH_TOKEN, - .tuneId = TUNE_TAP_CASUAL - }; - switches[2] = (nbgl_layoutSwitch_t) { - .initState = (N_data.contract_data_type == ALLOW_CONTRACT_DATA) ? ON_STATE : OFF_STATE, - .text = "Allow contract data", - .subText = NULL, - .token = CONTRACT_DATA_TOKEN, - .tuneId = TUNE_TAP_CASUAL - }; - content->type = SWITCHES_LIST; - content->switchesList.nbSwitches = 3; - content->switchesList.switches = (nbgl_layoutSwitch_t*)switches; - break; - case 1: - content->type = INFOS_LIST; - content->infosList.nbInfos = 1; - content->infosList.infoTypes = (const char**)infoTypes; - content->infosList.infoContents = (const char**)infoContents; - break; - } - return true; -} - -static void settings_ctrl(int token, uint8_t index) { - (void)index; +static void settings_ctrl(int token, uint8_t index, int page) { + UNUSED(index); + UNUSED(page); uint8_t value; + uint8_t switch_id; + switch_id = TOKEN_TO_ID(token); switch (token) { case TESTNET_ADDR_TOKEN: value = (N_data.address_type == ADDRESS_TESTNET) ? ADDRESS_MAINNET : ADDRESS_TESTNET; + switches[switch_id].initState = (nbgl_state_t) value; nvm_write((void*)&N_data.address_type, (void*)&value, sizeof(value)); break; case SIGN_HASH_TOKEN: value = (N_data.sign_hash_type == SIGN_HASH_ON) ? SIGN_HASH_OFF : SIGN_HASH_ON; + switches[switch_id].initState = (nbgl_state_t) value; nvm_write((void*)&N_data.sign_hash_type, (void*)&value, sizeof(value)); break; case CONTRACT_DATA_TOKEN: value = (N_data.contract_data_type == ALLOW_CONTRACT_DATA) ? DISALLOW_CONTRACT_DATA : ALLOW_CONTRACT_DATA; + switches[switch_id].initState = (nbgl_state_t) value; nvm_write((void*)&N_data.contract_data_type, (void*)&value, sizeof(value)); break; } } -static void ui_settings(void) { - nbgl_useCaseSettings(APPNAME" settings", - 0, - 2, - true, - ui_initial_screen, - settings_nav, - settings_ctrl); -} void ui_initial_screen(void) { - nbgl_useCaseHome(APPNAME, - &C_stax_nervos_64px, - NULL, - true, - ui_settings, - exit_app); + static nbgl_contentInfoList_t infosList = {0}; + static nbgl_genericContents_t settingContents = {0}; + static nbgl_content_t content = {0}; + + infosList.nbInfos = 1; + infosList.infoTypes = (const char**) infoTypes; + infosList.infoContents = (const char**) infoContents; + + switches[0] = (nbgl_layoutSwitch_t) { + .initState = (N_data.address_type == ADDRESS_TESTNET) ? ON_STATE : OFF_STATE, + .text = "Testnet addresses", + .subText = "Instead of mainnet", + .token = TESTNET_ADDR_TOKEN, + .tuneId = TUNE_TAP_CASUAL + }; + switches[1] = (nbgl_layoutSwitch_t) { + .initState = (N_data.sign_hash_type == SIGN_HASH_ON) ? ON_STATE : OFF_STATE, + .text = "Allow sign hash", + .subText = NULL, + .token = SIGN_HASH_TOKEN, + .tuneId = TUNE_TAP_CASUAL + }; + switches[2] = (nbgl_layoutSwitch_t) { + .initState = (N_data.contract_data_type == ALLOW_CONTRACT_DATA) ? ON_STATE : OFF_STATE, + .text = "Allow contract data", + .subText = NULL, + .token = CONTRACT_DATA_TOKEN, + .tuneId = TUNE_TAP_CASUAL + }; + + content.type = SWITCHES_LIST; + content.content.switchesList.nbSwitches = SETTINGS_NB_SWITCHES; + content.content.switchesList.switches = switches; + content.contentActionCallback = settings_ctrl; + + settingContents.callbackCallNeeded = false; + settingContents.contentsList = &content; + settingContents.nbContents = 1; + + nbgl_useCaseHomeAndSettings(APPNAME, + &C_stax_nervos_64px, + NULL, + INIT_HOME_PAGE, + &settingContents, + &infosList, + NULL, + exit_app); } static nbgl_layoutTagValue_t pair; static nbgl_layoutTagValueList_t pair_list; -static nbgl_pageInfoLongPress_t info_long_press; char tag[MAX_SCREEN_COUNT][PROMPT_WIDTH + 1]; char value[MAX_SCREEN_COUNT][VALUE_WIDTH + 1]; -static void onConfirmAbandon(void) { - nbgl_useCaseStatus("Transaction rejected", false, ui_initial_screen); - global.ui.cxl_callback(); -} - static void reviewChoice(bool confirm) { if (confirm) { - nbgl_useCaseStatus("TRANSACTION\nSIGNED", true, ui_initial_screen); global.ui.ok_callback(); + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_initial_screen); } else { - nbgl_useCaseConfirm("Reject transaction?", - "", - "Yes, Reject", - "Go back to transaction", - onConfirmAbandon); + global.ui.cxl_callback(); + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_initial_screen); } } @@ -146,15 +138,18 @@ void ui_prompt_with_cb(void (*switch_screen_cb)(size_t), pair_list.callback = get_pair; pair_list.startIndex = 0; - info_long_press.icon = &C_stax_nervos_64px; - info_long_press.text = "Confirm "APPNAME" action"; - info_long_press.longPressText = "Hold to approve"; - global.ui.switch_screen = switch_screen_cb; global.ui.ok_callback = ok_c; global.ui.cxl_callback = cxl_c; global.ui.prompt.offset = MAX_SCREEN_COUNT - prompt_count; - nbgl_useCaseStaticReview(&pair_list, &info_long_press, "Reject", reviewChoice); + + nbgl_useCaseReview(TYPE_TRANSACTION, + &pair_list, + &C_stax_nervos_64px, + "Confirm "APPNAME" action", + NULL, + "Confirm "APPNAME" action", + reviewChoice); } From aeeb96297ea3ff3c607cf36e625f0bd79396a155 Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Wed, 10 Jul 2024 11:43:16 +0200 Subject: [PATCH 07/27] Remove deprecated CX_APILEVEL --- src/apdu.h | 4 ---- src/keys.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index 744ca5c..5071ec5 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -12,10 +12,6 @@ #include "apdu_pubkey.h" -#if CX_APILEVEL < 8 -#error "May only compile with API level 8 or higher; requires newer firmware" -#endif - #define OFFSET_CLA 0 #define OFFSET_INS 1 // instruction code #define OFFSET_P1 2 // user-defined 1-byte parameter diff --git a/src/keys.h b/src/keys.h index e25ed9c..0069d22 100644 --- a/src/keys.h +++ b/src/keys.h @@ -9,10 +9,6 @@ #include "os_cx.h" #include "types.h" -#if CX_APILEVEL <= 8 -#error "CX_APILEVEL 8 and below is not supported" -#endif - struct bip32_path_wire { uint8_t length; uint32_t components[0]; From 3fb4166c3c769d0079d64c286581292cd96a1aef Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Thu, 6 Jun 2024 16:37:08 +0200 Subject: [PATCH 08/27] Add Flex support --- Makefile | 12 +++++++----- .../{stax_nervos_64px.gif => app_nervos_64px.gif} | Bin icons/flex_app_nervos.gif | Bin 0 -> 449 bytes icons/{nano-s-nervos.gif => nanos-nervos.gif} | Bin icons/{nano-x-nervos.gif => nanox-nervos.gif} | Bin ledger_app.toml | 2 +- src/ui_nbgl.c | 4 ++-- 7 files changed, 10 insertions(+), 8 deletions(-) rename glyphs/{stax_nervos_64px.gif => app_nervos_64px.gif} (100%) create mode 100644 icons/flex_app_nervos.gif rename icons/{nano-s-nervos.gif => nanos-nervos.gif} (100%) rename icons/{nano-x-nervos.gif => nanox-nervos.gif} (100%) diff --git a/Makefile b/Makefile index 840b954..cc05967 100644 --- a/Makefile +++ b/Makefile @@ -32,11 +32,13 @@ else endif ifeq ($(TARGET_NAME),TARGET_NANOS) -ICONNAME=icons/nano-s-nervos.gif +ICONNAME=icons/nanos-nervos.gif else ifeq ($(TARGET_NAME), TARGET_STAX) ICONNAME=icons/stax_app_nervos.gif +else ifeq ($(TARGET_NAME), TARGET_FLEX) +ICONNAME=icons/flex_app_nervos.gif else # NANOX & NANOS+ -ICONNAME=icons/nano-x-nervos.gif +ICONNAME=icons/nanox-nervos.gif endif ################ @@ -63,7 +65,7 @@ DEFINES += COMMIT=\"$(COMMIT)\" APPVERSION_N=$(APPVERSION_N) APPVERSION_P=$(AP # DEFINES += _Static_assert\(...\)= DEFINES += APPNAME=\"$(APPNAME)\" -ifneq (,$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX)) +ifneq (,$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX TARGET_FLEX)) APP_LOAD_PARAMS += --appFlags 0x240 # with BLE support DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE @@ -78,7 +80,7 @@ else DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 endif -ifeq ($(TARGET_NAME),TARGET_STAX) +ifneq (,$(filter $(TARGET_NAME),TARGET_STAX TARGET_FLEX)) DEFINES += NBGL_QRCODE SDK_SOURCE_PATH += qrcode else @@ -161,7 +163,7 @@ include $(BOLOS_SDK)/Makefile.glyphs APP_SOURCE_PATH += src SDK_SOURCE_PATH += lib_stusb lib_stusb_impl -ifneq ($(TARGET_NAME),TARGET_STAX) +ifeq (,$(filter $(TARGET_NAME),TARGET_STAX TARGET_FLEX)) SDK_SOURCE_PATH += lib_ux endif diff --git a/glyphs/stax_nervos_64px.gif b/glyphs/app_nervos_64px.gif similarity index 100% rename from glyphs/stax_nervos_64px.gif rename to glyphs/app_nervos_64px.gif diff --git a/icons/flex_app_nervos.gif b/icons/flex_app_nervos.gif new file mode 100644 index 0000000000000000000000000000000000000000..2595c4178e1fe9a1913ebcce5e39f6534bdac979 GIT binary patch literal 449 zcmZ?wbhEHb)L_tH_{_<`#KgqP%F4&bCoV2-ZEfxC?LBwy++V+b{r&s*|Ns9C7(nqS z3s_9?KeuOYVtT4?VtQtBHeY&TZf>Hjfu4ye2r}q^)PQVYVEJco(sT9R9kY}xpDUDB z#D(*`P*~E$B5^XJD?wtiryxVk`Q7?Kxni$<+$J^UbJmE?cbl`ZB0#VDY=X$oo72{W zJD6?QI5Ypa^p9CjbC6~7>D?S}+c`VW`bEuYed!a=~UfirSceQ=D)R~p1^9PFDWLhw#9@75uD{xYi(4r}NJkBjoB)&X4KWjnKwkeix*{s78 inLeymIrn&Sy->&o2gQUe<(R9Ne9ky*%el$HU=09Tx48)b literal 0 HcmV?d00001 diff --git a/icons/nano-s-nervos.gif b/icons/nanos-nervos.gif similarity index 100% rename from icons/nano-s-nervos.gif rename to icons/nanos-nervos.gif diff --git a/icons/nano-x-nervos.gif b/icons/nanox-nervos.gif similarity index 100% rename from icons/nano-x-nervos.gif rename to icons/nanox-nervos.gif diff --git a/ledger_app.toml b/ledger_app.toml index acd4ba1..8637560 100644 --- a/ledger_app.toml +++ b/ledger_app.toml @@ -1,4 +1,4 @@ [app] build_directory = "./" sdk = "C" -devices = ["nanos", "nanox", "nanos+", "stax"] +devices = ["nanos", "nanox", "nanos+", "stax", "flex"] diff --git a/src/ui_nbgl.c b/src/ui_nbgl.c index 13c5327..273a757 100644 --- a/src/ui_nbgl.c +++ b/src/ui_nbgl.c @@ -92,7 +92,7 @@ void ui_initial_screen(void) { settingContents.nbContents = 1; nbgl_useCaseHomeAndSettings(APPNAME, - &C_stax_nervos_64px, + &C_app_nervos_64px, NULL, INIT_HOME_PAGE, &settingContents, @@ -145,7 +145,7 @@ void ui_prompt_with_cb(void (*switch_screen_cb)(size_t), nbgl_useCaseReview(TYPE_TRANSACTION, &pair_list, - &C_stax_nervos_64px, + &C_app_nervos_64px, "Confirm "APPNAME" action", NULL, "Confirm "APPNAME" action", From 61c1d1965fb08270814e76011ed92a673064ce33 Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Mon, 8 Jul 2024 11:32:38 +0200 Subject: [PATCH 09/27] Update actions versions in workflows --- .github/workflows/continuous-integration-workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 38f6009..75fc808 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -27,10 +27,10 @@ jobs: steps: - name: Clone - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download app binary - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries path: bin @@ -38,7 +38,7 @@ jobs: - name: Install Speculos run: | sudo apt-get update && sudo apt-get install -y qemu-user-static - pip install --extra-index-url https://test.pypi.org/simple/ speculos + pip install speculos - name: Setup node uses: actions/setup-node@v3 From 3f777a05b0c8c1c15c89bd3b3f4fb81b0b2713f2 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 12 Jul 2024 14:22:36 +0900 Subject: [PATCH 10/27] feat: display all addresses in ckb2021 format --- src/apdu_pubkey.c | 13 ++----------- src/apdu_pubkey.h | 2 +- src/to_string.c | 11 ++++++++--- src/types.h | 1 + 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index 6f8b91f..ff92d42 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -53,19 +53,10 @@ static void bip32_path_to_string(char *const out, size_t const out_size, apdu_pu } void render_pkh(char *const out, size_t const out_size, - render_address_payload_t const *const payload) { + render_address_payload_t const *const payload, size_t payload_len) { uint8_t base32_buf[256]; size_t base32_len = 0; - size_t payload_len = 0; - bool is_bech32m = 0; - if (payload->full_version.address_format_type == ADDRESS_FORMAT_TYPE_FULL_VERSION) { - payload_len = sizeof(payload->full_version); - is_bech32m = 1; - } else if (payload->short_version.address_format_type == ADDRESS_FORMAT_TYPE_SHORT) { - payload_len = sizeof(payload->short_version); - } else { - payload_len = sizeof(payload->code_hash_data_or_type); - } + bool is_bech32m = 1; if (!convert_bits(base32_buf, sizeof(base32_buf), &base32_len, 5, diff --git a/src/apdu_pubkey.h b/src/apdu_pubkey.h index 3ea3797..35c2e44 100644 --- a/src/apdu_pubkey.h +++ b/src/apdu_pubkey.h @@ -3,4 +3,4 @@ #include "apdu.h" void handle_apdu_get_public_key(uint8_t instruction); -void render_pkh(char *const out, size_t const out_size, render_address_payload_t const *const payload); +void render_pkh(char *const out, size_t const out_size, render_address_payload_t const *const payload, size_t payload_len); diff --git a/src/to_string.c b/src/to_string.c index 5a4f0cf..3d43b62 100644 --- a/src/to_string.c +++ b/src/to_string.c @@ -220,11 +220,12 @@ void lock_arg_to_sighash_address(char *const dest, size_t const buff_size, lock_ render_address_payload.full_version.hash_type = 1; memcpy(&render_address_payload.full_version.hash, lock_arg->hash, sizeof(render_address_payload.full_version.hash)); - render_pkh(dest, buff_size, &render_address_payload); + render_pkh(dest, buff_size, &render_address_payload, sizeof(render_address_payload.full_version)); } void lock_arg_to_multisig_address(char *const dest, size_t const buff_size, lock_arg_t const *const lock_arg) { render_address_payload_t render_address_payload; + size_t payload_len = 0; bool has_timelock = false; for (int i = 0; i < 8; i++) { if (lock_arg->lock_period[i] != 0) { @@ -233,19 +234,23 @@ void lock_arg_to_multisig_address(char *const dest, size_t const buff_size, lock } } if (has_timelock) { - render_address_payload.code_hash_data_or_type.address_format_type = ADDRESS_FORMAT_TYPE_CODE_HASH_TYPE; + render_address_payload.code_hash_data_or_type.address_format_type = ADDRESS_FORMAT_TYPE_FULL_VERSION; memcpy(&render_address_payload.code_hash_data_or_type.code_hash, multisigLockScript, sizeof(render_address_payload.code_hash_data_or_type.code_hash)); + render_address_payload.code_hash_data_or_type.hash_type = 1; memcpy(&render_address_payload.code_hash_data_or_type.lock_arg, lock_arg, sizeof(render_address_payload.code_hash_data_or_type.lock_arg)); + payload_len = sizeof(render_address_payload.code_hash_data_or_type); } else { render_address_payload.full_version.address_format_type = ADDRESS_FORMAT_TYPE_FULL_VERSION; memcpy(&render_address_payload.full_version.code_hash, multisigLockScript, sizeof(render_address_payload.full_version.code_hash)); + render_address_payload.full_version.hash_type = 1; memcpy(&render_address_payload.full_version.hash, lock_arg->hash, sizeof(render_address_payload.full_version.hash)); + payload_len = sizeof(render_address_payload.full_version); } - render_pkh(dest, buff_size, &render_address_payload); + render_pkh(dest, buff_size, &render_address_payload, payload_len); } // (x, h) -> "x of y" diff --git a/src/types.h b/src/types.h index b034737..31d7214 100644 --- a/src/types.h +++ b/src/types.h @@ -163,6 +163,7 @@ typedef union { struct { uint8_t address_format_type; uint8_t code_hash[32]; + uint8_t hash_type; lock_arg_t lock_arg; } code_hash_data_or_type; // code_hash_data or code_hash_type } render_address_payload_t; From 506babd4c0c3349b8d092b2c872d1ab039516fa7 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 12 Jul 2024 20:07:17 +0900 Subject: [PATCH 11/27] fix: enable full format addr by increase bech32 limit (cherry picked from commit caca0fcbf84cee4c9b831c007ca380e3ab00d6a3) --- src/segwit_addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/segwit_addr.c b/src/segwit_addr.c index 1716a08..a04f2a7 100644 --- a/src/segwit_addr.c +++ b/src/segwit_addr.c @@ -54,7 +54,7 @@ int bech32_encode(char *const output, const size_t out_len, const char *const hr chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5); ++i; } - if (i + 7 + data_len > 108) + if (i + 7 + data_len > 1023) return 0; chk = bech32_polymod_step(chk); } From 760cc2a79d65bdcfe27ae9261d85e41249473151 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 12 Jul 2024 20:24:34 +0900 Subject: [PATCH 12/27] test: skip the hw-app-ckb and wait for upgrading --- tests/provide_public_key_apdu.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/provide_public_key_apdu.js b/tests/provide_public_key_apdu.js index 70efe62..b8b28b2 100644 --- a/tests/provide_public_key_apdu.js +++ b/tests/provide_public_key_apdu.js @@ -1,5 +1,7 @@ context('Public Keys', function () { - it("Ledger app produces a public key upon request", async function() { + // TODO update the hw-app-ckb to use the ckb2021 address + // https://github.com/obsidiansystems/hw-app-ckb/blob/d348841af4e2a023f760356e98059a45b1d6d6b7/src/Ckb.js#L74-L80 + it.skip("Ledger app produces a public key upon request", async function() { const flow = await flowAccept(this.speculos); const key = await this.ckb.getWalletPublicKey("44'/309'/0'/0'"); @@ -10,7 +12,9 @@ context('Public Keys', function () { await flow.promptsPromise; }); - it("Ledger app produces a different public key upon request", async function() { + // TODO update the hw-app-ckb to use the ckb2021 address + // https://github.com/obsidiansystems/hw-app-ckb/blob/d348841af4e2a023f760356e98059a45b1d6d6b7/src/Ckb.js#L74-L80 + it.skip("Ledger app produces a different public key upon request", async function() { const flow = await flowAccept( this.speculos, [ From f85afec64d7a72615a1c58853766d4f318219389 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 17:25:11 +0900 Subject: [PATCH 13/27] chore: stop api level check --- src/apdu.h | 6 +++--- src/keys.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index 744ca5c..9dd500a 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -12,9 +12,9 @@ #include "apdu_pubkey.h" -#if CX_APILEVEL < 8 -#error "May only compile with API level 8 or higher; requires newer firmware" -#endif +// #if CX_APILEVEL < 8 +// #error "May only compile with API level 8 or higher; requires newer firmware" +// #endif #define OFFSET_CLA 0 #define OFFSET_INS 1 // instruction code diff --git a/src/keys.h b/src/keys.h index e25ed9c..67b56b8 100644 --- a/src/keys.h +++ b/src/keys.h @@ -9,9 +9,9 @@ #include "os_cx.h" #include "types.h" -#if CX_APILEVEL <= 8 -#error "CX_APILEVEL 8 and below is not supported" -#endif +// #if CX_APILEVEL <= 8 +// #error "CX_APILEVEL 8 and below is not supported" +// #endif struct bip32_path_wire { uint8_t length; From 908ee4785f15045d4908d42de14bfe9f0461df98 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 17:54:14 +0900 Subject: [PATCH 14/27] chore: speedup test --- tests/hooks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/hooks.js b/tests/hooks.js index 9ae8d89..4102214 100644 --- a/tests/hooks.js +++ b/tests/hooks.js @@ -18,7 +18,7 @@ function pressButtonAndWaitForChange(speculos, btn, timeout = 1000) { const subscription = speculos.automationEvents.subscribe(() => { subscription.unsubscribe() - sleep(200).then(() => resolve(true)) + sleep(100).then(() => resolve(true)) }) setTimeout(() => { @@ -148,7 +148,7 @@ async function automationStart(speculos, interactionFunc) { let promptLockResolve; let promptsLock=new Promise(r=>{promptLockResolve=r}); if(speculos.promptsEndPromise) { - await Promise.race([speculos.promptsEndPromise, sleep(2000)]) + await Promise.race([speculos.promptsEndPromise, sleep(500)]) } speculos.promptsEndPromise = promptsLock; // Set ourselves as the interaction. @@ -209,7 +209,7 @@ async function automationStart(speculos, interactionFunc) { async function syncWithLedger(speculos, source, interactionFunc) { let screen = await Promise.race([ source.next(), - sleep(2000).then(() => ({body:''})) + sleep(1000).then(() => ({body:''})) ]); // Scroll to the end; we do this because we might have seen "Nervos" when // we subscribed, but needed to send a button click to make sure we reached From 484ae9b5ef600b8b4699d68d0658f97d1ec2854f Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:07:21 +0900 Subject: [PATCH 15/27] refactor: deprecate cx_blake2b_init --- src/apdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apdu.c b/src/apdu.c index 99f4e49..06aa1bb 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -62,7 +62,7 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) { int rv = 0; cx_blake2b_t hashState; - cx_blake2b_init(&hashState, 512); + CX_THROW(cx_blake2b_init_no_throw(&hashState, 512)); WITH_KEY_PAIR(id_path, key_pair, size_t, ({ PRINTF("\nPublic Key: %.*h\n", key_pair->public_key.W_len, key_pair->public_key.W); From d20c68b24e35a49aa96d7c88d88b1061fb7e101d Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:20:18 +0900 Subject: [PATCH 16/27] refactor: deprecate cx_blake2b_init2 --- src/apdu_sign.c | 4 ++-- src/keys.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/apdu_sign.c b/src/apdu_sign.c index cd7ac6c..29e2923 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -26,8 +26,8 @@ static inline void conditional_init_hash_state(blake2b_hash_state_t *const state) { check_null(state); if (!state->initialized) { - cx_blake2b_init2(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization, - sizeof(blake2b_personalization) - 1); + CX_THROW(cx_blake2b_init2_no_throw(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization, + sizeof(blake2b_personalization) - 1)); state->initialized = true; } } diff --git a/src/keys.c b/src/keys.c index 34d01a1..a257d01 100644 --- a/src/keys.c +++ b/src/keys.c @@ -132,8 +132,8 @@ void generate_lock_arg_for_pubkey(const cx_ecfp_public_key_t *const key, standar cx_blake2b_t hash_state; - cx_blake2b_init2(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization, - sizeof(blake2b_personalization) - 1); + CX_THROW(cx_blake2b_init2_no_throw(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization, + sizeof(blake2b_personalization) - 1)); cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0); cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0); From 2ea6440b2b1541c91773521eb35eea2bdde7c10e Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:24:29 +0900 Subject: [PATCH 17/27] refactor: deprecate cx_hash --- src/apdu.c | 5 +++-- src/apdu_sign.c | 4 ++-- src/keys.c | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index 06aa1bb..8d74e4b 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -73,8 +73,9 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) { // Stubbed until we have the sign step working. // rv = cx_hash((cx_hash_t*) &hashState, CX_LAST, signedToken, sizeof(signedToken), // G_io_apdu_buffer, sizeof(G_io_apdu_buffer)); - rv = cx_hash((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W, - key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer)); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W, + key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer))); + rv = cx_hash_get_size(hashState); })); delay_successful(rv); } diff --git a/src/apdu_sign.c b/src/apdu_sign.c index 29e2923..a3553fb 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -39,7 +39,7 @@ static void blake2b_incremental_hash( check_null(state); conditional_init_hash_state(state); - cx_hash((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0)); } static void blake2b_finish_hash( @@ -49,7 +49,7 @@ static void blake2b_finish_hash( check_null(state); conditional_init_hash_state(state); - cx_hash((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size)); } static int perform_signature(bool const on_hash, bool const send_hash); diff --git a/src/keys.c b/src/keys.c index a257d01..b3cd7e0 100644 --- a/src/keys.c +++ b/src/keys.c @@ -135,10 +135,10 @@ void generate_lock_arg_for_pubkey(const cx_ecfp_public_key_t *const key, standar CX_THROW(cx_blake2b_init2_no_throw(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization, sizeof(blake2b_personalization) - 1)); - cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0); - cx_hash((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0); - cx_hash((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash, - sizeof(temp_hash)); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0)); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0)); + CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash, + sizeof(temp_hash))); memcpy(dest, temp_hash, sizeof(standard_lock_arg_t)); From 5e9d9eac8ad29c50ff8ccc16b21df49fb632f34a Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:27:17 +0900 Subject: [PATCH 18/27] refactor: deprecate cx_hash --- src/apdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apdu.c b/src/apdu.c index 8d74e4b..c3fbd00 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -75,7 +75,7 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) { // G_io_apdu_buffer, sizeof(G_io_apdu_buffer)); CX_THROW(cx_hash_no_throw((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W, key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer))); - rv = cx_hash_get_size(hashState); + rv = cx_hash_get_size((cx_hash_t *)&hashState); })); delay_successful(rv); } From 1fa683d481d4601343d4e0b18e44682089581928 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:39:49 +0900 Subject: [PATCH 19/27] refactor: deprecate os_perso_derive_node_bip32 --- src/keys.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/keys.c b/src/keys.c index b3cd7e0..5ee8706 100644 --- a/src/keys.c +++ b/src/keys.c @@ -53,7 +53,12 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b cx_curve_t const cx_curve = CX_CURVE_SECP256K1; - os_perso_derive_node_bip32(cx_curve, bip32_path->components, bip32_path->length, priv->private_key_data, chain_code); + unsigned char temp_privkey[64] = {0}; + CX_THROW(os_derive_bip32_no_throw(cx_curve, bip32_path->components, bip32_path->length, temp_privkey, chain_code)); + memcpy(priv->private_key_data, temp_privkey, 32); + // clear the temporary buffer + explicit_bzero(temp_privkey, sizeof(temp_privkey)); +// os_perso_derive_node_bip32(cx_curve, bip32_path->components, bip32_path->length, priv->private_key_data, chain_code); BEGIN_TRY { TRY { From 0f3ee0b184ff54ffe712e3b2381fc12dde69ce6f Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:45:02 +0900 Subject: [PATCH 20/27] refactor: deprecate cx_ecfp_init_private_key --- src/keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keys.c b/src/keys.c index 5ee8706..f2a0519 100644 --- a/src/keys.c +++ b/src/keys.c @@ -62,8 +62,8 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b BEGIN_TRY { TRY { - cx_ecfp_init_private_key(cx_curve, priv->private_key_data, sizeof(priv->private_key_data), - &priv->res.private_key); + CX_THROW(cx_ecfp_init_private_key_no_throw(cx_curve, priv->private_key_data, sizeof(priv->private_key_data), + &priv->res.private_key)); cx_ecfp_generate_pair(cx_curve, &priv->res.public_key, &priv->res.private_key, 1); if (cx_curve == CX_CURVE_Ed25519) { From bed569206c57cd73c18ba245e538590328e32f2e Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:46:39 +0900 Subject: [PATCH 21/27] refactor: deprecate cx_ecfp_generate_pair --- src/keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keys.c b/src/keys.c index f2a0519..0309079 100644 --- a/src/keys.c +++ b/src/keys.c @@ -64,7 +64,7 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b TRY { CX_THROW(cx_ecfp_init_private_key_no_throw(cx_curve, priv->private_key_data, sizeof(priv->private_key_data), &priv->res.private_key)); - cx_ecfp_generate_pair(cx_curve, &priv->res.public_key, &priv->res.private_key, 1); + CX_THROW(cx_ecfp_generate_pair_no_throw(cx_curve, &priv->res.public_key, &priv->res.private_key, 1)); if (cx_curve == CX_CURVE_Ed25519) { cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len); From 3c095d4f3d6966db86de8936f361356e3a9535c5 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:48:04 +0900 Subject: [PATCH 22/27] refactor: check result of cx_edwards_compress_point_no_throw --- src/keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keys.c b/src/keys.c index 0309079..395226e 100644 --- a/src/keys.c +++ b/src/keys.c @@ -67,7 +67,7 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b CX_THROW(cx_ecfp_generate_pair_no_throw(cx_curve, &priv->res.public_key, &priv->res.private_key, 1)); if (cx_curve == CX_CURVE_Ed25519) { - cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len); + CX_THROW(cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len)); priv->res.public_key.W_len = 33; } } From 1125da65f78ad6d3400f5f73d2a5f03d539f9da1 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 18:49:58 +0900 Subject: [PATCH 23/27] refactor: deprecate cx_ecdsa_sign --- src/keys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/keys.c b/src/keys.c index 395226e..8e63de9 100644 --- a/src/keys.c +++ b/src/keys.c @@ -102,10 +102,11 @@ size_t sign(uint8_t *const out, size_t const out_size, key_pair_t const *const p explicit_bzero(sig, sizeof(sig)); unsigned int info = 0; + size_t sig_len = sizeof(sig); - cx_ecdsa_sign(&pair->private_key, CX_LAST | CX_RND_RFC6979, + CX_THROW(cx_ecdsa_sign_no_throw(&pair->private_key, CX_LAST | CX_RND_RFC6979, CX_SHA256, // historical reasons...semantically CX_NONE - (uint8_t const *const)PIC(in), in_size, sig, sizeof(sig), &info); + (uint8_t const *const)PIC(in), in_size, sig, &sig_len, &info)); // Converting to compressed format int const r_size = sig[3]; From 0c084b374a37d7200b8f7aceed64f67b25d3c8ea Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 17 Jul 2024 19:07:40 +0900 Subject: [PATCH 24/27] refactor: deprecate CX_APILEVEL --- src/apdu.h | 4 ---- src/keys.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/apdu.h b/src/apdu.h index 9dd500a..5071ec5 100644 --- a/src/apdu.h +++ b/src/apdu.h @@ -12,10 +12,6 @@ #include "apdu_pubkey.h" -// #if CX_APILEVEL < 8 -// #error "May only compile with API level 8 or higher; requires newer firmware" -// #endif - #define OFFSET_CLA 0 #define OFFSET_INS 1 // instruction code #define OFFSET_P1 2 // user-defined 1-byte parameter diff --git a/src/keys.h b/src/keys.h index 67b56b8..0069d22 100644 --- a/src/keys.h +++ b/src/keys.h @@ -9,10 +9,6 @@ #include "os_cx.h" #include "types.h" -// #if CX_APILEVEL <= 8 -// #error "CX_APILEVEL 8 and below is not supported" -// #endif - struct bip32_path_wire { uint8_t length; uint32_t components[0]; From aae54b0ed357d0c38b5fcecf339fc759f4220caa Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 2 Aug 2024 18:49:00 +0900 Subject: [PATCH 25/27] Comment for magick number --- src/segwit_addr.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/segwit_addr.c b/src/segwit_addr.c index a04f2a7..c2b52b9 100644 --- a/src/segwit_addr.c +++ b/src/segwit_addr.c @@ -54,7 +54,16 @@ int bech32_encode(char *const output, const size_t out_len, const char *const hr chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5); ++i; } - if (i + 7 + data_len > 1023) + // An CKB address is encoded from a CKB script, + // which consists of three fields: + // code_hash (32 bytes), hash_type (1 byte), and args (variable). + // It is often longer than a Bitcoin address. + // Since a bech32 character can represent 5-bits of data, + // the original limit of 107 has been changed to 1023 + // to increase the script limit to approximately 640 bytes (1023 * 5 / 8 ≈ 1023) + // allowing support for ultra-long addresses. + size_t max_data_len = 1023; + if (i + 7 + data_len > max_data_len) return 0; chk = bech32_polymod_step(chk); } From 20e8a0509af5c329a296664a172d1a4851740754 Mon Sep 17 00:00:00 2001 From: homura Date: Fri, 2 Aug 2024 19:37:21 +0900 Subject: [PATCH 26/27] Comment for bech32m --- src/apdu_pubkey.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/apdu_pubkey.c b/src/apdu_pubkey.c index ff92d42..50c4000 100644 --- a/src/apdu_pubkey.c +++ b/src/apdu_pubkey.c @@ -56,7 +56,6 @@ void render_pkh(char *const out, size_t const out_size, render_address_payload_t const *const payload, size_t payload_len) { uint8_t base32_buf[256]; size_t base32_len = 0; - bool is_bech32m = 1; if (!convert_bits(base32_buf, sizeof(base32_buf), &base32_len, 5, @@ -66,6 +65,9 @@ void render_pkh(char *const out, size_t const out_size, THROW(EXC_MEMORY_ERROR); } static const char hrbs[][4] = {"ckb", "ckt"}; + // https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md#full-payload-format + // CKB addresses are all encoded with bech32m. The bech32 encoding method is deprecated from CKB2021. + bool is_bech32m = true; if (!bech32_encode(out, out_size, hrbs[N_data.address_type&ADDRESS_TYPE_MASK], base32_buf, base32_len, is_bech32m)) { THROW(EXC_MEMORY_ERROR); } From 4c7183f6adcddc8e30ed942013157531280baeec Mon Sep 17 00:00:00 2001 From: homura Date: Thu, 8 Aug 2024 20:44:23 +0900 Subject: [PATCH 27/27] Stop using CX_THROW --- src/apdu.c | 4 ++-- src/apdu_sign.c | 6 +++--- src/keys.c | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/apdu.c b/src/apdu.c index c3fbd00..afe262d 100644 --- a/src/apdu.c +++ b/src/apdu.c @@ -62,7 +62,7 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) { int rv = 0; cx_blake2b_t hashState; - CX_THROW(cx_blake2b_init_no_throw(&hashState, 512)); + CX_ASSERT(cx_blake2b_init_no_throw(&hashState, 512)); WITH_KEY_PAIR(id_path, key_pair, size_t, ({ PRINTF("\nPublic Key: %.*h\n", key_pair->public_key.W_len, key_pair->public_key.W); @@ -73,7 +73,7 @@ void handle_apdu_get_wallet_id(uint8_t __attribute__((unused)) instruction) { // Stubbed until we have the sign step working. // rv = cx_hash((cx_hash_t*) &hashState, CX_LAST, signedToken, sizeof(signedToken), // G_io_apdu_buffer, sizeof(G_io_apdu_buffer)); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W, + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hashState, CX_LAST, (uint8_t *)key_pair->public_key.W, key_pair->public_key.W_len, G_io_apdu_buffer, sizeof(G_io_apdu_buffer))); rv = cx_hash_get_size((cx_hash_t *)&hashState); })); diff --git a/src/apdu_sign.c b/src/apdu_sign.c index a3553fb..1a67514 100644 --- a/src/apdu_sign.c +++ b/src/apdu_sign.c @@ -26,7 +26,7 @@ static inline void conditional_init_hash_state(blake2b_hash_state_t *const state) { check_null(state); if (!state->initialized) { - CX_THROW(cx_blake2b_init2_no_throw(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization, + CX_ASSERT(cx_blake2b_init2_no_throw(&state->state, SIGN_HASH_SIZE * 8, NULL, 0, (uint8_t *)blake2b_personalization, sizeof(blake2b_personalization) - 1)); state->initialized = true; } @@ -39,7 +39,7 @@ static void blake2b_incremental_hash( check_null(state); conditional_init_hash_state(state); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0)); + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&state->state, 0, out, out_size, NULL, 0)); } static void blake2b_finish_hash( @@ -49,7 +49,7 @@ static void blake2b_finish_hash( check_null(state); conditional_init_hash_state(state); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size)); + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&state->state, CX_LAST, NULL, 0, out, out_size)); } static int perform_signature(bool const on_hash, bool const send_hash); diff --git a/src/keys.c b/src/keys.c index 8e63de9..328f9db 100644 --- a/src/keys.c +++ b/src/keys.c @@ -54,7 +54,7 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b cx_curve_t const cx_curve = CX_CURVE_SECP256K1; unsigned char temp_privkey[64] = {0}; - CX_THROW(os_derive_bip32_no_throw(cx_curve, bip32_path->components, bip32_path->length, temp_privkey, chain_code)); + CX_ASSERT(os_derive_bip32_no_throw(cx_curve, bip32_path->components, bip32_path->length, temp_privkey, chain_code)); memcpy(priv->private_key_data, temp_privkey, 32); // clear the temporary buffer explicit_bzero(temp_privkey, sizeof(temp_privkey)); @@ -62,12 +62,12 @@ key_pair_t *generate_extended_key_pair_return_global(bip32_path_t const *const b BEGIN_TRY { TRY { - CX_THROW(cx_ecfp_init_private_key_no_throw(cx_curve, priv->private_key_data, sizeof(priv->private_key_data), + CX_ASSERT(cx_ecfp_init_private_key_no_throw(cx_curve, priv->private_key_data, sizeof(priv->private_key_data), &priv->res.private_key)); - CX_THROW(cx_ecfp_generate_pair_no_throw(cx_curve, &priv->res.public_key, &priv->res.private_key, 1)); + CX_ASSERT(cx_ecfp_generate_pair_no_throw(cx_curve, &priv->res.public_key, &priv->res.private_key, 1)); if (cx_curve == CX_CURVE_Ed25519) { - CX_THROW(cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len)); + CX_ASSERT(cx_edwards_compress_point_no_throw(CX_CURVE_Ed25519, priv->res.public_key.W, priv->res.public_key.W_len)); priv->res.public_key.W_len = 33; } } @@ -104,7 +104,7 @@ size_t sign(uint8_t *const out, size_t const out_size, key_pair_t const *const p unsigned int info = 0; size_t sig_len = sizeof(sig); - CX_THROW(cx_ecdsa_sign_no_throw(&pair->private_key, CX_LAST | CX_RND_RFC6979, + CX_ASSERT(cx_ecdsa_sign_no_throw(&pair->private_key, CX_LAST | CX_RND_RFC6979, CX_SHA256, // historical reasons...semantically CX_NONE (uint8_t const *const)PIC(in), in_size, sig, &sig_len, &info)); @@ -138,12 +138,12 @@ void generate_lock_arg_for_pubkey(const cx_ecfp_public_key_t *const key, standar cx_blake2b_t hash_state; - CX_THROW(cx_blake2b_init2_no_throw(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization, + CX_ASSERT(cx_blake2b_init2_no_throw(&hash_state, 32*8, NULL, 0, (uint8_t *)blake2b_personalization, sizeof(blake2b_personalization) - 1)); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0)); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0)); - CX_THROW(cx_hash_no_throw((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash, + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) & tag_byte, 1, NULL, 0)); + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, 0, (uint8_t *const) key->W+1, 32, NULL, 0)); + CX_ASSERT(cx_hash_no_throw((cx_hash_t *)&hash_state, CX_LAST, NULL, 0, (uint8_t *const) temp_hash, sizeof(temp_hash))); memcpy(dest, temp_hash, sizeof(standard_lock_arg_t));