Merge pull request #351 from LedgerHQ/develop

App release 1.9.20
This commit is contained in:
apaillier-ledger
2022-10-11 10:24:40 +02:00
committed by GitHub
779 changed files with 4257 additions and 904 deletions

56
.github/workflows/build-workflow.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Compilation
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
- develop
jobs:
nano_debug_build:
name: Build debug application for NanoS, X and S+
strategy:
matrix:
include:
- SDK: "$NANOS_SDK"
artifact: nanos
- SDK: "$NANOX_SDK"
artifact: nanox
- SDK: "$NANOSP_SDK"
artifact: nanosp
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- name: Clone
uses: actions/checkout@v2
with:
submodules: recursive
- name: Build an altcoin
run: |
make BOLOS_SDK=${{ matrix.SDK }} DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic
mv bin/app.elf ethereum_classic_${{ matrix.artifact }}.elf
- name: Upload altcoin binary
uses: actions/upload-artifact@v2
with:
name: ethereum_classic_${{ matrix.artifact }}
path: ./ethereum_classic_${{ matrix.artifact }}.elf
- name: Build Ethereum
run: |
make clean
make BOLOS_SDK=${{ matrix.SDK }} DEBUG=1 ALLOW_DATA=1
mv bin/app.elf ethereum_${{ matrix.artifact }}.elf
- name: Upload app binary
uses: actions/upload-artifact@v2
with:
name: ethereum_${{ matrix.artifact }}
path: ./ethereum_${{ matrix.artifact }}.elf

View File

@@ -1,4 +1,4 @@
name: Compilation & tests name: Tests
on: on:
workflow_dispatch: workflow_dispatch:
@@ -11,79 +11,6 @@ on:
- develop - develop
jobs: jobs:
job_build_debug_nano_s:
name: Build debug Nano S
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- name: Clone
uses: actions/checkout@v2
with:
submodules: recursive
- name: Build an altcoin
run: |
make DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic
mv bin/app.elf ethereum_classic_nanos.elf
- name: Upload altcoin binary
uses: actions/upload-artifact@v2
with:
name: ethereum_classic_nanos
path: ./ethereum_classic_nanos.elf
- name: Build Ethereum
run: |
make clean
make DEBUG=1 ALLOW_DATA=1
mv bin/app.elf ethereum_nanos.elf
- name: Upload app binary
uses: actions/upload-artifact@v2
with:
name: ethereum_nanos
path: ./ethereum_nanos.elf
job_build_debug_nano_x:
name: Build debug Nano X
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- name: Clone
uses: actions/checkout@v2
with:
submodules: recursive
- name: Build an altcoin Nano X
run: |
make clean
make BOLOS_SDK=$NANOX_SDK DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic
mv bin/app.elf ethereum_classic_nanox.elf
- name: Upload altcoin binary
uses: actions/upload-artifact@v2
with:
name: ethereum_classic_nanox
path: ./ethereum_classic_nanox.elf
- name: Build Ethereum Nano X
run: |
make clean
make BOLOS_SDK=$NANOX_SDK DEBUG=1 ALLOW_DATA=1
mv bin/app.elf ethereum_nanox.elf
- name: Upload app binary
uses: actions/upload-artifact@v2
with:
name: ethereum_nanox
path: ./ethereum_nanox.elf
scan-build: scan-build:
name: Clang Static Analyzer name: Clang Static Analyzer
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -104,8 +31,12 @@ jobs:
name: scan-build name: scan-build
path: scan-build path: scan-build
building_for_e2e_tests: # =====================================================
name: Building binaries for E2E tests # ZEMU TESTS
# =====================================================
building_for_e2e_zemu_tests:
name: Building binaries for E2E Zemu tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
@@ -114,17 +45,19 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Build testing binaries - name: Build testing binaries
run: cd tests && ./build_local_test_elfs.sh run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
cd tests/zemu/ && ./build_local_test_elfs.sh
- name: Upload app binaries - name: Upload app binaries
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: e2e_elfs name: e2e_elfs
path: ./tests/elfs/ path: ./tests/zemu/elfs/
jobs-e2e-tests: jobs-e2e-zemu-tests:
name: E2E tests name: E2E Zemu tests
needs: [building_for_e2e_tests] needs: [building_for_e2e_zemu_tests]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Test - name: Test
@@ -146,10 +79,10 @@ jobs:
run: npm install -g yarn run: npm install -g yarn
- name: Build/Install build js deps - name: Build/Install build js deps
run: cd tests && yarn install run: cd tests/zemu/ && yarn install
- name: Create tmp folder for artifacts - name: Create tmp folder for artifacts
run: mkdir tests/elfs run: mkdir tests/zemu/elfs
- name: Download app binaries - name: Download app binaries
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
@@ -157,7 +90,70 @@ jobs:
path: tmp/ path: tmp/
- name: Gather elfs - name: Gather elfs
run: cp `find tmp/e2e_elfs/ -name "*.elf"` tests/elfs/ run: cp `find tmp/e2e_elfs/ -name "*.elf"` tests/zemu/elfs/
- name: Run zemu tests - name: Run zemu tests
run: cd tests && yarn test run: cd tests/zemu/ && yarn test
# =====================================================
# SPECULOS TESTS
# =====================================================
building_for_e2e_speculos_tests:
name: Building binaries for E2E Speculos tests
runs-on: ubuntu-latest
container:
image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- uses: actions/checkout@v2
- name: Build testing binaries
run: |
mkdir tests/speculos/elfs
make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOS_SDK && mv bin/app.elf tests/speculos/elfs/nanos.elf
make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOX_SDK && mv bin/app.elf tests/speculos/elfs/nanox.elf
make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOSP_SDK && mv bin/app.elf tests/speculos/elfs/nanosp.elf
- name: Upload app binaries
uses: actions/upload-artifact@v2
with:
name: e2e_elfs
path: ./tests/speculos/elfs
jobs-e2e-speculos-tests:
name: Speculos tests
strategy:
matrix:
model: ["nanosp", "nanos", "nanox"]
needs: [building_for_e2e_speculos_tests]
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v2
- name: Create tmp folder for artifacts
run: mkdir tests/speculos/elfs
- name: Download app binaries
uses: actions/download-artifact@v2
with:
path: tmp/
- name: Gather elfs
run: cp `find tmp/e2e_elfs/ -name "*.elf"` tests/speculos/elfs/
- name: Install dependencies
run: |
cd tests/speculos
sudo apt-get update && sudo apt-get install -y qemu-user-static
pip install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt
- name: Run speculos tests
run: |
cd tests/speculos
pytest --model ${{ matrix.model }} --path ./elfs/${{ matrix.model }}.elf --display headless

View File

@@ -19,8 +19,8 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Lint - name: Lint
uses: DoozyX/clang-format-lint-action@v0.13 uses: DoozyX/clang-format-lint-action@v0.14
with: with:
source: "./" source: "./"
extensions: "h,c" extensions: "h,c"
clangFormatVersion: 12.0.0 clangFormatVersion: 12.0.1

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ tests/elfs/*
tests/snapshots-tmp tests/snapshots-tmp
.vscode .vscode
.idea

View File

@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-10-10
### Added
- (clone) XDCNetwork
- (clone) Meter
- (clone) Multivac
- (clone) Tecra
- (clone) ApothemNetwork
### Changed
- EIP-191 improvements, now lets the user see the entire message one chunk at a time (255 characters for LNX & LNS+, 99 for LNS)
### Fixed
- Allow swap with variants
### Removed
- Compound support (will become its own plugin)
## [1.9.19](https://github.com/ledgerhq/app-ethereum/compare/1.9.18...1.9.19) - 2022-06-15 ## [1.9.19](https://github.com/ledgerhq/app-ethereum/compare/1.9.18...1.9.19) - 2022-06-15
### Added ### Added
@@ -16,7 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed ### Changed
- EIP-191 signatures now show (up to 99 characters on LNS and 255 on LNX & LNS) the actual data contained in the message (clear-signing) - EIP-191 signatures now show (up to 99 characters on LNS and 255 on LNX & LNS+) the actual data contained in the message (clear-signing)
### Fixed ### Fixed
@@ -64,7 +86,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed ### Fixed
- Fixed stark order signature on LNS - Fixed stark order signature on LNS
## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17 ## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17
@@ -76,7 +98,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed ### Fixed
- Fixed stark order signature on LNX - Fixed stark order signature on LNX
## [1.9.11](https://github.com/ledgerhq/app-ethereum/compare/1.9.10...1.9.11) - 2021-10-12 ## [1.9.11](https://github.com/ledgerhq/app-ethereum/compare/1.9.10...1.9.11) - 2021-10-12

View File

@@ -34,9 +34,9 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'"
APPVERSION_M=1 APPVERSION_M=1
APPVERSION_N=9 APPVERSION_N=9
APPVERSION_P=19 APPVERSION_P=20
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) APP_LOAD_FLAGS= --appFlags 0xa40 --dep Ethereum:$(APPVERSION)
########################### ###########################
# Set Chain environnement # # Set Chain environnement #
@@ -194,6 +194,7 @@ SDK_SOURCE_PATH += lib_ux
ifeq ($(TARGET_NAME),TARGET_NANOX) ifeq ($(TARGET_NAME),TARGET_NANOX)
SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl
endif endif
APP_SOURCE_PATH += src_bagl
### initialize plugin SDK submodule if needed, rebuild it, and warn if a difference is noticed ### initialize plugin SDK submodule if needed, rebuild it, and warn if a difference is noticed
ifeq ($(CHAIN),ethereum) ifeq ($(CHAIN),ethereum)
@@ -222,13 +223,16 @@ delete:
python3 -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) python3 -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS)
install_tests: install_tests:
cd tests && (yarn install || sudo yarn install) cd tests/zemu/ && (yarn install || sudo yarn install)
run_tests: run_tests:
cd tests && (yarn test || sudo yarn test) cd tests/zemu/ && (yarn test || sudo yarn test)
test: install_tests run_tests test: install_tests run_tests
unit-test:
make -C tests/unit
# import generic rules from the sdk # import generic rules from the sdk
include $(BOLOS_SDK)/Makefile.rules include $(BOLOS_SDK)/Makefile.rules

View File

@@ -94,6 +94,8 @@ The address can be optionally checked on the device before being returned.
#### Description #### Description
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
This command signs an Ethereum transaction after having the user validate the following parameters This command signs an Ethereum transaction after having the user validate the following parameters
- Gas price - Gas price

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

BIN
icons/nanos_app_meter.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 B

BIN
icons/nanos_app_tecracoin.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/nanos_app_tecratestnet.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 B

BIN
icons/nanox_app_meter.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 B

BIN
icons/nanox_app_tecracoin.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/nanox_app_tecratestnet.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 B

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/550'"
DEFINES += CHAINID_UPCASE=\"APOTHEMNETWORK\" CHAINID_COINNAME=\"TXDC\" CHAIN_KIND=CHAIN_KIND_APOTHEMNETWORK CHAIN_ID=51
APPNAME = "ApothemNetwork"

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/60'"
DEFINES += CHAINID_UPCASE=\"METER\" CHAINID_COINNAME=\"MTR\" CHAIN_KIND=CHAIN_KIND_METER CHAIN_ID=82
APPNAME = "Meter"

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/60'"
DEFINES += CHAINID_UPCASE=\"MULTIVAC\" CHAINID_COINNAME=\"MTV\" CHAIN_KIND=CHAIN_KIND_MULTIVAC CHAIN_ID=62621
APPNAME = "MultiVAC"

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/554'" --path "44'/60'"
DEFINES += CHAINID_UPCASE=\"TECRA\" CHAINID_COINNAME=\"TCR\" CHAIN_KIND=CHAIN_KIND_TECRA CHAIN_ID=20531812
APPNAME = "TecraCoin"

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/554'" --path "44'/60'"
DEFINES += CHAINID_UPCASE=\"TECRATESTNET\" CHAINID_COINNAME=\"TCR\" CHAIN_KIND=CHAIN_KIND_TECRA CHAIN_ID=20531811
APPNAME = "TecraTestnet"

View File

@@ -0,0 +1,3 @@
APP_LOAD_PARAMS += --path "44'/550'"
DEFINES += CHAINID_UPCASE=\"XDCNETWORK\" CHAINID_COINNAME=\"XDC\" CHAIN_KIND=CHAIN_KIND_XDCNETWORK CHAIN_ID=50
APPNAME = "XDC Network"

View File

@@ -58,66 +58,67 @@
#define OFFSET_LC 4 #define OFFSET_LC 4
#define OFFSET_CDATA 5 #define OFFSET_CDATA 5
#define ERR_APDU_EMPTY 0x6982
#define ERR_APDU_SIZE_MISMATCH 0x6983
void handleGetPublicKey(uint8_t p1, void handleGetPublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleProvideErc20TokenInformation(uint8_t p1, void handleProvideErc20TokenInformation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleProvideNFTInformation(uint8_t p1, void handleProvideNFTInformation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleSign(uint8_t p1, void handleSign(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleGetAppConfiguration(uint8_t p1, void handleGetAppConfiguration(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleSignPersonalMessage(uint8_t p1, bool handleSignPersonalMessage(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *const payload,
uint16_t dataLength, uint8_t length);
unsigned int *flags,
unsigned int *tx);
void handleSignEIP712Message(uint8_t p1, void handleSignEIP712Message(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleSetExternalPlugin(uint8_t p1, void handleSetExternalPlugin(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleSetPlugin(uint8_t p1, void handleSetPlugin(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handlePerformPrivacyOperation(uint8_t p1, void handlePerformPrivacyOperation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
@@ -126,7 +127,7 @@ void handlePerformPrivacyOperation(uint8_t p1,
void handleGetEth2PublicKey(uint8_t p1, void handleGetEth2PublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
@@ -143,7 +144,7 @@ void handleSetEth2WinthdrawalIndex(uint8_t p1,
void handleStarkwareGetPublicKey(uint8_t p1, void handleStarkwareGetPublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
@@ -155,13 +156,13 @@ void handleStarkwareSignMessage(uint8_t p1,
unsigned int *tx); unsigned int *tx);
void handleStarkwareProvideQuantum(uint8_t p1, void handleStarkwareProvideQuantum(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);
void handleStarkwareUnsafeSign(uint8_t p1, void handleStarkwareUnsafeSign(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx); unsigned int *tx);

View File

@@ -67,7 +67,12 @@ typedef enum chain_kind_e {
CHAIN_KIND_OKC, CHAIN_KIND_OKC,
CHAIN_KIND_CUBE, CHAIN_KIND_CUBE,
CHAIN_KIND_SHIDEN, CHAIN_KIND_SHIDEN,
CHAIN_KIND_ASTAR CHAIN_KIND_ASTAR,
CHAIN_KIND_XDCNETWORK,
CHAIN_KIND_METER,
CHAIN_KIND_MULTIVAC,
CHAIN_KIND_TECRA,
CHAIN_KIND_APOTHEMNETWORK
} chain_kind_t; } chain_kind_t;
typedef struct chain_config_s { typedef struct chain_config_s {

31
src/common_ui.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef _COMMON_UI_H_
#define _COMMON_UI_H_
#include <stdbool.h>
void ui_idle(void);
void ui_warning_contract_data(void);
void ui_display_public_eth2(void);
void ui_display_privacy_public_key(void);
void ui_display_privacy_shared_secret(void);
void ui_display_public_key(void);
void ui_display_sign(void);
void ui_sign_712_v0(void);
void ui_display_stark_public(void);
void ui_confirm_selector(void);
void ui_confirm_parameter(void);
void ui_stark_limit_order(void);
void ui_stark_unsafe_sign(void);
void ui_stark_transfer(bool selfTransfer, bool conditional);
// EIP-191
void ui_191_start(void);
void ui_191_switch_to_message(void);
void ui_191_switch_to_message_end(void);
void ui_191_switch_to_sign(void);
void ui_191_switch_to_question(void);
#include "ui_callbacks.h"
#include <string.h>
#endif // _COMMON_UI_H_

View File

@@ -5,7 +5,9 @@
#include "network.h" #include "network.h"
#include "ethUtils.h" #include "ethUtils.h"
void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize) { void eth_plugin_prepare_init(ethPluginInitContract_t *init,
const uint8_t *selector,
uint32_t dataSize) {
memset((uint8_t *) init, 0, sizeof(ethPluginInitContract_t)); memset((uint8_t *) init, 0, sizeof(ethPluginInitContract_t));
init->selector = selector; init->selector = selector;
init->dataSize = dataSize; init->dataSize = dataSize;
@@ -111,7 +113,7 @@ static bool eth_plugin_perform_init_old_internal(uint8_t *contractAddress,
j++) { j++) {
if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) { if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) {
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) || if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck)) ()) { ((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
strlcpy(dataContext.tokenContext.pluginName, strlcpy(dataContext.tokenContext.pluginName,
INTERNAL_ETH_PLUGINS[i].alias, INTERNAL_ETH_PLUGINS[i].alias,
PLUGIN_ID_LENGTH); PLUGIN_ID_LENGTH);
@@ -346,13 +348,13 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
} }
break; break;
case ETH_PLUGIN_QUERY_CONTRACT_ID: case ETH_PLUGIN_QUERY_CONTRACT_ID:
if (((ethQueryContractID_t *) parameter)->result <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) { if (((ethQueryContractID_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
return ETH_PLUGIN_RESULT_UNAVAILABLE; return ETH_PLUGIN_RESULT_ERROR;
} }
break; break;
case ETH_PLUGIN_QUERY_CONTRACT_UI: case ETH_PLUGIN_QUERY_CONTRACT_UI:
if (((ethQueryContractUI_t *) parameter)->result <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) { if (((ethQueryContractUI_t *) parameter)->result != ETH_PLUGIN_RESULT_OK) {
return ETH_PLUGIN_RESULT_UNAVAILABLE; return ETH_PLUGIN_RESULT_ERROR;
} }
break; break;
default: default:

View File

@@ -6,7 +6,9 @@
#define NO_EXTRA_INFO(ctx, idx) \ #define NO_EXTRA_INFO(ctx, idx) \
(allzeroes(&(ctx.transactionContext.extraInfo[idx]), sizeof(extraInfo_t))) (allzeroes(&(ctx.transactionContext.extraInfo[idx]), sizeof(extraInfo_t)))
void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize); void eth_plugin_prepare_init(ethPluginInitContract_t *init,
const uint8_t *selector,
uint32_t dataSize);
void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *provideParameter, void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *provideParameter,
uint8_t *parameter, uint8_t *parameter,
uint32_t parameterOffset); uint32_t parameterOffset);

View File

@@ -4,7 +4,6 @@
bool erc20_plugin_available_check(void); bool erc20_plugin_available_check(void);
void erc20_plugin_call(int message, void* parameters); void erc20_plugin_call(int message, void* parameters);
void compound_plugin_call(int message, void* parameters);
void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size) { void copy_address(uint8_t* dst, const uint8_t* parameter, uint8_t dst_size) {
uint8_t copy_size = MIN(dst_size, ADDRESS_LENGTH); uint8_t copy_size = MIN(dst_size, ADDRESS_LENGTH);
@@ -29,17 +28,6 @@ static const uint8_t ERC20_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7,
const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = {ERC20_TRANSFER_SELECTOR, const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = {ERC20_TRANSFER_SELECTOR,
ERC20_APPROVE_SELECTOR}; ERC20_APPROVE_SELECTOR};
static const uint8_t COMPOUND_REDEEM_UNDERLYING_SELECTOR[SELECTOR_SIZE] = {0x85, 0x2a, 0x12, 0xe3};
static const uint8_t COMPOUND_REDEEM_SELECTOR[SELECTOR_SIZE] = {0xdb, 0x00, 0x6a, 0x75};
static const uint8_t COMPOUND_MINT_SELECTOR[SELECTOR_SIZE] = {0xa0, 0x71, 0x2d, 0x68};
static const uint8_t CETH_MINT_SELECTOR[SELECTOR_SIZE] = {0x12, 0x49, 0xc5, 0x8b};
const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS] = {
COMPOUND_REDEEM_UNDERLYING_SELECTOR,
COMPOUND_REDEEM_SELECTOR,
COMPOUND_MINT_SELECTOR,
CETH_MINT_SELECTOR};
#ifdef HAVE_ETH2 #ifdef HAVE_ETH2
static const uint8_t ETH2_DEPOSIT_SELECTOR[SELECTOR_SIZE] = {0x22, 0x89, 0x51, 0x18}; static const uint8_t ETH2_DEPOSIT_SELECTOR[SELECTOR_SIZE] = {0x22, 0x89, 0x51, 0x18};
@@ -111,12 +99,6 @@ const internalEthPlugin_t INTERNAL_ETH_PLUGINS[] = {
"-erc20", "-erc20",
erc20_plugin_call}, erc20_plugin_call},
{NULL,
(const uint8_t**) COMPOUND_SELECTORS,
NUM_COMPOUND_SELECTORS,
"-cmpd",
compound_plugin_call},
#ifdef HAVE_ETH2 #ifdef HAVE_ETH2
{NULL, (const uint8_t**) ETH2_SELECTORS, NUM_ETH2_SELECTORS, "-eth2", eth2_plugin_call}, {NULL, (const uint8_t**) ETH2_SELECTORS, NUM_ETH2_SELECTORS, "-eth2", eth2_plugin_call},

View File

@@ -29,9 +29,6 @@ typedef struct internalEthPlugin_t {
#define NUM_ERC20_SELECTORS 2 #define NUM_ERC20_SELECTORS 2
extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS]; extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS];
#define NUM_COMPOUND_SELECTORS 4
extern const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS];
#ifdef HAVE_ETH2 #ifdef HAVE_ETH2
#define NUM_ETH2_SELECTORS 1 #define NUM_ETH2_SELECTORS 1

View File

@@ -1,8 +1,4 @@
#include "shared_context.h" #include "shared_context.h"
#ifdef HAVE_UX_FLOW
#include "ui_flow.h"
#endif
#include "ui_callbacks.h"
#include "eth_plugin_handler.h" #include "eth_plugin_handler.h"
#include "ux.h" #include "ux.h"
#include "feature_signTx.h" #include "feature_signTx.h"

View File

@@ -17,8 +17,7 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_callbacks.h" #include "common_ui.h"
#include "ui_flow.h"
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
@@ -84,24 +83,6 @@ void reset_app_context() {
memset((uint8_t *) &tmpContent, 0, sizeof(tmpContent)); memset((uint8_t *) &tmpContent, 0, sizeof(tmpContent));
} }
void ui_idle(void) {
// reserve a display stack slot if none yet
if (G_ux.stack_count == 0) {
ux_stack_push();
}
ux_flow_init(0, ux_idle_flow, NULL);
}
void ui_warning_contract_data(void) {
ux_flow_init(0, ux_warning_contract_data_flow, NULL);
}
unsigned int io_seproxyhal_touch_exit(__attribute__((unused)) const bagl_element_t *e) {
// Go back to the dashboard
os_sched_exit(0);
return 0; // do not redraw the widget
}
void io_seproxyhal_send_status(uint32_t sw) { void io_seproxyhal_send_status(uint32_t sw) {
G_io_apdu_buffer[0] = ((sw >> 8) & 0xff); G_io_apdu_buffer[0] = ((sw >> 8) & 0xff);
G_io_apdu_buffer[1] = (sw & 0xff); G_io_apdu_buffer[1] = (sw & 0xff);
@@ -296,6 +277,21 @@ extraInfo_t *getKnownToken(uint8_t *contractAddress) {
case CHAIN_KIND_ASTAR: case CHAIN_KIND_ASTAR:
numTokens = NUM_TOKENS_ASTAR; numTokens = NUM_TOKENS_ASTAR;
break; break;
case CHAIN_KIND_XDCNETWORK:
numTokens = NUM_TOKENS_XDCNETWORK;
break;
case CHAIN_KIND_METER:
numTokens = NUM_TOKENS_METER;
break;
case CHAIN_KIND_MULTIVAC:
numTokens = NUM_TOKENS_MULTIVAC;
break;
case CHAIN_KIND_TECRA:
numTokens = NUM_TOKENS_TECRA;
break;
case CHAIN_KIND_APOTHEMNETWORK:
numTokens = NUM_TOKENS_APOTHEMNETWORK;
break;
} }
for (i = 0; i < numTokens; i++) { for (i = 0; i < numTokens; i++) {
switch (chainConfig->kind) { switch (chainConfig->kind) {
@@ -434,6 +430,21 @@ extraInfo_t *getKnownToken(uint8_t *contractAddress) {
case CHAIN_KIND_ASTAR: case CHAIN_KIND_ASTAR:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_ASTAR[i]); currentToken = (tokenDefinition_t *) PIC(&TOKENS_ASTAR[i]);
break; break;
case CHAIN_KIND_XDCNETWORK:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_XDCNETWORK[i]);
break;
case CHAIN_KIND_METER:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_METER[i]);
break;
case CHAIN_KIND_MULTIVAC:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_MULTIVAC[i]);
break;
case CHAIN_KIND_TECRA:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_TECRA[i]);
break;
case CHAIN_KIND_APOTHEMNETWORK:
currentToken = (tokenDefinition_t *) PIC(&TOKENS_APOTHEMNETWORK[i]);
break;
} }
if (memcmp(currentToken->address, tmpContent.txContent.destination, ADDRESS_LENGTH) == 0) { if (memcmp(currentToken->address, tmpContent.txContent.destination, ADDRESS_LENGTH) == 0) {
return currentToken; return currentToken;
@@ -480,6 +491,36 @@ void handleGetWalletId(volatile unsigned int *tx) {
#endif // HAVE_WALLET_ID_SDK #endif // HAVE_WALLET_ID_SDK
const uint8_t *parseBip32(const uint8_t *dataBuffer, uint16_t *dataLength, bip32_path_t *bip32) {
if (*dataLength < 1) {
PRINTF("Invalid data\n");
return NULL;
}
bip32->length = *dataBuffer;
if (bip32->length < 0x1 || bip32->length > MAX_BIP32_PATH) {
PRINTF("Invalid bip32\n");
return NULL;
}
dataBuffer++;
(*dataLength)--;
if (*dataLength < sizeof(uint32_t) * (bip32->length)) {
PRINTF("Invalid data\n");
return NULL;
}
for (uint8_t i = 0; i < bip32->length; i++) {
bip32->path[i] = U4BE(dataBuffer, 0);
dataBuffer += sizeof(uint32_t);
*dataLength -= sizeof(uint32_t);
}
return dataBuffer;
}
void handleApdu(unsigned int *flags, unsigned int *tx) { void handleApdu(unsigned int *flags, unsigned int *tx) {
unsigned short sw = 0; unsigned short sw = 0;
@@ -623,12 +664,11 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
case INS_SIGN_PERSONAL_MESSAGE: case INS_SIGN_PERSONAL_MESSAGE:
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
*flags |= IO_ASYNCH_REPLY;
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1], handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2], G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA, G_io_apdu_buffer + OFFSET_CDATA,
G_io_apdu_buffer[OFFSET_LC], G_io_apdu_buffer[OFFSET_LC]);
flags,
tx);
break; break;
case INS_SIGN_EIP_712_MESSAGE: case INS_SIGN_EIP_712_MESSAGE:
@@ -730,9 +770,11 @@ void app_main(void) {
// no apdu received, well, reset the session, and reset the // no apdu received, well, reset the session, and reset the
// bootloader configuration // bootloader configuration
if (rx == 0) { if (rx == 0) {
THROW(0x6982); THROW(ERR_APDU_EMPTY);
}
if (rx > OFFSET_LC && rx != (G_io_apdu_buffer[OFFSET_LC] + 5)) {
THROW(ERR_APDU_SIZE_MISMATCH);
} }
PRINTF("New APDU received:\n%.*H\n", rx, G_io_apdu_buffer);
handleApdu(&flags, &tx); handleApdu(&flags, &tx);
} }

View File

@@ -18,6 +18,11 @@
#define N_storage (*(volatile internalStorage_t *) PIC(&N_storage_real)) #define N_storage (*(volatile internalStorage_t *) PIC(&N_storage_real))
typedef struct bip32_path_t {
uint8_t length;
uint32_t path[MAX_BIP32_PATH];
} bip32_path_t;
typedef struct internalStorage_t { typedef struct internalStorage_t {
unsigned char dataAllowed; unsigned char dataAllowed;
unsigned char contractDetails; unsigned char contractDetails;
@@ -82,8 +87,7 @@ typedef union extraInfo_t {
} extraInfo_t; } extraInfo_t;
typedef struct transactionContext_t { typedef struct transactionContext_t {
uint8_t pathLength; bip32_path_t bip32;
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
union extraInfo_t extraInfo[MAX_ITEMS]; union extraInfo_t extraInfo[MAX_ITEMS];
uint8_t tokenSet[MAX_ITEMS]; uint8_t tokenSet[MAX_ITEMS];
@@ -91,15 +95,13 @@ typedef struct transactionContext_t {
} transactionContext_t; } transactionContext_t;
typedef struct messageSigningContext_t { typedef struct messageSigningContext_t {
uint8_t pathLength; bip32_path_t bip32;
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t hash[INT256_LENGTH]; uint8_t hash[INT256_LENGTH];
uint32_t remainingLength; uint32_t remainingLength;
} messageSigningContext_t; } messageSigningContext_t;
typedef struct messageSigningContext712_t { typedef struct messageSigningContext712_t {
uint8_t pathLength; bip32_path_t bip32;
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t domainHash[32]; uint8_t domainHash[32];
uint8_t messageHash[32]; uint8_t messageHash[32];
} messageSigningContext712_t; } messageSigningContext712_t;
@@ -217,5 +219,6 @@ extern uint32_t eth2WithdrawalIndex;
#endif #endif
void reset_app_context(void); void reset_app_context(void);
const uint8_t *parseBip32(const uint8_t *, uint16_t *, bip32_path_t *);
#endif // _SHARED_CONTEXT_H_ #endif // _SHARED_CONTEXT_H_

View File

@@ -2,10 +2,11 @@
#include "shared_context.h" #include "shared_context.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "ui_callbacks.h"
#include "utils.h" #include "utils.h"
#include "ethUtils.h" #include "ethUtils.h"
extraInfo_t *getKnownToken(uint8_t *contractAddress);
static unsigned char const C_cx_Stark256_n[] = { static unsigned char const C_cx_Stark256_n[] = {
// n: 0x0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f // n: 0x0800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,

View File

@@ -212,4 +212,14 @@ const tokenDefinition_t const TOKENS_ASTAR[NUM_TOKENS_ASTAR] = {};
const tokenDefinition_t const TOKENS_SHIDEN[NUM_TOKENS_SHIDEN] = {}; const tokenDefinition_t const TOKENS_SHIDEN[NUM_TOKENS_SHIDEN] = {};
const tokenDefinition_t const TOKENS_XDCNETWORK[NUM_TOKENS_XDCNETWORK] = {};
const tokenDefinition_t const TOKENS_METER[NUM_TOKENS_METER] = {};
const tokenDefinition_t const TOKENS_MULTIVAC[NUM_TOKENS_MULTIVAC] = {};
const tokenDefinition_t const TOKENS_TECRA[NUM_TOKENS_TECRA] = {};
const tokenDefinition_t const TOKENS_APOTHEMNETWORK[NUM_TOKENS_APOTHEMNETWORK] = {};
#endif #endif

View File

@@ -110,6 +110,11 @@ static const uint8_t LEDGER_SIGNATURE_PUBLIC_KEY[] = {
#define NUM_TOKENS_CUBE 0 #define NUM_TOKENS_CUBE 0
#define NUM_TOKENS_ASTAR 0 #define NUM_TOKENS_ASTAR 0
#define NUM_TOKENS_SHIDEN 0 #define NUM_TOKENS_SHIDEN 0
#define NUM_TOKENS_XDCNETWORK 0
#define NUM_TOKENS_METER 0
#define NUM_TOKENS_MULTIVAC 0
#define NUM_TOKENS_TECRA 0
#define NUM_TOKENS_APOTHEMNETWORK 0
extern tokenDefinition_t const TOKENS_AKROMA[NUM_TOKENS_AKROMA]; extern tokenDefinition_t const TOKENS_AKROMA[NUM_TOKENS_AKROMA];
extern tokenDefinition_t const TOKENS_ELLAISM[NUM_TOKENS_ELLAISM]; extern tokenDefinition_t const TOKENS_ELLAISM[NUM_TOKENS_ELLAISM];
@@ -155,6 +160,11 @@ extern tokenDefinition_t const TOKENS_WETHIO[NUM_TOKENS_WETHIO];
extern tokenDefinition_t const TOKENS_CUBE[NUM_TOKENS_CUBE]; extern tokenDefinition_t const TOKENS_CUBE[NUM_TOKENS_CUBE];
extern tokenDefinition_t const TOKENS_ASTAR[NUM_TOKENS_ASTAR]; extern tokenDefinition_t const TOKENS_ASTAR[NUM_TOKENS_ASTAR];
extern tokenDefinition_t const TOKENS_SHIDEN[NUM_TOKENS_SHIDEN]; extern tokenDefinition_t const TOKENS_SHIDEN[NUM_TOKENS_SHIDEN];
extern tokenDefinition_t const TOKENS_XDCNETWORK[NUM_TOKENS_XDCNETWORK];
extern tokenDefinition_t const TOKENS_METER[NUM_TOKENS_METER];
extern tokenDefinition_t const TOKENS_MULTIVAC[NUM_TOKENS_MULTIVAC];
extern tokenDefinition_t const TOKENS_TECRA[NUM_TOKENS_TECRA];
extern tokenDefinition_t const TOKENS_APOTHEMNETWORK[NUM_TOKENS_APOTHEMNETWORK];
#endif /* HAVE_TOKENS_LIST */ #endif /* HAVE_TOKENS_LIST */

View File

@@ -10,8 +10,8 @@ unsigned int io_seproxyhal_touch_tx_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_tx_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_address_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_address_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_signMessage_ok(void);
unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_signMessage_cancel(void);
unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_signMessage712_v0_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_signMessage712_v0_ok(const bagl_element_t *e);
@@ -19,8 +19,10 @@ unsigned int io_seproxyhal_touch_signMessage712_v0_cancel(const bagl_element_t *
unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_privacy_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_privacy_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_privacy_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_privacy_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e);
void ui_idle(void);
void ui_warning_contract_data(void); void ui_warning_contract_data(void);
void io_seproxyhal_send_status(uint32_t sw); void io_seproxyhal_send_status(uint32_t sw);

View File

@@ -54,7 +54,7 @@ int local_strchr(char *string, char ch) {
return -1; return -1;
} }
uint64_t u64_from_BE(uint8_t *in, uint8_t size) { uint64_t u64_from_BE(const uint8_t *in, uint8_t size) {
uint8_t i = 0; uint8_t i = 0;
uint64_t res = 0; uint64_t res = 0;

View File

@@ -28,7 +28,7 @@ void convertUint256BE(uint8_t* data, uint32_t length, uint256_t* target);
int local_strchr(char* string, char ch); int local_strchr(char* string, char ch);
uint64_t u64_from_BE(uint8_t* in, uint8_t size); uint64_t u64_from_BE(const uint8_t* in, uint8_t size);
bool uint256_to_decimal(const uint8_t* value, size_t value_len, char* out, size_t out_len); bool uint256_to_decimal(const uint8_t* value, size_t value_len, char* out, size_t out_len);

73
src_bagl/common_ui.c Normal file
View File

@@ -0,0 +1,73 @@
#ifdef HAVE_BAGL
#include "common_ui.h"
#include "ux.h"
#include "ui_flow.h"
void ui_idle(void) {
// reserve a display stack slot if none yet
if (G_ux.stack_count == 0) {
ux_stack_push();
}
ux_flow_init(0, ux_idle_flow, NULL);
}
void ui_warning_contract_data(void) {
ux_flow_init(0, ux_warning_contract_data_flow, NULL);
}
void ui_display_public_eth2(void) {
ux_flow_init(0, ux_display_public_eth2_flow, NULL);
}
void ui_display_privacy_public_key(void) {
ux_flow_init(0, ux_display_privacy_public_key_flow, NULL);
}
void ui_display_privacy_shared_secret(void) {
ux_flow_init(0, ux_display_privacy_shared_secret_flow, NULL);
}
void ui_display_public_key(void) {
ux_flow_init(0, ux_display_public_flow, NULL);
}
void ui_sign_712_v0(void) {
ux_flow_init(0, ux_sign_712_v0_flow, NULL);
}
#ifdef HAVE_STARKWARE
void ui_display_stark_public(void) {
ux_flow_init(0, ux_display_stark_public_flow, NULL);
}
void ui_stark_limit_order(void) {
ux_flow_init(0, ux_stark_limit_order_flow, NULL);
}
void ui_stark_unsafe_sign(void) {
ux_flow_init(0, ux_stark_unsafe_sign_flow, NULL);
}
void ui_stark_transfer(bool selfTransfer, bool conditional) {
if (selfTransfer) {
ux_flow_init(
0,
(conditional ? ux_stark_self_transfer_conditional_flow : ux_stark_self_transfer_flow),
NULL);
} else {
ux_flow_init(0,
(conditional ? ux_stark_transfer_conditional_flow : ux_stark_transfer_flow),
NULL);
}
}
#endif // HAVE_STARKWARE
void ui_confirm_selector(void) {
ux_flow_init(0, ux_confirm_selector_flow, NULL);
}
void ui_confirm_parameter(void) {
ux_flow_init(0, ux_confirm_parameter_flow, NULL);
}
#endif // HAVE_BAGL

View File

@@ -1,5 +1,5 @@
#include "shared_context.h" #include "shared_context.h"
#include "ui_callbacks.h" #include "common_ui.h"
void display_settings(const ux_flow_step_t* const start_step); void display_settings(const ux_flow_step_t* const start_step);
void switch_settings_blind_signing(void); void switch_settings_blind_signing(void);
@@ -184,4 +184,4 @@ UX_STEP_CB(
#endif #endif
// clang-format on // clang-format on
UX_FLOW(ux_warning_contract_data_flow, &ux_warning_contract_data_step); UX_FLOW(ux_warning_contract_data_flow, &ux_warning_contract_data_step);

View File

@@ -20,8 +20,6 @@ extern const ux_flow_step_t* const ux_confirm_parameter_flow[];
extern const ux_flow_step_t* const ux_approval_allowance_flow[]; extern const ux_flow_step_t* const ux_approval_allowance_flow[];
extern const ux_flow_step_t* const ux_sign_flow[];
extern const ux_flow_step_t* const ux_sign_712_v0_flow[]; extern const ux_flow_step_t* const ux_sign_712_v0_flow[];
extern const ux_flow_step_t* const ux_display_public_eth2_flow[]; extern const ux_flow_step_t* const ux_display_public_eth2_flow[];
@@ -30,6 +28,8 @@ extern const ux_flow_step_t* const ux_display_privacy_public_key_flow[];
extern const ux_flow_step_t* const ux_display_privacy_shared_secret_flow[]; extern const ux_flow_step_t* const ux_display_privacy_shared_secret_flow[];
extern const ux_flow_step_t* ux_approval_tx_flow[15];
#ifdef HAVE_STARKWARE #ifdef HAVE_STARKWARE
extern const ux_flow_step_t* const ux_display_stark_public_flow[]; extern const ux_flow_step_t* const ux_display_stark_public_flow[];

View File

@@ -0,0 +1,128 @@
#include "shared_context.h"
#include "ui_callbacks.h"
#include "common_ui.h"
#include "sign_message.h"
typedef enum { UI_191_POS_REVIEW, UI_191_POS_QUESTION, UI_191_POS_END } e_ui_191_position;
static uint8_t ui_pos;
static void dummy_pre_cb(void) {
if (ui_pos == UI_191_POS_REVIEW) {
question_switcher();
} else {
ux_flow_prev();
ui_pos = UI_191_POS_REVIEW;
}
}
static void dummy_post_cb(void) {
if (ui_pos == UI_191_POS_QUESTION) {
continue_displaying_message();
} else // UI_191_END
{
ui_191_switch_to_message_end();
}
}
// clang-format off
UX_STEP_NOCB(
ux_191_step_review,
pnn,
{
&C_icon_certificate,
"Sign",
"message",
});
UX_STEP_NOCB(
ux_191_step_message,
bnnn_paging,
{
.title = "Message",
.text = strings.tmp.tmp,
});
UX_STEP_INIT(
ux_191_step_dummy_pre,
NULL,
NULL,
{
dummy_pre_cb();
});
UX_STEP_CB(
ux_191_step_theres_more,
#ifdef TARGET_NANOS
nn,
#else
nnn,
#endif
skip_rest_of_message(),
{
#ifndef TARGET_NANOS
"Press right to",
"continue message",
#else
"Press right to read",
#endif
"Double-press to skip"
});
UX_STEP_INIT(
ux_191_step_dummy_post,
NULL,
NULL,
{
dummy_post_cb();
});
UX_STEP_CB(
ux_191_step_sign,
pbb,
io_seproxyhal_touch_signMessage_ok(),
{
&C_icon_validate_14,
"Sign",
"message",
});
UX_STEP_CB(
ux_191_step_cancel,
pbb,
io_seproxyhal_touch_signMessage_cancel(),
{
&C_icon_crossmark,
"Cancel",
"signature",
});
// clang-format on
UX_FLOW(ux_191_flow,
&ux_191_step_review,
&ux_191_step_message,
&ux_191_step_dummy_pre,
&ux_191_step_theres_more,
&ux_191_step_dummy_post,
&ux_191_step_sign,
&ux_191_step_cancel);
void ui_191_start(void) {
ux_flow_init(0, ux_191_flow, NULL);
ui_pos = UI_191_POS_REVIEW;
}
void ui_191_switch_to_message(void) {
ux_flow_init(0, ux_191_flow, &ux_191_step_message);
ui_pos = UI_191_POS_REVIEW;
}
void ui_191_switch_to_message_end(void) {
// Force it to a value that will make it automatically do a prev()
ui_pos = UI_191_POS_QUESTION;
ux_flow_init(0, ux_191_flow, &ux_191_step_dummy_pre);
}
void ui_191_switch_to_sign(void) {
ux_flow_init(0, ux_191_flow, &ux_191_step_sign);
ui_pos = UI_191_POS_END;
}
void ui_191_switch_to_question(void) {
ux_flow_init(0, ux_191_flow, &ux_191_step_theres_more);
ui_pos = UI_191_POS_QUESTION;
}

View File

@@ -6,6 +6,7 @@
#include "network.h" #include "network.h"
#include "eth_plugin_handler.h" #include "eth_plugin_handler.h"
#include "ui_plugin.h" #include "ui_plugin.h"
#include "common_ui.h"
// clang-format off // clang-format off
UX_STEP_NOCB( UX_STEP_NOCB(

View File

@@ -3,8 +3,6 @@
#include "shared_context.h" #include "shared_context.h"
#include "ui_callbacks.h" #include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_stark_pubkey_ok(const bagl_element_t *e);
// clang-format off // clang-format off
UX_STEP_NOCB( UX_STEP_NOCB(
ux_display_stark_public_flow_1_step, ux_display_stark_public_flow_1_step,

View File

@@ -4,8 +4,6 @@
#include "ui_callbacks.h" #include "ui_callbacks.h"
#include "ethUtils.h" #include "ethUtils.h"
unsigned int io_seproxyhal_touch_stark_ok(const bagl_element_t *e);
void stark_sign_display_master_account() { void stark_sign_display_master_account() {
snprintf(strings.tmp.tmp, snprintf(strings.tmp.tmp,
sizeof(strings.tmp.tmp), sizeof(strings.tmp.tmp),

View File

@@ -296,6 +296,12 @@ static void processV(txContext_t *context) {
PRINTF("Invalid type for RLP_V\n"); PRINTF("Invalid type for RLP_V\n");
THROW(EXCEPTION); THROW(EXCEPTION);
} }
if (context->currentFieldLength > sizeof(context->content->v)) {
PRINTF("Invalid length for RLP_V\n");
THROW(EXCEPTION);
}
if (context->currentFieldPos < context->currentFieldLength) { if (context->currentFieldPos < context->currentFieldLength) {
uint32_t copySize = uint32_t copySize =
MIN(context->commandLength, context->currentFieldLength - context->currentFieldPos); MIN(context->commandLength, context->currentFieldLength - context->currentFieldPos);
@@ -586,7 +592,7 @@ static parserStatus_e processTxInternal(txContext_t *context) {
} }
parserStatus_e processTx(txContext_t *context, parserStatus_e processTx(txContext_t *context,
uint8_t *buffer, const uint8_t *buffer,
uint32_t length, uint32_t length,
uint32_t processingFlags) { uint32_t processingFlags) {
parserStatus_e result; parserStatus_e result;
@@ -596,7 +602,7 @@ parserStatus_e processTx(txContext_t *context,
context->commandLength = length; context->commandLength = length;
context->processingFlags = processingFlags; context->processingFlags = processingFlags;
result = processTxInternal(context); result = processTxInternal(context);
PRINTF("result: %d\n"); PRINTF("result: %d\n", result);
} }
CATCH_OTHER(e) { CATCH_OTHER(e) {
result = USTREAM_FAULT; result = USTREAM_FAULT;

View File

@@ -142,7 +142,7 @@ typedef struct txContext_t {
uint32_t dataLength; uint32_t dataLength;
uint8_t rlpBuffer[5]; uint8_t rlpBuffer[5];
uint32_t rlpBufferPos; uint32_t rlpBufferPos;
uint8_t *workBuffer; const uint8_t *workBuffer;
uint32_t commandLength; uint32_t commandLength;
uint32_t processingFlags; uint32_t processingFlags;
ustreamProcess_t customProcessor; ustreamProcess_t customProcessor;
@@ -157,7 +157,7 @@ void initTx(txContext_t *context,
ustreamProcess_t customProcessor, ustreamProcess_t customProcessor,
void *extra); void *extra);
parserStatus_e processTx(txContext_t *context, parserStatus_e processTx(txContext_t *context,
uint8_t *buffer, const uint8_t *buffer,
uint32_t length, uint32_t length,
uint32_t processingFlags); uint32_t processingFlags);
parserStatus_e continueTx(txContext_t *context); parserStatus_e continueTx(txContext_t *context);

View File

@@ -27,7 +27,28 @@ const network_info_t NETWORK_MAPPING[] = {
{.chain_id = 11297108109, .name = "Palm Network", .ticker = "PALM "}, {.chain_id = 11297108109, .name = "Palm Network", .ticker = "PALM "},
{.chain_id = 1818, .name = "Cube", .ticker = "CUBE "}, {.chain_id = 1818, .name = "Cube", .ticker = "CUBE "},
{.chain_id = 336, .name = "Shiden", .ticker = "SDN "}, {.chain_id = 336, .name = "Shiden", .ticker = "SDN "},
{.chain_id = 592, .name = "Astar", .ticker = "ASTR "}}; {.chain_id = 592, .name = "Astar", .ticker = "ASTR "},
{.chain_id = 50, .name = "XDC", .ticker = "XDC "},
{.chain_id = 82, .name = "Meter", .ticker = "MTR "},
{.chain_id = 62621, .name = "Multivac", .ticker = "MTV "},
{.chain_id = 20531812, .name = "Tecra", .ticker = "TCR "},
{.chain_id = 20531811, .name = "TecraTestnet", .ticker = "TCR "},
{.chain_id = 51, .name = "Apothemnetwork", .ticker = "XDC "},
{.chain_id = 199, .name = "BTTC", .ticker = "BTT "},
{.chain_id = 1030, .name = "Conflux", .ticker = "CFX "},
{.chain_id = 61, .name = "Ethereum Classic", .ticker = "ETC "},
{.chain_id = 246, .name = "EnergyWebChain", .ticker = "EWC "},
{.chain_id = 14, .name = "Flare", .ticker = "FLR "},
{.chain_id = 16, .name = "Flare Coston", .ticker = "FLR "},
{.chain_id = 24, .name = "KardiaChain", .ticker = "KAI "},
{.chain_id = 1284, .name = "Moonbeam", .ticker = "GLMR "},
{.chain_id = 1285, .name = "Moonriver", .ticker = "MOVR "},
{.chain_id = 66, .name = "OKXChain", .ticker = "OKT "},
{.chain_id = 99, .name = "POA", .ticker = "POA "},
{.chain_id = 7341, .name = "Shyft", .ticker = "SHFT "},
{.chain_id = 19, .name = "Songbird", .ticker = "SGB "},
{.chain_id = 73799, .name = "Volta", .ticker = "VOLTA "},
{.chain_id = 25, .name = "Cronos", .ticker = "CRO "}};
uint64_t get_chain_id(void) { uint64_t get_chain_id(void) {
uint64_t chain_id = 0; uint64_t chain_id = 0;

View File

@@ -8,8 +8,8 @@
#define MAX_NETWORK_TICKER_LEN 8 #define MAX_NETWORK_TICKER_LEN 8
typedef struct network_info_s { typedef struct network_info_s {
const char name[NETWORK_STRING_MAX_SIZE]; const char *name;
const char ticker[MAX_NETWORK_TICKER_LEN]; const char *ticker;
uint64_t chain_id; uint64_t chain_id;
} network_info_t; } network_info_t;

View File

@@ -476,6 +476,11 @@ bool tostring256(uint256_t *number, uint32_t baseParam, char *out, uint32_t outL
divmod256(&rDiv, &base, &rDiv, &rMod); divmod256(&rDiv, &base, &rDiv, &rMod);
out[offset++] = HEXDIGITS[(uint8_t) LOWER(LOWER(rMod))]; out[offset++] = HEXDIGITS[(uint8_t) LOWER(LOWER(rMod))];
} while (!zero256(&rDiv)); } while (!zero256(&rDiv));
if (offset > (outLength - 1)) {
return false;
}
out[offset] = '\0'; out[offset] = '\0';
reverseString(out, offset); reverseString(out, offset);
return true; return true;

View File

@@ -1,11 +1,9 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
void handleGetAppConfiguration(uint8_t p1, void handleGetAppConfiguration(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {

View File

@@ -3,8 +3,9 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "feature_getEth2PublicKey.h" #include "feature_getEth2PublicKey.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
static const uint8_t BLS12_381_FIELD_MODULUS[] = { static const uint8_t BLS12_381_FIELD_MODULUS[] = {
0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
@@ -42,33 +43,29 @@ void getEth2PublicKey(uint32_t *bip32Path, uint8_t bip32PathLength, uint8_t *out
void handleGetEth2PublicKey(uint8_t p1, void handleGetEth2PublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
UNUSED(dataLength); bip32_path_t bip32;
uint32_t bip32Path[MAX_BIP32_PATH];
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer++);
if (!called_from_swap) { if (!called_from_swap) {
reset_app_context(); reset_app_context();
} }
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00); THROW(0x6B00);
} }
if (p2 != 0) { if (p2 != 0) {
THROW(0x6B00); THROW(0x6B00);
} }
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0); dataBuffer = parseBip32(dataBuffer, &dataLength, &bip32);
dataBuffer += 4;
if (dataBuffer == NULL) {
THROW(0x6a80);
} }
getEth2PublicKey(bip32Path, bip32PathLength, tmpCtx.publicKeyContext.publicKey.W);
getEth2PublicKey(bip32.path, bip32.length, tmpCtx.publicKeyContext.publicKey.W);
#ifndef NO_CONSENT #ifndef NO_CONSENT
if (p1 == P1_NON_CONFIRM) if (p1 == P1_NON_CONFIRM)
@@ -79,7 +76,7 @@ void handleGetEth2PublicKey(uint8_t p1,
} }
#ifndef NO_CONSENT #ifndef NO_CONSENT
else { else {
ux_flow_init(0, ux_display_public_eth2_flow, NULL); ui_display_public_eth2();
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;
} }

View File

@@ -2,7 +2,7 @@
#include "shared_context.h" #include "shared_context.h"
#include "feature_getEth2PublicKey.h" #include "feature_getEth2PublicKey.h"
#include "ui_callbacks.h" #include "common_ui.h"
unsigned int io_seproxyhal_touch_eth2_address_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_eth2_address_ok(__attribute__((unused)) const bagl_element_t *e) {
uint32_t tx = set_result_get_eth2_publicKey(); uint32_t tx = set_result_get_eth2_publicKey();

View File

@@ -1,45 +1,44 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "feature_getPublicKey.h" #include "feature_getPublicKey.h"
#include "ethUtils.h" #include "ethUtils.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
void handleGetPublicKey(uint8_t p1, void handleGetPublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
UNUSED(dataLength);
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
uint32_t bip32Path[MAX_BIP32_PATH]; bip32_path_t bip32;
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer++);
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
if (!called_from_swap) { if (!called_from_swap) {
reset_app_context(); reset_app_context();
} }
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00); THROW(0x6B00);
} }
if ((p2 != P2_CHAINCODE) && (p2 != P2_NO_CHAINCODE)) { if ((p2 != P2_CHAINCODE) && (p2 != P2_NO_CHAINCODE)) {
THROW(0x6B00); THROW(0x6B00);
} }
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0); dataBuffer = parseBip32(dataBuffer, &dataLength, &bip32);
dataBuffer += 4;
if (dataBuffer == NULL) {
THROW(0x6a80);
} }
tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE); tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32( os_perso_derive_node_bip32(
CX_CURVE_256K1, CX_CURVE_256K1,
bip32Path, bip32.path,
bip32PathLength, bip32.length,
privateKeyData, privateKeyData,
(tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL)); (tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL));
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
@@ -66,7 +65,7 @@ void handleGetPublicKey(uint8_t p1,
"0x%.*s", "0x%.*s",
40, 40,
tmpCtx.publicKeyContext.address); tmpCtx.publicKeyContext.address);
ux_flow_init(0, ux_display_public_flow, NULL); ui_display_public_key();
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;
} }

View File

@@ -1,6 +1,6 @@
#include "shared_context.h" #include "shared_context.h"
#include "feature_getPublicKey.h" #include "feature_getPublicKey.h"
#include "ui_callbacks.h" #include "common_ui.h"
unsigned int io_seproxyhal_touch_address_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_address_ok(__attribute__((unused)) const bagl_element_t *e) {
uint32_t tx = set_result_get_publicKey(); uint32_t tx = set_result_get_publicKey();

View File

@@ -2,8 +2,8 @@
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ethUtils.h" #include "ethUtils.h"
#include "ui_flow.h"
#include "feature_performPrivacyOperation.h" #include "feature_performPrivacyOperation.h"
#include "common_ui.h"
#define P2_PUBLIC_ENCRYPTION_KEY 0x00 #define P2_PUBLIC_ENCRYPTION_KEY 0x00
#define P2_SHARED_SECRET 0x01 #define P2_SHARED_SECRET 0x01
@@ -25,43 +25,39 @@ void decodeScalar(const uint8_t *scalarIn, uint8_t *scalarOut) {
void handlePerformPrivacyOperation(uint8_t p1, void handlePerformPrivacyOperation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
UNUSED(dataLength);
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
uint8_t privateKeyDataSwapped[INT256_LENGTH]; uint8_t privateKeyDataSwapped[INT256_LENGTH];
uint32_t bip32Path[MAX_BIP32_PATH]; bip32_path_t bip32;
uint8_t bip32PathLength = *(dataBuffer++);
cx_err_t status = CX_OK; cx_err_t status = CX_OK;
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
if (dataLength < 1 + 4 * bip32PathLength) {
THROW(0x6700);
}
} else if (p2 == P2_SHARED_SECRET) {
if (dataLength < 1 + 4 * bip32PathLength + 32) {
THROW(0x6700);
}
} else {
THROW(0x6B00);
}
cx_ecfp_private_key_t privateKey;
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00); THROW(0x6B00);
} }
for (uint8_t i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0); if ((p2 != P2_PUBLIC_ENCRYPTION_KEY) && (p2 != P2_SHARED_SECRET)) {
dataBuffer += 4; THROW(0x6700);
} }
dataBuffer = parseBip32(dataBuffer, &dataLength, &bip32);
if (dataBuffer == NULL) {
THROW(0x6a80);
}
if ((p2 == P2_SHARED_SECRET) && (dataLength < 32)) {
THROW(0x6700);
}
cx_ecfp_private_key_t privateKey;
os_perso_derive_node_bip32( os_perso_derive_node_bip32(
CX_CURVE_256K1, CX_CURVE_256K1,
bip32Path, bip32.path,
bip32PathLength, bip32.length,
privateKeyData, privateKeyData,
(tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL)); (tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL));
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
@@ -112,9 +108,9 @@ void handlePerformPrivacyOperation(uint8_t p1,
32, 32,
privateKeyData); privateKeyData);
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) { if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
ux_flow_init(0, ux_display_privacy_public_key_flow, NULL); ui_display_privacy_public_key();
} else { } else {
ux_flow_init(0, ux_display_privacy_shared_secret_flow, NULL); ui_display_privacy_shared_secret();
} }
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;

View File

@@ -1,6 +1,6 @@
#include "shared_context.h" #include "shared_context.h"
#include "feature_getPublicKey.h" #include "feature_getPublicKey.h"
#include "ui_callbacks.h" #include "common_ui.h"
#include "feature_performPrivacyOperation.h" #include "feature_performPrivacyOperation.h"
unsigned int io_seproxyhal_touch_privacy_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_privacy_ok(__attribute__((unused)) const bagl_element_t *e) {

View File

@@ -1,7 +1,8 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h" #include "tokens.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR #ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR
@@ -101,7 +102,7 @@ void handleProvideErc20TokenInformation(uint8_t p1,
void handleProvideErc20TokenInformation(uint8_t p1, void handleProvideErc20TokenInformation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
__attribute__((unused)) unsigned int *tx) { __attribute__((unused)) unsigned int *tx) {

View File

@@ -2,9 +2,10 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h" #include "tokens.h"
#include "utils.h" #include "utils.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
#define TYPE_SIZE 1 #define TYPE_SIZE 1
#define VERSION_SIZE 1 #define VERSION_SIZE 1
@@ -53,7 +54,7 @@ typedef bool verificationAlgo(const cx_ecfp_public_key_t *,
void handleProvideNFTInformation(uint8_t p1, void handleProvideNFTInformation(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
@@ -218,7 +219,7 @@ void handleProvideNFTInformation(uint8_t p1,
hashId, hashId,
hash, hash,
sizeof(hash), sizeof(hash),
workBuffer + offset, (uint8_t *) workBuffer + offset,
signatureLen)) { signatureLen)) {
#ifndef HAVE_BYPASS_SIGNATURES #ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid NFT signature\n"); PRINTF("Invalid NFT signature\n");

View File

@@ -5,7 +5,7 @@
void handleSetEth2WithdrawalIndex(uint8_t p1, void handleSetEth2WithdrawalIndex(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
__attribute__((unused)) unsigned int *flags, __attribute__((unused)) unsigned int *flags,
__attribute__((unused)) unsigned int *tx) { __attribute__((unused)) unsigned int *tx) {

View File

@@ -1,13 +1,14 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h" #include "tokens.h"
#include "eth_plugin_interface.h" #include "eth_plugin_interface.h"
#include "eth_plugin_internal.h" #include "eth_plugin_internal.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
void handleSetExternalPlugin(uint8_t p1, void handleSetExternalPlugin(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {

View File

@@ -1,10 +1,11 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "tokens.h" #include "tokens.h"
#include "eth_plugin_interface.h" #include "eth_plugin_interface.h"
#include "eth_plugin_internal.h" #include "eth_plugin_internal.h"
#include "utils.h" #include "utils.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
// Supported internal plugins // Supported internal plugins
#define ERC721_STR "ERC721" #define ERC721_STR "ERC721"
@@ -86,7 +87,7 @@ static pluginType_t getPluginType(char *pluginName, uint8_t pluginNameLength) {
void handleSetPlugin(uint8_t p1, void handleSetPlugin(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
@@ -248,7 +249,7 @@ void handleSetPlugin(uint8_t p1,
hashId, hashId,
hash, hash,
sizeof(hash), sizeof(hash),
workBuffer + offset, (unsigned char *) (workBuffer + offset),
signatureLen)) { signatureLen)) {
#ifndef HAVE_BYPASS_SIGNATURES #ifndef HAVE_BYPASS_SIGNATURES
PRINTF("Invalid NFT signature\n"); PRINTF("Invalid NFT signature\n");

View File

@@ -1,212 +1,284 @@
#include <stdbool.h> #include <stdbool.h>
#include "shared_context.h" #include <ctype.h>
#include <string.h>
#include "apdu_constants.h" #include "apdu_constants.h"
#include "utils.h" #include "sign_message.h"
#include "ui_flow.h" #include "common_ui.h"
static uint8_t processed_size;
static struct {
sign_message_state sign_state : 1;
bool ui_started : 1;
} states;
static const char SIGN_MAGIC[] = static const char SIGN_MAGIC[] =
"\x19" "\x19"
"Ethereum Signed Message:\n"; "Ethereum Signed Message:\n";
/** /**
* Check if a given character is a "special" displayable ASCII character * Send a response APDU with the given Status Word
* *
* @param[in] c character we're checking * @param[in] sw status word
* @return wether the character is special or not
*/ */
static inline bool is_char_special(char c) { static void apdu_reply(uint16_t sw) {
return ((c >= '\b') && (c <= '\r')); G_io_apdu_buffer[0] = (sw >> 8) & 0xff;
G_io_apdu_buffer[1] = sw & 0xff;
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
} }
/** /**
* Check if a given data is made of ASCII characters * Get unprocessed data from last received APDU
* *
* @param[in] data the input data * @return pointer to data in APDU buffer
* @param[in] the length of the input data
* @return wether the data is fully ASCII or not
*/ */
static bool is_data_ascii(const uint8_t *const data, size_t length) { static const uint8_t *unprocessed_data(void) {
for (uint8_t idx = 0; idx < length; ++idx) { return &G_io_apdu_buffer[OFFSET_CDATA] + processed_size;
if (!is_char_special(data[idx]) && ((data[idx] < 0x20) || (data[idx] > 0x7e))) { }
/**
* Get size of unprocessed data from last received APDU
*
* @return size of data in bytes
*/
static size_t unprocessed_length(void) {
return G_io_apdu_buffer[OFFSET_LC] - processed_size;
}
/**
* Get used space from UI buffer
*
* @return size in bytes
*/
static size_t ui_buffer_length(void) {
return strlen(UI_191_BUFFER);
}
/**
* Get remaining space from UI buffer
*
* @return size in bytes
*/
static size_t remaining_ui_buffer_length(void) {
// -1 for the ending NULL byte
return (sizeof(UI_191_BUFFER) - 1) - ui_buffer_length();
}
/**
* Get free space from UI buffer
*
* @return pointer to the free space
*/
static char *remaining_ui_buffer(void) {
return &UI_191_BUFFER[ui_buffer_length()];
}
/**
* Reset the UI buffer
*
* Simply sets its first byte to a NULL character
*/
static void reset_ui_buffer(void) {
UI_191_BUFFER[0] = '\0';
}
/**
* Handle the data specific to the first APDU of an EIP-191 signature
*
* @param[in] data the APDU payload
* @param[in] length the payload size
* @return pointer to the start of the start of the message; \ref NULL if it failed
*/
static const uint8_t *first_apdu_data(const uint8_t *data, uint16_t *length) {
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
appState = APP_STATE_SIGNING_MESSAGE;
data = parseBip32(data, length, &tmpCtx.messageSigningContext.bip32);
if (data == NULL) {
apdu_reply(0x6a80);
return NULL;
}
if (*length < sizeof(uint32_t)) {
PRINTF("Invalid data\n");
apdu_reply(0x6a80);
return NULL;
}
tmpCtx.messageSigningContext.remainingLength = U4BE(data, 0);
data += sizeof(uint32_t);
*length -= sizeof(uint32_t);
// Initialize message header + length
cx_keccak_init(&global_sha3, 256);
cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0);
snprintf(strings.tmp.tmp2,
sizeof(strings.tmp.tmp2),
"%u",
tmpCtx.messageSigningContext.remainingLength);
cx_hash((cx_hash_t *) &global_sha3,
0,
(uint8_t *) strings.tmp.tmp2,
strlen(strings.tmp.tmp2),
NULL,
0);
reset_ui_buffer();
states.sign_state = STATE_191_HASH_DISPLAY;
states.ui_started = false;
return data;
}
/**
* Feed the progressive hash with new data
*
* @param[in] data the new data
* @param[in] length the data length
* @return whether it was successful or not
*/
static bool feed_hash(const uint8_t *const data, const uint8_t length) {
if (length > tmpCtx.messageSigningContext.remainingLength) {
PRINTF("Error: Length mismatch ! (%u > %u)!\n",
length,
tmpCtx.messageSigningContext.remainingLength);
apdu_reply(0x6a80);
return false;
}
cx_hash((cx_hash_t *) &global_sha3, 0, data, length, NULL, 0);
if ((tmpCtx.messageSigningContext.remainingLength -= length) == 0) {
// Finalize hash
cx_hash((cx_hash_t *) &global_sha3,
CX_LAST,
NULL,
0,
tmpCtx.messageSigningContext.hash,
32);
}
return true;
}
/**
* Feed the UI with new data
*/
static void feed_display(void) {
int c;
while ((unprocessed_length() > 0) && (remaining_ui_buffer_length() > 0)) {
c = *(char *) unprocessed_data();
if (isspace(c)) // to replace all white-space characters as spaces
{
c = ' ';
}
if (isprint(c)) {
sprintf(remaining_ui_buffer(), "%c", (char) c);
processed_size += 1;
} else {
if (remaining_ui_buffer_length() >= 4) // 4 being the fixed length of \x00
{
snprintf(remaining_ui_buffer(), remaining_ui_buffer_length(), "\\x%02x", c);
processed_size += 1;
} else {
// fill the rest of the UI buffer spaces, to consider the buffer full
memset(remaining_ui_buffer(), ' ', remaining_ui_buffer_length());
}
}
}
if ((remaining_ui_buffer_length() == 0) ||
(tmpCtx.messageSigningContext.remainingLength == 0)) {
if (!states.ui_started) {
ui_191_start();
states.ui_started = true;
} else {
ui_191_switch_to_message();
}
}
if ((unprocessed_length() == 0) && (tmpCtx.messageSigningContext.remainingLength > 0)) {
apdu_reply(0x9000);
}
}
/**
* EIP-191 APDU handler
*
* @param[in] p1 instruction parameter 1
* @param[in] p2 instruction parameter 2
* @param[in] payload received data
* @param[in] length data length
* @return whether the handling of the APDU was successful or not
*/
bool handleSignPersonalMessage(uint8_t p1,
uint8_t p2,
const uint8_t *const payload,
uint8_t length) {
const uint8_t *data = payload;
uint16_t u16_length = length;
(void) p2;
processed_size = 0;
if (p1 == P1_FIRST) {
if ((data = first_apdu_data(data, &u16_length)) == NULL) {
return false; return false;
} }
processed_size = data - payload;
} else if (p1 != P1_MORE) {
PRINTF("Error: Unexpected P1 (%u)!\n", p1);
apdu_reply(0x6B00);
return false;
}
if (!feed_hash(data, u16_length)) {
return false;
}
if (states.sign_state == STATE_191_HASH_DISPLAY) {
feed_display();
} else // hash only
{
if (tmpCtx.messageSigningContext.remainingLength == 0) {
#ifdef NO_CONSENT
io_seproxyhal_touch_signMessage_ok();
#else
ui_191_switch_to_sign();
#endif
} else {
apdu_reply(0x9000);
}
} }
return true; return true;
} }
/** /**
* Initialize value string that will be displayed in the UX STEP * Decide whether to show the question to show more of the message or not
*
* @param[in] if the value is ASCII
*/ */
static void init_value_str(bool is_ascii) { void question_switcher(void) {
if (is_ascii) { if ((states.sign_state == STATE_191_HASH_DISPLAY) &&
strings.tmp.tmp[0] = '\0'; // init string as empty ((tmpCtx.messageSigningContext.remainingLength > 0) || (unprocessed_length() > 0))) {
ui_191_switch_to_question();
} else { } else {
strcpy(strings.tmp.tmp, "0x"); // will display the hex bytes instead // Go to Sign / Cancel
ui_191_switch_to_sign();
} }
} }
/** /**
* @return Whether the currently stored data is initialized as ASCII or not * The user has decided to skip the rest of the message
*/ */
static bool is_value_str_ascii() { void skip_rest_of_message(void) {
return (memcmp(strings.tmp.tmp, "0x", 2) != 0); states.sign_state = STATE_191_HASH_ONLY;
if (tmpCtx.messageSigningContext.remainingLength > 0) {
apdu_reply(0x9000);
} else {
ui_191_switch_to_sign();
}
} }
/** /**
* Update the global UI string variable by formatting & appending the new data to it * The user has decided to see the next chunk of the message
*
* @param[in] data the input data
* @param[in] length the data length
* @param[in] is_ascii wether the data is ASCII or not
*/ */
static void feed_value_str(const uint8_t *const data, size_t length, bool is_ascii) { void continue_displaying_message(void) {
uint16_t value_strlen = strlen(strings.tmp.tmp); reset_ui_buffer();
if (unprocessed_length() > 0) {
if ((value_strlen + 1) < sizeof(strings.tmp.tmp)) { feed_display();
if (is_ascii) {
uint8_t src_idx = 0;
uint16_t dst_idx = value_strlen;
bool prev_is_special = false;
while ((src_idx < length) && (dst_idx < sizeof(strings.tmp.tmp))) {
if (prev_is_special) {
if (!is_char_special(data[src_idx])) {
prev_is_special = false;
}
} else {
if (is_char_special(data[src_idx])) {
prev_is_special = true;
strings.tmp.tmp[dst_idx] = ' ';
dst_idx += 1;
}
}
if (!is_char_special(data[src_idx])) {
strings.tmp.tmp[dst_idx] = data[src_idx];
dst_idx += 1;
}
src_idx += 1;
}
if (dst_idx < sizeof(strings.tmp.tmp)) {
strings.tmp.tmp[dst_idx] = '\0';
} else {
const char marker[] = "...";
memcpy(strings.tmp.tmp + sizeof(strings.tmp.tmp) - sizeof(marker),
marker,
sizeof(marker));
}
} else {
// truncate to strings.tmp.tmp 's size
length = MIN(length, (sizeof(strings.tmp.tmp) - value_strlen) / 2);
for (size_t i = 0; i < length; i++) {
snprintf(strings.tmp.tmp + value_strlen + 2 * i,
sizeof(strings.tmp.tmp) - value_strlen - 2 * i,
"%02X",
data[i]);
}
}
}
}
void handleSignPersonalMessage(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx) {
UNUSED(tx);
uint8_t hashMessage[INT256_LENGTH];
if (p1 == P1_FIRST) {
char tmp[11] = {0};
uint32_t i;
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
if (appState != APP_STATE_IDLE) {
reset_app_context();
}
appState = APP_STATE_SIGNING_MESSAGE;
tmpCtx.messageSigningContext.pathLength = workBuffer[0];
if ((tmpCtx.messageSigningContext.pathLength < 0x01) ||
(tmpCtx.messageSigningContext.pathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.messageSigningContext.pathLength; i++) {
if (dataLength < sizeof(uint32_t)) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += sizeof(uint32_t);
dataLength -= sizeof(uint32_t);
}
if (dataLength < sizeof(uint32_t)) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext.remainingLength = U4BE(workBuffer, 0);
workBuffer += sizeof(uint32_t);
dataLength -= sizeof(uint32_t);
// Initialize message header + length
cx_keccak_init(&global_sha3, 256);
cx_hash((cx_hash_t *) &global_sha3,
0,
(uint8_t *) SIGN_MAGIC,
sizeof(SIGN_MAGIC) - 1,
NULL,
0);
snprintf(tmp, sizeof(tmp), "%u", tmpCtx.messageSigningContext.remainingLength);
cx_hash((cx_hash_t *) &global_sha3, 0, (uint8_t *) tmp, strlen(tmp), NULL, 0);
cx_sha256_init(&tmpContent.sha2);
init_value_str(is_data_ascii(workBuffer, dataLength));
} else if (p1 != P1_MORE) {
THROW(0x6B00);
}
if (p2 != 0) {
THROW(0x6B00);
}
if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_MESSAGE)) {
PRINTF("Signature not initialized\n");
THROW(0x6985);
}
if (dataLength > tmpCtx.messageSigningContext.remainingLength) {
THROW(0x6A80);
}
cx_hash((cx_hash_t *) &global_sha3, 0, workBuffer, dataLength, NULL, 0);
cx_hash((cx_hash_t *) &tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0);
tmpCtx.messageSigningContext.remainingLength -= dataLength;
feed_value_str(workBuffer, dataLength, is_value_str_ascii());
if (tmpCtx.messageSigningContext.remainingLength == 0) {
cx_hash((cx_hash_t *) &global_sha3,
CX_LAST,
workBuffer,
0,
tmpCtx.messageSigningContext.hash,
32);
cx_hash((cx_hash_t *) &tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32);
#ifdef NO_CONSENT
io_seproxyhal_touch_signMessage_ok(NULL);
#else // NO_CONSENT
ux_flow_init(0, ux_sign_flow, NULL);
#endif // NO_CONSENT
*flags |= IO_ASYNCH_REPLY;
} else {
THROW(0x9000);
} }
} }

View File

@@ -0,0 +1,12 @@
#ifndef SIGN_MESSAGE_H_
#define SIGN_MESSAGE_H_
#define UI_191_BUFFER strings.tmp.tmp
typedef enum { STATE_191_HASH_DISPLAY = 0, STATE_191_HASH_ONLY } sign_message_state;
void question_switcher(void);
void skip_rest_of_message(void);
void continue_displaying_message(void);
#endif // SIGN_MESSAGE_H_

View File

@@ -1,16 +1,15 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "shared_context.h" #include "common_ui.h"
#include "ui_callbacks.h"
unsigned int io_seproxyhal_touch_signMessage_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_signMessage_ok(void) {
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
uint8_t signature[100]; uint8_t signature[100];
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
uint32_t tx = 0; uint32_t tx = 0;
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(CX_CURVE_256K1, os_perso_derive_node_bip32(CX_CURVE_256K1,
tmpCtx.messageSigningContext.bip32Path, tmpCtx.messageSigningContext.bip32.path,
tmpCtx.messageSigningContext.pathLength, tmpCtx.messageSigningContext.bip32.length,
privateKeyData, privateKeyData,
NULL); NULL);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
@@ -46,8 +45,7 @@ unsigned int io_seproxyhal_touch_signMessage_ok(__attribute__((unused)) const ba
return 0; // do not redraw the widget return 0; // do not redraw the widget
} }
unsigned int io_seproxyhal_touch_signMessage_cancel(__attribute__((unused)) unsigned int io_seproxyhal_touch_signMessage_cancel(void) {
const bagl_element_t *e) {
reset_app_context(); reset_app_context();
G_io_apdu_buffer[0] = 0x69; G_io_apdu_buffer[0] = 0x69;
G_io_apdu_buffer[1] = 0x85; G_io_apdu_buffer[1] = 0x85;

View File

@@ -1,44 +0,0 @@
#include "shared_context.h"
#include "ui_callbacks.h"
// clang-format off
UX_STEP_NOCB(
ux_sign_flow_1_step,
pnn,
{
&C_icon_certificate,
"Sign",
"message",
});
UX_STEP_NOCB(
ux_sign_flow_2_step,
bnnn_paging,
{
.title = "Message",
.text = strings.tmp.tmp,
});
UX_STEP_CB(
ux_sign_flow_3_step,
pbb,
io_seproxyhal_touch_signMessage_ok(NULL),
{
&C_icon_validate_14,
"Sign",
"message",
});
UX_STEP_CB(
ux_sign_flow_4_step,
pbb,
io_seproxyhal_touch_signMessage_cancel(NULL),
{
&C_icon_crossmark,
"Cancel",
"signature",
});
// clang-format on
UX_FLOW(ux_sign_flow,
&ux_sign_flow_1_step,
&ux_sign_flow_2_step,
&ux_sign_flow_3_step,
&ux_sign_flow_4_step);

View File

@@ -1,16 +1,14 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "utils.h" #include "utils.h"
#include "ui_flow.h" #include "common_ui.h"
void handleSignEIP712Message(uint8_t p1, void handleSignEIP712Message(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
uint8_t i;
UNUSED(tx); UNUSED(tx);
if ((p1 != 00) || (p2 != 00)) { if ((p1 != 00) || (p2 != 00)) {
THROW(0x6B00); THROW(0x6B00);
@@ -18,38 +16,20 @@ void handleSignEIP712Message(uint8_t p1,
if (appState != APP_STATE_IDLE) { if (appState != APP_STATE_IDLE) {
reset_app_context(); reset_app_context();
} }
if (dataLength < 1) {
PRINTF("Invalid data\n"); workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32);
THROW(0x6a80);
} if (workBuffer == NULL || dataLength < 32 + 32) {
tmpCtx.messageSigningContext712.pathLength = workBuffer[0];
if ((tmpCtx.messageSigningContext712.pathLength < 0x01) ||
(tmpCtx.messageSigningContext712.pathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.messageSigningContext712.pathLength; i++) {
if (dataLength < 4) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.messageSigningContext712.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += 4;
dataLength -= 4;
}
if (dataLength < 32 + 32) {
PRINTF("Invalid data\n");
THROW(0x6a80); THROW(0x6a80);
} }
memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32);
memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32);
#ifdef NO_CONSENT #ifdef NO_CONSENT
io_seproxyhal_touch_signMessage_ok(NULL); io_seproxyhal_touch_signMessage_ok();
#else // NO_CONSENT #else // NO_CONSENT
ux_flow_init(0, ux_sign_712_v0_flow, NULL); ui_sign_712_v0();
#endif // NO_CONSENT #endif // NO_CONSENT
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;

View File

@@ -1,6 +1,6 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "shared_context.h" #include "shared_context.h"
#include "ui_callbacks.h" #include "common_ui.h"
static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01}; static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01};
@@ -34,8 +34,8 @@ unsigned int io_seproxyhal_touch_signMessage712_v0_ok(__attribute__((unused))
PRINTF("EIP712 hash to sign %.*H\n", 32, hash); PRINTF("EIP712 hash to sign %.*H\n", 32, hash);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(CX_CURVE_256K1, os_perso_derive_node_bip32(CX_CURVE_256K1,
tmpCtx.messageSigningContext712.bip32Path, tmpCtx.messageSigningContext712.bip32.path,
tmpCtx.messageSigningContext712.pathLength, tmpCtx.messageSigningContext712.bip32.length,
privateKeyData, privateKeyData,
NULL); NULL);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();

View File

@@ -1,54 +1,43 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "feature_signTx.h" #include "feature_signTx.h"
#include "eth_plugin_interface.h" #include "eth_plugin_interface.h"
void handleSign(uint8_t p1, void handleSign(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *workBuffer, const uint8_t *workBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
UNUSED(tx); UNUSED(tx);
parserStatus_e txResult; parserStatus_e txResult;
uint32_t i;
if (os_global_pin_is_validated() != BOLOS_UX_OK) { if (os_global_pin_is_validated() != BOLOS_UX_OK) {
PRINTF("Device is PIN-locked"); PRINTF("Device is PIN-locked");
THROW(0x6982); THROW(0x6982);
} }
if (p1 == P1_FIRST) { if (p1 == P1_FIRST) {
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
if (appState != APP_STATE_IDLE) { if (appState != APP_STATE_IDLE) {
reset_app_context(); reset_app_context();
} }
appState = APP_STATE_SIGNING_TX; appState = APP_STATE_SIGNING_TX;
tmpCtx.transactionContext.pathLength = workBuffer[0];
if ((tmpCtx.transactionContext.pathLength < 0x01) || workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.transactionContext.bip32);
(tmpCtx.transactionContext.pathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n"); if (workBuffer == NULL) {
THROW(0x6a80); THROW(0x6a80);
} }
workBuffer++;
dataLength--;
for (i = 0; i < tmpCtx.transactionContext.pathLength; i++) {
if (dataLength < 4) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
tmpCtx.transactionContext.bip32Path[i] = U4BE(workBuffer, 0);
workBuffer += 4;
dataLength -= 4;
}
tmpContent.txContent.dataPresent = false; tmpContent.txContent.dataPresent = false;
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL); initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL);
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
// EIP 2718: TransactionType might be present before the TransactionPayload. // EIP 2718: TransactionType might be present before the TransactionPayload.
uint8_t txType = *workBuffer; uint8_t txType = *workBuffer;
if (txType >= MIN_TX_TYPE && txType <= MAX_TX_TYPE) { if (txType >= MIN_TX_TYPE && txType <= MAX_TX_TYPE) {

View File

@@ -1,7 +1,5 @@
#include "shared_context.h" #include "shared_context.h"
#include "utils.h" #include "utils.h"
#include "ui_callbacks.h"
#include "ui_flow.h"
#include "feature_signTx.h" #include "feature_signTx.h"
#ifdef HAVE_STARKWARE #ifdef HAVE_STARKWARE
#include "stark_utils.h" #include "stark_utils.h"
@@ -9,6 +7,8 @@
#include "eth_plugin_handler.h" #include "eth_plugin_handler.h"
#include "network.h" #include "network.h"
#include "ethUtils.h" #include "ethUtils.h"
#include "common_ui.h"
#include "ui_callbacks.h"
#define ERR_SILENT_MODE_CHECK_FAILED 0x6001 #define ERR_SILENT_MODE_CHECK_FAILED 0x6001
@@ -144,7 +144,7 @@ customStatus_e customProcessor(txContext_t *context) {
dataContext.tokenContext.fieldOffset = 0; dataContext.tokenContext.fieldOffset = 0;
if (fieldPos == 0) { if (fieldPos == 0) {
array_hexstr(strings.tmp.tmp, dataContext.tokenContext.data, 4); array_hexstr(strings.tmp.tmp, dataContext.tokenContext.data, 4);
ux_flow_init(0, ux_confirm_selector_flow, NULL); ui_confirm_selector();
} else { } else {
uint32_t offset = 0; uint32_t offset = 0;
uint32_t i; uint32_t i;
@@ -159,7 +159,7 @@ customStatus_e customProcessor(txContext_t *context) {
strings.tmp.tmp[offset++] = ':'; strings.tmp.tmp[offset++] = ':';
} }
} }
ux_flow_init(0, ux_confirm_parameter_flow, NULL); ui_confirm_parameter();
} }
} else { } else {
return CUSTOM_HANDLED; return CUSTOM_HANDLED;
@@ -231,15 +231,26 @@ static void feesToString(uint256_t *rawFee, char *displayBuffer, uint32_t displa
i = 0; i = 0;
tickerOffset = 0; tickerOffset = 0;
memset(displayBuffer, 0, displayBufferSize); memset(displayBuffer, 0, displayBufferSize);
while (feeTicker[tickerOffset]) { while (feeTicker[tickerOffset]) {
if ((uint32_t) tickerOffset >= displayBufferSize) {
break;
}
displayBuffer[tickerOffset] = feeTicker[tickerOffset]; displayBuffer[tickerOffset] = feeTicker[tickerOffset];
tickerOffset++; tickerOffset++;
} }
while (G_io_apdu_buffer[i]) { while (G_io_apdu_buffer[i]) {
if ((uint32_t) (tickerOffset) + i >= displayBufferSize) {
break;
}
displayBuffer[tickerOffset + i] = G_io_apdu_buffer[i]; displayBuffer[tickerOffset + i] = G_io_apdu_buffer[i];
i++; i++;
} }
displayBuffer[tickerOffset + i] = '\0';
if ((uint32_t) (tickerOffset) + i < displayBufferSize) {
displayBuffer[tickerOffset + i] = '\0';
}
} }
// Compute the fees, transform it to a string, prepend a ticker to it and copy everything to // Compute the fees, transform it to a string, prepend a ticker to it and copy everything to
@@ -282,8 +293,8 @@ static void get_public_key(uint8_t *out, uint8_t outLength) {
} }
os_perso_derive_node_bip32(CX_CURVE_256K1, os_perso_derive_node_bip32(CX_CURVE_256K1,
tmpCtx.transactionContext.bip32Path, tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.pathLength, tmpCtx.transactionContext.bip32.length,
privateKeyData, privateKeyData,
NULL); NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);

View File

@@ -1,7 +1,7 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "shared_context.h" #include "shared_context.h"
#include "utils.h" #include "utils.h"
#include "ui_callbacks.h" #include "common_ui.h"
unsigned int io_seproxyhal_touch_tx_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_tx_ok(__attribute__((unused)) const bagl_element_t *e) {
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
@@ -10,8 +10,8 @@ unsigned int io_seproxyhal_touch_tx_ok(__attribute__((unused)) const bagl_elemen
uint32_t tx = 0; uint32_t tx = 0;
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(CX_CURVE_256K1, os_perso_derive_node_bip32(CX_CURVE_256K1,
tmpCtx.transactionContext.bip32Path, tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.pathLength, tmpCtx.transactionContext.bip32.length,
privateKeyData, privateKeyData,
NULL); NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);

View File

@@ -4,37 +4,37 @@
#include "apdu_constants.h" #include "apdu_constants.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "feature_stark_getPublicKey.h" #include "feature_stark_getPublicKey.h"
#include "ui_flow.h" #include "common_ui.h"
#include "os_io_seproxyhal.h"
void handleStarkwareGetPublicKey(uint8_t p1, void handleStarkwareGetPublicKey(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
unsigned int *tx) { unsigned int *tx) {
UNUSED(dataLength); bip32_path_t bip32;
uint8_t privateKeyData[32];
uint32_t bip32Path[MAX_BIP32_PATH];
uint32_t i;
uint8_t bip32PathLength = *(dataBuffer++);
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
uint8_t privateKeyData[32];
reset_app_context(); reset_app_context();
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) { if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00); THROW(0x6B00);
} }
if (p2 != 0) { if (p2 != 0) {
THROW(0x6B00); THROW(0x6B00);
} }
for (i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0); dataBuffer = parseBip32(dataBuffer, &dataLength, &bip32);
dataBuffer += 4;
if (dataBuffer == NULL) {
THROW(0x6a80);
} }
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(bip32Path, bip32PathLength, privateKeyData); starkDerivePrivateKey(bip32.path, bip32.length, privateKeyData);
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_Stark256, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1); cx_ecfp_generate_pair(CX_CURVE_Stark256, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
@@ -56,7 +56,7 @@ void handleStarkwareGetPublicKey(uint8_t p1,
"0x%.*H", "0x%.*H",
32, 32,
tmpCtx.publicKeyContext.publicKey.W + 1); tmpCtx.publicKeyContext.publicKey.W + 1);
ux_flow_init(0, ux_display_stark_public_flow, NULL); ui_display_stark_public();
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;
} }

View File

@@ -1,7 +1,7 @@
#ifdef HAVE_STARKWARE #ifdef HAVE_STARKWARE
#include "shared_context.h" #include "shared_context.h"
#include "ui_callbacks.h" #include "common_ui.h"
#include "feature_stark_getPublicKey.h" #include "feature_stark_getPublicKey.h"
unsigned int io_seproxyhal_touch_stark_pubkey_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_stark_pubkey_ok(__attribute__((unused)) const bagl_element_t *e) {

View File

@@ -2,12 +2,12 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "ui_flow.h"
#include "ethUtils.h" #include "ethUtils.h"
#include "common_ui.h"
void handleStarkwareProvideQuantum(uint8_t p1, void handleStarkwareProvideQuantum(uint8_t p1,
__attribute__((unused)) uint8_t p2, __attribute__((unused)) uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
__attribute__((unused)) unsigned int *flags, __attribute__((unused)) unsigned int *flags,
__attribute__((unused)) unsigned int *tx) { __attribute__((unused)) unsigned int *tx) {
@@ -35,7 +35,7 @@ void handleStarkwareProvideQuantum(uint8_t p1,
THROW(0x6700); THROW(0x6700);
} }
if (p1 == STARK_QUANTUM_LEGACY) { if (p1 == STARK_QUANTUM_LEGACY) {
addressZero = allzeroes(dataBuffer, 20); addressZero = allzeroes((void *) dataBuffer, 20);
} }
if ((p1 != STARK_QUANTUM_ETH) && !addressZero) { if ((p1 != STARK_QUANTUM_ETH) && !addressZero) {
for (i = 0; i < MAX_ITEMS; i++) { for (i = 0; i < MAX_ITEMS; i++) {

View File

@@ -3,10 +3,10 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "ui_flow.h"
#include "poorstream.h" #include "poorstream.h"
#include "ui_callbacks.h"
#include "ethUtils.h" #include "ethUtils.h"
#include "common_ui.h"
#include "os_io_seproxyhal.h"
#define U8BE(buf, off) \ #define U8BE(buf, off) \
(uint64_t)((((uint64_t) U4BE(buf, off)) << 32) | (((uint64_t) U4BE(buf, off + 4)) & 0xFFFFFFFF)) (uint64_t)((((uint64_t) U4BE(buf, off)) << 32) | (((uint64_t) U4BE(buf, off + 4)) & 0xFFFFFFFF))
@@ -20,7 +20,7 @@ void handleStarkwareSignMessage(uint8_t p1,
__attribute__((unused)) unsigned int *tx) { __attribute__((unused)) unsigned int *tx) {
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
uint32_t i; uint32_t i;
uint8_t bip32PathLength = *(dataBuffer); uint8_t bip32PathLength;
uint8_t offset = 1; uint8_t offset = 1;
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
poorstream_t bitstream; poorstream_t bitstream;
@@ -29,10 +29,19 @@ void handleStarkwareSignMessage(uint8_t p1,
uint8_t protocol = 2; uint8_t protocol = 2;
uint8_t preOffset, postOffset; uint8_t preOffset, postOffset;
uint8_t zeroTest; uint8_t zeroTest;
// Initial checks // Initial checks
if (appState != APP_STATE_IDLE) { if (appState != APP_STATE_IDLE) {
reset_app_context(); reset_app_context();
} }
if (dataLength < 1) {
PRINTF("Invalid data\n");
THROW(0x6a80);
}
bip32PathLength = *(dataBuffer);
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) { if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n"); PRINTF("Invalid path\n");
THROW(0x6a80); THROW(0x6a80);
@@ -70,10 +79,10 @@ void handleStarkwareSignMessage(uint8_t p1,
if (p2 != 0) { if (p2 != 0) {
THROW(0x6B00); THROW(0x6B00);
} }
tmpCtx.transactionContext.pathLength = bip32PathLength; tmpCtx.transactionContext.bip32.length = bip32PathLength;
for (i = 0; i < bip32PathLength; i++) { for (i = 0; i < bip32PathLength; i++) {
tmpCtx.transactionContext.bip32Path[i] = U4BE(dataBuffer, offset); tmpCtx.transactionContext.bip32.path[i] = U4BE(dataBuffer, offset);
PRINTF("Storing path %d %d\n", i, tmpCtx.transactionContext.bip32Path[i]); PRINTF("Storing path %d %d\n", i, tmpCtx.transactionContext.bip32.path[i]);
offset += 4; offset += 4;
} }
// Discard the path to use part of dataBuffer as a temporary buffer // Discard the path to use part of dataBuffer as a temporary buffer
@@ -205,7 +214,9 @@ void handleStarkwareSignMessage(uint8_t p1,
cx_ecfp_public_key_t publicKey; cx_ecfp_public_key_t publicKey;
// Check if the transfer is a self transfer // Check if the transfer is a self transfer
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, bip32PathLength, privateKeyData); starkDerivePrivateKey(tmpCtx.transactionContext.bip32.path,
bip32PathLength,
privateKeyData);
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1); cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1);
@@ -238,20 +249,9 @@ void handleStarkwareSignMessage(uint8_t p1,
} }
} }
if (order) { if (order) {
ux_flow_init(0, ux_stark_limit_order_flow, NULL); ui_stark_limit_order();
} else { } else {
if (selfTransfer) { ui_stark_transfer(selfTransfer, dataContext.starkContext.conditional);
ux_flow_init(
0,
(dataContext.starkContext.conditional ? ux_stark_self_transfer_conditional_flow
: ux_stark_self_transfer_flow),
NULL);
} else {
ux_flow_init(0,
(dataContext.starkContext.conditional ? ux_stark_transfer_conditional_flow
: ux_stark_transfer_flow),
NULL);
}
} }
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;

View File

@@ -3,15 +3,15 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "shared_context.h" #include "shared_context.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "ui_callbacks.h" #include "common_ui.h"
unsigned int io_seproxyhal_touch_stark_ok(__attribute__((unused)) const bagl_element_t *e) { unsigned int io_seproxyhal_touch_stark_ok(__attribute__((unused)) const bagl_element_t *e) {
uint8_t privateKeyData[32]; uint8_t privateKeyData[32];
uint8_t signature[72]; uint8_t signature[72];
uint32_t tx = 0; uint32_t tx = 0;
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, starkDerivePrivateKey(tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.pathLength, tmpCtx.transactionContext.bip32.length,
privateKeyData); privateKeyData);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
stark_sign(signature, stark_sign(signature,

View File

@@ -3,46 +3,43 @@
#include "shared_context.h" #include "shared_context.h"
#include "apdu_constants.h" #include "apdu_constants.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "ui_flow.h" #include "common_ui.h"
#include "ui_callbacks.h" #include "os_io_seproxyhal.h"
void handleStarkwareUnsafeSign(uint8_t p1, void handleStarkwareUnsafeSign(uint8_t p1,
uint8_t p2, uint8_t p2,
uint8_t *dataBuffer, const uint8_t *dataBuffer,
uint16_t dataLength, uint16_t dataLength,
unsigned int *flags, unsigned int *flags,
__attribute__((unused)) unsigned int *tx) { __attribute__((unused)) unsigned int *tx) {
uint32_t i;
uint8_t privateKeyData[INT256_LENGTH]; uint8_t privateKeyData[INT256_LENGTH];
cx_ecfp_public_key_t publicKey; cx_ecfp_public_key_t publicKey;
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
uint8_t bip32PathLength = *(dataBuffer);
uint8_t offset = 1;
// Initial checks // Initial checks
if (appState != APP_STATE_IDLE) { if (appState != APP_STATE_IDLE) {
reset_app_context(); reset_app_context();
} }
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
}
if ((p1 != 0) || (p2 != 0)) { if ((p1 != 0) || (p2 != 0)) {
THROW(0x6B00); THROW(0x6B00);
} }
if (dataLength != 32 + 4 * bip32PathLength + 1) { dataBuffer = parseBip32(dataBuffer, &dataLength, &tmpCtx.transactionContext.bip32);
if (dataBuffer == NULL) {
THROW(0x6a80);
}
if (dataLength != 32) {
THROW(0x6700); THROW(0x6700);
} }
tmpCtx.transactionContext.pathLength = bip32PathLength; memmove(dataContext.starkContext.w2, dataBuffer, 32);
for (i = 0; i < bip32PathLength; i++) {
tmpCtx.transactionContext.bip32Path[i] = U4BE(dataBuffer, offset);
PRINTF("Storing path %d %d\n", i, tmpCtx.transactionContext.bip32Path[i]);
offset += 4;
}
memmove(dataContext.starkContext.w2, dataBuffer + offset, 32);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, bip32PathLength, privateKeyData); starkDerivePrivateKey(tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.bip32.length,
privateKeyData);
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1); cx_ecfp_generate_pair(CX_CURVE_Stark256, &publicKey, &privateKey, 1);
@@ -50,7 +47,7 @@ void handleStarkwareUnsafeSign(uint8_t p1,
explicit_bzero(privateKeyData, sizeof(privateKeyData)); explicit_bzero(privateKeyData, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
memmove(dataContext.starkContext.w1, publicKey.W + 1, 32); memmove(dataContext.starkContext.w1, publicKey.W + 1, 32);
ux_flow_init(0, ux_stark_unsafe_sign_flow, NULL); ui_stark_unsafe_sign();
*flags |= IO_ASYNCH_REPLY; *flags |= IO_ASYNCH_REPLY;
} }

View File

@@ -3,7 +3,7 @@
#include "os_io_seproxyhal.h" #include "os_io_seproxyhal.h"
#include "shared_context.h" #include "shared_context.h"
#include "stark_utils.h" #include "stark_utils.h"
#include "ui_callbacks.h" #include "common_ui.h"
unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(__attribute__((unused)) unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(__attribute__((unused))
const bagl_element_t *e) { const bagl_element_t *e) {
@@ -13,8 +13,8 @@ unsigned int io_seproxyhal_touch_stark_unsafe_sign_ok(__attribute__((unused))
unsigned int info = 0; unsigned int info = 0;
uint32_t tx = 0; uint32_t tx = 0;
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
starkDerivePrivateKey(tmpCtx.transactionContext.bip32Path, starkDerivePrivateKey(tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.pathLength, tmpCtx.transactionContext.bip32.length,
privateKeyData); privateKeyData);
io_seproxyhal_io_heartbeat(); io_seproxyhal_io_heartbeat();
cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_Stark256, privateKeyData, 32, &privateKey);

View File

@@ -1,243 +0,0 @@
#include <string.h>
#include "eth_plugin_interface.h"
#include "shared_context.h" // TODO : rewrite as independant code
#include "eth_plugin_internal.h" // TODO : rewrite as independant code
#include "utils.h"
#include "ethUtils.h"
typedef enum {
COMPOUND_REDEEM_UNDERLYING = 0,
COMPOUND_REDEEM,
COMPOUND_MINT,
CETH_MINT
} compoundSelector_t;
static const uint8_t COMPOUND_EXPECTED_DATA_SIZE[] = {
4 + 32,
4 + 32,
4 + 32,
4,
};
// redeemUnderlying : redeemAmount (32)
// redeem underlying token
// redeem : redeemTokens (32)
// redeem Ctoken
// mint : mintAmount (32)
// lend some token
// mint :
// lend some Ether
typedef struct compound_parameters_t {
uint8_t selectorIndex;
uint8_t amount[32];
char ticker_1[MAX_TICKER_LEN];
uint8_t decimals;
} compound_parameters_t;
typedef struct underlying_asset_decimals_t {
char c_ticker[MAX_TICKER_LEN];
uint8_t decimals;
} underlying_asset_decimals_t;
/* Sadly, we don't have the information about the underlying asset's decimals, which can differ from
the cToken decimals. Therefore, we hardcode a binding table. If Compound adds a lot of token in the
future, we will have to move to a CAL based architecture instead, as this one doesn't scale well.*/
#define NUM_COMPOUND_BINDINGS 9
const underlying_asset_decimals_t UNDERLYING_ASSET_DECIMALS[NUM_COMPOUND_BINDINGS] = {
{"cDAI", 18},
{"CETH", 18},
{"CUSDC", 6},
{"CZRX", 18},
{"CUSDT", 6},
{"CBTC", 8},
{"CBAT", 18},
{"CREP", 18},
{"cSAI", 18},
};
bool get_underlying_asset_decimals(char *compound_ticker, uint8_t *out_decimals) {
for (size_t i = 0; i < NUM_COMPOUND_BINDINGS; i++) {
underlying_asset_decimals_t *binding =
(underlying_asset_decimals_t *) PIC(&UNDERLYING_ASSET_DECIMALS[i]);
if (strncmp(binding->c_ticker,
compound_ticker,
strnlen(binding->c_ticker, MAX_TICKER_LEN)) == 0) {
*out_decimals = binding->decimals;
return true;
}
}
return false;
}
void compound_plugin_call(int message, void *parameters) {
switch (message) {
case ETH_PLUGIN_INIT_CONTRACT: {
ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
size_t i;
for (i = 0; i < NUM_COMPOUND_SELECTORS; i++) {
if (memcmp((uint8_t *) PIC(COMPOUND_SELECTORS[i]), msg->selector, SELECTOR_SIZE) ==
0) {
context->selectorIndex = i;
break;
}
}
// enforce that ETH amount should be 0, except in ceth.mint case
if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)) {
if (context->selectorIndex != CETH_MINT) {
PRINTF("Eth amount is not zero and token minted is not CETH!\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
}
if (i == NUM_COMPOUND_SELECTORS) {
PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
if (msg->dataSize != COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex]) {
PRINTF("Unexpected data size for command %d expected %d got %d\n",
context->selectorIndex,
COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex],
msg->dataSize);
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
if (context->selectorIndex == CETH_MINT) {
// ETH amount 0x1234 is stored 0x12340000...000 instead of 0x00....001234, so we
// strip the following zeroes when copying
memset(context->amount, 0, sizeof(context->amount));
memmove(context->amount + sizeof(context->amount) -
msg->pluginSharedRO->txContent->value.length,
msg->pluginSharedRO->txContent->value.value,
msg->pluginSharedRO->txContent->value.length);
}
PRINTF("compound plugin inititialized\n");
msg->result = ETH_PLUGIN_RESULT_OK;
} break;
case ETH_PLUGIN_PROVIDE_PARAMETER: {
ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
PRINTF("compound plugin provide parameter %d %.*H\n",
msg->parameterOffset,
32,
msg->parameter);
if (context->selectorIndex != CETH_MINT) {
switch (msg->parameterOffset) {
case 4:
memmove(context->amount, msg->parameter, 32);
msg->result = ETH_PLUGIN_RESULT_OK;
break;
default:
PRINTF("Unhandled parameter offset\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
break;
}
} else {
PRINTF("CETH contract expects no parameters\n");
msg->result = ETH_PLUGIN_RESULT_ERROR;
}
} break;
case ETH_PLUGIN_FINALIZE: {
ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters;
PRINTF("compound plugin finalize\n");
msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination;
msg->numScreens = 2;
msg->uiType = ETH_UI_TYPE_GENERIC;
msg->result = ETH_PLUGIN_RESULT_OK;
} break;
case ETH_PLUGIN_PROVIDE_INFO: {
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
PRINTF("compound plugin provide token: %d\n", (msg->item1 != NULL));
if (msg->item1 != NULL) {
strlcpy(context->ticker_1, msg->item1->token.ticker, MAX_TICKER_LEN);
switch (context->selectorIndex) {
case COMPOUND_REDEEM_UNDERLYING:
case COMPOUND_MINT:
case CETH_MINT:
msg->result =
get_underlying_asset_decimals(context->ticker_1, &context->decimals)
? ETH_PLUGIN_RESULT_OK
: ETH_PLUGIN_RESULT_FALLBACK;
break;
// Only case where we use the compound contract decimals
case COMPOUND_REDEEM:
context->decimals = msg->item1->token.decimals;
msg->result = ETH_PLUGIN_RESULT_OK;
break;
default:
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
break;
}
} else {
msg->result = ETH_PLUGIN_RESULT_FALLBACK;
}
} break;
case ETH_PLUGIN_QUERY_CONTRACT_ID: {
ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
strlcpy(msg->name, "Type", msg->nameLength);
switch (context->selectorIndex) {
case COMPOUND_REDEEM_UNDERLYING:
case COMPOUND_REDEEM:
strlcpy(msg->version, "Redeem", msg->versionLength);
break;
case COMPOUND_MINT:
case CETH_MINT:
strlcpy(msg->version, "Lend", msg->versionLength);
break;
default:
break;
}
strlcat(msg->version, " Assets", msg->versionLength);
msg->result = ETH_PLUGIN_RESULT_OK;
} break;
case ETH_PLUGIN_QUERY_CONTRACT_UI: {
ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters;
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
switch (msg->screenIndex) {
case 0: {
strlcpy(msg->title, "Amount", msg->titleLength);
char *ticker_ptr = context->ticker_1;
/* skip "c" in front of cToken unless we use "redeem", as
redeem is the only operation dealing with a cToken amount */
if (context->selectorIndex != COMPOUND_REDEEM) {
ticker_ptr++;
}
amountToString(context->amount,
sizeof(context->amount),
context->decimals,
ticker_ptr,
msg->msg,
100);
msg->result = ETH_PLUGIN_RESULT_OK;
} break;
case 1:
strlcpy(msg->title, "Contract", msg->titleLength);
strlcpy(msg->msg, "Compound ", msg->msgLength);
strlcat(msg->msg,
context->ticker_1 + 1,
msg->msgLength); // remove the 'c' char at beginning of compound ticker
msg->result = ETH_PLUGIN_RESULT_OK;
break;
default:
break;
}
} break;
default:
PRINTF("Unhandled message %d\n", message);
}
}

View File

@@ -367,8 +367,8 @@ void starkware_get_source_address(char *destination) {
cx_ecfp_private_key_t privateKey; cx_ecfp_private_key_t privateKey;
cx_ecfp_public_key_t publicKey; cx_ecfp_public_key_t publicKey;
os_perso_derive_node_bip32(CX_CURVE_256K1, os_perso_derive_node_bip32(CX_CURVE_256K1,
tmpCtx.transactionContext.bip32Path, tmpCtx.transactionContext.bip32.path,
tmpCtx.transactionContext.pathLength, tmpCtx.transactionContext.bip32.length,
privateKeyData, privateKeyData,
NULL); NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey); cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);

25
tests/speculos/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
__pycache__/
*.py[cod]
*$py.class
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# generated by pip
pip-wheel-metadata/
# pytest debug logs generated via --debug
pytestdebug.log
.cache
.pytest_cache
.mypy_cache
.coverage
.coverage.*
coverage.xml

50
tests/speculos/README.md Normal file
View File

@@ -0,0 +1,50 @@
# Speculos functional tests
These tests are implemented in Python with the `SpeculosClient` interface which allows easy execution on the [Speculos](https://github.com/LedgerHQ/speculos) emulator.
## Requirements
- [python >= 3.8](https://www.python.org/downloads/)
- [pip](https://pip.pypa.io/en/stable/installation/)
### Dependencies
Python dependencies are listed in [requirements.txt](requirements.txt)
```shell
python3 -m pip install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt
```
> The extra index allows to fetch the latest version of Speculos.
## Usage
### Compilation app
Go to the root of the repository:
```sh
make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOX_SDK
mv bin/app.elf tests/speculos/<some name>.elf
```
Given the requirements are installed, just do (by default command):
```
cd tests/speculos/
pytest
```
### Custom options
- **--model:** "nanos", "nanox", "nanosp" | default: "nanos"
- **--display:** "qt", "headless" | default: "qt"
- **--path:** the path of the binary app | default: path of makefile compilation
## Example
With `nanox` binary app:
```sh
# the --path is variable to where you put your binary
pytest --model nanox --path ./elfs/nanox.elf
# Execute specific test:
pytest --model nanox --path ./elfs/nanox.elf test_pubkey_cmd.py
```

View File

@@ -0,0 +1,41 @@
from pathlib import Path
import pytest
from speculos.client import SpeculosClient
from ethereum_client.ethereum_cmd import EthereumCommand
SCRIPT_DIR = Path(__file__).absolute().parent
API_URL = "http://127.0.0.1:5000"
VERSION = {"nanos": "2.1", "nanox": "2.0.2", "nanosp": "1.0.3"}
def pytest_addoption(parser):
# nanos, nanox, nanosp
parser.addoption("--model", action="store", default="nanos")
# qt: default, requires a X server
# headless: nothing is displayed
parser.addoption("--display", action="store", default="qt")
path: str = SCRIPT_DIR.parent.parent / "bin" / "app.elf"
parser.addoption("--path", action="store", default=path)
@pytest.fixture()
def client(pytestconfig):
file_path = pytestconfig.getoption("path")
model = pytestconfig.getoption("model")
args = ['--log-level', 'speculos:DEBUG','--model', model, '--display', pytestconfig.getoption("display"), '--sdk', VERSION[model]]
with SpeculosClient(app=str(file_path), args=args) as client:
yield client
@pytest.fixture()
def cmd(client, pytestconfig):
yield EthereumCommand(
client=client,
debug=True,
model=pytestconfig.getoption("model"),
)

View File

@@ -0,0 +1,133 @@
# Documentation of Ethereum's client test
```sh
.
├── conftest.py # Configuration for pytest
├── ethereum_client # All utils of client test
│ ├── ethereum_cmd_builder.py # Creation of apdu to send
│ ├── ethereum_cmd.py # Send Apdu and parsing of response
│ ├── exception
│ │ ├── device_exception.py
│ │ └── errors.py
│ ├── plugin.py # Creation of content apdu which manage plugin, erc20Information, provide nft information
│ ├── transaction.py # Creation of content apdu which manage personal tx, transaction, eip712
│ └── utils.py
├── requirements.txt
├── screenshots # All screenshot of nanoS,X,SP for compare in tests
├── setup.cfg
# ========= All Tests =========
├── test_configuration_cmd.py
├── test_eip1559.py
├── test_eip191.py
├── test_eip2930.py
├── test_eip712.py
├── test_erc1155.py
├── test_erc20information.py
├── test_erc721.py
├── test_pubkey_cmd.py
└── test_sign_cmd.py
```
## Ethereum_client
### Ethereum_cmd_builder
```py
def chunked(size, source)
class EthereumCommandBuilder:
# Creation of the apdu
def get_configuration(self) -> bytes:
def set_plugin(self, plugin: Plugin) -> bytes:
def provide_nft_information(self, plugin: Plugin) -> bytes:
def provide_erc20_token_information(self, info: ERC20Information):
def get_public_key(self, bip32_path: str, display: bool = False) -> bytes:
def perform_privacy_operation(self, bip32_path: str, display: bool, shared_secret: bool) -> bytes:
def simple_sign_tx(self, bip32_path: str, transaction: Transaction) -> bytes:
def sign_eip712(self, bip32_path: str, transaction: EIP712) -> bytes:
def personal_sign_tx(self, bip32_path: str, transaction: PersonalTransaction) -> Tuple[bool,bytes]:
```
### Ethereum_cmd
```py
class EthereumCommand:
# Sending apdu and parsing the response in the right form
def get_configuration(self) -> Tuple[int, int, int, int]:
def set_plugin(self, plugin: Plugin):
def provide_nft_information(self, plugin: Plugin):
def provide_erc20_token_information(self, info: ERC20Information):
def get_public_key(self, bip32_path: str, result: List, display: bool = False) -> Tuple[bytes, bytes, bytes]:
def perform_privacy_operation(self, bip32_path: str, result: List, display: bool = False, shared_secret: bool = False) -> Tuple[bytes, bytes, bytes]:
def simple_sign_tx(self, bip32_path: str, transaction: Transaction, result: List = list()) -> None:
def sign_eip712(self, bip32_path: str, transaction: EIP712, result: List = list()) -> None:
def personal_sign_tx(self, bip32_path: str, transaction: PersonalTransaction, result: List = list()) -> None:
# Allows to send an apdu without return of speculos
def send_apdu(self, apdu: bytes) -> bytes:
# Allows to send an apdu with return of speculos
def send_apdu_context(self, apdu: bytes, result: List = list()) -> bytes:
```
### Utils
```py
def save_screenshot(cmd, path: str):
def compare_screenshot(cmd, path: str):
def parse_sign_response(response : bytes) -> Tuple[bytes, bytes, bytes]:
def bip32_path_from_string(path: str) -> List[bytes]:
def packed_bip32_path_from_string(path: str) -> bytes:
def write_varint(n: int) -> bytes:
def read_varint(buf: BytesIO, prefix: Optional[bytes] = None) -> int:
def read(buf: BytesIO, size: int) -> bytes:
def read_uint(buf: BytesIO,
```
## Tests new apdu
If a new instruction is programmed it will be necessary to create 2 new functions.
one in `ethereum_cmd_builder` :
- Creation of the raw apdu you can find some examples in this same file
and one in `ethereum_cmd`:
- Send the apdu to speculos and parse the answer in a `list` named result you can find some examples in this same file
## Example for write new tests
To send several apdu and get the return
```py
FIRST = bytes.fromhex("{YourAPDU}")
SECOND = bytes.fromhex("{YourAPDU}")
def test_multiple_raw_apdu(cmd):
result: list = []
cmd.send_apdu(FIRST)
with cmd.send_apdu_context(SECOND, result) as ex:
sleep(0.5)
# Here your code for press button and compare screen if you want
response: bytes = result[0] # response returning
# Here you function to parse response of some code
v, r, s = parse_sign_response(response)
# And here assertion of your tests
assert v == 0x25 # 37
assert r.hex() == "68ba082523584adbfc31d36d68b51d6f209ce0838215026bf1802a8f17dcdff4"
assert s.hex() == "7c92908fa05c8bc86507a3d6a1c8b3c2722ee01c836d89a61df60c1ab0b43fff"
```
To test an error
```py
def test_some_error(cmd):
result: list = []
with pytest.raises(ethereum_client.exception.errors.UnknownDeviceError) as error:
# With an function in ethereum_cmd
with cmd.send_apdu_context(bytes.fromhex("{YourAPDU}"), result) as ex:
pass
assert error.args[0] == '0x6a80'
```

View File

@@ -0,0 +1,226 @@
from ast import List
from contextlib import contextmanager
import struct
from time import sleep
from typing import Tuple
from speculos.client import SpeculosClient, ApduException
from ethereum_client.ethereum_cmd_builder import EthereumCommandBuilder, InsType
from ethereum_client.exception import DeviceException
from ethereum_client.transaction import EIP712, PersonalTransaction, Transaction
from ethereum_client.plugin import ERC20Information, Plugin
from ethereum_client.utils import parse_sign_response
class EthereumCommand:
def __init__(self,
client: SpeculosClient,
debug: bool = False,
model: str = "nanos") -> None:
self.client = client
self.builder = EthereumCommandBuilder(debug=debug)
self.debug = debug
self.model = model
def get_configuration(self) -> Tuple[int, int, int, int]:
try:
response = self.client._apdu_exchange(
self.builder.get_configuration()
) # type: int, bytes
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_GET_VERSION)
# response = FLAG (1) || MAJOR (1) || MINOR (1) || PATCH (1)
assert len(response) == 4
info, major, minor, patch = struct.unpack(
"BBBB",
response
) # type: int, int, int
return info, major, minor, patch
def set_plugin(self, plugin: Plugin):
try:
self.client._apdu_exchange(
self.builder.set_plugin(plugin=plugin)
)
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SET_PLUGIN)
def provide_nft_information(self, plugin: Plugin):
try:
self.client._apdu_exchange(
self.builder.provide_nft_information(plugin=plugin)
)
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_PROVIDE_NFT_INFORMATION)
def provide_erc20_token_information(self, info: ERC20Information):
try:
self.client._apdu_exchange(
self.builder.provide_erc20_token_information(info=info)
)
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_PROVIDE_ERC20)
@contextmanager
def get_public_key(self, bip32_path: str, result: List, display: bool = False) -> Tuple[bytes, bytes, bytes]:
try:
chunk: bytes = self.builder.get_public_key(bip32_path=bip32_path, display=display)
with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1],
p1=chunk[2], p2=chunk[3],
data=chunk[5:]) as exchange:
yield exchange
response: bytes = exchange.receive()
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_GET_PUBLIC_KEY)
# response = pub_key_len (1) ||
# pub_key (var) ||
# chain_code_len (1) ||
# chain_code (var)
offset: int = 0
pub_key_len: int = response[offset]
offset += 1
uncompressed_addr_len: bytes = response[offset:offset + pub_key_len]
offset += pub_key_len
eth_addr_len: int = response[offset]
offset += 1
eth_addr: bytes = response[offset:offset + eth_addr_len]
offset += eth_addr_len
chain_code: bytes = response[offset:]
assert len(response) == 1 + pub_key_len + 1 + eth_addr_len + 32 # 32 -> chain_code_len
result.append(uncompressed_addr_len)
result.append(eth_addr)
result.append(chain_code)
@contextmanager
def perform_privacy_operation(self, bip32_path: str, result: List, display: bool = False, shared_secret: bool = False) -> Tuple[bytes, bytes, bytes]:
try:
chunk: bytes = self.builder.perform_privacy_operation(bip32_path=bip32_path, display=display, shared_secret=shared_secret)
with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1],
p1=chunk[2], p2=chunk[3],
data=chunk[5:]) as exchange:
yield exchange
response: bytes = exchange.receive()
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_PERFORM_PRIVACY_OPERATION)
# response = Public encryption key or shared secret (32)
assert len(response) == 32
result.append(response)
def send_apdu(self, apdu: bytes) -> bytes:
try:
self.client.apdu_exchange(cla=apdu[0], ins=apdu[1],
p1=apdu[2], p2=apdu[3],
data=apdu[5:])
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX)
@contextmanager
def send_apdu_context(self, apdu: bytes, result: List = list()) -> bytes:
try:
with self.client.apdu_exchange_nowait(cla=apdu[0], ins=apdu[1],
p1=apdu[2], p2=apdu[3],
data=apdu[5:]) as exchange:
yield exchange
result.append(exchange.receive())
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX)
@contextmanager
def simple_sign_tx(self, bip32_path: str, transaction: Transaction, result: List = list()) -> None:
try:
chunk: bytes = self.builder.simple_sign_tx(bip32_path=bip32_path, transaction=transaction)
with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1],
p1=chunk[2], p2=chunk[3],
data=chunk[5:]) as exchange:
yield exchange
response: bytes = exchange.receive()
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX)
# response = V (1) || R (32) || S (32)
assert len(response) == 65
v, r, s = parse_sign_response(response)
result.append(v)
result.append(r)
result.append(s)
@contextmanager
def sign_eip712(self, bip32_path: str, transaction: EIP712, result: List = list()) -> None:
try:
chunk: bytes = self.builder.sign_eip712(bip32_path=bip32_path, transaction=transaction)
with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1],
p1=chunk[2], p2=chunk[3],
data=chunk[5:]) as exchange:
yield exchange
response: bytes = exchange.receive()
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_EIP712)
# response = V (1) || R (32) || S (32)
assert len(response) == 65
v, r, s = parse_sign_response(response)
result.append(v)
result.append(r)
result.append(s)
@contextmanager
def personal_sign_tx(self, bip32_path: str, transaction: PersonalTransaction, result: List = list()) -> None:
try:
for islast_apdu, apdu in self.builder.personal_sign_tx(bip32_path=bip32_path, transaction=transaction):
if islast_apdu:
with self.client.apdu_exchange_nowait(cla=apdu[0], ins=apdu[1],
p1=apdu[2], p2=apdu[3],
data=apdu[5:]) as exchange:
# the "yield" here allows to wait for a button interaction (click right, left, both)
yield exchange
response: bytes = exchange.receive()
else:
self.send_apdu(apdu)
except ApduException as error:
raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX)
# response = V (1) || R (32) || S (32)
v, r, s = parse_sign_response(response)
result.append(v)
result.append(r)
result.append(s)

View File

@@ -0,0 +1,293 @@
import enum
import logging
import struct
from typing import List, Tuple, Union, Iterator, cast
from ethereum_client.transaction import EIP712, PersonalTransaction, Transaction
from ethereum_client.plugin import ERC20Information, Plugin
from ethereum_client.utils import packed_bip32_path_from_string
MAX_APDU_LEN: int = 255
def chunked(size, source):
for i in range(0, len(source), size):
yield source[i:i+size]
def chunkify(data: bytes, chunk_len: int) -> Iterator[Tuple[bool, bytes]]:
size: int = len(data)
if size <= chunk_len:
yield True, data
return
chunk: int = size // chunk_len
remaining: int = size % chunk_len
offset: int = 0
for i in range(chunk):
yield False, data[offset:offset + chunk_len]
offset += chunk_len
if remaining:
yield True, data[offset:]
class InsType(enum.IntEnum):
INS_GET_PUBLIC_KEY = 0x02
INS_SIGN_TX = 0x04
INS_GET_CONFIGURATION = 0x06
INS_SIGN_PERSONAL_TX = 0x08
INS_PROVIDE_ERC20 = 0x0A
INS_SIGN_EIP712 = 0x0c
INS_ETH2_GET_PUBLIC_KEY = 0x0E
INS_SET_ETH2_WITHDRAWAL = 0x10
INS_SET_EXTERNAL_PLUGIN = 0x12
INS_PROVIDE_NFT_INFORMATION = 0x14
INS_SET_PLUGIN = 0x16
INS_PERFORM_PRIVACY_OPERATION = 0x18
class EthereumCommandBuilder:
"""APDU command builder for the Boilerplate application.
Parameters
----------
debug: bool
Whether you want to see logging or not.
Attributes
----------
debug: bool
Whether you want to see logging or not.
"""
CLA: int = 0xE0
def __init__(self, debug: bool = False):
"""Init constructor."""
self.debug = debug
def serialize(self,
cla: int,
ins: Union[int, enum.IntEnum],
p1: int = 0,
p2: int = 0,
cdata: bytes = b"") -> bytes:
"""Serialize the whole APDU command (header + data).
Parameters
----------
cla : int
Instruction class: CLA (1 byte)
ins : Union[int, IntEnum]
Instruction code: INS (1 byte)
p1 : int
Instruction parameter 1: P1 (1 byte).
p2 : int
Instruction parameter 2: P2 (1 byte).
cdata : bytes
Bytes of command data.
Returns
-------
bytes
Bytes of a complete APDU command.
"""
ins = cast(int, ins.value) if isinstance(ins, enum.IntEnum) else cast(int, ins)
header: bytes = struct.pack("BBBBB",
cla,
ins,
p1,
p2,
len(cdata)) # add Lc to APDU header
if self.debug:
logging.info("header: %s", header.hex())
logging.info("cdata: %s", cdata.hex())
return header + cdata
def get_configuration(self) -> bytes:
"""Command builder for GET_CONFIGURATON
Returns
-------
bytes
APDU command for GET_CONFIGURATON
"""
return self.serialize(cla=self.CLA,
ins=InsType.INS_GET_CONFIGURATION,
p1=0x00,
p2=0x00,
cdata=b"")
def _same_header_builder(self, data: Union[Plugin, ERC20Information], ins: int) -> bytes:
return self.serialize(cla=self.CLA,
ins=ins,
p1=0x00,
p2=0x00,
cdata=data.serialize())
def set_plugin(self, plugin: Plugin) -> bytes:
return self._same_header_builder(plugin, InsType.INS_SET_PLUGIN)
def provide_nft_information(self, plugin: Plugin) -> bytes:
return self._same_header_builder(plugin, InsType.INS_PROVIDE_NFT_INFORMATION)
def provide_erc20_token_information(self, info: ERC20Information):
return self._same_header_builder(info, InsType.INS_PROVIDE_ERC20)
def get_public_key(self, bip32_path: str, display: bool = False) -> bytes:
"""Command builder for GET_PUBLIC_KEY.
Parameters
----------
bip32_path: str
String representation of BIP32 path.
display : bool
Whether you want to display the address on the device.
Returns
-------
bytes
APDU command for GET_PUBLIC_KEY.
"""
cdata = packed_bip32_path_from_string(bip32_path)
return self.serialize(cla=self.CLA,
ins=InsType.INS_GET_PUBLIC_KEY,
p1=0x01 if display else 0x00,
p2=0x01,
cdata=cdata)
def perform_privacy_operation(self, bip32_path: str, display: bool, shared_secret: bool) -> bytes:
"""Command builder for INS_PERFORM_PRIVACY_OPERATION.
Parameters
----------
bip32_path : str
String representation of BIP32 path.
Third party public key on Curve25519 : 32 bytes
Optionnal if returning the shared secret
"""
cdata = packed_bip32_path_from_string(bip32_path)
return self.serialize(cla=self.CLA,
ins=InsType.INS_PERFORM_PRIVACY_OPERATION,
p1=0x01 if display else 0x00,
p2=0x01 if shared_secret else 0x00,
cdata=cdata)
def simple_sign_tx(self, bip32_path: str, transaction: Transaction) -> bytes:
"""Command builder for INS_SIGN_TX.
Parameters
----------
bip32_path : str
String representation of BIP32 path.
transaction : Transaction
Representation of the transaction to be signed.
Yields
-------
bytes
APDU command chunk for INS_SIGN_TX.
"""
cdata = packed_bip32_path_from_string(bip32_path)
tx: bytes = transaction.serialize()
cdata = cdata + tx
return self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_TX,
p1=0x00,
p2=0x00,
cdata=cdata)
def sign_eip712(self, bip32_path: str, transaction: EIP712) -> bytes:
"""Command builder for INS_SIGN_EIP712.
Parameters
----------
bip32_path : str
String representation of BIP32 path.
transaction : EIP712
Domain hash -> 32 bytes
Message hash -> 32 bytes
Yields
-------
bytes
APDU command chunk for INS_SIGN_EIP712.
"""
cdata = packed_bip32_path_from_string(bip32_path)
tx: bytes = transaction.serialize()
cdata = cdata + tx
return self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_EIP712,
p1=0x00,
p2=0x00,
cdata=cdata)
def personal_sign_tx(self, bip32_path: str, transaction: PersonalTransaction) -> Tuple[bool,bytes]:
"""Command builder for INS_SIGN_PERSONAL_TX.
Parameters
----------
bip32_path : str
String representation of BIP32 path.
transaction : Transaction
Representation of the transaction to be signed.
Yields
-------
bytes
APDU command chunk for INS_SIGN_PERSONAL_TX.
"""
cdata = packed_bip32_path_from_string(bip32_path)
tx: bytes = transaction.serialize()
cdata = cdata + tx
last_chunk = len(cdata) // MAX_APDU_LEN
# The generator allows to send apdu frames because we can't send an apdu > 255
for i, (chunk) in enumerate(chunked(MAX_APDU_LEN, cdata)):
if i == 0 and i == last_chunk:
yield True, self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_PERSONAL_TX,
p1=0x00,
p2=0x00,
cdata=chunk)
elif i == 0:
yield False, self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_PERSONAL_TX,
p1=0x00,
p2=0x00,
cdata=chunk)
elif i == last_chunk:
yield True, self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_PERSONAL_TX,
p1=0x80,
p2=0x00,
cdata=chunk)
else:
yield False, self.serialize(cla=self.CLA,
ins=InsType.INS_SIGN_PERSONAL_TX,
p1=0x80,
p2=0x00,
cdata=chunk)

View File

@@ -0,0 +1,35 @@
from .device_exception import DeviceException
from .errors import (UnknownDeviceError,
DenyError,
WrongP1P2Error,
WrongDataLengthError,
InsNotSupportedError,
ClaNotSupportedError,
WrongResponseLengthError,
DisplayBip32PathFailError,
DisplayAddressFailError,
DisplayAmountFailError,
WrongTxLengthError,
TxParsingFailError,
TxHashFail,
BadStateError,
SignatureFailError)
__all__ = [
"DeviceException",
"DenyError",
"UnknownDeviceError",
"WrongP1P2Error",
"WrongDataLengthError",
"InsNotSupportedError",
"ClaNotSupportedError",
"WrongResponseLengthError",
"DisplayBip32PathFailError",
"DisplayAddressFailError",
"DisplayAmountFailError",
"WrongTxLengthError",
"TxParsingFailError",
"TxHashFail",
"BadStateError",
"SignatureFailError"
]

View File

@@ -0,0 +1,38 @@
import enum
from typing import Dict, Any, Union
from .errors import *
class DeviceException(Exception): # pylint: disable=too-few-public-methods
exc: Dict[int, Any] = {
0x6985: DenyError,
0x6A86: WrongP1P2Error,
0x6A87: WrongDataLengthError,
0x6D00: InsNotSupportedError,
0x6E00: ClaNotSupportedError,
0xB000: WrongResponseLengthError,
0xB001: DisplayBip32PathFailError,
0xB002: DisplayAddressFailError,
0xB003: DisplayAmountFailError,
0xB004: WrongTxLengthError,
0xB005: TxParsingFailError,
0xB006: TxHashFail,
0xB007: BadStateError,
0xB008: SignatureFailError
}
def __new__(cls,
error_code: int,
ins: Union[int, enum.IntEnum, None] = None,
message: str = ""
) -> Any:
error_message: str = (f"Error in {ins!r} command"
if ins else "Error in command")
if error_code in DeviceException.exc:
return DeviceException.exc[error_code](hex(error_code),
error_message,
message)
return UnknownDeviceError(hex(error_code), error_message, message)

Some files were not shown because too many files have changed in this diff Show More