diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml new file mode 100644 index 00000000..f10d4e3e --- /dev/null +++ b/.github/workflows/build_and_functional_tests.yml @@ -0,0 +1,33 @@ +name: Build and run functional tests using ragger through reusable workflow + +# This workflow will build the app and then run functional tests using the Ragger framework upon Speculos emulation. +# It calls a reusable workflow developed by Ledger's internal developer team to build the application and upload the +# resulting binaries. +# It then calls another reusable workflow to run the Ragger tests on the compiled application binary. +# +# While this workflow is optional, having functional testing on your application is mandatory and this workflow and +# tooling environment is meant to be easy to use and adapt after forking your application + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + flags: "COIN=bitcoin_testnet" + + ragger_tests: + name: Run ragger tests using the reusable workflow + needs: build_application + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1 + with: + download_app_binaries_artifact: "compiled_app_binaries" diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml deleted file mode 100644 index 6fd8555c..00000000 --- a/.github/workflows/ci-workflow.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: CI - -on: - workflow_dispatch: - push: - branches: - - master - pull_request: - branches: - - master - - -jobs: - job_build: - name: Compilation - strategy: - matrix: - include: - - model: nanos - SDK: "$NANOS_SDK" - - model: nanox - SDK: "$NANOX_SDK" - - model: nanosp - SDK: "$NANOSP_SDK" - - model: stax - SDK: "$STAX_SDK" - - runs-on: ubuntu-latest - - container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Build - run: | - make BOLOS_SDK=${{ matrix.SDK }} && mv bin/ bitcoin-bin/ - make clean - make COIN=bitcoin_testnet_legacy BOLOS_SDK=${{ matrix.SDK }} && mv bin/ bitcoin-testnet-bin/ - - - name: Upload Bitcoin app binary - uses: actions/upload-artifact@v2 - with: - name: bitcoin-app-${{ matrix.model }} - path: bitcoin-bin - - - name: Upload Bitcoin Testnet app binary - uses: actions/upload-artifact@v2 - with: - name: bitcoin-testnet-app-${{ matrix.model }} - path: bitcoin-testnet-bin - - job_test: - name: Tests - strategy: - matrix: - include: - - model: nanos - - model: nanox - - model: nanosp - - model: stax - - needs: job_build - runs-on: ubuntu-latest - - container: - image: ghcr.io/ledgerhq/speculos:latest - ports: - - 1234:1234 - - 9999:9999 - - 40000:40000 - - 41000:41000 - - 42000:42000 - - 43000:43000 - options: --entrypoint /bin/bash - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Download Bitcoin app binary - uses: actions/download-artifact@v2 - with: - name: bitcoin-app-${{matrix.model}} - path: tests/bitcoin-bin - - - name: Download Bitcoin Testnet app binary - uses: actions/download-artifact@v2 - with: - name: bitcoin-testnet-app-${{ matrix.model }} - path: tests/bitcoin-testnet-bin - - - name: Run tests - run: | - cd tests && pip install -r requirements.txt && SPECULOS=/speculos/speculos.py pytest --model=${{ matrix.model }} - diff --git a/Makefile b/Makefile index 08a0fda0..707c4e1b 100644 --- a/Makefile +++ b/Makefile @@ -15,61 +15,20 @@ # limitations under the License. # **************************************************************************** -ifeq ($(BOLOS_SDK),) -$(error Environment variable BOLOS_SDK is not set) -endif -include $(BOLOS_SDK)/Makefile.defines - ######################################## # Mandatory configuration # ######################################## -# Name is defined later # Application version APPVERSION_M = 2 -APPVERSION_N = 3 +APPVERSION_N = 4 APPVERSION_P = 0 -APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" - -# Application source files -APP_SOURCE_PATH += src - -ICON_NANOS = icons/nanos_app_$(COIN).gif -ICON_NANOX = icons/nanox_app_$(COIN).gif -ICON_NANOSP = icons/nanox_app_$(COIN).gif -ICON_STAX = icons/stax_app_$(COIN).gif - -# Application allowed derivation curves. -CURVE_APP_LOAD_PARAMS = secp256k1 -# Application allowed derivation paths. -ifeq ($(COIN),$(filter $(COIN), hydra_testnet hydra)) -PATH_APP_LOAD_PARAMS = "44'/609'" -else -PATH_APP_LOAD_PARAMS = "" -endif - -HAVE_APPLICATION_FLAG_DERIVE_MASTER = 1 -HAVE_APPLICATION_FLAG_LIBRARY = 1 - -VARIANT_PARAM = COIN -VARIANT_VALUES = BOL - -# Temporary restriction until we have a Resistance Nano X icon -ifeq ($(TARGET_NAME),TARGET_NANOS) -VARIANT_VALUES = bitcoin_testnet_legacy bitcoin_legacy bitcoin_cash bitcoin_gold litecoin dogecoin dash horizen komodo stratis peercoin pivx viacoin vertcoin stealth digibyte bitcoin_private firo gamecredits zclassic xsn nix lbry ravencoin resistance hydra hydra_testnet xrhodium -else VARIANT_VALUES = bitcoin_testnet_legacy bitcoin_legacy bitcoin_cash bitcoin_gold litecoin dogecoin dash horizen komodo stratis peercoin pivx viacoin vertcoin stealth digibyte bitcoin_private firo gamecredits zclassic xsn nix lbry ravencoin hydra hydra_testnet xrhodium -endif - -ENABLE_BLUETOOTH = 1 -ENABLE_NBGL_QRCODE = 1 -ENABLE_SWAP = 1 -ifeq ($(TARGET_NAME),TARGET_STAX) -DEFINES += COIN_ICON=C_$(COIN)_64px -DEFINES += COIN_ICON_BITMAP=C_$(COIN)_64px_bitmap -endif +# Application source files +# There is no additional sources for bitcoin +#APP_SOURCE_PATH += src/ # simplify for tests ifndef COIN @@ -80,135 +39,450 @@ endif #DEBUG = 1 ifeq ($(COIN),bitcoin_testnet_legacy) -# Bitcoin testnet -DEFINES += BIP44_COIN_TYPE=1 BIP44_COIN_TYPE_2=1 COIN_P2PKH_VERSION=111 COIN_P2SH_VERSION=196 COIN_FAMILY=1 COIN_COINID=\"Bitcoin\" COIN_COINID_HEADER=\"BITCOIN\" COIN_COINID_NAME=\"Bitcoin\" COIN_COINID_SHORT=\"TEST\" COIN_NATIVE_SEGWIT_PREFIX=\"tb\" COIN_KIND=COIN_KIND_BITCOIN_TESTNET COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=1 +BIP44_COIN_TYPE_2=1 +COIN_P2PKH_VERSION=111 +COIN_P2SH_VERSION=196 +COIN_FAMILY=1 +COIN_COINID=\"Bitcoin\" +COIN_COINID_HEADER=\"BITCOIN\" +COIN_COINID_NAME=\"Bitcoin\" +COIN_COINID_SHORT=\"TEST\" +COIN_NATIVE_SEGWIT_PREFIX=\"tb\" +COIN_KIND=COIN_KIND_BITCOIN_TESTNET +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Bitcoin Test Legacy" + else ifeq ($(COIN),bitcoin_legacy) -# Bitcoin mainnet -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=0 COIN_P2SH_VERSION=5 COIN_FAMILY=1 COIN_COINID=\"Bitcoin\" COIN_COINID_HEADER=\"BITCOIN\" COIN_COINID_NAME=\"Bitcoin\" COIN_COINID_SHORT=\"BTC\" COIN_NATIVE_SEGWIT_PREFIX=\"bc\" COIN_KIND=COIN_KIND_BITCOIN COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +# Horizen +BIP44_COIN_TYPE=0 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=0 +COIN_P2SH_VERSION=5 +COIN_FAMILY=1 +COIN_COINID=\"Bitcoin\" +COIN_COINID_HEADER=\"BITCOIN\" +COIN_COINID_NAME=\"Bitcoin\" +COIN_COINID_SHORT=\"BTC\" +COIN_NATIVE_SEGWIT_PREFIX=\"bc\" +COIN_KIND=COIN_KIND_BITCOIN +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Bitcoin Legacy" -#LIB and global pin and + else ifeq ($(COIN),bitcoin_cash) # Bitcoin cash # Initial fork from Bitcoin, public key access is authorized. Signature is different thanks to the forkId -DEFINES += BIP44_COIN_TYPE=145 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=0 COIN_P2SH_VERSION=5 COIN_FAMILY=1 COIN_COINID=\"Bitcoin\" COIN_COINID_HEADER=\"BITCOINCASH\" COIN_COINID_NAME=\"BitcoinCash\" COIN_COINID_SHORT=\"BCH\" COIN_KIND=COIN_KIND_BITCOIN_CASH COIN_FORKID=0 +BIP44_COIN_TYPE=145 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=0 +COIN_P2SH_VERSION=5 +COIN_FAMILY=1 +COIN_COINID=\"Bitcoin\" +COIN_COINID_HEADER=\"BITCOINCASH\" +COIN_COINID_NAME=\"BitcoinCash\" +COIN_COINID_SHORT=\"BCH\" +COIN_KIND=COIN_KIND_BITCOIN_CASH +COIN_FORKID=0 APPNAME ="Bitcoin Cash" + else ifeq ($(COIN),bitcoin_gold) # Bitcoin Gold # Initial fork from Bitcoin, public key access is authorized. Signature is different thanks to the forkId -DEFINES += BIP44_COIN_TYPE=156 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=38 COIN_P2SH_VERSION=23 COIN_FAMILY=1 COIN_COINID=\"Bitcoin\\x20Gold\" COIN_COINID_HEADER=\"BITCOINGOLD\" COIN_COINID_NAME=\"BitcoinGold\" COIN_COINID_SHORT=\"BTG\" COIN_KIND=COIN_KIND_BITCOIN_GOLD COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT COIN_FORKID=79 +BIP44_COIN_TYPE=156 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=38 +COIN_P2SH_VERSION=23 +COIN_FAMILY=1 +COIN_COINID=\"Bitcoin\\x20Gold\" +COIN_COINID_HEADER=\"BITCOINGOLD\" +COIN_COINID_NAME=\"BitcoinGold\" +COIN_COINID_SHORT=\"BTG\" +COIN_KIND=COIN_KIND_BITCOIN_GOLD +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +COIN_FORKID=79 APPNAME ="Bitcoin Gold" + else ifeq ($(COIN),litecoin) # Litecoin -DEFINES += BIP44_COIN_TYPE=2 BIP44_COIN_TYPE_2=2 COIN_P2PKH_VERSION=48 COIN_P2SH_VERSION=50 COIN_FAMILY=1 COIN_COINID=\"Litecoin\" COIN_COINID_HEADER=\"LITECOIN\" COIN_COINID_NAME=\"Litecoin\" COIN_COINID_SHORT=\"LTC\" COIN_NATIVE_SEGWIT_PREFIX=\"ltc\" COIN_KIND=COIN_KIND_LITECOIN COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=2 +BIP44_COIN_TYPE_2=2 +COIN_P2PKH_VERSION=48 +COIN_P2SH_VERSION=50 +COIN_FAMILY=1 +COIN_COINID=\"Litecoin\" +COIN_COINID_HEADER=\"LITECOIN\" +COIN_COINID_NAME=\"Litecoin\" +COIN_COINID_SHORT=\"LTC\" +COIN_NATIVE_SEGWIT_PREFIX=\"ltc\" +COIN_KIND=COIN_KIND_LITECOIN +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Litecoin" + else ifeq ($(COIN),dogecoin) # Doge -DEFINES += BIP44_COIN_TYPE=3 BIP44_COIN_TYPE_2=3 COIN_P2PKH_VERSION=30 COIN_P2SH_VERSION=22 COIN_FAMILY=1 COIN_COINID=\"Dogecoin\" COIN_COINID_HEADER=\"DOGECOIN\" COIN_COINID_NAME=\"Dogecoin\" COIN_COINID_SHORT=\"DOGE\" COIN_KIND=COIN_KIND_DOGE +BIP44_COIN_TYPE=3 +BIP44_COIN_TYPE_2=3 +COIN_P2PKH_VERSION=30 +COIN_P2SH_VERSION=22 +COIN_FAMILY=1 +COIN_COINID=\"Dogecoin\" +COIN_COINID_HEADER=\"DOGECOIN\" +COIN_COINID_NAME=\"Dogecoin\" +COIN_COINID_SHORT=\"DOGE\" +COIN_KIND=COIN_KIND_DOGE APPNAME ="Dogecoin" + else ifeq ($(COIN),dash) # Dash -DEFINES += BIP44_COIN_TYPE=5 BIP44_COIN_TYPE_2=5 COIN_P2PKH_VERSION=76 COIN_P2SH_VERSION=16 COIN_FAMILY=1 COIN_COINID=\"DarkCoin\" COIN_COINID_HEADER=\"DASH\" COIN_COINID_NAME=\"Dash\" COIN_COINID_SHORT=\"DASH\" COIN_KIND=COIN_KIND_DASH +BIP44_COIN_TYPE=5 +BIP44_COIN_TYPE_2=5 +COIN_P2PKH_VERSION=76 +COIN_P2SH_VERSION=16 +COIN_FAMILY=1 +COIN_COINID=\"DarkCoin\" +COIN_COINID_HEADER=\"DASH\" +COIN_COINID_NAME=\"Dash\" +COIN_COINID_SHORT=\"DASH\" +COIN_KIND=COIN_KIND_DASH APPNAME ="Dash" + else ifeq ($(COIN),zcash) # Zcash (deprecated, code before the NU5 hard fork) $(error the zcash variant is deprecated and no longer functional since the NU5 hard fork) -DEFINES += BIP44_COIN_TYPE=133 BIP44_COIN_TYPE_2=133 COIN_P2PKH_VERSION=7352 COIN_P2SH_VERSION=7357 COIN_FAMILY=1 COIN_COINID=\"Zcash\" COIN_COINID_HEADER=\"ZCASH\" COIN_COINID_NAME=\"Zcash\" COIN_COINID_SHORT=\"ZEC\" COIN_KIND=COIN_KIND_ZCASH +BIP44_COIN_TYPE=133 +BIP44_COIN_TYPE_2=133 +COIN_P2PKH_VERSION=7352 +COIN_P2SH_VERSION=7357 +COIN_FAMILY=1 +COIN_COINID=\"Zcash\" +COIN_COINID_HEADER=\"ZCASH\" +COIN_COINID_NAME=\"Zcash\" +COIN_COINID_SHORT=\"ZEC\" +COIN_KIND=COIN_KIND_ZCASH # Switch to Canopy over Heartwood -DEFINES += COIN_CONSENSUS_BRANCH_ID=0xE9FF75A6 +BRANCH_ID=0xE9FF75A6 APPNAME ="Zcash" + else ifeq ($(COIN),horizen) # Horizen -DEFINES += BIP44_COIN_TYPE=121 BIP44_COIN_TYPE_2=121 COIN_P2PKH_VERSION=8329 COIN_P2SH_VERSION=8342 COIN_FAMILY=4 COIN_COINID=\"Horizen\" COIN_COINID_HEADER=\"HORIZEN\" COIN_COINID_NAME=\"Horizen\" COINID=$(COIN) COIN_COINID_SHORT=\"ZEN\" COIN_KIND=COIN_KIND_HORIZEN +BIP44_COIN_TYPE=121 +BIP44_COIN_TYPE_2=121 +COIN_P2PKH_VERSION=8329 +COIN_P2SH_VERSION=8342 +COIN_FAMILY=4 +COIN_COINID=\"Horizen\" +COIN_COINID_HEADER=\"HORIZEN\" +COIN_COINID_NAME=\"Horizen\" +COIN_COINID_SHORT=\"ZEN\" +COIN_KIND=COIN_KIND_HORIZEN APPNAME ="Horizen" + else ifeq ($(COIN),komodo) # Komodo -DEFINES += BIP44_COIN_TYPE=141 BIP44_COIN_TYPE_2=141 COIN_P2PKH_VERSION=60 COIN_P2SH_VERSION=85 COIN_FAMILY=1 COIN_COINID=\"Komodo\" COIN_COINID_HEADER=\"KOMODO\" COIN_COINID_NAME=\"Komodo\" COIN_COINID_SHORT=\"KMD\" COIN_KIND=COIN_KIND_KOMODO +BIP44_COIN_TYPE=141 +BIP44_COIN_TYPE_2=141 +COIN_P2PKH_VERSION=60 +COIN_P2SH_VERSION=85 +COIN_FAMILY=1 +COIN_COINID=\"Komodo\" +COIN_COINID_HEADER=\"KOMODO\" +COIN_COINID_NAME=\"Komodo\" +COIN_COINID_SHORT=\"KMD\" +COIN_KIND=COIN_KIND_KOMODO APPNAME ="Komodo" + else ifeq ($(COIN),stratis) # Stratis -DEFINES += BIP44_COIN_TYPE=105105 BIP44_COIN_TYPE_2=105105 COIN_P2PKH_VERSION=75 COIN_P2SH_VERSION=140 COIN_FAMILY=2 COIN_COINID=\"Stratis\" COIN_COINID_HEADER=\"STRATIS\" COIN_COINID_NAME=\"Stratis\" COIN_COINID_SHORT=\"STRAX\" COIN_KIND=COIN_KIND_STRATIS COIN_FLAGS=FLAG_PEERCOIN_SUPPORT +BIP44_COIN_TYPE=105105 +BIP44_COIN_TYPE_2=105105 +COIN_P2PKH_VERSION=75 +COIN_P2SH_VERSION=140 +COIN_FAMILY=2 +COIN_COINID=\"Stratis\" +COIN_COINID_HEADER=\"STRATIS\" +COIN_COINID_NAME=\"Stratis\" +COIN_COINID_SHORT=\"STRAX\" +COIN_KIND=COIN_KIND_STRATIS +COIN_FLAGS=FLAG_PEERCOIN_SUPPORT APPNAME ="Stratis" + else ifeq ($(COIN),xrhodium) #Xrhodium -DEFINES += BIP44_COIN_TYPE=10291 BIP44_COIN_TYPE_2=10291 COIN_P2PKH_VERSION=61 COIN_P2SH_VERSION=123 COIN_FAMILY=1 COIN_COINID=\"xrhodium\" COIN_COINID_HEADER=\"XRHODIUM\" COIN_COINID_NAME=\"xRhodium\" COIN_COINID_SHORT=\"XRC\" COIN_KIND=COIN_KIND_XRHODIUM +BIP44_COIN_TYPE=10291 +BIP44_COIN_TYPE_2=10291 +COIN_P2PKH_VERSION=61 +COIN_P2SH_VERSION=123 +COIN_FAMILY=1 +COIN_COINID=\"xrhodium\" +COIN_COINID_HEADER=\"XRHODIUM\" +COIN_COINID_NAME=\"xRhodium\" +COIN_COINID_SHORT=\"XRC\" +COIN_KIND=COIN_KIND_XRHODIUM APPNAME ="xRhodium" + else ifeq ($(COIN),peercoin) # Peercoin -DEFINES += BIP44_COIN_TYPE=6 BIP44_COIN_TYPE_2=6 COIN_P2PKH_VERSION=55 COIN_P2SH_VERSION=117 COIN_FAMILY=2 COIN_COINID=\"PPCoin\" COIN_COINID_HEADER=\"PEERCOIN\" COIN_COINID_NAME=\"Peercoin\" COIN_COINID_SHORT=\"PPC\" COIN_KIND=COIN_KIND_PEERCOIN COIN_FLAGS=FLAG_PEERCOIN_UNITS\|FLAG_PEERCOIN_SUPPORT +BIP44_COIN_TYPE=6 +BIP44_COIN_TYPE_2=6 +COIN_P2PKH_VERSION=55 +COIN_P2SH_VERSION=117 +COIN_FAMILY=2 +COIN_COINID=\"PPCoin\" +COIN_COINID_HEADER=\"PEERCOIN\" +COIN_COINID_NAME=\"Peercoin\" +COIN_COINID_SHORT=\"PPC\" +COIN_KIND=COIN_KIND_PEERCOIN +COIN_FLAGS=FLAG_PEERCOIN_UNITS\|FLAG_PEERCOIN_SUPPORT APPNAME ="Peercoin" + else ifeq ($(COIN),pivx) # PivX # 77 was used in the Chrome apps -DEFINES += BIP44_COIN_TYPE=119 BIP44_COIN_TYPE_2=77 COIN_P2PKH_VERSION=30 COIN_P2SH_VERSION=13 COIN_FAMILY=1 COIN_COINID=\"DarkNet\" COIN_COINID_HEADER=\"PIVX\" COIN_COINID_NAME=\"PivX\" COIN_COINID_SHORT=\"PIVX\" COIN_KIND=COIN_KIND_PIVX +BIP44_COIN_TYPE=119 +BIP44_COIN_TYPE_2=77 +COIN_P2PKH_VERSION=30 +COIN_P2SH_VERSION=13 +COIN_FAMILY=1 +COIN_COINID=\"DarkNet\" +COIN_COINID_HEADER=\"PIVX\" +COIN_COINID_NAME=\"PivX\" +COIN_COINID_SHORT=\"PIVX\" +COIN_KIND=COIN_KIND_PIVX APPNAME ="PivX" + else ifeq ($(COIN),stealth) # Stealth -DEFINES += BIP44_COIN_TYPE=125 BIP44_COIN_TYPE_2=125 COIN_P2PKH_VERSION=62 COIN_P2SH_VERSION=85 COIN_FAMILY=4 COIN_COINID=\"Stealth\" COIN_COINID_HEADER=\"STEALTH\" COIN_COINID_NAME=\"Stealth\" COIN_COINID_SHORT=\"XST\" COIN_KIND=COIN_KIND_STEALTH COIN_FLAGS=FLAG_PEERCOIN_UNITS\|FLAG_PEERCOIN_SUPPORT +BIP44_COIN_TYPE=125 +BIP44_COIN_TYPE_2=125 +COIN_P2PKH_VERSION=62 +COIN_P2SH_VERSION=85 +COIN_FAMILY=4 +COIN_COINID=\"Stealth\" +COIN_COINID_HEADER=\"STEALTH\" +COIN_COINID_NAME=\"Stealth\" +COIN_COINID_SHORT=\"XST\" +COIN_KIND=COIN_KIND_STEALTH +COIN_FLAGS=FLAG_PEERCOIN_UNITS\|FLAG_PEERCOIN_SUPPORT APPNAME ="Stealth" + else ifeq ($(COIN),viacoin) # Viacoin -DEFINES += BIP44_COIN_TYPE=14 BIP44_COIN_TYPE_2=14 COIN_P2PKH_VERSION=71 COIN_P2SH_VERSION=33 COIN_FAMILY=1 COIN_COINID=\"Viacoin\" COIN_COINID_HEADER=\"VIACOIN\" COIN_COINID_NAME=\"Viacoin\" COIN_COINID_SHORT=\"VIA\" COIN_KIND=COIN_KIND_VIACOIN COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=14 +BIP44_COIN_TYPE_2=14 +COIN_P2PKH_VERSION=71 +COIN_P2SH_VERSION=33 +COIN_FAMILY=1 +COIN_COINID=\"Viacoin\" +COIN_COINID_HEADER=\"VIACOIN\" +COIN_COINID_NAME=\"Viacoin\" +COIN_COINID_SHORT=\"VIA\" +COIN_KIND=COIN_KIND_VIACOIN +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Viacoin" + else ifeq ($(COIN),vertcoin) # Vertcoin # 128 was used in the Chrome apps -DEFINES += BIP44_COIN_TYPE=28 BIP44_COIN_TYPE_2=128 COIN_P2PKH_VERSION=71 COIN_P2SH_VERSION=5 COIN_FAMILY=1 COIN_COINID=\"Vertcoin\" COIN_COINID_HEADER=\"VERTCOIN\" COIN_COINID_NAME=\"Vertcoin\" COIN_COINID_SHORT=\"VTC\" COIN_NATIVE_SEGWIT_PREFIX=\"vtc\" COIN_KIND=COIN_KIND_VERTCOIN COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=28 +BIP44_COIN_TYPE_2=128 +COIN_P2PKH_VERSION=71 +COIN_P2SH_VERSION=5 +COIN_FAMILY=1 +COIN_COINID=\"Vertcoin\" +COIN_COINID_HEADER=\"VERTCOIN\" +COIN_COINID_NAME=\"Vertcoin\" +COIN_COINID_SHORT=\"VTC\" +COIN_NATIVE_SEGWIT_PREFIX=\"vtc\" +COIN_KIND=COIN_KIND_VERTCOIN +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Vertcoin" + else ifeq ($(COIN),digibyte) -DEFINES += BIP44_COIN_TYPE=20 BIP44_COIN_TYPE_2=20 COIN_P2PKH_VERSION=30 COIN_P2SH_VERSION=63 COIN_FAMILY=1 COIN_COINID=\"DigiByte\" COIN_COINID_HEADER=\"DIGIBYTE\" COIN_COINID_NAME=\"DigiByte\" COIN_COINID_SHORT=\"DGB\" COIN_NATIVE_SEGWIT_PREFIX=\"dgb\" COIN_KIND=COIN_KIND_DIGIBYTE COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=20 +BIP44_COIN_TYPE_2=20 +COIN_P2PKH_VERSION=30 +COIN_P2SH_VERSION=63 +COIN_FAMILY=1 +COIN_COINID=\"DigiByte\" +COIN_COINID_HEADER=\"DIGIBYTE\" +COIN_COINID_NAME=\"DigiByte\" +COIN_COINID_SHORT=\"DGB\" +COIN_NATIVE_SEGWIT_PREFIX=\"dgb\" +COIN_KIND=COIN_KIND_DIGIBYTE +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Digibyte" + else ifeq ($(COIN),qtum) -$(error the qtum variant is deprecated and has been moved to its dedicated repo +$(error the qtum variant is deprecated and has been moved to its dedicated repo) else ifeq ($(COIN),firo) -DEFINES += BIP44_COIN_TYPE=136 BIP44_COIN_TYPE_2=136 COIN_P2PKH_VERSION=82 COIN_P2SH_VERSION=7 COIN_FAMILY=1 COIN_COINID=\"Zcoin\" COIN_COINID_HEADER=\"FIRO\" COIN_COINID_NAME=\"Firo\" COIN_COINID_SHORT=\"FIRO\" COIN_KIND=COIN_KIND_FIRO +BIP44_COIN_TYPE=136 +BIP44_COIN_TYPE_2=136 +COIN_P2PKH_VERSION=82 +COIN_P2SH_VERSION=7 +COIN_FAMILY=1 +COIN_COINID=\"Zcoin\" +COIN_COINID_HEADER=\"FIRO\" +COIN_COINID_NAME=\"Firo\" +COIN_COINID_SHORT=\"FIRO\" +COIN_KIND=COIN_KIND_FIRO APPNAME ="Firo" + else ifeq ($(COIN),bitcoin_private) # Bitcoin Private # Initial fork from Bitcoin, public key access is authorized. Signature is different thanks to the forkId # Note : might need a third lock on ZClassic -DEFINES += BIP44_COIN_TYPE=183 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=4901 COIN_P2SH_VERSION=5039 COIN_FAMILY=1 COIN_COINID=\"BPrivate\" COIN_COINID_HEADER=\"BITCOINPRIVATE\" COIN_COINID_NAME=\"BPrivate\" COIN_COINID_SHORT=\"BTCP\" COIN_KIND=COIN_KIND_BITCOIN_PRIVATE COIN_FORKID=42 +BIP44_COIN_TYPE=183 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=4901 +COIN_P2SH_VERSION=5039 +COIN_FAMILY=1 +COIN_COINID=\"BPrivate\" +COIN_COINID_HEADER=\"BITCOINPRIVATE\" +COIN_COINID_NAME=\"BPrivate\" +COIN_COINID_SHORT=\"BTCP\" +COIN_KIND=COIN_KIND_BITCOIN_PRIVATE +COIN_FORKID=42 APPNAME ="Bitcoin Private" + else ifeq ($(COIN),gamecredits) # GameCredits -DEFINES += BIP44_COIN_TYPE=101 BIP44_COIN_TYPE_2=101 COIN_P2PKH_VERSION=38 COIN_P2SH_VERSION=62 COIN_FAMILY=1 COIN_COINID=\"GameCredits\" COIN_COINID_HEADER=\"GAMECREDITS\" COIN_COINID_NAME=\"GameCredits\" COIN_COINID_SHORT=\"GAME\" COIN_KIND=COIN_KIND_GAMECREDITS COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=101 +BIP44_COIN_TYPE_2=101 +COIN_P2PKH_VERSION=38 +COIN_P2SH_VERSION=62 +COIN_FAMILY=1 +COIN_COINID=\"GameCredits\" +COIN_COINID_HEADER=\"GAMECREDITS\" +COIN_COINID_NAME=\"GameCredits\" +COIN_COINID_SHORT=\"GAME\" +COIN_KIND=COIN_KIND_GAMECREDITS +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="GameCredits" + else ifeq ($(COIN),zclassic) # ZClassic -DEFINES += BIP44_COIN_TYPE=147 BIP44_COIN_TYPE_2=147 COIN_P2PKH_VERSION=7352 COIN_P2SH_VERSION=7357 COIN_FAMILY=1 COIN_COINID=\"ZClassic\" COIN_COINID_HEADER=\"ZCLASSIC\" COIN_COINID_NAME=\"ZClassic\" COIN_COINID_SHORT=\"ZCL\" COIN_KIND=COIN_KIND_ZCLASSIC +BIP44_COIN_TYPE=147 +BIP44_COIN_TYPE_2=147 +COIN_P2PKH_VERSION=7352 +COIN_P2SH_VERSION=7357 +COIN_FAMILY=1 +COIN_COINID=\"ZClassic\" +COIN_COINID_HEADER=\"ZCLASSIC\" +COIN_COINID_NAME=\"ZClassic\" +COIN_COINID_SHORT=\"ZCL\" +COIN_KIND=COIN_KIND_ZCLASSIC APPNAME ="ZClassic" + else ifeq ($(COIN),xsn) # XSN mainnet -DEFINES += BIP44_COIN_TYPE=384 BIP44_COIN_TYPE_2=384 COIN_P2PKH_VERSION=76 COIN_P2SH_VERSION=16 COIN_FAMILY=1 COIN_COINID=\"XSN\" COIN_COINID_HEADER=\"XSN\" COIN_COINID_NAME=\"XSN\" COIN_COINID_SHORT=\"XSN\" COIN_NATIVE_SEGWIT_PREFIX=\"xc\" COIN_KIND=COIN_KIND_XSN COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=384 +BIP44_COIN_TYPE_2=384 +COIN_P2PKH_VERSION=76 +COIN_P2SH_VERSION=16 +COIN_FAMILY=1 +COIN_COINID=\"XSN\" +COIN_COINID_HEADER=\"XSN\" +COIN_COINID_NAME=\"XSN\" +COIN_COINID_SHORT=\"XSN\" +COIN_NATIVE_SEGWIT_PREFIX=\"xc\" +COIN_KIND=COIN_KIND_XSN +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="XSN" + else ifeq ($(COIN),nix) # NIX -DEFINES += BIP44_COIN_TYPE=400 BIP44_COIN_TYPE_2=400 COIN_P2PKH_VERSION=38 COIN_P2SH_VERSION=53 COIN_FAMILY=1 COIN_COINID=\"NIX\" COIN_COINID_HEADER=\"NIX\" COIN_COINID_NAME=\"NIX\" COIN_COINID_SHORT=\"NIX\" COIN_NATIVE_SEGWIT_PREFIX=\"nix\" COIN_KIND=COIN_KIND_NIX COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=400 +BIP44_COIN_TYPE_2=400 +COIN_P2PKH_VERSION=38 +COIN_P2SH_VERSION=53 +COIN_FAMILY=1 +COIN_COINID=\"NIX\" +COIN_COINID_HEADER=\"NIX\" +COIN_COINID_NAME=\"NIX\" +COIN_COINID_SHORT=\"NIX\" +COIN_NATIVE_SEGWIT_PREFIX=\"nix\" +COIN_KIND=COIN_KIND_NIX +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="NIX" + else ifeq ($(COIN),lbry) # LBRY -DEFINES += BIP44_COIN_TYPE=140 BIP44_COIN_TYPE_2=140 COIN_P2PKH_VERSION=85 COIN_P2SH_VERSION=122 COIN_FAMILY=1 COIN_COINID=\"LBRY\" COIN_COINID_HEADER=\"LBRY\" COIN_COINID_NAME=\"LBRY\" COIN_COINID_SHORT=\"LBC\" COIN_KIND=COIN_KIND_LBRY +BIP44_COIN_TYPE=140 +BIP44_COIN_TYPE_2=140 +COIN_P2PKH_VERSION=85 +COIN_P2SH_VERSION=122 +COIN_FAMILY=1 +COIN_COINID=\"LBRY\" +COIN_COINID_HEADER=\"LBRY\" +COIN_COINID_NAME=\"LBRY\" +COIN_COINID_SHORT=\"LBC\" +COIN_KIND=COIN_KIND_LBRY APPNAME ="LBRY" + else ifeq ($(COIN),resistance) # Resistance -DEFINES += BIP44_COIN_TYPE=356 BIP44_COIN_TYPE_2=356 COIN_P2PKH_VERSION=7063 COIN_P2SH_VERSION=7068 COIN_FAMILY=1 COIN_COINID=\"Res\" COIN_COINID_HEADER=\"RES\" COIN_COINID_NAME=\"Res\" COIN_COINID_SHORT=\"RES\" COIN_KIND=COIN_KIND_RESISTANCE +BIP44_COIN_TYPE=356 +BIP44_COIN_TYPE_2=356 +COIN_P2PKH_VERSION=7063 +COIN_P2SH_VERSION=7068 +COIN_FAMILY=1 +COIN_COINID=\"Res\" +COIN_COINID_HEADER=\"RES\" +COIN_COINID_NAME=\"Res\" +COIN_COINID_SHORT=\"RES\" +COIN_KIND=COIN_KIND_RESISTANCE APPNAME ="Resistance" + else ifeq ($(COIN),ravencoin) # Ravencoin -DEFINES += BIP44_COIN_TYPE=175 BIP44_COIN_TYPE_2=175 COIN_P2PKH_VERSION=60 COIN_P2SH_VERSION=122 COIN_FAMILY=1 COIN_COINID=\"Ravencoin\" COIN_COINID_HEADER=\"RAVENCOIN\" COIN_COINID_NAME=\"Ravencoin\" COIN_COINID_SHORT=\"RVN\" COIN_KIND=COIN_KIND_RAVENCOIN +BIP44_COIN_TYPE=175 +BIP44_COIN_TYPE_2=175 +COIN_P2PKH_VERSION=60 +COIN_P2SH_VERSION=122 +COIN_FAMILY=1 +COIN_COINID=\"Ravencoin\" +COIN_COINID_HEADER=\"RAVENCOIN\" +COIN_COINID_NAME=\"Ravencoin\" +COIN_COINID_SHORT=\"RVN\" +COIN_KIND=COIN_KIND_RAVENCOIN APPNAME ="Ravencoin" + else ifeq ($(COIN),hydra_testnet) # Hydra testnet -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=66 COIN_P2SH_VERSION=128 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=0 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=66 +COIN_P2SH_VERSION=128 +COIN_FAMILY=3 +COIN_COINID=\"Hydra\" +COIN_COINID_HEADER=\"HYDRA\" +COIN_COINID_NAME=\"HYDRA\" +COIN_COINID_SHORT=\"HYDRA\" +COIN_NATIVE_SEGWIT_PREFIX=\"hc\" +COIN_KIND=COIN_KIND_HYDRA +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Hydra Test" +APP_LOAD_PARAMS += --path "44'/609'" + else ifeq ($(COIN),hydra) # Hydra mainnet -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=40 COIN_P2SH_VERSION=63 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +BIP44_COIN_TYPE=0 +BIP44_COIN_TYPE_2=0 +COIN_P2PKH_VERSION=40 +COIN_P2SH_VERSION=63 +COIN_FAMILY=3 +COIN_COINID=\"Hydra\" +COIN_COINID_HEADER=\"HYDRA\" +COIN_COINID_NAME=\"HYDRA\" +COIN_COINID_SHORT=\"HYDRA\" +COIN_NATIVE_SEGWIT_PREFIX=\"hc\" +COIN_KIND=COIN_KIND_HYDRA +COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT APPNAME ="Hydra" -else -ifeq ($(filter clean,$(MAKECMDGOALS)),) -$(error Unsupported COIN - use bitcoin_testnet, bitcoin, bitcoin_cash, bitcoin_gold, litecoin, dogecoin, dash, zcash, horizen, komodo, stratis, peercoin, pivx, viacoin, vertcoin, stealth, digibyte, bitcoin_private, firo, gamecredits, zclassic, xsn, nix, lbry, resistance, ravencoin, hydra, hydra_testnet, xrhodium) -endif +APP_LOAD_PARAMS += --path "44'/609'" + +else ifeq ($(filter clean,$(MAKECMDGOALS)),) +$(error Unsupported COIN - use $(VARIANT_VALUES)) endif -include $(BOLOS_SDK)/Makefile.standard_app +include lib-app-bitcoin/Makefile diff --git a/glyphs/icon_back.gif b/glyphs/icon_back.gif deleted file mode 100644 index f52f795d..00000000 Binary files a/glyphs/icon_back.gif and /dev/null differ diff --git a/glyphs/icon_certificate.gif b/glyphs/icon_certificate.gif deleted file mode 100644 index 38921546..00000000 Binary files a/glyphs/icon_certificate.gif and /dev/null differ diff --git a/glyphs/icon_coggle.gif b/glyphs/icon_coggle.gif deleted file mode 100644 index a51e687f..00000000 Binary files a/glyphs/icon_coggle.gif and /dev/null differ diff --git a/glyphs/icon_crossmark.gif b/glyphs/icon_crossmark.gif deleted file mode 100644 index c30bfe40..00000000 Binary files a/glyphs/icon_crossmark.gif and /dev/null differ diff --git a/glyphs/icon_dashboard.gif b/glyphs/icon_dashboard.gif deleted file mode 100644 index adceb5ac..00000000 Binary files a/glyphs/icon_dashboard.gif and /dev/null differ diff --git a/glyphs/icon_dashboard_x.gif b/glyphs/icon_dashboard_x.gif deleted file mode 100644 index 6e508aa7..00000000 Binary files a/glyphs/icon_dashboard_x.gif and /dev/null differ diff --git a/glyphs/icon_down.gif b/glyphs/icon_down.gif deleted file mode 100644 index 4f4e39ee..00000000 Binary files a/glyphs/icon_down.gif and /dev/null differ diff --git a/glyphs/icon_eye.gif b/glyphs/icon_eye.gif deleted file mode 100644 index a2bd5a42..00000000 Binary files a/glyphs/icon_eye.gif and /dev/null differ diff --git a/glyphs/icon_left.gif b/glyphs/icon_left.gif deleted file mode 100644 index 524226ba..00000000 Binary files a/glyphs/icon_left.gif and /dev/null differ diff --git a/glyphs/icon_right.gif b/glyphs/icon_right.gif deleted file mode 100644 index 15ff3cf5..00000000 Binary files a/glyphs/icon_right.gif and /dev/null differ diff --git a/glyphs/icon_up.gif b/glyphs/icon_up.gif deleted file mode 100644 index 4e13c064..00000000 Binary files a/glyphs/icon_up.gif and /dev/null differ diff --git a/glyphs/icon_validate_14.gif b/glyphs/icon_validate_14.gif deleted file mode 100644 index 0ceb67eb..00000000 Binary files a/glyphs/icon_validate_14.gif and /dev/null differ diff --git a/glyphs/icon_warning.gif b/glyphs/icon_warning.gif deleted file mode 100644 index a3ee4bfc..00000000 Binary files a/glyphs/icon_warning.gif and /dev/null differ diff --git a/include/apdu_constants.h b/include/apdu_constants.h deleted file mode 100644 index 1aa127d3..00000000 --- a/include/apdu_constants.h +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef APDU_CONSTANTS_H - -#define APDU_CONSTANTS_H - -#define CLA 0xE0 -#define ADM_CLA 0xD0 -#define NFCPAYMENT_CLA 0xF0 - -#define INS_SETUP 0x20 -#define INS_VERIFY_PIN 0x22 -#define INS_GET_OPERATION_MODE 0x24 -#define INS_SET_OPERATION_MODE 0x26 -#define INS_SET_KEYBOARD_CFG 0x28 -#define INS_GET_WALLET_PUBLIC_KEY 0x40 -#define INS_GET_TRUSTED_INPUT 0x42 -#define INS_HASH_INPUT_START 0x44 -#define INS_HASH_INPUT_FINALIZE 0x46 -#define INS_HASH_SIGN 0x48 -#define INS_HASH_INPUT_FINALIZE_FULL 0x4A -#define INS_GET_INTERNAL_CHAIN_INDEX 0x4C -#define INS_SIGN_MESSAGE 0x4E -#define INS_GET_TRANSACTION_LIMIT 0xA0 -#define INS_SET_TRANSACTION_LIMIT 0xA2 -#define INS_IMPORT_PRIVATE_KEY 0xB0 -#define INS_GET_PUBLIC_KEY 0xB2 -#define INS_DERIVE_BIP32_KEY 0xB4 -#define INS_SIGNVERIFY_IMMEDIATE 0xB6 -#define INS_GET_RANDOM 0xC0 -#define INS_GET_ATTESTATION 0xC2 -#define INS_GET_FIRMWARE_VERSION 0xC4 -#define INS_COMPOSE_MOFN_ADDRESS 0xC6 -#define INS_GET_POS_SEED 0xCA -#define INS_DEBUG 0xD0 - -#define INS_ADM_INIT_KEYS 0x20 -#define INS_ADM_INIT_ATTESTATION 0x22 -#define INS_ADM_GET_UPDATE_ID 0x24 -#define INS_ADM_SET_KEYCARD_SEED 0x26 -#define INS_ADM_FIRMWARE_UPDATE 0x42 - -#define INS_SET_USER_KEYCARD 0x10 -#define INS_SETUP_SECURE_SCREEN 0x12 -#define INS_SET_ALTERNATE_COIN_VER 0x14 -#define INS_GET_COIN_VER 0x16 - -#define INS_STORE_TRUST_ROOT_BIP70 0x30 -#define INS_CREATE_CERTIFICATE_BIP70 0x32 -#define INS_CREATE_PAYMENT_REQ_BIP70 0x34 -#define INS_PROCESS_CERTIFICATE_BIP70 0x36 -#define INS_PARSE_PAYMENT_REQ_BIP70 0x38 -#define INS_HASH_INPUT_FINALIZE_BIP70 0x3A -#define INS_ADM_SET_ROOT_BIP70 0x28 -#define INS_ADM_SET_BIP39_SHUFFLE 0x2A - -#define INS_NFCPAYMENT_SET_CONFIG 0x20 -#define INS_NFCPAYMENT_GET_CONFIG 0x22 -#define INS_NFCPAYMENT_STORE_UTXO 0x40 -#define INS_NFCPAYMENT_STORE_SCRIPT 0x42 -#define INS_NFCPAYMENT_GET_UTXO 0x44 -#define INS_NFCPAYMENT_DELETE_UTXO 0x46 -#define INS_NFCPAYMENT_GET_PAYMENT_TX 0x50 -#define INS_NFCPAYMENT_GET_LAST_TX 0x52 -#define INS_NFCPAYMENT_CONFIRM_TX 0x54 -#define INS_NFCPAYMENT_CONFIRM_CHANGE 0x56 -#define INS_NFCPAYMENT_GET_LAST_STAT 0x58 -#define INS_NFCPAYMENT_GET_DATA 0xC0 - -#define SW_PIN_REMAINING_ATTEMPTS 0x63C0 -#define SW_INCORRECT_LENGTH 0x6700 -#define SW_COMMAND_INCOMPATIBLE_FILE_STRUCTURE 0x6981 -#define SW_SECURITY_STATUS_NOT_SATISFIED 0x6982 -#define SW_CONDITIONS_OF_USE_NOT_SATISFIED 0x6985 -#define SW_INCORRECT_DATA 0x6A80 -#define SW_NOT_ENOUGH_MEMORY_SPACE 0x6A84 -#define SW_REFERENCED_DATA_NOT_FOUND 0x6A88 -#define SW_FILE_ALREADY_EXISTS 0x6A89 -#define SW_SWAP_WITHOUT_TRUSTED_INPUTS 0x6A8A -#define SW_INCORRECT_P1_P2 0x6B00 -#define SW_INS_NOT_SUPPORTED 0x6D00 -#define SW_CLA_NOT_SUPPORTED 0x6E00 -#define SW_TECHNICAL_PROBLEM 0x6F00 -#define SW_OK 0x9000 -#define SW_MEMORY_PROBLEM 0x9240 -#define SW_NO_EF_SELECTED 0x9400 -#define SW_INVALID_OFFSET 0x9402 -#define SW_FILE_NOT_FOUND 0x9404 -#define SW_INCONSISTENT_FILE 0x9408 -#define SW_ALGORITHM_NOT_SUPPORTED 0x9484 -#define SW_INVALID_KCV 0x9485 -#define SW_CODE_NOT_INITIALIZED 0x9802 -#define SW_ACCESS_CONDITION_NOT_FULFILLED 0x9804 -#define SW_CONTRADICTION_SECRET_CODE_STATUS 0x9808 -#define SW_CONTRADICTION_INVALIDATION 0x9810 -#define SW_CODE_BLOCKED 0x9840 -#define SW_MAX_VALUE_REACHED 0x9850 -#define SW_GP_AUTH_FAILED 0x6300 -#define SW_LICENSING 0x6F42 -#define SW_HALTED 0x6FAA -#define SW_APP_HALTED SW_CONDITIONS_OF_USE_NOT_SATISFIED - -#define ISO_OFFSET_CLA 0x00 -#define ISO_OFFSET_INS 0x01 -#define ISO_OFFSET_P1 0x02 -#define ISO_OFFSET_P2 0x03 -#define ISO_OFFSET_LC 0x04 -#define ISO_OFFSET_CDATA 0x05 - -#define BITID_DERIVE 0xB11D -#define BITID_DERIVE_MULTIPLE 0xB11E - -#include "os.h" -#include "secure_value.h" - -void commit_operation_mode(secu8 operationMode); - -unsigned short apdu_get_wallet_public_key(void); -unsigned short apdu_get_trusted_input(void); -unsigned short apdu_hash_input_start(void); -unsigned short apdu_hash_input_finalize(void); -unsigned short apdu_hash_sign(void); -unsigned short apdu_hash_input_finalize_full(void); -unsigned short apdu_sign_message(void); - -unsigned short apdu_get_firmware_version(void); - -unsigned short apdu_get_coin_version(void); - -#endif diff --git a/include/config.h b/include/config.h deleted file mode 100644 index dac4e214..00000000 --- a/include/config.h +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef CONFIG_H - -#define CONFIG_H - -//#define DISABLE_SECURE_VALUE - -#define VERBOSE_6F - -#define os_crc cx_crc16 - -#endif diff --git a/include/filesystem.h b/include/filesystem.h deleted file mode 100644 index fe994dc9..00000000 --- a/include/filesystem.h +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef FS_H - -#define FS_H - -#include "os.h" -#include "config.h" -#include "context.h" -#include "filesystem_tx.h" - -enum supported_modes_e { - SUPPORTED_MODE_WALLET = 0x01, - SUPPORTED_MODE_RELAXED_WALLET = 0x02, - SUPPORTED_MODE_SERVER = 0x04, - SUPPORTED_MODE_DEVELOPER = 0x08 -}; - -enum family_e { - FAMILY_BITCOIN = 0x01, - FAMILY_PEERCOIN = 0x02, - FAMILY_STEALTH = 0x04 -}; - -struct config_s { - secu8 supportedModes; - secu8 operationMode; - unsigned char options; - // unsigned short payToAddressVersion; - // unsigned short payToScriptHashVersion; - // unsigned char coinFamily; - // /** Current Coin ID */ - // unsigned char coinId[MAX_COIN_ID]; - // /** Current short Coin ID */ - // unsigned char shortCoinId[MAX_SHORT_COIN_ID]; - // /** Current Coin ID length */ - // unsigned char coinIdLength; - // /** Current short Coin ID length */ - // unsigned char shortCoinIdLength; -}; -typedef struct config_s config_t; - -typedef struct backup_area_s { - config_t config; - uint8_t trustedinput_key[32]; -} backup_area_t; - -typedef struct storage_s { - unsigned char storageInitialized; - - unsigned char config_valid; - backup_area_t bkp; - - unsigned char fidoTransport; - - uint8_t pubKeyRequestRestriction; - -} storage_t; - -// the global nvram memory variable -#if 0 -extern storage_t N_real; -#define N_btchip (*(storage_t *)PIC(&N_real)) -#else -extern storage_t const N_real; -#define N_btchip (*(volatile storage_t *)PIC(&N_real)) -#endif - -void set_operation_mode(unsigned char operationMode); - -#endif diff --git a/include/secure_value.h b/include/secure_value.h deleted file mode 100644 index 8052084a..00000000 --- a/include/secure_value.h +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef SECURE_VALUE_H - -#define SECURE_VALUE_H - -#include "os.h" - -typedef unsigned short secu8; -typedef unsigned long int secu16; - -void sbSet(secu8 *target, unsigned char source); -void sbCheck(secu8 source); -void ssSet(secu16 *target, unsigned short source); -void ssCheck(secu16 source); - -#define SB_GET(x) ((unsigned char)x) - -#define SB_SET(x, y) sbSet(&x, y); - -#define SB_CHECK(x) sbCheck(x); - -#define SS_GET(x) ((unsigned short)x) - -#define SS_SET(x, y) ssSet(&x, y); - -#define SS_CHECK(x) ssCheck(x); - -#define SSEC_DEF(x) unsigned char x = 0; -#define SSEC_INC(x) x++; -#define SSEC_CHECK(x, value) \ - if (x != value) \ - reset(); - -#endif diff --git a/include/swap_lib_calls.h b/include/swap_lib_calls.h deleted file mode 100644 index 806e5f09..00000000 --- a/include/swap_lib_calls.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -/* This file is the shared API between Exchange and the apps started in Library mode for Exchange - * - * DO NOT MODIFY THIS FILE IN APPLICATIONS OTHER THAN EXCHANGE - * On modification in Exchange, forward the changes to all applications supporting Exchange - */ - -#include "stdbool.h" -#include "stdint.h" -#include "context.h" - -#define RUN_APPLICATION 1 - -#define SIGN_TRANSACTION 2 - -#define CHECK_ADDRESS 3 - -#define GET_PRINTABLE_AMOUNT 4 - -/* - * Amounts are stored as bytes, with a max size of 16 (see protobuf - * specifications). Max 16B integer is 340282366920938463463374607431768211455 - * in decimal, which is a 32-long char string. - * The printable amount also contains spaces, the ticker symbol (with variable - * size, up to 12 in Ethereum for instance) and a terminating null byte, so 50 - * bytes total should be a fair maximum. - */ -#define MAX_PRINTABLE_AMOUNT_SIZE 50 - -// structure that should be send to specific coin application to get address -typedef struct check_address_parameters_s { - // IN - uint8_t *coin_configuration; - uint8_t coin_configuration_length; - // serialized path, segwit, version prefix, hash used, dictionary etc. - // fields and serialization format depends on specific coin app - uint8_t *address_parameters; - uint8_t address_parameters_length; - char *address_to_check; - char *extra_id_to_check; - // OUT - int result; -} check_address_parameters_t; - -// structure that should be send to specific coin application to get printable amount -typedef struct get_printable_amount_parameters_s { - // IN - uint8_t *coin_configuration; - uint8_t coin_configuration_length; - uint8_t *amount; - uint8_t amount_length; - bool is_fee; - // OUT - char printable_amount[MAX_PRINTABLE_AMOUNT_SIZE]; -} get_printable_amount_parameters_t; - -typedef struct create_transaction_parameters_s { - // IN - uint8_t *coin_configuration; - uint8_t coin_configuration_length; - uint8_t *amount; - uint8_t amount_length; - uint8_t *fee_amount; - uint8_t fee_amount_length; - char *destination_address; - char *destination_address_extra_id; - // OUT - uint8_t result; -} create_transaction_parameters_t; - -typedef struct libargs_s { - unsigned int id; - unsigned int command; - altcoin_config_t *coin_config; - union { - check_address_parameters_t *check_address; - create_transaction_parameters_t *create_transaction; - get_printable_amount_parameters_t *get_printable_amount; - }; -} libargs_t; diff --git a/lib-app-bitcoin/Makefile b/lib-app-bitcoin/Makefile new file mode 100644 index 00000000..331a3b64 --- /dev/null +++ b/lib-app-bitcoin/Makefile @@ -0,0 +1,140 @@ +# **************************************************************************** +# Ledger App Bitcoin +# (c) 2023 Ledger SAS. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# **************************************************************************** + +ifeq ($(BOLOS_SDK),) +$(error Environment variable BOLOS_SDK is not set) +endif +include $(BOLOS_SDK)/Makefile.defines + +######################################## +# Mandatory configuration # +######################################## +# Name is defined later + +ifeq ($(APPVERSION_M),) +$(error APPVERSION_M must be defined) +endif +ifeq ($(APPVERSION_N),) +$(error APPVERSION_N must be defined) +endif +ifeq ($(APPVERSION_P),) +$(error APPVERSION_P must be defined) +endif + +APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" + +APP_SOURCE_PATH += lib-app-bitcoin/ + +ICON_NANOS = icons/nanos_app_$(COIN).gif +ICON_NANOX = icons/nanox_app_$(COIN).gif +ICON_NANOSP = icons/nanox_app_$(COIN).gif +ICON_STAX = icons/stax_app_$(COIN).gif + +ifeq ($(TARGET_NAME),TARGET_STAX) +DEFINES += COIN_ICON=C_$(COIN)_64px +DEFINES += COIN_ICON_BITMAP=C_$(COIN)_64px_bitmap +endif + +# Application allowed derivation curves. +CURVE_APP_LOAD_PARAMS = secp256k1 + +# Application allowed derivation paths. +PATH_APP_LOAD_PARAMS = "" + +HAVE_APPLICATION_FLAG_DERIVE_MASTER = 1 +HAVE_APPLICATION_FLAG_LIBRARY = 1 + +VARIANT_PARAM = COIN + +ENABLE_BLUETOOTH = 1 +ENABLE_NBGL_QRCODE = 1 +ENABLE_SWAP = 1 + +ifndef BIP44_COIN_TYPE +$(error BIP44_COIN_TYPE must be defined) +endif + +ifndef BIP44_COIN_TYPE_2 +$(error BIP44_COIN_TYPE_2 must be defined) +endif + +ifndef COIN_P2PKH_VERSION +$(error COIN_P2PKH_VERSION must be defined) +endif + +ifndef COIN_P2SH_VERSION +$(error COIN_P2SH_VERSION must be defined) +endif + +ifndef COIN_FAMILY +$(error COIN_FAMILY must be defined) +endif + +ifndef COIN_COINID +$(error COIN_COINID must be defined) +endif + +ifndef COIN_COINID_HEADER +$(error COIN_COINID_HEADER must be defined) +endif + +ifndef COIN_COINID_NAME +$(error COIN_COINID_NAME must be defined) +endif + +ifndef COIN_COINID_SHORT +$(error COIN_COINID_SHORT must be defined) +endif + +ifndef COIN_NATIVE_SEGWIT_PREFIX +$(info COIN_NATIVE_SEGWIT_PREFIX automatically set to 0) +COIN_NATIVE_SEGWIT_PREFIX=0 +endif + +ifndef COIN_KIND +$(error COIN_KIND must be defined) +endif + +ifndef COIN_FLAGS +$(info COIN_FLAGS automatically set to 0) +COIN_FLAGS=0 +endif + +ifndef COIN_FORKID +$(info COIN_FORKID automatically set to 0) +COIN_FORKID=0 +endif + +ifeq ($(APPNAME),) +$(error APPNAME must be defined) +endif + +DEFINES +=BIP44_COIN_TYPE=$(BIP44_COIN_TYPE) +DEFINES +=BIP44_COIN_TYPE_2=$(BIP44_COIN_TYPE_2) +DEFINES +=COIN_P2PKH_VERSION=$(COIN_P2PKH_VERSION) +DEFINES +=COIN_P2SH_VERSION=$(COIN_P2SH_VERSION) +DEFINES +=COIN_FAMILY=$(COIN_FAMILY) +DEFINES +=COIN_COINID=$(COIN_COINID) +DEFINES +=COIN_COINID_HEADER=$(COIN_COINID_HEADER) +DEFINES +=COIN_COINID_NAME=$(COIN_COINID_NAME) +DEFINES +=COIN_COINID_SHORT=$(COIN_COINID_SHORT) +DEFINES +=COIN_NATIVE_SEGWIT_PREFIX=$(COIN_NATIVE_SEGWIT_PREFIX) +DEFINES +=COIN_KIND=$(COIN_KIND) +DEFINES +=COIN_FLAGS=$(COIN_FLAGS) +DEFINES +=COIN_FORKID=$(COIN_FORKID) + +include $(BOLOS_SDK)/Makefile.standard_app diff --git a/lib-app-bitcoin/apdu/apdu_constants.h b/lib-app-bitcoin/apdu/apdu_constants.h new file mode 100644 index 00000000..f5265425 --- /dev/null +++ b/lib-app-bitcoin/apdu/apdu_constants.h @@ -0,0 +1,60 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#pragma once + +#include "os.h" +#include "buffer.h" +#include "macros.h" + +#define CLA 0xE0 + +#define INS_GET_WALLET_PUBLIC_KEY 0x40 +#define INS_GET_TRUSTED_INPUT 0x42 +#define INS_HASH_INPUT_START 0x44 +#define INS_HASH_SIGN 0x48 +#define INS_HASH_INPUT_FINALIZE_FULL 0x4A +#define INS_SIGN_MESSAGE 0x4E +#define INS_GET_FIRMWARE_VERSION 0xC4 +#define INS_GET_COIN_VER 0x16 + +#define SW_INCORRECT_LENGTH 0x6700 +#define SW_SECURITY_STATUS_NOT_SATISFIED 0x6982 +#define SW_CONDITIONS_OF_USE_NOT_SATISFIED 0x6985 +#define SW_INCORRECT_DATA 0x6A80 +#define SW_SWAP_WITHOUT_TRUSTED_INPUTS 0x6A8A +#define SW_INCORRECT_P1_P2 0x6B00 +#define SW_INS_NOT_SUPPORTED 0x6D00 +#define SW_CLA_NOT_SUPPORTED 0x6E00 +#define SW_TECHNICAL_PROBLEM 0x6F00 +#define SW_TECHNICAL_PROBLEM_2 0x6F0F +#define SW_OK 0x9000 + +#define BITID_DERIVE 0xB11D +#define BITID_DERIVE_MULTIPLE 0xB11E + +#define ZCASH_USING_OVERWINTER 0x01 +#define ZCASH_USING_OVERWINTER_SAPLING 0x02 + +unsigned short handler_sign_message(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_hash_sign(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_hash_input_start(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_hash_input_finalize_full(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_get_wallet_public_key(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_get_trusted_input(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_get_firmware_version(void); +unsigned short handler_get_coin_version(void); + diff --git a/lib-app-bitcoin/apdu/dispatcher.c b/lib-app-bitcoin/apdu/dispatcher.c new file mode 100644 index 00000000..7e2c6c0e --- /dev/null +++ b/lib-app-bitcoin/apdu/dispatcher.c @@ -0,0 +1,117 @@ +/***************************************************************************** + * Ledger App Boilerplate. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#include +#include + +#include "buffer.h" +#include "io.h" +#include "ledger_assert.h" + +#include "dispatcher.h" +#include "apdu_constants.h" + +int apdu_dispatcher(const command_t *cmd) { + LEDGER_ASSERT(cmd != NULL, "NULL cmd"); + + if (cmd->cla != CLA) { + return io_send_sw(SW_CLA_NOT_SUPPORTED); + } + + buffer_t buf = {0}; + + switch (cmd->ins) { + case INS_GET_WALLET_PUBLIC_KEY: + PRINTF("Get wallet public key\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_get_wallet_public_key(&buf, cmd->p1, cmd->p2); + + case INS_GET_TRUSTED_INPUT: + PRINTF("Get trusted input\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_get_trusted_input(&buf, cmd->p1, cmd->p2); + + case INS_HASH_INPUT_START: + PRINTF("Hash input start\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_input_start(&buf, cmd->p1, cmd->p2); + + case INS_HASH_SIGN: + PRINTF("Hash sign\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_sign(&buf, cmd->p1, cmd->p2); + + case INS_HASH_INPUT_FINALIZE_FULL: + PRINTF("Hash input finalize full\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_input_finalize_full(&buf, cmd->p1, cmd->p2); + + case INS_SIGN_MESSAGE: + PRINTF("Sign message\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_sign_message(&buf, cmd->p1, cmd->p2); + + case INS_GET_FIRMWARE_VERSION: + PRINTF("Get firmware version\n"); + + return handler_get_firmware_version(); + + case INS_GET_COIN_VER: + PRINTF("Get coin version\n"); + + return handler_get_coin_version(); + + default: + PRINTF("Instruction not supported\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + } +} diff --git a/lib-app-bitcoin/apdu/dispatcher.h b/lib-app-bitcoin/apdu/dispatcher.h new file mode 100644 index 00000000..36e1f60c --- /dev/null +++ b/lib-app-bitcoin/apdu/dispatcher.h @@ -0,0 +1,14 @@ +#pragma once + +#include "parser.h" + +/** + * Dispatch APDU command received to the right handler. + * + * @param[in] cmd + * Structured APDU command (CLA, INS, P1, P2, Lc, Command data). + * + * @return zero or positive integer if success, negative integer otherwise. + * + */ +int apdu_dispatcher(const command_t *cmd); diff --git a/lib-app-bitcoin/app_main.c b/lib-app-bitcoin/app_main.c new file mode 100644 index 00000000..c2f9a62b --- /dev/null +++ b/lib-app-bitcoin/app_main.c @@ -0,0 +1,72 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "swap.h" +#include "io.h" + +#include "context.h" +#include "apdu_constants.h" +#include "dispatcher.h" +#include "ui.h" + + +void app_main(void) { + // Structured APDU command + command_t cmd; + + io_init(); + + if (!G_called_from_swap) { + ui_idle_flow(); + } + + context_init(); + + for (;;) { + // Length of APDU command received in G_io_apdu_buffer + int input_len = 0; + + // Receive command bytes in G_io_apdu_buffer + if ((input_len = io_recv_command()) < 0) { + PRINTF("=> io_recv_command failure\n"); + return; + } + + // Parse APDU command from G_io_apdu_buffer + if (!apdu_parser(&cmd, G_io_apdu_buffer, input_len)) { + PRINTF("=> /!\\ BAD LENGTH: %.*H\n", input_len, G_io_apdu_buffer); + io_send_sw(SW_INCORRECT_LENGTH); + continue; + } + + PRINTF("=> CLA=%02X | INS=%02X | P1=%02X | P2=%02X | Lc=%02X | CData=%.*H\n", + cmd.cla, + cmd.ins, + cmd.p1, + cmd.p2, + cmd.lc, + cmd.lc, + cmd.data); + + context.outLength = 0; + + // Dispatch structured APDU command to handler + if (apdu_dispatcher(&cmd) < 0) { + PRINTF("=> apdu_dispatcher failure\n"); + return; + } + } +} diff --git a/src/context.c b/lib-app-bitcoin/context.c similarity index 65% rename from src/context.c rename to lib-app-bitcoin/context.c index 3ff49151..2cf6da40 100644 --- a/src/context.c +++ b/lib-app-bitcoin/context.c @@ -15,23 +15,24 @@ * limitations under the License. ********************************************************************************/ -#include "internal.h" -#include "swap.h" +#include "context.h" +#include "filesystem.h" -void autosetup(void); +context_t context; +storage_t const N_real; /** * Initialize the application context on boot */ void context_init() { PRINTF("Context init\n"); - PRINTF("Backup size %d\n", sizeof(N_btchip.bkp)); - memset(&context_D, 0, sizeof(context_D)); - G_called_from_swap = 0; - context_D.currentOutputOffset = 0; - context_D.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - memset(context_D.totalOutputAmount, 0, - sizeof(context_D.totalOutputAmount)); - context_D.changeOutputFound = 0; - context_D.segwitWarningSeen = 0; + PRINTF("Backup size %d\n", sizeof(g_nvram_data.bkp)); + memset(&context, 0, sizeof(context)); + context.currentOutputOffset = 0; + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + memset(context.totalOutputAmount, 0, + sizeof(context.totalOutputAmount)); + context.changeOutputFound = 0; + context.segwitWarningSeen = 0; } + diff --git a/include/context.h b/lib-app-bitcoin/context.h similarity index 91% rename from include/context.h rename to lib-app-bitcoin/context.h index 4f23f4db..48a4aa1a 100644 --- a/include/context.h +++ b/lib-app-bitcoin/context.h @@ -15,13 +15,10 @@ * limitations under the License. ********************************************************************************/ -#ifndef CONTEXT_H - -#define CONTEXT_H +#pragma once #include "os.h" #include "cx.h" -#include "secure_value.h" #include "filesystem_tx.h" #ifdef HAVE_NBGL #include "nbgl_types.h" @@ -33,27 +30,6 @@ #define MAX_SHORT_COIN_ID 5 #define MAGIC_TRUSTED_INPUT 0x32 -#define MAGIC_DEV_KEY 0x01 - -#define ZCASH_USING_OVERWINTER 0x01 -#define ZCASH_USING_OVERWINTER_SAPLING 0x02 - -enum modes_e { - MODE_ISSUER = 0x00, - MODE_SETUP_NEEDED = 0xff, - MODE_WALLET = 0x01, - MODE_RELAXED_WALLET = 0x02, - MODE_SERVER = 0x04, - MODE_DEVELOPER = 0x08, -}; - -enum options_e { - OPTION_UNCOMPRESSED_KEYS = 0x01, - OPTION_DETERMINISTIC_SIGNATURE = 0x02, - OPTION_FREE_SIGHASHTYPE = 0x04, - OPTION_SKIP_2FA_P2SH = 0x08, - OPTION_ALLOW_ARBITRARY_CHANGE = 0x10 -}; /** * Current state of an untrusted transaction hashing @@ -156,8 +132,6 @@ struct tmp_output_s { typedef struct tmp_output_s tmp_output_t; struct context_s { - /** Flag if dongle has been halted */ - secu8 halted; /** Index of the output to convert into a trusted input in a transaction */ unsigned long int trustedInputIndex; /** (Integrity protected) transaction context */ @@ -200,9 +174,6 @@ struct context_s { /** Length of the outgoing command */ unsigned short outLength; - /** IO flags to reply with at the end of an APDU handler */ - unsigned char io_flags; - /** Status Word of the response */ unsigned short sw; @@ -284,4 +255,4 @@ typedef enum coin_kind_e { void context_init(void); -#endif +extern context_t context; diff --git a/lib-app-bitcoin/customizable_helpers.c b/lib-app-bitcoin/customizable_helpers.c new file mode 100644 index 00000000..78e70a39 --- /dev/null +++ b/lib-app-bitcoin/customizable_helpers.c @@ -0,0 +1,151 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "macros.h" + +#include "context.h" +#include "customizable_helpers.h" + +const unsigned char TRANSACTION_OUTPUT_SCRIPT_PRE[] = { + 0x19, 0x76, 0xA9, + 0x14}; // script length, OP_DUP, OP_HASH160, address length +const unsigned char TRANSACTION_OUTPUT_SCRIPT_POST[] = { + 0x88, 0xAC}; // OP_EQUALVERIFY, OP_CHECKSIG + +const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { + 0x17, 0xA9, 0x14}; // script length, OP_HASH160, address length +const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = {0x87}; // OP_EQUAL + +const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { + 0x3D, 0xA9, + 0x14}; // script length, OP_HASH160, address length + +const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = { + 0x87, // OP_EQUAL + 0x20, 0x9E, 0xC9, 0x84, 0x5A, 0xCB, 0x02, 0xFA, 0XB2, 0X4E, + 0x1C, 0x03, 0x68, 0xB3, 0xB5, 0x17, 0xC1, 0xA4, 0x48, 0x8F, + 0xBA, 0x97, 0xF0, 0xE3, 0x45, 0x9A, 0xC0, 0x53, 0xEA, 0x01, + 0x00, 0x00, 0x00, // ParamHash + 0x03, // Push 3 bytes to stack to make ParamHeight line up properly + 0xC0, 0x1F, 0x02, // ParamHeight (139200) -> hex -> endianness swapped + 0xB4}; // OP_CHECKBLOCKATHEIGHT + +const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE[] = {0x16, 0x00, 0x14}; +const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE[] = {0x22, 0x00, 0x20}; + +const unsigned char ZEN_OUTPUT_SCRIPT_PRE[] = { + 0x3F, 0x76, 0xA9, + 0x14}; // script length, OP_DUP, OP_HASH160, address length +const unsigned char ZEN_OUTPUT_SCRIPT_POST[] = { + 0x88, 0xAC, // OP_EQUALVERIFY, OP_CHECKSIG + 0x20, 0x9e, 0xc9, 0x84, 0x5a, 0xcb, 0x02, 0xfa, 0xb2, 0x4e, 0x1c, 0x03, + 0x68, 0xb3, 0xb5, 0x17, 0xc1, 0xa4, 0x48, 0x8f, 0xba, 0x97, 0xf0, 0xe3, + 0x45, 0x9a, 0xc0, 0x53, 0xea, 0x01, 0x00, 0x00, 0x00, // ParamHash + 0x03, // Push 3 bytes to stack to make ParamHeight line up properly + 0xc0, 0x1f, 0x02, // ParamHeight (139200) -> hex -> endianness swapped + 0xb4 // OP_CHECKBLOCKATHEIGHT +}; // BIP0115 Replay Protection + +WEAK unsigned char output_script_is_regular(unsigned char *buffer) { + if (COIN_NATIVE_SEGWIT_PREFIX) { + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || + (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { + return 1; + } + } + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) && + (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) { + return 1; + } + if (COIN_KIND == COIN_KIND_HORIZEN) { + if ((memcmp(buffer, ZEN_OUTPUT_SCRIPT_PRE, + sizeof(ZEN_OUTPUT_SCRIPT_PRE)) == 0) && + (memcmp(buffer + sizeof(ZEN_OUTPUT_SCRIPT_PRE) + 20, + ZEN_OUTPUT_SCRIPT_POST, + sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) { + return 1; + } + } + + return 0; +} + +WEAK unsigned char output_script_is_p2sh(unsigned char *buffer) { + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && + (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { + return 1; + } + if (COIN_KIND == COIN_KIND_HORIZEN) { + if ((memcmp(buffer, ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && + (memcmp(buffer + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, + ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { + return 1; + } + } + return 0; +} + +WEAK unsigned char output_script_is_native_witness(unsigned char *buffer) { + if (COIN_NATIVE_SEGWIT_PREFIX) { + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || + (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { + return 1; + } + } + return 0; +} + +WEAK unsigned char output_script_is_op_return(unsigned char *buffer) { + if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { + return ((buffer[1] == 0x6A) || ((buffer[1] == 0x00) && (buffer[2] == 0x6A))); + } + else { + return (buffer[1] == 0x6A); + } +} + +WEAK unsigned char output_script_is_op_create_or_call(unsigned char *buffer, + size_t size, + unsigned char value) { + return (!output_script_is_regular(buffer) && + !output_script_is_p2sh(buffer) && + !output_script_is_op_return(buffer) && (buffer[0] <= 0xEA) && + (buffer[0] < size) && + (buffer[buffer[0]] == value)); +} + +WEAK unsigned char output_script_is_op_create(unsigned char *buffer, + size_t size) { + return output_script_is_op_create_or_call(buffer, size, 0xC1); +} + +WEAK unsigned char output_script_is_op_call(unsigned char *buffer, + size_t size) { + return output_script_is_op_create_or_call(buffer, size, 0xC2); +} + diff --git a/include/bcd.h b/lib-app-bitcoin/customizable_helpers.h similarity index 58% rename from include/bcd.h rename to lib-app-bitcoin/customizable_helpers.h index 42eb41ff..ab7ac1c9 100644 --- a/include/bcd.h +++ b/lib-app-bitcoin/customizable_helpers.h @@ -15,14 +15,17 @@ * limitations under the License. ********************************************************************************/ -#ifndef BCD_H +#pragma once -#define BCD_H +#include "os.h" +#include "cx.h" -unsigned char -convert_hex_amount_to_displayable_no_globals(unsigned char *amount, unsigned int config_flag, unsigned char* out); +unsigned char output_script_is_regular(unsigned char *buffer); +unsigned char output_script_is_p2sh(unsigned char *buffer); +unsigned char output_script_is_op_return(unsigned char *buffer); +unsigned char output_script_is_native_witness(unsigned char *buffer); +unsigned char output_script_is_op_create(unsigned char *buffer, + size_t size); +unsigned char output_script_is_op_call(unsigned char *buffer, + size_t size); -unsigned char -convert_hex_amount_to_displayable(unsigned char *amount); - -#endif diff --git a/lib-app-bitcoin/customizable_ui.c b/lib-app-bitcoin/customizable_ui.c new file mode 100644 index 00000000..8a10da47 --- /dev/null +++ b/lib-app-bitcoin/customizable_ui.c @@ -0,0 +1,117 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "customizable_ui.h" +#include "customizable_helpers.h" +#include "helpers.h" +#include "context.h" +#include "display_variables.h" +#include "segwit_addr.h" +#include "cashaddr.h" +#include "be_operations.h" +#include "display_utils.h" +#include "read.h" + + +WEAK void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size) { + if (output_script_is_op_return(script)) { + strncpy(out, "OP_RETURN", out_size); + return; + } + if ((COIN_KIND == COIN_KIND_HYDRA) && + output_script_is_op_create(script, script_size)) { + strncpy(out, "OP_CREATE", out_size); + return; + } + if ((COIN_KIND == COIN_KIND_HYDRA) && + output_script_is_op_call(script, script_size)) { + strncpy(out, "OP_CALL", out_size); + return; + } + if (output_script_is_native_witness(script)) { + if (COIN_NATIVE_SEGWIT_PREFIX) { + segwit_addr_encode( + out, (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), 0, + script + OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET, + script[OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - 1]); + } + return; + } + unsigned char versionSize; + unsigned char address[22]; + unsigned short textSize; + int addressOffset = 3; + unsigned short version = COIN_P2SH_VERSION; + + if (output_script_is_regular(script)) { + addressOffset = 4; + version = COIN_P2PKH_VERSION; + } + + if (version > 255) { + versionSize = 2; + address[0] = (version >> 8); + address[1] = version; + } else { + versionSize = 1; + address[0] = version; + } + memmove(address + versionSize, script + addressOffset, 20); + + // Prepare address + if (context.usingCashAddr) { + cashaddr_encode( + address + versionSize, 20, (uint8_t *)out, out_size, + (version == COIN_P2SH_VERSION + ? CASHADDR_P2SH + : CASHADDR_P2PKH)); + } else { + textSize = public_key_to_encoded_base58( + address, 20 + versionSize, (unsigned char *)out, + out_size, version, 1); + out[textSize] = '\0'; + } +} + +WEAK uint8_t prepare_fees(void) { + if (context.transactionContext.relaxed) { + memmove(vars.tmp.feesAmount, "UNKNOWN", 7); + vars.tmp.feesAmount[7] = '\0'; + } else { + unsigned char fees[8]; + unsigned char borrow; + + borrow = transaction_amount_sub_be( + fees, context.transactionContext.transactionAmount, + context.totalOutputAmount); + if (borrow && COIN_KIND == COIN_KIND_KOMODO) { + memmove(vars.tmp.feesAmount, "REWARD", 6); + vars.tmp.feesAmount[6] = '\0'; + } + else { + if (borrow) { + PRINTF("Error : Fees not consistent"); + goto error; + } + format_sats_amount(COIN_COINID_SHORT, + (uint64_t)read_u64_be(fees, 0), // Cast prevents weird compilo bug + vars.tmp.feesAmount); + } + } + return 1; +error: + return 0; +} diff --git a/include/ecc.h b/lib-app-bitcoin/customizable_ui.h similarity index 82% rename from include/ecc.h rename to lib-app-bitcoin/customizable_ui.h index 4a07074d..75649c2b 100644 --- a/include/ecc.h +++ b/lib-app-bitcoin/customizable_ui.h @@ -14,13 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ - -#ifndef ECC_H - -#define ECC_H - +#pragma once #include "os.h" +#include "macros.h" -void compress_public_key_value(unsigned char *value); +void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size); -#endif +uint8_t prepare_fees(void); diff --git a/lib-app-bitcoin/display_utils.c b/lib-app-bitcoin/display_utils.c new file mode 100644 index 00000000..4bfae3f5 --- /dev/null +++ b/lib-app-bitcoin/display_utils.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include "ledger_assert.h" +#include "read.h" +#include "filesystem_tx.h" + +#include "./display_utils.h" + +// Division and modulus operators over uint64_t causes the inclusion of the __udivmoddi4 and other +// library functions that occupy more than 400 bytes. Since performance is not critical and division +// by 10 is sufficient, we avoid it with a binary search instead. +static uint64_t div10(uint64_t n) { + if (n < 10) return 0; // special case needed to make sure that n - 10 is safe + + // Since low, mid and high are always <= UINT64_MAX / 10, there is no risk of overflow + uint64_t low = 0; + uint64_t high = UINT64_MAX / 10; + + while (true) { + uint64_t mid = (low + high) / 2; + + // the result equals mid if and only if mid * 10 <= n < mid * 10 + 10 + // care is taken to make sure overflows and underflows are impossible + if (mid * 10 > n - 10 && n >= mid * 10) { + return mid; + } else if (n < mid * 10) { + high = mid - 1; + } else /* n >= 10 * mid + 10 */ { + low = mid + 1; + } + } +} + +static uint64_t div100000000(uint64_t n) { + uint64_t res = n; + for (int i = 0; i < 8; i++) res = div10(res); + return res; +} + +static size_t n_digits(uint64_t number) { + size_t count = 0; + do { + count++; + + // HACK: avoid __udivmoddi4 + // number /= 10; + + number = div10(number); + } while (number != 0); + return count; +} + +void format_sats_amount(const char *coin_name, + uint64_t amount, + char out[static MAX_AMOUNT_LENGTH + 1]) { + size_t coin_name_len = strlen(coin_name); + strncpy(out, coin_name, MAX_AMOUNT_LENGTH + 1); + out[coin_name_len] = ' '; + + char *amount_str = out + coin_name_len + 1; + + // HACK: avoid __udivmoddi4 + // uint64_t integral_part = amount / 100000000; + // uint32_t fractional_part = (uint32_t) (amount % 100000000); + uint64_t integral_part = div100000000(amount); + uint32_t fractional_part = (uint32_t) (amount - integral_part * 100000000); + + // format the integral part, starting from the least significant digit + size_t integral_part_digit_count = n_digits(integral_part); + for (unsigned int i = 0; i < integral_part_digit_count; i++) { + // HACK: avoid __udivmoddi4 + // amount_str[integral_part_digit_count - 1 - i] = '0' + (integral_part % 10); + // integral_part /= 10; + + uint64_t tmp_quotient = div10(integral_part); + char tmp_remainder = (char) (integral_part - 10 * tmp_quotient); + amount_str[integral_part_digit_count - 1 - i] = '0' + tmp_remainder; + integral_part = tmp_quotient; + } + + if (fractional_part == 0) { + amount_str[integral_part_digit_count] = '\0'; + } else { + // format the fractional part (exactly 8 digits, possibly with trailing zeros) + amount_str[integral_part_digit_count] = '.'; + char *fract_part_str = amount_str + integral_part_digit_count + 1; + snprintf(fract_part_str, 8 + 1, "%08u", fractional_part); + + // drop trailing zeros + for (int i = 7; i > 0 && fract_part_str[i] == '0'; i--) { + fract_part_str[i] = '\0'; + } + } +} + +unsigned char format_path(const unsigned char *bip32Path, char* out, unsigned char max_out_len) { + + unsigned char bip32PathLength; + unsigned char i, offset; + unsigned int current_level; + bool hardened; + + bip32PathLength = bip32Path[0]; + + LEDGER_ASSERT(bip32PathLength <= MAX_BIP32_PATH, "Wrong path len: %d", bip32PathLength); + + bip32Path++; + out[0] = ' '; + offset=1; + for (i = 0; i < bip32PathLength; i++) { + current_level = read_u32_be(bip32Path, 0); + hardened = (bool)(current_level & 0x80000000); + if(hardened) { + //remove hardening flag + current_level ^= 0x80000000; + } + bip32Path += 4; + snprintf(out+offset, max_out_len-offset, "%u", current_level); + offset = strnlen(out, max_out_len); + LEDGER_ASSERT(offset < max_out_len - 2, "OVERFLOW"); + if(hardened) out[offset++] = '\''; + + out[offset++] = '/'; + out[offset] = '\0'; + } + // remove last '/' + out[offset-1] = '\0'; + + return offset -1; +} + diff --git a/lib-app-bitcoin/display_utils.h b/lib-app-bitcoin/display_utils.h new file mode 100644 index 00000000..f5f28ba7 --- /dev/null +++ b/lib-app-bitcoin/display_utils.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +// up to 5 chars for ticker, 1 space, up to 20 digits (20 = digits of 2^64), + 1 decimal separator +#define MAX_AMOUNT_LENGTH (5 + 1 + 20 + 1) + +/** + * Converts a 64-bits unsigned integer into a decimal rapresentation, where the `amount` is a + * multiple of 1/100_000_000th. Trailing decimal zeros are not appended (and no decimal point is + * present if the `amount` is a multiple of 100_000_000). The resulting string is prefixed with a + * ticker name (up to 5 characters long), followed by a space. + * + * @param coin_name a zero-terminated ticker name, at most 5 characterso long (not including the + * terminating 0) + * @param amount the amount to format + * @param out the output array which must be at least MAX_AMOUNT_LENGTH + 1 bytes long + */ +void format_sats_amount(const char *coin_name, + uint64_t amount, + char out[static MAX_AMOUNT_LENGTH + 1]); + +unsigned char format_path(const unsigned char *bip32Path, char* out, unsigned char max_out_len); + diff --git a/src/display_variables.c b/lib-app-bitcoin/display_variables.c similarity index 100% rename from src/display_variables.c rename to lib-app-bitcoin/display_variables.c diff --git a/src/public_ram_variables.h b/lib-app-bitcoin/filesystem.h similarity index 65% rename from src/public_ram_variables.h rename to lib-app-bitcoin/filesystem.h index 3251c5da..fd615b04 100644 --- a/src/public_ram_variables.h +++ b/lib-app-bitcoin/filesystem.h @@ -15,14 +15,26 @@ * limitations under the License. ********************************************************************************/ -#ifndef _PUBLIC_RAM_VARIABLES_H_ -#define _PUBLIC_RAM_VARIABLES_H_ +#pragma once -#include "config.h" - -#include "secure_value.h" +#include "os.h" #include "context.h" +#include "filesystem_tx.h" + +enum family_e { + FAMILY_BITCOIN = 0x01, + FAMILY_PEERCOIN = 0x02, + FAMILY_STEALTH = 0x04 +}; + +typedef struct backup_area_s { + uint8_t trustedinput_key[32]; +} backup_area_t; -extern context_t context_D; +typedef struct storage_s { + backup_area_t bkp; +} storage_t; -#endif /* _PUBLIC_RAM_VARIABLES_H_ */ +// the global nvram memory variable +extern storage_t const N_real; +#define g_nvram_data (*(volatile storage_t *)PIC(&N_real)) diff --git a/include/filesystem_tx.h b/lib-app-bitcoin/filesystem_tx.h similarity index 97% rename from include/filesystem_tx.h rename to lib-app-bitcoin/filesystem_tx.h index e9f13f84..8a413c08 100644 --- a/include/filesystem_tx.h +++ b/lib-app-bitcoin/filesystem_tx.h @@ -15,10 +15,7 @@ * limitations under the License. ********************************************************************************/ -#ifndef FS_TX_H - -#define FS_TX_H - +#pragma once #include "os.h" #define MAX_BIP32_PATH 10 @@ -43,5 +40,3 @@ struct transaction_summary_s { unsigned char sighashType; }; typedef struct transaction_summary_s transaction_summary_t; - -#endif diff --git a/src/apdu_get_coin_version.c b/lib-app-bitcoin/handler/get_coin_version.c similarity index 53% rename from src/apdu_get_coin_version.c rename to lib-app-bitcoin/handler/get_coin_version.c index 97e0ac72..1366783d 100644 --- a/src/apdu_get_coin_version.c +++ b/lib-app-bitcoin/handler/get_coin_version.c @@ -15,29 +15,35 @@ * limitations under the License. ********************************************************************************/ -#include "internal.h" -#include "apdu_constants.h" +#include "io.h" +#include "write.h" -#define P1_VERSION_ONLY 0x00 -#define P1_VERSION_COINID 0x01 +#include "context.h" +#include "apdu_constants.h" -unsigned short apdu_get_coin_version() { +WEAK unsigned short handler_get_coin_version(void) { uint8_t offset = 0; + size_t string_size; + + write_u16_be(G_io_apdu_buffer, offset, COIN_P2PKH_VERSION); + offset += 2; + + write_u16_be(G_io_apdu_buffer, offset, COIN_P2SH_VERSION); + offset += 2; - G_io_apdu_buffer[offset++] = COIN_P2PKH_VERSION >> 8; - G_io_apdu_buffer[offset++] = COIN_P2PKH_VERSION; - G_io_apdu_buffer[offset++] = COIN_P2SH_VERSION >> 8; - G_io_apdu_buffer[offset++] = COIN_P2SH_VERSION; G_io_apdu_buffer[offset++] = COIN_FAMILY; - G_io_apdu_buffer[offset++] = strlen(COIN_COINID); - memmove(G_io_apdu_buffer + offset, COIN_COINID, - strlen(COIN_COINID)); - offset += strlen(COIN_COINID); - G_io_apdu_buffer[offset++] = strlen(COIN_COINID_SHORT); - memmove(G_io_apdu_buffer + offset, COIN_COINID_SHORT, - strlen(COIN_COINID_SHORT)); - offset += strlen(COIN_COINID_SHORT); - context_D.outLength = offset; - - return SW_OK; + + string_size = strlen(COIN_COINID); + G_io_apdu_buffer[offset++] = string_size; + memmove(G_io_apdu_buffer + offset, COIN_COINID, string_size); + offset += string_size; + + string_size = strlen(COIN_COINID_SHORT); + G_io_apdu_buffer[offset++] = string_size + memmove(G_io_apdu_buffer + offset, COIN_COINID_SHORT, string_size); + offset += string_size; + + context.outLength = offset; + + return io_send_response_pointer(G_io_apdu_buffer, offset, SW_OK); } diff --git a/src/apdu_get_firmware_version.c b/lib-app-bitcoin/handler/get_firmware_version.c similarity index 52% rename from src/apdu_get_firmware_version.c rename to lib-app-bitcoin/handler/get_firmware_version.c index b2b90954..6f11725a 100644 --- a/src/apdu_get_firmware_version.c +++ b/lib-app-bitcoin/handler/get_firmware_version.c @@ -14,52 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ +#include "io.h" -#include "internal.h" +#include "context.h" #include "apdu_constants.h" -#define FEATURES_COMPRESSED_KEYS 0x01 #define FEATURES_SELF_SCREEN_BUTTONS 0x02 -#define FEATURES_EXTERNAL_SCREEN_BUTTONS 0x04 #define FEATURES_NFC 0x08 #define FEATURES_BLE 0x10 -#define FEATURES_TEE 0x20 #define MODE_SETUP 0x01 #define MODE_OPERATION 0x02 +#define FEATURES (FEATURES_NFC | FEATURES_BLE | FEATURES_SELF_SCREEN_BUTTONS) +#define MODE (MODE_SETUP | MODE_OPERATION) + #define ARCH_ID 0x30 -// Java Card is 0x60 #define TCS_LOADER_PATCH_VERSION 0 -void get_firmware_version(unsigned char *buffer) { - buffer[0] = ARCH_ID; - buffer[1] = MAJOR_VERSION; - buffer[2] = MINOR_VERSION; - buffer[3] = PATCH_VERSION; - buffer[4] = 1; - buffer[5] = TCS_LOADER_PATCH_VERSION; -} - -unsigned short apdu_get_firmware_version() { - G_io_apdu_buffer[0] = - (((N_btchip.bkp.config.options & OPTION_UNCOMPRESSED_KEYS) != 0) - ? 0x00 - : 0x01); - - G_io_apdu_buffer[0] |= FEATURES_NFC; - G_io_apdu_buffer[0] |= FEATURES_BLE; - - G_io_apdu_buffer[0] |= FEATURES_SELF_SCREEN_BUTTONS; - - get_firmware_version(G_io_apdu_buffer + 1); +WEAK unsigned short handler_get_firmware_version() { - G_io_apdu_buffer[7] = 0x00; - G_io_apdu_buffer[7] |= MODE_SETUP; - G_io_apdu_buffer[7] |= MODE_OPERATION; + G_io_apdu_buffer[0] = FEATURES; + G_io_apdu_buffer[1] = ARCH_ID; + G_io_apdu_buffer[2] = MAJOR_VERSION; + G_io_apdu_buffer[3] = MINOR_VERSION; + G_io_apdu_buffer[4] = PATCH_VERSION; + G_io_apdu_buffer[5] = 1; + G_io_apdu_buffer[6] = TCS_LOADER_PATCH_VERSION; + G_io_apdu_buffer[7] = MODE; - context_D.outLength = 0x08; + context.outLength = 0x08; - return SW_OK; + return io_send_response_pointer(G_io_apdu_buffer, 0x08, SW_OK); } diff --git a/src/apdu_get_trusted_input.c b/lib-app-bitcoin/handler/get_trusted_input.c similarity index 51% rename from src/apdu_get_trusted_input.c rename to lib-app-bitcoin/handler/get_trusted_input.c index 48fa119a..6925b36b 100644 --- a/src/apdu_get_trusted_input.c +++ b/lib-app-bitcoin/handler/get_trusted_input.c @@ -14,58 +14,59 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" #include "lib_standard_app/read.h" #include "lib_standard_app/write.h" +#include "io.h" + +#include "context.h" +#include "filesystem.h" +#include "transaction.h" +#include "apdu_constants.h" #define GET_TRUSTED_INPUT_P1_FIRST 0x00 #define GET_TRUSTED_INPUT_P1_NEXT 0x80 -unsigned short apdu_get_trusted_input() { - unsigned char apduLength; +WEAK unsigned short handler_get_trusted_input(buffer_t* buffer, uint8_t p1, uint8_t p2) { unsigned char dataOffset = 0; - apduLength = G_io_apdu_buffer[ISO_OFFSET_LC]; - if (G_io_apdu_buffer[ISO_OFFSET_P1] == GET_TRUSTED_INPUT_P1_FIRST) { + if (p1 == GET_TRUSTED_INPUT_P1_FIRST) { // Initialize - context_D.transactionTargetInput = - read_u32_be(G_io_apdu_buffer, ISO_OFFSET_CDATA); - context_D.transactionContext.transactionState = + context.transactionTargetInput = + read_u32_be(buffer->ptr, 0); + context.transactionContext.transactionState = TRANSACTION_NONE; - context_D.trustedInputProcessed = 0; - context_D.transactionContext.consumeP2SH = 0; + context.trustedInputProcessed = 0; + context.transactionContext.consumeP2SH = 0; dataOffset = 4; - context_D.transactionHashOption = TRANSACTION_HASH_FULL; - context_D.usingSegwit = 0; - context_D.usingOverwinter = 0; - } else if (G_io_apdu_buffer[ISO_OFFSET_P1] != GET_TRUSTED_INPUT_P1_NEXT) { - return SW_INCORRECT_P1_P2; + context.transactionHashOption = TRANSACTION_HASH_FULL; + context.usingSegwit = 0; + context.usingOverwinter = 0; + } else if (p1 != GET_TRUSTED_INPUT_P1_NEXT) { + return io_send_sw(SW_INCORRECT_P1_P2); } - if (G_io_apdu_buffer[ISO_OFFSET_P2] != 0x00) { - return SW_INCORRECT_P1_P2; + if (p2 != 0x00) { + return io_send_sw(SW_INCORRECT_P1_P2); } - context_D.transactionBufferPointer = - G_io_apdu_buffer + ISO_OFFSET_CDATA + dataOffset; - context_D.transactionDataRemaining = apduLength - dataOffset; + + context.transactionBufferPointer = (uint8_t* ) buffer->ptr + dataOffset; + context.transactionDataRemaining = buffer->size - dataOffset; transaction_parse(PARSE_MODE_TRUSTED_INPUT); - if (context_D.transactionContext.transactionState == + if (context.transactionContext.transactionState == TRANSACTION_PARSED) { - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_NONE; - if (!context_D.trustedInputProcessed) { + if (!context.trustedInputProcessed) { // Output was not found - return SW_INCORRECT_DATA; + return io_send_sw(SW_INCORRECT_DATA); } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, CX_LAST, + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, NULL, 0, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32)) { - return SW_TECHNICAL_PROBLEM; + return io_send_sw(SW_TECHNICAL_PROBLEM); } // Otherwise prepare @@ -75,14 +76,14 @@ unsigned short apdu_get_trusted_input() { cx_hash_sha256(G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32, G_io_apdu_buffer + 4, 32); write_u32_le(G_io_apdu_buffer, 4 + 32, - context_D.transactionTargetInput); + context.transactionTargetInput); memmove(G_io_apdu_buffer + 4 + 32 + 4, - context_D.transactionContext.transactionAmount, 8); + context.transactionContext.transactionAmount, 8); - cx_hmac_sha256((uint8_t *)N_btchip.bkp.trustedinput_key, - sizeof(N_btchip.bkp.trustedinput_key), G_io_apdu_buffer, + cx_hmac_sha256((uint8_t *)g_nvram_data.bkp.trustedinput_key, + sizeof(g_nvram_data.bkp.trustedinput_key), G_io_apdu_buffer, TRUSTED_INPUT_SIZE, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32); - context_D.outLength = TRUSTED_INPUT_TOTAL_SIZE; + context.outLength = TRUSTED_INPUT_TOTAL_SIZE; } - return SW_OK; + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } diff --git a/src/apdu_get_wallet_public_key.c b/lib-app-bitcoin/handler/get_wallet_public_key.c similarity index 62% rename from src/apdu_get_wallet_public_key.c rename to lib-app-bitcoin/handler/get_wallet_public_key.c index 53baced4..d376bda6 100644 --- a/src/apdu_get_wallet_public_key.c +++ b/lib-app-bitcoin/handler/get_wallet_public_key.c @@ -15,18 +15,28 @@ * limitations under the License. ********************************************************************************/ -#include "internal.h" +#include "context.h" +#include "helpers.h" #include "apdu_constants.h" -#include "bagl_extensions.h" +#include "extensions.h" #include "segwit_addr.h" #include "cashaddr.h" -#include "apdu_get_wallet_public_key.h" #include "lib_standard_app/read.h" #include "swap.h" +#include "io.h" -int get_public_key_chain_code(unsigned char* keyPath, size_t keyPath_len, unsigned char* publicKey, unsigned char* chainCode) { +#define P1_NO_DISPLAY 0x00 +#define P1_DISPLAY 0x01 +#define P1_REQUEST_TOKEN 0x02 + +#define P2_LEGACY 0x00 +#define P2_SEGWIT 0x01 +#define P2_NATIVE_SEGWIT 0x02 +#define P2_CASHADDR 0x03 + +static int get_public_key_chain_code(const unsigned char* keyPath, size_t keyPath_len, unsigned char* publicKey, unsigned char* chainCode) { uint8_t public_key[65]; int keyLength = 0; @@ -42,59 +52,57 @@ int get_public_key_chain_code(unsigned char* keyPath, size_t keyPath_len, unsign return keyLength; } -unsigned short apdu_get_wallet_public_key() { +WEAK unsigned short handler_get_wallet_public_key(buffer_t* buffer, uint8_t p1, uint8_t p2) { unsigned char keyLength; unsigned char chainCode[32]; uint8_t is_derivation_path_unusual = 0; - bool display = (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_DISPLAY); - bool segwit = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_SEGWIT); - bool nativeSegwit = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NATIVE_SEGWIT); - bool cashAddr = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_CASHADDR); + bool display = (p1 == P1_DISPLAY); + bool segwit = (p2 == P2_SEGWIT); + bool nativeSegwit = (p2 == P2_NATIVE_SEGWIT); + bool cashAddr = (p2 == P2_CASHADDR); + if (display && G_called_from_swap) { - return SW_INCORRECT_DATA; + PRINTF("Refused INS when in SWAP mode\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); } - switch (G_io_apdu_buffer[ISO_OFFSET_P1]) { - case P1_NO_DISPLAY: - case P1_DISPLAY: - case P1_REQUEST_TOKEN: - break; - default: - return SW_INCORRECT_P1_P2; + + if (p1 != P1_NO_DISPLAY && p1 != P1_DISPLAY) { + PRINTF("Wrong P1 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); } - switch (G_io_apdu_buffer[ISO_OFFSET_P2]) { - case P2_NATIVE_SEGWIT: - if (!(COIN_NATIVE_SEGWIT_PREFIX)) { - return SW_INCORRECT_P1_P2; - } - __attribute__((fallthrough)); - case P2_LEGACY: - case P2_SEGWIT: - break; - case P2_CASHADDR: - if (COIN_KIND != COIN_KIND_BITCOIN_CASH) { - return SW_INCORRECT_P1_P2; - } - break; - default: - return SW_INCORRECT_P1_P2; + if (p2 == P2_NATIVE_SEGWIT && !COIN_NATIVE_SEGWIT_PREFIX) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); } - if (G_io_apdu_buffer[ISO_OFFSET_LC] < 0x01) { - return SW_INCORRECT_LENGTH; + if (p2 == P2_CASHADDR && COIN_KIND != COIN_KIND_BITCOIN_CASH) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); } + + if (p2 != P2_NATIVE_SEGWIT && p2 != P2_LEGACY && p2 != P2_SEGWIT && p2 != P2_CASHADDR) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if (buffer->size < 0x01) { + PRINTF("Wrong size\n"); + return io_send_sw(SW_INCORRECT_LENGTH); + } + if (display) { - is_derivation_path_unusual = set_key_path_to_display(G_io_apdu_buffer + ISO_OFFSET_CDATA); + is_derivation_path_unusual = set_key_path_to_display(buffer->ptr); } - unsigned char bip44_enforced = enforce_bip44_coin_type(G_io_apdu_buffer + ISO_OFFSET_CDATA, true); + unsigned char bip44_enforced = enforce_bip44_coin_type(buffer->ptr, true); G_io_apdu_buffer[0] = 65; - keyLength = get_public_key_chain_code(G_io_apdu_buffer + ISO_OFFSET_CDATA, MAX_BIP32_PATH_LENGTH, G_io_apdu_buffer + 1, chainCode); + keyLength = get_public_key_chain_code(buffer->ptr, MAX_BIP32_PATH_LENGTH, G_io_apdu_buffer + 1, chainCode); if (keyLength == 0) { - return SW_TECHNICAL_PROBLEM; + return io_send_sw(SW_TECHNICAL_PROBLEM); } if (cashAddr) { @@ -145,7 +153,7 @@ unsigned short apdu_get_wallet_public_key() { // output chain code memmove(G_io_apdu_buffer + 1 + 65 + 1 + keyLength, chainCode, sizeof(chainCode)); - context_D.outLength = 1 + 65 + 1 + keyLength + sizeof(chainCode); + context.outLength = 1 + 65 + 1 + keyLength + sizeof(chainCode); // privacy : force display the address if the path isn't standard // and could reveal another fork holdings according to BIP 44 rules @@ -155,31 +163,24 @@ unsigned short apdu_get_wallet_public_key() { if (display) { if (keyLength > 50) { - return SW_INCORRECT_DATA; + return io_send_sw(SW_INCORRECT_DATA); } // Hax, avoid wasting space memmove(G_io_apdu_buffer + 200, G_io_apdu_buffer + 67, keyLength); G_io_apdu_buffer[200 + keyLength] = '\0'; - context_D.io_flags |= IO_ASYNCH_REPLY; - bagl_display_public_key(is_derivation_path_unusual); + display_public_key(is_derivation_path_unusual); + return 0; } - - return SW_OK; + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } -void bagl_user_action_display(unsigned char confirming) { - unsigned short sw = SW_OK; +int user_action_display(unsigned char confirming) { // confirm and finish the apdu exchange //spaghetti if (confirming) { - context_D.outLength -= - 2; // status was already set by the last call + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } else { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - context_D.outLength = 0; + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); } - G_io_apdu_buffer[context_D.outLength++] = sw >> 8; - G_io_apdu_buffer[context_D.outLength++] = sw; - - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, context_D.outLength); } diff --git a/lib-app-bitcoin/handler/hash_input_finalize_full.c b/lib-app-bitcoin/handler/hash_input_finalize_full.c new file mode 100644 index 00000000..8fc61a9d --- /dev/null +++ b/lib-app-bitcoin/handler/hash_input_finalize_full.c @@ -0,0 +1,661 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +// TODO Trustlet, BAGL : process each output separately. +// review nvm_write policy + +#include "crypto_helpers.h" +#include "read.h" +#include "swap.h" +#include "io.h" + +#include "context.h" +#include "helpers.h" +#include "customizable_helpers.h" +#include "apdu_constants.h" +#include "extensions.h" +#include "ui.h" +#include "be_operations.h" + +#define FINALIZE_P1_MORE 0x00 +#define FINALIZE_P1_LAST 0x80 +#define FINALIZE_P1_CHANGEINFO 0xFF + +#define FINALIZE_P2_DEFAULT 0x00 + +#define FLAG_SIGNATURE 0x01 +#define FLAG_CHANGE_VALIDATED 0x80 + +void hash_input_finalize_full_reset(void) { + context.currentOutputOffset = 0; + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + memset(context.totalOutputAmount, 0, + sizeof(context.totalOutputAmount)); + context.changeOutputFound = 0; +} + +static int check_output_displayable(bool* displayable) { + *displayable = true; + unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, + nullAmount = 1; + + for (j = 0; j < 8; j++) { + if (context.currentOutput[j] != 0) { + nullAmount = 0; + break; + } + } + if (!nullAmount) { + swap_bytes(amount, context.currentOutput, 8); + transaction_amount_add_be(context.totalOutputAmount, + context.totalOutputAmount, amount); + } + isOpReturn = + output_script_is_op_return(context.currentOutput + 8); + isP2sh = output_script_is_p2sh(context.currentOutput + 8); + isNativeSegwit = output_script_is_native_witness( + context.currentOutput + 8); +#ifndef __clang_analyzer__ + unsigned char isOpCreate = + output_script_is_op_create(context.currentOutput + 8, + sizeof(context.currentOutput) - 8); + unsigned char isOpCall = + output_script_is_op_call(context.currentOutput + 8, + sizeof(context.currentOutput) - 8); + if (((COIN_KIND == COIN_KIND_HYDRA) && + !output_script_is_regular(context.currentOutput + 8) && + !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || + (!(COIN_KIND == COIN_KIND_HYDRA) && + !output_script_is_regular(context.currentOutput + 8) && + !isP2sh && !(nullAmount && isOpReturn))) { + PRINTF("Error : Unrecognized output script"); + return -1; + } +#endif + if (context.tmpCtx.output.changeInitialized && !isOpReturn) { + bool changeFound = false; + unsigned char addressOffset = + (isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET + : isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH + : OUTPUT_SCRIPT_REGULAR_PRE_LENGTH); + if (!isP2sh && + memcmp(context.currentOutput + 8 + addressOffset, + context.tmpCtx.output.changeAddress, + 20) == 0) { + changeFound = true; + } else if (isP2sh && context.usingSegwit) { + unsigned char changeSegwit[22]; + changeSegwit[0] = 0x00; + changeSegwit[1] = 0x14; + memmove(changeSegwit + 2, + context.tmpCtx.output.changeAddress, 20); + public_key_hash160(changeSegwit, 22, changeSegwit); + if (memcmp(context.currentOutput + 8 + addressOffset, + changeSegwit, 20) == 0) { + if (COIN_FLAGS & FLAG_SEGWIT_CHANGE_SUPPORT) { + changeFound = true; + } else { + // Attempt to avoid fatal failures on Bitcoin Cash + PRINTF("Error : Non spendable Segwit change"); + return -1; + } + } + } + if (changeFound) { + if (context.changeOutputFound) { + PRINTF("Error : Multiple change output found"); + return -1; + } + context.changeOutputFound = true; + *displayable = false; + } + } + return 0; +} + +int handle_output_state(unsigned int* processed) { + uint32_t discardSize = 0; + context.discardSize = 0; + *processed = 0; + switch (context.outputParsingState) { + case OUTPUT_PARSING_NUMBER_OUTPUTS: { + context.totalOutputs = 0; + if (context.currentOutputOffset < 1) { + break; + } + if (context.currentOutput[0] < 0xFD) { + context.totalOutputs = context.remainingOutputs = + context.currentOutput[0]; + discardSize = 1; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } + if (context.currentOutput[0] == 0xFD) { + if (context.currentOutputOffset < 3) { + break; + } + context.totalOutputs = context.remainingOutputs = + (context.currentOutput[2] << 8) | + context.currentOutput[1]; + discardSize = 3; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } else if (context.currentOutput[0] == 0xFE) { + if (context.currentOutputOffset < 5) { + break; + } + context.totalOutputs = context.remainingOutputs = + read_u32_le(context.currentOutput, 1); + discardSize = 5; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } else { + return -1; + } + } break; + + case OUTPUT_PARSING_OUTPUT: { + unsigned int scriptSize = 0; + if (context.currentOutputOffset < 9) { + break; + } + if (context.currentOutput[8] < 0xFD) { + scriptSize = context.currentOutput[8]; + discardSize = 1; + } else if (context.currentOutput[8] == 0xFD) { + if (context.currentOutputOffset < 9 + 2) { + break; + } + scriptSize = read_u32_le(context.currentOutput, 9); + discardSize = 3; + } else { + // Unrealistically large script + return -1; + } + if (context.currentOutputOffset < + 8 + discardSize + scriptSize) { + discardSize = 0; + break; + } + + *processed = 1; + + discardSize += 8 + scriptSize; + + bool displayable; + if (check_output_displayable(&displayable)) { + return -1; + } + + if (displayable) { + // The output can be processed by the UI + + context.discardSize = discardSize; + discardSize = 0; + *processed = 2; + } else { + context.remainingOutputs--; + } + } break; + + default: + return -1; + } + + if (discardSize != 0) { + memmove(context.currentOutput, + context.currentOutput + discardSize, + context.currentOutputOffset - discardSize); + context.currentOutputOffset -= discardSize; + } + + return 0; +} + +// out should be 32 bytes, even only 20 bytes is significant for output +int get_pubkey_hash160(unsigned char* keyPath, size_t keyPath_len, unsigned char* out) { + cx_ecfp_public_key_t public_key; + if (get_public_key(keyPath, keyPath_len, public_key.W, NULL)) { + return -1; + } + compress_public_key_value(public_key.W); + + public_key_hash160( + public_key.W, // IN + 33, // INLEN + out // OUT + ); + return 0; +} + +static unsigned short hash_input_finalize_full_internal(transaction_summary_t *transactionSummary, buffer_t* buffer, uint8_t p1, uint8_t p2, bool *async) { + unsigned char authorizationHash[32]; + unsigned short sw = SW_OK; + unsigned char *target = G_io_apdu_buffer; + unsigned char hashOffset = 0; + + (void) p2; + + if ((p1 != FINALIZE_P1_MORE) && (p1 != FINALIZE_P1_LAST) && + (p1 != FINALIZE_P1_CHANGEINFO)) { + return SW_INCORRECT_P1_P2; + } + + // See if there is a hashing offset + if (context.usingSegwit && + (context.tmpCtx.output.multipleOutput == 0)) { + unsigned char firstByte = buffer->ptr[0]; + if (firstByte < 0xfd) { + hashOffset = 1; + } else if (firstByte == 0xfd) { + hashOffset = 3; + } else if (firstByte == 0xfe) { + hashOffset = 5; + } + } + + // Check state + if (context.transactionContext.transactionState != + TRANSACTION_PRESIGN_READY) { + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; + } + + if (p1 == FINALIZE_P1_CHANGEINFO) { + if (!context.transactionContext.firstSigned) { + // Already validated, should be prevented on the client side + return SW_OK; + } + if (!context.tmpCtx.output.changeAccepted) { + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; + } + memset(transactionSummary, 0, + sizeof(transaction_summary_t)); + if (buffer->ptr[0] == 0x00) { + // Called with no change path, abort, should be prevented on + // the client side + return SW_OK; + } + memmove(transactionSummary->keyPath, + buffer->ptr, + MAX_BIP32_PATH_LENGTH); + + if (get_pubkey_hash160(transactionSummary->keyPath, sizeof(transactionSummary->keyPath), context.tmpCtx.output.changeAddress)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Change address = %.*H\n", 20, context.tmpCtx.output.changeAddress); + + context.tmpCtx.output.changeInitialized = 1; + context.tmpCtx.output.changeAccepted = 0; + + // if the bip44 change path provided is not canonical or its index are unsual, ask for user approval + if(bip44_derivation_guard(transactionSummary->keyPath, true)) { + if (G_called_from_swap) { + PRINTF("In swap mode only standart path is allowed\n"); + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; + } + *async = true; + context.outputParsingState = BIP44_CHANGE_PATH_VALIDATION; + request_change_path_approval(transactionSummary->keyPath); + } + + return SW_OK; + } + + // Always update the transaction & authorization hashes with the + // given data + // For SegWit, this has been reset to hold hashOutputs + if (!context.segwitParsedOnce) { + if ((int)(buffer->size - hashOffset) < 0) { + sw = SW_INCORRECT_DATA; + goto discardTransaction; + } + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, buffer->ptr + hashOffset, buffer->size - hashOffset, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + else { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", buffer->size - hashOffset, buffer->ptr + hashOffset); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + hashOffset, + buffer->size - hashOffset, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + } + + if (context.transactionContext.firstSigned) { + if ((context.currentOutputOffset + buffer->size) > + sizeof(context.currentOutput)) { + PRINTF("Output is too long to be checked\n"); + sw = SW_INCORRECT_DATA; + goto discardTransaction; + } + memmove(context.currentOutput + + context.currentOutputOffset, + buffer->ptr, buffer->size); + context.currentOutputOffset += buffer->size; + + unsigned int processed = 1; + while (processed == 1) { + if (handle_output_state(&processed)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + + if (processed == 2) { + *async = true; + } + // Finalize the TX if necessary + + if ((context.remainingOutputs == 0) && + (!*async)) { + *async = true; + context.outputParsingState = + OUTPUT_FINALIZE_TX; + } + } + + if (p1 == FINALIZE_P1_MORE) { + if (!context.usingSegwit) { + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); + if (cx_hash_no_throw( + &context.transactionHashAuthorization.header, + 0, buffer->ptr, buffer->size, + NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + G_io_apdu_buffer[0] = 0x00; + context.outLength = 1; + context.tmpCtx.output.multipleOutput = 1; + return SW_OK; + } + + if (!context.usingSegwit) { + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, + CX_LAST, buffer->ptr, + buffer->size, authorizationHash, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + + if (context.usingSegwit) { + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, context.segwit.cache.hashedOutputs, 0, context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + else { + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, + context.segwit.cache.hashedOutputs, 0, + context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, + context.segwit.cache.hashedOutputs, + sizeof(context.segwit.cache.hashedOutputs), + context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + PRINTF("hashOutputs\n%.*H\n",32,context.segwit.cache.hashedOutputs); + if (cx_hash_no_throw( + &context.transactionHashAuthorization.header, + CX_LAST, G_io_apdu_buffer, 0, authorizationHash, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); + } else { + if (cx_hash_no_throw( + &context.transactionHashAuthorization.header, + CX_LAST, + (unsigned char *)&context.segwit.cache, + sizeof(context.segwit.cache), + authorizationHash, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); + } + } + + if (context.transactionContext.firstSigned) { + if (!context.tmpCtx.output.changeInitialized) { + memset(transactionSummary, 0, + sizeof(transaction_summary_t)); + } + + transactionSummary->payToAddressVersion = COIN_P2PKH_VERSION; + transactionSummary->payToScriptHashVersion = COIN_P2SH_VERSION; + + // Generate new nonce + + cx_rng(transactionSummary->transactionNonce, 8); + } + + G_io_apdu_buffer[0] = 0x00; + target++; + + *target = 0x00; + target++; + + context.outLength = (target - G_io_apdu_buffer); + + // Check that the input being signed is part of the same + // transaction, otherwise abort + // (this is done to keep the transaction counter limit per session + // synchronized) + if (context.transactionContext.firstSigned) { + memmove(transactionSummary->authorizationHash, + authorizationHash, + sizeof(transactionSummary->authorizationHash)); + return SW_OK; + } else { + if (os_secure_memcmp( + authorizationHash, + transactionSummary->authorizationHash, + sizeof(transactionSummary->authorizationHash))) { + PRINTF("Authorization hash not matching, aborting\n"); + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; + } + + if (context.usingSegwit && + !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = + TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = + TRANSACTION_SIGN_READY; + } + hash_input_finalize_full_reset(); + return SW_OK; + } + +discardTransaction: + hash_input_finalize_full_reset(); + ui_transaction_error(); + context.transactionContext.transactionState = + TRANSACTION_NONE; + context.outLength = 0; + + memmove(G_io_apdu_buffer, context.currentOutput, + context.currentOutputOffset); + context.outLength = context.currentOutputOffset; + return sw; +} + +unsigned short handler_hash_input_finalize_full(buffer_t* buffer, uint8_t p1, uint8_t p2) { + bool is_async = false; + PRINTF("state=%d\n", context.outputParsingState); + unsigned short sw = hash_input_finalize_full_internal( + &context.transactionSummary, buffer, p1, p2, &is_async); + + if (is_async) { + // if the UI reject the processing of the request, then reply + // immediately + int status; + if(context.outputParsingState == BIP44_CHANGE_PATH_VALIDATION) { + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + return 0; + } + else if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + status = finalize_tx(); + } + else { + status = confirm_single_output(); + } + if (status == 0) { + ui_transaction_error(); + context.transactionContext.transactionState = + TRANSACTION_NONE; + context.outLength = 0; + sw = SW_INCORRECT_DATA; + return io_send_sw(sw); + } + else if (status == 2) { + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + return 0; + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); +} + +unsigned char user_action(unsigned char confirming) { + unsigned short sw = SW_OK; + + // confirm and finish the apdu exchange //spaghetti + + if (confirming) { + // Check if all inputs have been confirmed + + if (context.outputParsingState == + OUTPUT_PARSING_OUTPUT) { + context.remainingOutputs--; + } + + while (context.remainingOutputs != 0) { + memmove(context.currentOutput, + context.currentOutput + + context.discardSize, + context.currentOutputOffset - + context.discardSize); + context.currentOutputOffset -= + context.discardSize; + unsigned int processed = 1; + while (processed == 1) { + if (handle_output_state(&processed)) { + context.transactionContext.transactionState = + TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + break; + } + } + + if (processed == 2) { + if (!confirm_single_output()) { + context.transactionContext.transactionState = + TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + break; + } else { + // Let the UI play + return 1; + } + } else { + // Out of data to process, wait for the next call + break; + } + } + + if ((context.outputParsingState == + OUTPUT_PARSING_OUTPUT) && + (context.remainingOutputs == 0)) { + context.outputParsingState = OUTPUT_FINALIZE_TX; + if (!finalize_tx()) { + context.outputParsingState = + OUTPUT_PARSING_NONE; + context.transactionContext.transactionState = + TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + } else { + // Let the UI play + return 1; + } + } + + if (context.outputParsingState == + OUTPUT_FINALIZE_TX) { + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && + !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = + TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = + TRANSACTION_SIGN_READY; + } + } + } else { + // Discard transaction + context.transactionContext.transactionState = + TRANSACTION_NONE; + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + context.outLength = 0; + } + + if ((context.outputParsingState == OUTPUT_FINALIZE_TX) || + (sw != SW_OK)) { + + // we've finished the processing of the input + hash_input_finalize_full_reset(); + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + +} diff --git a/lib-app-bitcoin/handler/hash_input_start.c b/lib-app-bitcoin/handler/hash_input_start.c new file mode 100644 index 00000000..274418a9 --- /dev/null +++ b/lib-app-bitcoin/handler/hash_input_start.c @@ -0,0 +1,136 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "io.h" + +#include "context.h" +#include "transaction.h" +#include "apdu_constants.h" +#include "extensions.h" +#include "swap.h" + +#define P1_FIRST 0x00 +#define P1_NEXT 0x80 +#define P2_NEW 0x00 +#define P2_NEW_SEGWIT 0x02 +#define P2_NEW_SEGWIT_CASHADDR 0x03 +#define P2_NEW_SEGWIT_OVERWINTER 0x04 +#define P2_NEW_SEGWIT_SAPLING 0x05 +#define P2_CONTINUE 0x80 + +#define IS_INPUT() \ + (buffer->size - 1 > 8 \ + && buffer->size - 1 <= TRUSTED_INPUT_TOTAL_SIZE + 2 \ + && buffer->ptr[0] <= 0x02) \ + +#define IS_INPUT_TRUSTED() \ + (buffer->ptr[0] == 0x01 \ + && buffer->ptr[1] == TRUSTED_INPUT_TOTAL_SIZE \ + && buffer->ptr[2] == MAGIC_TRUSTED_INPUT \ + && buffer->ptr[3] == 0x00) + +WEAK unsigned short handler_hash_input_start(buffer_t* buffer, uint8_t p1, uint8_t p2) { + if (p1 == P1_FIRST) { + // Initialize + context.transactionContext.transactionState = + TRANSACTION_NONE; + context.transactionHashOption = TRANSACTION_HASH_BOTH; + } else if (p1 != P1_NEXT) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if ((p2 == P2_NEW) || + (p2 == P2_NEW_SEGWIT) || + (p2 == P2_NEW_SEGWIT_CASHADDR) || + (p2 == P2_NEW_SEGWIT_OVERWINTER) || + (p2 == P2_NEW_SEGWIT_SAPLING)) { + if (p1 == P1_FIRST) { + unsigned char usingSegwit = + (p2 == P2_NEW_SEGWIT) || + (p2 == P2_NEW_SEGWIT_CASHADDR) || + (p2 == P2_NEW_SEGWIT_OVERWINTER) || + (p2 == P2_NEW_SEGWIT_SAPLING); + // Master transaction reset + context.transactionContext.firstSigned = 1; + context.transactionContext.consumeP2SH = 0; + context.transactionContext.relaxed = 0; + context.usingSegwit = usingSegwit; + + if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { + unsigned char usingCashAddr = (p2 == P2_NEW_SEGWIT_CASHADDR); + context.usingCashAddr = usingCashAddr; + } + else { + context.usingCashAddr = 0; + } + + context.usingOverwinter = 0; + if ((COIN_KIND == COIN_KIND_ZCASH) || (COIN_KIND == COIN_KIND_KOMODO) || (COIN_KIND == COIN_KIND_ZCLASSIC) || (COIN_KIND == COIN_KIND_RESISTANCE)) { + if (p2 == P2_NEW_SEGWIT_OVERWINTER) { + context.usingOverwinter = ZCASH_USING_OVERWINTER; + } + else + if (p2 == P2_NEW_SEGWIT_SAPLING) { + context.usingOverwinter = ZCASH_USING_OVERWINTER_SAPLING; + } + } + context.overwinterSignReady = 0; + context.segwitParsedOnce = 0; + // Initialize for screen pairing + memset(&context.tmpCtx.output, 0, + sizeof(context.tmpCtx.output)); + context.tmpCtx.output.changeAccepted = 1; + // Reset segwitWarningSeen flag to prevent displaying the warning for each + // segwit input when coontinuing from a previous session (P2=0x80) + context.segwitWarningSeen = 0; + } + } else if (p2 != P2_CONTINUE) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + + // In segwit mode, warn user one time only to update its client wallet... + if (context.usingSegwit + && !context.segwitWarningSeen + && (p1 == P1_NEXT) + && (p2 != P2_CONTINUE) + // ...if input is not passed as a TrustedInput + && IS_INPUT() + && !IS_INPUT_TRUSTED()) + { + if(G_called_from_swap){ + /* There is no point in displaying a warning when the app is signing + in silent mode, as its UI is hidden behind the exchange app*/ + return io_send_sw(SW_SWAP_WITHOUT_TRUSTED_INPUTS); + } + context.segwitWarningSeen = 1; + request_segwit_input_approval(); + // Start parsing of the 1st chunk + context.transactionBufferPointer = (uint8_t*) buffer->ptr; + context.transactionDataRemaining = buffer->size; + + transaction_parse(PARSE_MODE_SIGNATURE); + return 0; + } + + // Start parsing of the 1st chunk + context.transactionBufferPointer = (uint8_t*) buffer->ptr; + context.transactionDataRemaining = buffer->size; + + transaction_parse(PARSE_MODE_SIGNATURE); + + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); +} diff --git a/lib-app-bitcoin/handler/hash_sign.c b/lib-app-bitcoin/handler/hash_sign.c new file mode 100644 index 00000000..c4496660 --- /dev/null +++ b/lib-app-bitcoin/handler/hash_sign.c @@ -0,0 +1,192 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "macros.h" +#include "io.h" +#include "ledger_assert.h" +#include "read.h" +#include "write.h" +#include "swap.h" + +#include "context.h" +#include "helpers.h" +#include "apdu_constants.h" +#include "extensions.h" +#include "display_variables.h" +#include "ui.h" + +#define SIGHASH_ALL 0x01 + +#ifndef COIN_FORKID +#define COIN_FORKID 0 +#endif + +WEAK unsigned short handler_hash_sign(buffer_t* buffer, uint8_t p1, uint8_t p2) { + unsigned long int lockTime; + uint32_t sighashType; + unsigned char dataBuffer[8]; + unsigned char authorizationLength; + unsigned char *parameters = (uint8_t*)buffer->ptr; + + if ((p1 != 0) || (p2 != 0)) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + +#define HASH_LENGTH 1 + 1 + 4 + 1 + if (buffer->size < HASH_LENGTH) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + // Zcash special - store parameters for later + + if ((context.usingOverwinter) && + (!context.overwinterSignReady) && + (context.segwitParsedOnce) && + (context.transactionContext.transactionState == TRANSACTION_NONE)) { + unsigned long int expiryHeight; + parameters += (4 * buffer->ptr[0]) + 1; + authorizationLength = *(parameters++); + parameters += authorizationLength; + lockTime = read_u32_be(parameters, 0); + parameters += 4; + sighashType = *(parameters++); + expiryHeight = read_u32_be(parameters, 0); + write_u32_le(context.nLockTime, 0, lockTime); + write_u32_le(context.sigHashType, 0, sighashType); + write_u32_le(context.nExpiryHeight, 0, expiryHeight); + context.overwinterSignReady = 1; + return io_send_sw(SW_OK); + } + + if (context.transactionContext.transactionState != + TRANSACTION_SIGN_READY) { + PRINTF("Invalid transaction state %d\n", context.transactionContext.transactionState); + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + if (context.usingOverwinter && !context.overwinterSignReady) { + PRINTF("Overwinter not ready to sign\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + // Read parameters + if (buffer->ptr[0] > MAX_BIP32_PATH) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); + } + memmove(context.transactionSummary.keyPath, + buffer->ptr, + MAX_BIP32_PATH_LENGTH); + parameters += (4 * buffer->ptr[0]) + 1; + authorizationLength = *(parameters++); + parameters += authorizationLength; + lockTime = read_u32_be(parameters, 0); + parameters += 4; + sighashType = *(parameters++); + context.transactionSummary.sighashType = sighashType; + + // if bitcoin cash OR forkid is set, then use the fork id + if ((COIN_KIND == COIN_KIND_BITCOIN_CASH) || + (COIN_FORKID)) { +#define SIGHASH_FORKID 0x40 + if (sighashType != (SIGHASH_ALL | SIGHASH_FORKID)) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); + } + sighashType |= (COIN_FORKID << 8); + + } else { + if (sighashType != SIGHASH_ALL) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); + } + } + + // Finalize the hash + if (!context.usingOverwinter) { + write_u32_le(dataBuffer, 0, lockTime); + write_u32_le(dataBuffer, 4, sighashType); + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + dataBuffer, sizeof(dataBuffer), NULL, 0)) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); + } + } + + // Check if the path needs to be enforced + if (!enforce_bip44_coin_type(context.transactionSummary.keyPath, false)) { + request_sign_path_approval(context.transactionSummary.keyPath); + } + else { + // Sign immediately + user_action_signtx(1, 0); + } + if (G_called_from_swap) { + // if we signed all outputs we should exit, + // but only after sending response, so lets raise the + // vars.swap_data.should_exit flag and check it on timer later + vars.swap_data.alreadySignedInputs++; + if (vars.swap_data.alreadySignedInputs >= vars.swap_data.totalNumberOfInputs) { + vars.swap_data.should_exit = 1; + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + return 0; +} + +int user_action_signtx(unsigned char confirming, unsigned char direct) { + // confirm and finish the apdu exchange //spaghetti + if (confirming) { + unsigned char hash[32]; + if (context.usingOverwinter) { + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, hash, 0, hash, 32) == CX_OK, "Hash Failed"); + } + else { + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, + hash, 0, hash, 32) == CX_OK, "Hash Failed"); + PRINTF("Hash1\n%.*H\n", sizeof(hash), hash); + + // Rehash + cx_hash_sha256(hash, sizeof(hash), hash, 32); + } + PRINTF("Hash2\n%.*H\n", sizeof(hash), hash); + // Sign + size_t out_len = sizeof(G_io_apdu_buffer); + sign_finalhash( + context.transactionSummary.keyPath, + sizeof(context.transactionSummary.keyPath), + hash, sizeof(hash), + G_io_apdu_buffer, &out_len); + + context.outLength = G_io_apdu_buffer[1] + 2; + G_io_apdu_buffer[context.outLength++] = context.transactionSummary.sighashType; + ui_transaction_finish(); + + } else { + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + if (!direct) { + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + return 0; +} + diff --git a/lib-app-bitcoin/handler/sign_message.c b/lib-app-bitcoin/handler/sign_message.c new file mode 100644 index 00000000..01a6a332 --- /dev/null +++ b/lib-app-bitcoin/handler/sign_message.c @@ -0,0 +1,279 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "context.h" +#include "helpers.h" +#include "apdu_constants.h" +#include "extensions.h" +#include "lib_standard_app/read.h" +#include "swap.h" +#include "io.h" + +#define P1_PREPARE 0x00 +#define P1_SIGN 0x80 +#define P2_LEGACY 0x00 +#define P2_FIRST 0x01 +#define P2_OTHER 0x80 + +#define BITID_NONE 0 +#define BITID_POWERCYCLE 1 +#define BITID_MULTIPLE 2 + +unsigned char const SIGNMAGIC[] = {' ', 'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M', + 'e', 's', 's', 'a', 'g', 'e', ':', '\n'}; + +// TODO : support longer messages +unsigned char message_check_bit_id(unsigned char *bip32Path) { + unsigned char i; + unsigned char bip32PathLength = bip32Path[0]; + bip32Path++; + for (i = 0; i < bip32PathLength; i++) { + unsigned short account = read_u32_be(bip32Path, 0); + bip32Path += 4; + + if (account == BITID_DERIVE) { + return BITID_POWERCYCLE; + } + if (account == BITID_DERIVE_MULTIPLE) { + return BITID_MULTIPLE; + } + } + return BITID_NONE; +} + +unsigned short message_compute_hash(void) { + unsigned char hash[32]; + unsigned short sw = SW_OK; + + context.outLength = 0; + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, hash, + 0, hash, 32)) { + goto discard; + } + + if (cx_hash_sha256(hash, sizeof(hash), hash, 32) == 0) { + goto discard; + } + + size_t out_len = 100; + sign_finalhash( + context.transactionSummary.keyPath, + sizeof(context.transactionSummary.keyPath), + hash, sizeof(hash), // IN + G_io_apdu_buffer, &out_len); // OUT + context.outLength = G_io_apdu_buffer[1] + 2; + memset(&context.transactionSummary, 0, + sizeof(transaction_summary_t)); + return sw; + + discard: + sw = SW_TECHNICAL_PROBLEM_2; + return sw; +} + + +static unsigned short sign_message_internal(buffer_t* buffer, uint8_t p1, uint8_t p2) { + unsigned short sw = SW_OK; + unsigned char apduLength = buffer->size; + unsigned short offset = 0; + + if ((p1 != P1_PREPARE) && (p1 != P1_SIGN)) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + if (p1 == P1_PREPARE) { + if ((p2 != P2_FIRST) && (p2 != P2_OTHER) && (p2 != P2_LEGACY)) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + } + + if (p1 == P1_PREPARE) { + if ((p2 == P2_FIRST) || (p2 == P2_LEGACY)) { + unsigned char chunkLength; + unsigned char messageLength[3]; + unsigned char messageLengthSize; + memset(&context.transactionSummary, 0, + sizeof(transaction_summary_t)); + if (buffer->ptr[0] > MAX_BIP32_PATH) { + PRINTF("Invalid path\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + context.transactionSummary.payToAddressVersion = COIN_P2PKH_VERSION; + context.transactionSummary.payToScriptHashVersion = COIN_P2SH_VERSION; + memmove( + context.transactionSummary.keyPath, + buffer->ptr, MAX_BIP32_PATH_LENGTH); + offset += (4 * buffer->ptr[0]) + 1; + if (p2 == P2_LEGACY) { + context.transactionSummary.messageLength = + buffer->ptr[offset]; + offset++; + } else { + context.transactionSummary.messageLength = + (buffer->ptr[offset] << 8) | + (buffer->ptr[offset + 1]); + offset += 2; + } + if (context.transactionSummary.messageLength == + 0) { + PRINTF("Null message length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + context.hashedMessageLength = 0; + + // Horizen signed message magic header is "Zcash" + // See https://github.com/HorizenOfficial/zen/blob/v5.0.0/src/main.cpp#L122 + const char* magicHeader = (COIN_KIND != COIN_KIND_HORIZEN) ? COIN_COINID : "Zcash"; + + cx_sha256_init_no_throw(&context.transactionHashFull.sha256); + cx_sha256_init_no_throw(&context.transactionHashAuthorization); + + chunkLength = + strlen(magicHeader) + sizeof(SIGNMAGIC); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + &chunkLength, 1, NULL, 0)) { + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + (uint8_t *)magicHeader, + strlen(magicHeader), NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + (unsigned char *)SIGNMAGIC, sizeof(SIGNMAGIC), NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (context.transactionSummary.messageLength < + 0xfd) { + messageLength[0] = + context.transactionSummary.messageLength; + messageLengthSize = 1; + } else { + messageLength[0] = 0xfd; + messageLength[1] = + (context.transactionSummary.messageLength & + 0xff); + messageLength[2] = ((context.transactionSummary + .messageLength >> + 8) & + 0xff); + messageLengthSize = 3; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + messageLength, messageLengthSize, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + chunkLength = apduLength - offset; + if ((context.hashedMessageLength + chunkLength) > + context.transactionSummary.messageLength) { + PRINTF("Invalid data length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + offset, chunkLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw( + &context.transactionHashAuthorization.header, + 0, buffer->ptr + offset, chunkLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + context.hashedMessageLength += chunkLength; + G_io_apdu_buffer[0] = 0x00; + if (context.hashedMessageLength == + context.transactionSummary.messageLength) { + G_io_apdu_buffer[1] = 0x00; + context.outLength = 2; + } else { + context.outLength = 1; + } + } else { + if ((context.hashedMessageLength + apduLength) > + context.transactionSummary.messageLength) { + PRINTF("Invalid data length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + offset, apduLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw( + &context.transactionHashAuthorization.header, + 0, buffer->ptr + offset, apduLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + context.hashedMessageLength += apduLength; + G_io_apdu_buffer[0] = 0x00; + if (context.hashedMessageLength == + context.transactionSummary.messageLength) { + G_io_apdu_buffer[1] = 0x00; + context.outLength = 2; + } else { + context.outLength = 1; + } + } + } else { + if ((context.transactionSummary.messageLength == 0) || + (context.hashedMessageLength != + context.transactionSummary.messageLength)) { + PRINTF("Invalid length to sign\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (message_check_bit_id(context.transactionSummary.keyPath) != BITID_NONE) { + sw = message_compute_hash(); + } else { + confirm_message_signature(); + return 0; + } + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + + discard : + memset(&context.transactionSummary, 0, + sizeof(transaction_summary_t)); + return io_send_sw(sw); +} + +WEAK unsigned short handler_sign_message(buffer_t* buffer, uint8_t p1, uint8_t p2) { + if (G_called_from_swap) { + return io_send_sw(SW_SECURITY_STATUS_NOT_SATISFIED); + } + + return sign_message_internal(buffer, p1, p2); +} + +int user_action_message_signing(unsigned char confirming) { + unsigned short sw; + if (confirming) { + sw = message_compute_hash(); + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + } else { + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } +} diff --git a/lib-app-bitcoin/helpers.c b/lib-app-bitcoin/helpers.c new file mode 100644 index 00000000..50a50f8b --- /dev/null +++ b/lib-app-bitcoin/helpers.c @@ -0,0 +1,231 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "lib_standard_app/crypto_helpers.h" +#include "lib_standard_app/bip32.h" +#include "ledger_assert.h" +#include "io.h" +#include "base58.h" +#include "read.h" + +#include "context.h" +#include "helpers.h" + +void public_key_hash160(unsigned char *in, unsigned short inlen, + unsigned char out[static CX_RIPEMD160_SIZE]) { + unsigned char buffer[CX_SHA256_SIZE]; + cx_hash_sha256(in, inlen, buffer, sizeof(buffer)); + cx_ripemd160_hash(buffer, sizeof(buffer), out); +} + +void compute_checksum(unsigned char* in, unsigned short inlen, unsigned char * output) { + unsigned char checksumBuffer[32]; + cx_hash_sha256(in, inlen, checksumBuffer, 32); + cx_hash_sha256(checksumBuffer, 32, checksumBuffer, 32); + + PRINTF("Checksum\n%.*H\n",4,checksumBuffer); + memmove(output, checksumBuffer, 4); +} + +unsigned short public_key_to_encoded_base58( + unsigned char *in, unsigned short inlen, unsigned char *out, + unsigned short outlen, unsigned short version, + unsigned char alreadyHashed) { + unsigned char tmpBuffer[34]; + + unsigned char versionSize = (version > 255 ? 2 : 1); + short outputLen; + + if (!alreadyHashed) { + PRINTF("To hash\n%.*H\n",inlen,in); + public_key_hash160(in, inlen, tmpBuffer + versionSize); + PRINTF("Hash160\n%.*H\n",20,(tmpBuffer + versionSize)); + if (version > 255) { + tmpBuffer[0] = (version >> 8); + tmpBuffer[1] = version; + } else { + tmpBuffer[0] = version; + } + } else { + memmove(tmpBuffer, in, 20 + versionSize); + } + + compute_checksum(tmpBuffer, 20 + versionSize, tmpBuffer + 20 + versionSize); + + outputLen = base58_encode(tmpBuffer, 24 + versionSize, (char *)out, outlen); + LEDGER_ASSERT(outputLen >= 0, "Error encoding public key"); + + return outputLen; +} + +void swap_bytes(unsigned char *target, unsigned char *source, + unsigned char size) { + unsigned char i; + for (i = 0; i < size; i++) { + target[i] = source[size - 1 - i]; + } +} + +/* +Checks if the values of a derivation path are within "normal" (arbitrary) ranges: +Account < 100, change == 1 or 0, address index < 50000 +Returns 1 if the path is unusual, or not compliant with BIP44*/ +unsigned char bip44_derivation_guard(const unsigned char *bip32Path, bool is_change_path) { + unsigned char path_len; + bip32_path_t bip32PathInt; + + path_len = bip32Path[0]; + if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { + return 1; + } + + // If the path length is not compliant with BIP44 or if the purpose don't match regular usage, return a warning + if(path_len != BIP44_PATH_LEN || + ((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 44 && + (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 49 && + (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 84)) { + return 1; + } + + // If the coin type doesn't match, return a warning + if ((BIP44_COIN_TYPE != 0) && + (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE) && + ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE_2))) { + return 1; + } + + // If the account or address index is very high or if the change isn't 1, return a warning + if((bip32PathInt.path[BIP44_ACCOUNT_OFFSET]^0x80000000) > MAX_BIP44_ACCOUNT_RECOMMENDED || + bip32PathInt.path[BIP44_CHANGE_OFFSET] != is_change_path?1:0 || + bip32PathInt.path[BIP44_ADDRESS_INDEX_OFFSET] > MAX_BIP44_ADDRESS_INDEX_RECOMMENDED) { + return 1; + } + + return 0; +} + +/* +Only enforce the structure or coin type for consumed UTXOs or a public address +Returns 0 if the path is non compliant, or 1 if compliant +*/ +unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, bool for_pubkey) { + bip32_path_t bip32PathInt; + // No enforcement required + if (BIP44_COIN_TYPE == 0) { + return 1; + } + // Path is too short - always require a user validation if signing + if (bip32Path[0] < 2) { + return for_pubkey; + } + + if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { + return 1; + } + + // Path is not compliant with BIP 44 or derivatives - valid if not signing + if (!(((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 44 || + (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 49 || + (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 84))) { + return for_pubkey; + } + + if (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE) || + ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE_2)) { + // Valid BIP 44 path + return 1; + } + // Everything else needs a user validation + return 0; +} + +int sign_finalhash(unsigned char* path, size_t path_len, unsigned char *in, unsigned short inlen, + unsigned char *out, size_t* outlen) { + + unsigned int info = 0; + + io_seproxyhal_io_heartbeat(); + + bip32_path_t bip32Path; + bip32Path.length = path[0]; + + if (!parse_serialized_path(&bip32Path, path, path_len)) { + return -1; + } + + if (bip32_derive_ecdsa_sign_hash_256( + CX_CURVE_SECP256K1, + bip32Path.path, + bip32Path.length, + CX_LAST | CX_RND_RFC6979, + CX_SHA256, + in, + inlen, + out, + outlen, + &info) != CX_OK) { + return -1; + } + + // Store information about the parity of the 'y' coordinate + if (info & CX_ECCINFO_PARITY_ODD) { + out[0] |= 0x01; + } + + io_seproxyhal_io_heartbeat(); + return 0; +} + +int get_public_key(const unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode) { + + bip32_path_t bip32Path; + + if (!parse_serialized_path(&bip32Path, keyPath, keyPath_len)) { + return -1; + } + + if (bip32_derive_get_pubkey_256( + CX_CURVE_SECP256K1, + bip32Path.path, + bip32Path.length, + raw_pubkey, + chainCode, + CX_SHA512) != CX_OK) + { + return -1; + } + + return 0; +} + +void compress_public_key_value(unsigned char *value) { + bool odd = (value[64] & 1); + value[0] = odd ? 0x03 : 0x02; +} + +bool parse_serialized_path(bip32_path_t* path, const unsigned char* serialized_path, unsigned char serialized_path_length) { + if (serialized_path_length < 1 || + serialized_path[0] > MAX_BIP32_PATH || + serialized_path[0] * 4 + 1 > serialized_path_length) + return false; + path->length = serialized_path[0]; + serialized_path++; + for (int i = 0; i < path->length; i += 1, serialized_path += 4) { + path->path[i] = read_u32_be(serialized_path, 0); + } + return true; +} diff --git a/include/helpers.h b/lib-app-bitcoin/helpers.h similarity index 55% rename from include/helpers.h rename to lib-app-bitcoin/helpers.h index febe122a..88314f74 100644 --- a/include/helpers.h +++ b/lib-app-bitcoin/helpers.h @@ -15,13 +15,12 @@ * limitations under the License. ********************************************************************************/ -#ifndef HELPERS_H - -#define HELPERS_H +#pragma once #include "os.h" #include "cx.h" #include "stdbool.h" +#include "filesystem_tx.h" #define OUTPUT_SCRIPT_REGULAR_PRE_LENGTH 4 #define OUTPUT_SCRIPT_REGULAR_POST_LENGTH 2 @@ -30,36 +29,29 @@ #define OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET 3 -unsigned char output_script_is_regular(unsigned char *buffer); -unsigned char output_script_is_p2sh(unsigned char *buffer); -unsigned char output_script_is_op_return(unsigned char *buffer); -unsigned char output_script_is_native_witness(unsigned char *buffer); - -unsigned char output_script_is_op_create(unsigned char *buffer, - size_t size); -unsigned char output_script_is_op_call(unsigned char *buffer, - size_t size); +typedef struct bip32_path { + unsigned char length; + unsigned int path[MAX_BIP32_PATH]; +} bip32_path_t; void public_key_hash160(unsigned char *in, unsigned short inlen, - unsigned char *out); + unsigned char out [static CX_RIPEMD160_SIZE]); unsigned short public_key_to_encoded_base58( unsigned char *in, unsigned short inlen, unsigned char *out, unsigned short outlen, unsigned short version, unsigned char alreadyHashed); -unsigned char bip44_derivation_guard(unsigned char *bip32Path, bool is_change_path); -unsigned char enforce_bip44_coin_type(unsigned char *bip32Path, bool for_pubkey); -unsigned char bip32_print_path(unsigned char *bip32Path, char* out, unsigned char max_out_len); +unsigned char bip44_derivation_guard(const unsigned char *bip32Path, bool is_change_path); +unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, bool for_pubkey); void swap_bytes(unsigned char *target, unsigned char *source, unsigned char size); int sign_finalhash(unsigned char *path, size_t path_len, unsigned char *in, unsigned short inlen, - unsigned char *out, size_t* outlen, - unsigned char rfc6979); + unsigned char *out, size_t* outlen); + +int get_public_key(const unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode); -void transaction_add_output(unsigned char *hash160Address, - unsigned char *amount, unsigned char p2sh); -int get_public_key(unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode); +void compress_public_key_value(unsigned char *value); -#endif +bool parse_serialized_path(bip32_path_t* path, const unsigned char* serialized_path, unsigned char serialized_path_length); diff --git a/src/swap/handle_check_address.c b/lib-app-bitcoin/swap/handle_check_address.c similarity index 93% rename from src/swap/handle_check_address.c rename to lib-app-bitcoin/swap/handle_check_address.c index 1fd13dec..95e6d166 100644 --- a/src/swap/handle_check_address.c +++ b/lib-app-bitcoin/swap/handle_check_address.c @@ -1,14 +1,19 @@ #include "handle_check_address.h" -#include "os.h" #include "helpers.h" -#include "bip32_path.h" -#include "ecc.h" -#include "apdu_get_wallet_public_key.h" +#include "context.h" #include "cashaddr.h" #include "segwit_addr.h" -#include -bool derive_compressed_public_key( +#define P1_NO_DISPLAY 0x00 +#define P1_DISPLAY 0x01 +#define P1_REQUEST_TOKEN 0x02 + +#define P2_LEGACY 0x00 +#define P2_SEGWIT 0x01 +#define P2_NATIVE_SEGWIT 0x02 +#define P2_CASHADDR 0x03 + +static bool derive_compressed_public_key( unsigned char* serialized_path, unsigned char serialized_path_length, unsigned char* public_key, unsigned char public_key_length) { UNUSED(public_key_length); @@ -23,7 +28,7 @@ bool derive_compressed_public_key( return true; } -bool get_address_from_compressed_public_key( +static bool get_address_from_compressed_public_key( unsigned char format, unsigned char* compressed_pub_key, unsigned short payToAddressVersion, diff --git a/src/swap/handle_check_address.h b/lib-app-bitcoin/swap/handle_check_address.h similarity index 91% rename from src/swap/handle_check_address.h rename to lib-app-bitcoin/swap/handle_check_address.h index 01d8e138..2e5eaa54 100644 --- a/src/swap/handle_check_address.h +++ b/lib-app-bitcoin/swap/handle_check_address.h @@ -2,7 +2,6 @@ #define _HANDLE_CHECK_ADDRESS_H_ #include "swap_lib_calls.h" -#include "context.h" void swap_handle_check_address(check_address_parameters_t* check_address_params); diff --git a/lib-app-bitcoin/swap/handle_get_printable_amount.c b/lib-app-bitcoin/swap/handle_get_printable_amount.c new file mode 100644 index 00000000..d801bfab --- /dev/null +++ b/lib-app-bitcoin/swap/handle_get_printable_amount.c @@ -0,0 +1,20 @@ +#include "read.h" + +#include "handle_get_printable_amount.h" +#include "display_utils.h" + +void swap_handle_get_printable_amount(get_printable_amount_parameters_t* params) { + params->printable_amount[0] = 0; + if (params->amount_length > 8) { + PRINTF("Amount is too big"); + return; + } + unsigned char amount[8]; + memset(amount, 0, 8); + memcpy(amount + (8 - params->amount_length), params->amount, params->amount_length); + + format_sats_amount(COIN_COINID_SHORT, + (uint64_t)(read_u64_be(amount, 0)), // Cast prevents weird compilo bug + params->printable_amount); + return; +} \ No newline at end of file diff --git a/src/swap/handle_get_printable_amount.h b/lib-app-bitcoin/swap/handle_get_printable_amount.h similarity index 100% rename from src/swap/handle_get_printable_amount.h rename to lib-app-bitcoin/swap/handle_get_printable_amount.h diff --git a/src/swap/handle_swap_sign_transaction.c b/lib-app-bitcoin/swap/handle_swap_sign_transaction.c similarity index 83% rename from src/swap/handle_swap_sign_transaction.c rename to lib-app-bitcoin/swap/handle_swap_sign_transaction.c index c29af60c..9452ef94 100644 --- a/src/swap/handle_swap_sign_transaction.c +++ b/lib-app-bitcoin/swap/handle_swap_sign_transaction.c @@ -1,6 +1,5 @@ #include "handle_swap_sign_transaction.h" #include "os_io_seproxyhal.h" -#include "public_ram_variables.h" #include "display_variables.h" #include "context.h" #include "usbd_core.h" @@ -68,28 +67,6 @@ bool swap_copy_transaction_parameters(create_transaction_parameters_t* params) { return true; } -void swap_handle_swap_sign_transaction(void) { - context_init(); - io_seproxyhal_init(); - UX_INIT(); -#ifdef HAVE_NBGL - nbgl_useCaseSpinner("Signing"); -#endif // HAVE_BAGL - USB_power(0); - USB_power(1); - //ui_idle(); - PRINTF("USB power ON/OFF\n"); -#ifdef TARGET_NANOX - // grab the current plane mode setting - G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); -#endif // TARGET_NANOX -#ifdef HAVE_BLE - BLE_power(0, NULL); - BLE_power(1, "Nano X"); -#endif // HAVE_BLE - app_main(); -} - void __attribute__((noreturn)) swap_finalize_exchange_sign_transaction(bool is_success) { *G_swap_sign_return_value_address = is_success; os_lib_end(); diff --git a/src/swap/handle_swap_sign_transaction.h b/lib-app-bitcoin/swap/handle_swap_sign_transaction.h similarity index 83% rename from src/swap/handle_swap_sign_transaction.h rename to lib-app-bitcoin/swap/handle_swap_sign_transaction.h index 7203d065..a9e2914c 100644 --- a/src/swap/handle_swap_sign_transaction.h +++ b/lib-app-bitcoin/swap/handle_swap_sign_transaction.h @@ -2,12 +2,9 @@ #define _HANDLE_SWAP_SIGN_TRANSACTION_H_ #include "swap_lib_calls.h" -#include "context.h" bool swap_copy_transaction_parameters(create_transaction_parameters_t* sign_transaction_params); -void swap_handle_swap_sign_transaction(void); - void __attribute__((noreturn)) swap_finalize_exchange_sign_transaction(bool is_success); #endif // _HANDLE_SWAP_SIGN_TRANSACTION_H_ diff --git a/src/transaction.c b/lib-app-bitcoin/transaction.c similarity index 57% rename from src/transaction.c rename to lib-app-bitcoin/transaction.c index 2806d623..ab59b215 100644 --- a/src/transaction.c +++ b/lib-app-bitcoin/transaction.c @@ -15,14 +15,19 @@ * limitations under the License. ********************************************************************************/ -#include "internal.h" -#include "apdu_constants.h" -#include "display_variables.h" #include "ledger_assert.h" #include "lib_standard_app/read.h" #include "lib_standard_app/write.h" #include "swap.h" +#include "apdu_constants.h" +#include "display_variables.h" +#include "be_operations.h" +#include "transaction.h" +#include "context.h" +#include "filesystem.h" +#include "helpers.h" + #ifndef COIN_CONSENSUS_BRANCH_ID #define COIN_CONSENSUS_BRANCH_ID 0 #endif @@ -31,95 +36,57 @@ #define CONSENSUS_BRANCH_ID_SAPLING 0x76b809bb #define CONSENSUS_BRANCH_ID_ZCLASSIC 0x930b540d +unsigned char const OVERWINTER_PARAM_PREVOUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'P', 'r', 'e', 'v', 'o', 'u', 't', 'H', 'a', 's', 'h' }; +unsigned char const OVERWINTER_PARAM_SEQUENCE[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'e', 'q', 'u', 'e', 'n', 'c', 'H', 'a', 's', 'h' }; +unsigned char const OVERWINTER_PARAM_OUTPUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'O', 'u', 't', 'p', 'u', 't', 's', 'H', 'a', 's', 'h' }; +unsigned char const OVERWINTER_PARAM_SIGHASH[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'i', 'g', 'H', 'a', 's', 'h', 0, 0, 0, 0 }; +unsigned char const OVERWINTER_NO_JOINSPLITS[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + // Check if fOverwintered flag is set and if nVersion is >= 0x03 #define TRUSTED_INPUT_OVERWINTER ( (COIN_KIND == COIN_KIND_ZCASH || \ COIN_KIND == COIN_KIND_ZCLASSIC || \ COIN_KIND == COIN_KIND_KOMODO) && \ - (read_u32_le(context_D.transactionVersion, 0) & (1<<31)) && \ - (read_u32_le(context_D.transactionVersion, 0) ^ (1<<31)) >= 0x03 \ + (read_u32_le(context.transactionVersion, 0) & (1<<31)) && \ + (read_u32_le(context.transactionVersion, 0) ^ (1<<31)) >= 0x03 \ ) -#define DEBUG_LONG "%d" - -void check_transaction_available(unsigned char x) { - if (context_D.transactionDataRemaining < x) { - PRINTF("Check transaction available failed %d < %d\n", context_D.transactionDataRemaining, x); - THROW(EXCEPTION); - } +static void check_transaction_available(unsigned char x) { + LEDGER_ASSERT(context.transactionDataRemaining >= x, "Check transaction available failed %d < %d\n", context.transactionDataRemaining, x); } #define OP_HASH160 0xA9 #define OP_EQUAL 0x87 #define OP_CHECKMULTISIG 0xAE -unsigned char transaction_amount_add_be(unsigned char *target, - unsigned char *a, - unsigned char *b) { - unsigned char carry = 0; - unsigned char i; - for (i = 0; i < 8; i++) { - unsigned short val = a[8 - 1 - i] + b[8 - 1 - i] + (carry ? 1 : 0); - carry = (val > 255); - target[8 - 1 - i] = (val & 255); - } - return carry; -} - -unsigned char transaction_amount_sub_be(unsigned char *target, - unsigned char *a, - unsigned char *b) { - unsigned char borrow = 0; - unsigned char i; - for (i = 0; i < 8; i++) { - unsigned short tmpA = a[8 - 1 - i]; - unsigned short tmpB = b[8 - 1 - i]; - if (borrow) { - if (tmpA <= tmpB) { - tmpA += (255 + 1) - 1; - } else { - borrow = 0; - tmpA--; - } - } - if (tmpA < tmpB) { - borrow = 1; - tmpA += 255 + 1; - } - target[8 - 1 - i] = (unsigned char)(tmpA - tmpB); - } - - return borrow; -} - -void transaction_offset(unsigned char value) { - if ((context_D.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", value, context_D.transactionBufferPointer); - if (context_D.usingOverwinter) { - LEDGER_ASSERT(cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); +static void transaction_offset(unsigned char value) { + if ((context.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", value, context.transactionBufferPointer); + if (context.usingOverwinter) { + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); } else { - LEDGER_ASSERT(cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - context_D.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); } } - if ((context_D.transactionHashOption & + if ((context.transactionHashOption & TRANSACTION_HASH_AUTHORIZATION) != 0) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", value, context_D.transactionBufferPointer); - LEDGER_ASSERT(cx_hash_no_throw(&context_D.transactionHashAuthorization.header, 0, - context_D.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", value, context.transactionBufferPointer); + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashAuthorization.header, 0, + context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); } } -void transaction_offset_increase(unsigned char value) { +static void transaction_offset_increase(unsigned char value) { transaction_offset(value); - context_D.transactionBufferPointer += value; - context_D.transactionDataRemaining -= value; + context.transactionBufferPointer += value; + context.transactionDataRemaining -= value; } -unsigned long int transaction_get_varint(void) { +static unsigned long int transaction_get_varint(void) { unsigned char firstByte; check_transaction_available(1); - firstByte = *context_D.transactionBufferPointer; + firstByte = *context.transactionBufferPointer; if (firstByte < 0xFD) { transaction_offset_increase(1); return firstByte; @@ -128,8 +95,8 @@ unsigned long int transaction_get_varint(void) { transaction_offset_increase(1); check_transaction_available(2); result = - (unsigned long int)(*context_D.transactionBufferPointer) | - ((unsigned long int)(*(context_D.transactionBufferPointer + + (unsigned long int)(*context.transactionBufferPointer) | + ((unsigned long int)(*(context.transactionBufferPointer + 1)) << 8); transaction_offset_increase(2); @@ -138,39 +105,34 @@ unsigned long int transaction_get_varint(void) { unsigned long int result; transaction_offset_increase(1); check_transaction_available(4); - result = read_u32_le(context_D.transactionBufferPointer, 0); + result = read_u32_le(context.transactionBufferPointer, 0); transaction_offset_increase(4); return result; } else { - PRINTF("Varint parsing failed\n"); - THROW(INVALID_PARAMETER); - return 0; + LEDGER_ASSERT(false, "Varint parsing failed"); + __builtin_unreachable(); } } void transaction_parse(unsigned char parseMode) { - unsigned char optionP2SHSkip2FA = - ((N_btchip.bkp.config.options & OPTION_SKIP_2FA_P2SH) != 0); - BEGIN_TRY { - TRY { for (;;) { - switch (context_D.transactionContext.transactionState) { + switch (context.transactionContext.transactionState) { case TRANSACTION_NONE: { PRINTF("Init transaction parser\n"); // Reset transaction state - context_D.transactionContext + context.transactionContext .transactionRemainingInputsOutputs = 0; - context_D.transactionContext + context.transactionContext .transactionCurrentInputOutput = 0; - context_D.transactionContext.scriptRemaining = 0; + context.transactionContext.scriptRemaining = 0; memset( - context_D.transactionContext.transactionAmount, - 0, sizeof(context_D.transactionContext + context.transactionContext.transactionAmount, + 0, sizeof(context.transactionContext .transactionAmount)); // TODO : transactionControlFid // Reset hashes - if (context_D.usingOverwinter) { - if (context_D.segwitParsedOnce) { + if (context.usingOverwinter) { + if (context.segwitParsedOnce) { uint8_t parameters[16]; memmove(parameters, OVERWINTER_PARAM_SIGHASH, 16); if (COIN_KIND == COIN_KIND_ZCLASSIC) { @@ -178,124 +140,124 @@ void transaction_parse(unsigned char parseMode) { } else { write_u32_le(parameters, 12, - context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING ? + context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING ? (COIN_CONSENSUS_BRANCH_ID != 0 ? COIN_CONSENSUS_BRANCH_ID : CONSENSUS_BRANCH_ID_SAPLING) : CONSENSUS_BRANCH_ID_OVERWINTER); } - if (cx_blake2b_init2_no_throw(&context_D.transactionHashFull.blake2b, 256, NULL, 0, parameters, 16)) { + if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, parameters, 16)) { goto fail; } } } else { - if (cx_sha256_init_no_throw(&context_D.transactionHashFull.sha256)) { + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { goto fail; } } if (cx_sha256_init_no_throw( - &context_D.transactionHashAuthorization)) { + &context.transactionHashAuthorization)) { goto fail; } - if (context_D.usingSegwit) { - context_D.transactionHashOption = 0; - if (!context_D.segwitParsedOnce) { - if (context_D.usingOverwinter) { - if (cx_blake2b_init2_no_throw(&context_D.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_PREVOUTS, 16)) { + if (context.usingSegwit) { + context.transactionHashOption = 0; + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_blake2b_init2_no_throw(&context.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_PREVOUTS, 16)) { goto fail; } - if (cx_blake2b_init2_no_throw(&context_D.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_SEQUENCE, 16)) { + if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_SEQUENCE, 16)) { goto fail; } } else { if (cx_sha256_init_no_throw( - &context_D.segwit.hash.hashPrevouts.sha256)) { + &context.segwit.hash.hashPrevouts.sha256)) { goto fail; } } } else { PRINTF("Resume SegWit hash\n"); - PRINTF("SEGWIT Version\n%.*H\n",sizeof(context_D.transactionVersion),context_D.transactionVersion); - PRINTF("SEGWIT HashedPrevouts\n%.*H\n",sizeof(context_D.segwit.cache.hashedPrevouts),context_D.segwit.cache.hashedPrevouts); - PRINTF("SEGWIT HashedSequence\n%.*H\n",sizeof(context_D.segwit.cache.hashedSequence),context_D.segwit.cache.hashedSequence); - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.transactionVersion, sizeof(context_D.transactionVersion), NULL, 0)) { + PRINTF("SEGWIT Version\n%.*H\n",sizeof(context.transactionVersion),context.transactionVersion); + PRINTF("SEGWIT HashedPrevouts\n%.*H\n",sizeof(context.segwit.cache.hashedPrevouts),context.segwit.cache.hashedPrevouts); + PRINTF("SEGWIT HashedSequence\n%.*H\n",sizeof(context.segwit.cache.hashedSequence),context.segwit.cache.hashedSequence); + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionVersion, sizeof(context.transactionVersion), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.nVersionGroupId, sizeof(context_D.nVersionGroupId), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nVersionGroupId, sizeof(context.nVersionGroupId), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.segwit.cache.hashedPrevouts, sizeof(context_D.segwit.cache.hashedPrevouts), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedPrevouts, sizeof(context.segwit.cache.hashedPrevouts), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.segwit.cache.hashedSequence, sizeof(context_D.segwit.cache.hashedSequence), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedSequence, sizeof(context.segwit.cache.hashedSequence), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.segwit.cache.hashedOutputs, sizeof(context_D.segwit.cache.hashedOutputs), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedOutputs, sizeof(context.segwit.cache.hashedOutputs), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { goto fail; } - if (context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedSpend) + if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedSpend) goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedOutputs + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedOutputs goto fail; } } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.nLockTime, sizeof(context_D.nLockTime), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nLockTime, sizeof(context.nLockTime), NULL, 0)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.nExpiryHeight, sizeof(context_D.nExpiryHeight), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nExpiryHeight, sizeof(context.nExpiryHeight), NULL, 0)) { goto fail; } - if (context_D.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { + if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { unsigned char valueBalance[8]; memset(valueBalance, 0, sizeof(valueBalance)); - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, valueBalance, sizeof(valueBalance), NULL, 0)) { // sapling valueBalance + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, valueBalance, sizeof(valueBalance), NULL, 0)) { // sapling valueBalance goto fail; } } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.sigHashType, sizeof(context_D.sigHashType), NULL, 0)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.sigHashType, sizeof(context.sigHashType), NULL, 0)) { goto fail; } } else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context_D.transactionVersion), context_D.transactionVersion); + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.transactionVersion), context.transactionVersion); if (cx_hash_no_throw( - &context_D.transactionHashFull.sha256.header, 0, - context_D.transactionVersion, - sizeof(context_D.transactionVersion), + &context.transactionHashFull.sha256.header, 0, + context.transactionVersion, + sizeof(context.transactionVersion), NULL, 0)) { goto fail; } - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context_D.segwit.cache.hashedPrevouts), context_D.segwit.cache.hashedPrevouts); + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.segwit.cache.hashedPrevouts), context.segwit.cache.hashedPrevouts); if (cx_hash_no_throw( - &context_D.transactionHashFull.sha256.header, 0, - context_D.segwit.cache.hashedPrevouts, - sizeof(context_D.segwit.cache + &context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedPrevouts, + sizeof(context.segwit.cache .hashedPrevouts), NULL, 0)) { goto fail; } - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context_D.segwit.cache.hashedSequence), context_D.segwit.cache.hashedSequence); + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.segwit.cache.hashedSequence), context.segwit.cache.hashedSequence); if (cx_hash_no_throw( - &context_D.transactionHashFull.sha256.header, 0, - context_D.segwit.cache.hashedSequence, - sizeof(context_D.segwit.cache + &context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedSequence, + sizeof(context.segwit.cache .hashedSequence), NULL, 0)) { goto fail; } - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", sizeof(context_D.segwit.cache), (unsigned char *)&context_D.segwit.cache); - if (cx_hash_no_throw(&context_D + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", sizeof(context.segwit.cache), (unsigned char *)&context.segwit.cache); + if (cx_hash_no_throw(&context .transactionHashAuthorization.header, 0, - (unsigned char *)&context_D + (unsigned char *)&context .segwit.cache, - sizeof(context_D.segwit.cache), + sizeof(context.segwit.cache), NULL, 0)) { goto fail; } @@ -305,25 +267,25 @@ void transaction_parse(unsigned char parseMode) { // Parse the beginning of the transaction // Version check_transaction_available(4); - memmove(context_D.transactionVersion, - context_D.transactionBufferPointer, 4); + memmove(context.transactionVersion, + context.transactionBufferPointer, 4); transaction_offset_increase(4); - if (context_D.usingOverwinter || + if (context.usingOverwinter || TRUSTED_INPUT_OVERWINTER) { // nVersionGroupId check_transaction_available(4); - memmove(context_D.nVersionGroupId, - context_D.transactionBufferPointer, 4); + memmove(context.nVersionGroupId, + context.transactionBufferPointer, 4); transaction_offset_increase(4); } if (COIN_FLAGS & FLAG_PEERCOIN_SUPPORT) { if (((COIN_FAMILY == FAMILY_PEERCOIN && - (context_D.transactionVersion[0] < 3))) || + (context.transactionVersion[0] < 3))) || ((COIN_FAMILY == FAMILY_STEALTH) && - (context_D.transactionVersion[0] < 2))) { + (context.transactionVersion[0] < 2))) { // Timestamp check_transaction_available(4); transaction_offset_increase(4); @@ -331,10 +293,10 @@ void transaction_parse(unsigned char parseMode) { } // Number of inputs - context_D.transactionContext + context.transactionContext .transactionRemainingInputsOutputs = transaction_get_varint(); - PRINTF("Number of inputs : " DEBUG_LONG "\n",context_D.transactionContext.transactionRemainingInputsOutputs); + PRINTF("Number of inputs : %d\n", context.transactionContext.transactionRemainingInputsOutputs); if (G_called_from_swap && parseMode == PARSE_MODE_SIGNATURE) { // remember number of inputs to know when to exit from library // we will count number of already signed inputs and compare with this value @@ -342,13 +304,13 @@ void transaction_parse(unsigned char parseMode) { // (when for ex. we sign segregated witness) if (vars.swap_data.totalNumberOfInputs == 0) { vars.swap_data.totalNumberOfInputs = - context_D.transactionContext.transactionRemainingInputsOutputs; + context.transactionContext.transactionRemainingInputsOutputs; } // Reseting the flag, because we should check address ones for each input vars.swap_data.was_address_checked = 0; } // Ready to proceed - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_DEFINED_WAIT_INPUT; __attribute__((fallthrough)); @@ -357,14 +319,14 @@ void transaction_parse(unsigned char parseMode) { case TRANSACTION_DEFINED_WAIT_INPUT: { unsigned char trustedInputFlag = 1; PRINTF("Process input\n"); - if (context_D.transactionContext + if (context.transactionContext .transactionRemainingInputsOutputs == 0) { // No more inputs to hash, move forward - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_INPUT_HASHING_DONE; continue; } - if (context_D.transactionDataRemaining < 1) { + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } @@ -382,23 +344,23 @@ void transaction_parse(unsigned char parseMode) { // Expect the trusted input flag and trusted input length check_transaction_available(2); - switch (*context_D.transactionBufferPointer) { + switch (*context.transactionBufferPointer) { case 0: - if (context_D.usingSegwit) { + if (context.usingSegwit) { PRINTF("Non trusted input used in segwit mode\n"); goto fail; } trustedInputFlag = 0; break; case 1: - if (context_D.usingSegwit) { + if (context.usingSegwit) { // Segwit inputs can be passed as TrustedInput also PRINTF("Trusted input used in segwit mode\n"); } trustedInputFlag = 1; break; case 2: - if (!context_D.usingSegwit) { + if (!context.usingSegwit) { PRINTF("Segwit input not used in segwit mode\n"); goto fail; } @@ -408,18 +370,10 @@ void transaction_parse(unsigned char parseMode) { PRINTF("Invalid trusted input flag\n"); goto fail; } - /* - trustedInputLength = - *(context_D.transactionBufferPointer + 1); - if (trustedInputLength > sizeof(trustedInput)) { - PRINTF("Trusted input too long\n"); - goto fail; - } - */ // Check TrustedInput (TI) integrity, be it a non-segwit TI or a segwit TI if (trustedInputFlag) { trustedInputLength = *( - context_D.transactionBufferPointer + 1); + context.transactionBufferPointer + 1); if ((trustedInputLength > sizeof(trustedInput)) || (trustedInputLength < 8)) { PRINTF("Invalid trusted input size\n"); @@ -429,16 +383,16 @@ void transaction_parse(unsigned char parseMode) { check_transaction_available(2 + trustedInputLength); // Check TrustedInput Hmac cx_hmac_sha256( - (uint8_t *)N_btchip.bkp.trustedinput_key, - sizeof(N_btchip.bkp.trustedinput_key), - context_D.transactionBufferPointer + 2, + (uint8_t *)g_nvram_data.bkp.trustedinput_key, + sizeof(g_nvram_data.bkp.trustedinput_key), + context.transactionBufferPointer + 2, trustedInputLength - 8, trustedInput, trustedInputLength); - PRINTF("====> Input HMAC: %.*H\n", 8, context_D.transactionBufferPointer + 2 + trustedInputLength - 8); + PRINTF("====> Input HMAC: %.*H\n", 8, context.transactionBufferPointer + 2 + trustedInputLength - 8); PRINTF("====> Computed HMAC: %.*H\n", 8, trustedInput); if (os_secure_memcmp( trustedInput, // Contains computed Hmac for now - context_D.transactionBufferPointer + + context.transactionBufferPointer + 2 + trustedInputLength - 8, 8) != 0) { PRINTF("Invalid signature\n"); @@ -447,34 +401,34 @@ void transaction_parse(unsigned char parseMode) { // Hmac is valid. If TrustedInput contains a segwit input, update data pointer & length // to fake the parser into believing a normal segwit input was received. Do not use // transaction_offset_increase() here as it could update the hash being computed. - if (context_D.usingSegwit) { + if (context.usingSegwit) { // Overwrite the no longer needed HMAC's 1st byte w/ the input script length byte. - *(context_D.transactionBufferPointer + 1 + TRUSTED_INPUT_SIZE + 1) = - *(context_D.transactionBufferPointer + 1 + TRUSTED_INPUT_TOTAL_SIZE + 1); + *(context.transactionBufferPointer + 1 + TRUSTED_INPUT_SIZE + 1) = + *(context.transactionBufferPointer + 1 + TRUSTED_INPUT_TOTAL_SIZE + 1); // Set tx data pointer on TI header's (i.e. 0x38||0x32||0x00||Nonce (2B)) last byte // before prevout tx hash. Also remove HMAC size from remaining data length. - context_D.transactionBufferPointer += 5; - context_D.transactionDataRemaining -= (5+8); + context.transactionBufferPointer += 5; + context.transactionDataRemaining -= (5+8); } } // Handle pure segwit inputs, whether trusted or not (i.e. InputHashStart 1st APDU's P2==02 // & data[0]=={0x01, 0x02}) - if (context_D.usingSegwit) { + if (context.usingSegwit) { transaction_offset_increase(1); // Set tx pointer on 1st byte of hash check_transaction_available( 36); // prevout : 32 hash + 4 index - if (!context_D.segwitParsedOnce) { - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.segwit.hash.hashPrevouts.blake2b.header, 0, context_D.transactionBufferPointer, 36, NULL, 0)) { + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts.blake2b.header, 0, context.transactionBufferPointer, 36, NULL, 0)) { goto fail; } } else { if (cx_hash_no_throw( - &context_D.segwit.hash.hashPrevouts + &context.segwit.hash.hashPrevouts .sha256.header, 0, - context_D.transactionBufferPointer, + context.transactionBufferPointer, 36, NULL, 0)) { goto fail; } @@ -483,58 +437,45 @@ void transaction_parse(unsigned char parseMode) { check_transaction_available(8); // update amount swap_bytes( amount, - context_D.transactionBufferPointer, + context.transactionBufferPointer, 8); if (transaction_amount_add_be( - context_D.transactionContext + context.transactionContext .transactionAmount, - context_D.transactionContext + context.transactionContext .transactionAmount, amount)) { PRINTF("Overflow\n"); goto fail; } - PRINTF("Adding amount\n%.*H\n",8,context_D.transactionBufferPointer); - PRINTF("New amount\n%.*H\n",8,context_D.transactionContext.transactionAmount); + PRINTF("Adding amount\n%.*H\n",8,context.transactionBufferPointer); + PRINTF("New amount\n%.*H\n",8,context.transactionContext.transactionAmount); transaction_offset_increase(8); } else { - context_D.transactionHashOption = + context.transactionHashOption = TRANSACTION_HASH_FULL; transaction_offset_increase(36); - context_D.transactionHashOption = 0; + context.transactionHashOption = 0; check_transaction_available(8); // save amount memmove( - context_D.inputValue, - context_D.transactionBufferPointer, + context.inputValue, + context.transactionBufferPointer, 8); transaction_offset_increase(8); - context_D.transactionHashOption = + context.transactionHashOption = TRANSACTION_HASH_FULL; } } // Handle non-segwit inputs (i.e. InputHashStart 1st APDU's P2==00 && data[0]==0x00) else if (!trustedInputFlag) { - if (!optionP2SHSkip2FA) { - PRINTF("Untrusted input not authorized\n"); - goto fail; - } - context_D.transactionBufferPointer++; - context_D.transactionDataRemaining--; - check_transaction_available( - 36); // prevout : 32 hash + 4 index - transaction_offset_increase(36); - PRINTF("Marking relaxed input\n"); - context_D.transactionContext.relaxed = 1; - /* - PRINTF("Clearing P2SH consumption\n"); - context_D.transactionContext.consumeP2SH = 0; - */ + PRINTF("Untrusted input not authorized\n"); + goto fail; } // Handle non-segwit TrustedInput (i.e. InputHashStart 1st APDU's P2==00 & data[0]==0x01) - else if (trustedInputFlag && !context_D.usingSegwit) { + else if (trustedInputFlag && !context.usingSegwit) { memmove( trustedInput, - context_D.transactionBufferPointer + 2, + context.transactionBufferPointer + 2, trustedInputLength - 8); if (trustedInput[0] != MAGIC_TRUSTED_INPUT) { PRINTF("Failed to verify trusted input signature\n"); @@ -542,33 +483,24 @@ void transaction_parse(unsigned char parseMode) { } // Update the hash with prevout data savePointer = - context_D.transactionBufferPointer; - /* - // Check if a P2SH script is used - if ((trustedInput[1] & FLAG_TRUSTED_INPUT_P2SH) == - 0) { - PRINTF("Clearing P2SH consumption\n"); - context_D.transactionContext.consumeP2SH = - 0; - } - */ - context_D.transactionBufferPointer = + context.transactionBufferPointer; + context.transactionBufferPointer = trustedInput + 4; - PRINTF("Trusted input hash\n%.*H\n",36,context_D.transactionBufferPointer); + PRINTF("Trusted input hash\n%.*H\n",36,context.transactionBufferPointer); transaction_offset(36); - context_D.transactionBufferPointer = + context.transactionBufferPointer = savePointer + (2 + trustedInputLength); - context_D.transactionDataRemaining -= + context.transactionDataRemaining -= (2 + trustedInputLength); // Update the amount swap_bytes(amount, trustedInput + 40, 8); if (transaction_amount_add_be( - context_D.transactionContext + context.transactionContext .transactionAmount, - context_D.transactionContext + context.transactionContext .transactionAmount, amount)) { PRINTF("Overflow\n"); @@ -576,90 +508,85 @@ void transaction_parse(unsigned char parseMode) { } PRINTF("Adding amount\n%.*H\n",8,(trustedInput + 40)); - PRINTF("New amount\n%.*H\n",8,context_D.transactionContext.transactionAmount); + PRINTF("New amount\n%.*H\n",8,context.transactionContext.transactionAmount); } - if (!context_D.usingSegwit) { + if (!context.usingSegwit) { // Do not include the input script length + value in // the authentication hash - context_D.transactionHashOption = + context.transactionHashOption = TRANSACTION_HASH_FULL; } } // Read the script length - context_D.transactionContext.scriptRemaining = + context.transactionContext.scriptRemaining = transaction_get_varint(); - PRINTF("Script to read " DEBUG_LONG "\n",context_D.transactionContext.scriptRemaining); + PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); if ((parseMode == PARSE_MODE_SIGNATURE) && - !trustedInputFlag && !context_D.usingSegwit) { + !trustedInputFlag && !context.usingSegwit) { // Only proceeds if this is not to be signed - so length // should be null - if (context_D.transactionContext + if (context.transactionContext .scriptRemaining != 0) { PRINTF("Request to sign relaxed input\n"); - if (!optionP2SHSkip2FA) { - goto fail; - } + goto fail; } } // Move on - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT; __attribute__((fallthrough)); } case TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT: { unsigned char dataAvailable; - PRINTF("Process input script, remaining " DEBUG_LONG "\n",context_D.transactionContext.scriptRemaining); - if (context_D.transactionDataRemaining < 1) { + PRINTF("Process input script, remaining %d\n", context.transactionContext.scriptRemaining); + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Scan for P2SH consumption - huge shortcut, but fine // enough // Also usable in SegWit mode - if (context_D.transactionContext.scriptRemaining == + if (context.transactionContext.scriptRemaining == 1) { - if (*context_D.transactionBufferPointer == + if (*context.transactionBufferPointer == OP_CHECKMULTISIG) { - if (optionP2SHSkip2FA) { - PRINTF("Marking P2SH consumption\n"); - context_D.transactionContext - .consumeP2SH = 1; - } + PRINTF("Marking P2SH consumption\n"); + context.transactionContext.consumeP2SH = 1; } else { // When using the P2SH shortcut, all inputs must use // P2SH PRINTF("Disabling P2SH consumption\n"); - context_D.transactionContext.consumeP2SH = 0; + context.transactionContext.consumeP2SH = 0; } transaction_offset_increase(1); - context_D.transactionContext.scriptRemaining--; + context.transactionContext.scriptRemaining--; } - if (context_D.transactionContext.scriptRemaining == + if (context.transactionContext.scriptRemaining == 0) { if (parseMode == PARSE_MODE_SIGNATURE) { - if (!context_D.usingSegwit) { + if (!context.usingSegwit) { // Restore dual hash for signature + // authentication - context_D.transactionHashOption = + context.transactionHashOption = TRANSACTION_HASH_BOTH; } else { - if (context_D.segwitParsedOnce) { + if (context.segwitParsedOnce) { // Append the saved value - PRINTF("SEGWIT Add value\n%.*H\n",8,context_D.inputValue); - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.inputValue, 8, NULL, 0)) { + PRINTF("SEGWIT Add value\n%.*H\n",8,context.inputValue); + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.inputValue, 8, NULL, 0)) { goto fail; } } else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context_D.inputValue), context_D.inputValue); - if (cx_hash_no_throw(&context_D + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.inputValue), context.inputValue); + if (cx_hash_no_throw(&context .transactionHashFull.sha256.header, - 0, context_D.inputValue, 8, + 0, context.inputValue, 8, NULL, 0)) { goto fail; } @@ -669,19 +596,19 @@ void transaction_parse(unsigned char parseMode) { } // Sequence check_transaction_available(4); - if (context_D.usingSegwit && - !context_D.segwitParsedOnce) { - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, context_D.transactionBufferPointer, 4, NULL, 0)) { + if (context.usingSegwit && + !context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionBufferPointer, 4, NULL, 0)) { goto fail; } } else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", 4, context_D.transactionBufferPointer); - if (cx_hash_no_throw(&context_D.transactionHashFull + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", 4, context.transactionBufferPointer); + if (cx_hash_no_throw(&context.transactionHashFull .sha256.header, 0, - context_D.transactionBufferPointer, + context.transactionBufferPointer, 4, NULL, 0)) { goto fail; } @@ -689,29 +616,29 @@ void transaction_parse(unsigned char parseMode) { } transaction_offset_increase(4); // Move to next input - context_D.transactionContext + context.transactionContext .transactionRemainingInputsOutputs--; - context_D.transactionContext + context.transactionContext .transactionCurrentInputOutput++; - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_DEFINED_WAIT_INPUT; continue; } // Save the last script byte for the P2SH check dataAvailable = - (context_D.transactionDataRemaining > - context_D.transactionContext + (context.transactionDataRemaining > + context.transactionContext .scriptRemaining - 1 - ? context_D.transactionContext + ? context.transactionContext .scriptRemaining - 1 - : context_D.transactionDataRemaining); + : context.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); - context_D.transactionContext.scriptRemaining -= + context.transactionContext.scriptRemaining -= dataAvailable; break; } @@ -719,46 +646,46 @@ void transaction_parse(unsigned char parseMode) { PRINTF("Input hashing done\n"); if (parseMode == PARSE_MODE_SIGNATURE) { // inputs have been prepared, stop the parsing here - if (context_D.usingSegwit && - !context_D.segwitParsedOnce) { + if (context.usingSegwit && + !context.segwitParsedOnce) { unsigned char hashedPrevouts[32]; unsigned char hashedSequence[32]; // Flush the cache - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32)) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, CX_LAST, hashedSequence, 0, hashedSequence, 32)) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, hashedSequence, 0, hashedSequence, 32)) { goto fail; } } else { - if (cx_hash_no_throw(&context_D.segwit.hash.hashPrevouts + if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts .sha256.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32)) { goto fail; } if (cx_sha256_init_no_throw( - &context_D.segwit.hash.hashPrevouts.sha256)) { + &context.segwit.hash.hashPrevouts.sha256)) { goto fail; } - if (cx_hash_no_throw(&context_D.segwit.hash.hashPrevouts + if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts .sha256.header, CX_LAST, hashedPrevouts, sizeof(hashedPrevouts), hashedPrevouts, 32)) { goto fail; } - if (cx_hash_no_throw(&context_D.transactionHashFull + if (cx_hash_no_throw(&context.transactionHashFull .sha256.header, CX_LAST, hashedSequence, 0, hashedSequence, 32)) { goto fail; } if (cx_sha256_init_no_throw( - &context_D.transactionHashFull.sha256)) { + &context.transactionHashFull.sha256)) { goto fail; } PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(hashedSequence), hashedSequence); - if (cx_hash_no_throw(&context_D.transactionHashFull + if (cx_hash_no_throw(&context.transactionHashFull .sha256.header, CX_LAST, hashedSequence, sizeof(hashedSequence), hashedSequence, 32)) { @@ -767,153 +694,139 @@ void transaction_parse(unsigned char parseMode) { } memmove( - context_D.segwit.cache.hashedPrevouts, + context.segwit.cache.hashedPrevouts, hashedPrevouts, sizeof(hashedPrevouts)); memmove( - context_D.segwit.cache.hashedSequence, + context.segwit.cache.hashedSequence, hashedSequence, sizeof(hashedSequence)); - PRINTF("hashPrevout\n%.*H\n",32,context_D.segwit.cache.hashedPrevouts); - PRINTF("hashSequence\n%.*H\n",32,context_D.segwit.cache.hashedSequence); + PRINTF("hashPrevout\n%.*H\n",32,context.segwit.cache.hashedPrevouts); + PRINTF("hashSequence\n%.*H\n",32,context.segwit.cache.hashedSequence); } - if (context_D.usingSegwit && - context_D.segwitParsedOnce) { - if (!context_D.usingOverwinter) { - PRINTF("SEGWIT hashedOutputs\n%.*H\n",sizeof(context_D.segwit.cache.hashedOutputs),context_D.segwit.cache.hashedOutputs); + if (context.usingSegwit && + context.segwitParsedOnce) { + if (!context.usingOverwinter) { + PRINTF("SEGWIT hashedOutputs\n%.*H\n",sizeof(context.segwit.cache.hashedOutputs),context.segwit.cache.hashedOutputs); if (cx_hash_no_throw( - &context_D.transactionHashFull.sha256.header, 0, - context_D.segwit.cache.hashedOutputs, - sizeof(context_D.segwit.cache + &context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedOutputs, + sizeof(context.segwit.cache .hashedOutputs), NULL, 0)) { goto fail; } } - context_D.transactionContext + context.transactionContext .transactionState = TRANSACTION_SIGN_READY; } else { - context_D.transactionContext + context.transactionContext .transactionState = TRANSACTION_PRESIGN_READY; - if (context_D.usingOverwinter) { - if (cx_blake2b_init2_no_throw(&context_D.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_OUTPUTS, 16)) { + if (context.usingOverwinter) { + if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_OUTPUTS, 16)) { goto fail; } } else - if (context_D.usingSegwit) { - if (cx_sha256_init_no_throw(&context_D.transactionHashFull.sha256)) { + if (context.usingSegwit) { + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { goto fail; } } } continue; } - if (context_D.transactionDataRemaining < 1) { + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Number of outputs - context_D.transactionContext + context.transactionContext .transactionRemainingInputsOutputs = transaction_get_varint(); - context_D.transactionContext + context.transactionContext .transactionCurrentInputOutput = 0; - PRINTF("Number of outputs : " DEBUG_LONG "\n", - context_D.transactionContext.transactionRemainingInputsOutputs); + PRINTF("Number of outputs : %d\n", + context.transactionContext.transactionRemainingInputsOutputs); // Ready to proceed - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_DEFINED_WAIT_OUTPUT; __attribute__((fallthrough)); } case TRANSACTION_DEFINED_WAIT_OUTPUT: { - if (context_D.transactionContext + if (context.transactionContext .transactionRemainingInputsOutputs == 0) { // No more outputs to hash, move forward - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_OUTPUT_HASHING_DONE; continue; } - if (context_D.transactionDataRemaining < 1) { + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } // Amount check_transaction_available(8); if ((parseMode == PARSE_MODE_TRUSTED_INPUT) && - (context_D.transactionContext + (context.transactionContext .transactionCurrentInputOutput == - context_D.transactionTargetInput)) { + context.transactionTargetInput)) { // Save the amount - memmove(context_D.transactionContext + memmove(context.transactionContext .transactionAmount, - context_D.transactionBufferPointer, + context.transactionBufferPointer, 8); - context_D.trustedInputProcessed = 1; + context.trustedInputProcessed = 1; } transaction_offset_increase(8); // Read the script length - context_D.transactionContext.scriptRemaining = + context.transactionContext.scriptRemaining = transaction_get_varint(); - PRINTF("Script to read " DEBUG_LONG "\n",context_D.transactionContext.scriptRemaining); + PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); // Move on - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT; __attribute__((fallthrough)); } case TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT: { unsigned char dataAvailable; - PRINTF("Process output script, remaining " DEBUG_LONG "\n",context_D.transactionContext.scriptRemaining); - /* - // Special check if consuming a P2SH script - if (parseMode == PARSE_MODE_TRUSTED_INPUT) { - // Assume the full input script is sent in a single APDU, - then do the ghetto validation - if ((context_D.transactionBufferPointer[0] == - OP_HASH160) && - (context_D.transactionBufferPointer[context_D.transactionDataRemaining - - 1] == OP_EQUAL)) { - PRINTF("Marking P2SH output\n"); - context_D.transactionContext.consumeP2SH = 1; - } - } - */ - if (context_D.transactionDataRemaining < 1) { + PRINTF("Process output script, remaining %d\n", context.transactionContext.scriptRemaining); + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } - if (context_D.transactionContext.scriptRemaining == + if (context.transactionContext.scriptRemaining == 0) { // Move to next output - context_D.transactionContext + context.transactionContext .transactionRemainingInputsOutputs--; - context_D.transactionContext + context.transactionContext .transactionCurrentInputOutput++; - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_DEFINED_WAIT_OUTPUT; continue; } dataAvailable = - (context_D.transactionDataRemaining > - context_D.transactionContext + (context.transactionDataRemaining > + context.transactionContext .scriptRemaining - ? context_D.transactionContext + ? context.transactionContext .scriptRemaining - : context_D.transactionDataRemaining); + : context.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); - context_D.transactionContext.scriptRemaining -= + context.transactionContext.scriptRemaining -= dataAvailable; break; } case TRANSACTION_OUTPUT_HASHING_DONE: { PRINTF("Output hashing done\n"); - if (context_D.transactionDataRemaining < 1) { + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } @@ -921,17 +834,17 @@ void transaction_parse(unsigned char parseMode) { check_transaction_available(4); transaction_offset_increase(4); - if (context_D.transactionDataRemaining == 0) { - context_D.transactionContext.transactionState = + if (context.transactionDataRemaining == 0) { + context.transactionContext.transactionState = TRANSACTION_PARSED; continue; } else { - context_D.transactionHashOption = 0; - context_D.transactionContext.scriptRemaining = + context.transactionHashOption = 0; + context.transactionContext.scriptRemaining = transaction_get_varint(); - context_D.transactionHashOption = + context.transactionHashOption = TRANSACTION_HASH_FULL; - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_PROCESS_EXTRA; continue; } @@ -940,30 +853,30 @@ void transaction_parse(unsigned char parseMode) { case TRANSACTION_PROCESS_EXTRA: { unsigned char dataAvailable; - if (context_D.transactionContext.scriptRemaining == + if (context.transactionContext.scriptRemaining == 0) { - context_D.transactionContext.transactionState = + context.transactionContext.transactionState = TRANSACTION_PARSED; continue; } - if (context_D.transactionDataRemaining < 1) { + if (context.transactionDataRemaining < 1) { // No more data to read, ok goto ok; } dataAvailable = - (context_D.transactionDataRemaining > - context_D.transactionContext + (context.transactionDataRemaining > + context.transactionContext .scriptRemaining - ? context_D.transactionContext + ? context.transactionContext .scriptRemaining - : context_D.transactionDataRemaining); + : context.transactionDataRemaining); if (dataAvailable == 0) { goto ok; } transaction_offset_increase(dataAvailable); - context_D.transactionContext.scriptRemaining -= + context.transactionContext.scriptRemaining -= dataAvailable; break; } @@ -986,20 +899,8 @@ void transaction_parse(unsigned char parseMode) { } fail: - PRINTF("Transaction parse - fail\n"); - THROW(EXCEPTION); - ok : {} - } - CATCH_OTHER(e) { - PRINTF("Transaction parse - surprise fail\n"); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - THROW(e); - } - // before the finally to restore the surrounding context if an exception - // is raised during finally - FINALLY { - } - } - END_TRY; + LEDGER_ASSERT(false, "Transaction parse - fail\n"); + ok: + return; + } diff --git a/include/btchip.h b/lib-app-bitcoin/transaction.h similarity index 67% rename from include/btchip.h rename to lib-app-bitcoin/transaction.h index f53e0e1d..fa88a83b 100644 --- a/include/btchip.h +++ b/lib-app-bitcoin/transaction.h @@ -15,22 +15,19 @@ * limitations under the License. ********************************************************************************/ -#ifndef H +#pragma once -#define H -#include "config.h" -#include "os.h" -#include "os_io_seproxyhal.h" +#define TRANSACTION_HASH_NONE 0x00 +#define TRANSACTION_HASH_FULL 0x01 +#define TRANSACTION_HASH_AUTHORIZATION 0x02 +#define TRANSACTION_HASH_BOTH 0x03 -#include "stdlib.h" -#include "stdbool.h" -#include "string.h" +#define PARSE_MODE_TRUSTED_INPUT 0x01 +#define PARSE_MODE_SIGNATURE 0x02 -#define L_DEBUG_NOPREFIX(x) +#define TRUSTED_INPUT_SIZE 48 +#define TRUSTED_INPUT_TOTAL_SIZE (TRUSTED_INPUT_SIZE + 8) -#define SW_TECHNICAL_DETAILS(x) SW_TECHNICAL_PROBLEM +void transaction_parse(unsigned char parseMode); -#include "secure_value.h" - -#endif diff --git a/src/display_variables.h b/lib-app-bitcoin/ui/display_variables.h similarity index 87% rename from src/display_variables.h rename to lib-app-bitcoin/ui/display_variables.h index c0ac6f8f..723009ab 100644 --- a/src/display_variables.h +++ b/lib-app-bitcoin/ui/display_variables.h @@ -1,7 +1,7 @@ -#ifndef _DISPLAY_VARIABLES_H_ -#define _DISPLAY_VARIABLES_H_ +#pragma once #include "os.h" + // A path contains 10 elements max, which max length in ascii is 1 whitespace + 10 char + optional quote "'" + "/" + \0" #define MAX_DERIV_PATH_ASCII_LENGTH 1 + 10*(10+2) + 1 #define MAX_CHAR_PER_LINE 25 @@ -26,8 +26,8 @@ union display_variables { // of char fullAddress[65]; // the address - char fullAmount[20]; // full amount - char feesAmount[20]; // fees + char fullAmount[28]; // full amount + char feesAmount[28]; // fees } tmp; struct { @@ -38,5 +38,3 @@ union display_variables { }; extern union display_variables vars; - -#endif diff --git a/src/bagl_extensions.h b/lib-app-bitcoin/ui/extensions.h similarity index 57% rename from src/bagl_extensions.h rename to lib-app-bitcoin/ui/extensions.h index c3029694..7aed64c9 100644 --- a/src/bagl_extensions.h +++ b/lib-app-bitcoin/ui/extensions.h @@ -15,44 +15,40 @@ * limitations under the License. ********************************************************************************/ -#ifndef _BAGL_H_ -#define _BAGL_H_ +#pragma once // btchip asking the per-output UI -unsigned int bagl_confirm_single_output(void); +unsigned int confirm_single_output(void); // btchip display token -void bagl_display_token(void); +void display_token(void); // btchip finalizing the transaction -unsigned int bagl_finalize_tx(void); +unsigned int finalize_tx(void); // UI response to btchip to finish the exchange -unsigned char bagl_user_action(unsigned char confirming); +unsigned char user_action(unsigned char confirming); // request the UI to redisplay the idle screen -void bagl_idle(void); +void idle(void); // btchip asking message signing confirmation -void bagl_confirm_message_signature(void); +void confirm_message_signature(void); // UI response to message signature -void bagl_user_action_message_signing(unsigned char confirming); +int user_action_message_signing(unsigned char confirming); // Public key display -uint8_t set_key_path_to_display(unsigned char* keyPath); -void bagl_display_public_key(uint8_t is_derivation_path_unusual); -void bagl_user_action_display(unsigned char confirming); +uint8_t set_key_path_to_display(const unsigned char* keyPath); +void display_public_key(uint8_t is_derivation_path_unusual); +int user_action_display(unsigned char confirming); -void bagl_request_pubkey_approval(void); -void bagl_request_change_path_approval(unsigned char* change_path); +void request_pubkey_approval(void); +void request_change_path_approval(unsigned char* change_path); // UI to confirm processing of tx with segwit inputs -void bagl_request_segwit_input_approval(void); +void request_segwit_input_approval(void); // UI to confirm signing path -void bagl_request_sign_path_approval(unsigned char *derivation_path); -void bagl_user_action_signtx(unsigned char confirming, unsigned char direct); - - -#endif /* _BAGL_H_ */ +void request_sign_path_approval(unsigned char* change_path); +int user_action_signtx(unsigned char confirming, unsigned char direct); diff --git a/lib-app-bitcoin/ui/main_ui.c b/lib-app-bitcoin/ui/main_ui.c new file mode 100644 index 00000000..5d2579bb --- /dev/null +++ b/lib-app-bitcoin/ui/main_ui.c @@ -0,0 +1,311 @@ +/******************************************************************************* +* Ledger App - Bitcoin Wallet +* (c) 2016-2019 Ledger +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ +#include "os.h" +#include "cx.h" +#include "format.h" +#include "read.h" +#include "write.h" +#include "bip32.h" +#include "swap.h" +#include "string.h" + + +#include "context.h" +#include "helpers.h" +#include "customizable_helpers.h" +#include "customizable_ui.h" +#include "extensions.h" +#include "display_utils.h" +#include "ux.h" +#include "display_variables.h" +#include "swap_lib_calls.h" +#include "handle_swap_sign_transaction.h" +#include "handle_get_printable_amount.h" +#include "handle_check_address.h" +#include "ui.h" +#include "be_operations.h" + +#define OMNI_ASSETID 1 +#define MAIDSAFE_ASSETID 3 +#define USDT_ASSETID 31 + +static uint8_t check_fee_swap() { + unsigned char fees[8]; + unsigned char borrow; + + borrow = transaction_amount_sub_be( + fees, context.transactionContext.transactionAmount, + context.totalOutputAmount); + if ((borrow != 0) || (memcmp(fees, vars.swap_data.fees, 8) != 0)) + return 0; + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = + TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = + TRANSACTION_SIGN_READY; + } + context.sw = 0x9000; + context.outLength = 0; + G_io_apdu_buffer[context.outLength++] = 0x90; + G_io_apdu_buffer[context.outLength++] = 0x00; + + return 1; +} + +#define OMNI_ASSETID 1 +#define MAIDSAFE_ASSETID 3 +#define USDT_ASSETID 31 + +static void prepare_single_output(void) { + // TODO : special display for OP_RETURN + unsigned char amount[8]; + unsigned int offset = 0; + char tmp[80] = {0}; + + swap_bytes(amount, context.currentOutput + offset, 8); + offset += 8; + + get_address_from_output_script(context.currentOutput + offset, sizeof(context.currentOutput) - offset, tmp, sizeof(tmp)); + strncpy(vars.tmp.fullAddress, tmp, sizeof(vars.tmp.fullAddress) - 1); + + // Prepare amount + + // Handle Omni simple send + if ((context.currentOutput[offset + 2] == 0x14) && + (memcmp(context.currentOutput + offset + 3, "omni", 4) == 0) && + (memcmp(context.currentOutput + offset + 3 + 4, "\0\0\0\0", 4) == 0)) { + uint32_t omniAssetId = read_u32_be(context.currentOutput, offset + 3 + 4 + 4); + switch(omniAssetId) { + case OMNI_ASSETID: + strcpy(vars.tmp.fullAmount, "OMNI "); + break; + case USDT_ASSETID: + strcpy(vars.tmp.fullAmount, "USDT "); + break; + case MAIDSAFE_ASSETID: + strcpy(vars.tmp.fullAmount, "MAID "); + break; + default: + snprintf(vars.tmp.fullAmount, sizeof(vars.tmp.fullAmount), "OMNI asset %d ", omniAssetId); + break; + } + format_sats_amount(vars.tmp.fullAmount, + (uint64_t) read_u64_be(context.currentOutput, offset + 3 + 4 + 4 + 4), // Cast prevents weird compilo bug + vars.tmp.fullAmount); + } + else { + format_sats_amount(COIN_COINID_SHORT, + (uint64_t)read_u64_be(amount, 0), // Cast prevents weird compilo bug + vars.tmp.fullAmount); + } +} + +static uint8_t prepare_message_signature(void) { + uint8_t buffer[32]; + + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, CX_LAST, + (uint8_t*)vars.tmp.fullAmount, 0, buffer, 32)) { + return 0; + } + + format_hex((const uint8_t*) buffer, sizeof(buffer), vars.tmp.fullAddress, sizeof(vars.tmp.fullAddress)); + + return 1; +} + + +extern int handle_output_state(unsigned int* processed); +extern void hash_input_finalize_full_reset(void); + +// Analog of confirm_single_output to work +// in silent mode, when called from SWAP app +unsigned int silent_confirm_single_output() { + char tmp[80] = {0}; + unsigned char amount[8]; + while (true) { + // in swap operation we can only have 1 "external" output + if (vars.swap_data.was_address_checked) { + PRINTF("Address was already checked\n"); + return 0; + } + vars.swap_data.was_address_checked = 1; + // check amount + swap_bytes(amount, context.currentOutput, 8); + if (memcmp(amount, vars.swap_data.amount, 8) != 0) { + PRINTF("Amount not matched\n"); + return 0; + } + get_address_from_output_script(context.currentOutput + 8, sizeof(context.currentOutput) - 8, tmp, sizeof(tmp)); + if (strcmp(tmp, vars.swap_data.destination_address) != 0) { + PRINTF("Address not matched\n"); + return 0; + } + + // Check if all inputs have been confirmed + + if (context.outputParsingState == + OUTPUT_PARSING_OUTPUT) { + context.remainingOutputs--; + if (context.remainingOutputs == 0) + break; + } + + memmove(context.currentOutput, + context.currentOutput + + context.discardSize, + context.currentOutputOffset - + context.discardSize); + context.currentOutputOffset -= context.discardSize; + unsigned int processed = true; + while (processed == 1) { + if (handle_output_state(&processed)) { + PRINTF("Error in handle output state \n"); + return 0; + } + } + + if (processed != 2) { + // Out of data to process, wait for the next call + break; + } + } + + if ((context.outputParsingState == OUTPUT_PARSING_OUTPUT) && + (context.remainingOutputs == 0)) { + context.outputParsingState = OUTPUT_FINALIZE_TX; + // check fees + unsigned char fees[8]; + + if ((transaction_amount_sub_be(fees, + context.transactionContext.transactionAmount, + context.totalOutputAmount) != 0) || + (memcmp(fees, vars.swap_data.fees, 8) != 0)) { + PRINTF("Fees is not matched\n"); + return 0; + } + } + + if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && + !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = + TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = + TRANSACTION_SIGN_READY; + } + } + if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + // we've finished the processing of the input + hash_input_finalize_full_reset(); + } + + return 1; +} + +unsigned int confirm_single_output(void) { + if (G_called_from_swap) { + if (silent_confirm_single_output()) { + return 2; + } + return 0; + } + prepare_single_output(); + + ui_confirm_single_flow(); + return 1; +} + +unsigned int finalize_tx(void) { + if (G_called_from_swap) { + if (check_fee_swap()) { + return 2; + } + } + + if (!prepare_fees()) { + return 0; + } + + ui_finalize_flow(); + return 1; +} + +void confirm_message_signature(void) { + if (!prepare_message_signature()) { + return; + } + + ui_sign_message_flow(); +} + +uint8_t set_key_path_to_display(const unsigned char* keyPath) { + format_path(keyPath, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); + return bip44_derivation_guard(keyPath, false); +} + +void display_public_key(uint8_t is_derivation_path_unusual) { + // append a white space at the end of the address to avoid glitch on nano S + strlcat((char *)G_io_apdu_buffer + 200, " ", sizeof(G_io_apdu_buffer) - 200); + + if (is_derivation_path_unusual) { + ui_display_public_with_warning_flow(); + } + else { + ui_display_public_flow(); + } +} + +void display_token(void) +{ + ui_display_token_flow(); +} + +void request_pubkey_approval(void) +{ + ui_request_pubkey_approval_flow(); +} + +void request_change_path_approval(unsigned char* change_path) +{ + format_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); + ui_request_change_path_approval_flow(); +} + +void request_sign_path_approval(unsigned char* change_path) +{ + format_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); + ui_request_sign_path_approval_flow(); +} + +void request_segwit_input_approval(void) +{ + ui_request_segwit_input_approval_flow(); +} + + diff --git a/src/ui.h b/lib-app-bitcoin/ui/ui.h similarity index 100% rename from src/ui.h rename to lib-app-bitcoin/ui/ui.h diff --git a/src/ui_bagl.c b/lib-app-bitcoin/ui/ui_bagl.c similarity index 97% rename from src/ui_bagl.c rename to lib-app-bitcoin/ui/ui_bagl.c index 6c4ee66e..9a60828b 100644 --- a/src/ui_bagl.c +++ b/lib-app-bitcoin/ui/ui_bagl.c @@ -18,17 +18,18 @@ #ifdef HAVE_BAGL ////////////////////////////////////////////////////////////////////// #include "display_variables.h" -#include "internal.h" +#include "transaction.h" +#include "context.h" #include "ui.h" -#include "bagl_extensions.h" +#include "extensions.h" bagl_element_t tmp_element; static unsigned int io_seproxyhal_touch_verify_cancel(const bagl_element_t *e) { UNUSED(e); // user denied the transaction, tell the USB side - if (!bagl_user_action(0)) { + if (!user_action(0)) { // redraw ui ui_idle_flow(); } @@ -38,7 +39,7 @@ static unsigned int io_seproxyhal_touch_verify_cancel(const bagl_element_t *e) { static unsigned int io_seproxyhal_touch_verify_ok(const bagl_element_t *e) { UNUSED(e); // user accepted the transaction, tell the USB side - if (!bagl_user_action(1)) { + if (!user_action(1)) { // redraw ui ui_idle_flow(); } @@ -49,7 +50,7 @@ static unsigned int io_seproxyhal_touch_message_signature_verify_cancel(const bagl_element_t *e) { UNUSED(e); // user denied the transaction, tell the USB side - bagl_user_action_message_signing(0); + user_action_message_signing(0); // redraw ui ui_idle_flow(); return 0; // DO NOT REDRAW THE BUTTON @@ -59,7 +60,7 @@ static unsigned int io_seproxyhal_touch_message_signature_verify_ok(const bagl_element_t *e) { UNUSED(e); // user accepted the transaction, tell the USB side - bagl_user_action_message_signing(1); + user_action_message_signing(1); // redraw ui ui_idle_flow(); return 0; // DO NOT REDRAW THE BUTTON @@ -69,7 +70,7 @@ static unsigned int io_seproxyhal_touch_display_cancel(const bagl_element_t *e) { UNUSED(e); // user denied the transaction, tell the USB side - bagl_user_action_display(0); + user_action_display(0); // redraw ui ui_idle_flow(); return 0; // DO NOT REDRAW THE BUTTON @@ -78,16 +79,15 @@ io_seproxyhal_touch_display_cancel(const bagl_element_t *e) { static unsigned int io_seproxyhal_touch_display_ok(const bagl_element_t *e) { UNUSED(e); // user accepted the transaction, tell the USB side - bagl_user_action_display(1); // redraw ui ui_idle_flow(); - return 0; // DO NOT REDRAW THE BUTTON + return user_action_display(1); } static unsigned int io_seproxyhal_touch_sign_cancel(const bagl_element_t *e) { UNUSED(e); // user denied the transaction, tell the USB side - bagl_user_action_signtx(0, 0); + user_action_signtx(0, 0); // redraw ui ui_idle_flow(); return 0; // DO NOT REDRAW THE BUTTON @@ -96,7 +96,7 @@ static unsigned int io_seproxyhal_touch_sign_cancel(const bagl_element_t *e) { static unsigned int io_seproxyhal_touch_sign_ok(const bagl_element_t *e) { UNUSED(e); // user accepted the transaction, tell the USB side - bagl_user_action_signtx(1, 0); + user_action_signtx(1, 0); // redraw ui ui_idle_flow(); return 0; // DO NOT REDRAW THE BUTTON @@ -107,8 +107,7 @@ void io_seproxyhal_display(const bagl_element_t *element) { io_seproxyhal_display_default((bagl_element_t *)element); } } - -////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// UX_STEP_NOCB(ux_idle_flow_1_step, nn, { "Application", @@ -390,7 +389,7 @@ void ui_sign_message_flow(void) { ux_flow_init(0, ux_sign_flow, NULL); } void ui_confirm_single_flow(void) { snprintf(vars.tmp.feesAmount, sizeof(vars.tmp.feesAmount), "output #%d", - context_D.totalOutputs - context_D.remainingOutputs + + context.totalOutputs - context.remainingOutputs + 1); ux_flow_init(0, ux_confirm_single_flow, NULL); } diff --git a/src/ui_menu_nbgl.c b/lib-app-bitcoin/ui/ui_menu_nbgl.c similarity index 97% rename from src/ui_menu_nbgl.c rename to lib-app-bitcoin/ui/ui_menu_nbgl.c index 20b865e6..de876fc7 100644 --- a/src/ui_menu_nbgl.c +++ b/lib-app-bitcoin/ui/ui_menu_nbgl.c @@ -17,10 +17,9 @@ #ifdef HAVE_NBGL #include "display_variables.h" -#include "internal.h" #include "ui.h" -#include "bagl_extensions.h" +#include "extensions.h" #include "nbgl_use_case.h" #define NB_INFO_FIELDS 2 diff --git a/src/ui_nbgl.c b/lib-app-bitcoin/ui/ui_nbgl.c similarity index 96% rename from src/ui_nbgl.c rename to lib-app-bitcoin/ui/ui_nbgl.c index ee41f795..8876b6bd 100644 --- a/src/ui_nbgl.c +++ b/lib-app-bitcoin/ui/ui_nbgl.c @@ -19,9 +19,9 @@ #include "ui.h" #include "nbgl_use_case.h" #include "display_variables.h" -#include "internal.h" +#include "context.h" -#include "bagl_extensions.h" +#include "extensions.h" typedef enum { MESSAGE_TYPE, @@ -55,55 +55,55 @@ static UiContext_t uiContext = {.transaction_prompt_done = 0}; // User action callbacks static void approved_user_action_callback(void) { - if (!bagl_user_action(1)) { + if (!user_action(1)) { ui_idle_flow(); } } static void approved_user_action_processing_callback(void) { - if (!bagl_user_action(1)) { + if (!user_action(1)) { nbgl_useCaseSpinner("Processing"); } } static void abandon_user_action_callback(void) { - if (!bagl_user_action(0)) { + if (!user_action(0)) { ui_idle_flow(); } } static void approved_user_action_message_signing_callback(void) { - bagl_user_action_message_signing(1); + user_action_message_signing(1); ui_idle_flow(); } static void abandon_user_action_message_signing_callback(void) { - bagl_user_action_message_signing(0); + user_action_message_signing(0); ui_idle_flow(); } static void approved_user_action_display_processing_callback(void) { - bagl_user_action_display(1); + user_action_display(1); nbgl_useCaseSpinner("Processing"); } static void approved_user_action_display_callback(void) { - bagl_user_action_display(1); + user_action_display(1); ui_idle_flow(); } static void abandon_user_action_display_callback(void) { - bagl_user_action_display(0); + user_action_display(0); ui_idle_flow(); } static void approved_user_action_signtx_callback(void) { - bagl_user_action_signtx(1, 0); + user_action_signtx(1, 0); ui_idle_flow(); } static void abandon_user_action_signtx_callback(void) { - bagl_user_action_signtx(0, 0); + user_action_signtx(0, 0); ui_idle_flow(); } @@ -310,7 +310,7 @@ void ui_confirm_single_flow(void) { ui_transaction_start(ui_confirm_single_flow); } else { snprintf(vars.tmp.feesAmount, sizeof(vars.tmp.feesAmount), "#%d", - context_D.totalOutputs - context_D.remainingOutputs + + context.totalOutputs - context.remainingOutputs + 1); uiContext.tagValues[0].item = "Output"; @@ -479,7 +479,7 @@ static void prompt_public_key(bool warning) { } } -static void display_public_key(bool warning) { +static void display_show_public_key(bool warning) { uiContext.abandon_status = "Address verification\ncancelled"; uiContext.approved_status = "ADDRESS\nVERIFIED"; uiContext.prompt_cancel_message = "Reject\nAddress?"; @@ -507,11 +507,11 @@ static void display_public_key(bool warning) { } void ui_display_public_with_warning_flow(void) { - display_public_key(true); + display_show_public_key(true); } void ui_display_public_flow(void) { - display_public_key(false); + display_show_public_key(false); } void ui_transaction_finish(void) { diff --git a/lib-app-bitcoin/utils/be_operations.c b/lib-app-bitcoin/utils/be_operations.c new file mode 100644 index 00000000..43c916e3 --- /dev/null +++ b/lib-app-bitcoin/utils/be_operations.c @@ -0,0 +1,42 @@ +#include "be_operations.h" + +unsigned char transaction_amount_add_be(unsigned char *target, + unsigned char *a, + unsigned char *b) { + unsigned char carry = 0; + unsigned char i; + for (i = 0; i < 8; i++) { + unsigned short val = a[8 - 1 - i] + b[8 - 1 - i] + (carry ? 1 : 0); + carry = (val > 255); + target[8 - 1 - i] = (val & 255); + } + return carry; +} + +unsigned char transaction_amount_sub_be(unsigned char *target, + unsigned char *a, + unsigned char *b) { + unsigned char borrow = 0; + unsigned char i; + for (i = 0; i < 8; i++) { + unsigned short tmpA = a[8 - 1 - i]; + unsigned short tmpB = b[8 - 1 - i]; + if (borrow) { + if (tmpA <= tmpB) { + tmpA += (255 + 1) - 1; + } else { + borrow = 0; + tmpA--; + } + } + if (tmpA < tmpB) { + borrow = 1; + tmpA += 255 + 1; + } + target[8 - 1 - i] = (unsigned char)(tmpA - tmpB); + } + + return borrow; +} + + diff --git a/src/transaction.h b/lib-app-bitcoin/utils/be_operations.h similarity index 71% rename from src/transaction.h rename to lib-app-bitcoin/utils/be_operations.h index 01b50048..8dc7a9a2 100644 --- a/src/transaction.h +++ b/lib-app-bitcoin/utils/be_operations.h @@ -15,23 +15,7 @@ * limitations under the License. ********************************************************************************/ -#ifndef _TRANSACTION_H_ -#define _TRANSACTION_H_ - -#include "secure_value.h" - -#define TRANSACTION_HASH_NONE 0x00 -#define TRANSACTION_HASH_FULL 0x01 -#define TRANSACTION_HASH_AUTHORIZATION 0x02 -#define TRANSACTION_HASH_BOTH 0x03 - -#define PARSE_MODE_TRUSTED_INPUT 0x01 -#define PARSE_MODE_SIGNATURE 0x02 - -#define TRUSTED_INPUT_SIZE 48 -#define TRUSTED_INPUT_TOTAL_SIZE (TRUSTED_INPUT_SIZE + 8) - -void transaction_parse(unsigned char parseMode); +#pragma once // target = a + b unsigned char transaction_amount_add_be(unsigned char *target, @@ -42,5 +26,3 @@ unsigned char transaction_amount_add_be(unsigned char *target, unsigned char transaction_amount_sub_be(unsigned char *target, unsigned char *a, unsigned char *b); - -#endif /* _TRANSACTION_H_ */ diff --git a/src/cashaddr.c b/lib-app-bitcoin/utils/cashaddr.c similarity index 100% rename from src/cashaddr.c rename to lib-app-bitcoin/utils/cashaddr.c diff --git a/src/cashaddr.h b/lib-app-bitcoin/utils/cashaddr.h similarity index 100% rename from src/cashaddr.h rename to lib-app-bitcoin/utils/cashaddr.h diff --git a/src/segwit_addr.c b/lib-app-bitcoin/utils/segwit_addr.c similarity index 100% rename from src/segwit_addr.c rename to lib-app-bitcoin/utils/segwit_addr.c diff --git a/src/segwit_addr.h b/lib-app-bitcoin/utils/segwit_addr.h similarity index 100% rename from src/segwit_addr.h rename to lib-app-bitcoin/utils/segwit_addr.h diff --git a/src/apdu_get_wallet_public_key.h b/src/apdu_get_wallet_public_key.h deleted file mode 100644 index 429076d6..00000000 --- a/src/apdu_get_wallet_public_key.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _APDU_GET_WALLET_PUBLIC_KEY_H_ -#define _APDU_GET_WALLET_PUBLIC_KEY_H_ - -#define P1_NO_DISPLAY 0x00 -#define P1_DISPLAY 0x01 -#define P1_REQUEST_TOKEN 0x02 - -#define P2_LEGACY 0x00 -#define P2_SEGWIT 0x01 -#define P2_NATIVE_SEGWIT 0x02 -#define P2_CASHADDR 0x03 - -#endif //_APDU_GET_WALLET_PUBLIC_KEY_H_ \ No newline at end of file diff --git a/src/apdu_hash_input_finalize_full.c b/src/apdu_hash_input_finalize_full.c deleted file mode 100644 index 3e69d334..00000000 --- a/src/apdu_hash_input_finalize_full.c +++ /dev/null @@ -1,647 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -// TODO Trustlet, BAGL : process each output separately. -// review nvm_write policy - -#include "internal.h" -#include "apdu_constants.h" -#include "bagl_extensions.h" -#include "ui.h" -#include "lib_standard_app/crypto_helpers.h" -#include "lib_standard_app/read.h" -#include "swap.h" - -#define FINALIZE_P1_MORE 0x00 -#define FINALIZE_P1_LAST 0x80 -#define FINALIZE_P1_CHANGEINFO 0xFF - -#define FINALIZE_P2_DEFAULT 0x00 - -#define FLAG_SIGNATURE 0x01 -#define FLAG_CHANGE_VALIDATED 0x80 - -void apdu_hash_input_finalize_full_reset(void) { - context_D.currentOutputOffset = 0; - context_D.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - memset(context_D.totalOutputAmount, 0, - sizeof(context_D.totalOutputAmount)); - context_D.changeOutputFound = 0; -} - -static bool check_output_displayable() { - bool displayable = true; - unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, - nullAmount = 1; - unsigned char isOpCreate, isOpCall; - - for (j = 0; j < 8; j++) { - if (context_D.currentOutput[j] != 0) { - nullAmount = 0; - break; - } - } - if (!nullAmount) { - swap_bytes(amount, context_D.currentOutput, 8); - transaction_amount_add_be(context_D.totalOutputAmount, - context_D.totalOutputAmount, amount); - } - isOpReturn = - output_script_is_op_return(context_D.currentOutput + 8); - isP2sh = output_script_is_p2sh(context_D.currentOutput + 8); - isNativeSegwit = output_script_is_native_witness( - context_D.currentOutput + 8); - isOpCreate = - output_script_is_op_create(context_D.currentOutput + 8, - sizeof(context_D.currentOutput) - 8); - isOpCall = - output_script_is_op_call(context_D.currentOutput + 8, - sizeof(context_D.currentOutput) - 8); - if (((COIN_KIND == COIN_KIND_HYDRA) && - !output_script_is_regular(context_D.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || - (!(COIN_KIND == COIN_KIND_HYDRA) && - !output_script_is_regular(context_D.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn))) { - PRINTF("Error : Unrecognized output script"); - THROW(EXCEPTION); - } - if (context_D.tmpCtx.output.changeInitialized && !isOpReturn) { - bool changeFound = false; - unsigned char addressOffset = - (isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - : isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH - : OUTPUT_SCRIPT_REGULAR_PRE_LENGTH); - if (!isP2sh && - memcmp(context_D.currentOutput + 8 + addressOffset, - context_D.tmpCtx.output.changeAddress, - 20) == 0) { - changeFound = true; - } else if (isP2sh && context_D.usingSegwit) { - unsigned char changeSegwit[22]; - changeSegwit[0] = 0x00; - changeSegwit[1] = 0x14; - memmove(changeSegwit + 2, - context_D.tmpCtx.output.changeAddress, 20); - public_key_hash160(changeSegwit, 22, changeSegwit); - if (memcmp(context_D.currentOutput + 8 + addressOffset, - changeSegwit, 20) == 0) { - if (COIN_FLAGS & FLAG_SEGWIT_CHANGE_SUPPORT) { - changeFound = true; - } else { - // Attempt to avoid fatal failures on Bitcoin Cash - PRINTF("Error : Non spendable Segwit change"); - THROW(EXCEPTION); - } - } - } - if (changeFound) { - if (context_D.changeOutputFound) { - PRINTF("Error : Multiple change output found"); - THROW(EXCEPTION); - } - context_D.changeOutputFound = true; - displayable = false; - } - } - - return displayable; -} - -bool handle_output_state() { - uint32_t discardSize = 0; - context_D.discardSize = 0; - bool processed = false; - switch (context_D.outputParsingState) { - case OUTPUT_PARSING_NUMBER_OUTPUTS: { - context_D.totalOutputs = 0; - if (context_D.currentOutputOffset < 1) { - break; - } - if (context_D.currentOutput[0] < 0xFD) { - context_D.totalOutputs = context_D.remainingOutputs = - context_D.currentOutput[0]; - discardSize = 1; - context_D.outputParsingState = OUTPUT_PARSING_OUTPUT; - processed = true; - break; - } - if (context_D.currentOutput[0] == 0xFD) { - if (context_D.currentOutputOffset < 3) { - break; - } - context_D.totalOutputs = context_D.remainingOutputs = - (context_D.currentOutput[2] << 8) | - context_D.currentOutput[1]; - discardSize = 3; - context_D.outputParsingState = OUTPUT_PARSING_OUTPUT; - processed = true; - break; - } else if (context_D.currentOutput[0] == 0xFE) { - if (context_D.currentOutputOffset < 5) { - break; - } - context_D.totalOutputs = context_D.remainingOutputs = - read_u32_le(context_D.currentOutput, 1); - discardSize = 5; - context_D.outputParsingState = OUTPUT_PARSING_OUTPUT; - processed = true; - break; - } else { - THROW(EXCEPTION); - } - } break; - - case OUTPUT_PARSING_OUTPUT: { - unsigned int scriptSize; - if (context_D.currentOutputOffset < 9) { - break; - } - if (context_D.currentOutput[8] < 0xFD) { - scriptSize = context_D.currentOutput[8]; - discardSize = 1; - } else if (context_D.currentOutput[8] == 0xFD) { - if (context_D.currentOutputOffset < 9 + 2) { - break; - } - scriptSize = read_u32_le(context_D.currentOutput, 9); - discardSize = 3; - } else { - // Unrealistically large script - THROW(EXCEPTION); - } - if (context_D.currentOutputOffset < - 8 + discardSize + scriptSize) { - discardSize = 0; - break; - } - - processed = true; - - discardSize += 8 + scriptSize; - - if (check_output_displayable()) { - context_D.io_flags |= IO_ASYNCH_REPLY; - - // The output can be processed by the UI - - context_D.discardSize = discardSize; - discardSize = 0; - } else { - context_D.remainingOutputs--; - } - } break; - - default: - THROW(EXCEPTION); - } - - if (discardSize != 0) { - memmove(context_D.currentOutput, - context_D.currentOutput + discardSize, - context_D.currentOutputOffset - discardSize); - context_D.currentOutputOffset -= discardSize; - } - - return processed; -} - -// out should be 32 bytes, even only 20 bytes is significant for output -int get_pubkey_hash160(unsigned char* keyPath, size_t keyPath_len, unsigned char* out) { - cx_ecfp_public_key_t public_key; - int keyLength; - if (get_public_key(keyPath, keyPath_len, public_key.W, NULL)) { - return -1; - } - - compress_public_key_value(public_key.W); - keyLength = 33; - - public_key_hash160( - public_key.W, // IN - keyLength, // INLEN - out // OUT - ); - return 0; -} - -unsigned short apdu_hash_input_finalize_full_internal( - transaction_summary_t *transactionSummary) { - unsigned char authorizationHash[32]; - unsigned char apduLength; - unsigned short sw = SW_OK; - unsigned char *target = G_io_apdu_buffer; - unsigned char p1 = G_io_apdu_buffer[ISO_OFFSET_P1]; - unsigned char hashOffset = 0; - - apduLength = G_io_apdu_buffer[ISO_OFFSET_LC]; - - if ((p1 != FINALIZE_P1_MORE) && (p1 != FINALIZE_P1_LAST) && - (p1 != FINALIZE_P1_CHANGEINFO)) { - return SW_INCORRECT_P1_P2; - } - - // See if there is a hashing offset - if (context_D.usingSegwit && - (context_D.tmpCtx.output.multipleOutput == 0)) { - unsigned char firstByte = G_io_apdu_buffer[ISO_OFFSET_CDATA]; - if (firstByte < 0xfd) { - hashOffset = 1; - } else if (firstByte == 0xfd) { - hashOffset = 3; - } else if (firstByte == 0xfe) { - hashOffset = 5; - } - } - - // Check state - if (context_D.transactionContext.transactionState != - TRANSACTION_PRESIGN_READY) { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - - if (p1 == FINALIZE_P1_CHANGEINFO) { - if (!context_D.transactionContext.firstSigned) { - // Already validated, should be prevented on the client side -return_OK: - return SW_OK; - } - if (!context_D.tmpCtx.output.changeAccepted) { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - memset(transactionSummary, 0, - sizeof(transaction_summary_t)); - if (G_io_apdu_buffer[ISO_OFFSET_CDATA] == 0x00) { - // Called with no change path, abort, should be prevented on - // the client side - goto return_OK; - } - memmove(transactionSummary->keyPath, - G_io_apdu_buffer + ISO_OFFSET_CDATA, - MAX_BIP32_PATH_LENGTH); - - if (get_pubkey_hash160(transactionSummary->keyPath, sizeof(transactionSummary->keyPath), context_D.tmpCtx.output.changeAddress)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - PRINTF("Change address = %.*H\n", 20, context_D.tmpCtx.output.changeAddress); - - context_D.tmpCtx.output.changeInitialized = 1; - context_D.tmpCtx.output.changeAccepted = 0; - - // if the bip44 change path provided is not canonical or its index are unsual, ask for user approval - if(bip44_derivation_guard(transactionSummary->keyPath, true)) { - if (G_called_from_swap) { - PRINTF("In swap mode only standart path is allowed\n"); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - context_D.io_flags |= IO_ASYNCH_REPLY; - context_D.outputParsingState = BIP44_CHANGE_PATH_VALIDATION; - bagl_request_change_path_approval(transactionSummary->keyPath); - } - - goto return_OK; - } - - // Always update the transaction & authorization hashes with the - // given data - // For SegWit, this has been reset to hold hashOutputs - if (!context_D.segwitParsedOnce) { - if ((int)(apduLength - hashOffset) < 0) { - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, 0, G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset, apduLength - hashOffset, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", apduLength - hashOffset, G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset); - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset, - apduLength - hashOffset, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - } - - if (context_D.transactionContext.firstSigned) { - if ((context_D.currentOutputOffset + apduLength) > - sizeof(context_D.currentOutput)) { - PRINTF("Output is too long to be checked\n"); - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - memmove(context_D.currentOutput + - context_D.currentOutputOffset, - G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength); - context_D.currentOutputOffset += apduLength; - - while (handle_output_state() && - (!(context_D.io_flags & IO_ASYNCH_REPLY))) - ; - - // Finalize the TX if necessary - - if ((context_D.remainingOutputs == 0) && - (!(context_D.io_flags & IO_ASYNCH_REPLY))) { - context_D.io_flags |= IO_ASYNCH_REPLY; - context_D.outputParsingState = - OUTPUT_FINALIZE_TX; - } - } - - if (G_io_apdu_buffer[ISO_OFFSET_P1] == FINALIZE_P1_MORE) { - if (!context_D.usingSegwit) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", apduLength, G_io_apdu_buffer + ISO_OFFSET_CDATA); - if (cx_hash_no_throw( - &context_D.transactionHashAuthorization.header, - 0, G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength, - NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - G_io_apdu_buffer[0] = 0x00; - context_D.outLength = 1; - context_D.tmpCtx.output.multipleOutput = 1; - goto return_OK; - } - - if (!context_D.usingSegwit) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", apduLength, G_io_apdu_buffer + ISO_OFFSET_CDATA); - if (cx_hash_no_throw(&context_D.transactionHashAuthorization.header, - CX_LAST, G_io_apdu_buffer + ISO_OFFSET_CDATA, - apduLength, authorizationHash, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - - if (context_D.usingSegwit) { - if (!context_D.segwitParsedOnce) { - if (context_D.usingOverwinter) { - if (cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, CX_LAST, context_D.segwit.cache.hashedOutputs, 0, context_D.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - else { - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, - CX_LAST, - context_D.segwit.cache.hashedOutputs, 0, - context_D.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - if (cx_sha256_init_no_throw(&context_D.transactionHashFull.sha256)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, - CX_LAST, - context_D.segwit.cache.hashedOutputs, - sizeof(context_D.segwit.cache.hashedOutputs), - context_D.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - } - PRINTF("hashOutputs\n%.*H\n",32,context_D.segwit.cache.hashedOutputs); - if (cx_hash_no_throw( - &context_D.transactionHashAuthorization.header, - CX_LAST, G_io_apdu_buffer, 0, authorizationHash, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); - } else { - if (cx_hash_no_throw( - &context_D.transactionHashAuthorization.header, - CX_LAST, - (unsigned char *)&context_D.segwit.cache, - sizeof(context_D.segwit.cache), - authorizationHash, 32)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discardTransaction; - } - PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); - } - } - - if (context_D.transactionContext.firstSigned) { - if (!context_D.tmpCtx.output.changeInitialized) { - memset(transactionSummary, 0, - sizeof(transaction_summary_t)); - } - - transactionSummary->payToAddressVersion = COIN_P2PKH_VERSION; - transactionSummary->payToScriptHashVersion = COIN_P2SH_VERSION; - - // Generate new nonce - - cx_rng(transactionSummary->transactionNonce, 8); - } - - G_io_apdu_buffer[0] = 0x00; - target++; - - *target = 0x00; - target++; - - context_D.outLength = (target - G_io_apdu_buffer); - - // Check that the input being signed is part of the same - // transaction, otherwise abort - // (this is done to keep the transaction counter limit per session - // synchronized) - if (context_D.transactionContext.firstSigned) { - memmove(transactionSummary->authorizationHash, - authorizationHash, - sizeof(transactionSummary->authorizationHash)); - goto return_OK; - } else { - if (os_secure_memcmp( - authorizationHash, - transactionSummary->authorizationHash, - sizeof(transactionSummary->authorizationHash))) { - PRINTF("Authorization hash not matching, aborting\n"); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - - if (context_D.usingSegwit && - !context_D.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context_D.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context_D.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - sw = SW_OK; - } - apdu_hash_input_finalize_full_reset(); - return sw; - -discardTransaction: - apdu_hash_input_finalize_full_reset(); - ui_transaction_error(); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - context_D.outLength = 0; - - memmove(G_io_apdu_buffer, context_D.currentOutput, - context_D.currentOutputOffset); - context_D.outLength = context_D.currentOutputOffset; - return sw; -} - -unsigned short apdu_hash_input_finalize_full() { - PRINTF("state=%d\n", context_D.outputParsingState); - unsigned short sw = apdu_hash_input_finalize_full_internal( - &context_D.transactionSummary); - if (context_D.io_flags & IO_ASYNCH_REPLY) { - // if the UI reject the processing of the request, then reply - // immediately - bool status; - if(context_D.outputParsingState == BIP44_CHANGE_PATH_VALIDATION) { - context_D.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - return sw; - } - else if (context_D.outputParsingState == OUTPUT_FINALIZE_TX) { - status = bagl_finalize_tx(); - } - else { - status = bagl_confirm_single_output(); - } - if (!status) { - ui_transaction_error(); - context_D.io_flags &= ~IO_ASYNCH_REPLY; - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - context_D.outLength = 0; - sw = SW_INCORRECT_DATA; - } - } - return sw; -} - -unsigned char bagl_user_action(unsigned char confirming) { - unsigned short sw = SW_OK; - - // confirm and finish the apdu exchange //spaghetti - - if (confirming) { - // Check if all inputs have been confirmed - - if (context_D.outputParsingState == - OUTPUT_PARSING_OUTPUT) { - context_D.remainingOutputs--; - } - - while (context_D.remainingOutputs != 0) { - memmove(context_D.currentOutput, - context_D.currentOutput + - context_D.discardSize, - context_D.currentOutputOffset - - context_D.discardSize); - context_D.currentOutputOffset -= - context_D.discardSize; - context_D.io_flags &= ~IO_ASYNCH_REPLY; - while (handle_output_state() && - (!(context_D.io_flags & IO_ASYNCH_REPLY))) - ; - if (context_D.io_flags & IO_ASYNCH_REPLY) { - if (!bagl_confirm_single_output()) { - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_INCORRECT_DATA; - break; - } else { - // Let the UI play - return 1; - } - } else { - // Out of data to process, wait for the next call - break; - } - } - - if ((context_D.outputParsingState == - OUTPUT_PARSING_OUTPUT) && - (context_D.remainingOutputs == 0)) { - context_D.outputParsingState = OUTPUT_FINALIZE_TX; - if (!bagl_finalize_tx()) { - context_D.outputParsingState = - OUTPUT_PARSING_NONE; - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_INCORRECT_DATA; - } else { - // Let the UI play - return 1; - } - } - - if (context_D.outputParsingState == - OUTPUT_FINALIZE_TX) { - context_D.transactionContext.firstSigned = 0; - - if (context_D.usingSegwit && - !context_D.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context_D.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context_D.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - } - context_D.outLength -= - 2; // status was already set by the last call - } else { - // Discard transaction - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - context_D.outLength = 0; - } - G_io_apdu_buffer[context_D.outLength++] = sw >> 8; - G_io_apdu_buffer[context_D.outLength++] = sw; - - if ((context_D.outputParsingState == OUTPUT_FINALIZE_TX) || - (sw != SW_OK)) { - - // we've finished the processing of the input - apdu_hash_input_finalize_full_reset(); - } - - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, context_D.outLength); - - return 0; -} diff --git a/src/apdu_hash_input_start.c b/src/apdu_hash_input_start.c deleted file mode 100644 index bc315ae5..00000000 --- a/src/apdu_hash_input_start.c +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" -#include "bagl_extensions.h" -#include "swap.h" - -#define P1_FIRST 0x00 -#define P1_NEXT 0x80 -#define P2_NEW 0x00 -#define P2_NEW_SEGWIT 0x02 -#define P2_NEW_SEGWIT_CASHADDR 0x03 -#define P2_NEW_SEGWIT_OVERWINTER 0x04 -#define P2_NEW_SEGWIT_SAPLING 0x05 -#define P2_CONTINUE 0x80 - -#define IS_INPUT() \ - (G_io_apdu_buffer[ISO_OFFSET_LC] - 1 > 8 \ - && G_io_apdu_buffer[ISO_OFFSET_LC] - 1 <= TRUSTED_INPUT_TOTAL_SIZE + 2 \ - && G_io_apdu_buffer[ISO_OFFSET_CDATA] <= 0x02) \ - -#define IS_INPUT_TRUSTED() \ - (G_io_apdu_buffer[ISO_OFFSET_CDATA] == 0x01 \ - && G_io_apdu_buffer[ISO_OFFSET_CDATA + 1] == TRUSTED_INPUT_TOTAL_SIZE \ - && G_io_apdu_buffer[ISO_OFFSET_CDATA + 2] == MAGIC_TRUSTED_INPUT \ - && G_io_apdu_buffer[ISO_OFFSET_CDATA + 3] == 0x00) - -unsigned short apdu_hash_input_start() { - unsigned char apduLength; - apduLength = G_io_apdu_buffer[ISO_OFFSET_LC]; - - if (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_FIRST) { - // Initialize - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - context_D.transactionHashOption = TRANSACTION_HASH_BOTH; - } else if (G_io_apdu_buffer[ISO_OFFSET_P1] != P1_NEXT) { - return SW_INCORRECT_P1_P2; - } - - if ((G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_OVERWINTER) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_SAPLING)) { - if (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_FIRST) { - unsigned char usingSegwit = - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_OVERWINTER) || - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_SAPLING); - unsigned char usingCashAddr = - (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR); - // Master transaction reset - context_D.transactionContext.firstSigned = 1; - context_D.transactionContext.consumeP2SH = 0; - context_D.transactionContext.relaxed = 0; - context_D.usingSegwit = usingSegwit; - context_D.usingCashAddr = - (COIN_KIND == COIN_KIND_BITCOIN_CASH ? usingCashAddr - : 0); - context_D.usingOverwinter = 0; - if ((COIN_KIND == COIN_KIND_ZCASH) || (COIN_KIND == COIN_KIND_KOMODO) || (COIN_KIND == COIN_KIND_ZCLASSIC) || (COIN_KIND == COIN_KIND_RESISTANCE)) { - if (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_OVERWINTER) { - context_D.usingOverwinter = ZCASH_USING_OVERWINTER; - } - else - if (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_SAPLING) { - context_D.usingOverwinter = ZCASH_USING_OVERWINTER_SAPLING; - } - } - context_D.overwinterSignReady = 0; - context_D.segwitParsedOnce = 0; - // Initialize for screen pairing - memset(&context_D.tmpCtx.output, 0, - sizeof(context_D.tmpCtx.output)); - context_D.tmpCtx.output.changeAccepted = 1; - // Reset segwitWarningSeen flag to prevent displaying the warning for each - // segwit input when coontinuing from a previous session (P2=0x80) - context_D.segwitWarningSeen = 0; - } - } else if (G_io_apdu_buffer[ISO_OFFSET_P2] != P2_CONTINUE) { - return SW_INCORRECT_P1_P2; - } - - // In segwit mode, warn user one time only to update its client wallet... - if (context_D.usingSegwit - && !context_D.segwitWarningSeen - &&(G_io_apdu_buffer[ISO_OFFSET_P1] == P1_NEXT) - && (G_io_apdu_buffer[ISO_OFFSET_P2] != P2_CONTINUE) - // ...if input is not passed as a TrustedInput - && IS_INPUT() - && !IS_INPUT_TRUSTED()) - { - if(G_called_from_swap){ - /* There is no point in displaying a warning when the app is signing - in silent mode, as its UI is hidden behind the exchange app*/ - return SW_SWAP_WITHOUT_TRUSTED_INPUTS; - } - context_D.segwitWarningSeen = 1; - context_D.io_flags |= IO_ASYNCH_REPLY; - bagl_request_segwit_input_approval(); - } - - // Start parsing of the 1st chunk - context_D.transactionBufferPointer = - G_io_apdu_buffer + ISO_OFFSET_CDATA; - context_D.transactionDataRemaining = apduLength; - - transaction_parse(PARSE_MODE_SIGNATURE); - - return SW_OK; -} diff --git a/src/apdu_hash_sign.c b/src/apdu_hash_sign.c deleted file mode 100644 index 9f670031..00000000 --- a/src/apdu_hash_sign.c +++ /dev/null @@ -1,196 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" -#include "bagl_extensions.h" -#include "display_variables.h" -#include "ui.h" -#include "ledger_assert.h" -#include "lib_standard_app/read.h" -#include "lib_standard_app/write.h" -#include "swap.h" - -#define SIGHASH_ALL 0x01 - -#ifndef COIN_FORKID -#define COIN_FORKID 0 -#endif - -unsigned short apdu_hash_sign() { - unsigned long int lockTime; - uint32_t sighashType; - unsigned char dataBuffer[8]; - unsigned char authorizationLength; - unsigned char *parameters = G_io_apdu_buffer + ISO_OFFSET_CDATA; - unsigned short sw = SW_TECHNICAL_DETAILS(0xF); - - if ((G_io_apdu_buffer[ISO_OFFSET_P1] != 0) || - (G_io_apdu_buffer[ISO_OFFSET_P2] != 0)) { - return SW_INCORRECT_P1_P2; - } - - if (G_io_apdu_buffer[ISO_OFFSET_LC] < (1 + 1 + 4 + 1)) { - return SW_INCORRECT_LENGTH; - } - - // Zcash special - store parameters for later - - if ((context_D.usingOverwinter) && - (!context_D.overwinterSignReady) && - (context_D.segwitParsedOnce) && - (context_D.transactionContext.transactionState == TRANSACTION_NONE)) { - unsigned long int expiryHeight; - parameters += (4 * G_io_apdu_buffer[ISO_OFFSET_CDATA]) + 1; - authorizationLength = *(parameters++); - parameters += authorizationLength; - lockTime = read_u32_be(parameters, 0); - parameters += 4; - sighashType = *(parameters++); - expiryHeight = read_u32_be(parameters, 0); - write_u32_le(context_D.nLockTime, 0, lockTime); - write_u32_le(context_D.sigHashType, 0, sighashType); - write_u32_le(context_D.nExpiryHeight, 0, expiryHeight); - context_D.overwinterSignReady = 1; - return SW_OK; - } - - if (context_D.transactionContext.transactionState != - TRANSACTION_SIGN_READY) { - PRINTF("Invalid transaction state %d\n", context_D.transactionContext.transactionState); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - - if (context_D.usingOverwinter && !context_D.overwinterSignReady) { - PRINTF("Overwinter not ready to sign\n"); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - - // Read parameters - if (G_io_apdu_buffer[ISO_OFFSET_CDATA] > MAX_BIP32_PATH) { - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - memmove(context_D.transactionSummary.keyPath, - G_io_apdu_buffer + ISO_OFFSET_CDATA, - MAX_BIP32_PATH_LENGTH); - parameters += (4 * G_io_apdu_buffer[ISO_OFFSET_CDATA]) + 1; - authorizationLength = *(parameters++); - parameters += authorizationLength; - lockTime = read_u32_be(parameters, 0); - parameters += 4; - sighashType = *(parameters++); - context_D.transactionSummary.sighashType = sighashType; - - // if bitcoin cash OR forkid is set, then use the fork id - if (COIN_KIND == COIN_KIND_BITCOIN_CASH || (COIN_FORKID)) { -#define SIGHASH_FORKID 0x40 - if (sighashType != (SIGHASH_ALL | SIGHASH_FORKID)) { - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - sighashType |= (COIN_FORKID << 8); - } else { - if (sighashType != SIGHASH_ALL) { - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - } - - // Finalize the hash - if (!context_D.usingOverwinter) { - write_u32_le(dataBuffer, 0, lockTime); - write_u32_le(dataBuffer, 4, sighashType); - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - dataBuffer, sizeof(dataBuffer), NULL, 0)) { - goto discardTransaction; - } - } - - // Check if the path needs to be enforced - if (!enforce_bip44_coin_type(context_D.transactionSummary.keyPath, false)) { - context_D.io_flags |= IO_ASYNCH_REPLY; - bagl_request_sign_path_approval(context_D.transactionSummary.keyPath); - } - else { - // Sign immediately - bagl_user_action_signtx(1, 1); - } - sw = SW_OK; - if (G_called_from_swap) { - // if we signed all outputs we should exit, - // but only after sending response, so lets raise the - // vars.swap_data.should_exit flag and check it on timer later - vars.swap_data.alreadySignedInputs++; - if (vars.swap_data.alreadySignedInputs >= vars.swap_data.totalNumberOfInputs) { - vars.swap_data.should_exit = 1; - } - } - - return sw; - - discardTransaction: - context_D.transactionContext.transactionState = TRANSACTION_NONE; - return sw; -} - -void bagl_user_action_signtx(unsigned char confirming, unsigned char direct) { - unsigned short sw = SW_OK; - // confirm and finish the apdu exchange //spaghetti - if (confirming) { - unsigned char hash[32]; - if (context_D.usingOverwinter) { - LEDGER_ASSERT(cx_hash_no_throw(&context_D.transactionHashFull.blake2b.header, CX_LAST, hash, 0, hash, 32) == CX_OK, "Hash Failed"); - } - else { - LEDGER_ASSERT(cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, CX_LAST, - hash, 0, hash, 32) == CX_OK, "Hash Failed"); - PRINTF("Hash1\n%.*H\n", sizeof(hash), hash); - - // Rehash - cx_hash_sha256(hash, sizeof(hash), hash, 32); - } - PRINTF("Hash2\n%.*H\n", sizeof(hash), hash); - // Sign - size_t out_len = sizeof(G_io_apdu_buffer); - sign_finalhash( - context_D.transactionSummary.keyPath, - sizeof(context_D.transactionSummary.keyPath), - hash, sizeof(hash), - G_io_apdu_buffer, &out_len, - 1); - - context_D.outLength = G_io_apdu_buffer[1] + 2; - G_io_apdu_buffer[context_D.outLength++] = context_D.transactionSummary.sighashType; - ui_transaction_finish(); - - } else { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - context_D.outLength = 0; - } - - if (!direct) { - G_io_apdu_buffer[context_D.outLength++] = sw >> 8; - G_io_apdu_buffer[context_D.outLength++] = sw; - - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, context_D.outLength); - } -} - diff --git a/src/apdu_sign_message.c b/src/apdu_sign_message.c deleted file mode 100644 index 9a5bbc54..00000000 --- a/src/apdu_sign_message.c +++ /dev/null @@ -1,290 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" -#include "bagl_extensions.h" -#include "lib_standard_app/read.h" -#include "swap.h" - -#define P1_PREPARE 0x00 -#define P1_SIGN 0x80 -#define P2_LEGACY 0x00 -#define P2_FIRST 0x01 -#define P2_OTHER 0x80 - -#define BITID_NONE 0 -#define BITID_POWERCYCLE 1 -#define BITID_MULTIPLE 2 - -//#define SLIP_13 0x8000000D - -unsigned short compute_hash(void); - -unsigned char checkBitId(unsigned char *bip32Path) { - unsigned char i; - unsigned char bip32PathLength = bip32Path[0]; - bip32Path++; - for (i = 0; i < bip32PathLength; i++) { - unsigned short account = read_u32_be(bip32Path, 0); - bip32Path += 4; - - if (account == BITID_DERIVE) { - return BITID_POWERCYCLE; - } - if (account == BITID_DERIVE_MULTIPLE) { - return BITID_MULTIPLE; - } - } - return BITID_NONE; -} - -// TODO : support longer messages - -unsigned short apdu_sign_message_internal() { - unsigned short sw = SW_OK; - unsigned char p1 = G_io_apdu_buffer[ISO_OFFSET_P1]; - unsigned char p2 = G_io_apdu_buffer[ISO_OFFSET_P2]; - unsigned char apduLength = G_io_apdu_buffer[ISO_OFFSET_LC]; - unsigned short offset = ISO_OFFSET_CDATA; - - if ((p1 != P1_PREPARE) && (p1 != P1_SIGN)) { - return SW_INCORRECT_P1_P2; - } - if (p1 == P1_PREPARE) { - if ((p2 != P2_FIRST) && (p2 != P2_OTHER) && (p2 != P2_LEGACY)) { - return SW_INCORRECT_P1_P2; - } - } - - if (p1 == P1_PREPARE) { - if ((p2 == P2_FIRST) || (p2 == P2_LEGACY)) { - unsigned char chunkLength; - unsigned char messageLength[3]; - unsigned char messageLengthSize; - memset(&context_D.transactionSummary, 0, - sizeof(transaction_summary_t)); - if (G_io_apdu_buffer[offset] > MAX_BIP32_PATH) { - PRINTF("Invalid path\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - context_D.transactionSummary.payToAddressVersion = COIN_P2PKH_VERSION; - context_D.transactionSummary.payToScriptHashVersion = COIN_P2SH_VERSION; - memmove( - context_D.transactionSummary.keyPath, - G_io_apdu_buffer + offset, MAX_BIP32_PATH_LENGTH); - offset += (4 * G_io_apdu_buffer[offset]) + 1; - if (p2 == P2_LEGACY) { - context_D.transactionSummary.messageLength = - G_io_apdu_buffer[offset]; - offset++; - } else { - context_D.transactionSummary.messageLength = - (G_io_apdu_buffer[offset] << 8) | - (G_io_apdu_buffer[offset + 1]); - offset += 2; - } - if (context_D.transactionSummary.messageLength == - 0) { - PRINTF("Null message length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - context_D.hashedMessageLength = 0; - if (cx_sha256_init_no_throw(&context_D.transactionHashFull.sha256)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - if (cx_sha256_init_no_throw( - &context_D.transactionHashAuthorization)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - // Horizen signed message magic header is "Zcash" - // See https://github.com/HorizenOfficial/zen/blob/v5.0.0/src/main.cpp#L122 - const char* magicHeader = (COIN_KIND != COIN_KIND_HORIZEN) ? COIN_COINID : "Zcash"; - chunkLength = - strlen(magicHeader) + SIGNMAGIC_LENGTH; - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - &chunkLength, 1, NULL, 0)) { - goto discard; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - (uint8_t *)magicHeader, - strlen(magicHeader), NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - (unsigned char *)SIGNMAGIC, SIGNMAGIC_LENGTH, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - if (context_D.transactionSummary.messageLength < - 0xfd) { - messageLength[0] = - context_D.transactionSummary.messageLength; - messageLengthSize = 1; - } else { - messageLength[0] = 0xfd; - messageLength[1] = - (context_D.transactionSummary.messageLength & - 0xff); - messageLength[2] = ((context_D.transactionSummary - .messageLength >> - 8) & - 0xff); - messageLengthSize = 3; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - messageLength, messageLengthSize, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - chunkLength = apduLength - (offset - ISO_OFFSET_CDATA); - if ((context_D.hashedMessageLength + chunkLength) > - context_D.transactionSummary.messageLength) { - PRINTF("Invalid data length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - G_io_apdu_buffer + offset, chunkLength, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - if (cx_hash_no_throw( - &context_D.transactionHashAuthorization.header, - 0, G_io_apdu_buffer + offset, chunkLength, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - context_D.hashedMessageLength += chunkLength; - G_io_apdu_buffer[0] = 0x00; - if (context_D.hashedMessageLength == - context_D.transactionSummary.messageLength) { - G_io_apdu_buffer[1] = 0x00; - context_D.outLength = 2; - } else { - context_D.outLength = 1; - } - } else { - if ((context_D.hashedMessageLength + apduLength) > - context_D.transactionSummary.messageLength) { - PRINTF("Invalid data length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, 0, - G_io_apdu_buffer + offset, apduLength, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - if (cx_hash_no_throw( - &context_D.transactionHashAuthorization.header, - 0, G_io_apdu_buffer + offset, apduLength, NULL, 0)) { - sw = SW_TECHNICAL_DETAILS(0x0F); - goto discard; - } - context_D.hashedMessageLength += apduLength; - G_io_apdu_buffer[0] = 0x00; - if (context_D.hashedMessageLength == - context_D.transactionSummary.messageLength) { - G_io_apdu_buffer[1] = 0x00; - context_D.outLength = 2; - } else { - context_D.outLength = 1; - } - } - } else { - if ((context_D.transactionSummary.messageLength == 0) || - (context_D.hashedMessageLength != - context_D.transactionSummary.messageLength)) { - PRINTF("Invalid length to sign\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (checkBitId(context_D.transactionSummary.keyPath) != BITID_NONE) { - sw = compute_hash(); - } else { - context_D.io_flags |= IO_ASYNCH_REPLY; - return SW_OK; - } - } - return sw; - - discard : - memset(&context_D.transactionSummary, 0, - sizeof(transaction_summary_t)); - return sw; -} - -unsigned short apdu_sign_message() { - if (G_called_from_swap) { - return SW_SECURITY_STATUS_NOT_SATISFIED; - } - unsigned short sw = apdu_sign_message_internal(); - if (context_D.io_flags & IO_ASYNCH_REPLY) { - bagl_confirm_message_signature(); - } - return sw; -} - -unsigned short compute_hash() { - unsigned char hash[32]; - unsigned short sw = SW_OK; - - context_D.outLength = 0; - if (cx_hash_no_throw(&context_D.transactionHashFull.sha256.header, CX_LAST, hash, - 0, hash, 32)) { - goto discard; - } - - if (cx_hash_sha256(hash, sizeof(hash), hash, 32) == 0) { - goto discard; - } - - size_t out_len = 100; - sign_finalhash( - context_D.transactionSummary.keyPath, - sizeof(context_D.transactionSummary.keyPath), - hash, sizeof(hash), // IN - G_io_apdu_buffer, &out_len, // OUT - 1); - context_D.outLength = G_io_apdu_buffer[1] + 2; - memset(&context_D.transactionSummary, 0, - sizeof(transaction_summary_t)); - return sw; - - discard: - sw = SW_TECHNICAL_DETAILS(0x0F); - return sw; -} - -void bagl_user_action_message_signing(unsigned char confirming) { - unsigned short sw; - if (confirming) { - sw = compute_hash(); - } else { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - } - G_io_apdu_buffer[context_D.outLength++] = sw >> 8; - G_io_apdu_buffer[context_D.outLength++] = sw; - - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, context_D.outLength); -} diff --git a/src/bcd.c b/src/bcd.c deleted file mode 100644 index a7878d0b..00000000 --- a/src/bcd.c +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" - -#define SCRATCH_SIZE 21 - -unsigned char -convert_hex_amount_to_displayable_no_globals(unsigned char *amount, unsigned int config_flag, unsigned char* out) { - unsigned char LOOP1; - unsigned char LOOP2; - if (!(config_flag & FLAG_PEERCOIN_UNITS)) { - LOOP1 = 13; - LOOP2 = 8; - } else { - LOOP1 = 15; - LOOP2 = 6; - } - unsigned short scratch[SCRATCH_SIZE]; - unsigned char offset = 0; - unsigned char nonZero = 0; - unsigned char i; - unsigned char targetOffset = 0; - unsigned char workOffset; - unsigned char j; - unsigned char nscratch = SCRATCH_SIZE; - unsigned char smin = nscratch - 2; - unsigned char comma = 0; - - for (i = 0; i < SCRATCH_SIZE; i++) { - scratch[i] = 0; - } - for (i = 0; i < 8; i++) { - for (j = 0; j < 8; j++) { - unsigned char k; - unsigned short shifted_in = - (((amount[i] & 0xff) & ((1 << (7 - j)))) != 0) ? (short)1 - : (short)0; - for (k = smin; k < nscratch; k++) { - scratch[k] += ((scratch[k] >= 5) ? 3 : 0); - } - if (scratch[smin] >= 8) { - smin -= 1; - } - for (k = smin; k < nscratch - 1; k++) { - scratch[k] = - ((scratch[k] << 1) & 0xF) | ((scratch[k + 1] >= 8) ? 1 : 0); - } - scratch[nscratch - 1] = ((scratch[nscratch - 1] << 1) & 0x0F) | - (shifted_in == 1 ? 1 : 0); - } - } - - for (i = 0; i < LOOP1; i++) { - if (!nonZero && (scratch[offset] == 0)) { - offset++; - } else { - nonZero = 1; - out[targetOffset++] = scratch[offset++] + '0'; - } - } - if (targetOffset == 0) { - out[targetOffset++] = '0'; - } - workOffset = offset; - for (i = 0; i < LOOP2; i++) { - unsigned char allZero = 1; - for (j = i; j < LOOP2; j++) { - if (scratch[workOffset + j] != 0) { - allZero = 0; - break; - } - } - if (allZero) { - break; - } - if (!comma) { - out[targetOffset++] = '.'; - comma = 1; - } - out[targetOffset++] = scratch[offset++] + '0'; - } - return targetOffset; -} - -unsigned char -convert_hex_amount_to_displayable(unsigned char *amount) { - return convert_hex_amount_to_displayable_no_globals(amount, COIN_FLAGS, context_D.tmp); -} diff --git a/src/bip32_path.c b/src/bip32_path.c deleted file mode 100644 index 8be5d787..00000000 --- a/src/bip32_path.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "lib_standard_app/read.h" -#include "bip32_path.h" -#include "helpers.h" - -bool parse_serialized_path(bip32_path_t* path, unsigned char* serialized_path, unsigned char serialized_path_length) { - if (serialized_path_length < 1 || - serialized_path[0] > MAX_BIP32_PATH || - serialized_path[0] * 4 + 1 > serialized_path_length) - return false; - path->length = serialized_path[0]; - serialized_path++; - for (int i = 0; i < path->length; i += 1, serialized_path += 4) { - path->path[i] = read_u32_be(serialized_path, 0); - } - return true; -} diff --git a/src/bip32_path.h b/src/bip32_path.h deleted file mode 100644 index 819ea8c9..00000000 --- a/src/bip32_path.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _BIP32_PATH_H_ -#define _BIP32_PATH_H_ - -#include "stdbool.h" - -#define MAX_BIP32_PATH 10 -#define MAX_BIP32_PATH_LENGTH (4 * MAX_BIP32_PATH) + 1 - -typedef struct bip32_path { - unsigned char length; - unsigned int path[MAX_BIP32_PATH]; -} bip32_path_t; - -bool parse_serialized_path(bip32_path_t* path, unsigned char* serialized_path, unsigned char serialized_path_length); - -#endif \ No newline at end of file diff --git a/src/btchip.c b/src/btchip.c deleted file mode 100644 index 2d65d16f..00000000 --- a/src/btchip.c +++ /dev/null @@ -1,606 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ -#include "os.h" -#include "cx.h" - -#include "string.h" - -#include "internal.h" - -#include "bagl_extensions.h" - -#include "segwit_addr.h" -#include "cashaddr.h" - -#include "ux.h" -#include "display_variables.h" -#include "swap_lib_calls.h" - -#include "handle_swap_sign_transaction.h" -#include "handle_get_printable_amount.h" -#include "handle_check_address.h" -#include "ui.h" -#include "lib_standard_app/format.h" -#include "read.h" -#include "write.h" -#include "bip32.h" -#include "swap.h" - -unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) { - switch (channel & ~(IO_FLAGS)) { - case CHANNEL_KEYBOARD: - break; - - // multiplexed io exchange over a SPI channel and TLV encapsulated protocol - case CHANNEL_SPI: - if (tx_len) { - io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - - if (channel & IO_RESET_AFTER_REPLIED) { - reset(); - } - return 0; // nothing received from the master so far (it's a tx - // transaction) - } else { - return io_seproxyhal_spi_recv(G_io_apdu_buffer, - sizeof(G_io_apdu_buffer), 0); - } - - default: - THROW(INVALID_PARAMETER); - } - return 0; -} - -unsigned char io_event(unsigned char channel) { - UNUSED(channel); - // nothing done with the event, throw an error on the transport layer if - // needed - - // can't have more than one tag in the reply, not supported yet. - switch (G_io_seproxyhal_spi_buffer[0]) { -#ifdef HAVE_NBGL - case SEPROXYHAL_TAG_FINGER_EVENT: - UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer); - break; -#endif // HAVE_NBGL - - case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: -#ifdef HAVE_BAGL - UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); -#endif // HAVE_BAGL - break; - - case SEPROXYHAL_TAG_STATUS_EVENT: - if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && - !(U4BE(G_io_seproxyhal_spi_buffer, 3) & - SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { - THROW(EXCEPTION_IO_RESET); - } - __attribute__((fallthrough)); - default: - UX_DEFAULT_EVENT(); - break; - - case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: -#ifdef HAVE_BAGL - UX_DISPLAYED_EVENT({}); -#endif // HAVE_BAGL -#ifdef HAVE_NBGL - UX_DEFAULT_EVENT(); -#endif // HAVE_NBGL - break; - - case SEPROXYHAL_TAG_TICKER_EVENT: - // TODO: found less hacky way to exit library after sending response - // this mechanism is used for Swap/Exchange functionality - // when application is in silent mode, and should return to caller, - // after responding some APDUs - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {}); - break; - } - - // close the event if not done previously (by a display or whatever) - if (!io_seproxyhal_spi_is_status_sent()) { - io_seproxyhal_general_status(); - } - - // command has been processed, DO NOT reset the current APDU transport - return 1; -} - -uint8_t check_fee_swap() { - unsigned char fees[8]; - unsigned char borrow; - - borrow = transaction_amount_sub_be( - fees, context_D.transactionContext.transactionAmount, - context_D.totalOutputAmount); - if ((borrow != 0) || (memcmp(fees, vars.swap_data.fees, 8) != 0)) - return 0; - context_D.transactionContext.firstSigned = 0; - - if (context_D.usingSegwit && !context_D.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context_D.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context_D.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - context_D.sw = 0x9000; - context_D.outLength = 0; - G_io_apdu_buffer[context_D.outLength++] = 0x90; - G_io_apdu_buffer[context_D.outLength++] = 0x00; - - return 1; -} - -uint8_t prepare_fees(void) { - if (context_D.transactionContext.relaxed) { - memmove(vars.tmp.feesAmount, "UNKNOWN", 7); - vars.tmp.feesAmount[7] = '\0'; - } else { - unsigned char fees[8]; - unsigned short textSize; - unsigned char borrow; - - borrow = transaction_amount_sub_be( - fees, context_D.transactionContext.transactionAmount, - context_D.totalOutputAmount); - if (borrow && COIN_KIND == COIN_KIND_KOMODO) { - memmove(vars.tmp.feesAmount, "REWARD", 6); - vars.tmp.feesAmount[6] = '\0'; - } - else { - if (borrow) { - PRINTF("Error : Fees not consistent"); - goto error; - } - memmove(vars.tmp.feesAmount, COIN_COINID_SHORT, - strlen(COIN_COINID_SHORT)); - vars.tmp.feesAmount[strlen(COIN_COINID_SHORT)] = ' '; - context_D.tmp = - (unsigned char *)(vars.tmp.feesAmount + - strlen(COIN_COINID_SHORT) + 1); - textSize = convert_hex_amount_to_displayable(fees); - vars.tmp.feesAmount[textSize + strlen(COIN_COINID_SHORT) + 1] = - '\0'; - } - } - return 1; -error: - return 0; -} - -#define OMNI_ASSETID 1 -#define MAIDSAFE_ASSETID 3 -#define USDT_ASSETID 31 - -void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size) { - if (output_script_is_op_return(script)) { - strncpy(out, "OP_RETURN", out_size); - return; - } - if ((COIN_KIND == COIN_KIND_HYDRA) && - output_script_is_op_create(script, script_size)) { - strncpy(out, "OP_CREATE", out_size); - return; - } - if ((COIN_KIND == COIN_KIND_HYDRA) && - output_script_is_op_call(script, script_size)) { - strncpy(out, "OP_CALL", out_size); - return; - } - if (output_script_is_native_witness(script)) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - segwit_addr_encode( - out, (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), 0, - script + OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET, - script[OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - 1]); - } - return; - } - unsigned char versionSize; - unsigned char address[22]; - unsigned short textSize; - int addressOffset = 3; - unsigned short version = COIN_P2SH_VERSION; - - if (output_script_is_regular(script)) { - addressOffset = 4; - version = COIN_P2PKH_VERSION; - } - - if (version > 255) { - versionSize = 2; - address[0] = (version >> 8); - address[1] = version; - } else { - versionSize = 1; - address[0] = version; - } - memmove(address + versionSize, script + addressOffset, 20); - - // Prepare address - if (context_D.usingCashAddr) { - cashaddr_encode( - address + versionSize, 20, (uint8_t *)out, out_size, - (version == COIN_P2SH_VERSION - ? CASHADDR_P2SH - : CASHADDR_P2PKH)); - } else { - textSize = public_key_to_encoded_base58( - address, 20 + versionSize, (unsigned char *)out, - out_size, version, 1); - out[textSize] = '\0'; - } -} - -uint8_t prepare_single_output(void) { - // TODO : special display for OP_RETURN - unsigned char amount[8]; - unsigned int offset = 0; - unsigned short textSize; - char tmp[80] = {0}; - - swap_bytes(amount, context_D.currentOutput + offset, 8); - offset += 8; - - get_address_from_output_script(context_D.currentOutput + offset, sizeof(context_D.currentOutput) - offset, tmp, sizeof(tmp)); - strncpy(vars.tmp.fullAddress, tmp, sizeof(vars.tmp.fullAddress) - 1); - - // Prepare amount - - // Handle Omni simple send - if ((context_D.currentOutput[offset + 2] == 0x14) && - (memcmp(context_D.currentOutput + offset + 3, "omni", 4) == 0) && - (memcmp(context_D.currentOutput + offset + 3 + 4, "\0\0\0\0", 4) == 0)) { - uint8_t headerLength; - uint32_t omniAssetId = read_u32_be(context_D.currentOutput, offset + 3 + 4 + 4); - switch(omniAssetId) { - case OMNI_ASSETID: - strcpy(vars.tmp.fullAmount, "OMNI "); - break; - case USDT_ASSETID: - strcpy(vars.tmp.fullAmount, "USDT "); - break; - case MAIDSAFE_ASSETID: - strcpy(vars.tmp.fullAmount, "MAID "); - break; - default: - snprintf(vars.tmp.fullAmount, sizeof(vars.tmp.fullAmount), "OMNI asset %d ", omniAssetId); - break; - } - headerLength = strlen(vars.tmp.fullAmount); - context_D.tmp = (uint8_t *)vars.tmp.fullAmount + headerLength; - textSize = convert_hex_amount_to_displayable(context_D.currentOutput + offset + 3 + 4 + 4 + 4); - vars.tmp.fullAmount[textSize + headerLength] = '\0'; - } - else { - memmove(vars.tmp.fullAmount, COIN_COINID_SHORT, - strlen(COIN_COINID_SHORT)); - vars.tmp.fullAmount[strlen(COIN_COINID_SHORT)] = ' '; - context_D.tmp = - (unsigned char *)(vars.tmp.fullAmount + - strlen(COIN_COINID_SHORT) + 1); - textSize = convert_hex_amount_to_displayable(amount); - vars.tmp.fullAmount[textSize + strlen(COIN_COINID_SHORT) + 1] = - '\0'; - } - - return 1; -} - -uint8_t prepare_message_signature(void) { - uint8_t buffer[32]; - - if (cx_hash_no_throw(&context_D.transactionHashAuthorization.header, CX_LAST, - (uint8_t*)vars.tmp.fullAmount, 0, buffer, 32)) { - return 0; - } - - format_hex((const uint8_t*) buffer, sizeof(buffer), vars.tmp.fullAddress, sizeof(vars.tmp.fullAddress)); - - return 1; -} - - -extern bool handle_output_state(void); -extern void apdu_hash_input_finalize_full_reset(void); - -// Analog of bagl_confirm_single_output to work -// in silent mode, when called from SWAP app -unsigned int silent_confirm_single_output() { - char tmp[80] = {0}; - unsigned char amount[8]; - while (true) { - // in swap operation we can only have 1 "external" output - if (vars.swap_data.was_address_checked) { - PRINTF("Address was already checked\n"); - return 0; - } - vars.swap_data.was_address_checked = 1; - // check amount - swap_bytes(amount, context_D.currentOutput, 8); - if (memcmp(amount, vars.swap_data.amount, 8) != 0) { - PRINTF("Amount not matched\n"); - return 0; - } - get_address_from_output_script(context_D.currentOutput + 8, sizeof(context_D.currentOutput) - 8, tmp, sizeof(tmp)); - if (strcmp(tmp, vars.swap_data.destination_address) != 0) { - PRINTF("Address not matched\n"); - return 0; - } - - // Check if all inputs have been confirmed - - if (context_D.outputParsingState == - OUTPUT_PARSING_OUTPUT) { - context_D.remainingOutputs--; - if (context_D.remainingOutputs == 0) - break; - } - - memmove(context_D.currentOutput, - context_D.currentOutput + - context_D.discardSize, - context_D.currentOutputOffset - - context_D.discardSize); - context_D.currentOutputOffset -= context_D.discardSize; - context_D.io_flags &= ~IO_ASYNCH_REPLY; - while (handle_output_state() && - (!(context_D.io_flags & IO_ASYNCH_REPLY))) - ; - if (!(context_D.io_flags & IO_ASYNCH_REPLY)) { - // Out of data to process, wait for the next call - break; - } - } - - if ((context_D.outputParsingState == OUTPUT_PARSING_OUTPUT) && - (context_D.remainingOutputs == 0)) { - context_D.outputParsingState = OUTPUT_FINALIZE_TX; - // check fees - unsigned char fees[8]; - - if ((transaction_amount_sub_be(fees, - context_D.transactionContext.transactionAmount, - context_D.totalOutputAmount) != 0) || - (memcmp(fees, vars.swap_data.fees, 8) != 0)) { - PRINTF("Fees is not matched\n"); - return 0; - } - } - - if (context_D.outputParsingState == OUTPUT_FINALIZE_TX) { - context_D.transactionContext.firstSigned = 0; - - if (context_D.usingSegwit && - !context_D.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context_D.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context_D.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context_D.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - } - if (context_D.outputParsingState == OUTPUT_FINALIZE_TX) { - // we've finished the processing of the input - apdu_hash_input_finalize_full_reset(); - } - - return 1; -} - -unsigned int bagl_confirm_single_output(void) { - if (G_called_from_swap) { - return silent_confirm_single_output(); - } - if (!prepare_single_output()) { - return 0; - } - - ui_confirm_single_flow(); - return 1; -} - -unsigned int bagl_finalize_tx(void) { - if (G_called_from_swap) { - return check_fee_swap(); - } - - if (!prepare_fees()) { - return 0; - } - - ui_finalize_flow(); - return 1; -} - -void bagl_confirm_message_signature(void) { - if (!prepare_message_signature()) { - return; - } - - ui_sign_message_flow(); -} - -uint8_t set_key_path_to_display(unsigned char* keyPath) { - bip32_print_path(keyPath, vars.tmp_warning.derivation_path, MAX_DERIV_PATH_ASCII_LENGTH); - return bip44_derivation_guard(keyPath, false); -} - -void bagl_display_public_key(uint8_t is_derivation_path_unusual) { - // append a white space at the end of the address to avoid glitch on nano S - strlcat((char *)G_io_apdu_buffer + 200, " ", sizeof(G_io_apdu_buffer) - 200); - - if (is_derivation_path_unusual) { - ui_display_public_with_warning_flow(); - } - else { - ui_display_public_flow(); - } -} - -void bagl_display_token(void) -{ - ui_display_token_flow(); -} - -void bagl_request_pubkey_approval(void) -{ - ui_request_pubkey_approval_flow(); -} - -void bagl_request_change_path_approval(unsigned char* change_path) -{ - - bip32_print_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); - ui_request_change_path_approval_flow(); -} - -void bagl_request_sign_path_approval(unsigned char* change_path) -{ - bip32_print_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); - ui_request_sign_path_approval_flow(); -} - -void bagl_request_segwit_input_approval(void) -{ - ui_request_segwit_input_approval_flow(); -} - - -#include "os.h" - -#include "internal.h" - -#include "os_io_seproxyhal.h" - -#include "apdu_constants.h" -#include "display_variables.h" - -#include "handle_swap_sign_transaction.h" - -#define TECHNICAL_NOT_IMPLEMENTED 0x99 - -#define COMMON_CLA 0xB0 - -void app_dispatch(void) { - unsigned char cla; - unsigned char ins; - unsigned char dispatched; - - // nothing to reply for now - context_D.outLength = 0; - context_D.io_flags = 0; - - cla = G_io_apdu_buffer[ISO_OFFSET_CLA]; - ins = G_io_apdu_buffer[ISO_OFFSET_INS]; - for (dispatched = 0; dispatched < DISPATCHER_APDUS; dispatched++) { - if ((cla == DISPATCHER_CLA[dispatched]) && - (ins == DISPATCHER_INS[dispatched])) { - break; - } - } - if (dispatched == DISPATCHER_APDUS) { - context_D.sw = SW_INS_NOT_SUPPORTED; - goto sendSW; - } - if (DISPATCHER_DATA_IN[dispatched]) { - if (G_io_apdu_buffer[ISO_OFFSET_LC] == 0x00 || - context_D.inLength - 5 == 0) { - context_D.sw = SW_INCORRECT_LENGTH; - goto sendSW; - } - // notify we need to receive data - // io_exchange(CHANNEL_APDU | IO_RECEIVE_DATA, 0); - } - // call the apdu handler - context_D.sw = ((apduProcessingFunction)PIC( - DISPATCHER_FUNCTIONS[dispatched]))(); - - // an APDU has been replied. request for power off time extension from the - // common ux -#ifdef IO_APP_ACTIVITY - IO_APP_ACTIVITY(); -#endif // IO_APP_ACTIVITY - -sendSW: - if (G_called_from_swap) { - context_D.io_flags &= ~IO_ASYNCH_REPLY; - if(context_D.sw != SW_OK) { - vars.swap_data.should_exit = 1; - } - } - // prepare SW after replied data - G_io_apdu_buffer[context_D.outLength] = - (context_D.sw >> 8); - G_io_apdu_buffer[context_D.outLength + 1] = - (context_D.sw & 0xff); - context_D.outLength += 2; - return; -} - -void app_main(void) { - context_init(); - - ui_idle_flow(); - - memset(G_io_apdu_buffer, 0, 255); // paranoia - - // Process the incoming APDUs - - // first exchange, no out length :) only wait the apdu - context_D.outLength = 0; - context_D.io_flags = 0; - for (;;) { - - // memset(G_io_apdu_buffer, 0, 255); // paranoia - - if (G_called_from_swap && vars.swap_data.should_exit) { - context_D.io_flags |= IO_RETURN_AFTER_TX; - } - - // receive the whole apdu using the 7 bytes headers (ledger transport) - context_D.inLength = - io_exchange(CHANNEL_APDU | context_D.io_flags, - // use the previous outlength as the reply - context_D.outLength); - - if (G_called_from_swap && vars.swap_data.should_exit) { - swap_finalize_exchange_sign_transaction(context_D.sw == SW_OK); - } - - PRINTF("New APDU received:\n%.*H\n", context_D.inLength, G_io_apdu_buffer); - - app_dispatch(); - - // reply during reception of next apdu - } - - PRINTF("End of main loop\n"); - - // in case reached - reset(); -} diff --git a/src/ecc.c b/src/ecc.c deleted file mode 100644 index 69092c81..00000000 --- a/src/ecc.c +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" - -void compress_public_key_value(unsigned char *value) { - value[0] = ((value[64] & 1) ? 0x03 : 0x02); -} diff --git a/src/helpers.c b/src/helpers.c deleted file mode 100644 index 605acb06..00000000 --- a/src/helpers.c +++ /dev/null @@ -1,407 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" -#include "lib_standard_app/crypto_helpers.h" -#include "lib_standard_app/read.h" -#include "lib_standard_app/base58.h" -#include "bip32_path.h" -#include "ledger_assert.h" - -const unsigned char TRANSACTION_OUTPUT_SCRIPT_PRE[] = { - 0x19, 0x76, 0xA9, - 0x14}; // script length, OP_DUP, OP_HASH160, address length -const unsigned char TRANSACTION_OUTPUT_SCRIPT_POST[] = { - 0x88, 0xAC}; // OP_EQUALVERIFY, OP_CHECKSIG - -const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { - 0x17, 0xA9, 0x14}; // script length, OP_HASH160, address length -const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = {0x87}; // OP_EQUAL - -const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { - 0x3D, 0xA9, - 0x14}; // script length, OP_HASH160, address length - -const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = { - 0x87, // OP_EQUAL - 0x20, 0x9E, 0xC9, 0x84, 0x5A, 0xCB, 0x02, 0xFA, 0XB2, 0X4E, - 0x1C, 0x03, 0x68, 0xB3, 0xB5, 0x17, 0xC1, 0xA4, 0x48, 0x8F, - 0xBA, 0x97, 0xF0, 0xE3, 0x45, 0x9A, 0xC0, 0x53, 0xEA, 0x01, - 0x00, 0x00, 0x00, // ParamHash - 0x03, // Push 3 bytes to stack to make ParamHeight line up properly - 0xC0, 0x1F, 0x02, // ParamHeight (139200) -> hex -> endianness swapped - 0xB4}; // OP_CHECKBLOCKATHEIGHT - -const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE[] = {0x16, 0x00, 0x14}; -const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE[] = {0x22, 0x00, 0x20}; - -const unsigned char ZEN_OUTPUT_SCRIPT_PRE[] = { - 0x3F, 0x76, 0xA9, - 0x14}; // script length, OP_DUP, OP_HASH160, address length -const unsigned char ZEN_OUTPUT_SCRIPT_POST[] = { - 0x88, 0xAC, // OP_EQUALVERIFY, OP_CHECKSIG - 0x20, 0x9e, 0xc9, 0x84, 0x5a, 0xcb, 0x02, 0xfa, 0xb2, 0x4e, 0x1c, 0x03, - 0x68, 0xb3, 0xb5, 0x17, 0xc1, 0xa4, 0x48, 0x8f, 0xba, 0x97, 0xf0, 0xe3, - 0x45, 0x9a, 0xc0, 0x53, 0xea, 0x01, 0x00, 0x00, 0x00, // ParamHash - 0x03, // Push 3 bytes to stack to make ParamHeight line up properly - 0xc0, 0x1f, 0x02, // ParamHeight (139200) -> hex -> endianness swapped - 0xb4 // OP_CHECKBLOCKATHEIGHT -}; // BIP0115 Replay Protection - -unsigned char output_script_is_regular(unsigned char *buffer) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || - (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { - return 1; - } - } - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) && - (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, - TRANSACTION_OUTPUT_SCRIPT_POST, - sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) { - return 1; - } - if (COIN_KIND == COIN_KIND_HORIZEN) { - if ((memcmp(buffer, ZEN_OUTPUT_SCRIPT_PRE, - sizeof(ZEN_OUTPUT_SCRIPT_PRE)) == 0) && - (memcmp(buffer + sizeof(ZEN_OUTPUT_SCRIPT_PRE) + 20, - ZEN_OUTPUT_SCRIPT_POST, - sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) { - return 1; - } - } - - return 0; -} - -unsigned char output_script_is_p2sh(unsigned char *buffer) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && - (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, - TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { - return 1; - } - if (COIN_KIND == COIN_KIND_HORIZEN) { - if ((memcmp(buffer, ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, - sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && - (memcmp(buffer + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, - ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, - sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { - return 1; - } - } - return 0; -} - -unsigned char output_script_is_native_witness(unsigned char *buffer) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || - (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { - return 1; - } - } - return 0; -} - -unsigned char output_script_is_op_return(unsigned char *buffer) { - if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { - return ((buffer[1] == 0x6A) || ((buffer[1] == 0x00) && (buffer[2] == 0x6A))); - } - else { - return (buffer[1] == 0x6A); - } -} - -static unsigned char output_script_is_op_create_or_call(unsigned char *buffer, - size_t size, - unsigned char value) { - return (!output_script_is_regular(buffer) && - !output_script_is_p2sh(buffer) && - !output_script_is_op_return(buffer) && (buffer[0] <= 0xEA) && - (buffer[0] < size) && - (buffer[buffer[0]] == value)); -} - -unsigned char output_script_is_op_create(unsigned char *buffer, - size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC1); -} - -unsigned char output_script_is_op_call(unsigned char *buffer, - size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC2); -} - -void public_key_hash160(unsigned char *in, unsigned short inlen, - unsigned char *out) { - cx_ripemd160_t riprip; - unsigned char buffer[32]; - cx_hash_sha256(in, inlen, buffer, 32); - cx_ripemd160_init(&riprip); - LEDGER_ASSERT(cx_hash_no_throw(&riprip.header, CX_LAST, buffer, 32, out, 20) == CX_OK, "hash160"); -} - -void compute_checksum(unsigned char* in, unsigned short inlen, unsigned char * output) { - unsigned char checksumBuffer[32]; - cx_hash_sha256(in, inlen, checksumBuffer, 32); - cx_hash_sha256(checksumBuffer, 32, checksumBuffer, 32); - - PRINTF("Checksum\n%.*H\n",4,checksumBuffer); - memmove(output, checksumBuffer, 4); -} - -unsigned short public_key_to_encoded_base58( - unsigned char *in, unsigned short inlen, unsigned char *out, - unsigned short outlen, unsigned short version, - unsigned char alreadyHashed) { - unsigned char tmpBuffer[34]; - - unsigned char versionSize = (version > 255 ? 2 : 1); - size_t outputLen; - - if (!alreadyHashed) { - PRINTF("To hash\n%.*H\n",inlen,in); - public_key_hash160(in, inlen, tmpBuffer + versionSize); - PRINTF("Hash160\n%.*H\n",20,(tmpBuffer + versionSize)); - if (version > 255) { - tmpBuffer[0] = (version >> 8); - tmpBuffer[1] = version; - } else { - tmpBuffer[0] = version; - } - } else { - memmove(tmpBuffer, in, 20 + versionSize); - } - - compute_checksum(tmpBuffer, 20 + versionSize, tmpBuffer + 20 + versionSize); - - outputLen = base58_encode(tmpBuffer, 24 + versionSize, (char *)out, outlen); - if (outputLen < 0) { - THROW(EXCEPTION); - } - return outputLen; -} - -void swap_bytes(unsigned char *target, unsigned char *source, - unsigned char size) { - unsigned char i; - for (i = 0; i < size; i++) { - target[i] = source[size - 1 - i]; - } -} - -/* -Checks if the values of a derivation path are within "normal" (arbitrary) ranges: -Account < 100, change == 1 or 0, address index < 50000 -Returns 1 if the path is unusual, or not compliant with BIP44*/ -unsigned char bip44_derivation_guard(unsigned char *bip32Path, bool is_change_path) { - - unsigned char path_len; - bip32_path_t bip32PathInt; - - path_len = bip32Path[0]; - if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { - return 1; - } - - // If the path length is not compliant with BIP44 or if the purpose don't match regular usage, return a warning - if(path_len != BIP44_PATH_LEN || - ((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 44 && - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 49 && - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 84)) { - return 1; - } - - // If the coin type doesn't match, return a warning - if ((BIP44_COIN_TYPE != 0) && - (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE) && - ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE_2))) { - return 1; - } - - // If the account or address index is very high or if the change isn't 1, return a warning - if((bip32PathInt.path[BIP44_ACCOUNT_OFFSET]^0x80000000) > MAX_BIP44_ACCOUNT_RECOMMENDED || - bip32PathInt.path[BIP44_CHANGE_OFFSET] != is_change_path?1:0 || - bip32PathInt.path[BIP44_ADDRESS_INDEX_OFFSET] > MAX_BIP44_ADDRESS_INDEX_RECOMMENDED) { - return 1; - } - - return 0; -} - -/* -Only enforce the structure or coin type for consumed UTXOs or a public address -Returns 0 if the path is non compliant, or 1 if compliant -*/ -unsigned char enforce_bip44_coin_type(unsigned char *bip32Path, bool for_pubkey) { - bip32_path_t bip32PathInt; - // No enforcement required - if (BIP44_COIN_TYPE == 0) { - return 1; - } - // Path is too short - always require a user validation if signing - if (bip32Path[0] < 2) { - return for_pubkey; - } - - if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { - return 1; - } - - // Path is not compliant with BIP 44 or derivatives - valid if not signing - if (!(((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 44 || - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 49 || - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 84))) { - return for_pubkey; - } - - if (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE) || - ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE_2)) { - // Valid BIP 44 path - return 1; - } - // Everything else needs a user validation - return 0; -} - -// Print a BIP32 path as an ascii string to display on the device screen -// On the Ledger Blue, if the string is longer than 30 char, the string will be split in multiple lines -unsigned char bip32_print_path(unsigned char *bip32Path, char* out, unsigned char max_out_len) { - - unsigned char bip32PathLength; - unsigned char i, offset; - unsigned int current_level; - bool hardened; - - bip32PathLength = bip32Path[0]; - if (bip32PathLength > MAX_BIP32_PATH) { - THROW(INVALID_PARAMETER); - } - bip32Path++; - out[0] = ' '; - offset=1; - for (i = 0; i < bip32PathLength; i++) { - current_level = read_u32_be(bip32Path, 0); - hardened = (bool)(current_level & 0x80000000); - if(hardened) { - //remove hardening flag - current_level ^= 0x80000000; - } - bip32Path += 4; - snprintf(out+offset, max_out_len-offset, "%u", current_level); - offset = strnlen(out, max_out_len); - if(offset >= max_out_len - 2) THROW(EXCEPTION_OVERFLOW); - if(hardened) out[offset++] = '\''; - - out[offset++] = '/'; - out[offset] = '\0'; - } - // remove last '/' - out[offset-1] = '\0'; - - return offset -1; -} - -void transaction_add_output(unsigned char *hash160Address, - unsigned char *amount, unsigned char p2sh) { - const unsigned char *pre = (p2sh ? TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE - : TRANSACTION_OUTPUT_SCRIPT_PRE); - const unsigned char *post = (p2sh ? TRANSACTION_OUTPUT_SCRIPT_P2SH_POST - : TRANSACTION_OUTPUT_SCRIPT_POST); - unsigned char sizePre = (p2sh ? sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) - : sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)); - unsigned char sizePost = (p2sh ? sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST) - : sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)); - if (amount != NULL) { - swap_bytes(context_D.tmp, amount, 8); - context_D.tmp += 8; - } - memmove(context_D.tmp, (void *)pre, sizePre); - context_D.tmp += sizePre; - memmove(context_D.tmp, hash160Address, 20); - context_D.tmp += 20; - memmove(context_D.tmp, (void *)post, sizePost); - context_D.tmp += sizePost; -} - - -int sign_finalhash(unsigned char* path, size_t path_len, unsigned char *in, unsigned short inlen, - unsigned char *out, size_t* outlen, - unsigned char rfc6979) { - - unsigned int info = 0; - - io_seproxyhal_io_heartbeat(); - - bip32_path_t bip32Path; - bip32Path.length = path[0]; - - if (!parse_serialized_path(&bip32Path, path, path_len)) { - return -1; - } - - if (bip32_derive_ecdsa_sign_hash_256( - CX_CURVE_SECP256K1, - bip32Path.path, - bip32Path.length, - CX_LAST | (rfc6979 ? CX_RND_RFC6979 : CX_RND_TRNG), - CX_SHA256, - in, - inlen, - out, - outlen, - &info) != CX_OK) { - return -1; - } - - // Store information about the parity of the 'y' coordinate - if (info & CX_ECCINFO_PARITY_ODD) { - out[0] |= 0x01; - } - - io_seproxyhal_io_heartbeat(); - return 0; -} - -int get_public_key(unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode) { - - bip32_path_t bip32Path; - - if (!parse_serialized_path(&bip32Path, keyPath, keyPath_len)) { - return -1; - } - - if (bip32_derive_get_pubkey_256( - CX_CURVE_SECP256K1, - bip32Path.path, - bip32Path.length, - raw_pubkey, - chainCode, - CX_SHA512) != CX_OK) - { - return -1; - } - - return 0; -} diff --git a/src/internal.h b/src/internal.h deleted file mode 100644 index 1b84471e..00000000 --- a/src/internal.h +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "btchip.h" -#include "public_ram_variables.h" -#include "rom_variables.h" -#include "filesystem.h" -#include "bcd.h" -#include "ecc.h" -#include "helpers.h" -#include "transaction.h" - -#endif diff --git a/src/nvram.c b/src/nvram.c deleted file mode 100644 index a956708f..00000000 --- a/src/nvram.c +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" - -#include "public_ram_variables.h" - -storage_t const N_real; diff --git a/src/public_ram_variables.c b/src/public_ram_variables.c deleted file mode 100644 index a0acc359..00000000 --- a/src/public_ram_variables.c +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "public_ram_variables.h" - -context_t context_D; - diff --git a/src/rom_variables.c b/src/rom_variables.c deleted file mode 100644 index 375e3d86..00000000 --- a/src/rom_variables.c +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include "internal.h" -#include "apdu_constants.h" - -unsigned char const SIGNMAGIC[] = {' ', 'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M', - 'e', 's', 's', 'a', 'g', 'e', ':', '\n'}; - -unsigned char const OVERWINTER_PARAM_PREVOUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'P', 'r', 'e', 'v', 'o', 'u', 't', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_SEQUENCE[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'e', 'q', 'u', 'e', 'n', 'c', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_OUTPUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'O', 'u', 't', 'p', 'u', 't', 's', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_SIGHASH[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'i', 'g', 'H', 'a', 's', 'h', 0, 0, 0, 0 }; -unsigned char const OVERWINTER_NO_JOINSPLITS[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - -unsigned char const DISPATCHER_CLA[] = { - CLA, // apdu_get_wallet_public_key, - CLA, // apdu_get_trusted_input, - CLA, // apdu_hash_input_start, - CLA, // apdu_hash_sign, - CLA, // apdu_hash_input_finalize_full, - CLA, // apdu_sign_message, - CLA, // apdu_get_firmware_version, - CLA, // apdu_get_coin_version -}; - -unsigned char const DISPATCHER_INS[] = { - INS_GET_WALLET_PUBLIC_KEY, // apdu_get_wallet_public_key, - INS_GET_TRUSTED_INPUT, // apdu_get_trusted_input, - INS_HASH_INPUT_START, // apdu_hash_input_start, - INS_HASH_SIGN, // apdu_hash_sign, - INS_HASH_INPUT_FINALIZE_FULL, // apdu_hash_input_finalize_full, - INS_SIGN_MESSAGE, // apdu_sign_message, - INS_GET_FIRMWARE_VERSION, // apdu_get_firmware_version, - INS_GET_COIN_VER, // apdu_get_coin_version -}; - -unsigned char const DISPATCHER_DATA_IN[] = { - 1, // apdu_get_wallet_public_key, - 1, // apdu_get_trusted_input, - 1, // apdu_hash_input_start, - 1, // apdu_hash_sign, - 1, // apdu_hash_input_finalize_full, - 1, // apdu_sign_message, - 0, // apdu_get_firmware_version, - 0, // apdu_get_coin_version -}; - -apduProcessingFunction const DISPATCHER_FUNCTIONS[] = { - apdu_get_wallet_public_key, - apdu_get_trusted_input, - apdu_hash_input_start, - apdu_hash_sign, - apdu_hash_input_finalize_full, - apdu_sign_message, - apdu_get_firmware_version, - apdu_get_coin_version, -}; diff --git a/src/rom_variables.h b/src/rom_variables.h deleted file mode 100644 index 82d5fa89..00000000 --- a/src/rom_variables.h +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* -* Ledger App - Bitcoin Wallet -* (c) 2016-2019 Ledger -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#ifndef _ROM_VARIABLES_ -#define _ROM_VARIABLES_ - -#include "internal.h" - -#define SIGNMAGIC_LENGTH 17 - -extern unsigned char const SIGNMAGIC[SIGNMAGIC_LENGTH]; - -extern unsigned char const OVERWINTER_PARAM_PREVOUTS[16]; -extern unsigned char const OVERWINTER_PARAM_SEQUENCE[16]; -extern unsigned char const OVERWINTER_PARAM_OUTPUTS[16]; -extern unsigned char const OVERWINTER_PARAM_SIGHASH[16]; -extern unsigned char const OVERWINTER_NO_JOINSPLITS[32]; - -#define HDKEY_VERSION_LENGTH 4 - -#define APDU_DEBUG_LENGTH 0 - -#define APDU_NFCPAYMENT_LENGTH 0 - -#define APDU_BIP70_LENGTH 0 - -#define APDU_MOFN_LENGTH 0 - -#define APDU_KEYCARD_LENGTH 0 - -#define APDU_PORTABLE_LENGTH 5 - -#define APDU_KEYBOARD_LENGTH 0 - -#define APDU_LEGACY_SETUP_LENGTH 0 - -#define APDU_DEVELOPER_MODE_LENGTH 0 - -#define APDU_BASE_LENGTH 13 - -#define DISPATCHER_APDUS 13 - -typedef unsigned short (*apduProcessingFunction)(void); - -extern unsigned char const DISPATCHER_CLA[DISPATCHER_APDUS]; -extern unsigned char const DISPATCHER_INS[DISPATCHER_APDUS]; -extern unsigned char const DISPATCHER_DATA_IN[DISPATCHER_APDUS]; -extern apduProcessingFunction const DISPATCHER_FUNCTIONS[DISPATCHER_APDUS]; - -#endif /* _ROM_VARIABLES_ */ \ No newline at end of file diff --git a/src/swap/handle_get_printable_amount.c b/src/swap/handle_get_printable_amount.c deleted file mode 100644 index 8f9d8fd7..00000000 --- a/src/swap/handle_get_printable_amount.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "handle_get_printable_amount.h" -#include "bcd.h" -#include - -void swap_handle_get_printable_amount(get_printable_amount_parameters_t* params) { - params->printable_amount[0] = 0; - if (params->amount_length > 8) { - PRINTF("Amount is too big"); - return; - } - unsigned char amount[8]; - memset(amount, 0, 8); - memcpy(amount + (8 - params->amount_length), params->amount, params->amount_length); - unsigned char coin_name_length = strlen(COIN_COINID_SHORT); - memmove(params->printable_amount, COIN_COINID_SHORT, coin_name_length); - params->printable_amount[coin_name_length] = ' '; - int res_length = convert_hex_amount_to_displayable_no_globals(amount, COIN_FLAGS, (uint8_t *)params->printable_amount + coin_name_length + 1); - params->printable_amount[res_length + coin_name_length + 1] = '\0'; - - return; -} \ No newline at end of file