diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 3647c3fd4..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,65 +0,0 @@ -version: 2.1 -jobs: - emulator-build-test: - docker: - - image: circleci/python:3.7 - steps: - - checkout - - run: - name: Update Submodules - command: | - git submodule update --init --recursive - - setup_remote_docker - - run: - name: Build Emulator and Run Tests - command: | - pushd ./scripts/emulator - set +e - docker-compose up --build firmware-unit - docker-compose up --build python-keepkey - set -e - mkdir -p ../../test-reports - docker cp "$(docker-compose ps -q firmware-unit)":/kkemu/test-reports/. ../../test-reports/ - docker cp "$(docker-compose ps -q python-keepkey)":/kkemu/test-reports/. ../../test-reports/ - popd - [ "$(cat test-reports/python-keepkey/status)$(cat test-reports/firmware-unit/status)" == "00" ] || exit 1 - - store_test_results: - path: test-reports - - emulator-publish: - docker: - - image: circleci/python:3.7 - steps: - - checkout - - run: - name: Update Submodules - command: | - git submodule update --init --recursive - - setup_remote_docker - - run: - name: Build Emulator - command: | - pushd ./scripts/emulator - docker-compose build kkemu - popd - - run: - name: Publish Emulator - command: | - docker login -u $KK_DOCKERHUB_USER -p $KK_DOCKERHUB_PASS - docker push kktech/kkemu - LATEST_IMAGE_ID=$(docker images | grep -i kktech/kkemu | grep -i latest | awk '{print $3}' ) - FW_VERSION=$(cat ./CMakeLists.txt | tr -d '\n' | awk -v FS="(KeepKeyFirmwareVERSION|LANGUAGES)" '{print $1}' | awk '{print $4}') - docker tag $LATEST_IMAGE_ID kktech/kkemu:v$FW_VERSION - docker push kktech/kkemu - -workflows: - version: 2 - emulator: - jobs: - - emulator-build-test - - emulator-publish: - filters: - branches: - only: master - requires: - - emulator-build-test diff --git a/.cppcheck-suppressions b/.cppcheck-suppressions new file mode 100644 index 000000000..755955245 --- /dev/null +++ b/.cppcheck-suppressions @@ -0,0 +1,20 @@ +missingIncludeSystem +unknownMacro +syntaxError +preprocessorErrorDirective:include/pb.h +returnDanglingLifetime:lib/firmware/tiny-json.c +memleakOnRealloc:lib/transport/pb_decode.c +uninitvar:lib/board/messages.c +bufferAccessOutOfBounds:lib/firmware/storage.c +subtractPointers:tools/blupdater/main.c +comparePointers:tools/blupdater/main.c +ctunullpointer + +# Third-party / library code (not our code to fix) +nullPointerRedundantCheck:include/keepkey/firmware/tiny-json.h +unreadVariable:lib/firmware/tiny-json.c +*:lib/transport/pb_decode.c +*:lib/transport/pb_encode.c + +# Callback function signatures — changing const would break callback typedefs +constParameterCallback diff --git a/.github/GIT-FLOW.md b/.github/GIT-FLOW.md new file mode 100644 index 000000000..5ca5668a8 --- /dev/null +++ b/.github/GIT-FLOW.md @@ -0,0 +1,26 @@ +# Git-Flow Branching Model + +| Branch | Purpose | Created from | Merges to | +|--------|---------|-------------|-----------| +| `master` | Production releases (tagged) | — | — | +| `develop` | Integration branch | — | — | +| `feature/*` | New features | `develop` | `develop` | +| `release/*` | Release prep (version bump, final fixes) | `develop` | `master` + `develop` | +| `hotfix/*` | Critical production fixes | `master` | `master` + `develop` | +| `fix/*` | Non-critical bug fixes | `develop` | `develop` | + +## Release Process + +```bash +git checkout -b release/v7.11.0 develop +# bump CMakeLists.txt VERSION, push, CI must pass +# PR to master → merge → tag +git tag v7.11.0 && git push origin v7.11.0 +# → release.yml builds firmware + creates draft GitHub Release +# merge release branch back to develop +``` + +## CI/CD + +- **ci.yml**: push to master, develop, feature/*, fix/*, release/*, hotfix/* +- **release.yml**: `v*` tag → build + hash + draft GitHub Release diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..2f358ddc5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,571 @@ +# KeepKey Firmware CI Pipeline +# +# FAIL FAST — quick checks gate everything, no wasted compute. +# +# Stage 1: GATE (seconds, no Docker) +# ├─ lint-format clang-format diff check +# ├─ static-analysis cppcheck static analysis +# ├─ secret-scan gitleaks credential detection +# └─ check-submodules verify all deps present +# +# Stage 2: BUILD (parallel, gated by Stage 1) +# ├─ build-emulator Docker image → artifact +# └─ build-arm-firmware cross-compile → .bin/.elf (downloadable) +# +# Stage 3: TEST (parallel, gated by Stage 2) +# ├─ unit-tests GoogleTest (make xunit) +# └─ python-integration full test suite +# +# Stage 4: PUBLISH (manual trigger, all tests must pass) +# └─ publish-emulator DockerHub push (workflow_dispatch only) + +name: CI + +on: + push: + branches: [master, develop, 'feature/**', 'fix/**', 'release/**', 'hotfix/**'] + pull_request: + branches: [master, develop] + workflow_dispatch: + inputs: + publish_emulator: + description: 'Publish emulator image to DockerHub' + required: false + type: boolean + default: false + +env: + BASE_IMAGE: kktech/firmware:v15 + EMU_IMAGE: kkemu-ci + +jobs: + # ═══════════════════════════════════════════════════════════ + # STAGE 1: GATE — kill bad PRs in seconds + # ═══════════════════════════════════════════════════════════ + + lint-format: + runs-on: ubuntu-latest + timeout-minutes: 3 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Install clang-format-20 (pinned stable) + run: | + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" | sudo tee /etc/apt/sources.list.d/llvm.list + sudo apt-get update -qq && sudo apt-get install -y -qq clang-format-20 + sudo ln -sf /usr/bin/clang-format-20 /usr/bin/clang-format + + - name: Check code formatting + run: | + echo "Using: $(clang-format --version)" + FAILED=0 + for f in $(find include/keepkey lib/firmware lib/board lib/transport/src \ + -name '*.c' -o -name '*.h' 2>/dev/null | grep -v generated | grep -v '.pb.'); do + if ! clang-format --style=file --dry-run --Werror "$f" 2>/dev/null; then + echo "::warning file=$f::Formatting differs from .clang-format" + FAILED=1 + fi + done + if [ "$FAILED" = "1" ]; then + echo "" + echo "::error::Code formatting check failed. Run: clang-format -i to fix." + exit 1 + fi + + secret-scan: + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + + - name: Install gitleaks + run: | + GITLEAKS_VERSION=$(curl -sSf https://api.github.com/repos/gitleaks/gitleaks/releases/latest \ + | grep -oP '"tag_name":\s*"v\K[^"]+') + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xz -C /usr/local/bin gitleaks + gitleaks version + + - name: Run gitleaks + run: gitleaks detect --source . --verbose --redact + + static-analysis: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + submodules: false + + - name: Install cppcheck + run: sudo apt-get update && sudo apt-get install -y cppcheck + + - name: Run cppcheck + run: | + cppcheck \ + --enable=warning,style,performance,portability \ + --std=c11 \ + --platform=unspecified \ + --inconclusive \ + --force \ + --inline-suppr \ + --suppressions-list=.cppcheck-suppressions \ + -I include \ + -I deps/crypto/trezor-firmware/crypto \ + -I deps/device-protocol \ + -DSTM32F2=1 \ + -DUSE_ETHEREUM=1 \ + -DUSE_KECCAK=1 \ + -DUSE_NANO=1 \ + -DPB_FIELD_16BIT=1 \ + -DEMULATOR=1 \ + --template='::warning file={file},line={line},col={column}::{severity}: {message} [{id}]' \ + --output-file=cppcheck_report.txt \ + --error-exitcode=1 \ + lib/ include/keepkey/ tools/ 2>&1; CPPCHECK_RC=$? + + # Count issues by severity + ERRORS=$(grep -c '\berror:' cppcheck_report.txt 2>/dev/null || true) + WARNINGS=$(grep -c '\bwarning:' cppcheck_report.txt 2>/dev/null || true) + STYLE=$(grep -c '\bstyle:' cppcheck_report.txt 2>/dev/null || true) + PERF=$(grep -c '\bperformance:' cppcheck_report.txt 2>/dev/null || true) + PORT=$(grep -c '\bportability:' cppcheck_report.txt 2>/dev/null || true) + : "${ERRORS:=0}" "${WARNINGS:=0}" "${STYLE:=0}" "${PERF:=0}" "${PORT:=0}" + TOTAL=$((ERRORS + WARNINGS + STYLE + PERF + PORT)) + + echo "## cppcheck summary" >> "$GITHUB_STEP_SUMMARY" + echo "| Severity | Count |" >> "$GITHUB_STEP_SUMMARY" + echo "|----------|-------|" >> "$GITHUB_STEP_SUMMARY" + echo "| error | $ERRORS |" >> "$GITHUB_STEP_SUMMARY" + echo "| warning | $WARNINGS |" >> "$GITHUB_STEP_SUMMARY" + echo "| style | $STYLE |" >> "$GITHUB_STEP_SUMMARY" + echo "| performance | $PERF |" >> "$GITHUB_STEP_SUMMARY" + echo "| portability | $PORT |" >> "$GITHUB_STEP_SUMMARY" + echo "| **total** | **$TOTAL** |" >> "$GITHUB_STEP_SUMMARY" + + # Print findings as GitHub annotations + cat cppcheck_report.txt + + if [ "$CPPCHECK_RC" -ne 0 ]; then + echo "::error::cppcheck exited with error code $CPPCHECK_RC" + exit 1 + fi + + if [ "$TOTAL" -gt 0 ]; then + echo "::error::cppcheck found $TOTAL issue(s) — zero-warning policy enforced" + exit 1 + fi + + echo "cppcheck: clean — zero findings" + + - name: Upload cppcheck report + uses: actions/upload-artifact@v7 + if: always() + with: + name: cppcheck-report + path: cppcheck_report.txt + retention-days: 30 + + check-submodules: + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Verify submodules are declared + run: | + echo "Checking required submodule declarations..." + REQUIRED=( + "deps/crypto/trezor-firmware" + "deps/device-protocol" + "deps/python-keepkey" + "deps/googletest" + "deps/qrenc/QR-Code-generator" + "deps/sca-hardening/SecAESSTM32" + ) + FAILED=0 + for sub in "${REQUIRED[@]}"; do + if ! grep -q "$sub" .gitmodules; then + echo "::error::Missing required submodule: $sub" + FAILED=1 + else + echo " ✓ $sub" + fi + done + [ "$FAILED" = "0" ] || exit 1 + + # ═══════════════════════════════════════════════════════════ + # STAGE 2: BUILD — compile only after gate passes + # ═══════════════════════════════════════════════════════════ + + build-emulator: + needs: [lint-format, static-analysis, check-submodules, secret-scan] + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Init submodules + run: | + git submodule update --init deps/crypto/trezor-firmware + git submodule update --init deps/device-protocol + git submodule update --init --recursive deps/python-keepkey + git submodule update --init deps/googletest + git submodule update --init deps/qrenc/QR-Code-generator + git submodule update --init deps/sca-hardening/SecAESSTM32 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Cache base image + id: cache-base + uses: actions/cache@v5 + with: + path: /tmp/base-image.tar + key: base-image-${{ env.BASE_IMAGE }} + + - name: Pull and cache base image + if: steps.cache-base.outputs.cache-hit != 'true' + run: | + docker pull ${{ env.BASE_IMAGE }} + docker save ${{ env.BASE_IMAGE }} -o /tmp/base-image.tar + + - name: Load base image from cache + if: steps.cache-base.outputs.cache-hit == 'true' + run: docker load -i /tmp/base-image.tar + + - name: Build emulator image + run: | + docker build \ + -t ${{ env.EMU_IMAGE }} \ + -f scripts/emulator/Dockerfile \ + . + + - name: Save emulator image + run: docker save ${{ env.EMU_IMAGE }} -o /tmp/emu-image.tar + + - name: Upload emulator image artifact + uses: actions/upload-artifact@v7 + with: + name: emu-image + path: /tmp/emu-image.tar + retention-days: 1 + + build-arm-firmware: + needs: [lint-format, static-analysis, check-submodules, secret-scan] + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Init submodules + run: | + git submodule update --init deps/crypto/trezor-firmware + git submodule update --init deps/device-protocol + git submodule update --init --recursive deps/python-keepkey + git submodule update --init deps/googletest + git submodule update --init deps/qrenc/QR-Code-generator + git submodule update --init deps/sca-hardening/SecAESSTM32 + + - name: Cache base image + id: cache-base + uses: actions/cache@v5 + with: + path: /tmp/base-image.tar + key: base-image-${{ env.BASE_IMAGE }} + + - name: Pull and cache base image + if: steps.cache-base.outputs.cache-hit != 'true' + run: | + docker pull ${{ env.BASE_IMAGE }} + docker save ${{ env.BASE_IMAGE }} -o /tmp/base-image.tar + + - name: Load base image from cache + if: steps.cache-base.outputs.cache-hit == 'true' + run: docker load -i /tmp/base-image.tar + + - name: Extract firmware version + id: version + run: | + FW_VERSION=$(sed -n '/^project/,/)/p' CMakeLists.txt | grep -oP '\d+\.\d+\.\d+') + GIT_SHORT=$(git rev-parse --short HEAD) + echo "fw_version=${FW_VERSION}" >> "$GITHUB_OUTPUT" + echo "git_short=${GIT_SHORT}" >> "$GITHUB_OUTPUT" + echo "Firmware version: ${FW_VERSION} (${GIT_SHORT})" + + - name: Cross-compile firmware for ARM + run: | + docker run --rm \ + -v ${{ github.workspace }}:/root/keepkey-firmware:z \ + ${{ env.BASE_IMAGE }} /bin/sh -c "\ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_COLOR_MAKEFILE=ON && \ + make && \ + mkdir -p /root/keepkey-firmware/bin && \ + cp bin/*.bin /root/keepkey-firmware/bin/ && \ + cp bin/*.elf /root/keepkey-firmware/bin/ && \ + chmod -R a+rw /root/keepkey-firmware/bin" + + - name: Rename firmware artifacts + run: | + cd bin + for f in *.bin; do + [ -f "$f" ] || continue + mv "$f" "firmware.keepkey.v${{ steps.version.outputs.fw_version }}-${{ steps.version.outputs.git_short }}-${f}" + done + for f in *.elf; do + [ -f "$f" ] || continue + mv "$f" "firmware.keepkey.v${{ steps.version.outputs.fw_version }}-${{ steps.version.outputs.git_short }}-${f}" + done + ls -lh + echo "::notice::Firmware v${{ steps.version.outputs.fw_version }} built successfully" + + - name: Upload firmware artifacts + uses: actions/upload-artifact@v7 + with: + name: firmware-v${{ steps.version.outputs.fw_version }}-${{ steps.version.outputs.git_short }} + path: | + bin/*.bin + bin/*.elf + retention-days: 90 + + # ═══════════════════════════════════════════════════════════ + # STAGE 3: TEST — run only after builds succeed + # ═══════════════════════════════════════════════════════════ + + unit-tests: + needs: build-emulator + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Download emulator image + uses: actions/download-artifact@v8 + with: + name: emu-image + path: /tmp + + - name: Load emulator image + run: docker load -i /tmp/emu-image.tar + + - name: Run unit tests + run: | + # make xunit returns non-zero if any test fails — capture + # exit code so JUnit XML still gets copied for reporting + docker run --rm \ + -v ${{ github.workspace }}/test-reports:/kkemu/test-reports \ + --entrypoint /bin/sh \ + ${{ env.EMU_IMAGE }} \ + -c "mkdir -p /kkemu/test-reports/firmware-unit && \ + make xunit; RC=\$?; \ + cp -r unittests/*.xml /kkemu/test-reports/firmware-unit/ 2>/dev/null; \ + exit \$RC" + + - name: Upload unit test results + uses: actions/upload-artifact@v7 + if: always() + with: + name: unit-test-results + path: test-reports/firmware-unit/ + retention-days: 30 + + python-integration-tests: + needs: [lint-format, static-analysis, check-submodules, secret-scan] + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Init submodules + run: | + git submodule update --init deps/crypto/trezor-firmware + git submodule update --init deps/device-protocol + git submodule update --init --recursive deps/python-keepkey + git submodule update --init deps/googletest + git submodule update --init deps/qrenc/QR-Code-generator + git submodule update --init deps/sca-hardening/SecAESSTM32 + + - name: Build and run tests (docker compose) + working-directory: scripts/emulator + run: | + # Run each test container — capture exit codes, always extract reports + docker compose up --build --exit-code-from firmware-unit firmware-unit; FW_RC=$? + docker compose up --build --exit-code-from python-keepkey python-keepkey; PY_RC=$? + + mkdir -p ${{ github.workspace }}/test-reports + + echo "=== Extracting test reports from Docker ===" + PY_CONTAINER=$(docker compose ps -a -q python-keepkey) + FW_CONTAINER=$(docker compose ps -a -q firmware-unit) + + docker cp "$FW_CONTAINER":/kkemu/test-reports/. ${{ github.workspace }}/test-reports/ || echo "WARN: firmware-unit docker cp failed" + docker cp "$PY_CONTAINER":/kkemu/test-reports/. ${{ github.workspace }}/test-reports/ || echo "WARN: python-keepkey docker cp failed" + + echo "=== Extracted files ===" + find ${{ github.workspace }}/test-reports -type f | head -30 + echo "=== Screenshot PNGs ===" + find ${{ github.workspace }}/test-reports/screenshots -name '*.png' 2>/dev/null | wc -l + echo "PNGs on host" + + echo "firmware-unit exit code: $FW_RC" + echo "python-keepkey exit code: $PY_RC" + + if [ "$FW_RC" -ne 0 ]; then + echo "::error::firmware-unit tests failed (exit code $FW_RC)" + fi + if [ "$PY_RC" -ne 0 ]; then + echo "::error::python-keepkey tests failed (exit code $PY_RC)" + fi + [ "$FW_RC" -eq 0 ] && [ "$PY_RC" -eq 0 ] || exit 1 + + - name: Upload Python test results + uses: actions/upload-artifact@v7 + if: always() + with: + name: python-test-results + path: test-reports/python-keepkey/ + retention-days: 30 + + - name: Upload OLED screenshots + uses: actions/upload-artifact@v4 + if: always() + with: + name: oled-screenshots + path: test-reports/screenshots/ + retention-days: 90 + if-no-files-found: warn + + - name: Tear down + if: always() + working-directory: scripts/emulator + run: docker compose down -v || true + + # ═══════════════════════════════════════════════════════════ + # STAGE 3b: TEST REPORT — generate PDF from test artifacts + # ═══════════════════════════════════════════════════════════ + + generate-test-report: + needs: [unit-tests, python-integration-tests] + if: always() + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Download unit test results + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: unit-test-results + path: test-reports/firmware-unit/ + + - name: Download python test results + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: python-test-results + path: test-reports/python-keepkey/ + + - name: Download OLED screenshots + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: oled-screenshots + path: test-reports/screenshots/ + + - name: Extract firmware version + id: version + run: | + FW_VERSION=$(sed -n '/^project/,/)/p' CMakeLists.txt | grep -oP '\d+\.\d+\.\d+' || echo "unknown") + echo "fw_version=${FW_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Init python-keepkey submodule + run: git submodule update --init deps/python-keepkey + + - name: Generate test report PDF + env: + FW_VERSION: ${{ steps.version.outputs.fw_version }} + run: python3 scripts/generate-test-report.py + + - name: Upload test report + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-report + path: test-report.pdf + retention-days: 90 + + # ═══════════════════════════════════════════════════════════ + # STAGE 4: PUBLISH — manual trigger only, all tests must pass + # ═══════════════════════════════════════════════════════════ + + publish-emulator: + needs: [unit-tests, python-integration-tests, build-arm-firmware] + if: >- + github.event_name == 'workflow_dispatch' && + github.event.inputs.publish_emulator == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Download emulator image + uses: actions/download-artifact@v8 + with: + name: emu-image + path: /tmp + + - name: Load emulator image + run: docker load -i /tmp/emu-image.tar + + - name: Extract firmware version + id: version + run: | + FW_VERSION=$(sed -n '/^project/,/)/p' CMakeLists.txt | grep -oP '\d+\.\d+\.\d+') + echo "fw_version=${FW_VERSION}" >> "$GITHUB_OUTPUT" + echo "Firmware version: ${FW_VERSION}" + + - name: Tag images for publish + run: | + docker tag ${{ env.EMU_IMAGE }} kktech/kkemu:latest + docker tag ${{ env.EMU_IMAGE }} kktech/kkemu:v${{ steps.version.outputs.fw_version }} + + - name: Login to DockerHub + uses: docker/login-action@v4 + with: + username: ${{ secrets.KK_DOCKERHUB_USER }} + password: ${{ secrets.KK_DOCKERHUB_PASS }} + + - name: Push emulator images + run: | + docker push kktech/kkemu:latest + docker push kktech/kkemu:v${{ steps.version.outputs.fw_version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..ee6c178c7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,204 @@ +# KeepKey Firmware Release Pipeline +# +# Triggered by version tags (v*) on master. +# Builds firmware, computes reproducible hashes, +# and creates a draft GitHub Release with all artifacts. +# +# Git-flow: tag master after merging a release/* or hotfix/* branch. +# +# Usage: +# git tag v7.11.0 +# git push origin v7.11.0 + +name: Release + +on: + push: + tags: + - 'v*' + +env: + BASE_IMAGE: kktech/firmware:v15 + +permissions: + contents: write + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 3 + outputs: + fw_version: ${{ steps.version.outputs.fw_version }} + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Extract and verify version + id: version + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + FW_VERSION=$(sed -n '/^project/,/)/p' CMakeLists.txt | grep -oP '\d+\.\d+\.\d+') + echo "fw_version=${FW_VERSION}" >> "$GITHUB_OUTPUT" + if [ "$TAG_VERSION" != "$FW_VERSION" ]; then + echo "::error::Tag (${TAG_VERSION}) != CMakeLists.txt (${FW_VERSION})" + exit 1 + fi + + build-firmware: + needs: validate + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Cache base image + id: cache-base + uses: actions/cache@v5 + with: + path: /tmp/base-image.tar + key: base-image-${{ env.BASE_IMAGE }} + + - name: Pull and cache base image + if: steps.cache-base.outputs.cache-hit != 'true' + run: | + docker pull ${{ env.BASE_IMAGE }} + docker save ${{ env.BASE_IMAGE }} -o /tmp/base-image.tar + + - name: Load base image from cache + if: steps.cache-base.outputs.cache-hit == 'true' + run: docker load -i /tmp/base-image.tar + + - name: Cross-compile firmware + run: | + docker run --rm \ + -v ${{ github.workspace }}:/root/keepkey-firmware:z \ + ${{ env.BASE_IMAGE }} /bin/sh -c "\ + mkdir /root/build && cd /root/build && \ + cmake -C /root/keepkey-firmware/cmake/caches/device.cmake /root/keepkey-firmware \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_COLOR_MAKEFILE=ON && \ + make && \ + mkdir -p /root/keepkey-firmware/release && \ + cp bin/firmware.keepkey.bin /root/keepkey-firmware/release/ && \ + cp bin/firmware.keepkey.elf /root/keepkey-firmware/release/ && \ + cp bin/bootloader.bin /root/keepkey-firmware/release/ 2>/dev/null || true && \ + chmod -R a+rw /root/keepkey-firmware/release" + + - name: Compute hashes + working-directory: release + run: | + echo "# KeepKey Firmware v${{ needs.validate.outputs.fw_version }} — Hash Manifest" > HASHES.txt + echo "" >> HASHES.txt + for f in *.bin; do + [ -f "$f" ] || continue + FULL_HASH=$(sha256sum "$f" | awk '{print $1}') + echo "sha256 (full) $f $FULL_HASH" >> HASHES.txt + FILE_SIZE=$(stat -c%s "$f") + if [ "$FILE_SIZE" -gt 256 ]; then + PAYLOAD_HASH=$(tail -c +257 "$f" | sha256sum | awk '{print $1}') + echo "sha256 (payload) $f $PAYLOAD_HASH" >> HASHES.txt + fi + echo "" >> HASHES.txt + done + cat HASHES.txt + + - name: Rename artifacts + working-directory: release + run: | + VER="${{ needs.validate.outputs.fw_version }}" + [ -f firmware.keepkey.bin ] && mv firmware.keepkey.bin "firmware.keepkey.v${VER}.bin" + [ -f firmware.keepkey.elf ] && mv firmware.keepkey.elf "firmware.keepkey.v${VER}.elf" + [ -f bootloader.bin ] && mv bootloader.bin "bootloader.v${VER}.bin" + ls -lh + + - name: Upload release artifacts + uses: actions/upload-artifact@v7 + with: + name: release-firmware + path: release/* + retention-days: 90 + + test: + needs: validate + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Cache base image + id: cache-base + uses: actions/cache@v5 + with: + path: /tmp/base-image.tar + key: base-image-${{ env.BASE_IMAGE }} + + - name: Pull and cache base image + if: steps.cache-base.outputs.cache-hit != 'true' + run: | + docker pull ${{ env.BASE_IMAGE }} + docker save ${{ env.BASE_IMAGE }} -o /tmp/base-image.tar + + - name: Load base image from cache + if: steps.cache-base.outputs.cache-hit == 'true' + run: docker load -i /tmp/base-image.tar + + - name: Build and test emulator + run: | + docker build -t kkemu-release -f scripts/emulator/Dockerfile . + docker run --rm --entrypoint /bin/sh kkemu-release \ + -c "make xunit; RC=\$?; exit \$RC" + + create-release: + needs: [validate, build-firmware, test] + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v6 + + - name: Download firmware artifacts + uses: actions/download-artifact@v8 + with: + name: release-firmware + path: artifacts + + - name: Prepare release assets + run: | + mkdir -p release-assets + cp artifacts/*.bin artifacts/*.elf artifacts/HASHES.txt release-assets/ + ls -lh release-assets/ + + - name: Generate release body + run: | + VER="${{ needs.validate.outputs.fw_version }}" + cat > release-body.md < **DRAFT** — firmware must be signed by 3/5 key holders before publishing. + + ### Signing Checklist + - [ ] Built on multiple machines, hashes match + - [ ] Signed on air-gapped machine (3/5 signers) + - [ ] Storage upgrade tested on production device + - [ ] Signed .bin uploaded, replacing unsigned + - [ ] Release notes finalized + EOF + + - name: Create draft release + uses: softprops/action-gh-release@v2 + with: + draft: true + name: "Firmware v${{ needs.validate.outputs.fw_version }}" + body_path: release-body.md + files: release-assets/* + fail_on_unmatched_files: true diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 000000000..72450417f --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1,15 @@ +# Gitleaks false positives — reviewed 2026-03-11 +# All are crypto example code, public keys, or test fixtures + +# U2F Trezor development key (shared/public dev key for testing) +fe3e5e001c8143cd2fe60c22c2cdbf983b4a460c:include/keepkey/firmware/u2f/trezordevkey.pem:private-key:1 + +# curve25519-donna README — example code showing pk/shared variables +8b9b1414da5c3617a767fba68e19bcd9e630497e:crypto/public/curve25519-donna/README.md:generic-api-key:82 +8b9b1414da5c3617a767fba68e19bcd9e630497e:crypto/public/curve25519-donna/README.md:generic-api-key:87 + +# Bootloader public signing keys (intentionally in source) +ae5c38605cc548dcce0c9d83e796eabe834956f4:bootloader/local/baremetal/bootloader_main.c:generic-api-key:64 + +# Test wallet data from 2014 +e257a3ad1745bc2dffb38eae0f40dc52ba7de4a6:keepkey_app/test/keepkey_wallet.dat:generic-api-key:7 diff --git a/.python-version b/.python-version new file mode 100644 index 000000000..8cc1b46f5 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.15 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d68c588a..7139272b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.7.2) project( KeepKeyFirmware - VERSION 7.10.0 + VERSION 7.14.0 LANGUAGES C CXX ASM) set(BOOTLOADER_MAJOR_VERSION 2) diff --git a/deps/device-protocol b/deps/device-protocol index 323802f17..bf8646b81 160000 --- a/deps/device-protocol +++ b/deps/device-protocol @@ -1 +1 @@ -Subproject commit 323802f17dd44165a5100357df771348c8b49672 +Subproject commit bf8646b817401d4623e0977ddb4c961b1101121f diff --git a/deps/python-keepkey b/deps/python-keepkey index f1dd2b684..62cc0d894 160000 --- a/deps/python-keepkey +++ b/deps/python-keepkey @@ -1 +1 @@ -Subproject commit f1dd2b6847346abe8ea2985b22d688de4911643f +Subproject commit 62cc0d894421df6f1686e66f90f242604625b16a diff --git a/docs/testing/TEST-SYSTEM.md b/docs/testing/TEST-SYSTEM.md new file mode 100644 index 000000000..08a550117 --- /dev/null +++ b/docs/testing/TEST-SYSTEM.md @@ -0,0 +1,405 @@ +# KeepKey Firmware Test System + +Single source of truth for the test suite, report generation, screenshot capture, and release validation pipeline. + +--- + +## Architecture + +``` +python-keepkey (release/7.14.0) +├── keepkeylib/ +│ ├── client.py # SCREENSHOT capture in callback_ButtonRequest +│ ├── debuglink.py # read_layout() → 2048-byte OLED buffer +│ └── messages_*_pb2.py # protobuf definitions per chain +├── tests/ +│ ├── common.py # KeepKeyTest base class + screenshot dir setup +│ ├── conftest.py # pytest plugin: per-test screenshot directories +│ ├── config.py # transport config (UDP host:port) +│ └── test_msg_*.py # test files (one per chain/feature) +└── scripts/ + └── generate-test-report.py # THE report generator (1061 lines, version-aware) +``` + +**Everything lives in python-keepkey.** The firmware repo consumes it as a submodule at `deps/python-keepkey/`. + +--- + +## Report Generator: Version-Gated Sections + +`scripts/generate-test-report.py` defines a `SECTIONS` array where each section has a `min_firmware_version`: + +```python +SECTIONS = [ + ('C', 'Core - Device Lifecycle', '7.0.0', ...), # always shown + ('B', 'Bitcoin', '7.0.0', ...), # always shown + ('E', 'Ethereum', '7.0.0', ...), # always shown + # ... existing chains ... + ('V', 'EVM Clear-Signing', '7.14.0', ...), # shown when fw >= 7.14.0 + ('S', 'Solana', '7.14.0', ...), # shown when fw >= 7.14.0 + ('T', 'TRON', '7.14.0', ...), # shown when fw >= 7.14.0 + ('N', 'TON', '7.14.0', ...), # shown when fw >= 7.14.0 + ('Z', 'Zcash Orchard', '7.14.0', ...), # shown when fw >= 7.14.0 + ('D', 'BIP-85 Child Derivation','7.14.0', ...), # shown when fw >= 7.14.0 +] +``` + +When the firmware version is 7.10.0 (branches before mega merge), only existing chain sections appear. When 7.14.0, new feature sections appear at the TOP of the report with `[NEW]` tags. + +Each test in a section maps to a specific `test_msg_*.py::TestClass::test_method`. The generator looks up the method name in JUnit XML results to determine pass/fail/pending. + +### Running the generator + +```bash +# Against live emulator (auto-detects version) +python3 scripts/generate-test-report.py --output=test-report.pdf + +# Against JUnit XML from CI +python3 scripts/generate-test-report.py \ + --fw-version=7.14.0 \ + --junit=test-reports/python-keepkey/junit.xml \ + --screenshots=test-reports/screenshots/ \ + --output=test-report.pdf +``` + +### Adding a new feature (future releases) + +1. Add a new section tuple to `SECTIONS` with the target firmware version +2. Add test entries mapping to test file methods +3. Push to `release/` on python-keepkey +4. The report automatically includes the section when firmware version matches + +--- + +## Screenshot Capture System + +### Why Two Phases + +Screenshot capture adds a `DebugLinkGetState` round-trip on every `ButtonRequest`. This: +- Adds ~50-100ms latency per button press +- Can cause timing issues in multi-step flows (recovery cipher, complex signing) + +**Phase 1**: Run screenshot-compatible tests with `KEEPKEY_SCREENSHOT=1`. These are simple request-response tests (address display, wipe) that tolerate the extra latency. + +**Phase 2**: Run the full test suite without screenshots for accurate pass/fail results. + +### How Screenshots Work + +1. `KEEPKEY_SCREENSHOT=1` env var enables capture (client.py line 61) +2. `conftest.py` creates per-test directories: `screenshots/{module}/{test_method}/` +3. `callback_ButtonRequest()` calls `_capture_oled()` BEFORE pressing the button +4. `_capture_oled()` reads 2048-byte OLED layout via DebugLink, decodes 256x64 monochrome, writes PNG +5. Screenshots named `btn00000.png`, `btn00001.png`, etc. per test +6. Report generator skips first 2 frames (setUp wipe+load) and embeds the rest inline + +### Screenshot Directory Layout + +``` +screenshots/ +├── msg_wipedevice/ +│ └── test_wipe_device/ +│ ├── btn00000.png # setUp wipe confirm (skipped in report) +│ ├── btn00001.png # setUp load_device (skipped in report) +│ └── btn00002.png # actual test screen (embedded in report) +├── msg_solana_getaddress/ +│ └── test_solana_get_address/ +│ ├── btn00000.png +│ ├── btn00001.png +│ └── btn00002.png # Solana address display with full base58 +└── ... +``` + +### Known Screenshot Limitations (verified 2026-03-27) + +1. **`show_display=True` screenshots capture wrong screen**: `_capture_oled()` in `callback_ButtonRequest` reads the OLED buffer via DebugLink BEFORE the firmware renders the address. The captured frame shows the previous screen (recovery sentence, home screen), not the address display. This is a timing race between the DebugLink read and the firmware OLED render. + +2. **Screenshot capture corrupts address responses**: When `KEEPKEY_SCREENSHOT=1`, the DebugLink round-trip in `callback_ButtonRequest` can cause the `show_display=True` response to return an empty address. Tests that assert on address content will fail in screenshot mode. + +3. **TON `raw_address` proto bug**: The `TonAddress.raw_address` field is defined as proto `string` but the firmware populates it with binary data (non-UTF-8). Causes `UnicodeDecodeError` when `show_display=True`. Needs proto fix: change to `bytes` type. + +4. **Screenshot file naming**: `conftest.py` uses `scr*` prefix but report generator expects `btn*` prefix. Need to align. + +### Implication for Phase 1/Phase 2 + +Show-display tests (`test_*_show_address`) go in Phase 1 with relaxed assertions (no address content checks). Address correctness is validated by non-show tests in Phase 2. The show tests exist ONLY to trigger the OLED display flow for screenshot capture. + +--- + +## Feature Gating (per-branch test control) + +### Problem + +During development, firmware branches are at different feature states: +- `develop` (7.10.0): no new chains +- After PR #1-6 merge: still 7.10.0, no new chains +- After PR #7 (mega): jumps to 7.14.0, gets Solana/TRON/TON/EVM/BIP-85 +- After PR #8 (zcash): gets Zcash Orchard + +The `requires_firmware("7.14.0")` check gates on version number, but two branches at "7.14.0" may have different features (mega without zcash vs mega with zcash). + +### Solution: Feature-based gating + +Like Trezor's test system, tests should declare which MESSAGE TYPES they need: + +```python +# Existing (works for version gating) +self.requires_firmware("7.14.0") + +# Existing (works for message-level feature gating) +self.requires_message("SolanaGetAddress") # skips if proto not available +self.requires_message("TronGetAddress") +self.requires_message("ZcashSignPCZT") # skips if Zcash not in this build +self.requires_message("EthereumTxMetadata") # skips if EVM clear-signing not available +``` + +The `requires_message()` method (already on `release/7.14.0`) checks if the protobuf message type exists in the current build's pb2 modules. This handles: +- 7.10.0 branches: all new chain tests skip (no Solana/TRON/TON protos) +- Mega branch: Solana/TRON/TON/EVM/BIP-85 tests run, Zcash skips +- Zcash branch: all tests run + +### Report Generator Integration + +The report generator's `SECTIONS` array already gates by `min_firmware_version`. Tests that skip via `requires_message()` show as `--` (pending/grey) in the report, which is correct — the feature isn't in this build. + +### Phase 1 Filter: Version-Aware + +Instead of hardcoding chain names: +```bash +# WRONG: hardcoded, breaks on 7.10.0 branches +pytest -k "test_solana_get or test_tron_get or test_ton_get" + +# RIGHT: run all show tests, let requires_firmware/requires_message skip +pytest -k "test_show or test_show_address or test_wipe_device or test_bip85" +``` + +The `requires_firmware()` and `requires_message()` decorators handle the skipping. No need to maintain a separate filter list per version. + +--- + +## Removed Files (stray / dangerous) + +The following files were removed from `tests/` on `release/7.14.0` (commit `04ef8e7`): + +| File | Lines | Why Removed | +|------|-------|-------------| +| `test_zcash_complete_nownodes.py` | 416 | Hardcoded RPC credentials (`78787ba8...`), internal IP (`100.117.181.111`), NOWNodes API key, interactive `input()` prompt for MAINNET broadcast | +| `test_zcash_v5_complete.py` | 313 | External RPC calls + mainnet broadcast | +| `test_zcash_nu6.py` | 184 | Mainnet broadcast tool disguised as test | +| `test_nu6_final.py` | 150 | "Sign and automatically broadcast NU6 transaction" | + +These are manual integration tools, not unit tests. pytest collected them and they failed with timeouts, polluting CI results. Zcash signing is properly tested by `test_msg_zcash_orchard.py` and `test_msg_zcash_sign_pczt.py` (emulator-only, zero external dependencies). + +**Rule**: No test file in `tests/` should import `requests`, hit external APIs, contain hardcoded credentials, or prompt for user input. + +--- + +## Local Verification Results (2026-03-27) + +Verified against emulator (alpha branch, firmware 7.14.0, port 12044): + +**Phase 1 (screenshots)**: 6 tests passed, 15 OLED PNGs captured +- BIP-85 seed derivation: 3 pages of mnemonic words (embedded in PDF) +- Solana address: full 44-char base58 with QR code +- TRON address: full 34-char Base58Check with QR code +- TON address: full 48-char base64url with QR code +- Wipe device: confirmation screen + +**Phase 2 (full suite)**: 372 passed, 0 failed, 6 skipped + +**Report generation**: 134 tests in PDF, 133 passed, 1 pending (N5 TON comment test) +- New Feature sections at top with `[NEW]` tags +- Version-gated: only shown because `fw_version=7.14.0` +- Inline OLED screenshots for BIP-85 (Solana/TRON/TON screenshots need path alignment for inline embedding) + +--- + +## CI Pipeline Integration + +### Current Flow (firmware repo) + +``` +scripts/emulator/python-keepkey-tests.sh: + Phase 1: KEEPKEY_SCREENSHOT=1 pytest -k "" → junit-screenshots.xml + PNGs + Phase 2: pytest (full suite) → junit.xml + exit status + +scripts/generate-test-report.py: + Reads junit.xml + screenshots/ → test-report.pdf +``` + +### Target Flow (consolidated) + +``` +scripts/emulator/python-keepkey-tests.sh: + Phase 1: KEEPKEY_SCREENSHOT=1 pytest -k "" → screenshots/ + Phase 2: pytest --junitxml=junit.xml (full suite) + +deps/python-keepkey/scripts/generate-test-report.py: + Reads junit.xml + screenshots/ + auto-detects fw version → test-report.pdf +``` + +The firmware repo's `scripts/generate-test-report.py` should be a thin wrapper: + +```python +#!/usr/bin/env python3 +"""Delegates to python-keepkey's version-aware report generator.""" +import subprocess, sys, os +script = os.path.join(os.path.dirname(__file__), '..', 'deps', 'python-keepkey', 'scripts', 'generate-test-report.py') +sys.exit(subprocess.call([sys.executable, script] + sys.argv[1:])) +``` + +--- + +## Tech Debt Inventory + +### BROKEN: Must Fix Before Rehearsal + +| # | Issue | Location | Fix | +|---|-------|----------|-----| +| 1 | Firmware `scripts/generate-test-report.py` is 314-line dumb version on alpha | alpha branch | Replace with wrapper to python-keepkey's generator | +| 2 | Phase 1 `-k` filter hardcodes chain names, not version-aware | `python-keepkey-tests.sh` | Use `requires_firmware()` skip mechanism — run ALL display tests, let the test self-skip | +| 3 | Phase 1 filter missing EVM clear-signing, Zcash display tests | `python-keepkey-tests.sh` | Expand filter or remove filter entirely | +| 4 | `generate-test-report.py` exists in 3 places (firmware scripts/, python-keepkey scripts/, zoo scripts/) | Multiple repos | Delete firmware copies, single source in python-keepkey | +| 5 | `scripts/zoo/generate_report.py` is a separate manual review PDF | firmware repo | Keep as separate tool, do not confuse with test report | + +### WRONG: Tests That Need Fixing + +| # | Test | Issue | Fix | +|---|------|-------|-----| +| 1 | TON `test_ton_sign_structured` | Was sending structured fields without `raw_tx` — firmware requires it | FIXED in 4b4dc05 | +| 2 | TON `test_ton_sign_with_memo` | Same | FIXED in 4b4dc05 | +| 3 | TON `test_ton_sign_deterministic` | Same | FIXED in 4b4dc05 | +| 4 | EVM `test_valid_metadata_returns_verified` + 8 others | Client missing `ethereum_send_tx_metadata()` method | FIXED in 4b4dc05 | +| 5 | Zcash Z1-Z9 all pending in mega report | Zcash tests require `requires_message("ZcashSignPCZT")` — proto available but tests don't execute | Investigate: may be `requires_firmware` version check or pb2 import issue | + +### STALE: Versions Out of Sync + +| Branch | `generate-test-report.py` | python-keepkey pin | Status | +|--------|--------------------------|-------------------|--------| +| alpha | 314 lines (dumb) | BitHighlander fork `5f32810` | WRONG — needs upstream pin + wrapper | +| develop | 1058 lines (rich, inline copy) | upstream `release/7.14.0` | WRONG — inline copy, should be wrapper | +| feat/ci-test-report-v2 | 1069 lines (rich, inline copy) | upstream `release/7.14.0` | WRONG — inline copy | +| feat/7.14.0-mega-v3 | 1050 lines (rich, inline copy) | upstream `4b4dc05` | WRONG — inline copy | + +**All firmware branches** should use the wrapper pattern pointing to `deps/python-keepkey/scripts/generate-test-report.py`. + +--- + +## Release Branch Checklist + +### Before creating any firmware feature branch + +1. Confirm python-keepkey `release/` has: + - [ ] All test files for the feature (`test_msg__*.py`) + - [ ] Section entry in `SECTIONS` array with correct `min_firmware_version` + - [ ] `conftest.py` and `common.py` screenshot infrastructure + - [ ] `ethereum_send_tx_metadata()` or equivalent client methods for new message types + - [ ] `requires_message()` guards for chain-specific pb2 imports + +2. Confirm firmware branch has: + - [ ] `deps/python-keepkey` pinned to upstream `release/` (NOT fork) + - [ ] `scripts/generate-test-report.py` is the thin wrapper (NOT inline copy) + - [ ] `.github/workflows/ci.yml` calls the wrapper + - [ ] `scripts/emulator/python-keepkey-tests.sh` Phase 1 filter includes new chain display tests + +### Per-PR Review (SOP Gate 3) + +For each PR's `test-report.pdf`: +1. Header matches branch, commit SHA, firmware version +2. New feature sections appear at TOP with `[NEW]` tag (if firmware version warrants) +3. Zero FAIL or ERROR +4. Every SKIP has documented justification +5. New chain sections have OLED screenshots inline (not just "OLED needed: ...") +6. No regressions — previously-passing tests still pass + +--- + +## Merge Sequence for Test System Consolidation + +This is the prerequisite work before the release dress rehearsal. + +### Step 0: Prove locally + +```bash +# Start emulator +cd projects/keepkey-firmware +KEEPKEY_UDP_PORT=12044 build-emu/bin/kkemu & + +# Run Phase 1 (screenshots) +cd deps/python-keepkey/tests +KEEPKEY_SCREENSHOT=1 \ +SCREENSHOT_DIR=/tmp/test-screenshots \ +KK_TRANSPORT_MAIN=127.0.0.1:12044 \ +KK_TRANSPORT_DEBUG=127.0.0.1:12045 \ +pytest -v -k "test_get_address or test_getaddress or test_wipe or test_bip85" \ + --junitxml=/tmp/junit-screenshots.xml --timeout=120 + +# Run Phase 2 (full suite) +KK_TRANSPORT_MAIN=127.0.0.1:12044 \ +KK_TRANSPORT_DEBUG=127.0.0.1:12045 \ +pytest -v --junitxml=/tmp/junit.xml + +# Generate report +cd ../scripts +python3 generate-test-report.py \ + --fw-version=7.14.0 \ + --junit=/tmp/junit.xml \ + --screenshots=/tmp/test-screenshots \ + --output=/tmp/test-report.pdf +``` + +Verify the PDF has: +- Per-chain sections with descriptions +- `[NEW]` tags on 7.14.0 features +- Inline OLED screenshots for address display tests +- Green checkmarks for passing tests + +### Step 1: Update python-keepkey `release/7.14.0` + +- [ ] Verify all test files present and correct +- [ ] Verify `SECTIONS` array complete for 7.14.0 +- [ ] Verify conftest.py + screenshot system works +- [ ] Push any fixes + +### Step 2: Update firmware `feat/ci-test-report-v2` (PR #0) + +- [ ] Replace `scripts/generate-test-report.py` with thin wrapper +- [ ] Update `python-keepkey-tests.sh` Phase 1 filter (version-aware or broad) +- [ ] Pin `deps/python-keepkey` to upstream `release/7.14.0` +- [ ] Push and verify CI produces correct PDF + +### Step 3: Reset develop, merge PR #0 + +- [ ] Reset develop to upstream +- [ ] Merge PR #0 (CI infrastructure) +- [ ] Verify develop's CI produces correct 7.10.0 baseline report + +### Step 4: Sequential PR merges per SOP + +- [ ] PR #1 (nanopb) → report should show 7.10.0, 91 tests, existing chains only +- [ ] PR #3 (bip39) → same +- [ ] PR #5 (lynx) → same +- [ ] PR #6 (bip85) → same (BIP-85 tests gated by 7.14.0, won't appear yet) +- [ ] PR #7 (mega) → report jumps to 7.14.0, new sections appear, screenshots required +- [ ] PR #8 (zcash) → Zcash section tests execute + +### Step 5: Verify each PDF + +Each PR's `test-report.pdf` is downloaded and reviewed: +- Pre-mega PRs: 91 tests, existing chains, no `[NEW]` sections +- Mega PR: 134 tests, `[NEW]` sections at top, inline OLED screenshots +- Zcash PR: Zcash section tests execute (no longer `--` pending) + +--- + +## File Reference + +| File | Location | Purpose | Owner | +|------|----------|---------|-------| +| `generate-test-report.py` | `python-keepkey/scripts/` | THE report generator (1061 lines) | python-keepkey `release/7.14.0` | +| `conftest.py` | `python-keepkey/tests/` | pytest screenshot directory plugin | python-keepkey `release/7.14.0` | +| `common.py` | `python-keepkey/tests/` | KeepKeyTest base + screenshot setup | python-keepkey `release/7.14.0` | +| `client.py` | `python-keepkey/keepkeylib/` | SCREENSHOT capture + ButtonRequest hook | python-keepkey `release/7.14.0` | +| `debuglink.py` | `python-keepkey/keepkeylib/` | `read_layout()` OLED buffer read | python-keepkey `release/7.14.0` | +| `python-keepkey-tests.sh` | firmware `scripts/emulator/` | CI test runner (Phase 1 + Phase 2) | firmware repo | +| `ci.yml` | firmware `.github/workflows/` | CI pipeline definition | firmware repo | +| `generate_report.py` | firmware `scripts/zoo/` | Manual review PDF (separate from test report) | firmware repo | diff --git a/include/keepkey/board/common.h b/include/keepkey/board/common.h index 7e96e62fe..76ee955e1 100644 --- a/include/keepkey/board/common.h +++ b/include/keepkey/board/common.h @@ -26,12 +26,13 @@ #define HW_ENTROPY_LEN (12 + 32) extern uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN]; -void __attribute__((noreturn)) -__fatal_error(const char *expr, const char *msg, const char *file, int line, - const char *func); -void __attribute__((noreturn)) -error_shutdown(const char *line1, const char *line2, const char *line3, - const char *line4); +void __attribute__((noreturn)) __fatal_error(const char* expr, const char* msg, + const char* file, int line, + const char* func); +void __attribute__((noreturn)) error_shutdown(const char* line1, + const char* line2, + const char* line3, + const char* line4); #define ensure(expr, msg) \ (((expr) == sectrue) \ @@ -43,8 +44,8 @@ void hal_delay(uint32_t ms); void wait_random(void); void drbg_init(void); -void drbg_reseed(const uint8_t *entropy, size_t len); -void drbg_generate(uint8_t *buf, size_t len); +void drbg_reseed(const uint8_t* entropy, size_t len); +void drbg_generate(uint8_t* buf, size_t len); uint32_t drbg_random32(void); #endif diff --git a/include/keepkey/board/confirm_sm.h b/include/keepkey/board/confirm_sm.h index eaa89f1c4..fa74c829f 100644 --- a/include/keepkey/board/confirm_sm.h +++ b/include/keepkey/board/confirm_sm.h @@ -28,26 +28,25 @@ /* implement a means to display debug information */ #ifdef DEBUG_ON - // Examples - // DEBUG_DISPLAY("here"); - // DEBUG_DISPLAY("%d %s", slot, account); - #define DEBUG_DISPLAY(...)\ - {\ - char _str[61]={0};\ - snprintf(_str, 60, __VA_ARGS__);\ - (void)review(ButtonRequestType_ButtonRequest_Other, _str, " ");\ +// Examples +// DEBUG_DISPLAY("here"); +// DEBUG_DISPLAY("%d %s", slot, account); +#define DEBUG_DISPLAY(...) \ + { \ + char _str[61] = {0}; \ + snprintf(_str, 60, __VA_ARGS__); \ + (void)review(ButtonRequestType_ButtonRequest_Other, _str, " "); \ } - // Example - // DEBUG_DISPLAY_VAL("sig", "sig %s", 65, resp->signature.bytes[_ctr]); - #define DEBUG_DISPLAY_VAL(TITLE,VALNAME,SIZE,BYTES) \ - {\ - char _str[SIZE+1];\ - int _ctr;\ - for (_ctr=0; _ctrsignature.bytes[_ctr]); +#define DEBUG_DISPLAY_VAL(TITLE, VALNAME, SIZE, BYTES) \ + { \ + char _str[SIZE + 1]; \ + int _ctr; \ + for (_ctr = 0; _ctr < SIZE / 2; _ctr++) { \ + snprintf(&_str[2 * _ctr], 3, "%02x", BYTES); \ + } \ + (void)review(ButtonRequestType_ButtonRequest_Other, TITLE, VALNAME, _str); \ } #endif @@ -68,8 +67,8 @@ typedef enum { /* Define the given layout dialog texts for each screen */ typedef struct { - const char *request_title; - const char *request_body; + const char* request_title; + const char* request_body; } ScreenLine; typedef ScreenLine ScreenLines; @@ -82,9 +81,9 @@ typedef struct { bool immediate; } StateInfo; -#define isprint(c) ((c) >= 0x20 && (c) < 0x7f) +#define isprint(c) ((c) >= 0x20 && (c) < 0x7f) -typedef void (*layout_notification_t)(const char *str1, const char *str2, +typedef void (*layout_notification_t)(const char* str1, const char* str2, NotificationType type); /// User confirmation. @@ -92,21 +91,22 @@ typedef void (*layout_notification_t)(const char *str1, const char *str2, /// \param request_title Title of confirm message. /// \param request_body Body of confirm message. /// \returns true iff the device confirmed. -bool confirm(ButtonRequestType type, const char *request_title, - const char *request_body, ...) +bool confirm(ButtonRequestType type, const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 3, 4))); -bool confirm_constant_power(ButtonRequestType type, const char *request_title, const char *request_body, - ...) __attribute__((format(printf, 3, 4))); +bool confirm_constant_power(ButtonRequestType type, const char* request_title, + const char* request_body, ...) + __attribute__((format(printf, 3, 4))); /// User confirmation. /// \param type The kind of button request to send to the host. /// \param request_title Title of confirm message. /// \param request_body Body of confirm message. /// \returns true iff the device confirmed. -bool confirm_with_custom_button_request(ButtonRequest *button_request, - const char *request_title, - const char *request_body, ...) +bool confirm_with_custom_button_request(const ButtonRequest* button_request, + const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 3, 4))); /// User confirmation, custom layout. @@ -117,8 +117,8 @@ bool confirm_with_custom_button_request(ButtonRequest *button_request, /// \returns true iff the device confirmed. bool confirm_with_custom_layout(layout_notification_t layout_notification_func, ButtonRequestType type, - const char *request_title, - const char *request_body, ...) + const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 4, 5))); /// User confirmation. @@ -127,33 +127,33 @@ bool confirm_with_custom_layout(layout_notification_t layout_notification_func, /// \param request_title Title of confirm message. /// \param request_body Body of confirm message. /// \returns true iff the device confirmed. -bool confirm_without_button_request(const char *request_title, - const char *request_body, ...) +bool confirm_without_button_request(const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 2, 3))); /// Like confirm, but always \returns true. /// \param request_title Title of confirm message. /// \param request_body Body of confirm message. -bool review(ButtonRequestType type, const char *request_title, - const char *request_body, ...) +bool review(ButtonRequestType type, const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 3, 4))); /// Like confirm, but always \returns true. Does not message the host for /// ButtonAcks. \param request_title Title of confirm message. \param /// request_body Body of confirm message. -bool review_without_button_request(const char *request_title, - const char *request_body, ...) +bool review_without_button_request(const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 2, 3))); -bool review_with_icon(ButtonRequestType type, IconType iconNum, const char *request_title, - const char *request_body, ...) - __attribute__((format(printf, 4, 5))); +bool review_with_icon(ButtonRequestType type, IconType iconNum, + const char* request_title, const char* request_body, ...) + __attribute__((format(printf, 4, 5))); /// Like confirm, but always \returns true and immediately. /// \param request_title Title of confirm message. /// \param request_body Body of confirm message. -bool review_immediate(ButtonRequestType type, const char *request_title, - const char *request_body, ...) +bool review_immediate(ButtonRequestType type, const char* request_title, + const char* request_body, ...) __attribute__((format(printf, 3, 4))); #endif diff --git a/include/keepkey/board/draw.h b/include/keepkey/board/draw.h index 2e9ccfa02..f1e6f0627 100644 --- a/include/keepkey/board/draw.h +++ b/include/keepkey/board/draw.h @@ -41,17 +41,17 @@ typedef struct { uint16_t width; } BoxDrawableParams; -bool draw_char_with_shift(Canvas *canvas, DrawableParams *p, uint16_t *x_shift, - uint16_t *y_shift, const CharacterImage *img); -void draw_string(Canvas *canvas, const Font *font, const char *c, - DrawableParams *p, uint16_t width, uint16_t line_height); -void draw_char(Canvas *canvas, const Font *font, char c, DrawableParams *p); -void draw_char_simple(Canvas *canvas, const Font *font, char c, uint8_t color, +bool draw_char_with_shift(Canvas* canvas, DrawableParams* p, uint16_t* x_shift, + uint16_t* y_shift, const CharacterImage* img); +void draw_string(Canvas* canvas, const Font* font, const char* str_write, + const DrawableParams* p, uint16_t width, uint16_t line_height); +void draw_char(Canvas* canvas, const Font* font, char c, DrawableParams* p); +void draw_char_simple(Canvas* canvas, const Font* font, char c, uint8_t color, uint16_t x, uint16_t y); -void draw_box(Canvas *canvas, BoxDrawableParams *params); -void draw_box_simple(Canvas *canvas, uint8_t color, uint16_t x, uint16_t y, +void draw_box(Canvas* canvas, BoxDrawableParams* p); +void draw_box_simple(Canvas* canvas, uint8_t color, uint16_t x, uint16_t y, uint16_t width, uint16_t height); -bool draw_bitmap_mono_rle(Canvas *canvas, const AnimationFrame *frame, +bool draw_bitmap_mono_rle(Canvas* canvas, const AnimationFrame* frame, bool erase); #endif diff --git a/include/keepkey/board/font.h b/include/keepkey/board/font.h index 8ab656dc1..95933f2a5 100644 --- a/include/keepkey/board/font.h +++ b/include/keepkey/board/font.h @@ -24,7 +24,7 @@ /* Data pertaining to the image of a character */ typedef struct { - const uint8_t *data; + const uint8_t* data; uint16_t width; uint16_t height; } CharacterImage; @@ -32,26 +32,26 @@ typedef struct { /* Character information. */ typedef struct { long int code; - const CharacterImage *image; + const CharacterImage* image; } Character; /* A complete font package. */ typedef struct { int length; int size; - const Character *characters; + const Character* characters; } Font; -const Font *get_pin_font(void); -const Font *get_title_font(void); -const Font *get_body_font(void); +const Font* get_pin_font(void); +const Font* get_title_font(void); +const Font* get_body_font(void); -const CharacterImage *font_get_char(const Font *font, char c); +const CharacterImage* font_get_char(const Font* font, char c); -uint32_t font_height(const Font *font); -uint32_t font_width(const Font *font); +uint32_t font_height(const Font* font); +uint32_t font_width(const Font* font); -uint32_t calc_str_width(const Font *font, const char *str); -uint32_t calc_str_line(const Font *font, const char *str, uint16_t line_width); +uint32_t calc_str_width(const Font* font, const char* str); +uint32_t calc_str_line(const Font* font, const char* str, uint16_t line_width); #endif diff --git a/include/keepkey/board/keepkey_board.h b/include/keepkey/board/keepkey_board.h index dd4fe5248..a2e520a48 100644 --- a/include/keepkey/board/keepkey_board.h +++ b/include/keepkey/board/keepkey_board.h @@ -62,7 +62,8 @@ #define VERSION_STR(x) VERSION_NUM(x) #define RESET_PARAM_NONE 0 -// This is the ASCII string "UPDT" interpreted as an integer in little-endian form. +// This is the ASCII string "UPDT" interpreted as an integer in little-endian +// form. #define RESET_PARAM_REQUEST_UPDATE 0x54445055 /* Flash metadata structure which will contains unique identifier @@ -88,9 +89,9 @@ void board_init(void); void kk_board_init(void); void __stack_chk_fail(void) __attribute__((noreturn)); -uint32_t calc_crc32(const void *data, int word_len); +uint32_t calc_crc32(const void* data, int word_len); void __attribute__((noreturn)) shutdown(void); -void memset_reg(void *start, void *stop, uint32_t val); +void memset_reg(void* start, void* stop, uint32_t val); #endif diff --git a/include/keepkey/board/keepkey_display.h b/include/keepkey/board/keepkey_display.h index 71fb78e92..8b23406cb 100644 --- a/include/keepkey/board/keepkey_display.h +++ b/include/keepkey/board/keepkey_display.h @@ -31,8 +31,8 @@ #define DEFAULT_DISPLAY_BRIGHTNESS 100 /* Percent */ void display_hw_init(void); -Canvas *display_canvas_init(void); -Canvas *display_canvas(void); +Canvas* display_canvas_init(void); +Canvas* display_canvas(void); void display_refresh(void); void display_set_brightness(int percentage); void display_turn_on(void); diff --git a/include/keepkey/board/keepkey_flash.h b/include/keepkey/board/keepkey_flash.h index 2074e601a..ba61060ba 100644 --- a/include/keepkey/board/keepkey_flash.h +++ b/include/keepkey/board/keepkey_flash.h @@ -41,16 +41,16 @@ intptr_t flash_write_helper(Allocation group); void flash_erase(Allocation group); void flash_erase_word(Allocation group); bool flash_write(Allocation group, uint32_t offset, uint32_t len, - const uint8_t *data); + const uint8_t* data); bool flash_write_word(Allocation group, uint32_t offset, uint32_t len, - const uint8_t *data); + const uint8_t* data); bool flash_chk_status(void); bool is_mfg_mode(void); bool set_mfg_mode_off(void); -const char *flash_getModel(void); +const char* flash_getModel(void); bool flash_setModel(const char (*model)[32]); -const char *flash_programModel(void); +const char* flash_programModel(void); void flash_collectHWEntropy(bool privileged); -void flash_readHWEntropy(uint8_t *buff, size_t size); +void flash_readHWEntropy(uint8_t* buff, size_t size); #endif diff --git a/include/keepkey/board/keepkey_usart.h b/include/keepkey/board/keepkey_usart.h index 308433a3e..7fea7228a 100644 --- a/include/keepkey/board/keepkey_usart.h +++ b/include/keepkey/board/keepkey_usart.h @@ -33,7 +33,7 @@ #define LARGE_DEBUG_BUF 128 #ifndef EMULATOR -void dbg_print(const char *pStr, ...) __attribute__((format(printf, 1, 2))); +void dbg_print(const char* pStr, ...) __attribute__((format(printf, 1, 2))); #else #define dbg_print(FMT, ...) \ do { \ diff --git a/include/keepkey/board/layout.h b/include/keepkey/board/layout.h index b3be90b96..ed5ab8f33 100644 --- a/include/keepkey/board/layout.h +++ b/include/keepkey/board/layout.h @@ -44,7 +44,7 @@ /* Title */ #define TITLE_COLOR 0xFF #define TITLE_WIDTH 206 -#define TITLE_WIDTH_WITH_ICON TITLE_WIDTH-LEFT_MARGIN_WITH_ICON +#define TITLE_WIDTH_WITH_ICON TITLE_WIDTH - LEFT_MARGIN_WITH_ICON #define TITLE_ROWS 1 #define TITLE_FONT_LINE_PADDING 0 #define TITLE_CHAR_MAX 128 @@ -53,7 +53,7 @@ #define BODY_TOP_MARGIN 7 #define BODY_COLOR 0xFF #define BODY_WIDTH 225 -#define BODY_WIDTH_WITH_ICON BODY_WIDTH-LEFT_MARGIN_WITH_ICON +#define BODY_WIDTH_WITH_ICON BODY_WIDTH - LEFT_MARGIN_WITH_ICON #define BODY_ROWS 3 #define BODY_FONT_LINE_PADDING 4 #define BODY_CHAR_MAX 352 @@ -77,11 +77,11 @@ typedef enum { } NotificationType; typedef enum { - NO_ICON=0, + NO_ICON = 0, ETHEREUM_ICON, } IconType; -typedef void (*AnimateCallback)(void *data, uint32_t duration, +typedef void (*AnimateCallback)(void* data, uint32_t duration, uint32_t elapsed); typedef struct Animation Animation; typedef void (*leaving_handler_t)(void); @@ -89,43 +89,44 @@ typedef void (*leaving_handler_t)(void); struct Animation { uint32_t duration; uint32_t elapsed; - void *data; + void* data; AnimateCallback animate_callback; - Animation *next; + Animation* next; }; typedef struct { - Animation *head; + Animation* head; int size; } AnimationQueue; void layout_has_icon(bool tf); -void layout_init(Canvas *canvas); -Canvas *layout_get_canvas(void); +void layout_init(Canvas* new_canvas); +Canvas* layout_get_canvas(void); void call_leaving_handler(void); void layout_firmware_update_confirmation(void); -void layout_standard_notification(const char *str1, const char *str2, +void layout_standard_notification(const char* str1, const char* str2, NotificationType type); -void layout_constant_power_notification(const char *str1, const char *str2, NotificationType type); -void layout_notification_icon(NotificationType type, DrawableParams *sp); +void layout_constant_power_notification(const char* str1, const char* str2, + NotificationType type); +void layout_notification_icon(NotificationType type, DrawableParams* sp); void layout_add_icon(IconType type); -void layout_warning(const char *prompt); -void layout_warning_static(const char *str); -void layout_simple_message(const char *str); +void layout_warning(const char* str); +void layout_warning_static(const char* str); +void layout_simple_message(const char* str); void layout_version(int32_t major, int32_t minor, int32_t patch); void layout_home(void); void layout_home_reversed(void); void animate(void); bool is_animating(void); void force_animation_start(void); -void animating_progress_handler(const char *desc, int permil); -void layoutProgress(const char *desc, int permil); -void layoutProgressForAuth(const char *otp, const char *desc, int permil); -void layoutProgressSwipe(const char *desc, int permil); -void layout_add_animation(AnimateCallback callback, void *data, +void animating_progress_handler(const char* desc, int permil); +void layoutProgress(const char* desc, int permil); +void layoutProgressForAuth(const char* otp, const char* desc, int permil); +void layoutProgressSwipe(const char* desc, int permil); +void layout_add_animation(AnimateCallback callback, void* data, uint32_t duration); -void layout_animate_images(void *data, uint32_t duration, uint32_t elapsed); +void layout_animate_images(void* data, uint32_t duration, uint32_t elapsed); void layout_clear(void); #if DEBUG_LINK void layout_debuglink_watermark(void); @@ -133,6 +134,6 @@ void layout_debuglink_watermark(void); void layout_clear_animations(void); void layout_clear_static(void); -void kk_strupr(char *str); -void kk_strlwr(char *str); +void kk_strupr(char* str); +void kk_strlwr(char* str); #endif diff --git a/include/keepkey/board/memcmp_s.h b/include/keepkey/board/memcmp_s.h index 1fb6ea072..b1b978752 100644 --- a/include/keepkey/board/memcmp_s.h +++ b/include/keepkey/board/memcmp_s.h @@ -7,6 +7,6 @@ /// \param lhs must be an array of at least `len` bytes /// \param rhs must be an array of at least `len` bytes /// \param len must be in [32, 255), otherwise behavior is undefined. -int memcmp_s(const void *lhs, const void *rhs, size_t len); +int memcmp_s(const void* lhs, const void* rhs, size_t len); #endif diff --git a/include/keepkey/board/memory.h b/include/keepkey/board/memory.h index 6e47126a1..1f1c9337c 100644 --- a/include/keepkey/board/memory.h +++ b/include/keepkey/board/memory.h @@ -20,7 +20,7 @@ #ifndef MEMORY_H #define MEMORY_H -//#include +// #include #include "trezor/crypto/sha2.h" #include @@ -73,17 +73,19 @@ // clang-format on #ifdef EMULATOR -extern uint8_t *emulator_flash_base; +extern uint8_t* emulator_flash_base; #define FLASH_PTR(x) (emulator_flash_base + (x - FLASH_ORIGIN)) #else -#define FLASH_PTR(x) (const uint8_t *)(x) +#define FLASH_PTR(x) (const uint8_t*)(x) #endif -#define STACK_GOOD 1 // do not change this value, must equal SUCCESS in eip712 error list -#define STACK_TOO_SMALL 13 // do not change this value, used in eip712 error list +#define STACK_GOOD \ + 1 // do not change this value, must equal SUCCESS in eip712 error list +#define STACK_TOO_SMALL \ + 13 // do not change this value, used in eip712 error list -#define OPTION_BYTES_1 ((uint64_t *)0x1FFFC000) -#define OPTION_BYTES_2 ((uint64_t *)0x1FFFC008) +#define OPTION_BYTES_1 ((uint64_t*)0x1FFFC000) +#define OPTION_BYTES_2 ((uint64_t*)0x1FFFC008) #define OPTION_RDP 0xCCFF #define OPTION_WRP 0xFF9E @@ -130,34 +132,32 @@ extern uint8_t *emulator_flash_base; #define FLASH_META_MAGIC (FLASH_META_START) #define FLASH_META_CODELEN \ - (FLASH_META_MAGIC + sizeof(((app_meta_td *)NULL)->magic)) + (FLASH_META_MAGIC + sizeof(((app_meta_td*)NULL)->magic)) #define FLASH_META_SIGINDEX1 \ - (FLASH_META_CODELEN + sizeof(((app_meta_td *)NULL)->code_len)) + (FLASH_META_CODELEN + sizeof(((app_meta_td*)NULL)->code_len)) #define FLASH_META_SIGINDEX2 \ - (FLASH_META_SIGINDEX1 + sizeof(((app_meta_td *)NULL)->sig_index1)) + (FLASH_META_SIGINDEX1 + sizeof(((app_meta_td*)NULL)->sig_index1)) #define FLASH_META_SIGINDEX3 \ - (FLASH_META_SIGINDEX2 + sizeof(((app_meta_td *)NULL)->sig_index2)) + (FLASH_META_SIGINDEX2 + sizeof(((app_meta_td*)NULL)->sig_index2)) #define FLASH_SIG_FLAG \ - (FLASH_META_SIGINDEX3 + sizeof(((app_meta_td *)NULL)->sig_index3)) + (FLASH_META_SIGINDEX3 + sizeof(((app_meta_td*)NULL)->sig_index3)) #define FLASH_META_FLAGS \ - (FLASH_SIG_FLAG + sizeof(((app_meta_td *)NULL)->sig_flag)) + (FLASH_SIG_FLAG + sizeof(((app_meta_td*)NULL)->sig_flag)) #define FLASH_META_RESERVE \ - (FLASH_META_FLAGS + sizeof(((app_meta_td *)NULL)->meta_flags)) -#define FLASH_META_SIG1 \ - (FLASH_META_RESERVE + sizeof(((app_meta_td *)NULL)->rsv)) -#define FLASH_META_SIG2 (FLASH_META_SIG1 + sizeof(((app_meta_td *)NULL)->sig1)) -#define FLASH_META_SIG3 (FLASH_META_SIG2 + sizeof(((app_meta_td *)NULL)->sig2)) + (FLASH_META_FLAGS + sizeof(((app_meta_td*)NULL)->meta_flags)) +#define FLASH_META_SIG1 (FLASH_META_RESERVE + sizeof(((app_meta_td*)NULL)->rsv)) +#define FLASH_META_SIG2 (FLASH_META_SIG1 + sizeof(((app_meta_td*)NULL)->sig1)) +#define FLASH_META_SIG3 (FLASH_META_SIG2 + sizeof(((app_meta_td*)NULL)->sig2)) -#define META_MAGIC_SIZE (sizeof(((app_meta_td *)NULL)->magic)) +#define META_MAGIC_SIZE (sizeof(((app_meta_td*)NULL)->magic)) #define FLASH_APP_START \ (FLASH_META_START + FLASH_META_DESC_LEN) // 0x0806_0200 - 0x080F_FFFF #define FLASH_APP_LEN (FLASH_END - FLASH_APP_START) -#define SIG_FLAG (*(uint8_t const *)FLASH_SIG_FLAG) - -#define META_FLAGS (*(uint32_t const *)FLASH_META_FLAGS) +#define SIG_FLAG (*(uint8_t const*)FLASH_SIG_FLAG) +#define META_FLAGS (*(uint32_t const*)FLASH_META_FLAGS) /* Misc Info. */ #define FLASH_BOOTSTRAP_SECTOR 0 @@ -252,9 +252,11 @@ static const FlashSector flash_sector_map[] = { {11, 0x080E0000, APP_FLASH_SECT_LEN, FLASH_APP}, {-1, 0, 0, FLASH_INVALID}}; - -#define STACK_REENTRANCY_REQ 1280 // calculate this from a re-entrant call (unsigned)&p - (unsigned)&end) -#define STACK_SIZE_GUARD (STACK_REENTRANCY_REQ + 64) // Can't recurse without this much stack available +#define STACK_REENTRANCY_REQ \ + 1280 // calculate this from a re-entrant call (unsigned)&p - (unsigned)&end) +#define STACK_SIZE_GUARD \ + (STACK_REENTRANCY_REQ + \ + 64) // Can't recurse without this much stack available int memcheck(unsigned stackGuardSize); void mpu_config(int); @@ -270,16 +272,16 @@ void memory_unlock(void); /// \param hash Buffer to be filled with hash. /// Must be at least SHA256_DIGEST_LENGTH bytes long. /// \param cached Whether a cached value is acceptable. -int memory_bootloader_hash(uint8_t *hash, bool cached); +int memory_bootloader_hash(uint8_t* hash, bool cached); -int memory_firmware_hash(uint8_t *hash); -int memory_storage_hash(uint8_t *hash, Allocation storage_location); -bool find_active_storage(Allocation *storage_location); +int memory_firmware_hash(uint8_t* hash); +int memory_storage_hash(uint8_t* hash, Allocation storage_location); +bool find_active_storage(Allocation* storage_location); /// Find the storage location *after* the active one. Allocation next_storage(Allocation active); -void memory_getDeviceLabel(char *str, size_t len); +void memory_getDeviceLabel(char* str, size_t len); /// Write the marker that allows the firmware to boot with secrets preserved. bool storage_protect_off(void); @@ -288,13 +290,13 @@ bool storage_protect_off(void); bool storage_protect_on(void); /// Wipe if the status is not STORAGE_PROTECT_DISABLED -void storage_protect_wipe(uint32_t status); +void storage_protect_wipe(uint32_t storage_protect_status); /// \returns STORAGE_PROTECT_{ENABLED,DISABLED} uint32_t storage_protect_status(void); -extern void *_timerusr_isr; -extern void *_buttonusr_isr; -extern void *_mmhusr_isr; +extern void* _timerusr_isr; +extern void* _buttonusr_isr; +extern void* _mmhusr_isr; #endif diff --git a/include/keepkey/board/messages.h b/include/keepkey/board/messages.h index 7fca69570..fd0c6e4c3 100644 --- a/include/keepkey/board/messages.h +++ b/include/keepkey/board/messages.h @@ -33,36 +33,36 @@ #define MSG_IN(ID, STRUCT_NAME, PROCESS_FUNC) \ [ID].msg_id = (ID), [ID].type = (NORMAL_MSG), [ID].dir = (IN_MSG), \ [ID].fields = (STRUCT_NAME##_fields), [ID].dispatch = (PARSABLE), \ - [ID].process_func = (void (*)(void *))(PROCESS_FUNC), + [ID].process_func = (void (*)(void*))(PROCESS_FUNC), #define MSG_OUT(ID, STRUCT_NAME, PROCESS_FUNC) \ [ID].msg_id = (ID), [ID].type = (NORMAL_MSG), [ID].dir = (OUT_MSG), \ [ID].fields = (STRUCT_NAME##_fields), [ID].dispatch = (PARSABLE), \ - [ID].process_func = (void (*)(void *))(PROCESS_FUNC), + [ID].process_func = (void (*)(void*))(PROCESS_FUNC), #define RAW_IN(ID, STRUCT_NAME, PROCESS_FUNC) \ [ID].msg_id = (ID), [ID].type = (NORMAL_MSG), [ID].dir = (IN_MSG), \ [ID].fields = (STRUCT_NAME##_fields), [ID].dispatch = (RAW), \ - [ID].process_func = (void (*)(void *))(void *)(PROCESS_FUNC), + [ID].process_func = (void (*)(void*))(void*)(PROCESS_FUNC), #define DEBUG_IN(ID, STRUCT_NAME, PROCESS_FUNC) \ [ID].msg_id = (ID), [ID].type = (DEBUG_MSG), [ID].dir = (IN_MSG), \ [ID].fields = (STRUCT_NAME##_fields), [ID].dispatch = (PARSABLE), \ - [ID].process_func = (void (*)(void *))(PROCESS_FUNC), + [ID].process_func = (void (*)(void*))(PROCESS_FUNC), #define DEBUG_OUT(ID, STRUCT_NAME, PROCESS_FUNC) \ [ID].msg_id = (ID), [ID].type = (DEBUG_MSG), [ID].dir = (OUT_MSG), \ [ID].fields = (STRUCT_NAME##_fields), [ID].dispatch = (PARSABLE), \ - [ID].process_func = (void (*)(void *))(PROCESS_FUNC), + [ID].process_func = (void (*)(void*))(PROCESS_FUNC), #define NO_PROCESS_FUNC 0 -typedef void (*msg_handler_t)(void *ptr); -typedef void (*msg_failure_t)(FailureType, const char *); -typedef bool (*usb_tx_handler_t)(uint8_t *, uint32_t); +typedef void (*msg_handler_t)(void* ptr); +typedef void (*msg_failure_t)(FailureType, const char*); +typedef bool (*usb_tx_handler_t)(uint8_t*, uint32_t); #if DEBUG_LINK -typedef void (*msg_debug_link_get_state_t)(DebugLinkGetState *); +typedef void (*msg_debug_link_get_state_t)(DebugLinkGetState*); #endif typedef enum { @@ -77,7 +77,7 @@ typedef enum { IN_MSG, OUT_MSG } MessageMapDirection; typedef enum { PARSABLE, RAW } MessageMapDispatch; typedef struct { - const pb_field_t *fields; + const pb_field_t* fields; msg_handler_t process_func; MessageMapDispatch dispatch; MessageMapType type; @@ -86,7 +86,7 @@ typedef struct { } MessagesMap_t; typedef struct { - const uint8_t *buffer; + const uint8_t* buffer; uint32_t length; } RawMessage; @@ -97,38 +97,38 @@ typedef enum { RAW_MESSAGE_ERROR } RawMessageState; -typedef void (*raw_msg_handler_t)(RawMessage *msg, uint32_t frame_length); +typedef void (*raw_msg_handler_t)(RawMessage* msg, uint32_t frame_length); -const pb_field_t *message_fields(MessageMapType type, MessageType msg_id, +const pb_field_t* message_fields(MessageMapType type, MessageType msg_id, MessageMapDirection dir); -bool msg_write(MessageType msg_id, const void *msg); +bool msg_write(MessageType msg_id, const void* msg); #if DEBUG_LINK -bool msg_debug_write(MessageType msg_id, const void *msg); +bool msg_debug_write(MessageType msg_id, const void* msg); #endif -void msg_map_init(const void *map, const size_t size); +void msg_map_init(const void* map, const size_t size); void set_msg_failure_handler(msg_failure_t failure_func); -void call_msg_failure_handler(FailureType code, const char *text); +void call_msg_failure_handler(FailureType code, const char* text); #if DEBUG_LINK void set_msg_debug_link_get_state_handler( msg_debug_link_get_state_t debug_link_get_state_func); -void call_msg_debug_link_get_state_handler(DebugLinkGetState *msg); +void call_msg_debug_link_get_state_handler(DebugLinkGetState* msg); #endif void msg_init(void); -void handle_usb_rx(const void *data, size_t len); +void handle_usb_rx(const void* msg, size_t len); #if DEBUG_LINK -void handle_debug_usb_rx(const void *data, size_t len); +void handle_debug_usb_rx(const void* msg, size_t len); #endif -MessageType wait_for_tiny_msg(uint8_t *buf); -MessageType check_for_tiny_msg(uint8_t *buf); +MessageType wait_for_tiny_msg(uint8_t* buf); +MessageType check_for_tiny_msg(uint8_t* buf); -uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count); -int encode_pb(const void *source_ptr, const pb_field_t *fields, uint8_t *buffer, +uint32_t parse_pb_varint(RawMessage* msg, uint8_t varint_count); +int encode_pb(const void* source_ptr, const pb_field_t* fields, uint8_t* buffer, uint32_t len); #endif diff --git a/include/keepkey/board/otp.h b/include/keepkey/board/otp.h index 2e25f1158..3c38e8ba8 100644 --- a/include/keepkey/board/otp.h +++ b/include/keepkey/board/otp.h @@ -30,9 +30,9 @@ bool flash_otp_is_locked(uint8_t block); bool flash_otp_lock(uint8_t block); -bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, +bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t* data, uint8_t datalen); -bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, +bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t* data, uint8_t datalen); #endif diff --git a/include/keepkey/board/pin.h b/include/keepkey/board/pin.h index 8a07a32cb..f43aa9540 100644 --- a/include/keepkey/board/pin.h +++ b/include/keepkey/board/pin.h @@ -51,7 +51,7 @@ typedef struct { uint16_t pin; } Pin; -void pin_init_output(const Pin *pin, OutputMode output_mode, +void pin_init_output(const Pin* pin, OutputMode output_mode, PullMode pull_mode); #endif diff --git a/include/keepkey/board/pubkeys.h b/include/keepkey/board/pubkeys.h index f07a4c27c..f3ad493c7 100644 --- a/include/keepkey/board/pubkeys.h +++ b/include/keepkey/board/pubkeys.h @@ -23,11 +23,14 @@ #include // The firmware has been designed to allow future key rotations on a 5 key basis -// i.e, the current keys are indexed 0-4, the next future set of keys will be 5-9, etc. -// When new keys are rotated in, the old keys must be recognized as expired. +// i.e, the current keys are indexed 0-4, the next future set of keys will be +// 5-9, etc. When new keys are rotated in, the old keys must be recognized as +// expired. #define PUBKEYS 5 -#define EXP_PUBKEYS 5 // This is a nop prior to future key rotation, intended to indicate which keys are expired. +#define EXP_PUBKEYS \ + 5 // This is a nop prior to future key rotation, intended to indicate which + // keys are expired. #define PUBKEY_LENGTH 65 #define SIGNATURES 3 diff --git a/include/keepkey/board/resources.h b/include/keepkey/board/resources.h index e5963a3fa..683b79eea 100644 --- a/include/keepkey/board/resources.h +++ b/include/keepkey/board/resources.h @@ -25,19 +25,19 @@ #include #include -const AnimationFrame *get_ethereum_icon_frame(void); -const AnimationFrame *get_confirm_icon_frame(void); -const AnimationFrame *get_confirmed_frame(void); -const AnimationFrame *get_unplug_frame(void); -const AnimationFrame *get_warning_frame(void); -const AnimationFrame *get_logo_frame(void); +const AnimationFrame* get_ethereum_icon_frame(void); +const AnimationFrame* get_confirm_icon_frame(void); +const AnimationFrame* get_confirmed_frame(void); +const AnimationFrame* get_unplug_frame(void); +const AnimationFrame* get_warning_frame(void); +const AnimationFrame* get_logo_frame(void); -const VariantAnimation *get_confirming_animation(void); -const VariantAnimation *get_warning_animation(void); -const VariantAnimation *get_logo_animation(void); -const VariantAnimation *get_logo_reversed_animation(void); +const VariantAnimation* get_confirming_animation(void); +const VariantAnimation* get_warning_animation(void); +const VariantAnimation* get_logo_animation(void); +const VariantAnimation* get_logo_reversed_animation(void); -uint32_t get_image_animation_duration(const VariantAnimation *animation); -int get_image_animation_frame(const VariantAnimation *animation, +uint32_t get_image_animation_duration(const VariantAnimation* animation); +int get_image_animation_frame(const VariantAnimation* animation, const uint32_t elapsed, bool loop); #endif diff --git a/include/keepkey/board/timer.h b/include/keepkey/board/timer.h index 440e69368..717be19a1 100644 --- a/include/keepkey/board/timer.h +++ b/include/keepkey/board/timer.h @@ -28,20 +28,20 @@ #define MAX_RUNNABLES 3 /* Max number of queue for task manager */ typedef void (*callback_func_t)(void); -typedef void (*Runnable)(void *context); +typedef void (*Runnable)(void* context); typedef struct RunnableNode RunnableNode; struct RunnableNode { uint32_t remaining; Runnable runnable; - void *context; + void* context; uint32_t period; bool repeating; - RunnableNode *next; + RunnableNode* next; }; typedef struct { - RunnableNode *head; + RunnableNode* head; int size; } RunnableQueue; @@ -69,8 +69,8 @@ uint32_t fi_defense_delay(volatile uint32_t value); void delay_ms_with_callback(uint32_t ms, callback_func_t callback_func, uint32_t frequency_ms); -void post_delayed(Runnable runnable, void *context, uint32_t ms_delay); -void post_periodic(Runnable runnable, void *context, uint32_t period_ms, +void post_delayed(Runnable runnable, void* context, uint32_t delay_ms); +void post_periodic(Runnable runnable, void* context, uint32_t period_ms, uint32_t delay_ms); void remove_runnable(Runnable runnable); void clear_runnables(void); diff --git a/include/keepkey/board/usb.h b/include/keepkey/board/usb.h index 885a5a949..068ac50a8 100644 --- a/include/keepkey/board/usb.h +++ b/include/keepkey/board/usb.h @@ -55,23 +55,23 @@ space for it. */ #define USBD_CONTROL_BUFFER_SIZE 128 -typedef void (*usb_rx_callback_t)(const void *buf, size_t len); -typedef void (*usb_u2f_rx_callback_t)(char tiny, const U2FHID_FRAME *buf); +typedef void (*usb_rx_callback_t)(const void* buf, size_t len); +typedef void (*usb_u2f_rx_callback_t)(char tiny, const U2FHID_FRAME* buf); void usb_set_rx_callback(usb_rx_callback_t callback); void usb_set_u2f_rx_callback(usb_u2f_rx_callback_t callback); char usbTiny(char set); -void usbInit(const char *origin_url); +void usbInit(const char* origin_url); bool usbInitialized(void); void usbPoll(void); typedef struct _usbd_device usbd_device; -usbd_device *get_usb_init_stat(void); +usbd_device* get_usb_init_stat(void); -bool usb_tx(uint8_t *message, uint32_t len); +bool usb_tx(const uint8_t* msg, uint32_t len); #if DEBUG_LINK -bool usb_debug_tx(uint8_t *message, uint32_t len); +bool usb_debug_tx(const uint8_t* msg, uint32_t len); void usb_set_debug_rx_callback(usb_rx_callback_t callback); #endif diff --git a/include/keepkey/board/util.h b/include/keepkey/board/util.h index 17ccdc3c0..5272a41ea 100644 --- a/include/keepkey/board/util.h +++ b/include/keepkey/board/util.h @@ -42,19 +42,19 @@ #define CONCAT(A, B) CONCAT_IMPL(A, B) // converts uint32 to hexa (8 digits) -void uint32hex(uint32_t num, char *str); +void uint32hex(uint32_t num, char* str); // converts data to hexa -void data2hex(const void *data, uint32_t len, char *str); +void data2hex(const void* data, uint32_t len, char* str); // read protobuf integer and advance pointer -uint32_t readprotobufint(uint8_t **ptr); -void rev_byte_order(uint8_t *bfr, size_t len); -void dec64_to_str(uint64_t dec64_val, char *str); +uint32_t readprotobufint(uint8_t** ptr); +void rev_byte_order(uint8_t* bfr, size_t len); +void dec64_to_str(uint64_t dec64_val, char* str); -bool is_valid_ascii(const uint8_t *data, uint32_t size); +bool is_valid_ascii(const uint8_t* data, uint32_t size); -int base_to_precision(uint8_t *dest, const uint8_t *value, +int base_to_precision(uint8_t* dest, const uint8_t* value, const uint8_t dest_len, const uint8_t value_len, const uint8_t precision); diff --git a/include/keepkey/board/variant.h b/include/keepkey/board/variant.h index 1599a7379..fc3748843 100644 --- a/include/keepkey/board/variant.h +++ b/include/keepkey/board/variant.h @@ -13,7 +13,7 @@ typedef struct Image_ { uint16_t w; uint16_t h; uint32_t length; - const uint8_t *data; + const uint8_t* data; } Image; typedef struct AnimationFrame_ { @@ -21,7 +21,7 @@ typedef struct AnimationFrame_ { uint16_t y; uint16_t duration; uint8_t color; - const Image *image; + const Image* image; } AnimationFrame; typedef struct VariantAnimation_ { @@ -31,11 +31,11 @@ typedef struct VariantAnimation_ { typedef struct VariantInfo_ { uint16_t version; - const char *name; - const VariantAnimation *logo; - const VariantAnimation *logo_reversed; + const char* name; + const VariantAnimation* logo; + const VariantAnimation* logo_reversed; uint32_t screensaver_timeout; // DEPRECATED - const VariantAnimation *screensaver; + const VariantAnimation* screensaver; } VariantInfo; typedef struct SignedVariantInfo_ { @@ -54,12 +54,12 @@ Model getModel(void); /// Get the VariantInfo from sector 4 of flash (if it exists), otherwise /// fallback on keepkey imagery. -const VariantInfo *variant_getInfo(void) __attribute__((weak)); +const VariantInfo* variant_getInfo(void) __attribute__((weak)); /// Get the Screensaver. -const VariantAnimation *variant_getScreensaver(void); +const VariantAnimation* variant_getScreensaver(void); /// Get the HomeScreen. -const VariantAnimation *variant_getLogo(bool reverse); +const VariantAnimation* variant_getLogo(bool reversed); #endif diff --git a/include/keepkey/board/webusb_defs.h b/include/keepkey/board/webusb_defs.h index b8566f12c..458fafddc 100644 --- a/include/keepkey/board/webusb_defs.h +++ b/include/keepkey/board/webusb_defs.h @@ -48,11 +48,9 @@ struct webusb_platform_descriptor { // from https://wicg.github.io/webusb/#webusb-platform-capability-descriptor // see also this (for endianness explanation) // https://github.com/WICG/webusb/issues/115#issuecomment-352206549 -#define WEBUSB_UUID \ - { \ - 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, \ - 0x88, 0x15, 0xB6, 0x65 \ - } +#define WEBUSB_UUID \ + {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, \ + 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} struct webusb_url_descriptor { uint8_t bLength; diff --git a/include/keepkey/board/winusb.h b/include/keepkey/board/winusb.h index 0e59a1de3..6e47fb5f3 100644 --- a/include/keepkey/board/winusb.h +++ b/include/keepkey/board/winusb.h @@ -26,7 +26,7 @@ typedef struct _usbd_device usbd_device; // Arbitrary, but must be equivalent to the last character in extra string #define WINUSB_MS_VENDOR_CODE '!' #define WINUSB_EXTRA_STRING \ - { 'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE } + {'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE} extern void winusb_setup(usbd_device* usbd_dev, uint8_t interface); diff --git a/include/keepkey/emulator/emulator.h b/include/keepkey/emulator/emulator.h index a47d74668..1376297d3 100644 --- a/include/keepkey/emulator/emulator.h +++ b/include/keepkey/emulator/emulator.h @@ -23,10 +23,10 @@ #include void emulatorPoll(void); -void emulatorRandom(void *buffer, size_t size); +void emulatorRandom(void* buffer, size_t size); void emulatorSocketInit(void); -size_t emulatorSocketRead(int *iface, void *buffer, size_t size); -size_t emulatorSocketWrite(int iface, const void *buffer, size_t size); +size_t emulatorSocketRead(int* iface, void* buffer, size_t size); +size_t emulatorSocketWrite(int iface, const void* buffer, size_t size); #endif diff --git a/include/keepkey/firmware/app_confirm.h b/include/keepkey/firmware/app_confirm.h index 906e8debc..15fcd7c64 100644 --- a/include/keepkey/firmware/app_confirm.h +++ b/include/keepkey/firmware/app_confirm.h @@ -28,30 +28,30 @@ #define CONFIRM_SIGN_IDENTITY_TITLE 32 #define CONFIRM_SIGN_IDENTITY_BODY 416 -bool confirm_cipher(bool encrypt, const char *key); -bool confirm_encrypt_msg(const char *msg, bool signing); -bool confirm_decrypt_msg(const char *msg, const char *address); +bool confirm_cipher(bool encrypt, const char* key); +bool confirm_encrypt_msg(const char* msg, bool signing); +bool confirm_decrypt_msg(const char* msg, const char* address); bool confirm_transfer_output(ButtonRequestType button_request, - const char *amount, const char *to); + const char* amount, const char* to); bool confirm_transaction_output(ButtonRequestType button_request, - const char *amount, const char *to); + const char* amount, const char* to); bool confirm_transaction_output_no_bold(ButtonRequestType button_request, - const char *amount, const char *to); + const char* amount, const char* to); bool confirm_erc_token_transfer(ButtonRequestType button_request, - const char *msg_body); + const char* msg_body); -bool confirm_transaction(const char *total_amount, const char *fee); +bool confirm_transaction(const char* total_amount, const char* fee); bool confirm_load_device(bool is_node); -bool confirm_address(const char *desc, const char *address); -bool confirm_xpub(const char *node_str, const char *xpub); -bool confirm_sign_identity(const IdentityType *identity, const char *challenge); -bool confirm_cosmos_address(const char *desc, const char *address); -bool confirm_osmosis_address(const char *desc, const char *address); -bool confirm_ethereum_address(const char *desc, const char *address); -bool confirm_nano_address(const char *desc, const char *address); -bool confirm_omni(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size); -bool confirm_data(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size); +bool confirm_address(const char* desc, const char* address); +bool confirm_xpub(const char* node_str, const char* xpub); +bool confirm_sign_identity(const IdentityType* identity, const char* challenge); +bool confirm_cosmos_address(const char* desc, const char* address); +bool confirm_osmosis_address(const char* desc, const char* address); +bool confirm_ethereum_address(const char* desc, const char* address); +bool confirm_nano_address(const char* desc, const char* address); +bool confirm_omni(ButtonRequestType button_request, const char* title, + const uint8_t* data, uint32_t size); +bool confirm_data(ButtonRequestType button_request, const char* title, + const uint8_t* data, uint32_t size); #endif diff --git a/include/keepkey/firmware/app_layout.h b/include/keepkey/firmware/app_layout.h index 8c7eb17e7..fc6d9ca96 100644 --- a/include/keepkey/firmware/app_layout.h +++ b/include/keepkey/firmware/app_layout.h @@ -99,31 +99,31 @@ typedef struct { void layout_screen_test(void); void layout_screensaver(void); -void layout_tx_info(const char *address, uint64_t amount_in_satoshi); -void layout_notification_no_title(const char *title, const char *body, +void layout_tx_info(const char* address, uint64_t amount_in_satoshi); +void layout_notification_no_title(const char* title, const char* body, NotificationType type, bool bold); -void layout_notification_no_title_bold(const char *title, const char *body, +void layout_notification_no_title_bold(const char* title, const char* body, NotificationType type); -void layout_notification_no_title_no_bold(const char *title, const char *body, +void layout_notification_no_title_no_bold(const char* title, const char* body, NotificationType type); -void layout_xpub_notification(const char *desc, const char *xpub, +void layout_xpub_notification(const char* desc, const char* xpub, NotificationType type); -void layout_address_notification(const char *desc, const char *address, +void layout_address_notification(const char* desc, const char* address, NotificationType type); -void layout_cosmos_address_notification(const char *desc, const char *address, +void layout_cosmos_address_notification(const char* desc, const char* address, NotificationType type); -void layout_osmosis_address_notification(const char *desc, const char *address, +void layout_osmosis_address_notification(const char* desc, const char* address, NotificationType type); -void layout_ethereum_address_notification(const char *desc, const char *address, +void layout_ethereum_address_notification(const char* desc, const char* address, NotificationType type); -void layout_nano_address_notification(const char *desc, const char *address, +void layout_nano_address_notification(const char* desc, const char* address, NotificationType type); -void layout_pin(const char *prompt, char *pin); -void layout_cipher(const char *current_word, const char *cipher); -void layout_address(const char *address, QRSize qr_size); +void layout_pin(const char* str, char* pin); +void layout_cipher(const char* current_word, const char* cipher); +void layout_address(const char* address, QRSize qr_size); void set_leaving_handler(leaving_handler_t leaving_func); -void layoutU2FDialog(bool request, const char *title, const char *body, ...) +void layoutU2FDialog(bool request, const char* title, const char* body, ...) __attribute__((format(printf, 3, 4))); #endif diff --git a/include/keepkey/firmware/authenticator.h b/include/keepkey/firmware/authenticator.h index 614d555fc..bb9981f68 100644 --- a/include/keepkey/firmware/authenticator.h +++ b/include/keepkey/firmware/authenticator.h @@ -18,14 +18,18 @@ #ifndef __AUTHENTICATOR_H__ #define __AUTHENTICATOR_H__ -// WARNING: Changing these defines changes the size of authStruct, which in turn changes the secret storage size -// in saved in flash. These value must be coordinated with the size of uint8_t encrypted_sec[] in -// in lib/firmware/storage.h and the storage version must be bumped. -#define DOMAIN_SIZE 12 // allow 11 chars for domain -#define ACCOUNT_SIZE 12 // allow 11 chars for account string -#define AUTHSECRET_SIZE_MAX 20 // 128-bit key len is the recommended minimum, this is room for 160-bit -#define AUTHDATA_SIZE 10 // WARNING: This value must be coordinated with the size of uint8_t encrypted_sec[] in - // in lib/firmware/storage.h and the storage version must be bumped +// WARNING: Changing these defines changes the size of authStruct, which in turn +// changes the secret storage size in saved in flash. These value must be +// coordinated with the size of uint8_t encrypted_sec[] in in +// lib/firmware/storage.h and the storage version must be bumped. +#define DOMAIN_SIZE 12 // allow 11 chars for domain +#define ACCOUNT_SIZE 12 // allow 11 chars for account string +#define AUTHSECRET_SIZE_MAX \ + 20 // 128-bit key len is the recommended minimum, this is room for 160-bit +#define AUTHDATA_SIZE \ + 10 // WARNING: This value must be coordinated with the size of uint8_t + // encrypted_sec[] in in lib/firmware/storage.h and the storage version + // must be bumped enum AUTH_ERR_TYPE { NOERR = 0, @@ -40,7 +44,6 @@ enum AUTH_ERR_TYPE { NUM_AUTHERRS }; - typedef struct _HMAC_SHA1_CTX { uint8_t o_key_pad[SHA1_BLOCK_LENGTH]; SHA1_CTX ctx; @@ -50,23 +53,23 @@ typedef struct _authStruct { char domain[DOMAIN_SIZE]; char account[ACCOUNT_SIZE]; char authSecret[AUTHSECRET_SIZE_MAX]; - uint8_t secretSize; // this is zero if slot is not filled + uint8_t secretSize; // this is zero if slot is not filled } authType; -void hmac_sha1_Init(HMAC_SHA1_CTX *hctx, const uint8_t *key, - const uint32_t keylen); -void hmac_sha1_Update(HMAC_SHA1_CTX *hctx, const uint8_t *msg, - const uint32_t msglen); -void hmac_sha1_Final(HMAC_SHA1_CTX *hctx, uint8_t *hmac); -void hmac_sha1(const uint8_t *key, const uint32_t keylen, const uint8_t *msg, - const uint32_t msglen, uint8_t *hmac); +void hmac_sha1_Init(HMAC_SHA1_CTX* hctx, const uint8_t* key, + const uint32_t keylen); +void hmac_sha1_Update(HMAC_SHA1_CTX* hctx, const uint8_t* msg, + const uint32_t msglen); +void hmac_sha1_Final(HMAC_SHA1_CTX* hctx, uint8_t* hmac); +void hmac_sha1(const uint8_t* key, const uint32_t keylen, const uint8_t* msg, + const uint32_t msglen, uint8_t* hmac); -unsigned generateOTP(char *accountWithMsg, char otpStr[]); -unsigned addAuthAccount(char *accountWithSeed); -unsigned getAuthAccount(char *slotStr, char acc[]); -unsigned removeAuthAccount(char *account); +unsigned generateOTP(char* accountWithMsg, char otpStr[]); +unsigned addAuthAccount(char* accountWithSeed); +unsigned getAuthAccount(const char* slotStr, char acc[]); +unsigned removeAuthAccount(char* domAcc); void wipeAuthData(void); #if DEBUG_LINK -void getAuthSlot(char *authSlotData); +void getAuthSlot(char* authSlotData); #endif #endif diff --git a/include/keepkey/firmware/binance.h b/include/keepkey/firmware/binance.h index dffcad6e6..9f42dcc60 100644 --- a/include/keepkey/firmware/binance.h +++ b/include/keepkey/firmware/binance.h @@ -12,15 +12,15 @@ typedef struct _BinanceTransferMsg BinanceTransferMsg; typedef struct _BinanceTransferMsg_BinanceInputOutput BinanceInputOutput; typedef struct _BinanceTransferMsg_BinanceCoin BinanceCoin; -bool binance_signTxInit(const HDNode *node, const BinanceSignTx *msg); -bool binance_serializeCoin(const BinanceCoin *coin); -bool binance_serializeInputOutput(const BinanceInputOutput *io); -bool binance_signTxUpdateTransfer(const BinanceTransferMsg *msg); -bool binance_signTxUpdateMsgSend(const uint64_t amount, const char *to_address); -bool binance_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool binance_signTxInit(const HDNode* _node, const BinanceSignTx* _msg); +bool binance_serializeCoin(const BinanceCoin* coin); +bool binance_serializeInputOutput(const BinanceInputOutput* io); +bool binance_signTxUpdateTransfer(const BinanceTransferMsg* _msg); +bool binance_signTxUpdateMsgSend(const uint64_t amount, const char* to_address); +bool binance_signTxFinalize(uint8_t* public_key, uint8_t* signature); bool binance_signingIsInited(void); bool binance_signingIsFinished(void); void binance_signAbort(void); -const BinanceSignTx *binance_getBinanceSignTx(void); +const BinanceSignTx* binance_getBinanceSignTx(void); #endif diff --git a/include/keepkey/firmware/coins.def b/include/keepkey/firmware/coins.def index de2643175..5d872061c 100644 --- a/include/keepkey/firmware/coins.def +++ b/include/keepkey/firmware/coins.def @@ -5,6 +5,7 @@ X(true, "Testnet", true, "TEST", true, 111, true, 10000000, true, 19 X(true, "BitcoinCash", true, "BCH", true, 0, true, 500000, true, 5, true, "Bitcoin Signed Message:\n", true, 0x80000091, true, 0, true, 8, false, NO_CONTRACT, true, 76067358, true, false, true, true, true, SECP256K1_STRING, true, "bitcoincash", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Namecoin", true, "NMC", true, 52, true, 10000000, true, 5, true, "Namecoin Signed Message:\n", true, 0x80000007, false, 0, true, 8, false, NO_CONTRACT, true, 27108450, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Litecoin", true, "LTC", true, 48, true, 1000000, true, 50, true, "Litecoin Signed Message:\n", true, 0x80000002, false, 0, true, 8, false, NO_CONTRACT, true, 27108450, true, true, true, false, true, SECP256K1_STRING, false, "", true, "ltc", false, false, true, 28471030, true, 78792518, false, "", true, false ) +X(true, "Lynx", true, "LYNX", true, 45, true, 1000000, true, 22, true, "Lynx Signed Message:\n", true, 0x800000bf, false, 0, true, 8, false, NO_CONTRACT, true, 76067358, true, true, true, false, true, SECP256K1_STRING, false, "", true, "lynx", false, false, true, 77429938, true, 78792518, false, "", true, true ) X(true, "Dogecoin", true, "DOGE", true, 30, true, 1000000000, true, 22, true, "Dogecoin Signed Message:\n", true, 0x80000003, false, 0, true, 8, false, NO_CONTRACT, true, 49990397, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, "Dash", true, "DASH", true, 76, true, 100000, true, 16, true, "DarkCoin Signed Message:\n", true, 0x80000005, false, 0, true, 8, false, NO_CONTRACT, true, 50221772, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "", true, false ) X(true, ETHEREUM, true, "ETH", true, NA, true, 100000, true, NA, true, "Ethereum Signed Message:\n", true, 0x8000003c, true, 1, true, 18, false, NO_CONTRACT, false, 0, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "", true, false ) diff --git a/include/keepkey/firmware/coins.h b/include/keepkey/firmware/coins.h index b94e36cca..f5ede136d 100644 --- a/include/keepkey/firmware/coins.h +++ b/include/keepkey/firmware/coins.h @@ -63,19 +63,18 @@ enum { // SLIP-44 hardened coin type for all Testnet coins #define SLIP44_TESTNET 0x80000001 - extern const CoinType coins[]; -const CoinType *coinByShortcut(const char *shortcut); -const CoinType *coinByName(const char *name); -const CoinType *coinByNameOrTicker(const char *name); -const CoinType *coinByChainAddress(uint8_t chain_id, const uint8_t *address); -const CoinType *coinByAddressType(uint32_t address_type); -const CoinType *coinBySlip44(uint32_t bip44_account_path); -void coin_amnt_to_str(const CoinType *coin, uint64_t amnt, char *buf, int len); +const CoinType* coinByShortcut(const char* shortcut); +const CoinType* coinByName(const char* name); +const CoinType* coinByNameOrTicker(const char* name); +const CoinType* coinByChainAddress(uint8_t chain_id, const uint8_t* address); +const CoinType* coinByAddressType(uint32_t address_type); +const CoinType* coinBySlip44(uint32_t bip44_account_path); +void coin_amnt_to_str(const CoinType* coin, uint64_t amnt, char* buf, int len); /// \brief Parses node path to precise BIP32 equivalent string -bool bip32_path_to_string(char *str, size_t len, const uint32_t *address_n, +bool bip32_path_to_string(char* str, size_t len, const uint32_t* address_n, size_t address_n_count); /** @@ -92,14 +91,14 @@ bool bip32_path_to_string(char *str, size_t len, const uint32_t *address_n, * address indexes \returns true iff the path matches a known * bip44/bip49/bip84/etc account */ -bool bip32_node_to_string(char *node_str, size_t len, const CoinType *coin, - const uint32_t *address_n, size_t address_n_count, +bool bip32_node_to_string(char* node_str, size_t len, const CoinType* coin, + const uint32_t* address_n, size_t address_n_count, bool whole_account, bool show_addridx); /// \returns true iff the coin_name is for an eth-like coin. -bool isEthereumLike(const char *coin_name); +bool isEthereumLike(const char* coin_name); /// \returns true iff the coin_name is for an account-based coin. -bool isAccountBased(const char *coin_name); +bool isAccountBased(const char* coin_name); #endif diff --git a/include/keepkey/firmware/crypto.h b/include/keepkey/firmware/crypto.h index 58a2a5bc6..3ef451ff8 100644 --- a/include/keepkey/firmware/crypto.h +++ b/include/keepkey/firmware/crypto.h @@ -32,26 +32,26 @@ #define ser_length_size(len) ((len) < 253 ? 1 : (len) < 0x10000 ? 3 : 5) -uint32_t ser_length(uint32_t len, uint8_t *out); -uint32_t ser_length_hash(Hasher *hasher, uint32_t len); -uint32_t deser_length(const uint8_t *in, uint32_t *out); +uint32_t ser_length(uint32_t len, uint8_t* out); +uint32_t ser_length_hash(Hasher* hasher, uint32_t len); +uint32_t deser_length(const uint8_t* in, uint32_t* out); -int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, - uint8_t *signature); +int sshMessageSign(HDNode* node, const uint8_t* message, size_t message_len, + uint8_t* signature); -int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, - uint8_t *signature); +int gpgMessageSign(HDNode* node, const uint8_t* message, size_t message_len, + uint8_t* signature); -int cryptoGetECDHSessionKey(const HDNode *node, const uint8_t *peer_public_key, - uint8_t *session_key); +int cryptoGetECDHSessionKey(const HDNode* node, const uint8_t* peer_public_key, + uint8_t* session_key); -int cryptoMessageSign(const CoinType *coin, HDNode *node, - InputScriptType script_type, const uint8_t *message, - size_t message_len, uint8_t *signature); +int cryptoMessageSign(const CoinType* coin, HDNode* node, + InputScriptType script_type, const uint8_t* message, + size_t message_len, uint8_t* signature); -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, - size_t message_len, const char *address, - const uint8_t *signature); +int cryptoMessageVerify(const CoinType* coin, const uint8_t* message, + size_t message_len, const char* address, + const uint8_t* signature); /* ECIES disabled // ECIES: http://memwallet.info/btcmssgs.html @@ -64,13 +64,13 @@ hmac_len, const uint8_t *privkey, uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw); */ -uint8_t *cryptoHDNodePathToPubkey(const CoinType *coin, - const HDNodePathType *hdnodepath); -int cryptoMultisigPubkeyIndex(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - const uint8_t *pubkey); -int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, - uint8_t *hash); -int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash); +uint8_t* cryptoHDNodePathToPubkey(const CoinType* coin, + const HDNodePathType* hdnodepath); +int cryptoMultisigPubkeyIndex(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + const uint8_t* pubkey); +int cryptoMultisigFingerprint(const MultisigRedeemScriptType* multisig, + uint8_t* hash); +int cryptoIdentityFingerprint(const IdentityType* identity, uint8_t* hash); #endif diff --git a/include/keepkey/firmware/eip712.h b/include/keepkey/firmware/eip712.h index c93866933..686acd6de 100644 --- a/include/keepkey/firmware/eip712.h +++ b/include/keepkey/firmware/eip712.h @@ -16,18 +16,17 @@ * along with this library. If not, see . */ - -/* - Produces hashes based on the metamask v4 rules. This is different from the EIP-712 spec - in how arrays of structs are hashed but is compatable with metamask. - See https://github.com/MetaMask/eth-sig-util/pull/107 +/* + Produces hashes based on the metamask v4 rules. This is different from the + EIP-712 spec in how arrays of structs are hashed but is compatable with + metamask. See https://github.com/MetaMask/eth-sig-util/pull/107 eip712 data rules: Parser wants to see C strings, not javascript strings: - requires all complete json message strings to be enclosed by braces, i.e., { ... } - Cannot have entire json string quoted, i.e., "{ ... }" will not work. - Remove all quote escape chars, e.g., {"types": not {\"types\": - int values must be hex. Negative sign indicates negative value, e.g., -5, -8a67 + requires all complete json message strings to be enclosed by braces, + i.e., { ... } Cannot have entire json string quoted, i.e., "{ ... }" will not + work. Remove all quote escape chars, e.g., {"types": not {\"types\": int + values must be hex. Negative sign indicates negative value, e.g., -5, -8a67 Note: Do not prefix ints or uints with 0x All hex and byte strings must be big-endian Byte strings and address should be prefixed by 0x @@ -38,71 +37,68 @@ #include "keepkey/firmware/tiny-json.h" #define USE_KECCAK 1 -#define ADDRESS_SIZE 42 -#define JSON_OBJ_POOL_SIZE 100 -#define STRBUFSIZE 511 -#define MAX_USERDEF_TYPES 10 // This is max number of user defined type allowed -#define MAX_TYPESTRING 33 // maximum size for a type string -#define MAX_ENCBYTEN_SIZE 66 +#define ADDRESS_SIZE 42 +#define JSON_OBJ_POOL_SIZE 100 +#define STRBUFSIZE 511 +#define MAX_USERDEF_TYPES 10 // This is max number of user defined type allowed +#define MAX_TYPESTRING 33 // maximum size for a type string +#define MAX_ENCBYTEN_SIZE 66 typedef enum { - NOT_ENCODABLE = 0, - ADDRESS, - STRING, - UINT, - INT, - BYTES, - BYTES_N, - BOOL, - UDEF_TYPE, - PREV_USERDEF, - TOO_MANY_UDEFS + NOT_ENCODABLE = 0, + ADDRESS, + STRING, + UINT, + INT, + BYTES, + BYTES_N, + BOOL, + UDEF_TYPE, + PREV_USERDEF, + TOO_MANY_UDEFS } basicType; -typedef enum { - DOMAIN = 1, - MESSAGE -} dm; +typedef enum { DOMAIN = 1, MESSAGE } dm; // error list status -#define SUCCESS 1 -#define NULL_MSG_HASH 2 // this is legal, not an error -#define GENERAL_ERROR 3 -#define UDEF_NAME_ERROR 4 -#define UDEFS_OVERFLOW 5 -#define UDEF_ARRAY_NAME_ERR 6 -#define ADDR_STRING_VFLOW 7 -#define BYTESN_STRING_ERROR 8 -#define BYTESN_SIZE_ERROR 9 -#define INT_ARRAY_ERROR 10 -#define BYTESN_ARRAY_ERROR 11 -#define BOOL_ARRAY_ERROR 12 +#define SUCCESS 1 +#define NULL_MSG_HASH 2 // this is legal, not an error +#define GENERAL_ERROR 3 +#define UDEF_NAME_ERROR 4 +#define UDEFS_OVERFLOW 5 +#define UDEF_ARRAY_NAME_ERR 6 +#define ADDR_STRING_VFLOW 7 +#define BYTESN_STRING_ERROR 8 +#define BYTESN_SIZE_ERROR 9 +#define INT_ARRAY_ERROR 10 +#define BYTESN_ARRAY_ERROR 11 +#define BOOL_ARRAY_ERROR 12 // #define STACK_TOO_SMALL 13 // reserved - defined in memory.h -#define JSON_PTYPENAMEERR 14 -#define JSON_PTYPEVALERR 15 -#define JSON_TYPESPROPERR 16 -#define JSON_TYPE_SPROPERR 17 -#define JSON_DPROPERR 18 -#define MSG_NO_DS 19 -#define JSON_MPROPERR 20 -#define JSON_PTYPESOBJERR 21 -#define JSON_TYPE_S_ERR 22 +#define JSON_PTYPENAMEERR 14 +#define JSON_PTYPEVALERR 15 +#define JSON_TYPESPROPERR 16 +#define JSON_TYPE_SPROPERR 17 +#define JSON_DPROPERR 18 +#define MSG_NO_DS 19 +#define JSON_MPROPERR 20 +#define JSON_PTYPESOBJERR 21 +#define JSON_TYPE_S_ERR 22 #define JSON_TYPE_S_NAMEERR 23 -#define UNUSED_ERR_2 24 // available for re-use -#define JSON_NO_PAIRS 25 -#define JSON_PAIRS_NOTEXT 26 -#define JSON_NO_PAIRS_SIB 27 -#define TYPE_NOT_ENCODABLE 28 -#define JSON_NOPAIRVAL 29 -#define JSON_NOPAIRNAME 30 -#define JSON_TYPE_T_NOVAL 31 -#define ADDR_STRING_NULL 32 -#define JSON_TYPE_WNOVAL 33 +#define UNUSED_ERR_2 24 // available for re-use +#define JSON_NO_PAIRS 25 +#define JSON_PAIRS_NOTEXT 26 +#define JSON_NO_PAIRS_SIB 27 +#define TYPE_NOT_ENCODABLE 28 +#define JSON_NOPAIRVAL 29 +#define JSON_NOPAIRNAME 30 +#define JSON_TYPE_T_NOVAL 31 +#define ADDR_STRING_NULL 32 +#define JSON_TYPE_WNOVAL 33 -#define LAST_ERROR JSON_TYPE_WNOVAL +#define LAST_ERROR JSON_TYPE_WNOVAL -int encode(const json_t *jsonTypes, const json_t *jsonVals, const char *typeS, uint8_t *hashRet); +int encode(const json_t* jsonTypes, const json_t* jsonVals, const char* typeS, + uint8_t* hashRet); #endif - diff --git a/include/keepkey/firmware/eos-contracts/eosio.system.h b/include/keepkey/firmware/eos-contracts/eosio.system.h index 1065f24e2..6dc64da55 100644 --- a/include/keepkey/firmware/eos-contracts/eosio.system.h +++ b/include/keepkey/firmware/eos-contracts/eosio.system.h @@ -40,54 +40,54 @@ typedef struct _EosActionVoteProducer EosActionVoteProducer; typedef struct _EosAuthorization EosAuthorization; /// \returns true iff successful. -bool eos_compileActionDelegate(const EosActionCommon *common, - const EosActionDelegate *action); +bool eos_compileActionDelegate(const EosActionCommon* common, + const EosActionDelegate* action); /// \returns true iff successful. -bool eos_compileActionUndelegate(const EosActionCommon *common, - const EosActionUndelegate *action); +bool eos_compileActionUndelegate(const EosActionCommon* common, + const EosActionUndelegate* action); /// \returns true iff successful. -bool eos_compileActionRefund(const EosActionCommon *common, - const EosActionRefund *action); +bool eos_compileActionRefund(const EosActionCommon* common, + const EosActionRefund* action); /// \returns true iff successful. -bool eos_compileActionBuyRam(const EosActionCommon *common, - const EosActionBuyRam *action); +bool eos_compileActionBuyRam(const EosActionCommon* common, + const EosActionBuyRam* action); /// \returns true iff successful. -bool eos_compileActionBuyRamBytes(const EosActionCommon *common, - const EosActionBuyRamBytes *action); +bool eos_compileActionBuyRamBytes(const EosActionCommon* common, + const EosActionBuyRamBytes* action); /// \returns true iff successful. -bool eos_compileActionSellRam(const EosActionCommon *common, - const EosActionSellRam *action); +bool eos_compileActionSellRam(const EosActionCommon* common, + const EosActionSellRam* action); /// \returns true iff successful. -bool eos_compileActionVoteProducer(const EosActionCommon *common, - const EosActionVoteProducer *action); +bool eos_compileActionVoteProducer(const EosActionCommon* common, + const EosActionVoteProducer* action); /// \returns true iff successful. -bool eos_compileAuthorization(const char *title, const EosAuthorization *auth); +bool eos_compileAuthorization(const char* title, const EosAuthorization* auth); /// \returns true iff successful. -bool eos_compileActionUpdateAuth(const EosActionCommon *common, - const EosActionUpdateAuth *action); +bool eos_compileActionUpdateAuth(const EosActionCommon* common, + const EosActionUpdateAuth* action); /// \returns true iff successful. -bool eos_compileActionDeleteAuth(const EosActionCommon *common, - const EosActionDeleteAuth *action); +bool eos_compileActionDeleteAuth(const EosActionCommon* common, + const EosActionDeleteAuth* action); /// \returns true iff successful. -bool eos_compileActionLinkAuth(const EosActionCommon *common, - const EosActionLinkAuth *action); +bool eos_compileActionLinkAuth(const EosActionCommon* common, + const EosActionLinkAuth* action); /// \returns true iff successful. -bool eos_compileActionUnlinkAuth(const EosActionCommon *common, - const EosActionUnlinkAuth *action); +bool eos_compileActionUnlinkAuth(const EosActionCommon* common, + const EosActionUnlinkAuth* action); /// \returns true iff successful. -bool eos_compileActionNewAccount(const EosActionCommon *common, - const EosActionNewAccount *action); +bool eos_compileActionNewAccount(const EosActionCommon* common, + const EosActionNewAccount* action); #endif diff --git a/include/keepkey/firmware/eos-contracts/eosio.token.h b/include/keepkey/firmware/eos-contracts/eosio.token.h index 32437f0d0..4ea3fa7d5 100644 --- a/include/keepkey/firmware/eos-contracts/eosio.token.h +++ b/include/keepkey/firmware/eos-contracts/eosio.token.h @@ -26,7 +26,7 @@ typedef struct _EosActionCommon EosActionCommon; typedef struct _EosActionTransfer EosActionTransfer; /// \returns true iff successful. -bool eos_compileActionTransfer(const EosActionCommon *common, - const EosActionTransfer *action); +bool eos_compileActionTransfer(const EosActionCommon* common, + const EosActionTransfer* action); #endif diff --git a/include/keepkey/firmware/eos.h b/include/keepkey/firmware/eos.h index b905fb162..3f925c9b8 100644 --- a/include/keepkey/firmware/eos.h +++ b/include/keepkey/firmware/eos.h @@ -55,25 +55,25 @@ typedef enum _EosContractName { } EosContractName; /// \returns true iff the asset can be correctly decoded. -bool eos_formatAsset(const EosAsset *asset, char str[EOS_ASSET_STR_SIZE]); +bool eos_formatAsset(const EosAsset* asset, char str[EOS_ASSET_STR_SIZE]); /// \returns true iff the name can be correctly decoded. bool eos_formatName(uint64_t name, char str[EOS_NAME_STR_SIZE]); /// \returns true iff successful. -bool eos_derivePublicKey(const uint32_t *addr_n, size_t addr_n_count, - uint8_t *public_key, size_t len); +bool eos_derivePublicKey(const uint32_t* addr_n, size_t addr_n_count, + uint8_t* public_key, size_t len); /// \returns true iff successful. -bool eos_getPublicKey(const HDNode *node, const curve_info *curve, - EosPublicKeyKind kind, char *str, size_t len); +bool eos_getPublicKey(const HDNode* n, const curve_info* curve, + EosPublicKeyKind kind, char* pubkey, size_t len); /// \returns true iff successful. -bool eos_publicKeyToWif(const uint8_t *public_key, EosPublicKeyKind kind, - char *pubkey, size_t len); +bool eos_publicKeyToWif(const uint8_t* public_key, EosPublicKeyKind kind, + char* pubkey, size_t len); -void eos_signingInit(const uint8_t *chain_id, uint32_t num_actions, - const EosTxHeader *_header, const HDNode *_root, +void eos_signingInit(const uint8_t* chain_id, uint32_t num_actions, + const EosTxHeader* _header, const HDNode* _root, const uint32_t _address_n[8], size_t _address_n_count); bool eos_signingIsInited(void); @@ -87,24 +87,24 @@ uint32_t eos_actionsRemaining(void); bool eos_hasActionUnknownDataRemaining(void); /// \returns true iff successful. -bool eos_compileActionUnknown(const EosActionCommon *common, - const EosActionUnknown *action); +bool eos_compileActionUnknown(const EosActionCommon* common, + const EosActionUnknown* action); /// \brief Append a u64 to the hash, with variable length encoding. /// \param hasher If nonnull, the hasher to append to. /// \param val The value to append. /// \returns the number of bytes hashed. -size_t eos_hashUInt(Hasher *hasher, uint64_t val); +size_t eos_hashUInt(Hasher* hasher, uint64_t val); /// \returns true iff successful. -bool eos_compileAsset(const EosAsset *asset); +bool eos_compileAsset(const EosAsset* asset); /// \returns true iff successful. -bool eos_compileActionCommon(const EosActionCommon *common); +bool eos_compileActionCommon(const EosActionCommon* common); /// \returns true iff successful. -bool eos_compilePermissionLevel(const EosPermissionLevel *auth); +bool eos_compilePermissionLevel(const EosPermissionLevel* auth); -bool eos_signTx(EosSignedTx *sig); +bool eos_signTx(EosSignedTx* tx); #endif diff --git a/include/keepkey/firmware/ethereum.h b/include/keepkey/firmware/ethereum.h index 289d10da6..77c8f4775 100644 --- a/include/keepkey/firmware/ethereum.h +++ b/include/keepkey/firmware/ethereum.h @@ -33,22 +33,22 @@ typedef struct _EthereumMessageSignature EthereumMessageSignature; typedef struct _TokenType TokenType; typedef struct _CoinType CoinType; -void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, +void ethereum_signing_init(EthereumSignTx* msg, const HDNode* node, bool needs_confirm); void ethereum_signing_abort(void); -void ethereum_signing_txack(EthereumTxAck *msg); -void format_ethereum_address(const uint8_t *to, char *destination_str, +void ethereum_signing_txack(EthereumTxAck* tx); +void format_ethereum_address(const uint8_t* to, char* destination_str, uint32_t destination_str_len); -bool ethereum_isStandardERC20Transfer(const EthereumSignTx *msg); +bool ethereum_isStandardERC20Transfer(const EthereumSignTx* msg); /// \pre requires that `ethereum_isStandardERC20Transfer(msg)` /// \returns true iff successful -bool ethereum_getStandardERC20Recipient(const EthereumSignTx *msg, - char *address, size_t len); +bool ethereum_getStandardERC20Recipient(const EthereumSignTx* msg, + char* address, size_t len); /// \pre requires that `ethereum_isStandardERC20Transfer(msg)` /// \returns true iff successful -bool ethereum_getStandardERC20Coin(const EthereumSignTx *msg, CoinType *coin); +bool ethereum_getStandardERC20Coin(const EthereumSignTx* msg, CoinType* coin); /** * \brief Get the number of decimals associated with an erc20 token @@ -56,23 +56,23 @@ bool ethereum_getStandardERC20Coin(const EthereumSignTx *msg, CoinType *coin); * table in coins.c \returns uint32_t The number of decimals to interpret * the token with */ -uint32_t ethereum_get_decimal(const char *token_shortcut); +uint32_t ethereum_get_decimal(const char* token_shortcut); -void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, - EthereumMessageSignature *resp); -int ethereum_message_verify(const EthereumVerifyMessage *msg); +void ethereum_message_sign(const EthereumSignMessage* msg, const HDNode* node, + EthereumMessageSignature* resp); +int ethereum_message_verify(const EthereumVerifyMessage* msg); -void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, - uint32_t chain_id, char *buf, int buflen); +void ethereumFormatAmount(const bignum256* amnt, const TokenType* token, + uint32_t cid, char* buf, int buflen); -void bn_from_bytes(const uint8_t *value, size_t value_len, bignum256 *val); +void bn_from_bytes(const uint8_t* value, size_t value_len, bignum256* val); - -void ethereum_typed_hash_sign(const EthereumSignTypedHash *msg, - const HDNode *node, - EthereumTypedDataSignature *resp); -bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n, +void ethereum_typed_hash_sign(const EthereumSignTypedHash* msg, + const HDNode* node, + EthereumTypedDataSignature* resp); +bool ethereum_path_check(uint32_t address_n_count, const uint32_t* address_n, bool pubkey_export, uint64_t chain); -void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature *resp, const HDNode *node); +void e712_types_values(Ethereum712TypesValues* msg, + EthereumTypedDataSignature* resp, const HDNode* node); #endif diff --git a/include/keepkey/firmware/ethereum_contracts.h b/include/keepkey/firmware/ethereum_contracts.h index 78fe1c6c9..1d11ca614 100644 --- a/include/keepkey/firmware/ethereum_contracts.h +++ b/include/keepkey/firmware/ethereum_contracts.h @@ -26,11 +26,11 @@ typedef struct _EthereumSignTx EthereumSignTx; /// \returns true iff there is custom support for this ETH signing request -bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg, - const HDNode *node); +bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx* msg, + const HDNode* node); /// \pre requires that `ethereum_contractHandled(msg)` /// \return true iff the user has confirmed the custom ETH signing request -bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx *msg, - const HDNode *node); +bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx* msg, + const HDNode* node); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/makerdao.h b/include/keepkey/firmware/ethereum_contracts/makerdao.h index 8d9f37f0d..f041a421a 100644 --- a/include/keepkey/firmware/ethereum_contracts/makerdao.h +++ b/include/keepkey/firmware/ethereum_contracts/makerdao.h @@ -25,32 +25,32 @@ typedef struct _EthereumSignTx EthereumSignTx; -bool makerdao_isOasisDEXAddress(const uint8_t *address, uint32_t chain_id); +bool makerdao_isOasisDEXAddress(const uint8_t* address, uint32_t chain_id); -bool makerdao_isOpen(const EthereumSignTx *msg); -bool makerdao_confirmOpen(const EthereumSignTx *msg); -bool makerdao_isClose(const EthereumSignTx *msg); -bool makerdao_confirmClose(const EthereumSignTx *msg); -bool makerdao_isGive(const EthereumSignTx *msg); -bool makerdao_confirmGive(const EthereumSignTx *msg); -bool makerdao_isLockAndDraw2(const EthereumSignTx *msg); -bool makerdao_confirmLockAndDraw2(const EthereumSignTx *msg); -bool makerdao_isCreateOpenLockAndDraw(const EthereumSignTx *msg); -bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx *msg); -bool makerdao_isLock(const EthereumSignTx *msg); -bool makerdao_confirmLock(const EthereumSignTx *msg); -bool makerdao_isDraw(const EthereumSignTx *msg); -bool makerdao_confirmDraw(const EthereumSignTx *msg); -bool makerdao_isLockAndDraw3(const EthereumSignTx *msg); -bool makerdao_confirmLockAndDraw3(const EthereumSignTx *msg); -bool makerdao_isFree(const EthereumSignTx *msg); -bool makerdao_confirmFree(const EthereumSignTx *msg); -bool makerdao_isWipe(const EthereumSignTx *msg); -bool makerdao_confirmWipe(const EthereumSignTx *msg); -bool makerdao_isWipeAndFree(const EthereumSignTx *msg); -bool makerdao_confirmWipeAndFree(const EthereumSignTx *msg); +bool makerdao_isOpen(const EthereumSignTx* msg); +bool makerdao_confirmOpen(const EthereumSignTx* msg); +bool makerdao_isClose(const EthereumSignTx* msg); +bool makerdao_confirmClose(const EthereumSignTx* msg); +bool makerdao_isGive(const EthereumSignTx* msg); +bool makerdao_confirmGive(const EthereumSignTx* msg); +bool makerdao_isLockAndDraw2(const EthereumSignTx* msg); +bool makerdao_confirmLockAndDraw2(const EthereumSignTx* msg); +bool makerdao_isCreateOpenLockAndDraw(const EthereumSignTx* msg); +bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx* msg); +bool makerdao_isLock(const EthereumSignTx* msg); +bool makerdao_confirmLock(const EthereumSignTx* msg); +bool makerdao_isDraw(const EthereumSignTx* msg); +bool makerdao_confirmDraw(const EthereumSignTx* msg); +bool makerdao_isLockAndDraw3(const EthereumSignTx* msg); +bool makerdao_confirmLockAndDraw3(const EthereumSignTx* msg); +bool makerdao_isFree(const EthereumSignTx* msg); +bool makerdao_confirmFree(const EthereumSignTx* msg); +bool makerdao_isWipe(const EthereumSignTx* msg); +bool makerdao_confirmWipe(const EthereumSignTx* msg); +bool makerdao_isWipeAndFree(const EthereumSignTx* msg); +bool makerdao_confirmWipeAndFree(const EthereumSignTx* msg); -bool makerdao_isMakerDAO(uint32_t data_total, const EthereumSignTx *msg); -bool makerdao_confirmMakerDAO(uint32_t data_total, const EthereumSignTx *msg); +bool makerdao_isMakerDAO(uint32_t data_total, const EthereumSignTx* msg); +bool makerdao_confirmMakerDAO(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/saproxy.h b/include/keepkey/firmware/ethereum_contracts/saproxy.h index 293a2cd44..23b02a216 100644 --- a/include/keepkey/firmware/ethereum_contracts/saproxy.h +++ b/include/keepkey/firmware/ethereum_contracts/saproxy.h @@ -23,11 +23,14 @@ #include #include -#define SAPROXY_ADDRESS "\xbd\x6a\x40\xbb\x90\x4a\xea\x5a\x49\xc5\x90\x50\xb5\x39\x5f\x74\x84\xa4\x20\x3d" - +#define SAPROXY_ADDRESS \ + "\xbd\x6a\x40\xbb\x90\x4a\xea\x5a\x49\xc5\x90\x50\xb5\x39\x5f\x74\x84\xa4" \ + "\x20\x3d" + typedef struct _EthereumSignTx EthereumSignTx; -bool sa_isWithdrawFromSalary(const EthereumSignTx *msg); -bool sa_confirmWithdrawFromSalary(uint32_t data_total, const EthereumSignTx *msg); +bool sa_isWithdrawFromSalary(const EthereumSignTx* msg); +bool sa_confirmWithdrawFromSalary(uint32_t data_total, + const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/thortx.h b/include/keepkey/firmware/ethereum_contracts/thortx.h index f21a0061f..8f61c27f3 100644 --- a/include/keepkey/firmware/ethereum_contracts/thortx.h +++ b/include/keepkey/firmware/ethereum_contracts/thortx.h @@ -23,15 +23,26 @@ #include #include -#define ETH_ADDRESS "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" -#define ETH_NATIVE "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee" +#define ETH_ADDRESS \ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ + "\x00\x00" +#define ETH_NATIVE \ + "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee" \ + "\xee\xee" #define THOR_ROUTER "42a5ed456650a09dc10ebc6361a7480fdd61f27b" -typedef struct _EthereumSignTx EthereumSignTx; +/* deposit(address,address,uint256,string) — legacy selector */ +#define THOR_SELECTOR_DEPOSIT "\x1f\xec\xe7\xb4" +/* depositWithExpiry(address,address,uint256,string,uint256) — current selector + */ +#define THOR_SELECTOR_DEPOSIT_WITH_EXPIRY "\x44\xbc\x93\x7b" -bool thor_isThorchainTx(const EthereumSignTx *msg); -bool thor_confirmThorTx(uint32_t data_total, const EthereumSignTx *msg); +typedef struct _EthereumSignTx EthereumSignTx; +bool thor_has_deposit_selector(const EthereumSignTx* msg); +bool thor_is_expiry_variant(const EthereumSignTx* msg); +bool thor_isThorchainTx(const EthereumSignTx* msg); +bool thor_confirmThorTx(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/zxappliquid.h b/include/keepkey/firmware/ethereum_contracts/zxappliquid.h index 0290b9e9f..429ed26e9 100644 --- a/include/keepkey/firmware/ethereum_contracts/zxappliquid.h +++ b/include/keepkey/firmware/ethereum_contracts/zxappliquid.h @@ -23,11 +23,13 @@ #include #include -#define MAX_ALLOWANCE "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +#define MAX_ALLOWANCE \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" typedef struct _EthereumSignTx EthereumSignTx; -bool zx_isZxApproveLiquid(const EthereumSignTx *msg); -bool zx_confirmApproveLiquidity(uint32_t data_total, const EthereumSignTx *msg); +bool zx_isZxApproveLiquid(const EthereumSignTx* msg); +bool zx_confirmApproveLiquidity(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/zxliquidtx.h b/include/keepkey/firmware/ethereum_contracts/zxliquidtx.h index 50a61476d..6287da2bd 100644 --- a/include/keepkey/firmware/ethereum_contracts/zxliquidtx.h +++ b/include/keepkey/firmware/ethereum_contracts/zxliquidtx.h @@ -23,11 +23,13 @@ #include #include -#define UNISWAP_ROUTER_ADDRESS "\x7a\x25\x0d\x56\x30\xB4\xcF\x53\x97\x39\xdF\x2C\x5d\xAc\xb4\xc6\x59\xF2\x48\x8D" +#define UNISWAP_ROUTER_ADDRESS \ + "\x7a\x25\x0d\x56\x30\xB4\xcF\x53\x97\x39\xdF\x2C\x5d\xAc\xb4\xc6\x59\xF2" \ + "\x48\x8D" typedef struct _EthereumSignTx EthereumSignTx; -bool zx_isZxLiquidTx(const EthereumSignTx *msg); -bool zx_confirmZxLiquidTx(uint32_t data_total, const EthereumSignTx *msg); +bool zx_isZxLiquidTx(const EthereumSignTx* msg); +bool zx_confirmZxLiquidTx(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/zxswap.h b/include/keepkey/firmware/ethereum_contracts/zxswap.h index c7b945646..41777e2b2 100644 --- a/include/keepkey/firmware/ethereum_contracts/zxswap.h +++ b/include/keepkey/firmware/ethereum_contracts/zxswap.h @@ -23,11 +23,13 @@ #include #include -#define ZXSWAP_ADDRESS "\xde\xf1\xc0\xde\xd9\xbe\xc7\xf1\xa1\x67\x08\x19\x83\x32\x40\xf0\x27\xb2\x5e\xff" +#define ZXSWAP_ADDRESS \ + "\xde\xf1\xc0\xde\xd9\xbe\xc7\xf1\xa1\x67\x08\x19\x83\x32\x40\xf0\x27\xb2" \ + "\x5e\xff" typedef struct _EthereumSignTx EthereumSignTx; -bool zx_isZxSwap(const EthereumSignTx *msg); -bool zx_confirmZxSwap(uint32_t data_total, const EthereumSignTx *msg); +bool zx_isZxSwap(const EthereumSignTx* msg); +bool zx_confirmZxSwap(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_contracts/zxtransERC20.h b/include/keepkey/firmware/ethereum_contracts/zxtransERC20.h index 2f408447d..0272e09bf 100644 --- a/include/keepkey/firmware/ethereum_contracts/zxtransERC20.h +++ b/include/keepkey/firmware/ethereum_contracts/zxtransERC20.h @@ -23,11 +23,13 @@ #include #include -#define ZXSWAP_ADDRESS "\xde\xf1\xc0\xde\xd9\xbe\xc7\xf1\xa1\x67\x08\x19\x83\x32\x40\xf0\x27\xb2\x5e\xff" +#define ZXSWAP_ADDRESS \ + "\xde\xf1\xc0\xde\xd9\xbe\xc7\xf1\xa1\x67\x08\x19\x83\x32\x40\xf0\x27\xb2" \ + "\x5e\xff" typedef struct _EthereumSignTx EthereumSignTx; -bool zx_isZxTransformERC20(const EthereumSignTx *msg); -bool zx_confirmZxTransERC20(uint32_t data_total, const EthereumSignTx *msg); +bool zx_isZxTransformERC20(const EthereumSignTx* msg); +bool zx_confirmZxTransERC20(uint32_t data_total, const EthereumSignTx* msg); #endif diff --git a/include/keepkey/firmware/ethereum_tokens.h b/include/keepkey/firmware/ethereum_tokens.h index ad7683ba8..7e26bd164 100644 --- a/include/keepkey/firmware/ethereum_tokens.h +++ b/include/keepkey/firmware/ethereum_tokens.h @@ -37,8 +37,8 @@ enum { #define TOKENS_COUNT ((int)TokenIndexLast - (int)TokenIndexFirst) typedef struct _TokenType { - const char *const address; - const char *const ticker; + const char* const address; + const char* const ticker; uint8_t chain_id; uint8_t decimals; } TokenType; @@ -47,11 +47,11 @@ typedef struct _CoinType CoinType; extern const TokenType tokens[]; -extern const TokenType *UnknownToken; +extern const TokenType* UnknownToken; -const TokenType *tokenIter(int32_t *ctr); +const TokenType* tokenIter(int32_t* ctr); -const TokenType *tokenByChainAddress(uint8_t chain_id, const uint8_t *address); +const TokenType* tokenByChainAddress(uint8_t chain_id, const uint8_t* address); /// Tokens don't have unique tickers, so this might not return the one you're /// looking for :/ @@ -64,8 +64,8 @@ const TokenType *tokenByChainAddress(uint8_t chain_id, const uint8_t *address); /// \param[out] token The found token, assuming it was uniquely determinable. /// \returns true iff the token can be uniquely found in the list of known /// tokens. -bool tokenByTicker(uint8_t chain_id, const char *ticker, - const TokenType **token); +bool tokenByTicker(uint8_t chain_id, const char* ticker, + const TokenType** token); -void coinFromToken(CoinType *coin, const TokenType *token); +void coinFromToken(CoinType* coin, const TokenType* token); #endif diff --git a/include/keepkey/firmware/fsm.h b/include/keepkey/firmware/fsm.h index 19e911106..ca80eb7db 100644 --- a/include/keepkey/firmware/fsm.h +++ b/include/keepkey/firmware/fsm.h @@ -24,11 +24,11 @@ #include "keepkey/board/messages.h" #define RESP_INIT(TYPE) \ - TYPE *resp = (TYPE *)msg_resp; \ + TYPE* resp = (TYPE*)msg_resp; \ _Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \ memset(resp, 0, sizeof(TYPE)); -#define ENTROPY_BUF sizeof(((Entropy *)NULL)->entropy.bytes) +#define ENTROPY_BUF sizeof(((Entropy*)NULL)->entropy.bytes) #define BTC_ADDRESS_SIZE 35 #define RAW_TX_ACK_VARINT_COUNT 4 @@ -38,95 +38,103 @@ void fsm_init(void); -void fsm_sendSuccess(const char *text); - -void fsm_sendFailure(FailureType code, const char *text); - -void fsm_msgInitialize(Initialize *msg); -void fsm_msgGetFeatures(GetFeatures *msg); -void fsm_msgGetCoinTable(GetCoinTable *msg); -void fsm_msgPing(Ping *msg); -void fsm_msgChangePin(ChangePin *msg); -void fsm_msgChangeWipeCode(ChangeWipeCode *msg); -void fsm_msgWipeDevice(WipeDevice *msg); -void fsm_msgFirmwareErase(FirmwareErase *msg); -void fsm_msgFirmwareUpload(FirmwareUpload *msg); -void fsm_msgGetEntropy(GetEntropy *msg); -void fsm_msgGetPublicKey(GetPublicKey *msg); -void fsm_msgLoadDevice(LoadDevice *msg); -void fsm_msgResetDevice(ResetDevice *msg); -void fsm_msgSignTx(SignTx *msg); +void fsm_sendSuccess(const char* text); + +void fsm_sendFailure(FailureType code, const char* text); + +void fsm_msgInitialize(Initialize* msg); +void fsm_msgGetFeatures(GetFeatures* msg); +void fsm_msgGetCoinTable(GetCoinTable* msg); +void fsm_msgPing(Ping* msg); +void fsm_msgChangePin(ChangePin* msg); +void fsm_msgChangeWipeCode(ChangeWipeCode* msg); +void fsm_msgWipeDevice(WipeDevice* msg); +void fsm_msgFirmwareErase(FirmwareErase* msg); +void fsm_msgFirmwareUpload(FirmwareUpload* msg); +void fsm_msgGetEntropy(GetEntropy* msg); +void fsm_msgGetPublicKey(GetPublicKey* msg); +void fsm_msgLoadDevice(LoadDevice* msg); +void fsm_msgResetDevice(ResetDevice* msg); +void fsm_msgSignTx(SignTx* msg); // void fsm_msgPinMatrixAck(PinMatrixAck *msg); -void fsm_msgCancel(Cancel *msg); -void fsm_msgTxAck(TxAck *msg); -void fsm_msgCipherKeyValue(CipherKeyValue *msg); -void fsm_msgClearSession(ClearSession *msg); -void fsm_msgApplySettings(ApplySettings *msg); +void fsm_msgCancel(Cancel* msg); +void fsm_msgTxAck(TxAck* msg); +void fsm_msgCipherKeyValue(CipherKeyValue* msg); +void fsm_msgClearSession(ClearSession* msg); +void fsm_msgApplySettings(ApplySettings* msg); // void fsm_msgButtonAck(ButtonAck *msg); -void fsm_msgGetAddress(GetAddress *msg); -void fsm_msgEntropyAck(EntropyAck *msg); -void fsm_msgSignMessage(SignMessage *msg); -void fsm_msgVerifyMessage(VerifyMessage *msg); -void fsm_msgSignIdentity(SignIdentity *msg); -void fsm_msgEncryptMessage(EncryptMessage *msg); -void fsm_msgDecryptMessage(DecryptMessage *msg); +void fsm_msgGetAddress(GetAddress* msg); +void fsm_msgEntropyAck(EntropyAck* msg); +void fsm_msgSignMessage(SignMessage* msg); +void fsm_msgVerifyMessage(VerifyMessage* msg); +void fsm_msgSignIdentity(SignIdentity* msg); +void fsm_msgEncryptMessage(EncryptMessage* msg); +void fsm_msgDecryptMessage(DecryptMessage* msg); // void fsm_msgPassphraseAck(PassphraseAck *msg); -void fsm_msgRecoveryDevice(RecoveryDevice *msg); -void fsm_msgWordAck(WordAck *msg); +void fsm_msgRecoveryDevice(RecoveryDevice* msg); +void fsm_msgWordAck(WordAck* msg); -void fsm_msgCharacterAck(CharacterAck *msg); -void fsm_msgApplyPolicies(ApplyPolicies *msg); +void fsm_msgCharacterAck(CharacterAck* msg); +void fsm_msgApplyPolicies(ApplyPolicies* msg); // ethereum -void fsm_msgEthereumGetAddress(EthereumGetAddress *msg); -void fsm_msgEthereumSignTx(EthereumSignTx *msg); -void fsm_msgEthereumTxAck(EthereumTxAck *msg); -void fsm_msgEthereumSignMessage(EthereumSignMessage *msg); -void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg); -void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg); -void fsm_msgEthereum712TypesValues(Ethereum712TypesValues *msg); +void fsm_msgEthereumGetAddress(EthereumGetAddress* msg); +void fsm_msgEthereumSignTx(EthereumSignTx* msg); +void fsm_msgEthereumTxAck(EthereumTxAck* msg); +void fsm_msgEthereumSignMessage(EthereumSignMessage* msg); +void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage* msg); +void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash* msg); +void fsm_msgEthereum712TypesValues(Ethereum712TypesValues* msg); -void fsm_msgNanoGetAddress(NanoGetAddress *msg); -void fsm_msgNanoSignTx(NanoSignTx *msg); +void fsm_msgNanoGetAddress(NanoGetAddress* msg); +void fsm_msgNanoSignTx(NanoSignTx* msg); /// Modifies the RippleSignTx, setting the flag to indicate /// that the ECDSA sig is canonical. -void fsm_msgRippleSignTx(RippleSignTx *msg); -void fsm_msgRippleGetAddress(const RippleGetAddress *msg); +void fsm_msgRippleSignTx(RippleSignTx* msg); +void fsm_msgRippleGetAddress(const RippleGetAddress* msg); -void fsm_msgEosGetPublicKey(const EosGetPublicKey *msg); -void fsm_msgEosSignTx(const EosSignTx *msg); -void fsm_msgEosTxActionAck(const EosTxActionAck *msg); +void fsm_msgEosGetPublicKey(const EosGetPublicKey* msg); +void fsm_msgEosSignTx(const EosSignTx* msg); +void fsm_msgEosTxActionAck(const EosTxActionAck* msg); -void fsm_msgBinanceGetAddress(const BinanceGetAddress *msg); -void fsm_msgBinanceSignTx(const BinanceSignTx *msg); -void fsm_msgBinanceTransferMsg(const BinanceTransferMsg *msg); +void fsm_msgBinanceGetAddress(const BinanceGetAddress* msg); +void fsm_msgBinanceSignTx(const BinanceSignTx* msg); +void fsm_msgBinanceTransferMsg(const BinanceTransferMsg* msg); -void fsm_msgCosmosGetAddress(const CosmosGetAddress *msg); -void fsm_msgCosmosSignTx(const CosmosSignTx *msg); -void fsm_msgCosmosMsgAck(const CosmosMsgAck *msg); +void fsm_msgCosmosGetAddress(const CosmosGetAddress* msg); +void fsm_msgCosmosSignTx(const CosmosSignTx* msg); +void fsm_msgCosmosMsgAck(const CosmosMsgAck* msg); -void fsm_msgOsmosisGetAddress(const OsmosisGetAddress *msg); -void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg); -void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg); +void fsm_msgOsmosisGetAddress(const OsmosisGetAddress* msg); +void fsm_msgOsmosisSignTx(const OsmosisSignTx* msg); +void fsm_msgOsmosisMsgAck(const OsmosisMsgAck* msg); -void fsm_msgThorchainGetAddress(const ThorchainGetAddress *msg); -void fsm_msgThorchainSignTx(const ThorchainSignTx *msg); -void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg); +void fsm_msgThorchainGetAddress(const ThorchainGetAddress* msg); +void fsm_msgThorchainSignTx(const ThorchainSignTx* msg); +void fsm_msgThorchainMsgAck(const ThorchainMsgAck* msg); -void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg); -void fsm_msgMayachainSignTx(const MayachainSignTx *msg); -void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg); +void fsm_msgMayachainGetAddress(const MayachainGetAddress* msg); +void fsm_msgMayachainSignTx(const MayachainSignTx* msg); +void fsm_msgMayachainMsgAck(const MayachainMsgAck* msg); + +void fsm_msgTronGetAddress(const TronGetAddress* msg); +void fsm_msgTronSignTx(TronSignTx* msg); +void fsm_msgTonGetAddress(const TonGetAddress* msg); +void fsm_msgTonSignTx(TonSignTx* msg); +void fsm_msgSolanaGetAddress(const SolanaGetAddress* msg); +void fsm_msgSolanaSignTx(const SolanaSignTx* msg); +void fsm_msgSolanaSignMessage(const SolanaSignMessage* msg); #if DEBUG_LINK // void fsm_msgDebugLinkDecision(DebugLinkDecision *msg); -void fsm_msgDebugLinkGetState(DebugLinkGetState *msg); -void fsm_msgDebugLinkStop(DebugLinkStop *msg); +void fsm_msgDebugLinkGetState(DebugLinkGetState* msg); +void fsm_msgDebugLinkStop(DebugLinkStop* msg); #endif -void fsm_msgDebugLinkFlashDump(DebugLinkFlashDump *msg); -void fsm_msgFlashWrite(FlashWrite *msg); -void fsm_msgFlashHash(FlashHash *msg); -void fsm_msgSoftReset(SoftReset *msg); +void fsm_msgDebugLinkFlashDump(DebugLinkFlashDump* msg); +void fsm_msgFlashWrite(FlashWrite* msg); +void fsm_msgFlashHash(FlashHash* msg); +void fsm_msgSoftReset(SoftReset* msg); #endif diff --git a/include/keepkey/firmware/mayachain.h b/include/keepkey/firmware/mayachain.h index 6c2cf2b59..c3d40380d 100644 --- a/include/keepkey/firmware/mayachain.h +++ b/include/keepkey/firmware/mayachain.h @@ -10,21 +10,22 @@ typedef struct _MayachainSignTx MayachainSignTx; typedef struct _MayachainMsgDeposit MayachainMsgDeposit; -bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg); -bool mayachain_signTxUpdateMsgSend(const uint64_t amount, const char *to_address, const char *denom); -bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit *depmsg); -bool mayachain_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool mayachain_signTxInit(const HDNode* _node, const MayachainSignTx* _msg); +bool mayachain_signTxUpdateMsgSend(const uint64_t amount, + const char* to_address, const char* denom); +bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit* depmsg); +bool mayachain_signTxFinalize(uint8_t* public_key, uint8_t* signature); bool mayachain_signingIsInited(void); bool mayachain_signingIsFinished(void); void mayachain_signAbort(void); -const MayachainSignTx *mayachain_getMayachainSignTx(void); +const MayachainSignTx* mayachain_getMayachainSignTx(void); // Mayachain swap data parse and confirm -// input: +// input: // swapStr - string in mayachain swap format // size - size of input string (must be <= 256) // output: // true if mayachain data parsed and confirmed by user, false otherwise -bool mayachain_parseConfirmMemo(const char *swapStr, size_t size); +bool mayachain_parseConfirmMemo(const char* swapStr, size_t size); #endif diff --git a/include/keepkey/firmware/nano.h b/include/keepkey/firmware/nano.h index edfe1f605..d71da9cb2 100644 --- a/include/keepkey/firmware/nano.h +++ b/include/keepkey/firmware/nano.h @@ -10,27 +10,28 @@ #define MAX_NANO_ADDR_SIZE 100 -bool nano_path_mismatched(const CoinType *coin, const uint32_t *address_n, +bool nano_path_mismatched(const CoinType* _coin, const uint32_t* address_n, const uint32_t address_n_count); -bool nano_bip32_to_string(char *node_str, size_t len, const CoinType *coin, - const uint32_t *address_n, +bool nano_bip32_to_string(char* node_str, size_t len, const CoinType* _coin, + const uint32_t* address_n, const size_t address_n_count); -void nano_hash_block_data(const uint8_t account_pk[32], - const uint8_t parent_hash[32], const uint8_t link[32], - const uint8_t representative_pk[32], - const uint8_t balance[16], uint8_t out_hash[32]); +void nano_hash_block_data(const uint8_t _account_pk[32], + const uint8_t _parent_hash[32], + const uint8_t _link[32], + const uint8_t _representative_pk[32], + const uint8_t _balance[16], uint8_t _out_hash[32]); -const char *nano_getKnownRepName(const char *addr); -void nano_truncateAddress(const CoinType *coin, char *str); +const char* nano_getKnownRepName(const char* addr); +void nano_truncateAddress(const CoinType* _coin, char* str); void nano_signingAbort(void); -bool nano_signingInit(const NanoSignTx *msg, const HDNode *node, - const CoinType *coin); -bool nano_parentHash(const NanoSignTx *msg); -bool nano_currentHash(const NanoSignTx *msg, const HDNode *recip); -bool nano_sanityCheck(const NanoSignTx *nano); -bool nano_signTx(const NanoSignTx *msg, HDNode *node, NanoSignedTx *resp); +bool nano_signingInit(const NanoSignTx* msg, const HDNode* node, + const CoinType* _coin); +bool nano_parentHash(const NanoSignTx* msg); +bool nano_currentHash(const NanoSignTx* msg, const HDNode* recip); +bool nano_sanityCheck(const NanoSignTx* msg); +bool nano_signTx(const NanoSignTx* msg, HDNode* node, NanoSignedTx* resp); #endif diff --git a/include/keepkey/firmware/osmosis.h b/include/keepkey/firmware/osmosis.h index 9a3f39498..330c4b9a1 100644 --- a/include/keepkey/firmware/osmosis.h +++ b/include/keepkey/firmware/osmosis.h @@ -14,62 +14,62 @@ typedef struct _OsmosisMsgSwap OsmosisMsgSwap; void debug_intermediate_hash(void); -bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_msg); - -bool osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address); - -bool osmosis_signTxUpdateMsgDelegate(const char *amount, - const char *delegator_address, - const char *validator_address, - const char *denom); - -bool osmosis_signTxUpdateMsgUndelegate(const char *amount, - const char *delegator_address, - const char *validator_address, - const char *denom); - -bool osmosis_signTxUpdateMsgRedelegate(const char *amount, - const char *delegator_address, - const char *validator_src_address, - const char *validator_dst_address, - const char *denom); - -bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char *sender, - const char *share_out_amount, - const char *amount_in_max_a, - const char *denom_in_max_a, - const char *amount_in_max_b, - const char *denom_in_max_b); - -bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char *sender, - const char *share_out_amount, - const char *amount_out_min_a, - const char *denom_out_min_a, - const char *amount_out_min_b, - const char *denom_out_min_b); - -bool osmosis_signTxUpdateMsgRewards(const char *delegator_address, - const char *validator_address); - -bool osmosis_signTxUpdateMsgIBCTransfer(const char *amount, const char *sender, - const char *receiver, - const char *source_channel, - const char *source_port, - const char *revision_number, - const char *revision_height, - const char *denom); +bool osmosis_signTxInit(const HDNode* _node, const OsmosisSignTx* _msg); + +bool osmosis_signTxUpdateMsgSend(const char* amount, const char* to_address); + +bool osmosis_signTxUpdateMsgDelegate(const char* amount, + const char* delegator_address, + const char* validator_address, + const char* denom); + +bool osmosis_signTxUpdateMsgUndelegate(const char* amount, + const char* delegator_address, + const char* validator_address, + const char* denom); + +bool osmosis_signTxUpdateMsgRedelegate(const char* amount, + const char* delegator_address, + const char* validator_src_address, + const char* validator_dst_address, + const char* denom); + +bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char* sender, + const char* share_out_amount, + const char* amount_in_max_a, + const char* denom_in_max_a, + const char* amount_in_max_b, + const char* denom_in_max_b); + +bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char* sender, + const char* share_out_amount, + const char* amount_out_min_a, + const char* denom_out_min_a, + const char* amount_out_min_b, + const char* denom_out_min_b); + +bool osmosis_signTxUpdateMsgRewards(const char* delegator_address, + const char* validator_address); + +bool osmosis_signTxUpdateMsgIBCTransfer(const char* amount, const char* sender, + const char* receiver, + const char* source_channel, + const char* source_port, + const char* revision_number, + const char* revision_height, + const char* denom); bool osmosis_signTxUpdateMsgSwap(const uint64_t pool_id, - const char *token_out_denom, - const char *sender, - const char *token_in_amount, - const char *token_in_denom, - const char *token_out_min_amount); + const char* token_out_denom, + const char* sender, + const char* token_in_amount, + const char* token_in_denom, + const char* token_out_min_amount); -bool osmosis_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool osmosis_signTxFinalize(uint8_t* public_key, uint8_t* signature); bool osmosis_signingIsInited(void); bool osmosis_signingIsFinished(void); void osmosis_signAbort(void); -const OsmosisSignTx *osmosis_getOsmosisSignTx(void); +const OsmosisSignTx* osmosis_getOsmosisSignTx(void); #endif diff --git a/include/keepkey/firmware/passphrase_sm.h b/include/keepkey/firmware/passphrase_sm.h index 41ae172be..58a14755a 100644 --- a/include/keepkey/firmware/passphrase_sm.h +++ b/include/keepkey/firmware/passphrase_sm.h @@ -24,7 +24,7 @@ #include -#define PASSPHRASE_BUF sizeof(((PassphraseAck *)NULL)->passphrase) +#define PASSPHRASE_BUF sizeof(((PassphraseAck*)NULL)->passphrase) /* State for Passphrase SM */ typedef enum { diff --git a/include/keepkey/firmware/pin_sm.h b/include/keepkey/firmware/pin_sm.h index 5e075ee02..0afb0b4a6 100644 --- a/include/keepkey/firmware/pin_sm.h +++ b/include/keepkey/firmware/pin_sm.h @@ -24,7 +24,7 @@ #include -#define PIN_BUF sizeof(((PinMatrixAck *)NULL)->pin) +#define PIN_BUF sizeof(((PinMatrixAck*)NULL)->pin) #define PIN_FAIL_DELAY_START 2 #define MAX_PIN_FAIL_ATTEMPTS 32 @@ -51,7 +51,7 @@ typedef struct { /// Authenticate user PIN for device access. /// \param prompt Text to show user along with PIN matrix. /// \returns true iff the PIN was correct. -bool pin_protect(const char *prompt); +bool pin_protect(const char* prompt); /// Prompt for PIN only if it is not already cached. /// \returns true iff the pin was correct (or already cached). @@ -72,7 +72,7 @@ bool change_wipe_code(void); #if DEBUG_LINK /// Gets randomized PIN matrix. -const char *get_pin_matrix(void); +const char* get_pin_matrix(void); #endif #endif diff --git a/include/keepkey/firmware/policy.h b/include/keepkey/firmware/policy.h index 89d0e59fa..4a7882e70 100644 --- a/include/keepkey/firmware/policy.h +++ b/include/keepkey/firmware/policy.h @@ -34,7 +34,7 @@ static const PolicyType policies[] = { {true, "AdvancedMode", true, false}, }; -int run_policy_compile_output(const CoinType *coin, const HDNode *root, - void *vin, void *vout, bool needs_confirm); +int run_policy_compile_output(const CoinType* coin, const HDNode* root, + void* vin, void* vout, bool needs_confirm); #endif diff --git a/include/keepkey/firmware/recovery_cipher.h b/include/keepkey/firmware/recovery_cipher.h index a6ec97fed..46774c834 100644 --- a/include/keepkey/firmware/recovery_cipher.h +++ b/include/keepkey/firmware/recovery_cipher.h @@ -29,19 +29,19 @@ #define BIP39_MAX_WORD_LEN 8 void recovery_cipher_init(uint32_t _word_count, bool passphrase_protection, - bool pin_protection, const char *language, - const char *label, bool _enforce_wordlist, + bool pin_protection, const char* language, + const char* label, bool _enforce_wordlist, uint32_t _auto_lock_delay_ms, uint32_t _u2f_counter, bool _dry_run); void next_character(void); -void recovery_character(const char *character); +void recovery_character(const char* character); void recovery_delete_character(void); void recovery_cipher_finalize(void); void recovery_cipher_abort(void); #if DEBUG_LINK -const char *recovery_get_cipher(void); -const char *recovery_get_auto_completed_word(void); +const char* recovery_get_cipher(void); +const char* recovery_get_auto_completed_word(void); #endif /// Determine if two strings are exact matches for length passed @@ -50,12 +50,12 @@ const char *recovery_get_auto_completed_word(void); /// \param str1 The first string. /// \param str2 The second string. /// \return true iff the strings match -bool exact_str_match(const char *str1, const char *str2, uint32_t len); +bool exact_str_match(const char* str1, const char* str2, uint32_t len); /// \brief Attempts to auto complete a partial word /// /// \param partial_word[in/out] word that will be attempted to be auto /// completed. \returns true iff partial_word was auto completed -bool attempt_auto_complete(char *partial_word); +bool attempt_auto_complete(char* partial_word); #endif diff --git a/include/keepkey/firmware/reset.h b/include/keepkey/firmware/reset.h index 9e785b7f7..a41cb101d 100644 --- a/include/keepkey/firmware/reset.h +++ b/include/keepkey/firmware/reset.h @@ -23,21 +23,22 @@ #include #include -#define MAX_WORDS 24 -#define MAX_WORD_LEN 10 -#define MAX_PAGES 6 -#define ADDITIONAL_WORD_PAD 5 -#define WORDS_PER_SCREEN 24 -#define TOKENED_MNEMONIC_BUF MAX_WORDS * (MAX_WORD_LEN + 1) + 1 -#define FORMATTED_MNEMONIC_BUF MAX_WORDS * (MAX_WORD_LEN + ADDITIONAL_WORD_PAD) + 1 -#define MNEMONIC_BY_SCREEN_BUF WORDS_PER_SCREEN * (MAX_WORD_LEN + 1) + 1 +#define MAX_WORDS 24 +#define MAX_WORD_LEN 10 +#define MAX_PAGES 6 +#define ADDITIONAL_WORD_PAD 5 +#define WORDS_PER_SCREEN 24 +#define TOKENED_MNEMONIC_BUF MAX_WORDS*(MAX_WORD_LEN + 1) + 1 +#define FORMATTED_MNEMONIC_BUF \ + MAX_WORDS*(MAX_WORD_LEN + ADDITIONAL_WORD_PAD) + 1 +#define MNEMONIC_BY_SCREEN_BUF WORDS_PER_SCREEN*(MAX_WORD_LEN + 1) + 1 void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, - const char *language, const char *label, bool no_backup, + const char* language, const char* label, bool _no_backup, uint32_t _auto_lock_delay_ms, uint32_t _u2f_counter); -void reset_entropy(const uint8_t *ext_entropy, uint32_t len); -uint32_t reset_get_int_entropy(uint8_t *entropy); -const char *reset_get_word(void); +void reset_entropy(const uint8_t* ext_entropy, uint32_t len); +uint32_t reset_get_int_entropy(uint8_t* entropy); +const char* reset_get_word(void); #endif diff --git a/include/keepkey/firmware/ripple.h b/include/keepkey/firmware/ripple.h index d4230a4d4..409052c4a 100644 --- a/include/keepkey/firmware/ripple.h +++ b/include/keepkey/firmware/ripple.h @@ -59,37 +59,37 @@ extern const RippleFieldMapping RFM_destinationTag; bool ripple_getAddress(const uint8_t public_key[33], char address[MAX_ADDR_SIZE]); -void ripple_formatAmount(char *buf, size_t len, uint64_t amount); +void ripple_formatAmount(char* buf, size_t len, uint64_t amount); -void ripple_serializeType(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m); +void ripple_serializeType(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m); -void ripple_serializeInt16(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int16_t val); +void ripple_serializeInt16(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int16_t val); -void ripple_serializeInt32(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int32_t val); +void ripple_serializeInt32(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int32_t val); -void ripple_serializeAmount(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int64_t amount); +void ripple_serializeAmount(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int64_t amount); -void ripple_serializeVarint(bool *ok, uint8_t **buf, const uint8_t *end, +void ripple_serializeVarint(bool* ok, uint8_t** buf, const uint8_t* end, int val); -void ripple_serializeBytes(bool *ok, uint8_t **buf, const uint8_t *end, - const uint8_t *bytes, size_t count); +void ripple_serializeBytes(bool* ok, uint8_t** buf, const uint8_t* end, + const uint8_t* bytes, size_t count); -void ripple_serializeAddress(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, const char *address); +void ripple_serializeAddress(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, const char* address); -void ripple_serializeVL(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, const uint8_t *bytes, +void ripple_serializeVL(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, const uint8_t* bytes, size_t count); -bool ripple_serialize(uint8_t **buf, const uint8_t *end, const RippleSignTx *tx, - const char *source_address, const uint8_t *pubkey, - const uint8_t *sig, size_t sig_len); +bool ripple_serialize(uint8_t** buf, const uint8_t* end, const RippleSignTx* tx, + const char* source_address, const uint8_t* pubkey, + const uint8_t* sig, size_t sig_len); -void ripple_signTx(const HDNode *node, RippleSignTx *tx, RippleSignedTx *resp); +void ripple_signTx(const HDNode* node, RippleSignTx* tx, RippleSignedTx* resp); #endif diff --git a/include/keepkey/firmware/ripple_base58.h b/include/keepkey/firmware/ripple_base58.h index 98c9e83fe..b8b5c3595 100644 --- a/include/keepkey/firmware/ripple_base58.h +++ b/include/keepkey/firmware/ripple_base58.h @@ -33,15 +33,15 @@ extern const char ripple_b58digits_ordered[]; extern const int8_t ripple_b58digits_map[]; -int ripple_encode_check(const uint8_t *data, int len, HasherType hasher_type, - char *str, int strsize); -int ripple_decode_check(const char *str, HasherType hasher_type, uint8_t *data, +int ripple_encode_check(const uint8_t* data, int datalen, + HasherType hasher_type, char* str, int strsize); +int ripple_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen); // Private -bool ripple_b58tobin(void *bin, size_t *binszp, const char *b58); -int ripple_b58check(const void *bin, size_t binsz, HasherType hasher_type, - const char *base58str); -bool ripple_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz); +bool ripple_b58tobin(void* bin, size_t* binszp, const char* b58); +int ripple_b58check(const void* bin, size_t binsz, HasherType hasher_type, + const char* base58str); +bool ripple_b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz); #endif diff --git a/include/keepkey/firmware/signing.h b/include/keepkey/firmware/signing.h index 1295d1984..55f4c17d1 100644 --- a/include/keepkey/firmware/signing.h +++ b/include/keepkey/firmware/signing.h @@ -25,10 +25,10 @@ #include #include -void signing_init(const SignTx *msg, const CoinType *_coin, - const HDNode *_root); +void signing_init(const SignTx* msg, const CoinType* _coin, + const HDNode* _root); void signing_abort(void); -void signing_txack(TransactionType *tx); +void signing_txack(TransactionType* tx); void send_fsm_co_error_message(int co_error); #endif diff --git a/include/keepkey/firmware/signtx_tendermint.h b/include/keepkey/firmware/signtx_tendermint.h index 8933e2d02..9277a370f 100644 --- a/include/keepkey/firmware/signtx_tendermint.h +++ b/include/keepkey/firmware/signtx_tendermint.h @@ -9,41 +9,41 @@ typedef struct _TendermintSignTx TendermintSignTx; -bool tendermint_signTxInit(const HDNode *_node, const void *_msg, - const size_t msgsize, const char *denom); +bool tendermint_signTxInit(const HDNode* _node, const void* _msg, + const size_t msgsize, const char* denom); bool tendermint_signTxUpdateMsgSend(const uint64_t amount, - const char *to_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix); + const char* to_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix); bool tendermint_signTxUpdateMsgDelegate(const uint64_t amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix); + const char* delegator_address, + const char* validator_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix); bool tendermint_signTxUpdateMsgUndelegate(const uint64_t amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, - const char *denom, - const char *msgTypePrefix); + const char* delegator_address, + const char* validator_address, + const char* chainstr, + const char* denom, + const char* msgTypePrefix); bool tendermint_signTxUpdateMsgRedelegate( - const uint64_t amount, const char *delegator_address, - const char *validator_src_address, const char *validator_dst_address, - const char *chainstr, const char *denom, const char *msgTypePrefix); -bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix); + const uint64_t amount, const char* delegator_address, + const char* validator_src_address, const char* validator_dst_address, + const char* chainstr, const char* denom, const char* msgTypePrefix); +bool tendermint_signTxUpdateMsgRewards(const uint64_t* amount, + const char* delegator_address, + const char* validator_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix); bool tendermint_signTxUpdateMsgIBCTransfer( - const uint64_t amount, const char *sender, const char *receiver, - const char *source_channel, const char *source_port, - const char *revision_number, const char *revision_height, - const char *chainstr, const char *denom, const char *msgTypePrefix); -bool tendermint_signTxFinalize(uint8_t *public_key, uint8_t *signature); + const uint64_t amount, const char* sender, const char* receiver, + const char* source_channel, const char* source_port, + const char* revision_number, const char* revision_height, + const char* chainstr, const char* denom, const char* msgTypePrefix); +bool tendermint_signTxFinalize(uint8_t* public_key, uint8_t* signature); bool tendermint_signingIsInited(void); bool tendermint_signingIsFinished(void); void tendermint_signAbort(void); -const void *tendermint_getSignTx(void); +const void* tendermint_getSignTx(void); #endif \ No newline at end of file diff --git a/include/keepkey/firmware/solana.h b/include/keepkey/firmware/solana.h new file mode 100644 index 000000000..042645e3e --- /dev/null +++ b/include/keepkey/firmware/solana.h @@ -0,0 +1,202 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef KEEPKEY_FIRMWARE_SOLANA_H +#define KEEPKEY_FIRMWARE_SOLANA_H + +#include "trezor/crypto/bip32.h" +#include "messages-solana.pb.h" + +#include +#include +#include + +#define SOL_DECIMALS 9 +#define SOL_PUBKEY_SIZE 32 +#define SOL_SIG_SIZE 64 +#define SOL_MAX_ACCOUNTS 32 +#define SOL_MAX_INSTRUCTIONS 8 +#define SOL_LAMPORTS_DIVISOR 1000000000ULL +#define SOL_MAX_TOKEN_DECIMALS 18 +#define SOL_MAX_DISPLAY_DECIMALS 9 + +/* Versioned transaction marker */ +#define SOL_VERSION_FLAG 0x80 +#define SOL_VERSION_MASK 0x7F + +/* Compact-u16 encoding constants */ +#define SOL_COMPACT_U16_CONTINUATION 0x80 +#define SOL_COMPACT_U16_DATA_MASK 0x7F +#define SOL_COMPACT_U16_BYTE3_MAX 3 + +/* System program instruction indices */ +#define SOL_SYS_CREATE_ACCOUNT 0 +#define SOL_SYS_ASSIGN 1 +#define SOL_SYS_TRANSFER 2 +#define SOL_SYS_ADVANCE_NONCE 4 +#define SOL_SYS_WITHDRAW_NONCE 5 +#define SOL_SYS_INITIALIZE_NONCE 6 +#define SOL_SYS_AUTHORIZE_NONCE 7 +#define SOL_SYS_ALLOCATE 8 + +/* SPL Token program instruction indices */ +#define SOL_TOKEN_TRANSFER_IX 3 +#define SOL_TOKEN_APPROVE_IX 4 +#define SOL_TOKEN_REVOKE_IX 5 +#define SOL_TOKEN_SET_AUTHORITY_IX 6 +#define SOL_TOKEN_MINT_TO_IX 7 +#define SOL_TOKEN_BURN_IX 8 +#define SOL_TOKEN_CLOSE_ACCOUNT_IX 9 +#define SOL_TOKEN_FREEZE_ACCOUNT_IX 10 +#define SOL_TOKEN_THAW_ACCOUNT_IX 11 +#define SOL_TOKEN_TRANSFER_CHECKED_IX 12 +#define SOL_TOKEN_MINT_TO_CHECKED_IX 14 +#define SOL_TOKEN_BURN_CHECKED_IX 15 +#define SOL_TOKEN_SYNC_NATIVE_IX 17 + +/* Stake program instruction indices */ +#define SOL_STAKE_AUTHORIZE_IX 1 +#define SOL_STAKE_DELEGATE_IX 2 +#define SOL_STAKE_SPLIT_IX 3 +#define SOL_STAKE_WITHDRAW_IX 4 +#define SOL_STAKE_DEACTIVATE_IX 5 +#define SOL_STAKE_MERGE_IX 7 + +/* Vote program instruction indices */ +#define SOL_VOTE_AUTHORIZE_IX 1 +#define SOL_VOTE_WITHDRAW_IX 3 +#define SOL_VOTE_UPDATE_VALIDATOR_IX 4 +#define SOL_VOTE_UPDATE_COMMISSION_IX 5 + +/* Compute Budget program instruction indices */ +#define SOL_CB_REQUEST_HEAP_FRAME 1 +#define SOL_CB_SET_COMPUTE_UNIT_LIMIT 2 +#define SOL_CB_SET_COMPUTE_UNIT_PRICE 3 +#define SOL_CB_SET_LOADED_ACCOUNTS_SIZE 4 + +/* Well-known program IDs */ +extern const uint8_t SOL_SYSTEM_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_TOKEN_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_TOKEN_2022_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_STAKE_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_VOTE_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_ATA_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_COMPUTE_BUDGET_PROGRAM[SOL_PUBKEY_SIZE]; +extern const uint8_t SOL_MEMO_PROGRAM[SOL_PUBKEY_SIZE]; + +/* Instruction types recognized by the parser */ +typedef enum { + SOL_INSTR_SYSTEM_TRANSFER, + SOL_INSTR_SYSTEM_CREATE_ACCOUNT, + SOL_INSTR_SYSTEM_ADVANCE_NONCE, + SOL_INSTR_SYSTEM_WITHDRAW_NONCE, + SOL_INSTR_SYSTEM_INITIALIZE_NONCE, + SOL_INSTR_SYSTEM_AUTHORIZE_NONCE, + SOL_INSTR_SYSTEM_ASSIGN, + SOL_INSTR_SYSTEM_ALLOCATE, + SOL_INSTR_TOKEN_TRANSFER, + SOL_INSTR_TOKEN_TRANSFER_CHECKED, + SOL_INSTR_TOKEN_APPROVE, + SOL_INSTR_TOKEN_REVOKE, + SOL_INSTR_TOKEN_SET_AUTHORITY, + SOL_INSTR_TOKEN_MINT_TO, + SOL_INSTR_TOKEN_BURN, + SOL_INSTR_TOKEN_CLOSE_ACCOUNT, + SOL_INSTR_TOKEN_FREEZE_ACCOUNT, + SOL_INSTR_TOKEN_THAW_ACCOUNT, + SOL_INSTR_TOKEN_SYNC_NATIVE, + SOL_INSTR_STAKE_DELEGATE, + SOL_INSTR_STAKE_WITHDRAW, + SOL_INSTR_STAKE_AUTHORIZE, + SOL_INSTR_STAKE_SPLIT, + SOL_INSTR_STAKE_DEACTIVATE, + SOL_INSTR_STAKE_MERGE, + SOL_INSTR_VOTE_AUTHORIZE, + SOL_INSTR_VOTE_WITHDRAW, + SOL_INSTR_VOTE_UPDATE_VALIDATOR, + SOL_INSTR_VOTE_UPDATE_COMMISSION, + SOL_INSTR_ATA_CREATE, + SOL_INSTR_COMPUTE_BUDGET_HEAP_FRAME, + SOL_INSTR_COMPUTE_BUDGET_UNIT_LIMIT, + SOL_INSTR_COMPUTE_BUDGET_UNIT_PRICE, + SOL_INSTR_COMPUTE_BUDGET_LOADED_ACCOUNTS_SIZE, + SOL_INSTR_MEMO, + SOL_INSTR_UNKNOWN, +} SolanaInstrType; + +/* Parsed instruction */ +typedef struct { + SolanaInstrType type; + uint8_t program_id[SOL_PUBKEY_SIZE]; + /* Decoded fields (filled based on type) */ + uint8_t from[SOL_PUBKEY_SIZE]; + uint8_t to[SOL_PUBKEY_SIZE]; + uint8_t authority[SOL_PUBKEY_SIZE]; + uint8_t extra[SOL_PUBKEY_SIZE]; + uint64_t amount; + uint64_t lamports; + uint64_t extra_value; + /* For token transfers */ + uint8_t mint[SOL_PUBKEY_SIZE]; + bool has_mint; + uint8_t extra_u8; +} SolanaParsedInstruction; + +/* Parsed transaction header */ +typedef struct { + uint8_t num_required_sigs; + uint8_t num_readonly_signed; + uint8_t num_readonly_unsigned; + uint8_t num_accounts; + uint8_t accounts[SOL_MAX_ACCOUNTS][SOL_PUBKEY_SIZE]; + uint8_t recent_blockhash[SOL_PUBKEY_SIZE]; + uint8_t num_instructions; + SolanaParsedInstruction instructions[SOL_MAX_INSTRUCTIONS]; +} SolanaParsedTx; + +/* Firmware review result for a Solana message */ +typedef enum { + SOL_TX_REVIEW_MALFORMED = 0, + SOL_TX_REVIEW_OPAQUE, + SOL_TX_REVIEW_VERIFIED, +} SolanaTxReview; + +/* Inspect a raw Solana transaction and classify it for signing UX */ +SolanaTxReview solana_inspectTx(const uint8_t* raw, size_t raw_len, + SolanaParsedTx* tx); + +/* Parse a raw Solana transaction */ +bool solana_parseTx(const uint8_t* raw, size_t raw_len, SolanaParsedTx* tx); + +/* Format SOL amount */ +void solana_formatAmount(char* buf, size_t len, uint64_t lamports); + +/* Format token amount with decimals */ +void solana_formatTokenAmount(char* buf, size_t len, uint64_t amount, + const char* symbol, uint8_t decimals); + +/* Look up token info from the host-provided list */ +const SolanaTokenInfo* solana_findTokenInfo( + const SolanaSignTx* msg, const uint8_t mint[SOL_PUBKEY_SIZE]); + +/* Sign transaction */ +bool solana_signTx(const HDNode* node, const SolanaSignTx* msg, + SolanaSignedTx* resp); + +#endif /* KEEPKEY_FIRMWARE_SOLANA_H */ diff --git a/include/keepkey/firmware/storage.h b/include/keepkey/firmware/storage.h index 185051e17..9fc8c1954 100644 --- a/include/keepkey/firmware/storage.h +++ b/include/keepkey/firmware/storage.h @@ -31,9 +31,10 @@ #define RANDOM_SALT_LEN 32 -#define STORAGE_DEFAULT_SCREENSAVER_TIMEOUT (10U * 60U * 1000U) /* 10 minutes \ - */ -#define STORAGE_MIN_SCREENSAVER_TIMEOUT (30U * 1000U) /* 30 seconds */ +#define STORAGE_DEFAULT_SCREENSAVER_TIMEOUT \ + (10U * 60U * 1000U) /* 10 minutes \ + */ +#define STORAGE_MIN_SCREENSAVER_TIMEOUT (30U * 1000U) /* 30 seconds */ /// \brief Validate storage content and copy data to shadow memory. void storage_init(void); @@ -60,18 +61,18 @@ void storage_commit(void); /// \brief Load configuration data from usb message to shadow memory typedef struct _LoadDevice LoadDevice; -void storage_loadDevice(LoadDevice *msg); +void storage_loadDevice(LoadDevice* msg); /// \brief Get the Root Node of the device. /// \param node[out] The Root Node. /// \param curve[in] ECDSA curve to use. /// \param usePassphrase[in] Whether the seed uses a passphrase. /// \return true iff the root node was found. -bool storage_getRootNode(const char *curve, bool usePassphrase, HDNode *node); +bool storage_getRootNode(const char* curve, bool usePassphrase, HDNode* node); /// \brief Fetch the node used for U2F signing. /// \returns true iff retrieval was successful. -bool storage_getU2FRoot(HDNode *node); +bool storage_getU2FRoot(HDNode* node); /// \brief Increment and return the next value for the U2F counter. uint32_t storage_nextU2FCounter(void); @@ -80,31 +81,31 @@ uint32_t storage_nextU2FCounter(void); void storage_setU2FCounter(uint32_t u2f_counter); /// \brief Set device label -void storage_setLabel(const char *label); +void storage_setLabel(const char* label); /// \brief Get device label -const char *storage_getLabel(void); +const char* storage_getLabel(void); /// \brief Set device language. -void storage_setLanguage(const char *lang); +void storage_setLanguage(const char* lang); /// \brief Get device language. -const char *storage_getLanguage(void); +const char* storage_getLanguage(void); /// \brief Validate pin. /// \return true iff the privided pin is correct. -bool storage_isPinCorrect(const char *pin); +bool storage_isPinCorrect(const char* pin); /// \brief Validate wipe code. /// \return true iff the privided wipe code is correct. -bool storage_isWipeCodeCorrect(const char *wipe_code); +bool storage_isWipeCodeCorrect(const char* wipe_code); bool storage_hasPin(void); -void storage_setPin(const char *pin); -void session_cachePin(const char *pin); +void storage_setPin(const char* pin); +void session_cachePin(const char* pin); bool session_isPinCached(void); bool storage_hasWipeCode(void); -void storage_setWipeCode(const char *wipe_code); +void storage_setWipeCode(const char* wipe_code); void storage_resetPinFails(void); void storage_increasePinFails(void); uint32_t storage_getPinFails(void); @@ -114,25 +115,25 @@ bool storage_isInitialized(void); bool storage_noBackup(void); void storage_setNoBackup(void); -const char *storage_getUuidStr(void); +const char* storage_getUuidStr(void); bool storage_getPassphraseProtected(void); void storage_setPassphraseProtected(bool passphrase); -void session_cachePassphrase(const char *passphrase); +void session_cachePassphrase(const char* passphrase); bool session_isPassphraseCached(void); /// \brief Set config mnemonic in shadow memory from words. void storage_setMnemonicFromWords(const char (*words)[12], - unsigned int num_words); + unsigned int word_count); /// \brief Set config mnemonic from a recovery sentence. -void storage_setMnemonic(const char *mnemonic); +void storage_setMnemonic(const char* m); /// \brief Get mnemonic from shadow memory -const char *storage_getShadowMnemonic(void); +const char* storage_getShadowMnemonic(void); /// \returns true iff storage is unlocked, and contains the provided mnemonic. -bool storage_containsMnemonic(const char *mnemonic); +bool storage_containsMnemonic(const char* mnemonic); /// \returns true iff the private key stored on device was imported. bool storage_getImported(void); @@ -147,20 +148,20 @@ typedef struct _PolicyType PolicyType; /// \brief Assign policy by name. /// \returns true iff assignment was successful. -bool storage_setPolicy(const char *policy_name, bool enabled); +bool storage_setPolicy(const char* policy_name, bool enabled); /// \brief Copy out all the policies in storage /// \param policies[out] Where to write the policies. -void storage_getPolicies(PolicyType *policies); +void storage_getPolicies(PolicyType* policy_data); /// \brief Status of policy in storage -bool storage_isPolicyEnabled(const char *policy_name); +bool storage_isPolicyEnabled(const char* policy_name); uint32_t storage_getAutoLockDelayMs(void); void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms); -bool storage_getAuthData(authType *returnData); -void storage_setAuthData(authType *setData); +bool storage_getAuthData(authType* returnData); +void storage_setAuthData(const authType* setData); void storage_wipeAuthData(void); #ifdef DEBUG_LINK @@ -173,10 +174,10 @@ bool storage_hasMnemonic(void); /// \returns true iff the active storage has a HDNode. bool storage_hasNode(void); -const char *storage_getPin(void); -const char *storage_getMnemonic(void); -HDNode *storage_getNode(void); -void storage_dumpNode(HDNodeType *dst, const HDNode *src); +const char* storage_getPin(void); +const char* storage_getMnemonic(void); +HDNode* storage_getNode(void); +void storage_dumpNode(HDNodeType* dst, const HDNode* src); #endif #endif diff --git a/include/keepkey/firmware/tendermint.h b/include/keepkey/firmware/tendermint.h index 586e85879..aa29399d1 100644 --- a/include/keepkey/firmware/tendermint.h +++ b/include/keepkey/firmware/tendermint.h @@ -13,7 +13,7 @@ typedef struct _SHA256_CTX SHA256_CTX; /** * \returns false iff the provided bip32 derivation path matches the given coin. */ -bool tendermint_pathMismatched(const CoinType *coin, const uint32_t *address_n, +bool tendermint_pathMismatched(const CoinType* coin, const uint32_t* address_n, const uint32_t address_n_count); /** @@ -25,13 +25,13 @@ bool tendermint_pathMismatched(const CoinType *coin, const uint32_t *address_n, * * \returns true if successful */ -bool tendermint_getAddress(const HDNode *node, const char *prefix, - char *address); +bool tendermint_getAddress(const HDNode* node, const char* prefix, + char* address); -void tendermint_sha256UpdateEscaped(SHA256_CTX *ctx, const char *s, size_t len); +void tendermint_sha256UpdateEscaped(SHA256_CTX* ctx, const char* s, size_t len); -bool tendermint_snprintf(SHA256_CTX *ctx, char *temp, size_t len, - const char *format, ...) +bool tendermint_snprintf(SHA256_CTX* ctx, char* temp, size_t len, + const char* format, ...) __attribute__((format(printf, 4, 5))); #endif diff --git a/include/keepkey/firmware/thorchain.h b/include/keepkey/firmware/thorchain.h index f88baff28..5ebb2a993 100644 --- a/include/keepkey/firmware/thorchain.h +++ b/include/keepkey/firmware/thorchain.h @@ -10,21 +10,22 @@ typedef struct _ThorchainSignTx ThorchainSignTx; typedef struct _ThorchainMsgDeposit ThorchainMsgDeposit; -bool thorchain_signTxInit(const HDNode *_node, const ThorchainSignTx *_msg); -bool thorchain_signTxUpdateMsgSend(const uint64_t amount, const char *to_address); -bool thorchain_signTxUpdateMsgDeposit(const ThorchainMsgDeposit *depmsg); -bool thorchain_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool thorchain_signTxInit(const HDNode* _node, const ThorchainSignTx* _msg); +bool thorchain_signTxUpdateMsgSend(const uint64_t amount, + const char* to_address); +bool thorchain_signTxUpdateMsgDeposit(const ThorchainMsgDeposit* depmsg); +bool thorchain_signTxFinalize(uint8_t* public_key, uint8_t* signature); bool thorchain_signingIsInited(void); bool thorchain_signingIsFinished(void); void thorchain_signAbort(void); -const ThorchainSignTx *thorchain_getThorchainSignTx(void); +const ThorchainSignTx* thorchain_getThorchainSignTx(void); // Thorchain swap data parse and confirm -// input: +// input: // swapStr - string in thorchain swap format // size - size of input string (must be <= 256) // output: // true if thorchain data parsed and confirmed by user, false otherwise -bool thorchain_parseConfirmMemo(const char *swapStr, size_t size); +bool thorchain_parseConfirmMemo(const char* swapStr, size_t size); #endif diff --git a/include/keepkey/firmware/tiny-json.h b/include/keepkey/firmware/tiny-json.h index 01f15b379..7ba75ec38 100644 --- a/include/keepkey/firmware/tiny-json.h +++ b/include/keepkey/firmware/tiny-json.h @@ -2,7 +2,7 @@ /* - + Licensed under the MIT License . SPDX-License-Identifier: MIT Copyright (c) 2016-2018 Rafa Garcia . @@ -24,145 +24,148 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + */ #ifndef _TINY_JSON_H_ -#define _TINY_JSON_H_ +#define _TINY_JSON_H_ #include #include #include #include -#define json_containerOf( ptr, type, member ) \ - ((type*)( (char*)ptr - offsetof( type, member ) )) +#define json_containerOf(ptr, type, member) \ + ((type*)((char*)ptr - offsetof(type, member))) /** @defgroup tinyJson Tiny JSON parser. - * @{ */ + * @{ */ /** Enumeration of codes of supported JSON properties types. */ typedef enum { - JSON_OBJ, JSON_ARRAY, JSON_TEXT, JSON_BOOLEAN, - JSON_INTEGER, JSON_REAL, JSON_NULL + JSON_OBJ, + JSON_ARRAY, + JSON_TEXT, + JSON_BOOLEAN, + JSON_INTEGER, + JSON_REAL, + JSON_NULL } jsonType_t; /** Structure to handle JSON properties. */ typedef struct json_s { - struct json_s* sibling; - char const* name; - union { - char const* value; - struct { - struct json_s* child; - struct json_s* last_child; - } c; - } u; - jsonType_t type; + struct json_s* sibling; + char const* name; + union { + char const* value; + struct { + struct json_s* child; + struct json_s* last_child; + } c; + } u; + jsonType_t type; } json_t; -extern int errno; +extern int errno; /** Parse a string to get a json. - * @param str String pointer with a JSON object. It will be modified. - * @param mem Array of json properties to allocate. - * @param qty Number of elements of mem. - * @retval Null pointer if any was wrong in the parse process. - * @retval If the parser process was successfully a valid handler of a json. - * This property is always unnamed and its type is JSON_OBJ. */ -json_t const* json_create( char* str, json_t mem[], unsigned int qty ); + * @param str String pointer with a JSON object. It will be modified. + * @param mem Array of json properties to allocate. + * @param qty Number of elements of mem. + * @retval Null pointer if any was wrong in the parse process. + * @retval If the parser process was successfully a valid handler of a json. + * This property is always unnamed and its type is JSON_OBJ. */ +json_t const* json_create(char* str, json_t mem[], unsigned int qty); /** Get the name of a json property. - * @param json A valid handler of a json property. - * @retval Pointer to null-terminated if property has name. - * @retval Null pointer if the property is unnamed. */ -static inline char const* json_getName( json_t const* json ) { - return json->name; + * @param json A valid handler of a json property. + * @retval Pointer to null-terminated if property has name. + * @retval Null pointer if the property is unnamed. */ +static inline char const* json_getName(json_t const* json) { + return json->name; } /** Get the value of a json property. - * The type of property cannot be JSON_OBJ or JSON_ARRAY. - * @param property A valid handler of a json property. - * @return Pointer to null-terminated string with the value. */ -static inline char const* json_getValue( json_t const* property ) { - return property->u.value; + * The type of property cannot be JSON_OBJ or JSON_ARRAY. + * @param property A valid handler of a json property. + * @return Pointer to null-terminated string with the value. */ +static inline char const* json_getValue(json_t const* property) { + return property->u.value; } /** Get the type of a json property. - * @param json A valid handler of a json property. - * @return The code of type.*/ -static inline jsonType_t json_getType( json_t const* json ) { - return json->type; -} - -/** Get the next sibling of a JSON property that is within a JSON object or array. - * @param json A valid handler of a json property. - * @retval The handler of the next sibling if found. - * @retval Null pointer if the json property is the last one. */ -static inline json_t const* json_getSibling( json_t const* json ) { - return json->sibling; + * @param json A valid handler of a json property. + * @return The code of type.*/ +static inline jsonType_t json_getType(json_t const* json) { return json->type; } + +/** Get the next sibling of a JSON property that is within a JSON object or + * array. + * @param json A valid handler of a json property. + * @retval The handler of the next sibling if found. + * @retval Null pointer if the json property is the last one. */ +static inline json_t const* json_getSibling(json_t const* json) { + return json->sibling; } /** Search a property by its name in a JSON object. - * @param obj A valid handler of a json object. Its type must be JSON_OBJ. - * @param property The name of property to get. - * @retval The handler of the json property if found. - * @retval Null pointer if not found. */ -json_t const* json_getProperty( json_t const* obj, char const* property ); - + * @param obj A valid handler of a json object. Its type must be JSON_OBJ. + * @param property The name of property to get. + * @retval The handler of the json property if found. + * @retval Null pointer if not found. */ +json_t const* json_getProperty(json_t const* obj, char const* property); /** Search a property by its name in a JSON object and return its value. - * @param obj A valid handler of a json object. Its type must be JSON_OBJ. - * @param property The name of property to get. - * @retval If found a pointer to null-terminated string with the value. - * @retval Null pointer if not found or it is an array or an object. */ -char const* json_getPropertyValue( json_t const* obj, char const* property ); + * @param obj A valid handler of a json object. Its type must be JSON_OBJ. + * @param property The name of property to get. + * @retval If found a pointer to null-terminated string with the value. + * @retval Null pointer if not found or it is an array or an object. */ +char const* json_getPropertyValue(json_t const* obj, char const* property); /** Get the first property of a JSON object or array. - * @param json A valid handler of a json property. - * Its type must be JSON_OBJ or JSON_ARRAY. - * @retval The handler of the first property if there is. - * @retval Null pointer if the json object has not properties. */ -static inline json_t const* json_getChild( json_t const* json ) { - return json->u.c.child; + * @param json A valid handler of a json property. + * Its type must be JSON_OBJ or JSON_ARRAY. + * @retval The handler of the first property if there is. + * @retval Null pointer if the json object has not properties. */ +static inline json_t const* json_getChild(json_t const* json) { + return json->u.c.child; } /** Get the value of a json boolean property. - * @param property A valid handler of a json object. Its type must be JSON_BOOLEAN. - * @return The value stdbool. */ -static inline bool json_getBoolean( json_t const* property ) { - return *property->u.value == 't'; + * @param property A valid handler of a json object. Its type must be + * JSON_BOOLEAN. + * @return The value stdbool. */ +static inline bool json_getBoolean(json_t const* property) { + return *property->u.value == 't'; } /** Get the value of a json integer property. - * @param property A valid handler of a json object. Its type must be JSON_INTEGER. - * @return The value stdint. */ -static inline int64_t json_getInteger( json_t const* property ) { - return strtoll( property->u.value,(char**)NULL, 10); + * @param property A valid handler of a json object. Its type must be + * JSON_INTEGER. + * @return The value stdint. */ +static inline int64_t json_getInteger(json_t const* property) { + return strtoll(property->u.value, (char**)NULL, 10); } /** Get the value of a json real property. - * @param property A valid handler of a json object. Its type must be JSON_REAL. - * @return The value. */ -static inline double json_getReal( json_t const* property ) { - return strtod( property->u.value,(char**)NULL ); + * @param property A valid handler of a json object. Its type must be JSON_REAL. + * @return The value. */ +static inline double json_getReal(json_t const* property) { + return strtod(property->u.value, (char**)NULL); } - - /** Structure to handle a heap of JSON properties. */ typedef struct jsonPool_s jsonPool_t; struct jsonPool_s { - json_t* (*init)( jsonPool_t* pool ); - json_t* (*alloc)( jsonPool_t* pool ); + json_t* (*init)(jsonPool_t* pool); + json_t* (*alloc)(jsonPool_t* pool); }; /** Parse a string to get a json. - * @param str String pointer with a JSON object. It will be modified. - * @param pool Custom json pool pointer. - * @retval Null pointer if any was wrong in the parse process. - * @retval If the parser process was successfully a valid handler of a json. - * This property is always unnamed and its type is JSON_OBJ. */ -json_t const* json_createWithPool( char* str, jsonPool_t* pool ); + * @param str String pointer with a JSON object. It will be modified. + * @param pool Custom json pool pointer. + * @retval Null pointer if any was wrong in the parse process. + * @retval If the parser process was successfully a valid handler of a json. + * This property is always unnamed and its type is JSON_OBJ. */ +json_t const* json_createWithPool(char* str, jsonPool_t* pool); /** @ } */ @@ -170,4 +173,4 @@ json_t const* json_createWithPool( char* str, jsonPool_t* pool ); } #endif -#endif /* _TINY_JSON_H_ */ +#endif /* _TINY_JSON_H_ */ diff --git a/include/keepkey/firmware/ton.h b/include/keepkey/firmware/ton.h new file mode 100644 index 000000000..7b9d42870 --- /dev/null +++ b/include/keepkey/firmware/ton.h @@ -0,0 +1,68 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef KEEPKEY_FIRMWARE_TON_H +#define KEEPKEY_FIRMWARE_TON_H + +#include "trezor/crypto/bip32.h" +#include "trezor/crypto/ed25519-donna/ed25519.h" + +#include "messages-ton.pb.h" + +// TON address length (Base64 URL-safe encoded, typically 48 chars) +#define TON_ADDRESS_MAX_LEN 64 +#define TON_RAW_ADDRESS_MAX_LEN 128 + +// TON decimals (1 TON = 1,000,000,000 nanoTON) +#define TON_DECIMALS 9 + +/** + * Generate TON address from Ed25519 public key + * @param public_key Ed25519 public key (32 bytes) + * @param bounceable Bounceable flag for address encoding + * @param testnet Testnet flag for address encoding + * @param workchain Workchain ID (-1 or 0) + * @param address Output buffer for user-friendly Base64 encoded address + * @param address_len Length of address output buffer + * @param raw_address Output buffer for raw address format (workchain:hash) + * @param raw_address_len Length of raw_address output buffer + * @return true on success, false on failure + */ +bool ton_get_address(const ed25519_public_key public_key, bool bounceable, + bool testnet, int32_t workchain, char* address, + size_t address_len, char* raw_address, + size_t raw_address_len); + +/** + * Format TON amount (nanoTON) for display + * @param buf Output buffer + * @param len Length of output buffer + * @param amount Amount in nanoTON (1 TON = 1,000,000,000 nanoTON) + */ +void ton_formatAmount(char* buf, size_t len, uint64_t amount); + +/** + * Sign a TON transaction + * @param node HD node containing private key + * @param msg TonSignTx request message + * @param resp TonSignedTx response message (will be filled with signature) + */ +bool ton_signTx(const HDNode* node, const TonSignTx* msg, TonSignedTx* resp); + +#endif diff --git a/include/keepkey/firmware/transaction.h b/include/keepkey/firmware/transaction.h index c49ef9d13..fd699554c 100644 --- a/include/keepkey/firmware/transaction.h +++ b/include/keepkey/firmware/transaction.h @@ -57,58 +57,58 @@ typedef struct { Hasher hasher; } TxStruct; -bool compute_address(const CoinType *coin, InputScriptType script_type, - const HDNode *node, bool has_multisig, - const MultisigRedeemScriptType *multisig, +bool compute_address(const CoinType* coin, InputScriptType script_type, + const HDNode* node, bool has_multisig, + const MultisigRedeemScriptType* multisig, char address[MAX_ADDR_SIZE]); -uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, - uint8_t *out); -uint32_t compile_script_multisig(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t *out); -uint32_t compile_script_multisig_hash(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t *hash); -uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, - const uint8_t *pubkey, uint32_t pubkey_len, - uint8_t sighash, uint8_t *out); -uint32_t serialize_script_multisig(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t sighash, uint8_t *out); -int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, - TxOutputBinType *out, bool needs_confirm); - -uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input); -uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data); -uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input); -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, +uint32_t compile_script_sig(uint32_t address_type, const uint8_t* pubkeyhash, + uint8_t* out); +uint32_t compile_script_multisig(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t* out); +uint32_t compile_script_multisig_hash(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t* hash); +uint32_t serialize_script_sig(const uint8_t* signature, uint32_t signature_len, + const uint8_t* pubkey, uint32_t pubkey_len, + uint8_t sighash, uint8_t* out); +uint32_t serialize_script_multisig(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t sighash, uint8_t* out); +int compile_output(const CoinType* coin, const HDNode* root, TxOutputType* in, + TxOutputBinType* out, bool needs_confirm); + +uint32_t tx_prevout_hash(Hasher* hasher, const TxInputType* input); +uint32_t tx_script_hash(Hasher* hasher, uint32_t size, const uint8_t* data); +uint32_t tx_sequence_hash(Hasher* hasher, const TxInputType* input); +uint32_t tx_output_hash(Hasher* hasher, const TxOutputBinType* output, bool decred); -uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out); +uint32_t tx_serialize_script(uint32_t size, const uint8_t* data, uint8_t* out); -uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out); -uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, - uint8_t *out); -uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, - uint8_t *out); -uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, - uint8_t *out); +uint32_t tx_serialize_footer(const TxStruct* tx, uint8_t* out); +uint32_t tx_serialize_input(TxStruct* tx, const TxInputType* input, + uint8_t* out); +uint32_t tx_serialize_output(TxStruct* tx, const TxOutputBinType* output, + uint8_t* out); +uint32_t tx_serialize_decred_witness(TxStruct* tx, const TxInputType* input, + uint8_t* out); -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, +void tx_init(TxStruct* tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, uint32_t version_group_id); -uint32_t tx_serialize_header_hash(TxStruct *tx); -uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); -uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output); -uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, +uint32_t tx_serialize_header_hash(TxStruct* tx); +uint32_t tx_serialize_input_hash(TxStruct* tx, const TxInputType* input); +uint32_t tx_serialize_output_hash(TxStruct* tx, const TxOutputBinType* output); +uint32_t tx_serialize_extra_data_hash(TxStruct* tx, const uint8_t* data, uint32_t datalen); -uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, - const TxInputType *input); -void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); - -uint32_t tx_input_weight(const CoinType *coin, const TxInputType *txinput); -uint32_t tx_output_weight(const CoinType *coin, const curve_info *curve, - const TxOutputType *txoutput); -uint32_t tx_decred_witness_weight(const TxInputType *txinput); +uint32_t tx_serialize_decred_witness_hash(TxStruct* tx, + const TxInputType* input); +void tx_hash_final(TxStruct* t, uint8_t* hash, bool reverse); + +uint32_t tx_input_weight(const CoinType* coin, const TxInputType* txinput); +uint32_t tx_output_weight(const CoinType* coin, const curve_info* curve, + const TxOutputType* txoutput); +uint32_t tx_decred_witness_weight(const TxInputType* txinput); #endif diff --git a/include/keepkey/firmware/tron.h b/include/keepkey/firmware/tron.h new file mode 100644 index 000000000..759680b85 --- /dev/null +++ b/include/keepkey/firmware/tron.h @@ -0,0 +1,59 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef KEEPKEY_FIRMWARE_TRON_H +#define KEEPKEY_FIRMWARE_TRON_H + +#include "trezor/crypto/bip32.h" + +#include "messages-tron.pb.h" + +// TRON address length (Base58Check, typically 34 chars starting with 'T') +#define TRON_ADDRESS_MAX_LEN 64 + +// TRON decimals (1 TRX = 1,000,000 SUN) +#define TRON_DECIMALS 6 + +/** + * Generate TRON address from secp256k1 public key + * @param public_key secp256k1 public key (33 bytes compressed) + * @param address Output buffer for Base58Check encoded address + * @param address_len Length of output buffer + * @return true on success, false on failure + */ +bool tron_getAddress(const uint8_t public_key[33], char* address, + size_t address_len); + +/** + * Format TRON amount (SUN) for display + * @param buf Output buffer + * @param len Length of output buffer + * @param amount Amount in SUN (1 TRX = 1,000,000 SUN) + */ +void tron_formatAmount(char* buf, size_t len, uint64_t amount); + +/** + * Sign a TRON transaction + * @param node HD node containing private key + * @param msg TronSignTx request message + * @param resp TronSignedTx response message (will be filled with signature) + */ +bool tron_signTx(const HDNode* node, const TronSignTx* msg, TronSignedTx* resp); + +#endif diff --git a/include/keepkey/firmware/txin_check.h b/include/keepkey/firmware/txin_check.h index 4b4f0070f..95cb9bb8f 100644 --- a/include/keepkey/firmware/txin_check.h +++ b/include/keepkey/firmware/txin_check.h @@ -27,11 +27,11 @@ #define AMT_STR_LEN 32 #define ADDR_STR_LEN 130 -void txin_dgst_addto(const uint8_t *data, size_t len); +void txin_dgst_addto(const uint8_t* data, size_t len); void txin_dgst_initialize(void); -bool txin_dgst_compare(const char *amt_str, const char *addr_str); +bool txin_dgst_compare(const char* amt_str, const char* addr_str); void txin_dgst_final(void); -void txin_dgst_getstrs(char *prev, char *cur, size_t len); -void txin_dgst_save_and_reset(char *amt_str, char *addr_str); +void txin_dgst_getstrs(char* prev, char* cur, size_t len); +void txin_dgst_save_and_reset(const char* amt_str, const char* addr_str); #endif diff --git a/include/keepkey/firmware/u2f.h b/include/keepkey/firmware/u2f.h index d8049ee38..548268613 100644 --- a/include/keepkey/firmware/u2f.h +++ b/include/keepkey/firmware/u2f.h @@ -35,27 +35,27 @@ typedef struct { #define APDU_LEN(A) (uint32_t)(((A).lc1 << 16) + ((A).lc2 << 8) + ((A).lc3)) -void u2fhid_read(char tiny, const U2FHID_FRAME *buf); -void u2fhid_init_cmd(const U2FHID_FRAME *f); -void u2fhid_read_start(const U2FHID_FRAME *f); -bool u2fhid_write(uint8_t *buf); -void u2fhid_init(const U2FHID_FRAME *in); -void u2fhid_ping(const uint8_t *buf, uint32_t len); -void u2fhid_wink(const uint8_t *buf, uint32_t len); -void u2fhid_sync(const uint8_t *buf, uint32_t len); -void u2fhid_lock(const uint8_t *buf, uint32_t len); -void u2fhid_msg(const APDU *a, uint32_t len); -void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt); - -uint8_t *u2f_out_data(void); -void u2f_register(const APDU *a); -void u2f_version(const APDU *a); -void u2f_authenticate(const APDU *a); - -void send_u2f_msg(const uint8_t *data, uint32_t len); +void u2fhid_read(char tiny, const U2FHID_FRAME* f); +void u2fhid_init_cmd(const U2FHID_FRAME* f); +void u2fhid_read_start(const U2FHID_FRAME* f); +bool u2fhid_write(uint8_t* buf); +void u2fhid_init(const U2FHID_FRAME* in); +void u2fhid_ping(const uint8_t* buf, uint32_t len); +void u2fhid_wink(const uint8_t* buf, uint32_t len); +void u2fhid_sync(const uint8_t* buf, uint32_t len); +void u2fhid_lock(const uint8_t* buf, uint32_t len); +void u2fhid_msg(const APDU* a, uint32_t len); +void queue_u2f_pkt(const U2FHID_FRAME* u2f_pkt); + +uint8_t* u2f_out_data(void); +void u2f_register(const APDU* a); +void u2f_version(const APDU* a); +void u2f_authenticate(const APDU* a); + +void send_u2f_msg(const uint8_t* data, uint32_t len); void send_u2f_error(uint16_t err); -void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, +void send_u2fhid_msg(const uint8_t cmd, const uint8_t* data, const uint32_t len); void send_u2fhid_error(uint32_t fcid, uint8_t err); diff --git a/include/keepkey/rand/rng.h b/include/keepkey/rand/rng.h index f84834a7b..93f1854a2 100644 --- a/include/keepkey/rand/rng.h +++ b/include/keepkey/rand/rng.h @@ -26,7 +26,7 @@ /// Reset the hardware random number generator void reset_rng(void); -void random_permute_char(char *buf, size_t len); -void random_permute_u16(uint16_t *buf, size_t count); +void random_permute_char(char* str, size_t len); +void random_permute_u16(uint16_t* buf, size_t count); #endif diff --git a/include/keepkey/transport/interface.h b/include/keepkey/transport/interface.h index ffebf205d..45e5a09e6 100644 --- a/include/keepkey/transport/interface.h +++ b/include/keepkey/transport/interface.h @@ -35,6 +35,9 @@ #include "messages-tendermint.pb.h" #include "messages-thorchain.pb.h" #include "messages-mayachain.pb.h" +#include "messages-tron.pb.h" +#include "messages-ton.pb.h" +#include "messages-solana.pb.h" #include "types.pb.h" #include "trezor_transport.h" diff --git a/include/keepkey/transport/messages-ethereum.options b/include/keepkey/transport/messages-ethereum.options index 74a251eba..65b6a1a2f 100644 --- a/include/keepkey/transport/messages-ethereum.options +++ b/include/keepkey/transport/messages-ethereum.options @@ -47,3 +47,6 @@ Ethereum712TypesValues.eip712types max_size:2048 Ethereum712TypesValues.eip712primetype max_size:80 Ethereum712TypesValues.eip712data max_size:2048 +EthereumTxMetadata.signed_payload max_size:1024 +EthereumMetadataAck.display_summary max_size:32 + diff --git a/include/keepkey/transport/messages-solana.options b/include/keepkey/transport/messages-solana.options new file mode 100644 index 000000000..7c9e3978f --- /dev/null +++ b/include/keepkey/transport/messages-solana.options @@ -0,0 +1,21 @@ +SolanaGetAddress.address_n max_count:8 +SolanaGetAddress.coin_name max_size:21 + +SolanaAddress.address max_size:64 + +SolanaTokenInfo.mint max_size:32 +SolanaTokenInfo.symbol max_size:13 + +SolanaSignTx.address_n max_count:8 +SolanaSignTx.coin_name max_size:21 +SolanaSignTx.raw_tx max_size:2048 +SolanaSignTx.token_info max_count:4 + +SolanaSignedTx.signature max_size:64 + +SolanaSignMessage.address_n max_count:8 +SolanaSignMessage.coin_name max_size:21 +SolanaSignMessage.message max_size:1024 + +SolanaMessageSignature.public_key max_size:32 +SolanaMessageSignature.signature max_size:64 diff --git a/include/keepkey/transport/messages-ton.options b/include/keepkey/transport/messages-ton.options new file mode 100644 index 000000000..413a14939 --- /dev/null +++ b/include/keepkey/transport/messages-ton.options @@ -0,0 +1,13 @@ +TonGetAddress.address_n max_count:8 +TonGetAddress.coin_name max_size:21 + +TonAddress.address max_size:50 +TonAddress.raw_address max_size:70 + +TonSignTx.address_n max_count:8 +TonSignTx.coin_name max_size:21 +TonSignTx.raw_tx max_size:1024 +TonSignTx.to_address max_size:50 +TonSignTx.memo max_size:121 + +TonSignedTx.signature max_size:64 diff --git a/include/keepkey/transport/messages-tron.options b/include/keepkey/transport/messages-tron.options new file mode 100644 index 000000000..9cd09facb --- /dev/null +++ b/include/keepkey/transport/messages-tron.options @@ -0,0 +1,21 @@ +TronGetAddress.address_n max_count:8 +TronGetAddress.coin_name max_size:21 + +TronAddress.address max_size:35 + +TronTransferContract.to_address max_size:35 + +TronTriggerSmartContract.contract_address max_size:35 +TronTriggerSmartContract.data max_size:512 + +TronSignTx.address_n max_count:8 +TronSignTx.coin_name max_size:21 +TronSignTx.raw_data max_size:2048 +TronSignTx.ref_block_bytes max_size:4 +TronSignTx.ref_block_hash max_size:32 +TronSignTx.contract_type max_size:64 +TronSignTx.to_address max_size:35 +TronSignTx.data max_size:256 + +TronSignedTx.signature max_size:65 +TronSignedTx.serialized_tx max_size:1024 diff --git a/include/keepkey/transport/messages.options b/include/keepkey/transport/messages.options index 41e0c377c..525b39161 100644 --- a/include/keepkey/transport/messages.options +++ b/include/keepkey/transport/messages.options @@ -109,7 +109,7 @@ ApplyPolicies.policy max_count:16 FirmwareUpload.payload_hash max_size:32 FirmwareUpload.payload max_size:0 -DebugLinkState.layout max_size:1024 +DebugLinkState.layout max_size:2048 DebugLinkState.pin max_size:10 DebugLinkState.matrix max_size:10 DebugLinkState.mnemonic max_size:241 @@ -131,3 +131,5 @@ FlashHash.challenge max_size:32 FlashWrite.data max_size:1024 FlashHashResponse.data max_size:32 + +Bip85Mnemonic.mnemonic max_size:241 diff --git a/lib/board/common.c b/lib/board/common.c index 06e4518d6..e37b94e42 100644 --- a/lib/board/common.c +++ b/lib/board/common.c @@ -40,16 +40,16 @@ void drbg_init() { hmac_drbg_init(&drbg_ctx, entropy, sizeof(entropy), NULL, 0); } -void drbg_reseed(const uint8_t *entropy, size_t len) { +void drbg_reseed(const uint8_t* entropy, size_t len) { hmac_drbg_reseed(&drbg_ctx, entropy, len, NULL, 0); } -void drbg_generate(uint8_t *buf, size_t len) { +void drbg_generate(uint8_t* buf, size_t len) { hmac_drbg_generate(&drbg_ctx, buf, len); } uint32_t drbg_random32(void) { uint32_t value = 0; - drbg_generate((uint8_t *)&value, sizeof(value)); + drbg_generate((uint8_t*)&value, sizeof(value)); return value; } diff --git a/lib/board/confirm_sm.c b/lib/board/confirm_sm.c index 534930f06..0669536ab 100644 --- a/lib/board/confirm_sm.c +++ b/lib/board/confirm_sm.c @@ -48,10 +48,10 @@ static CONFIDENTIAL char strbuf[BODY_CHAR_MAX]; /// Handler for push button being pressed. /// \param context current state context. -static void handle_screen_press(void *context) { +static void handle_screen_press(void* context) { assert(context != NULL); - StateInfo *si = (StateInfo *)context; + StateInfo* si = (StateInfo*)context; if (button_request_acked) { switch (si->display_state) { @@ -68,10 +68,10 @@ static void handle_screen_press(void *context) { /// Handler for push button being pressed. /// \param context current state context. -static void handle_screen_release(void *context) { +static void handle_screen_release(void* context) { assert(context != NULL); - StateInfo *si = (StateInfo *)context; + StateInfo* si = (StateInfo*)context; switch (si->display_state) { case CONFIRM_WAIT: @@ -91,10 +91,10 @@ static void handle_screen_release(void *context) { /// User has held down the push button for duration as requested. /// \param context current state context. -static void handle_confirm_timeout(void *context) { +static void handle_confirm_timeout(void* context) { assert(context != NULL); - StateInfo *si = (StateInfo *)context; + StateInfo* si = (StateInfo*)context; si->display_state = CONFIRMED; si->active_layout = LAYOUT_CONFIRMED; } @@ -104,7 +104,7 @@ static void handle_confirm_timeout(void *context) { /// \param si current state information. /// \param layout_notification_func layout callback for displaying confirm /// message. -static void swap_layout(ActiveLayout active_layout, volatile StateInfo *si, +static void swap_layout(ActiveLayout active_layout, volatile StateInfo* si, layout_notification_t layout_notification_func) { switch (active_layout) { case LAYOUT_REQUEST: @@ -126,9 +126,9 @@ static void swap_layout(ActiveLayout active_layout, volatile StateInfo *si, si->lines[active_layout].request_body, NOTIFICATION_CONFIRM_ANIMATION); if (si->immediate) { - post_delayed(&handle_confirm_timeout, (void *)si, 1); + post_delayed(&handle_confirm_timeout, (void*)si, 1); } else { - post_delayed(&handle_confirm_timeout, (void *)si, CONFIRM_TIMEOUT_MS); + post_delayed(&handle_confirm_timeout, (void*)si, CONFIRM_TIMEOUT_MS); } break; @@ -156,21 +156,22 @@ static void swap_layout(ActiveLayout active_layout, volatile StateInfo *si, /// \param requesta_body The body of the confirmation message. /// \param layout_notification_func layout callback for displaying confirm /// message. \returns true iff the device confirmed. -static bool confirm_helper(const char *request_title_param, const char *request_body, - layout_notification_t layout_notification_func, - bool constant_power, IconType iconNum, bool immediate) -{ +static bool confirm_helper(const char* request_title_param, + const char* request_body, + layout_notification_t layout_notification_func, + bool constant_power, IconType iconNum, + bool immediate) { bool ret_stat = false; volatile StateInfo state_info; ActiveLayout new_layout, cur_layout; DisplayState new_ds; uint16_t tiny_msg; static CONFIDENTIAL uint8_t msg_tiny_buf[MSG_TINY_BFR_SZ]; - const char *request_title; + const char* request_title; request_title = request_title_param; #if DEBUG_LINK - DebugLinkDecision *dld; + const DebugLinkDecision* dld; bool debug_decided = false; #endif @@ -178,7 +179,7 @@ static bool confirm_helper(const char *request_title_param, const char *request_ reset_msg_stack = false; - memset((void *)&state_info, 0, sizeof(state_info)); + memset((void*)&state_info, 0, sizeof(state_info)); state_info.immediate = immediate; state_info.display_state = HOME; state_info.active_layout = LAYOUT_REQUEST; @@ -197,10 +198,9 @@ static bool confirm_helper(const char *request_title_param, const char *request_ state_info.lines[LAYOUT_CONFIRMED].request_title = request_title; state_info.lines[LAYOUT_CONFIRMED].request_body = request_body; - keepkey_button_set_on_press_handler(&handle_screen_press, - (void *)&state_info); + keepkey_button_set_on_press_handler(&handle_screen_press, (void*)&state_info); keepkey_button_set_on_release_handler(&handle_screen_release, - (void *)&state_info); + (void*)&state_info); cur_layout = LAYOUT_INVALID; @@ -218,75 +218,71 @@ static bool confirm_helper(const char *request_title_param, const char *request_ #ifndef EMULATOR if (usbInitialized()) #else - if(1) + if (1) #endif - { - /* Listen for tiny messages */ - tiny_msg = check_for_tiny_msg(msg_tiny_buf); - - switch(tiny_msg) - { - case MessageType_MessageType_ButtonAck: - button_request_acked = true; - break; - - case MessageType_MessageType_Cancel: - case MessageType_MessageType_Initialize: - if(tiny_msg == MessageType_MessageType_Initialize) - { - reset_msg_stack = true; - } - - ret_stat = false; - goto confirm_helper_exit; + { + /* Listen for tiny messages */ + tiny_msg = check_for_tiny_msg(msg_tiny_buf); + + switch (tiny_msg) { + case MessageType_MessageType_ButtonAck: + button_request_acked = true; + break; + + case MessageType_MessageType_Cancel: + case MessageType_MessageType_Initialize: + if (tiny_msg == MessageType_MessageType_Initialize) { + reset_msg_stack = true; + } + + ret_stat = false; + goto confirm_helper_exit; #if DEBUG_LINK - case MessageType_MessageType_DebugLinkDecision: - dld = (DebugLinkDecision *)msg_tiny_buf; - ret_stat = dld->yes_no; - debug_decided = true; - break; + case MessageType_MessageType_DebugLinkDecision: + dld = (DebugLinkDecision*)msg_tiny_buf; + ret_stat = dld->yes_no; + debug_decided = true; + break; - case MessageType_MessageType_DebugLinkGetState: - call_msg_debug_link_get_state_handler((DebugLinkGetState *)msg_tiny_buf); - break; + case MessageType_MessageType_DebugLinkGetState: + call_msg_debug_link_get_state_handler( + (DebugLinkGetState*)msg_tiny_buf); + break; #endif - default: - break; /* break from switch statement and stay in the while loop*/ - } - } + default: + break; /* break from switch statement and stay in the while loop*/ + } + } - if(new_ds == FINISHED) - { - ret_stat = true; - break; /* confirmation done. Exiting function */ - } + if (new_ds == FINISHED) { + ret_stat = true; + break; /* confirmation done. Exiting function */ + } - if(cur_layout != new_layout) - { - swap_layout(new_layout, &state_info, layout_notification_func); - cur_layout = new_layout; - } + if (cur_layout != new_layout) { + swap_layout(new_layout, &state_info, layout_notification_func); + cur_layout = new_layout; + } #if DEBUG_LINK - if(debug_decided && button_request_acked) - { - break; /* confirmation done via debug link. Exiting function */ - } + if (debug_decided && button_request_acked) { + break; /* confirmation done via debug link. Exiting function */ + } #endif - if (iconNum != NO_ICON) { - layout_add_icon(iconNum); - } + if (iconNum != NO_ICON) { + layout_add_icon(iconNum); + } - display_constant_power(constant_power); + display_constant_power(constant_power); - display_refresh(); - animate(); - } + display_refresh(); + animate(); + } confirm_helper_exit: @@ -296,187 +292,190 @@ static bool confirm_helper(const char *request_title_param, const char *request_ return (ret_stat); } -bool confirm(ButtonRequestType type, const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - bool ret = confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return ret; +bool confirm(ButtonRequestType type, const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + bool ret = + confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return ret; } -bool confirm_constant_power(ButtonRequestType type, const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - bool ret = confirm_helper(request_title, strbuf, &layout_constant_power_notification, true, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return ret; +bool confirm_constant_power(ButtonRequestType type, const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + bool ret = + confirm_helper(request_title, strbuf, &layout_constant_power_notification, + true, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return ret; } +bool confirm_with_custom_button_request(const ButtonRequest* button_request, + const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); -bool confirm_with_custom_button_request(ButtonRequest *button_request, - const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; + /* Send button request */ + msg_write(MessageType_MessageType_ButtonRequest, button_request); - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - msg_write(MessageType_MessageType_ButtonRequest, button_request); - - bool ret = confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return ret; + bool ret = + confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return ret; } bool confirm_with_custom_layout(layout_notification_t layout_notification_func, ButtonRequestType type, - const char *request_title, const char *request_body, ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - bool ret = confirm_helper(request_title, strbuf, layout_notification_func, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return ret; + const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + bool ret = confirm_helper(request_title, strbuf, layout_notification_func, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return ret; } -bool confirm_without_button_request(const char *request_title, const char *request_body, - ...) -{ - button_request_acked = true; +bool confirm_without_button_request(const char* request_title, + const char* request_body, ...) { + button_request_acked = true; - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); - bool ret = confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return ret; + bool ret = + confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return ret; } -bool review(ButtonRequestType type, const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - (void)confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return true; +bool review(ButtonRequestType type, const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + (void)confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return true; } -bool review_without_button_request(const char *request_title, const char *request_body, - ...) -{ - button_request_acked = true; +bool review_without_button_request(const char* request_title, + const char* request_body, ...) { + button_request_acked = true; - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); - (void)confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, false); - memzero(strbuf, sizeof(strbuf)); - return true; + (void)confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, false); + memzero(strbuf, sizeof(strbuf)); + return true; } -bool review_with_icon(ButtonRequestType type, IconType iconNum, const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - (void)confirm_helper(request_title, strbuf, &layout_standard_notification, false, iconNum, false); - memzero(strbuf, sizeof(strbuf)); - return true; +bool review_with_icon(ButtonRequestType type, IconType iconNum, + const char* request_title, const char* request_body, + ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + (void)confirm_helper(request_title, strbuf, &layout_standard_notification, + false, iconNum, false); + memzero(strbuf, sizeof(strbuf)); + return true; } -bool review_immediate(ButtonRequestType type, const char *request_title, const char *request_body, - ...) -{ - button_request_acked = false; - - va_list vl; - va_start(vl, request_body); - vsnprintf(strbuf, sizeof(strbuf), request_body, vl); - va_end(vl); - - /* Send button request */ - ButtonRequest resp; - memset(&resp, 0, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - (void)confirm_helper(request_title, strbuf, &layout_standard_notification, false, NO_ICON, true); - memzero(strbuf, sizeof(strbuf)); - return true; +bool review_immediate(ButtonRequestType type, const char* request_title, + const char* request_body, ...) { + button_request_acked = false; + + va_list vl; + va_start(vl, request_body); + vsnprintf(strbuf, sizeof(strbuf), request_body, vl); + va_end(vl); + + /* Send button request */ + ButtonRequest resp; + memset(&resp, 0, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + (void)confirm_helper(request_title, strbuf, &layout_standard_notification, + false, NO_ICON, true); + memzero(strbuf, sizeof(strbuf)); + return true; } - diff --git a/lib/board/draw.c b/lib/board/draw.c index 4e7e6cf3a..3a51e145f 100644 --- a/lib/board/draw.c +++ b/lib/board/draw.c @@ -42,8 +42,8 @@ * OUTPUT * true/false whether image was drawn */ -bool draw_char_with_shift(Canvas *canvas, DrawableParams *p, uint16_t *x_shift, - uint16_t *y_shift, const CharacterImage *img) { +bool draw_char_with_shift(Canvas* canvas, DrawableParams* p, uint16_t* x_shift, + uint16_t* y_shift, const CharacterImage* img) { bool ret_stat = false; uint16_t start_index = (p->y * canvas->width) + p->x; @@ -51,15 +51,15 @@ bool draw_char_with_shift(Canvas *canvas, DrawableParams *p, uint16_t *x_shift, if (start_index >= (KEEPKEY_DISPLAY_HEIGHT * KEEPKEY_DISPLAY_WIDTH)) { return false; } - uint8_t *canvas_pixel = &canvas->buffer[start_index]; - uint8_t *canvas_end = &canvas->buffer[canvas->width * canvas->height]; + uint8_t* canvas_pixel = &canvas->buffer[start_index]; + const uint8_t* canvas_end = &canvas->buffer[canvas->width * canvas->height]; /* Check that this was a character that we have in the font */ if (img != NULL) { /* Check that it's within bounds. */ if (((img->width + p->x) <= canvas->width) && ((img->height + p->y) <= canvas->height)) { - const uint8_t *img_pixel = &img->data[0]; + const uint8_t* img_pixel = &img->data[0]; int y; @@ -108,10 +108,11 @@ bool draw_char_with_shift(Canvas *canvas, DrawableParams *p, uint16_t *x_shift, * OUTPUT * none */ -void draw_string(Canvas *canvas, const Font *font, const char *str_write, - DrawableParams *p, uint16_t width, uint16_t line_height) { - - uint16_t sepPixels = 0; // font char separation pixels for large font (pin font) +void draw_string(Canvas* canvas, const Font* font, const char* str_write, + const DrawableParams* p, uint16_t width, + uint16_t line_height) { + uint16_t sepPixels = + 0; // font char separation pixels for large font (pin font) if (!canvas) { return; @@ -126,9 +127,9 @@ void draw_string(Canvas *canvas, const Font *font, const char *str_write, DrawableParams char_params = *p; while (*str_write && have_space) { - const CharacterImage *img = font_get_char(font, *str_write); + const CharacterImage* img = font_get_char(font, *str_write); uint16_t word_width = img->width; - char *next_c = (char *)str_write + 1; + const char* next_c = str_write + 1; /* Allow line breaks */ if (*str_write == '\n') { @@ -184,8 +185,8 @@ void draw_string(Canvas *canvas, const Font *font, const char *str_write, * OUTPUT * none */ -void draw_char(Canvas *canvas, const Font *font, char c, DrawableParams *p) { - const CharacterImage *img = font_get_char(font, c); +void draw_char(Canvas* canvas, const Font* font, char c, DrawableParams* p) { + const CharacterImage* img = font_get_char(font, c); uint16_t x_offset = 0; /* Draw Character */ @@ -208,7 +209,7 @@ void draw_char(Canvas *canvas, const Font *font, char c, DrawableParams *p) { * OUTPUT * none */ -void draw_char_simple(Canvas *canvas, const Font *font, char c, uint8_t color, +void draw_char_simple(Canvas* canvas, const Font* font, char c, uint8_t color, uint16_t x, uint16_t y) { DrawableParams p; p.color = color; @@ -227,7 +228,7 @@ void draw_char_simple(Canvas *canvas, const Font *font, char c, uint8_t color, * OUTPUT * none */ -void draw_box(Canvas *canvas, BoxDrawableParams *p) { +void draw_box(Canvas* canvas, BoxDrawableParams* p) { uint16_t start_row = p->base.y; uint16_t end_row = start_row + p->height; end_row = (end_row >= canvas->height) ? canvas->height - 1 : end_row; @@ -237,8 +238,8 @@ void draw_box(Canvas *canvas, BoxDrawableParams *p) { end_col = (end_col >= canvas->width) ? canvas->width - 1 : end_col; uint16_t start_index = (start_row * canvas->width) + start_col; - uint8_t *canvas_pixel = &canvas->buffer[start_index]; - uint8_t *canvas_end = &canvas->buffer[canvas->width * canvas->height]; + uint8_t* canvas_pixel = &canvas->buffer[start_index]; + const uint8_t* canvas_end = &canvas->buffer[canvas->width * canvas->height]; uint16_t height = end_row - start_row; uint16_t width = end_col - start_col; @@ -271,7 +272,7 @@ void draw_box(Canvas *canvas, BoxDrawableParams *p) { * OUTPUT * none */ -void draw_box_simple(Canvas *canvas, uint8_t color, uint16_t x, uint16_t y, +void draw_box_simple(Canvas* canvas, uint8_t color, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { BoxDrawableParams box_params = {{color, x, y}, height, width}; draw_box(canvas, &box_params); @@ -286,13 +287,13 @@ void draw_box_simple(Canvas *canvas, uint8_t color, uint16_t x, uint16_t y, * OUTPUT * true/false whether image was drawn */ -bool draw_bitmap_mono_rle(Canvas *canvas, const AnimationFrame *frame, +bool draw_bitmap_mono_rle(Canvas* canvas, const AnimationFrame* frame, bool erase) { if (!frame || !canvas) { return false; } - const Image *img = frame->image; + const Image* img = frame->image; const uint8_t color = erase ? 0x0 : frame->color; /* Check that image will fit in bounds */ diff --git a/lib/board/font.c b/lib/board/font.c index 4762bada2..5fccfcb33 100644 --- a/lib/board/font.c +++ b/lib/board/font.c @@ -22,7 +22,8 @@ #include -/* --- Image Font ------------------------------------------------------------ */ +/* --- Image Font ------------------------------------------------------------ + */ static const uint8_t image_font_sadface_9x10[9 * 10] = { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, @@ -2497,7 +2498,7 @@ static const Font body_font = { * OUTPUT * PIN font */ -const Font *get_pin_font(void) { return &pin_font; } +const Font* get_pin_font(void) { return &pin_font; } /* * get_title_font() - Get pointer to title font @@ -2507,7 +2508,7 @@ const Font *get_pin_font(void) { return &pin_font; } * OUTPUT * title font */ -const Font *get_title_font(void) { return &title_font; } +const Font* get_title_font(void) { return &title_font; } /* * get_body_font() - Get pointer to body font @@ -2518,7 +2519,7 @@ const Font *get_title_font(void) { return &title_font; } * body font * */ -const Font *get_body_font(void) { return &body_font; } +const Font* get_body_font(void) { return &body_font; } /* * font_get_char() - Get a character from the provided font @@ -2533,7 +2534,7 @@ const Font *get_body_font(void) { return &body_font; } * pointer to ascii charactor image * */ -const CharacterImage *font_get_char(const Font *font, char c) { +const CharacterImage* font_get_char(const Font* font, char c) { for (int i = 0; i < font->length; i++) { if (font->characters[i].code == c) { return font->characters[i].image; @@ -2551,7 +2552,7 @@ const CharacterImage *font_get_char(const Font *font, char c) { * OUTPUT * font height */ -uint32_t font_height(const Font *font) { return font->size; } +uint32_t font_height(const Font* font) { return font->size; } /* * font_width() - Get font width @@ -2561,7 +2562,7 @@ uint32_t font_height(const Font *font) { return font->size; } * OUTPUT * font width */ -uint32_t font_width(const Font *font) { +uint32_t font_width(const Font* font) { /*Return worst case width using the | char as the reference. */ return font_get_char(font, '|')->width; } @@ -2575,7 +2576,7 @@ uint32_t font_width(const Font *font) { * OUTPUT * calculated string width */ -uint32_t calc_str_width(const Font *font, const char *str) { +uint32_t calc_str_width(const Font* font, const char* str) { int width = 0; while (str[0] != '\0') { @@ -2597,14 +2598,14 @@ uint32_t calc_str_width(const Font *font, const char *str) { * OUTPUT * line count */ -uint32_t calc_str_line(const Font *font, const char *str, uint16_t line_width) { +uint32_t calc_str_line(const Font* font, const char* str, uint16_t line_width) { uint8_t line_count = 1; uint16_t x_offset = 0; while (*str) { uint8_t character_width = font_get_char(font, str[0])->width; uint16_t word_width = character_width; - char *next_character = (char *)str + 1; + const char* next_character = str + 1; /* Allow line breaks */ if (*str == '\n') { diff --git a/lib/board/keepkey_board.c b/lib/board/keepkey_board.c index c2e83fa57..eeae2b65d 100644 --- a/lib/board/keepkey_board.c +++ b/lib/board/keepkey_board.c @@ -130,19 +130,19 @@ static uint32_t reverse(unsigned x) { * OUTPUT * crc32 of data */ -uint32_t calc_crc32(const void *data, int word_len) { +uint32_t calc_crc32(const void* data, int word_len) { uint32_t crc32 = 0; #ifndef EMULATOR crc_reset(); - crc32 = crc_calculate_block((uint32_t *)data, word_len); + crc32 = crc_calculate_block((uint32_t*)data, word_len); #else /// http://www.hackersdelight.org/hdcodetxt/crc.c.txt crc32 = 0xFFFFFFFF; for (int i = 0; i < word_len; i++) { - uint32_t byte = ((const char *)data)[i]; // Get next byte. - byte = reverse(byte); // 32-bit reversal. - for (int j = 0; j <= 7; j++) { // Do eight times. + uint32_t byte = ((const char*)data)[i]; // Get next byte. + byte = reverse(byte); // 32-bit reversal. + for (int j = 0; j <= 7; j++) { // Do eight times. if ((int)(crc32 ^ byte) < 0) crc32 = (crc32 << 1) ^ 0x04C11DB7; else diff --git a/lib/board/keepkey_button.c b/lib/board/keepkey_button.c index b0b453faa..be94fad02 100644 --- a/lib/board/keepkey_button.c +++ b/lib/board/keepkey_button.c @@ -34,8 +34,8 @@ static Handler on_press_handler = NULL; static Handler on_release_handler = NULL; -static void *on_release_handler_context = NULL; -static void *on_press_handler_context = NULL; +static void* on_release_handler_context = NULL; +static void* on_press_handler_context = NULL; #ifndef EMULATOR static const uint16_t BUTTON_PIN = GPIO7; @@ -91,7 +91,7 @@ void keepkey_button_init(void) { * none * */ -void keepkey_button_set_on_press_handler(Handler handler, void *context) { +void keepkey_button_set_on_press_handler(Handler handler, void* context) { on_press_handler = handler; on_press_handler_context = context; } @@ -107,7 +107,7 @@ void keepkey_button_set_on_press_handler(Handler handler, void *context) { * none * */ -void keepkey_button_set_on_release_handler(Handler handler, void *context) { +void keepkey_button_set_on_release_handler(Handler handler, void* context) { on_release_handler = handler; on_release_handler_context = context; } @@ -137,7 +137,10 @@ bool keepkey_button_up(void) { * OUTPUT * true/false state of push button down */ -bool keepkey_button_down(void) { return !keepkey_button_up(); } +bool keepkey_button_down(void) { + // cppcheck-suppress knownConditionTrueFalse + return !keepkey_button_up(); +} void buttonisr_usr(void) { #ifndef EMULATOR diff --git a/lib/board/keepkey_display.c b/lib/board/keepkey_display.c index 1a0fe38ab..65383efbb 100644 --- a/lib/board/keepkey_display.c +++ b/lib/board/keepkey_display.c @@ -260,7 +260,7 @@ static void display_write_ram(uint8_t val) { * OUTPUT * pointer to canvas */ -Canvas *display_canvas_init(void) { +Canvas* display_canvas_init(void) { /* Prepare the canvas */ canvas.buffer = canvas_buffer; canvas.width = KEEPKEY_DISPLAY_WIDTH; @@ -278,9 +278,9 @@ Canvas *display_canvas_init(void) { * OUTPUT * pointer to canvas */ -Canvas *display_canvas(void) { return &canvas; } +Canvas* display_canvas(void) { return &canvas; } -void (*DumpDisplay)(const uint8_t *buf) = 0; +void (*DumpDisplay)(const uint8_t* buf) = 0; void display_set_dump_callback(DumpDisplayCallback d) { DumpDisplay = d; } /* @@ -291,30 +291,28 @@ void display_set_dump_callback(DumpDisplayCallback d) { DumpDisplay = d; } * OUTPUT * none */ -void display_refresh(void) -{ - if (DumpDisplay) { - DumpDisplay(canvas.buffer); - } +void display_refresh(void) { + if (DumpDisplay) { + DumpDisplay(canvas.buffer); + } - if(!canvas.dirty) - { - return; - } + if (!canvas.dirty) { + return; + } - if (constant_power) { - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 128; x++) { - canvas.buffer[y * 256 + x] = 255 - canvas.buffer[y * 256 + x + 128]; - } - } + if (constant_power) { + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 128; x++) { + canvas.buffer[y * 256 + x] = 255 - canvas.buffer[y * 256 + x + 128]; + } } + } - display_prepare_gram_write(); + display_prepare_gram_write(); - int num_writes = canvas.width * canvas.height; + int num_writes = canvas.width * canvas.height; - int i; + int i; #ifdef INVERT_DISPLAY for (i = num_writes; i > 0; i -= 2) { @@ -348,15 +346,9 @@ void display_turn_on(void) { display_write_reg((uint8_t)0xAF); } * OUTPUT * none */ -void display_turn_off(void) -{ - display_write_reg((uint8_t)0xAE); -} +void display_turn_off(void) { display_write_reg((uint8_t)0xAE); } -void display_constant_power(bool enabled) -{ - constant_power = enabled; -} +void display_constant_power(bool enabled) { constant_power = enabled; } /* * display_hw_init(void) - Display hardware initialization diff --git a/lib/board/keepkey_flash.c b/lib/board/keepkey_flash.c index 5f92bb732..31243d3bd 100644 --- a/lib/board/keepkey_flash.c +++ b/lib/board/keepkey_flash.c @@ -51,7 +51,7 @@ uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN]; */ intptr_t flash_write_helper(Allocation group) { intptr_t start = 0; - const FlashSector *s = flash_sector_map; + const FlashSector* s = flash_sector_map; while (s->use != FLASH_INVALID) { if (s->use == group) { start = s->start; @@ -97,7 +97,7 @@ bool flash_chk_status(void) { */ void flash_erase_word(Allocation group) { #ifndef EMULATOR - const FlashSector *s = flash_sector_map; + const FlashSector* s = flash_sector_map; while (s->use != FLASH_INVALID) { if (s->use == group) { svc_flash_erase_sector((uint32_t)s->sector); @@ -119,7 +119,7 @@ void flash_erase_word(Allocation group) { * true/false status of write */ bool flash_write_word(Allocation group, uint32_t offset, uint32_t len, - const uint8_t *data) { + const uint8_t* data) { #ifndef EMULATOR bool retval = true; uint32_t start = flash_write_helper(group); @@ -162,7 +162,7 @@ bool flash_write_word(Allocation group, uint32_t offset, uint32_t len, fww_exit: return (retval); #else - memcpy((void *)(flash_write_helper(group) + offset), data, len); + memcpy((void*)(flash_write_helper(group) + offset), data, len); return true; #endif } @@ -179,7 +179,7 @@ bool flash_write_word(Allocation group, uint32_t offset, uint32_t len, * true/false status of write */ bool flash_write(Allocation group, uint32_t offset, uint32_t len, - const uint8_t *data) { + const uint8_t* data) { #ifndef EMULATOR bool retval = true; uint32_t start = flash_write_helper(group); @@ -188,7 +188,7 @@ bool flash_write(Allocation group, uint32_t offset, uint32_t len, } return (retval); #else - memcpy((void *)(flash_write_helper(group) + offset), data, len); + memcpy((void*)(flash_write_helper(group) + offset), data, len); return true; #endif } @@ -205,7 +205,7 @@ bool is_mfg_mode(void) { #if defined(EMULATOR) || defined(DEBUG_ON) return false; #else - if (*(uint32_t *)OTP_MFG_ADDR == OTP_MFG_SIG) { + if (*(uint32_t*)OTP_MFG_ADDR == OTP_MFG_SIG) { return false; } @@ -223,17 +223,17 @@ bool is_mfg_mode(void) { */ bool set_mfg_mode_off(void) { bool ret_val = false; - uint32_t tvar; #ifndef EMULATOR + uint32_t tvar; /* check OTP lock state before updating */ - if (*(uint8_t *)OTP_BLK_LOCK(OTP_MFG_ADDR) == 0xFF) { + if (*(uint8_t*)OTP_BLK_LOCK(OTP_MFG_ADDR) == 0xFF) { tvar = OTP_MFG_SIG; /* set manufactur'ed signature */ - svc_flash_pgm_blk(OTP_MFG_ADDR, (uint32_t)((uint8_t *)&tvar), + svc_flash_pgm_blk(OTP_MFG_ADDR, (uint32_t)((uint8_t*)&tvar), OTP_MFG_SIG_LEN); tvar = 0x00; /* set OTP lock */ if (svc_flash_pgm_blk(OTP_BLK_LOCK(OTP_MFG_ADDR), - (uint32_t)((uint8_t *)&tvar), 1)) { + (uint32_t)((uint8_t*)&tvar), 1)) { ret_val = true; } } @@ -242,16 +242,16 @@ bool set_mfg_mode_off(void) { return (ret_val); } -const char *flash_getModel(void) { +const char* flash_getModel(void) { #ifndef EMULATOR #ifdef DEBUG_ON return "K1-14AM"; // return a model number for debugger builds #endif - if (*((uint8_t *)OTP_MODEL_ADDR) == 0xFF) return NULL; + if (*((uint8_t*)OTP_MODEL_ADDR) == 0xFF) return NULL; - return (char *)OTP_MODEL_ADDR; + return (char*)OTP_MODEL_ADDR; #else // TODO: actually make this settable in the emulator return "K1-14AM"; @@ -261,9 +261,9 @@ const char *flash_getModel(void) { bool flash_setModel(const char (*model)[MODEL_STR_SIZE]) { #ifndef EMULATOR // Check OTP lock state before updating - if (*(uint8_t *)OTP_BLK_LOCK(OTP_MODEL_ADDR) != 0xFF) return false; + if (*(uint8_t*)OTP_BLK_LOCK(OTP_MODEL_ADDR) != 0xFF) return false; - svc_flash_pgm_blk(OTP_MODEL_ADDR, (uint32_t)((uint8_t *)model), + svc_flash_pgm_blk(OTP_MODEL_ADDR, (uint32_t)((uint8_t*)model), sizeof(*model)); uint8_t lock = 0x00; bool ret = svc_flash_pgm_blk(OTP_BLK_LOCK(OTP_MODEL_ADDR), (uint32_t)&lock, @@ -274,8 +274,8 @@ bool flash_setModel(const char (*model)[MODEL_STR_SIZE]) { #endif } -const char *flash_programModel(void) { - const char *ret = flash_getModel(); +const char* flash_programModel(void) { + const char* ret = flash_getModel(); if (ret) return ret; switch (get_bootloaderKind()) { @@ -298,6 +298,7 @@ const char *flash_programModel(void) { #define MODEL_ENTRY_KK(STRING, ENUM) \ static const char model[MODEL_STR_SIZE] = (STRING); #include "keepkey/board/models.def" + // cppcheck-suppress knownConditionTrueFalse if (!is_mfg_mode()) (void)flash_setModel(&model); return model; } @@ -305,6 +306,7 @@ const char *flash_programModel(void) { #define MODEL_ENTRY_SALT(STRING, ENUM) \ static const char model[MODEL_STR_SIZE] = (STRING); #include "keepkey/board/models.def" + // cppcheck-suppress knownConditionTrueFalse if (!is_mfg_mode()) (void)flash_setModel(&model); return model; } @@ -323,7 +325,7 @@ void flash_collectHWEntropy(bool privileged) { memzero(HW_ENTROPY_DATA, HW_ENTROPY_LEN); #else if (privileged) { - desig_get_unique_id((uint32_t *)HW_ENTROPY_DATA); + desig_get_unique_id((uint32_t*)HW_ENTROPY_DATA); // set entropy in the OTP randomness block if (!flash_otp_is_locked(FLASH_OTP_BLOCK_RANDOMNESS)) { uint8_t entropy[FLASH_OTP_BLOCK_SIZE] = {0}; @@ -342,6 +344,6 @@ void flash_collectHWEntropy(bool privileged) { #endif } -void flash_readHWEntropy(uint8_t *buff, size_t size) { +void flash_readHWEntropy(uint8_t* buff, size_t size) { memcpy(buff, HW_ENTROPY_DATA, MIN(sizeof(HW_ENTROPY_DATA), size)); } diff --git a/lib/board/keepkey_usart.c b/lib/board/keepkey_usart.c index 0bf0332b2..bdd4afceb 100644 --- a/lib/board/keepkey_usart.c +++ b/lib/board/keepkey_usart.c @@ -68,7 +68,8 @@ static bool put_console_char(int8_t c) { * OUTPUT * true/false update status */ -static bool get_console_input(char *read_char) { +// cppcheck-suppress constParameterPointer +static bool get_console_input(char* read_char) { #ifndef EMULATOR int timeout_cnt = 100; /* allow 100msec for USART busy timeout*/ bool ret_stat = false; @@ -99,7 +100,7 @@ static bool get_console_input(char *read_char) { * OUTPUT * none */ -static void display_debug_string(char *str) { +static void display_debug_string(const char* str) { do { put_console_char(*str); } while (*(str++)); @@ -164,7 +165,7 @@ void usart_init(void) { * none */ #ifndef EMULATOR -void dbg_print(const char *out_str, ...) { +void dbg_print(const char* out_str, ...) { char str[LARGE_DEBUG_BUF]; va_list arg; @@ -241,7 +242,7 @@ char read_console(void) { } #else // USART_DEBUG_ON #ifndef EMULATOR -void dbg_print(const char *pStr, ...) { (void)pStr; } +void dbg_print(const char* pStr, ...) { (void)pStr; } #endif void usart_init(void) {} #endif diff --git a/lib/board/layout.c b/lib/board/layout.c index c9726ef0f..59e9dd9e2 100644 --- a/lib/board/layout.c +++ b/lib/board/layout.c @@ -38,7 +38,7 @@ static AnimationQueue active_queue = {NULL, 0}; static AnimationQueue free_queue = {NULL, 0}; static Animation animations[MAX_ANIMATIONS]; -static Canvas *canvas = NULL; +static Canvas* canvas = NULL; static volatile bool animate_flag = false; static leaving_handler_t leaving_handler; static bool iconLayout = false; @@ -56,10 +56,10 @@ extern bool constant_power; static void layout_home_helper(bool reversed) { layout_clear(); - const VariantAnimation *logo; + const VariantAnimation* logo; logo = variant_getLogo(reversed); - layout_add_animation(&layout_animate_images, (void *)logo, + layout_add_animation(&layout_animate_images, (void*)logo, get_image_animation_duration(logo)); while (is_animating()) { @@ -76,7 +76,7 @@ static void layout_home_helper(bool reversed) { * OUTPUT * none */ -static void layout_animate_callback(void *context) { +static void layout_animate_callback(void* context) { (void)context; animate_flag = true; } @@ -92,7 +92,7 @@ static void layout_animate_callback(void *context) { */ #if defined(AGGRO_UNDEFINED_FN) static void layout_remove_animation(AnimateCallback callback) { - Animation *animation = animation_queue_get(&active_queue, callback); + Animation* animation = animation_queue_get(&active_queue, callback); if (animation != NULL) { animation_queue_push(&free_queue, animation); @@ -108,7 +108,7 @@ static void layout_remove_animation(AnimateCallback callback) { * OUTPUT * node pointed to by head pointer */ -static Animation *animation_queue_peek(AnimationQueue *queue) { +static Animation* animation_queue_peek(const AnimationQueue* queue) { return queue->head; } @@ -120,7 +120,7 @@ static Animation *animation_queue_peek(AnimationQueue *queue) { * OUTPUT * none */ -static void animation_queue_push(AnimationQueue *queue, Animation *node) { +static void animation_queue_push(AnimationQueue* queue, Animation* node) { if (queue->head != NULL) { node->next = queue->head; } else { @@ -139,8 +139,8 @@ static void animation_queue_push(AnimationQueue *queue, Animation *node) { * OUTPUT * pointer to a node from the queue */ -static Animation *animation_queue_pop(AnimationQueue *queue) { - Animation *animation = queue->head; +static Animation* animation_queue_pop(AnimationQueue* queue) { + Animation* animation = queue->head; if (animation != NULL) { queue->head = animation->next; @@ -159,17 +159,17 @@ static Animation *animation_queue_pop(AnimationQueue *queue) { * OUTPUT * pointer to Animation node */ -static Animation *animation_queue_get(AnimationQueue *queue, +static Animation* animation_queue_get(AnimationQueue* queue, AnimateCallback callback) { - Animation *current = queue->head; - Animation *result = NULL; + Animation* current = queue->head; + Animation* result = NULL; if (current != NULL) { if (current->animate_callback == callback) { result = current; queue->head = current->next; } else { - Animation *previous = current; + Animation* previous = current; current = current->next; while ((current != NULL) && (result == NULL)) { @@ -201,7 +201,7 @@ static Animation *animation_queue_get(AnimationQueue *queue, * OUTPUT * none */ -void layout_init(Canvas *new_canvas) { +void layout_init(Canvas* new_canvas) { canvas = new_canvas; int i; @@ -223,7 +223,7 @@ void layout_init(Canvas *new_canvas) { * OUTPUT * pointer to canvas */ -Canvas *layout_get_canvas(void) { return canvas; } +Canvas* layout_get_canvas(void) { return canvas; } /* * call_leaving_handler() - Call leaving handler @@ -239,11 +239,11 @@ void call_leaving_handler(void) { } } -void kk_strupr(char *str) { +void kk_strupr(char* str) { for (; *str; str++) *str = toupper(*str); } -void kk_strlwr(char *str) { +void kk_strlwr(char* str) { for (; *str; str++) *str = tolower(*str); } @@ -262,15 +262,15 @@ void layout_has_icon(bool tf) { * OUTPUT * none */ -void layout_standard_notification(const char *str1, const char *str2, +void layout_standard_notification(const char* str1, const char* str2, NotificationType type) { call_leaving_handler(); layout_clear(); DrawableParams sp; - const Font *title_font = get_title_font(); + const Font* title_font = get_title_font(); - const Font *body_font = get_body_font(); + const Font* body_font = get_body_font(); uint32_t body_line_count; uint16_t left_margin, body_width, title_width; @@ -316,7 +316,6 @@ void layout_standard_notification(const char *str1, const char *str2, layout_notification_icon(type, &sp); } - /* * layout_add_icon() - Display standard notification * @@ -335,50 +334,46 @@ void layout_add_icon(IconType type) { /* no action requires */ break; } - } -void layout_constant_power_notification(const char *str1, const char *str2, - NotificationType type) -{ - call_leaving_handler(); - layout_clear(); +void layout_constant_power_notification(const char* str1, const char* str2, + NotificationType type) { + call_leaving_handler(); + layout_clear(); - DrawableParams sp; - const Font *title_font = get_title_font(); - const Font *body_font = get_body_font(); - const uint32_t body_line_count = calc_str_line(body_font, str2, BODY_WIDTH); + DrawableParams sp; + const Font* title_font = get_title_font(); + const Font* body_font = get_body_font(); + const uint32_t body_line_count = calc_str_line(body_font, str2, BODY_WIDTH); - /* Determine vertical alignment and body width */ - sp.y = TOP_MARGIN; + /* Determine vertical alignment and body width */ + sp.y = TOP_MARGIN; - if(body_line_count == ONE_LINE) - { - sp.y = TOP_MARGIN_FOR_ONE_LINE; - } - else if(body_line_count == TWO_LINES) - { - sp.y = TOP_MARGIN_FOR_TWO_LINES; - } + if (body_line_count == ONE_LINE) { + sp.y = TOP_MARGIN_FOR_ONE_LINE; + } else if (body_line_count == TWO_LINES) { + sp.y = TOP_MARGIN_FOR_TWO_LINES; + } - /* Format Title */ - char upper_str1[TITLE_CHAR_MAX]; - strlcpy(upper_str1, str1, TITLE_CHAR_MAX); - kk_strupr(upper_str1); + /* Format Title */ + char upper_str1[TITLE_CHAR_MAX]; + strlcpy(upper_str1, str1, TITLE_CHAR_MAX); + kk_strupr(upper_str1); - /* Title */ - sp.x = 128 + LEFT_MARGIN; - sp.color = TITLE_COLOR; - draw_string(canvas, title_font, upper_str1, &sp, TITLE_WIDTH, font_height(title_font)); + /* Title */ + sp.x = 128 + LEFT_MARGIN; + sp.color = TITLE_COLOR; + draw_string(canvas, title_font, upper_str1, &sp, TITLE_WIDTH, + font_height(title_font)); - /* Body */ - sp.y += font_height(body_font) + BODY_TOP_MARGIN; - sp.x = 128 + LEFT_MARGIN; - sp.color = BODY_COLOR; - draw_string(canvas, body_font, str2, &sp, BODY_WIDTH, - font_height(body_font) + BODY_FONT_LINE_PADDING); + /* Body */ + sp.y += font_height(body_font) + BODY_TOP_MARGIN; + sp.x = 128 + LEFT_MARGIN; + sp.color = BODY_COLOR; + draw_string(canvas, body_font, str2, &sp, BODY_WIDTH, + font_height(body_font) + BODY_FONT_LINE_PADDING); - layout_notification_icon(type, &sp); + layout_notification_icon(type, &sp); } /* @@ -390,7 +385,7 @@ void layout_constant_power_notification(const char *str1, const char *str2, * OUTPUT * none */ -void layout_notification_icon(NotificationType type, DrawableParams *sp) { +void layout_notification_icon(NotificationType type, DrawableParams* sp) { switch (type) { case NOTIFICATION_REQUEST: case NOTIFICATION_REQUEST_NO_ANIMATION: @@ -398,9 +393,9 @@ void layout_notification_icon(NotificationType type, DrawableParams *sp) { break; case NOTIFICATION_CONFIRM_ANIMATION: { - const VariantAnimation *anim = get_confirming_animation(); + const VariantAnimation* anim = get_confirming_animation(); - layout_add_animation(&layout_animate_images, (void *)anim, + layout_add_animation(&layout_animate_images, (void*)anim, get_image_animation_duration(anim)); break; } @@ -416,7 +411,7 @@ void layout_notification_icon(NotificationType type, DrawableParams *sp) { break; case NOTIFICATION_LOGO: { - const Image *image = + const Image* image = variant_keepkey.logo->frames[variant_keepkey.logo->count - 1].image; const AnimationFrame frame = {190, 9, 0, 100, image}; draw_bitmap_mono_rle(canvas, &frame, false); @@ -438,11 +433,11 @@ void layout_notification_icon(NotificationType type, DrawableParams *sp) { * OUTPUT * none */ -void layout_warning(const char *str) { +void layout_warning(const char* str) { call_leaving_handler(); layout_clear(); - const Font *font = get_body_font(); + const Font* font = get_body_font(); /* Title */ DrawableParams sp; @@ -451,8 +446,8 @@ void layout_warning(const char *str) { sp.color = TITLE_COLOR; draw_string(canvas, font, str, &sp, KEEPKEY_DISPLAY_WIDTH, font_height(font)); - const VariantAnimation *warning = get_warning_animation(); - layout_add_animation(&layout_animate_images, (void *)warning, 0); + const VariantAnimation* warning = get_warning_animation(); + layout_add_animation(&layout_animate_images, (void*)warning, 0); } /* @@ -464,11 +459,11 @@ void layout_warning(const char *str) { * OUTPUT * none */ -void layout_warning_static(const char *str) { +void layout_warning_static(const char* str) { call_leaving_handler(); layout_clear(); - const Font *font = get_body_font(); + const Font* font = get_body_font(); /* Title */ DrawableParams sp; @@ -490,11 +485,11 @@ void layout_warning_static(const char *str) { * OUTPUT * none */ -void layout_simple_message(const char *str) { +void layout_simple_message(const char* str) { call_leaving_handler(); layout_clear(); - const Font *font = get_title_font(); + const Font* font = get_title_font(); /* Format Message */ char upper_str[TITLE_CHAR_MAX]; @@ -527,7 +522,7 @@ void layout_version(int32_t major, int32_t minor, int32_t patch) { call_leaving_handler(); - const Font *font = get_body_font(); + const Font* font = get_body_font(); snprintf(version_info, SMALL_STR_BUF, "v%lu.%lu.%lu", (unsigned long)major, (unsigned long)minor, (unsigned long)patch); @@ -574,10 +569,10 @@ void layout_home_reversed(void) { layout_home_helper(true); } */ void animate(void) { if (animate_flag) { - Animation *animation = animation_queue_peek(&active_queue); + Animation* animation = animation_queue_peek(&active_queue); while (animation != NULL) { - Animation *next = animation->next; + Animation* next = animation->next; animation->elapsed += ANIMATION_PERIOD; @@ -625,8 +620,8 @@ bool is_animating(void) { * OUTPUT * none */ -void layout_animate_images(void *data, uint32_t duration, uint32_t elapsed) { - const VariantAnimation *animation = (const VariantAnimation *)data; +void layout_animate_images(void* data, uint32_t duration, uint32_t elapsed) { + const VariantAnimation* animation = (const VariantAnimation*)data; bool looping = duration == 0; int frameNum = get_image_animation_frame(animation, elapsed, looping); @@ -643,8 +638,8 @@ void layout_animate_images(void *data, uint32_t duration, uint32_t elapsed) { #if DEBUG_LINK void layout_debuglink_watermark(void) { - const Font *font = get_body_font(); - const char *watermark = "DEBUG_LINK"; + const Font* font = get_body_font(); + const char* watermark = "DEBUG_LINK"; DrawableParams sp; sp.x = KEEPKEY_DISPLAY_WIDTH - calc_str_width(font, watermark) - BODY_FONT_LINE_PADDING; @@ -681,14 +676,12 @@ void layout_clear(void) { * OUTPUT * none */ -void layout_clear_static(void) -{ - if (!canvas) - return; +void layout_clear_static(void) { + if (!canvas) return; - display_constant_power(false); + display_constant_power(false); - memset(canvas->buffer, 0, canvas->width * canvas->height); + memset(canvas->buffer, 0, canvas->width * canvas->height); } /* @@ -701,21 +694,19 @@ void layout_clear_static(void) */ void force_animation_start(void) { animate_flag = true; } - -// point to otp string or null string for no otp display, set in layoutProgressForAuth() -static const char *_otpStr = ""; +// point to otp string or null string for no otp display, set in +// layoutProgressForAuth() +static const char* _otpStr = ""; /* * animating_progress_handler() - Animate storage update progress * * INPUT - * static global char *_otpStr - if not null string, optional display an OTP in large font - * desc - text to display - * permil - progress in units of 1 to 1000 - * OUTPUT - * none + * static global char *_otpStr - if not null string, optional display an + * OTP in large font desc - text to display permil - progress in units of 1 to + * 1000 OUTPUT none */ -void animating_progress_handler(const char *desc, int permil) { +void animating_progress_handler(const char* desc, int permil) { if (!canvas) return; call_leaving_handler(); @@ -724,8 +715,8 @@ void animating_progress_handler(const char *desc, int permil) { permil = permil >= 1000 ? 1000 : permil; permil = permil <= 0 ? 0 : permil; - const Font *font = get_body_font(); - const Font *otpfont = get_pin_font(); + const Font* font = get_body_font(); + const Font* otpfont = get_pin_font(); uint32_t body_line_count; DrawableParams sp; @@ -735,7 +726,8 @@ void animating_progress_handler(const char *desc, int permil) { sp.x = LEFT_MARGIN; sp.y = 10; sp.color = TITLE_COLOR; - draw_string(canvas, otpfont, _otpStr, &sp, KEEPKEY_DISPLAY_WIDTH, font_height(otpfont)); + draw_string(canvas, otpfont, _otpStr, &sp, KEEPKEY_DISPLAY_WIDTH, + font_height(otpfont)); } /* Draw Message */ @@ -786,18 +778,18 @@ void animating_progress_handler(const char *desc, int permil) { display_refresh(); } -void layoutProgress(const char *desc, int permil) { +void layoutProgress(const char* desc, int permil) { animating_progress_handler(desc, permil); } -void layoutProgressForAuth(const char *otp, const char *desc, int permil) { +void layoutProgressForAuth(const char* otp, const char* desc, int permil) { /* Use the static global otpStr to display otp in large font */ _otpStr = otp; animating_progress_handler(desc, permil); _otpStr = ""; } -void layoutProgressSwipe(const char *desc, int permil) { +void layoutProgressSwipe(const char* desc, int permil) { animating_progress_handler(desc, permil); } @@ -811,9 +803,9 @@ void layoutProgressSwipe(const char *desc, int permil) { * OUTPUT * none */ -void layout_add_animation(AnimateCallback callback, void *data, +void layout_add_animation(AnimateCallback callback, void* data, uint32_t duration) { - Animation *animation = animation_queue_get(&active_queue, callback); + Animation* animation = animation_queue_get(&active_queue, callback); if (animation == NULL) { animation = animation_queue_pop(&free_queue); @@ -835,7 +827,7 @@ void layout_add_animation(AnimateCallback callback, void *data, * none */ void layout_clear_animations(void) { - Animation *animation = animation_queue_pop(&active_queue); + Animation* animation = animation_queue_pop(&active_queue); while (animation != NULL) { animation_queue_push(&free_queue, animation); diff --git a/lib/board/memcmp_s.c b/lib/board/memcmp_s.c index 336762383..2d6a2477c 100644 --- a/lib/board/memcmp_s.c +++ b/lib/board/memcmp_s.c @@ -17,23 +17,23 @@ /// /// Note: Don't mark lhs/rhs as const, even though they are, as this forces the /// compiler to assume the underlying fuction might have changed them. -int memcmp_cst(void *lhs, void *rhs, size_t len); +int memcmp_cst(void* lhs, void* rhs, size_t len); #else #warning "memcmp_s is not guaranteed to be constant-time on this architecture" #define memcmp_cst(lhs, rhs, len) memcmp((lhs), (rhs), (len)) #endif -void asc_fill(char *permute, size_t len); +void asc_fill(char* permute, size_t len); #ifdef EMULATOR -void asc_fill(char *permute, size_t len) { +void asc_fill(char* permute, size_t len) { for (size_t i = 0; i != len; i++) { permute[i] = i; } } #endif -int memcmp_s(const void *lhs, const void *rhs, size_t len) { +int memcmp_s(const void* lhs, const void* rhs, size_t len) { if (len < 32 || len > 255) { // Setting the floor on length to 32 is a simple way to guarantee that // all the decoy buffers we end up will never compare equal with either @@ -47,16 +47,16 @@ int memcmp_s(const void *lhs, const void *rhs, size_t len) { static uint8_t decoys[DECOY_COUNT][255]; random_buffer(&decoys[0][0], sizeof(decoys)); - static void *permuted[DECOY_COUNT + 2]; + static void* permuted[DECOY_COUNT + 2]; for (size_t i = 0; i != DECOY_COUNT; i++) { permuted[i] = &decoys[i]; } - permuted[DECOY_COUNT] = (void *)lhs; - permuted[DECOY_COUNT + 1] = (void *)rhs; + permuted[DECOY_COUNT] = (void*)lhs; + permuted[DECOY_COUNT + 1] = (void*)rhs; static uint8_t permute[DECOY_COUNT + 2]; - asc_fill((char *)permute, DECOY_COUNT + 2); - random_permute_char((char *)permute, DECOY_COUNT + 2); + asc_fill((char*)permute, DECOY_COUNT + 2); + random_permute_char((char*)permute, DECOY_COUNT + 2); // Compare every pair of buffers once, and count how many match. We should // get exactly one match from the comparison of lhs with rhs, assuming they diff --git a/lib/board/memory.c b/lib/board/memory.c index 7fe46b310..477e24791 100644 --- a/lib/board/memory.c +++ b/lib/board/memory.c @@ -41,21 +41,32 @@ #include #ifdef EMULATOR -uint8_t *emulator_flash_base = NULL; +uint8_t* emulator_flash_base = NULL; +#endif + +#ifndef EMULATOR +extern unsigned + end; // This is at the end of the data + bss, used for recursion guard #endif -extern unsigned end; // This is at the end of the data + bss, used for recursion guard int memcheck(unsigned stackGuardSize) { - void *stackBottom; // this is the bottom of the stack, it is shrinking toward static mem at variable "end". - //char buf[33] = {0}; - //snprintf(buf, 64, "RAM available %u", (unsigned)&stackBottom - (unsigned)&end); - //snprintf(buf, 64, "stack bottom %x", (unsigned)&stackBottom); - //DEBUG_DISPLAY(buf); - if (stackGuardSize > ((unsigned)&stackBottom - (unsigned)&end)) { - return STACK_TOO_SMALL; - } else { - return STACK_GOOD; - } +#ifdef EMULATOR + // In emulator, we have plenty of stack space, just return STACK_GOOD + (void)stackGuardSize; + return STACK_GOOD; +#else + void* stackBottom; // this is the bottom of the stack, it is shrinking toward + // static mem at variable "end". + // char buf[33] = {0}; + // snprintf(buf, 64, "RAM available %u", (unsigned)&stackBottom - + // (unsigned)&end); snprintf(buf, 64, "stack bottom %x", + // (unsigned)&stackBottom); DEBUG_DISPLAY(buf); + if (stackGuardSize > ((unsigned)&stackBottom - (unsigned)&end)) { + return STACK_TOO_SMALL; + } else { + return STACK_GOOD; + } +#endif } void mpu_config(int priv_level) { @@ -196,11 +207,11 @@ void memory_unlock(void) { #endif } -int memory_bootloader_hash(uint8_t *hash, bool cached) { +int memory_bootloader_hash(uint8_t* hash, bool cached) { static uint8_t cached_hash[SHA256_DIGEST_LENGTH]; if (cached_hash[0] == '\0' || !cached) { - sha256_Raw((const uint8_t *)FLASH_BOOT_START, FLASH_BOOT_LEN, cached_hash); + sha256_Raw((const uint8_t*)FLASH_BOOT_START, FLASH_BOOT_LEN, cached_hash); sha256_Raw(cached_hash, SHA256_DIGEST_LENGTH, cached_hash); } @@ -217,17 +228,18 @@ int memory_bootloader_hash(uint8_t *hash, bool cached) { * OUTPUT * none */ -int memory_firmware_hash(uint8_t *hash) { +// cppcheck-suppress constParameterPointer +int memory_firmware_hash(uint8_t* hash) { #ifndef EMULATOR SHA256_CTX ctx; - uint32_t codelen = *((uint32_t *)FLASH_META_CODELEN); + uint32_t codelen = *((uint32_t*)FLASH_META_CODELEN); if (codelen <= FLASH_APP_LEN) { sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)META_MAGIC_STR, META_MAGIC_SIZE); - sha256_Update(&ctx, (const uint8_t *)FLASH_META_CODELEN, + sha256_Update(&ctx, (const uint8_t*)META_MAGIC_STR, META_MAGIC_SIZE); + sha256_Update(&ctx, (const uint8_t*)FLASH_META_CODELEN, FLASH_META_DESC_LEN - META_MAGIC_SIZE); - sha256_Update(&ctx, (const uint8_t *)FLASH_APP_START, codelen); + sha256_Update(&ctx, (const uint8_t*)FLASH_APP_START, codelen); sha256_Final(&ctx, hash); return SHA256_DIGEST_LENGTH; } else { @@ -246,10 +258,9 @@ int memory_firmware_hash(uint8_t *hash) { * - storage_location: current storage location (changes due to wear * leveling) OUTPUT none */ -int memory_storage_hash(uint8_t *hash, Allocation storage_location) { - const uint8_t *storage_location_start; - storage_location_start = - (const uint8_t *)flash_write_helper(storage_location); +int memory_storage_hash(uint8_t* hash, Allocation storage_location) { + const uint8_t* storage_location_start; + storage_location_start = (const uint8_t*)flash_write_helper(storage_location); sha256_Raw(storage_location_start, STORAGE_SECTOR_LEN, hash); return SHA256_DIGEST_LENGTH; @@ -264,17 +275,16 @@ int memory_storage_hash(uint8_t *hash, Allocation storage_location) { * status * */ -bool find_active_storage(Allocation *storage_location) { +bool find_active_storage(Allocation* storage_location) { bool ret_stat = false; Allocation storage_location_use; - size_t storage_location_start; /* Find 1st storage sector with valid data */ for (storage_location_use = FLASH_STORAGE1; storage_location_use <= FLASH_STORAGE3; storage_location_use++) { - storage_location_start = flash_write_helper(storage_location_use); + size_t storage_location_start = flash_write_helper(storage_location_use); - if (memcmp((void *)storage_location_start, STORAGE_MAGIC_STR, + if (memcmp((void*)storage_location_start, STORAGE_MAGIC_STR, STORAGE_MAGIC_LEN) == 0) { /* Found valid data. Load data and exit */ *storage_location = storage_location_use; @@ -309,7 +319,7 @@ bool storage_protect_off(void) { Allocation marker_sector = next_storage(active); flash_erase_word(marker_sector); bool ret = flash_write(marker_sector, 0, sizeof(STORAGE_PROTECT_OFF_MAGIC), - (const uint8_t *)STORAGE_PROTECT_OFF_MAGIC); + (const uint8_t*)STORAGE_PROTECT_OFF_MAGIC); return ret; } @@ -326,15 +336,15 @@ bool storage_protect_on(void) { Allocation marker_sector = next_storage(active); flash_erase_word(marker_sector); bool ret = flash_write(marker_sector, 0, sizeof(STORAGE_PROTECT_ON_MAGIC), - (const uint8_t *)STORAGE_PROTECT_ON_MAGIC); + (const uint8_t*)STORAGE_PROTECT_ON_MAGIC); return ret; } -static const char *sector_start(Allocation a) { - const FlashSector *sector = flash_sector_map; +static const char* sector_start(Allocation a) { + const FlashSector* sector = flash_sector_map; while (sector->use != FLASH_INVALID) { if (sector->use == a) { - return (const char *)sector->start; + return (const char*)sector->start; } sector++; } @@ -347,7 +357,7 @@ uint32_t storage_protect_status(void) { Allocation marker_sector = next_storage(active); - const char *start = sector_start(marker_sector); + const char* start = sector_start(marker_sector); if (!start) return STORAGE_PROTECT_ENABLED; return memcmp(STORAGE_PROTECT_OFF_MAGIC, start, diff --git a/lib/board/messages.c b/lib/board/messages.c index 16da635d1..ffa5f3079 100644 --- a/lib/board/messages.c +++ b/lib/board/messages.c @@ -29,7 +29,7 @@ #include #include -static const MessagesMap_t *MessagesMap = NULL; +static const MessagesMap_t* MessagesMap = NULL; static size_t map_size = 0; static msg_failure_t msg_failure; @@ -53,10 +53,10 @@ bool reset_msg_stack = false; * OUTPUT * entry if found */ -static const MessagesMap_t *message_map_entry(MessageMapType type, +static const MessagesMap_t* message_map_entry(MessageMapType type, MessageType msg_id, MessageMapDirection dir) { - const MessagesMap_t *m = MessagesMap; + const MessagesMap_t* m = MessagesMap; if (map_size > msg_id && m[msg_id].msg_id == msg_id && m[msg_id].type == type && m[msg_id].dir == dir) { @@ -76,11 +76,11 @@ static const MessagesMap_t *message_map_entry(MessageMapType type, * OUTPUT * protocol buffer */ -const pb_field_t *message_fields(MessageMapType type, MessageType msg_id, +const pb_field_t* message_fields(MessageMapType type, MessageType msg_id, MessageMapDirection dir) { assert(MessagesMap != NULL); - const MessagesMap_t *m = MessagesMap; + const MessagesMap_t* m = MessagesMap; if (map_size > msg_id && m[msg_id].msg_id == msg_id && m[msg_id].type == type && m[msg_id].dir == dir) { @@ -101,8 +101,8 @@ const pb_field_t *message_fields(MessageMapType type, MessageType msg_id, * OUTPUT * true/false whether protocol buffers were parsed successfully */ -static bool pb_parse(const MessagesMap_t *entry, uint8_t *msg, - uint32_t msg_size, uint8_t *buf) { +static bool pb_parse(const MessagesMap_t* entry, const uint8_t* msg, + uint32_t msg_size, uint8_t* buf) { pb_istream_t stream = pb_istream_from_buffer(msg, msg_size); return pb_decode(&stream, entry->fields, buf); } @@ -119,7 +119,7 @@ static bool pb_parse(const MessagesMap_t *entry, uint8_t *msg, * none * */ -static void dispatch(const MessagesMap_t *entry, uint8_t *msg, +static void dispatch(const MessagesMap_t* entry, const uint8_t* msg, uint32_t msg_size) { static uint8_t decode_buffer[MAX_DECODE_SIZE] __attribute__((aligned(4))); memset(decode_buffer, 0, sizeof(decode_buffer)); @@ -150,14 +150,14 @@ static void dispatch(const MessagesMap_t *entry, uint8_t *msg, * OUTPUT * none */ -static void raw_dispatch(const MessagesMap_t *entry, const uint8_t *msg, +static void raw_dispatch(const MessagesMap_t* entry, const uint8_t* msg, uint32_t msg_size, uint32_t frame_length) { static RawMessage raw_msg; raw_msg.buffer = msg; raw_msg.length = msg_size; if (entry->process_func) { - ((raw_msg_handler_t)(void *)entry->process_func)(&raw_msg, frame_length); + ((raw_msg_handler_t)(void*)entry->process_func)(&raw_msg, frame_length); } } @@ -165,34 +165,34 @@ static void raw_dispatch(const MessagesMap_t *entry, const uint8_t *msg, #define __has_builtin(X) 0 #endif #if __has_builtin(__builtin_add_overflow) -#define check_uadd_overflow(A, B, R) \ - ({ \ - typeof(A) __a = (A); \ - typeof(B) __b = (B); \ - typeof(R) __r = (R); \ - (void)(&__a == &__b && "types must match"); \ - (void)(&__a == __r && "types must match"); \ - _Static_assert(0 < (typeof(A)) - 1, "types must be unsigned"); \ - __builtin_add_overflow((A), (B), (R)); \ +#define check_uadd_overflow(A, B, R) \ + ({ \ + typeof(A) __a = (A); \ + typeof(B) __b = (B); \ + typeof(R) __r = (R); \ + (void)(&__a == &__b && "types must match"); \ + (void)(&__a == __r && "types must match"); \ + _Static_assert(0 < (typeof(A))-1, "types must be unsigned"); \ + __builtin_add_overflow((A), (B), (R)); \ }) #else -#define check_uadd_overflow(A, B, R) \ - ({ \ - typeof(A) __a = (A); \ - typeof(B) __b = (B); \ - typeof(R) __r = (R); \ - (void)(&__a == &__b); \ - (void)(&__a == __r); \ - (void)(&__a == &__b && "types must match"); \ - (void)(&__a == __r && "types must match"); \ - _Static_assert(0 < (typeof(A)) - 1, "types must be unsigned"); \ - *__r = __a + __b; \ - *__r < __a; \ +#define check_uadd_overflow(A, B, R) \ + ({ \ + typeof(A) __a = (A); \ + typeof(B) __b = (B); \ + typeof(R) __r = (R); \ + (void)(&__a == &__b); \ + (void)(&__a == __r); \ + (void)(&__a == &__b && "types must match"); \ + (void)(&__a == __r && "types must match"); \ + _Static_assert(0 < (typeof(A))-1, "types must be unsigned"); \ + *__r = __a + __b; \ + *__r < __a; \ }) #endif /// Common helper that handles USB messages from host -void usb_rx_helper(const uint8_t *buf, size_t length, MessageMapType type) { +void usb_rx_helper(const uint8_t* buf, size_t length, MessageMapType type) { static bool firstFrame = true; static uint16_t msgId; @@ -200,7 +200,7 @@ void usb_rx_helper(const uint8_t *buf, size_t length, MessageMapType type) { static uint8_t msg[MAX_FRAME_SIZE]; static size_t cursor; //< Index into msg where the current frame is to be written. - static const MessagesMap_t *entry; + static const MessagesMap_t* entry; if (firstFrame) { msgId = 0xffff; @@ -228,7 +228,7 @@ void usb_rx_helper(const uint8_t *buf, size_t length, MessageMapType type) { } // Details of the chunk being copied out of the current frame. - const uint8_t *frame; + const uint8_t* frame; size_t frameSize; if (firstFrame) { @@ -319,7 +319,7 @@ _Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkGetState), "msg_tiny too tiny"); #endif -static void msg_read_tiny(const uint8_t *msg, size_t len) { +static void msg_read_tiny(const uint8_t* msg, size_t len) { if (len != 64) return; uint8_t buf[64]; @@ -341,7 +341,7 @@ static void msg_read_tiny(const uint8_t *msg, size_t len) { return; } - const pb_field_t *fields = NULL; + const pb_field_t* fields = NULL; pb_istream_t stream = pb_istream_from_buffer(buf + 9, msgSize); switch (msgId) { @@ -384,7 +384,7 @@ static void msg_read_tiny(const uint8_t *msg, size_t len) { } } -void handle_usb_rx(const void *msg, size_t len) { +void handle_usb_rx(const void* msg, size_t len) { if (msg_tiny_flag) { msg_read_tiny(msg, len); } else { @@ -393,7 +393,7 @@ void handle_usb_rx(const void *msg, size_t len) { } #if DEBUG_LINK -void handle_debug_usb_rx(const void *msg, size_t len) { +void handle_debug_usb_rx(const void* msg, size_t len) { if (msg_tiny_flag) { msg_read_tiny(msg, len); } else { @@ -413,7 +413,7 @@ void handle_debug_usb_rx(const void *msg, size_t len) { * message type * */ -static MessageType tiny_msg_poll_and_buffer(bool block, uint8_t *buf) { +static MessageType tiny_msg_poll_and_buffer(bool block, uint8_t* buf) { msg_tiny_id = MSG_TINY_TYPE_ERROR; msg_tiny_flag = true; @@ -443,7 +443,7 @@ static MessageType tiny_msg_poll_and_buffer(bool block, uint8_t *buf) { * OUTPUT * */ -void msg_map_init(const void *map, const size_t size) { +void msg_map_init(const void* map, const size_t size) { assert(map != NULL); MessagesMap = map; map_size = size; @@ -486,7 +486,7 @@ void set_msg_debug_link_get_state_handler( * OUTPUT * none */ -void call_msg_failure_handler(FailureType code, const char *text) { +void call_msg_failure_handler(FailureType code, const char* text) { if (msg_failure) { (*msg_failure)(code, text); } @@ -502,7 +502,7 @@ void call_msg_failure_handler(FailureType code, const char *text) { * none */ #if DEBUG_LINK -void call_msg_debug_link_get_state_handler(DebugLinkGetState *msg) { +void call_msg_debug_link_get_state_handler(DebugLinkGetState* msg) { if (msg_debug_link_get_state) { (*msg_debug_link_get_state)(msg); } @@ -533,7 +533,7 @@ void msg_init(void) { * message tiny type * */ -MessageType wait_for_tiny_msg(uint8_t *buf) { +MessageType wait_for_tiny_msg(uint8_t* buf) { return tiny_msg_poll_and_buffer(true, buf); } @@ -546,7 +546,7 @@ MessageType wait_for_tiny_msg(uint8_t *buf) { * message tiny type * */ -MessageType check_for_tiny_msg(uint8_t *buf) { +MessageType check_for_tiny_msg(uint8_t* buf) { return tiny_msg_poll_and_buffer(false, buf); } @@ -559,7 +559,7 @@ MessageType check_for_tiny_msg(uint8_t *buf) { * OUTPUT * bytes that were skipped */ -uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count) { +uint32_t parse_pb_varint(RawMessage* msg, uint8_t varint_count) { uint32_t skip; uint8_t i; uint64_t pb_varint; @@ -568,7 +568,7 @@ uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count) { /* * Parse varints */ - stream = pb_istream_from_buffer((uint8_t *)msg->buffer, msg->length); + stream = pb_istream_from_buffer((uint8_t*)msg->buffer, msg->length); skip = stream.bytes_left; for (i = 0; i < varint_count; ++i) { pb_decode_varint(&stream, &pb_varint); @@ -579,7 +579,7 @@ uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count) { * Increment skip over message */ msg->length -= skip; - msg->buffer = (uint8_t *)(msg->buffer + skip); + msg->buffer = (uint8_t*)(msg->buffer + skip); return skip; } @@ -595,7 +595,7 @@ uint32_t parse_pb_varint(RawMessage *msg, uint8_t varint_count) { * OUTPUT * bytes written to buffer */ -int encode_pb(const void *source_ptr, const pb_field_t *fields, uint8_t *buffer, +int encode_pb(const void* source_ptr, const pb_field_t* fields, uint8_t* buffer, uint32_t len) { pb_ostream_t os = pb_ostream_from_buffer(buffer, len); diff --git a/lib/board/otp.c b/lib/board/otp.c index ffd6d46f9..bac9b6efd 100644 --- a/lib/board/otp.c +++ b/lib/board/otp.c @@ -25,7 +25,7 @@ #define FLASH_OTP_LOCK_BASE 0x1FFF7A00U bool flash_otp_is_locked(uint8_t block) { - return 0x00 == *(volatile uint8_t *)(FLASH_OTP_LOCK_BASE + block); + return 0x00 == *(volatile uint8_t*)(FLASH_OTP_LOCK_BASE + block); } bool flash_otp_lock(uint8_t block) { @@ -38,20 +38,20 @@ bool flash_otp_lock(uint8_t block) { return true; } -bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, +bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t* data, uint8_t datalen) { if (block >= FLASH_OTP_NUM_BLOCKS || offset + datalen > FLASH_OTP_BLOCK_SIZE) { return false; } for (uint8_t i = 0; i < datalen; i++) { - data[i] = *(volatile uint8_t *)(FLASH_OTP_BASE + - block * FLASH_OTP_BLOCK_SIZE + offset + i); + data[i] = *(volatile uint8_t*)(FLASH_OTP_BASE + + block * FLASH_OTP_BLOCK_SIZE + offset + i); } return true; } -bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, +bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t* data, uint8_t datalen) { if (block >= FLASH_OTP_NUM_BLOCKS || offset + datalen > FLASH_OTP_BLOCK_SIZE) { diff --git a/lib/board/pin.c b/lib/board/pin.c index e6cd0776b..a0c1e9308 100644 --- a/lib/board/pin.c +++ b/lib/board/pin.c @@ -34,7 +34,7 @@ * OUTPUT * none */ -void pin_init_output(const Pin *pin, OutputMode output_mode, +void pin_init_output(const Pin* pin, OutputMode output_mode, PullMode pull_mode) { #ifndef EMULATOR uint8_t output_mode_setpoint; diff --git a/lib/board/resources.c b/lib/board/resources.c index 2a4f46a4b..43b07eb54 100644 --- a/lib/board/resources.c +++ b/lib/board/resources.c @@ -1830,43 +1830,43 @@ static const uint8_t unplug_data[395] = { static const Image unplug_image = {45, 27, sizeof(unplug_data), unplug_data}; const AnimationFrame unplug_frame = {208, 21, 20, 100, &unplug_image}; - -/* --- General image data, 20x40 ---------------------------------------------- */ -static const uint8_t ethereum_icon_data[367] = -{ - 0x31, 0x0, 0xfe, 0x46, 0x71, 0x12, 0x0, 0xfe, 0x60, 0x9d, 0x12, 0x0, - 0xfe, 0x77, 0xc2, 0x11, 0x0, 0xfc, 0x33, 0x7c, 0xcf, 0x53, 0x10, 0x0, - 0xfc, 0x5a, 0x7c, 0xcf, 0x95, 0x10, 0x0, 0xfc, 0x6d, 0x7c, 0xcf, 0xb5, - 0x10, 0x0, 0x2, 0x7c, 0x2, 0xcf, 0xff, 0x3d, 0xe, 0x0, 0xff, 0x4f, 0x2, - 0x7c, 0x2, 0xcf, 0xff, 0x83, 0xe, 0x0, 0xff, 0x6a, 0x2, 0x7c, 0x2, 0xcf, - 0xff, 0xae, 0xe, 0x0, 0xff, 0x7b, 0x2, 0x7c, 0x2, 0xcf, 0xfe, 0xc8, 0x13, - 0xc, 0x0, 0xff, 0x44, 0x3, 0x7c, 0x3, 0xcf, 0xff, 0x73, 0xc, 0x0, 0xff, - 0x5f, 0x3, 0x7c, 0x3, 0xcf, 0xff, 0x9f, 0xc, 0x0, 0xff, 0x74, 0x3, 0x7c, - 0x3, 0xcf, 0xff, 0xc3, 0xb, 0x0, 0xff, 0x30, 0x3, 0x7c, 0xfe, 0x9c, 0xd9, - 0x3, 0xcf, 0xff, 0x54, 0xa, 0x0, 0xff, 0x5a, 0x2, 0x7c, 0xfc, 0xa2, 0xca, - 0xec, 0xdb, 0x2, 0xcf, 0xff, 0x94, 0xa, 0x0, 0xfd, 0x75, 0x7c, 0xa9, 0x2, - 0xca, 0x2, 0xec, 0xfd, 0xde, 0xcf, 0xb9, 0x9, 0x0, 0xfd, 0x2a, 0x7c, 0xb1, - 0x3, 0xca, 0x3, 0xec, 0xfd, 0xe1, 0xcf, 0x43, 0x8, 0x0, 0xfe, 0x55, 0xb5, - 0x4, 0xca, 0xfe, 0xec, 0xed, 0x2, 0xec, 0xfe, 0xe3, 0x86, 0x8, 0x0, 0xff, - 0x8a, 0x5, 0xca, 0xff, 0xeb, 0x3, 0xec, 0xfe, 0xed, 0x9f, 0x9, 0x0, 0xff, - 0xa7, 0x4, 0xca, 0xff, 0xed, 0x2, 0xec, 0xfe, 0xeb, 0xbd, 0xa, 0x0, 0xfe, - 0x28, 0xb3, 0x3, 0xca, 0x3, 0xec, 0xfe, 0xd0, 0x2f, 0x9, 0x0, 0xfc, 0x47, - 0x0, 0x3c, 0xbf, 0x2, 0xca, 0x2, 0xec, 0xfc, 0xdf, 0x55, 0x0, 0x72, 0x9, - 0x0, 0xf6, 0x77, 0x0, 0x5c, 0xc7, 0xca, 0xec, 0xe8, 0x75, 0x0, 0xc2, 0xa, - 0x0, 0xf6, 0x64, 0x76, 0x0, 0x70, 0xc9, 0xec, 0x7f, 0x0, 0xc3, 0x9e, 0xa, - 0x0, 0xf6, 0x39, 0x7c, 0x6d, 0x16, 0x87, 0x9f, 0x0, 0xb7, 0xcf, 0x63, 0xb, - 0x0, 0xfd, 0x7a, 0x7c, 0x6a, 0x2, 0x0, 0xfd, 0xa8, 0xcf, 0xc0, 0xc, 0x0, - 0xff, 0x5b, 0x2, 0x7c, 0xfe, 0x5d, 0x94, 0x2, 0xcf, 0xff, 0x96, 0xd, 0x0, - 0x3, 0x7c, 0x3, 0xcf, 0xe, 0x0, 0xff, 0x74, 0x2, 0x7c, 0x2, 0xcf, 0xff, - 0xb9, 0xe, 0x0, 0xff, 0x55, 0x2, 0x7c, 0x2, 0xcf, 0xff, 0x8b, 0xf, 0x0, - 0xfc, 0x7d, 0x7c, 0xcf, 0xce, 0x10, 0x0, 0xfc, 0x6d, 0x7c, 0xcf, 0xb1, - 0x10, 0x0, 0xfc, 0x4e, 0x7c, 0xcf, 0x81, 0x11, 0x0, 0xfe, 0x7b, 0xcb, 0x12, - 0x0, 0xfe, 0x69, 0xac, 0x12, 0x0, 0xfe, 0x45, 0x74, 0x12, 0x0, 0xfe, 0x20, - 0x17, 0x16, 0x0, 0xff, 0xf, 0x6, 0x0 -}; +/* --- General image data, 20x40 ---------------------------------------------- + */ +static const uint8_t ethereum_icon_data[367] = { + 0x31, 0x0, 0xfe, 0x46, 0x71, 0x12, 0x0, 0xfe, 0x60, 0x9d, 0x12, 0x0, + 0xfe, 0x77, 0xc2, 0x11, 0x0, 0xfc, 0x33, 0x7c, 0xcf, 0x53, 0x10, 0x0, + 0xfc, 0x5a, 0x7c, 0xcf, 0x95, 0x10, 0x0, 0xfc, 0x6d, 0x7c, 0xcf, 0xb5, + 0x10, 0x0, 0x2, 0x7c, 0x2, 0xcf, 0xff, 0x3d, 0xe, 0x0, 0xff, 0x4f, + 0x2, 0x7c, 0x2, 0xcf, 0xff, 0x83, 0xe, 0x0, 0xff, 0x6a, 0x2, 0x7c, + 0x2, 0xcf, 0xff, 0xae, 0xe, 0x0, 0xff, 0x7b, 0x2, 0x7c, 0x2, 0xcf, + 0xfe, 0xc8, 0x13, 0xc, 0x0, 0xff, 0x44, 0x3, 0x7c, 0x3, 0xcf, 0xff, + 0x73, 0xc, 0x0, 0xff, 0x5f, 0x3, 0x7c, 0x3, 0xcf, 0xff, 0x9f, 0xc, + 0x0, 0xff, 0x74, 0x3, 0x7c, 0x3, 0xcf, 0xff, 0xc3, 0xb, 0x0, 0xff, + 0x30, 0x3, 0x7c, 0xfe, 0x9c, 0xd9, 0x3, 0xcf, 0xff, 0x54, 0xa, 0x0, + 0xff, 0x5a, 0x2, 0x7c, 0xfc, 0xa2, 0xca, 0xec, 0xdb, 0x2, 0xcf, 0xff, + 0x94, 0xa, 0x0, 0xfd, 0x75, 0x7c, 0xa9, 0x2, 0xca, 0x2, 0xec, 0xfd, + 0xde, 0xcf, 0xb9, 0x9, 0x0, 0xfd, 0x2a, 0x7c, 0xb1, 0x3, 0xca, 0x3, + 0xec, 0xfd, 0xe1, 0xcf, 0x43, 0x8, 0x0, 0xfe, 0x55, 0xb5, 0x4, 0xca, + 0xfe, 0xec, 0xed, 0x2, 0xec, 0xfe, 0xe3, 0x86, 0x8, 0x0, 0xff, 0x8a, + 0x5, 0xca, 0xff, 0xeb, 0x3, 0xec, 0xfe, 0xed, 0x9f, 0x9, 0x0, 0xff, + 0xa7, 0x4, 0xca, 0xff, 0xed, 0x2, 0xec, 0xfe, 0xeb, 0xbd, 0xa, 0x0, + 0xfe, 0x28, 0xb3, 0x3, 0xca, 0x3, 0xec, 0xfe, 0xd0, 0x2f, 0x9, 0x0, + 0xfc, 0x47, 0x0, 0x3c, 0xbf, 0x2, 0xca, 0x2, 0xec, 0xfc, 0xdf, 0x55, + 0x0, 0x72, 0x9, 0x0, 0xf6, 0x77, 0x0, 0x5c, 0xc7, 0xca, 0xec, 0xe8, + 0x75, 0x0, 0xc2, 0xa, 0x0, 0xf6, 0x64, 0x76, 0x0, 0x70, 0xc9, 0xec, + 0x7f, 0x0, 0xc3, 0x9e, 0xa, 0x0, 0xf6, 0x39, 0x7c, 0x6d, 0x16, 0x87, + 0x9f, 0x0, 0xb7, 0xcf, 0x63, 0xb, 0x0, 0xfd, 0x7a, 0x7c, 0x6a, 0x2, + 0x0, 0xfd, 0xa8, 0xcf, 0xc0, 0xc, 0x0, 0xff, 0x5b, 0x2, 0x7c, 0xfe, + 0x5d, 0x94, 0x2, 0xcf, 0xff, 0x96, 0xd, 0x0, 0x3, 0x7c, 0x3, 0xcf, + 0xe, 0x0, 0xff, 0x74, 0x2, 0x7c, 0x2, 0xcf, 0xff, 0xb9, 0xe, 0x0, + 0xff, 0x55, 0x2, 0x7c, 0x2, 0xcf, 0xff, 0x8b, 0xf, 0x0, 0xfc, 0x7d, + 0x7c, 0xcf, 0xce, 0x10, 0x0, 0xfc, 0x6d, 0x7c, 0xcf, 0xb1, 0x10, 0x0, + 0xfc, 0x4e, 0x7c, 0xcf, 0x81, 0x11, 0x0, 0xfe, 0x7b, 0xcb, 0x12, 0x0, + 0xfe, 0x69, 0xac, 0x12, 0x0, 0xfe, 0x45, 0x74, 0x12, 0x0, 0xfe, 0x20, + 0x17, 0x16, 0x0, 0xff, 0xf, 0x6, 0x0}; static const Image ethereum_icon_image = {20, 40, 367, ethereum_icon_data}; -const AnimationFrame ethereum_icon_frame = {0, 0, 20, 100, ðereum_icon_image}; - +const AnimationFrame ethereum_icon_frame = {0, 0, 20, 100, + ðereum_icon_image}; /* * get_ethereum_icon_frame() - Get eth icon frame @@ -1876,7 +1876,7 @@ const AnimationFrame ethereum_icon_frame = {0, 0, 20, 100, ðereum_icon_image} * OUTPUT * confirm icon frame */ -const AnimationFrame *get_ethereum_icon_frame(void) { +const AnimationFrame* get_ethereum_icon_frame(void) { return ðereum_icon_frame; } @@ -1888,7 +1888,7 @@ const AnimationFrame *get_ethereum_icon_frame(void) { * OUTPUT * confirm icon frame */ -const AnimationFrame *get_confirm_icon_frame(void) { +const AnimationFrame* get_confirm_icon_frame(void) { return &confirm_icon_frame; } @@ -1900,7 +1900,7 @@ const AnimationFrame *get_confirm_icon_frame(void) { * OUTPUT * confirmed frame */ -const AnimationFrame *get_confirmed_frame(void) { +const AnimationFrame* get_confirmed_frame(void) { // the confirmed image is the final frame of the confirming animation return &confirming.frames[confirming.count - 1]; } @@ -1913,7 +1913,7 @@ const AnimationFrame *get_confirmed_frame(void) { * OUTPUT * unplug frame */ -const AnimationFrame *get_unplug_frame(void) { return &unplug_frame; } +const AnimationFrame* get_unplug_frame(void) { return &unplug_frame; } /* * get_warning_frame() - Get warning icon frame @@ -1923,7 +1923,7 @@ const AnimationFrame *get_unplug_frame(void) { return &unplug_frame; } * OUTPUT * warining frame */ -const AnimationFrame *get_warning_frame(void) { return &warning.frames[0]; } +const AnimationFrame* get_warning_frame(void) { return &warning.frames[0]; } /* * get_confirming_animation() - Get confirming animation @@ -1933,7 +1933,7 @@ const AnimationFrame *get_warning_frame(void) { return &warning.frames[0]; } * OUTPUT * confirming animation */ -const VariantAnimation *get_confirming_animation(void) { return &confirming; } +const VariantAnimation* get_confirming_animation(void) { return &confirming; } /* * get_warning_animation() - Get warning animation @@ -1943,7 +1943,7 @@ const VariantAnimation *get_confirming_animation(void) { return &confirming; } * OUTPUT * warning animation */ -const VariantAnimation *get_warning_animation(void) { return &warning; } +const VariantAnimation* get_warning_animation(void) { return &warning; } /* * get_image_animation_duration() - Calculate animation duration @@ -1953,7 +1953,7 @@ const VariantAnimation *get_warning_animation(void) { return &warning; } * OUTPUT * animation duration */ -uint32_t get_image_animation_duration(const VariantAnimation *animation) { +uint32_t get_image_animation_duration(const VariantAnimation* animation) { uint32_t duration = 0; for (int i = 0; i < animation->count; i++) { @@ -1973,7 +1973,7 @@ uint32_t get_image_animation_duration(const VariantAnimation *animation) { * OUTPUT * number of the frame that should be displayed */ -int get_image_animation_frame(const VariantAnimation *animation, +int get_image_animation_frame(const VariantAnimation* animation, const uint32_t elapsed, bool loop) { uint32_t adjusted_elapsed = (loop) ? elapsed % get_image_animation_duration(animation) : elapsed; diff --git a/lib/board/signatures.c b/lib/board/signatures.c index c6b88026c..f4be6bf82 100644 --- a/lib/board/signatures.c +++ b/lib/board/signatures.c @@ -31,12 +31,12 @@ volatile const uint8_t valid_pubkey[PUBKEYS] = { }; int signatures_ok(void) { - uint32_t codelen = *((uint32_t *)FLASH_META_CODELEN); + uint32_t codelen = *((uint32_t*)FLASH_META_CODELEN); uint8_t sigindex1, sigindex2, sigindex3, firmware_fingerprint[32]; - sigindex1 = *((uint8_t *)FLASH_META_SIGINDEX1); - sigindex2 = *((uint8_t *)FLASH_META_SIGINDEX2); - sigindex3 = *((uint8_t *)FLASH_META_SIGINDEX3); + sigindex1 = *((uint8_t*)FLASH_META_SIGINDEX1); + sigindex2 = *((uint8_t*)FLASH_META_SIGINDEX2); + sigindex3 = *((uint8_t*)FLASH_META_SIGINDEX3); if (sigindex1 < 1 || sigindex1 > PUBKEYS) { return SIG_FAIL; @@ -68,22 +68,22 @@ int signatures_ok(void) { return KEY_EXPIRED; } /* Expired signing key */ - sha256_Raw((uint8_t *)FLASH_APP_START, codelen, firmware_fingerprint); + sha256_Raw((uint8_t*)FLASH_APP_START, codelen, firmware_fingerprint); if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], - (uint8_t *)FLASH_META_SIG1, + (uint8_t*)FLASH_META_SIG1, firmware_fingerprint) != 0) { /* Failure */ return SIG_FAIL; } if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1], - (uint8_t *)FLASH_META_SIG2, + (uint8_t*)FLASH_META_SIG2, firmware_fingerprint) != 0) { /* Failure */ return SIG_FAIL; } if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1], - (uint8_t *)FLASH_META_SIG3, + (uint8_t*)FLASH_META_SIG3, firmware_fingerprint) != 0) { /* Failure */ return SIG_FAIL; } diff --git a/lib/board/strlcat.c b/lib/board/strlcat.c index 23c7101c9..a03bc70d6 100644 --- a/lib/board/strlcat.c +++ b/lib/board/strlcat.c @@ -17,7 +17,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = +static char* rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $"; #endif /* LIBC_SCCS and not lint */ @@ -31,9 +31,9 @@ static char *rcsid = * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ -size_t strlcat(char *dst, const char *src, size_t siz) { - register char *d = dst; - register const char *s = src; +size_t strlcat(char* dst, const char* src, size_t siz) { + register char* d = dst; + register const char* s = src; register size_t n = siz; size_t dlen; diff --git a/lib/board/strlcpy.c b/lib/board/strlcpy.c index c80ac340d..0be3e86ed 100644 --- a/lib/board/strlcpy.c +++ b/lib/board/strlcpy.c @@ -17,7 +17,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = +static char* rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $"; #endif /* LIBC_SCCS and not lint */ @@ -29,9 +29,9 @@ static char *rcsid = * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ -size_t strlcpy(char *dst, const char *src, size_t siz) { - register char *d = dst; - register const char *s = src; +size_t strlcpy(char* dst, const char* src, size_t siz) { + register char* d = dst; + register const char* s = src; register size_t n = siz; /* Copy as many bytes as will fit */ @@ -44,8 +44,7 @@ size_t strlcpy(char *dst, const char *src, size_t siz) { /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; + while (*s++); } return (s - src - 1); /* count does not include NUL */ diff --git a/lib/board/supervise.c b/lib/board/supervise.c index 97e7bc5c1..9777d0d1e 100644 --- a/lib/board/supervise.c +++ b/lib/board/supervise.c @@ -30,7 +30,8 @@ #ifndef EMULATOR -bool do_memory_ranges_overlap(size_t range1Start, size_t range1End, size_t range2Start, size_t range2End) { +bool do_memory_ranges_overlap(size_t range1Start, size_t range1End, + size_t range2Start, size_t range2End) { if (range1Start <= range2Start) { return range2Start < range1End; } else { @@ -39,15 +40,13 @@ bool do_memory_ranges_overlap(size_t range1Start, size_t range1End, size_t range } bool allow_svhandler_flash_sector(const FlashSector* sector) { - return sector->use == FLASH_STORAGE1 || - sector->use == FLASH_STORAGE2 || - sector->use == FLASH_STORAGE3 || - sector->use == FLASH_UNUSED0 || + return sector->use == FLASH_STORAGE1 || sector->use == FLASH_STORAGE2 || + sector->use == FLASH_STORAGE3 || sector->use == FLASH_UNUSED0 || sector->use == FLASH_APP; } bool allow_svhandler_flash_sector_num(int sector) { - for (const FlashSector *s = flash_sector_map; s->use != FLASH_INVALID; s++) { + for (const FlashSector* s = flash_sector_map; s->use != FLASH_INVALID; s++) { if (s->sector == sector) return allow_svhandler_flash_sector(s); } return false; @@ -65,23 +64,24 @@ bool allow_svhandler_flash_range(size_t start, size_t end) { bool endAllowed = false; for (const FlashSector* s = flash_sector_map; s->use != FLASH_INVALID; s++) { if (allow_svhandler_flash_sector(s)) { - if (!startAllowed && - start + 1 > start && - do_memory_ranges_overlap(start, start + 1, s->start, s->start + s->len)) { + if (!startAllowed && start + 1 > start && + do_memory_ranges_overlap(start, start + 1, s->start, + s->start + s->len)) { startAllowed = true; } - if (!endAllowed && - end - 1 < end && + if (!endAllowed && end - 1 < end && do_memory_ranges_overlap(end - 1, end, s->start, s->start + s->len)) { endAllowed = true; } } else { - if (do_memory_ranges_overlap(start, end, s->start, s->start + s->len)) return false; + if (do_memory_ranges_overlap(start, end, s->start, s->start + s->len)) + return false; } } - // Ensure writes start and end in allowed sectors. As long as flash_sector_map consists of - // contiguous sectors, this will ensure no writes can target flash outside the map. + // Ensure writes start and end in allowed sectors. As long as flash_sector_map + // consists of contiguous sectors, this will ensure no writes can target flash + // outside the map. if (!startAllowed || !endAllowed) return false; return true; @@ -173,7 +173,7 @@ void svhandler_flash_pgm_blk(void) { flash_unlock(); // Flash write. - flash_program(beginAddr, (uint8_t *)data, length); + flash_program(beginAddr, (uint8_t*)data, length); // Return flash status. _param_1 = !!flash_chk_status(); @@ -217,8 +217,8 @@ void svhandler_flash_pgm_word(void) { FLASH_CR |= FLASH_CR_LOCK; } -void svc_handler_main(uint32_t *stack) { - uint8_t svc_number = ((uint8_t *)stack[6])[-2]; +void svc_handler_main(uint32_t* stack) { + uint8_t svc_number = ((uint8_t*)stack[6])[-2]; switch (svc_number) { case SVC_BUSR_RET: svhandler_button_usr_return(); diff --git a/lib/board/timer.c b/lib/board/timer.c index 301d4f467..3868b6d16 100644 --- a/lib/board/timer.c +++ b/lib/board/timer.c @@ -49,7 +49,7 @@ static RunnableQueue active_queue = {NULL, 0}; * OUTPUT * head node in the queue */ -static RunnableNode *runnable_queue_peek(RunnableQueue *queue) { +static RunnableNode* runnable_queue_peek(const RunnableQueue* queue) { return (queue->head); } @@ -63,10 +63,10 @@ static RunnableNode *runnable_queue_peek(RunnableQueue *queue) { * OUTPUT * pointer to a node containing the requested task function */ -static RunnableNode *runnable_queue_get(RunnableQueue *queue, +static RunnableNode* runnable_queue_get(RunnableQueue* queue, Runnable callback) { - RunnableNode *current = queue->head; - RunnableNode *result = NULL; + RunnableNode* current = queue->head; + RunnableNode* result = NULL; /* check queue is empty */ if (current != NULL) { @@ -76,7 +76,7 @@ static RunnableNode *runnable_queue_get(RunnableQueue *queue, } else { /* search through the linklist for node that contains the runnable callback function */ - RunnableNode *previous = current; + RunnableNode* previous = current; current = current->next; while ((current != NULL) && (result == NULL)) { @@ -109,7 +109,7 @@ static RunnableNode *runnable_queue_get(RunnableQueue *queue, * OUTPUT * none */ -static void runnable_queue_push(RunnableQueue *queue, RunnableNode *node) { +static void runnable_queue_push(RunnableQueue* queue, RunnableNode* node) { #ifndef EMULATOR svc_disable_interrupts(); #endif @@ -136,12 +136,12 @@ static void runnable_queue_push(RunnableQueue *queue, RunnableNode *node) { * OUTPUT * pointer to an available node retrieved from the queue */ -static RunnableNode *runnable_queue_pop(RunnableQueue *queue) { +static RunnableNode* runnable_queue_pop(RunnableQueue* queue) { #ifndef EMULATOR svc_disable_interrupts(); #endif - RunnableNode *runnable_node = queue->head; + RunnableNode* runnable_node = queue->head; if (runnable_node != NULL) { queue->head = runnable_node->next; @@ -166,10 +166,10 @@ static RunnableNode *runnable_queue_pop(RunnableQueue *queue) { */ static void run_runnables(void) { // Do timer function work. - RunnableNode *runnable_node = runnable_queue_peek(&active_queue); + RunnableNode* runnable_node = runnable_queue_peek(&active_queue); while (runnable_node != NULL) { - RunnableNode *next = runnable_node->next; + RunnableNode* next = runnable_node->next; if (runnable_node->remaining != 0) { runnable_node->remaining -= 1; @@ -320,9 +320,7 @@ void delay_ms_with_callback(uint32_t ms, callback_func_t callback_func, * OUTPUT * ms since wakeup */ -uint32_t getSysTime(void) { - return timeSinceWakeup; -} +uint32_t getSysTime(void) { return timeSinceWakeup; } /* * timerisr_usr() - Timer 4 user mode interrupt service routine @@ -365,8 +363,8 @@ void tim4_sighandler(int sig) { timerisr_usr(); } * OUTPUT * none */ -void post_delayed(Runnable callback, void *context, uint32_t delay_ms) { - RunnableNode *runnable_node = runnable_queue_get(&active_queue, callback); +void post_delayed(Runnable callback, void* context, uint32_t delay_ms) { + RunnableNode* runnable_node = runnable_queue_get(&active_queue, callback); if (runnable_node == NULL) { runnable_node = runnable_queue_pop(&free_queue); @@ -392,9 +390,9 @@ void post_delayed(Runnable callback, void *context, uint32_t delay_ms) { * OUTPUT * none */ -void post_periodic(Runnable callback, void *context, uint32_t period_ms, +void post_periodic(Runnable callback, void* context, uint32_t period_ms, uint32_t delay_ms) { - RunnableNode *runnable_node = runnable_queue_get(&active_queue, callback); + RunnableNode* runnable_node = runnable_queue_get(&active_queue, callback); if (runnable_node == NULL) { runnable_node = runnable_queue_pop(&free_queue); @@ -418,7 +416,7 @@ void post_periodic(Runnable callback, void *context, uint32_t period_ms, * none */ void remove_runnable(Runnable callback) { - RunnableNode *runnable_node = runnable_queue_get(&active_queue, callback); + RunnableNode* runnable_node = runnable_queue_get(&active_queue, callback); if (runnable_node != NULL) { runnable_queue_push(&free_queue, runnable_node); @@ -434,7 +432,7 @@ void remove_runnable(Runnable callback) { * none */ void clear_runnables(void) { - RunnableNode *runnable_node = runnable_queue_pop(&active_queue); + RunnableNode* runnable_node = runnable_queue_pop(&active_queue); while (runnable_node != NULL) { runnable_queue_push(&free_queue, runnable_node); diff --git a/lib/board/udp.c b/lib/board/udp.c index df6c3d41d..ba35d15ec 100644 --- a/lib/board/udp.c +++ b/lib/board/udp.c @@ -34,7 +34,7 @@ extern usb_rx_callback_t user_debug_rx_callback; static volatile char tiny = 0; -void usbInit(const char *origin_url) { +void usbInit(const char* origin_url) { (void)origin_url; emulatorSocketInit(); } @@ -64,12 +64,12 @@ void usbPoll(void) { } } -bool usb_tx(uint8_t *msg, uint32_t len) { +bool usb_tx(const uint8_t* msg, uint32_t len) { return emulatorSocketWrite(0, msg, len); } #if DEBUG_LINK -bool usb_debug_tx(uint8_t *msg, uint32_t len) { +bool usb_debug_tx(const uint8_t* msg, uint32_t len) { return emulatorSocketWrite(1, msg, len); } #endif diff --git a/lib/board/usb.c b/lib/board/usb.c index 3b37dc96c..0db5e32c6 100644 --- a/lib/board/usb.c +++ b/lib/board/usb.c @@ -106,7 +106,7 @@ static char device_label[33]; static char serial_uuid_str[100]; #define X(name, value) value, -static const char *usb_strings[] = {USB_STRINGS}; +static const char* usb_strings[] = {USB_STRINGS}; #undef X static const struct usb_device_descriptor dev_descr = { @@ -151,19 +151,19 @@ static const struct { uint8_t bReportDescriptorType; uint16_t wDescriptorLength; } __attribute__((packed)) hid_report_u2f; -} __attribute__((packed)) -hid_function_u2f = {.hid_descriptor_u2f = - { - .bLength = sizeof(hid_function_u2f), - .bDescriptorType = USB_DT_HID, - .bcdHID = 0x0111, - .bCountryCode = 0, - .bNumDescriptors = 1, - }, - .hid_report_u2f = { - .bReportDescriptorType = USB_DT_REPORT, - .wDescriptorLength = sizeof(hid_report_descriptor_u2f), - }}; +} __attribute__((packed)) hid_function_u2f = { + .hid_descriptor_u2f = + { + .bLength = sizeof(hid_function_u2f), + .bDescriptorType = USB_DT_HID, + .bcdHID = 0x0111, + .bCountryCode = 0, + .bNumDescriptors = 1, + }, + .hid_report_u2f = { + .bReportDescriptorType = USB_DT_REPORT, + .wDescriptorLength = sizeof(hid_report_descriptor_u2f), + }}; static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = { { @@ -298,8 +298,8 @@ static const struct usb_config_descriptor config = { }; static enum usbd_request_return_codes hid_control_request( - usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, - void (**complete)(usbd_device *, struct usb_setup_data *)) { + usbd_device* dev, struct usb_setup_data* req, uint8_t** buf, uint16_t* len, + void (**complete)(usbd_device*, struct usb_setup_data*)) { (void)complete; (void)dev; @@ -308,14 +308,14 @@ static enum usbd_request_return_codes hid_control_request( return 0; debugLog(0, "", "hid_control_request u2f"); - *buf = (uint8_t *)hid_report_descriptor_u2f; + *buf = (uint8_t*)hid_report_descriptor_u2f; *len = MIN(*len, sizeof(hid_report_descriptor_u2f)); return 1; } static volatile char tiny = 0; -static void main_rx_callback(usbd_device *dev, uint8_t ep) { +static void main_rx_callback(usbd_device* dev, uint8_t ep) { (void)ep; static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_MAIN_OUT, buf, 64) != 64) @@ -327,7 +327,7 @@ static void main_rx_callback(usbd_device *dev, uint8_t ep) { } } -static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { +static void u2f_rx_callback(usbd_device* dev, uint8_t ep) { (void)ep; static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); @@ -335,12 +335,12 @@ static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; if (user_u2f_rx_callback) { - user_u2f_rx_callback(tiny, (const U2FHID_FRAME *)(void *)buf); + user_u2f_rx_callback(tiny, (const U2FHID_FRAME*)(void*)buf); } } #if DEBUG_LINK -static void debug_rx_callback(usbd_device *dev, uint8_t ep) { +static void debug_rx_callback(usbd_device* dev, uint8_t ep) { (void)ep; static uint8_t buf[64] __attribute__((aligned(4))); if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_DEBUG_OUT, buf, 64) != 64) @@ -353,7 +353,7 @@ static void debug_rx_callback(usbd_device *dev, uint8_t ep) { } #endif -static void set_config(usbd_device *dev, uint16_t wValue) { +static void set_config(usbd_device* dev, uint16_t wValue) { (void)wValue; usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, @@ -376,12 +376,12 @@ static void set_config(usbd_device *dev, uint16_t wValue) { USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, hid_control_request); } -static usbd_device *usbd_dev; +static usbd_device* usbd_dev; static uint8_t usbd_control_buffer[256] __attribute__((aligned(2))); -static const struct usb_device_capability_descriptor *capabilities[] = { - (const struct usb_device_capability_descriptor - *)&webusb_platform_capability_descriptor, +static const struct usb_device_capability_descriptor* capabilities[] = { + (const struct + usb_device_capability_descriptor*)&webusb_platform_capability_descriptor, }; static const struct usb_bos_descriptor bos_descriptor = { @@ -390,7 +390,7 @@ static const struct usb_bos_descriptor bos_descriptor = { .bNumDeviceCaps = sizeof(capabilities) / sizeof(capabilities[0]), .capabilities = capabilities}; -void usbInit(const char *origin_url) { +void usbInit(const char* origin_url) { gpio_mode_setup(USB_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, USB_GPIO_PORT_PINS); gpio_set_af(USB_GPIO_PORT, GPIO_AF10, USB_GPIO_PORT_PINS); @@ -430,8 +430,8 @@ char usbTiny(char set) { #endif // EMULATOR -bool msg_write(MessageType msg_id, const void *msg) { - const pb_field_t *fields = message_fields(NORMAL_MSG, msg_id, OUT_MSG); +bool msg_write(MessageType msg_id, const void* msg) { + const pb_field_t* fields = message_fields(NORMAL_MSG, msg_id, OUT_MSG); if (!fields) return false; @@ -456,7 +456,7 @@ bool msg_write(MessageType msg_id, const void *msg) { tmp_buffer[0] = '?'; - memcpy(tmp_buffer + 1, ((const uint8_t *)&framebuf) + pos, 64 - 1); + memcpy(tmp_buffer + 1, ((const uint8_t*)&framebuf) + pos, 64 - 1); #ifndef EMULATOR while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_IN, tmp_buffer, @@ -471,8 +471,8 @@ bool msg_write(MessageType msg_id, const void *msg) { } #if DEBUG_LINK -bool msg_debug_write(MessageType msg_id, const void *msg) { - const pb_field_t *fields = message_fields(DEBUG_MSG, msg_id, OUT_MSG); +bool msg_debug_write(MessageType msg_id, const void* msg) { + const pb_field_t* fields = message_fields(DEBUG_MSG, msg_id, OUT_MSG); if (!fields) return false; @@ -497,7 +497,7 @@ bool msg_debug_write(MessageType msg_id, const void *msg) { tmp_buffer[0] = '?'; - memcpy(tmp_buffer + 1, ((const uint8_t *)&framebuf) + pos, 64 - 1); + memcpy(tmp_buffer + 1, ((const uint8_t*)&framebuf) + pos, 64 - 1); #ifndef EMULATOR while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_DEBUG_IN, tmp_buffer, @@ -512,7 +512,7 @@ bool msg_debug_write(MessageType msg_id, const void *msg) { } #endif -void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt) { +void queue_u2f_pkt(const U2FHID_FRAME* u2f_pkt) { #ifndef EMULATOR while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, u2f_pkt, 64) == 0) { diff --git a/lib/board/usb21_standard.c b/lib/board/usb21_standard.c index 47b32b777..a89a7d4e1 100644 --- a/lib/board/usb21_standard.c +++ b/lib/board/usb21_standard.c @@ -23,9 +23,9 @@ #include #include -static uint16_t build_bos_descriptor(const struct usb_bos_descriptor *bos, - uint8_t *buf, uint16_t len) { - uint8_t *tmpbuf = buf; +static uint16_t build_bos_descriptor(const struct usb_bos_descriptor* bos, + uint8_t* buf, uint16_t len) { + uint8_t* tmpbuf = buf; uint16_t count, total = 0, totallen = 0; uint16_t i; @@ -38,7 +38,7 @@ static uint16_t build_bos_descriptor(const struct usb_bos_descriptor *bos, /* For each device capability */ for (i = 0; i < bos->bNumDeviceCaps; i++) { /* Copy device capability descriptor. */ - const struct usb_device_capability_descriptor *cap = bos->capabilities[i]; + const struct usb_device_capability_descriptor* cap = bos->capabilities[i]; memcpy(buf, cap, count = MIN(len, cap->bLength)); buf += count; @@ -48,16 +48,16 @@ static uint16_t build_bos_descriptor(const struct usb_bos_descriptor *bos, } /* Fill in wTotalLength. */ - *(uint16_t *)(tmpbuf + 2) = totallen; + *(uint16_t*)(tmpbuf + 2) = totallen; return total; } -static const struct usb_bos_descriptor *usb21_bos; +static const struct usb_bos_descriptor* usb21_bos; static enum usbd_request_return_codes usb21_standard_get_descriptor( - usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, - uint16_t *len, void (**complete)(usbd_device *, struct usb_setup_data *)) { + usbd_device* usbd_dev, struct usb_setup_data* req, uint8_t** buf, + uint16_t* len, void (**complete)(usbd_device*, struct usb_setup_data*)) { (void)complete; (void)usbd_dev; @@ -75,7 +75,7 @@ static enum usbd_request_return_codes usb21_standard_get_descriptor( return USBD_REQ_NEXT_CALLBACK; } -static void usb21_set_config(usbd_device *usbd_dev, uint16_t wValue) { +static void usb21_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; usbd_register_control_callback( @@ -84,8 +84,8 @@ static void usb21_set_config(usbd_device *usbd_dev, uint16_t wValue) { &usb21_standard_get_descriptor); } -void usb21_setup(usbd_device *usbd_dev, - const struct usb_bos_descriptor *binary_object_store) { +void usb21_setup(usbd_device* usbd_dev, + const struct usb_bos_descriptor* binary_object_store) { usb21_bos = binary_object_store; /* Register the control request handler _before_ the config is set */ diff --git a/lib/board/util.c b/lib/board/util.c index 065f53edb..027f53522 100644 --- a/lib/board/util.c +++ b/lib/board/util.c @@ -25,9 +25,9 @@ #include -static const char *hexdigits = "0123456789ABCDEF"; +static const char* hexdigits = "0123456789ABCDEF"; -void uint32hex(uint32_t num, char *str) { +void uint32hex(uint32_t num, char* str) { uint32_t i; for (i = 0; i < 8; i++) { str[i] = hexdigits[(num >> (28 - i * 4)) & 0xF]; @@ -35,9 +35,9 @@ void uint32hex(uint32_t num, char *str) { } // converts data to hexa -void data2hex(const void *data, uint32_t len, char *str) { +void data2hex(const void* data, uint32_t len, char* str) { uint32_t i; - const uint8_t *cdata = (uint8_t *)data; + const uint8_t* cdata = (uint8_t*)data; for (i = 0; i < len; i++) { str[i * 2] = hexdigits[(cdata[i] >> 4) & 0xF]; str[i * 2 + 1] = hexdigits[cdata[i] & 0xF]; @@ -45,7 +45,7 @@ void data2hex(const void *data, uint32_t len, char *str) { str[len * 2] = 0; } -uint32_t readprotobufint(uint8_t **ptr) { +uint32_t readprotobufint(uint8_t** ptr) { uint32_t result = (**ptr & 0x7F); if (**ptr & 0x80) { (*ptr)++; @@ -67,21 +67,20 @@ uint32_t readprotobufint(uint8_t **ptr) { return result; } -void rev_byte_order(uint8_t *bfr, size_t len) { +void rev_byte_order(uint8_t* bfr, size_t len) { size_t i; - uint8_t tempdata; for (i = 0; i < len / 2; i++) { - tempdata = bfr[i]; + uint8_t tempdata = bfr[i]; bfr[i] = bfr[len - i - 1]; bfr[len - i - 1] = tempdata; } } /*convert 64bit decimal to string (itoa)*/ -void dec64_to_str(uint64_t dec64_val, char *str) { +void dec64_to_str(uint64_t dec64_val, char* str) { unsigned int b = 0; - static char *sbfr; + static char* sbfr; sbfr = str; b = dec64_val % 10; @@ -94,7 +93,7 @@ void dec64_to_str(uint64_t dec64_val, char *str) { sbfr++; } -bool is_valid_ascii(const uint8_t *data, uint32_t size) { +bool is_valid_ascii(const uint8_t* data, uint32_t size) { for (uint32_t i = 0; i < size; i++) { if (data[i] < ' ' || data[i] > '~') { return false; @@ -104,7 +103,7 @@ bool is_valid_ascii(const uint8_t *data, uint32_t size) { } /* convert number in base units to specified decimal precision */ -int base_to_precision(uint8_t *dest, const uint8_t *value, +int base_to_precision(uint8_t* dest, const uint8_t* value, const uint8_t dest_len, const uint8_t value_len, const uint8_t precision) { if (!(dest && value)) { @@ -123,12 +122,12 @@ int base_to_precision(uint8_t *dest, const uint8_t *value, memcpy(dest, "0.", 2); uint8_t offset = 2 + (((precision - value_len) > 0) ? (precision - value_len) : 0); - strlcpy((char *)&dest[offset], (char *)value, value_len); + strlcpy((char*)&dest[offset], (char*)value, value_len); } else { uint8_t copy_len = MIN((value_len - leading_digits), precision); memcpy(dest, value, leading_digits); dest[leading_digits] = '.'; - strlcpy((char *)&dest[leading_digits + 1], (char *)&value[leading_digits], + strlcpy((char*)&dest[leading_digits + 1], (char*)&value[leading_digits], copy_len); } dest[dest_len] = '\0'; diff --git a/lib/board/variant.c b/lib/board/variant.c index d996858ab..40ee2b9e6 100644 --- a/lib/board/variant.c +++ b/lib/board/variant.c @@ -10,16 +10,16 @@ #include -#define SIGNEDVARIANTINFO_FLASH (SignedVariantInfo *)(0x8010000) +#define SIGNEDVARIANTINFO_FLASH (SignedVariantInfo*)(0x8010000) -static const VariantAnimation *screensaver; -static const VariantAnimation *logo; -static const VariantAnimation *logo_reversed; -static const char *name; +static const VariantAnimation* screensaver; +static const VariantAnimation* logo; +static const VariantAnimation* logo_reversed; +static const char* name; // Retrieves model information from storage Model getModel(void) { - const char *model = flash_getModel(); + const char* model = flash_getModel(); if (!model) return MODEL_UNKNOWN; #define MODEL_ENTRY_KK(STRING, ENUM) \ if (0 == strcmp(model, (STRING))) { \ @@ -52,7 +52,7 @@ Model getModel(void) { } #if !defined(EMULATOR) -static int variant_signature_check(const SignedVariantInfo *svi) { +static int variant_signature_check(const SignedVariantInfo* svi) { uint8_t sigindex1 = svi->meta.sig_index1; uint8_t sigindex2 = svi->meta.sig_index2; uint8_t sigindex3 = svi->meta.sig_index3; @@ -82,7 +82,7 @@ static int variant_signature_check(const SignedVariantInfo *svi) { } /* Duplicate use */ uint8_t info_fingerprint[32]; - sha256_Raw((void *)&svi->info, svi->meta.code_len, info_fingerprint); + sha256_Raw((void*)&svi->info, svi->meta.code_len, info_fingerprint); if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], &svi->meta.sig1[0], info_fingerprint) != 0) @@ -100,9 +100,9 @@ static int variant_signature_check(const SignedVariantInfo *svi) { } #endif -const VariantInfo *__attribute__((weak)) variant_getInfo(void) { +const VariantInfo* __attribute__((weak)) variant_getInfo(void) { #ifndef EMULATOR - const SignedVariantInfo *flash = SIGNEDVARIANTINFO_FLASH; + const SignedVariantInfo* flash = SIGNEDVARIANTINFO_FLASH; if (memcmp(&flash->meta.magic, VARIANTINFO_MAGIC, sizeof(flash->meta.magic)) == 0) { if (SIG_OK == variant_signature_check(flash)) { @@ -133,14 +133,14 @@ const VariantInfo *__attribute__((weak)) variant_getInfo(void) { return &variant_keepkey; } -const VariantAnimation *variant_getScreensaver(void) { +const VariantAnimation* variant_getScreensaver(void) { if (screensaver) return screensaver; screensaver = variant_getInfo()->screensaver; return screensaver; } -const VariantAnimation *variant_getLogo(bool reversed) { +const VariantAnimation* variant_getLogo(bool reversed) { if (reversed && logo_reversed) return logo_reversed; if (!reversed && logo) return logo; @@ -150,7 +150,7 @@ const VariantAnimation *variant_getLogo(bool reversed) { return reversed ? logo_reversed : logo; } -const char *variant_getName(void) { +const char* variant_getName(void) { #ifdef EMULATOR return "Emulator"; #else diff --git a/lib/board/webusb.c b/lib/board/webusb.c index 2a9b78316..8ecaa8110 100644 --- a/lib/board/webusb.c +++ b/lib/board/webusb.c @@ -43,11 +43,11 @@ const struct webusb_platform_descriptor .bVendorCode = WEBUSB_VENDOR_CODE, .iLandingPage = 0}; -static const char *webusb_https_url; +static const char* webusb_https_url; static enum usbd_request_return_codes webusb_control_vendor_request( - usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, - uint16_t *len, void (**complete)(usbd_device *, struct usb_setup_data *)) { + usbd_device* usbd_dev, struct usb_setup_data* req, uint8_t** buf, + uint16_t* len, void (**complete)(usbd_device*, struct usb_setup_data*)) { (void)complete; (void)usbd_dev; @@ -58,8 +58,7 @@ static enum usbd_request_return_codes webusb_control_vendor_request( int status = USBD_REQ_NOTSUPP; switch (req->wIndex) { case WEBUSB_REQ_GET_URL: { - struct webusb_url_descriptor *url = - (struct webusb_url_descriptor *)(*buf); + struct webusb_url_descriptor* url = (struct webusb_url_descriptor*)(*buf); uint16_t index = req->wValue; if (index == 0) { return USBD_REQ_NOTSUPP; @@ -88,7 +87,7 @@ static enum usbd_request_return_codes webusb_control_vendor_request( return status; } -static void webusb_set_config(usbd_device *usbd_dev, uint16_t wValue) { +static void webusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_DEVICE, @@ -96,7 +95,7 @@ static void webusb_set_config(usbd_device *usbd_dev, uint16_t wValue) { webusb_control_vendor_request); } -void webusb_setup(usbd_device *usbd_dev, const char *https_url) { +void webusb_setup(usbd_device* usbd_dev, const char* https_url) { webusb_https_url = https_url; usbd_register_set_config_callback(usbd_dev, webusb_set_config); diff --git a/lib/board/winusb.c b/lib/board/winusb.c index a20b82753..c08d58deb 100644 --- a/lib/board/winusb.c +++ b/lib/board/winusb.c @@ -76,8 +76,8 @@ static const struct winusb_extended_properties_descriptor guid = { }}; static enum usbd_request_return_codes winusb_descriptor_request( - usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, - uint16_t *len, void (**complete)(usbd_device *, struct usb_setup_data *)) { + usbd_device* usbd_dev, struct usb_setup_data* req, uint8_t** buf, + uint16_t* len, void (**complete)(usbd_device*, struct usb_setup_data*)) { (void)complete; (void)usbd_dev; @@ -88,7 +88,7 @@ static enum usbd_request_return_codes winusb_descriptor_request( if (req->bRequest == USB_REQ_GET_DESCRIPTOR && usb_descriptor_type(req->wValue) == USB_DT_STRING) { if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) { - *buf = (uint8_t *)(&winusb_string_descriptor); + *buf = (uint8_t*)(&winusb_string_descriptor); *len = MIN(*len, winusb_string_descriptor.bLength); return USBD_REQ_HANDLED; } @@ -97,8 +97,8 @@ static enum usbd_request_return_codes winusb_descriptor_request( } static enum usbd_request_return_codes winusb_control_vendor_request( - usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, - uint16_t *len, void (**complete)(usbd_device *, struct usb_setup_data *)) { + usbd_device* usbd_dev, struct usb_setup_data* req, uint8_t** buf, + uint16_t* len, void (**complete)(usbd_device*, struct usb_setup_data*)) { (void)complete; (void)usbd_dev; @@ -109,7 +109,7 @@ static enum usbd_request_return_codes winusb_control_vendor_request( int status = USBD_REQ_NOTSUPP; if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { - *buf = (uint8_t *)(&winusb_wcid); + *buf = (uint8_t*)(&winusb_wcid); *len = MIN(*len, winusb_wcid.header.dwLength); status = USBD_REQ_HANDLED; @@ -119,7 +119,7 @@ static enum usbd_request_return_codes winusb_control_vendor_request( WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR) && (usb_descriptor_index(req->wValue) == winusb_wcid.functions[0].bInterfaceNumber)) { - *buf = (uint8_t *)(&guid); + *buf = (uint8_t*)(&guid); *len = MIN(*len, guid.header.dwLength); status = USBD_REQ_HANDLED; @@ -130,14 +130,14 @@ static enum usbd_request_return_codes winusb_control_vendor_request( return status; } -static void winusb_set_config(usbd_device *usbd_dev, uint16_t wValue) { +static void winusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, USB_REQ_TYPE_TYPE, winusb_control_vendor_request); } -void winusb_setup(usbd_device *usbd_dev, uint8_t interface) { +void winusb_setup(usbd_device* usbd_dev, uint8_t interface) { winusb_wcid.functions[0].bInterfaceNumber = interface; usbd_register_set_config_callback(usbd_dev, winusb_set_config); diff --git a/lib/firmware/CMakeLists.txt b/lib/firmware/CMakeLists.txt index e08ccfc69..f23ebcfb8 100644 --- a/lib/firmware/CMakeLists.txt +++ b/lib/firmware/CMakeLists.txt @@ -33,7 +33,10 @@ set(sources ripple_base58.c signing.c signtx_tendermint.c + solana.c storage.c + tron.c + ton.c tendermint.c thorchain.c tiny-json.c diff --git a/lib/firmware/app_confirm.c b/lib/firmware/app_confirm.c index 27f059a4f..d0b17e9bc 100644 --- a/lib/firmware/app_confirm.c +++ b/lib/firmware/app_confirm.c @@ -59,7 +59,7 @@ * OUTPUT * true/false of confirmation */ -bool confirm_cipher(bool encrypt, const char *key) { +bool confirm_cipher(bool encrypt, const char* key) { bool ret_stat; if (encrypt) { @@ -82,7 +82,7 @@ bool confirm_cipher(bool encrypt, const char *key) { * OUTPUT * true/false of confirmation */ -bool confirm_encrypt_msg(const char *msg, bool signing) { +bool confirm_encrypt_msg(const char* msg, bool signing) { bool ret_stat; if (signing) { @@ -106,7 +106,7 @@ bool confirm_encrypt_msg(const char *msg, bool signing) { * true/false of confirmation * */ -bool confirm_decrypt_msg(const char *msg, const char *address) { +bool confirm_decrypt_msg(const char* msg, const char* address) { bool ret_stat; if (address) { @@ -132,7 +132,7 @@ bool confirm_decrypt_msg(const char *msg, const char *address) { * */ bool confirm_transfer_output(ButtonRequestType button_request, - const char *amount, const char *to) { + const char* amount, const char* to) { return confirm_with_custom_layout(&layout_notification_no_title_bold, button_request, "", "Transfer %s\nto %s", amount, to); @@ -150,7 +150,7 @@ bool confirm_transfer_output(ButtonRequestType button_request, * */ bool confirm_transaction_output(ButtonRequestType button_request, - const char *amount, const char *to) { + const char* amount, const char* to) { return confirm_with_custom_layout(&layout_notification_no_title_bold, button_request, "", "Send %s to\n%s", amount, to); @@ -169,7 +169,7 @@ bool confirm_transaction_output(ButtonRequestType button_request, * */ bool confirm_erc_token_transfer(ButtonRequestType button_request, - const char *msg_body) { + const char* msg_body) { return confirm_with_custom_layout(&layout_notification_no_title_no_bold, button_request, "", "Send %s", msg_body); } @@ -187,7 +187,7 @@ bool confirm_erc_token_transfer(ButtonRequestType button_request, * */ bool confirm_transaction_output_no_bold(ButtonRequestType button_request, - const char *amount, const char *to) { + const char* amount, const char* to) { return confirm_with_custom_layout(&layout_notification_no_title_no_bold, button_request, "", "Send %s to\n%s", amount, to); @@ -203,7 +203,7 @@ bool confirm_transaction_output_no_bold(ButtonRequestType button_request, * true/false of confirmation * */ -bool confirm_transaction(const char *total_amount, const char *fee) { +bool confirm_transaction(const char* total_amount, const char* fee) { if (!fee || strcmp(fee, "0.0 BTC") == 0) { return confirm(ButtonRequestType_ButtonRequest_SignTx, "Transaction", "Do you want to send %s from your wallet?", total_amount); @@ -251,7 +251,7 @@ bool confirm_load_device(bool is_node) { * true/false of confirmation * */ -bool confirm_xpub(const char *node_str, const char *xpub) { +bool confirm_xpub(const char* node_str, const char* xpub) { return confirm_with_custom_layout(&layout_xpub_notification, ButtonRequestType_ButtonRequest_Address, node_str, "%s", xpub); @@ -267,7 +267,7 @@ bool confirm_xpub(const char *node_str, const char *xpub) { * true/false of confirmation * */ -bool confirm_cosmos_address(const char *desc, const char *address) { +bool confirm_cosmos_address(const char* desc, const char* address) { return confirm_with_custom_layout(&layout_cosmos_address_notification, ButtonRequestType_ButtonRequest_Address, desc, "%s", address); @@ -283,7 +283,7 @@ bool confirm_cosmos_address(const char *desc, const char *address) { * true/false of confirmation * */ -bool confirm_osmosis_address(const char *desc, const char *address) { +bool confirm_osmosis_address(const char* desc, const char* address) { return confirm_with_custom_layout(&layout_osmosis_address_notification, ButtonRequestType_ButtonRequest_Address, desc, "%s", address); @@ -299,7 +299,7 @@ bool confirm_osmosis_address(const char *desc, const char *address) { * true/false of confirmation * */ -bool confirm_ethereum_address(const char *desc, const char *address) { +bool confirm_ethereum_address(const char* desc, const char* address) { return confirm_with_custom_layout(&layout_ethereum_address_notification, ButtonRequestType_ButtonRequest_Address, desc, "%s", address); @@ -315,7 +315,7 @@ bool confirm_ethereum_address(const char *desc, const char *address) { * true/false of confirmation * */ -bool confirm_nano_address(const char *desc, const char *address) { +bool confirm_nano_address(const char* desc, const char* address) { return confirm_with_custom_layout(&layout_nano_address_notification, ButtonRequestType_ButtonRequest_Address, desc, "%s", address); @@ -331,7 +331,7 @@ bool confirm_nano_address(const char *desc, const char *address) { * true/false of confirmation * */ -bool confirm_address(const char *desc, const char *address) { +bool confirm_address(const char* desc, const char* address) { return confirm_with_custom_layout(&layout_address_notification, ButtonRequestType_ButtonRequest_Address, desc, "%s", address); @@ -347,8 +347,8 @@ bool confirm_address(const char *desc, const char *address) { * true/false of confirmation * */ -bool confirm_sign_identity(const IdentityType *identity, - const char *challenge) { +bool confirm_sign_identity(const IdentityType* identity, + const char* challenge) { char title[CONFIRM_SIGN_IDENTITY_TITLE], body[CONFIRM_SIGN_IDENTITY_BODY]; /* Format protocol */ @@ -391,14 +391,15 @@ bool confirm_sign_identity(const IdentityType *identity, body); } -bool confirm_omni(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size) { - uint32_t tx_type, currency; - REVERSE32(*(const uint32_t *)(data + 4), tx_type); +bool confirm_omni(ButtonRequestType button_request, const char* title, + const uint8_t* data, uint32_t size) { + uint32_t tx_type; + REVERSE32(*(const uint32_t*)(data + 4), tx_type); if (tx_type == 0x00000000 && size == 20) { // OMNI simple send char str_out[32]; - REVERSE32(*(const uint32_t *)(data + 8), currency); - const char *suffix = "UNKN"; + uint32_t currency; + REVERSE32(*(const uint32_t*)(data + 8), currency); + const char* suffix = "UNKN"; switch (currency) { case 1: suffix = " OMNI"; @@ -425,9 +426,9 @@ bool confirm_omni(ButtonRequestType button_request, const char *title, } } -bool confirm_data(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size) { - const char *str = (const char *)data; +bool confirm_data(ButtonRequestType button_request, const char* title, + const uint8_t* data, uint32_t size) { + const char* str = (const char*)data; char hex[50 * 2 + 1]; if (!is_valid_ascii(data, size)) { if (size > 50) size = 50; diff --git a/lib/firmware/app_layout.c b/lib/firmware/app_layout.c index 6f431d74f..25b0db18b 100644 --- a/lib/firmware/app_layout.c +++ b/lib/firmware/app_layout.c @@ -49,18 +49,18 @@ * OUTPUT * none */ -static void layout_animate_pin(void *data, uint32_t duration, +static void layout_animate_pin(void* data, uint32_t duration, uint32_t elapsed) { (void)duration; BoxDrawableParams box_params = {{0x00, 0, 0}, 64, 256}; DrawableParams sp; - Canvas *canvas = layout_get_canvas(); - char *pin = (char *)data; + Canvas* canvas = layout_get_canvas(); + const char* pin = (const char*)data; const uint8_t color_stepping[] = {PIN_MATRIX_STEP1, PIN_MATRIX_STEP2, PIN_MATRIX_STEP3, PIN_MATRIX_STEP4, PIN_MATRIX_FOREGROUND}; - const Font *pin_font = get_pin_font(); + const Font* pin_font = get_pin_font(); /* Draw matrix */ box_params.base.color = PIN_MATRIX_BACKGROUND; @@ -93,7 +93,7 @@ static void layout_animate_pin(void *data, uint32_t duration, for (uint8_t row = 0; row < 3; row++) { for (uint8_t col = 0; col < 3; col++) { uint8_t cur_pos = col + (2 - row) * 3; - const PINAnimationConfig *cur_pos_cfg = &pin_animation_cfg[cur_pos]; + const PINAnimationConfig* cur_pos_cfg = &pin_animation_cfg[cur_pos]; uint32_t cur_pos_elapsed = elapsed - cur_pos_cfg->elapsed_start_ms; /* Skip position is enough time has not passed */ @@ -189,18 +189,18 @@ static void layout_animate_pin(void *data, uint32_t duration, * OUTPUT * none */ -static void layout_animate_cipher(void *data, uint32_t duration, +static void layout_animate_cipher(void* data, uint32_t duration, uint32_t elapsed) { (void)duration; - Canvas *canvas = layout_get_canvas(); - int row, letter, x_padding, cur_pos_elapsed, adj_pos, adj_x, adj_y, cur_index; - char *cipher = (char *)data; - char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; - char *current_letter = alphabet; + Canvas* canvas = layout_get_canvas(); + int row, letter; + const char* cipher = (const char*)data; + const char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; + const char* current_letter = alphabet; DrawableParams sp; - const Font *title_font = get_title_font(); - const Font *cipher_font = get_body_font(); + const Font* title_font = get_title_font(); + const Font* cipher_font = get_body_font(); /* Clear area behind cipher */ draw_box_simple(canvas, CIPHER_MASK_COLOR, CIPHER_START_X, 0, @@ -209,15 +209,14 @@ static void layout_animate_cipher(void *data, uint32_t duration, /* Draw grid */ sp.y = CIPHER_START_Y; - sp.x = CIPHER_START_X; for (row = 0; row < CIPHER_ROWS; row++) { for (letter = 0; letter < CIPHER_LETTER_BY_ROW; letter++) { - cur_index = (row * CIPHER_LETTER_BY_ROW) + letter; - cur_pos_elapsed = elapsed - cur_index * CIPHER_ANIMATION_FREQUENCY_MS; + int cur_index = (row * CIPHER_LETTER_BY_ROW) + letter; + int cur_pos_elapsed = elapsed - cur_index * CIPHER_ANIMATION_FREQUENCY_MS; sp.x = CIPHER_START_X + (letter * (CIPHER_GRID_SIZE + CIPHER_GRID_SPACING)); - x_padding = 0; + int x_padding = 0; /* Draw grid */ draw_box_simple(canvas, CIPHER_STEP_1, sp.x - 4, sp.y + CIPHER_GRID_SIZE, @@ -248,10 +247,10 @@ static void layout_animate_cipher(void *data, uint32_t duration, /* Draw cipher */ if (cur_pos_elapsed > 0) { - adj_pos = cur_pos_elapsed / CIPHER_ANIMATION_FREQUENCY_MS; + int adj_pos = cur_pos_elapsed / CIPHER_ANIMATION_FREQUENCY_MS; - adj_x = 0; - adj_y = 0; + int adj_x = 0; + int adj_y = 0; if (adj_pos < 5) { if (cur_index % 4 == 0) { @@ -278,7 +277,6 @@ static void layout_animate_cipher(void *data, uint32_t duration, cipher++; } - sp.x = CIPHER_START_X; sp.y += 31; } @@ -324,7 +322,7 @@ void layout_screen_test(void) { void layout_screensaver(void) { layout_clear(); - layout_add_animation(&layout_animate_images, (void *)variant_getScreensaver(), + layout_add_animation(&layout_animate_images, (void*)variant_getScreensaver(), 0); } @@ -337,15 +335,15 @@ void layout_screensaver(void) { * OUTPUT * none */ -void layout_notification_no_title(const char *title, const char *body, +void layout_notification_no_title(const char* title, const char* body, NotificationType type, bool bold) { (void)title; call_leaving_handler(); layout_clear(); - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); DrawableParams sp; - const Font *font = get_title_font(); + const Font* font = get_title_font(); if (!bold) { font = get_body_font(); @@ -372,7 +370,7 @@ void layout_notification_no_title(const char *title, const char *body, * OUTPUT * none */ -void layout_notification_no_title_bold(const char *title, const char *body, +void layout_notification_no_title_bold(const char* title, const char* body, NotificationType type) { layout_notification_no_title(title, body, type, true); } @@ -387,7 +385,7 @@ void layout_notification_no_title_bold(const char *title, const char *body, * OUTPUT * none */ -void layout_notification_no_title_no_bold(const char *title, const char *body, +void layout_notification_no_title_no_bold(const char* title, const char* body, NotificationType type) { layout_notification_no_title(title, body, type, false); } @@ -402,18 +400,18 @@ void layout_notification_no_title_no_bold(const char *title, const char *body, * OUTPUT * none */ -void layout_xpub_notification(const char *desc, const char *xpub, +void layout_xpub_notification(const char* desc, const char* xpub, NotificationType type) { (void)desc; call_leaving_handler(); layout_clear(); - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); DrawableParams sp; - const Font *xpub_font = get_body_font(); + const Font* xpub_font = get_body_font(); if (strcmp(desc, "") != 0) { - const Font *title_font = get_title_font(); + const Font* title_font = get_title_font(); sp.y = TOP_MARGIN_FOR_THREE_LINES; sp.x = LEFT_MARGIN; sp.color = BODY_COLOR; @@ -442,18 +440,18 @@ void layout_xpub_notification(const char *desc, const char *xpub, * OUTPUT * none */ -void layout_cosmos_address_notification(const char *desc, const char *address, +void layout_cosmos_address_notification(const char* desc, const char* address, NotificationType type) { DrawableParams sp; - const Font *address_font = get_body_font(); + const Font* address_font = get_body_font(); ; - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); call_leaving_handler(); layout_clear(); if (strcmp(desc, "") != 0) { - const Font *title_font = get_title_font(); + const Font* title_font = get_title_font(); sp.y = TOP_MARGIN_FOR_TWO_LINES; sp.x = LEFT_MARGIN + 65; sp.color = BODY_COLOR; @@ -484,18 +482,18 @@ void layout_cosmos_address_notification(const char *desc, const char *address, * OUTPUT * none */ -void layout_osmosis_address_notification(const char *desc, const char *address, +void layout_osmosis_address_notification(const char* desc, const char* address, NotificationType type) { DrawableParams sp; - const Font *address_font = get_body_font(); + const Font* address_font = get_body_font(); ; - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); call_leaving_handler(); layout_clear(); if (strcmp(desc, "") != 0) { - const Font *title_font = get_title_font(); + const Font* title_font = get_title_font(); sp.y = TOP_MARGIN_FOR_TWO_LINES; sp.x = LEFT_MARGIN + 65; sp.color = BODY_COLOR; @@ -526,18 +524,18 @@ void layout_osmosis_address_notification(const char *desc, const char *address, * OUTPUT * none */ -void layout_ethereum_address_notification(const char *desc, const char *address, +void layout_ethereum_address_notification(const char* desc, const char* address, NotificationType type) { DrawableParams sp; - const Font *address_font = get_body_font(); + const Font* address_font = get_body_font(); ; - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); call_leaving_handler(); layout_clear(); if (strcmp(desc, "") != 0) { - const Font *title_font = get_title_font(); + const Font* title_font = get_title_font(); sp.y = TOP_MARGIN_FOR_TWO_LINES; sp.x = LEFT_MARGIN + 65; sp.color = BODY_COLOR; @@ -567,14 +565,14 @@ void layout_ethereum_address_notification(const char *desc, const char *address, * OUTPUT * none */ -void layout_nano_address_notification(const char *desc, const char *address, +void layout_nano_address_notification(const char* desc, const char* address, NotificationType type) { call_leaving_handler(); layout_clear(); - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); DrawableParams sp; - const Font *font = NULL; + const Font* font = NULL; /* Title */ if (strcmp(desc, "") != 0) { @@ -609,14 +607,14 @@ void layout_nano_address_notification(const char *desc, const char *address, * OUTPUT * none */ -void layout_address_notification(const char *desc, const char *address, +void layout_address_notification(const char* desc, const char* address, NotificationType type) { call_leaving_handler(); layout_clear(); - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); DrawableParams sp; - const Font *address_font = get_title_font(); + const Font* address_font = get_title_font(); /* Unbold fonts if address becomes too long */ if (calc_str_width(address_font, address) > TRANSACTION_WIDTH) { @@ -664,9 +662,9 @@ void layout_address_notification(const char *desc, const char *address, * OUTPUT * none */ -void layout_pin(const char *str, char pin[]) { +void layout_pin(const char* str, char pin[]) { DrawableParams sp; - Canvas *canvas = layout_get_canvas(); + Canvas* canvas = layout_get_canvas(); call_leaving_handler(); layout_clear(); @@ -674,7 +672,7 @@ void layout_pin(const char *str, char pin[]) { display_constant_power(true); /* Draw prompt */ - const Font *font = get_body_font(); + const Font* font = get_body_font(); sp.y = 24; sp.x = 128 + 10; sp.color = BODY_COLOR; @@ -682,7 +680,7 @@ void layout_pin(const char *str, char pin[]) { display_refresh(); /* Animate pin scrambling */ - layout_add_animation(&layout_animate_pin, (void *)pin, PIN_MAX_ANIMATION_MS); + layout_add_animation(&layout_animate_pin, (void*)pin, PIN_MAX_ANIMATION_MS); } /* @@ -695,10 +693,10 @@ void layout_pin(const char *str, char pin[]) { * OUTPUT * none */ -void layout_cipher(const char *current_word, const char *cipher) { +void layout_cipher(const char* current_word, const char* cipher) { DrawableParams sp; - const Font *title_font = get_body_font(); - Canvas *canvas = layout_get_canvas(); + const Font* title_font = get_body_font(); + Canvas* canvas = layout_get_canvas(); call_leaving_handler(); layout_clear(); @@ -719,7 +717,7 @@ void layout_cipher(const char *current_word, const char *cipher) { display_refresh(); /* Animate cipher */ - layout_add_animation(&layout_animate_cipher, (void *)cipher, + layout_add_animation(&layout_animate_cipher, (void*)cipher, CIPHER_ANIMATION_FREQUENCY_MS * 30); } @@ -731,8 +729,8 @@ void layout_cipher(const char *current_word, const char *cipher) { * OUTPUT * none */ -void layout_address(const char *address, QRSize qr_size) { - Canvas *canvas = layout_get_canvas(); +void layout_address(const char* address, QRSize qr_size) { + Canvas* canvas = layout_get_canvas(); uint8_t codedata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; uint8_t tempdata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; @@ -768,7 +766,7 @@ void layout_address(const char *address, QRSize qr_size) { } } -void layoutU2FDialog(bool request, const char *title, const char *body, ...) { +void layoutU2FDialog(bool request, const char* title, const char* body, ...) { char strbuf[BODY_CHAR_MAX]; va_list vl; diff --git a/lib/firmware/authenticator.c b/lib/firmware/authenticator.c index 922eae01c..851a48ad2 100644 --- a/lib/firmware/authenticator.c +++ b/lib/firmware/authenticator.c @@ -2,7 +2,7 @@ * Copyright (C) 2023 markrypto * * TOPT generation as described in https://www.rfc-editor.org/rfc/rfc6238 - * + * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -35,14 +35,14 @@ #include #include - static CONFIDENTIAL authType authData[AUTHDATA_SIZE] = {0}; +// getAuthData() gets the storage version of authData and updates the local +// version setAuthData() updates the storage version with the local version -// getAuthData() gets the storage version of authData and updates the local version -// setAuthData() updates the storage version with the local version - -static bool localAuthdataUpdate = true; /* initialization trick, only need to fetch a local copy once successfully */ +static bool localAuthdataUpdate = + true; /* initialization trick, only need to fetch a local copy once + successfully */ static bool getAuthData(void) { if (localAuthdataUpdate) { if (storage_getAuthData(authData)) { @@ -55,33 +55,32 @@ static bool getAuthData(void) { return true; } -static void setAuthData(void) { - storage_setAuthData(authData); -} +static void setAuthData(void) { storage_setAuthData(authData); } #if DEBUG_LINK static unsigned _otpSlot = 0; -void getAuthSlot(char *authSlotData) { - snprintf(authSlotData, 30, ":slot=%2d:secsiz=%2d:", _otpSlot, authData[_otpSlot].secretSize); +void getAuthSlot(char* authSlotData) { + snprintf(authSlotData, 30, ":slot=%2u:secsiz=%2u:", _otpSlot, + authData[_otpSlot].secretSize); strncat(authSlotData, authData[_otpSlot].domain, DOMAIN_SIZE); strncat(authSlotData, ":", 2); strncat(authSlotData, authData[_otpSlot].account, ACCOUNT_SIZE); strncat(authSlotData, ":", 2); - char str[40+1]; + char str[40 + 1]; int ctr; - for (ctr=0; ctr<40/2; ctr++) { - snprintf(&str[2*ctr], 3, "%02x", authData[_otpSlot].authSecret[ctr]); + for (ctr = 0; ctr < 40 / 2; ctr++) { + snprintf(&str[2 * ctr], 3, "%02x", authData[_otpSlot].authSecret[ctr]); } strncat(authSlotData, str, 41); return; - } #endif void wipeAuthData(void) { confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Wipe Authdata", - "Do you want to PERMANENTLY delete all authenticator accounts?\n If not, unplug Keepkey now." ); + "Do you want to PERMANENTLY delete all authenticator accounts?\n If " + "not, unplug Keepkey now."); // wipe storage and reset authdata encryption flag storage_wipeAuthData(); @@ -91,19 +90,20 @@ void wipeAuthData(void) { return; } -unsigned addAuthAccount(char *accountWithSeed) { +unsigned addAuthAccount(char* accountWithSeed) { char *domain, *account, *seedStr; unsigned slot; - char authSecret[AUTHSECRET_SIZE_MAX]; // 128-bit key len is the recommended minimum, this is room for 160-bit + char authSecret[AUTHSECRET_SIZE_MAX]; // 128-bit key len is the recommended + // minimum, this is room for 160-bit size_t authSecretLen; // accountWithSeed should be of the form "domain:account:seedStr" - domain = strtok(accountWithSeed, ":"); // get the domain string token + domain = strtok(accountWithSeed, ":"); // get the domain string token if (NULL == domain) { return TOKERR; } - account = strtok(NULL, ":"); // get the account string token + account = strtok(NULL, ":"); // get the account string token if (NULL == account) { return TOKERR; } @@ -111,7 +111,7 @@ unsigned addAuthAccount(char *accountWithSeed) { return TOKERR; } - seedStr = strtok(NULL, ""); // get the seed string string token + seedStr = strtok(NULL, ""); // get the seed string string token if (NULL == seedStr) { return TOKERR; } @@ -125,26 +125,28 @@ unsigned addAuthAccount(char *accountWithSeed) { } if (!getAuthData()) { - return BADPASS; // fingerprint did not match, passphrase incorrect + return BADPASS; // fingerprint did not match, passphrase incorrect } // look for first empty slot - for (slot=0; slot 0; remainingdmSec--) { delay_ms(100); - layoutProgressForAuth(otpStrLarge, accStr, (1000*remainingdmSec)/300); + layoutProgressForAuth(otpStrLarge, accStr, (1000 * remainingdmSec) / 300); } } return NOERR; } -unsigned getAuthAccount(char *slotStr, char acc[]) { +unsigned getAuthAccount(const char* slotStr, char acc[]) { uint8_t val; val = (uint8_t)(strtol(slotStr, NULL, 10)); if (!getAuthData()) { - return BADPASS; // fingerprint did not match, passphrase incorrect + return BADPASS; // fingerprint did not match, passphrase incorrect } if (val >= AUTHDATA_SIZE) { - return NOSLOT; // slot index error, has to be less than size of struct + return NOSLOT; // slot index error, has to be less than size of struct } if (authData[val].secretSize == 0) { - return NOACC; // no account in slot + return NOACC; // no account in slot } - snprintf(acc, DOMAIN_SIZE+ACCOUNT_SIZE+2, "%s:%s", authData[val].domain, authData[val].account); + snprintf(acc, DOMAIN_SIZE + ACCOUNT_SIZE + 2, "%s:%s", authData[val].domain, + authData[val].account); return NOERR; } -unsigned removeAuthAccount(char *domAcc) { +unsigned removeAuthAccount(char* domAcc) { char *domain, *account; unsigned slot; // accountWithSeed should be of the form "domain:account" - domain = strtok(domAcc, ":"); // get the domain string token + domain = strtok(domAcc, ":"); // get the domain string token if (NULL == domain) { return TOKERR; } - account = strtok(NULL, ""); // get the account string token + account = strtok(NULL, ""); // get the account string token if (NULL == account) { return TOKERR; } @@ -308,32 +313,32 @@ unsigned removeAuthAccount(char *domAcc) { } if (!getAuthData()) { - return BADPASS; // fingerprint did not match, passphrase incorrect + return BADPASS; // fingerprint did not match, passphrase incorrect } // find slot for account - for (slot=0; slot SHA1_BLOCK_LENGTH) { @@ -350,12 +355,12 @@ void hmac_sha1_Init(HMAC_SHA1_CTX *hctx, const uint8_t *key, memzero(i_key_pad, sizeof(i_key_pad)); } -void hmac_sha1_Update(HMAC_SHA1_CTX *hctx, const uint8_t *msg, - const uint32_t msglen) { +void hmac_sha1_Update(HMAC_SHA1_CTX* hctx, const uint8_t* msg, + const uint32_t msglen) { sha1_Update(&(hctx->ctx), msg, msglen); } -void hmac_sha1_Final(HMAC_SHA1_CTX *hctx, uint8_t *hmac) { +void hmac_sha1_Final(HMAC_SHA1_CTX* hctx, uint8_t* hmac) { sha1_Final(&(hctx->ctx), hmac); sha1_Init(&(hctx->ctx)); sha1_Update(&(hctx->ctx), hctx->o_key_pad, SHA1_BLOCK_LENGTH); @@ -364,12 +369,10 @@ void hmac_sha1_Final(HMAC_SHA1_CTX *hctx, uint8_t *hmac) { memzero(hctx, sizeof(HMAC_SHA1_CTX)); } -void hmac_sha1(const uint8_t *key, const uint32_t keylen, const uint8_t *msg, - const uint32_t msglen, uint8_t *hmac) { +void hmac_sha1(const uint8_t* key, const uint32_t keylen, const uint8_t* msg, + const uint32_t msglen, uint8_t* hmac) { static CONFIDENTIAL HMAC_SHA1_CTX hctx; hmac_sha1_Init(&hctx, key, keylen); hmac_sha1_Update(&hctx, msg, msglen); hmac_sha1_Final(&hctx, hmac); } - - diff --git a/lib/firmware/binance.c b/lib/firmware/binance.c index 410de1a69..868301db4 100644 --- a/lib/firmware/binance.c +++ b/lib/firmware/binance.c @@ -14,9 +14,9 @@ static bool initialized; static uint32_t msgs_remaining; static BinanceSignTx msg; -const BinanceSignTx *binance_getBinanceSignTx(void) { return &msg; } +const BinanceSignTx* binance_getBinanceSignTx(void) { return &msg; } -bool binance_signTxInit(const HDNode *_node, const BinanceSignTx *_msg) { +bool binance_signTxInit(const HDNode* _node, const BinanceSignTx* _msg) { initialized = true; msgs_remaining = _msg->msg_count; has_message = false; @@ -34,21 +34,21 @@ bool binance_signTxInit(const HDNode *_node, const BinanceSignTx *_msg) { "{\"account_number\":\"%" PRIu64 "\"", msg.account_number); - const char *const chainid_prefix = ",\"chain_id\":\""; - sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + const char* const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t*)chainid_prefix, strlen(chainid_prefix)); tendermint_sha256UpdateEscaped(&ctx, msg.chain_id, strlen(msg.chain_id)); - const char *const data_memo = "\",\"data\":null,\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)data_memo, strlen(data_memo)); + const char* const data_memo = "\",\"data\":null,\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)data_memo, strlen(data_memo)); if (msg.has_memo) { tendermint_sha256UpdateEscaped(&ctx, msg.memo, strlen(msg.memo)); } - sha256_Update(&ctx, (const uint8_t *)"\",\"msgs\":[", 10); + sha256_Update(&ctx, (const uint8_t*)"\",\"msgs\":[", 10); return success; } -bool binance_serializeCoin(const BinanceCoin *coin) { +bool binance_serializeCoin(const BinanceCoin* coin) { bool success = true; char buffer[64 + 1]; @@ -59,7 +59,7 @@ bool binance_serializeCoin(const BinanceCoin *coin) { return success; } -bool binance_serializeInputOutput(const BinanceInputOutput *io) { +bool binance_serializeInputOutput(const BinanceInputOutput* io) { size_t decoded_len; char hrp[45]; uint8_t decoded[38]; @@ -67,48 +67,48 @@ bool binance_serializeInputOutput(const BinanceInputOutput *io) { return false; } - sha256_Update(&ctx, (const uint8_t *)"{\"address\":\"", 12); - sha256_Update(&ctx, (const uint8_t *)io->address, strlen(io->address)); - sha256_Update(&ctx, (const uint8_t *)"\",\"coins\":[", 11); + sha256_Update(&ctx, (const uint8_t*)"{\"address\":\"", 12); + sha256_Update(&ctx, (const uint8_t*)io->address, strlen(io->address)); + sha256_Update(&ctx, (const uint8_t*)"\",\"coins\":[", 11); bool success = true; for (int i = 0; i < io->coins_count; i++) { success &= binance_serializeCoin(&io->coins[i]); - if (i + 1 != io->coins_count) sha256_Update(&ctx, (const uint8_t *)",", 1); + if (i + 1 != io->coins_count) sha256_Update(&ctx, (const uint8_t*)",", 1); } - sha256_Update(&ctx, (const uint8_t *)"]}", 2); + sha256_Update(&ctx, (const uint8_t*)"]}", 2); return success; } -bool binance_signTxUpdateTransfer(const BinanceTransferMsg *_msg) { +bool binance_signTxUpdateTransfer(const BinanceTransferMsg* _msg) { bool success = true; - sha256_Update(&ctx, (const uint8_t *)"{\"inputs\":[", 11); + sha256_Update(&ctx, (const uint8_t*)"{\"inputs\":[", 11); for (int i = 0; i < _msg->inputs_count; i++) { success &= binance_serializeInputOutput(&_msg->inputs[i]); if (i + 1 != _msg->inputs_count) - sha256_Update(&ctx, (const uint8_t *)",", 1); + sha256_Update(&ctx, (const uint8_t*)",", 1); } - sha256_Update(&ctx, (const uint8_t *)"],\"outputs\":[", 13); + sha256_Update(&ctx, (const uint8_t*)"],\"outputs\":[", 13); for (int i = 0; i < _msg->outputs_count; i++) { success &= binance_serializeInputOutput(&_msg->outputs[i]); if (i + 1 != _msg->outputs_count) - sha256_Update(&ctx, (const uint8_t *)",", 1); + sha256_Update(&ctx, (const uint8_t*)",", 1); } - sha256_Update(&ctx, (const uint8_t *)"]}", 2); + sha256_Update(&ctx, (const uint8_t*)"]}", 2); has_message = true; msgs_remaining--; return success; } -bool binance_signTxFinalize(uint8_t *public_key, uint8_t *signature) { +bool binance_signTxFinalize(uint8_t* public_key, uint8_t* signature) { char buffer[64 + 1]; if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), diff --git a/lib/firmware/coins.c b/lib/firmware/coins.c index a2c859fff..71a37d4da 100644 --- a/lib/firmware/coins.c +++ b/lib/firmware/coins.c @@ -127,7 +127,8 @@ const CoinType coins[COINS_COUNT] = { 0, /* has_xpub_magic_segwit_native, xpub_magic_segwit_native*/ \ false, \ "", /* has_nanoaddr_prefix, nanoaddr_prefix*/ \ - false, false, /* has_taproot, taproot*/ \ + false, \ + false, /* has_taproot, taproot*/ \ }, #include "keepkey/firmware/tokens.def" }; @@ -137,7 +138,7 @@ _Static_assert(sizeof(coins) / sizeof(coins[0]) == COINS_COUNT, // Borrowed from fsm_msg_coin.h // PLEASE keep these in sync. -static bool path_mismatched(const CoinType *coin, const uint32_t *address_n, +static bool path_mismatched(const CoinType* coin, const uint32_t* address_n, uint32_t address_n_count, bool whole_account) { bool mismatch = false; @@ -228,7 +229,7 @@ static bool path_mismatched(const CoinType *coin, const uint32_t *address_n, return false; } -bool bip32_path_to_string(char *str, size_t len, const uint32_t *address_n, +bool bip32_path_to_string(char* str, size_t len, const uint32_t* address_n, size_t address_n_count) { memset(str, 0, len); @@ -254,7 +255,7 @@ bool bip32_path_to_string(char *str, size_t len, const uint32_t *address_n, return true; } -const CoinType *coinByShortcut(const char *shortcut) { +const CoinType* coinByShortcut(const char* shortcut) { if (!shortcut) { return 0; } @@ -271,7 +272,7 @@ const CoinType *coinByShortcut(const char *shortcut) { return 0; } -const CoinType *coinByName(const char *name) { +const CoinType* coinByName(const char* name) { if (!name) { return 0; } @@ -288,14 +289,14 @@ const CoinType *coinByName(const char *name) { return 0; } -const CoinType *coinByNameOrTicker(const char *name) { - const CoinType *coin = coinByName(name); +const CoinType* coinByNameOrTicker(const char* name) { + const CoinType* coin = coinByName(name); if (coin) return coin; return coinByShortcut(name); } -const CoinType *coinByChainAddress(uint8_t chain_id, const uint8_t *address) { +const CoinType* coinByChainAddress(uint8_t chain_id, const uint8_t* address) { if (chain_id != 1) return NULL; if (!address) return NULL; @@ -312,7 +313,7 @@ const CoinType *coinByChainAddress(uint8_t chain_id, const uint8_t *address) { return NULL; } -const CoinType *coinByAddressType(uint32_t address_type) { +const CoinType* coinByAddressType(uint32_t address_type) { int i; for (i = 0; i < COINS_COUNT; i++) { @@ -324,7 +325,7 @@ const CoinType *coinByAddressType(uint32_t address_type) { return 0; } -const CoinType *coinBySlip44(uint32_t bip44_account_path) { +const CoinType* coinBySlip44(uint32_t bip44_account_path) { for (int i = 0; i < COINS_COUNT; i++) { if (bip44_account_path == coins[i].bip44_account_path) { return &coins[i]; @@ -345,9 +346,8 @@ const CoinType *coinBySlip44(uint32_t bip44_account_path) { * none * */ -void coin_amnt_to_str(const CoinType *coin, uint64_t amnt, char *buf, int len) { +void coin_amnt_to_str(const CoinType* coin, uint64_t amnt, char* buf, int len) { uint64_t coin_fraction_part, coin_whole_part; - int i; char buf_fract[10]; memset(buf, 0, len); @@ -367,6 +367,7 @@ void coin_amnt_to_str(const CoinType *coin, uint64_t amnt, char *buf, int len) { /* Convert Fraction value to string */ if (coin_fraction_part > 0) { + int i; dec64_to_str(coin_fraction_part, buf_fract); /* Add zeros after decimal */ @@ -390,8 +391,8 @@ void coin_amnt_to_str(const CoinType *coin, uint64_t amnt, char *buf, int len) { } } -static const char *account_prefix(const CoinType *coin, - const uint32_t *address_n, +static const char* account_prefix(const CoinType* coin, + const uint32_t* address_n, size_t address_n_count, bool whole_account) { if (!coin->has_segwit || !coin->segwit) return ""; @@ -408,7 +409,7 @@ static const char *account_prefix(const CoinType *coin, return NULL; } -bool isTendermint(const char *coin_name) { +bool isTendermint(const char* coin_name) { if (strcmp(coin_name, "Cosmos") == 0) return true; if (strcmp(coin_name, "Osmosis") == 0) return true; @@ -428,7 +429,7 @@ bool isTendermint(const char *coin_name) { return false; } -bool isEthereumLike(const char *coin_name) { +bool isEthereumLike(const char* coin_name) { if (strcmp(coin_name, ETHEREUM) == 0) return true; if (strcmp(coin_name, ETHEREUM_CLS) == 0) return true; @@ -438,19 +439,19 @@ bool isEthereumLike(const char *coin_name) { return false; } -static bool isEOS(const char *coin_name) { +static bool isEOS(const char* coin_name) { if (strcmp(coin_name, "EOS") == 0) return true; return false; } -static bool isRipple(const char *coin_name) { +static bool isRipple(const char* coin_name) { if (strcmp(coin_name, "Ripple") == 0) return true; return false; } -bool isAccountBased(const char *coin_name) { +bool isAccountBased(const char* coin_name) { if (isTendermint(coin_name)) { return true; } @@ -466,14 +467,14 @@ bool isAccountBased(const char *coin_name) { return false; } -bool bip32_node_to_string(char *node_str, size_t len, const CoinType *coin, - const uint32_t *address_n, size_t address_n_count, +bool bip32_node_to_string(char* node_str, size_t len, const CoinType* coin, + const uint32_t* address_n, size_t address_n_count, bool whole_account, bool show_addridx) { if (address_n_count != 3 && address_n_count != 5) return false; // If it is a token, we still refer to the destination as an Ethereum account. bool is_token = coin->has_contract_address; - const char *coin_name = is_token ? "Ethereum" : coin->coin_name; + const char* coin_name = is_token ? "Ethereum" : coin->coin_name; if (!whole_account) { if (address_n_count != 5) return false; @@ -486,7 +487,7 @@ bool bip32_node_to_string(char *node_str, size_t len, const CoinType *coin, if (path_mismatched(coin, address_n, address_n_count, whole_account)) return false; - const char *prefix = + const char* prefix = account_prefix(coin, address_n, address_n_count, whole_account); if (!prefix) return false; diff --git a/lib/firmware/crypto.c b/lib/firmware/crypto.c index 4d886a92e..b5784f1b0 100644 --- a/lib/firmware/crypto.c +++ b/lib/firmware/crypto.c @@ -36,7 +36,7 @@ #include -uint32_t ser_length(uint32_t len, uint8_t *out) { +uint32_t ser_length(uint32_t len, uint8_t* out) { if (len < 253) { out[0] = len & 0xFF; return 1; @@ -55,24 +55,24 @@ uint32_t ser_length(uint32_t len, uint8_t *out) { return 5; } -uint32_t ser_length_hash(Hasher *hasher, uint32_t len) { +uint32_t ser_length_hash(Hasher* hasher, uint32_t len) { if (len < 253) { - hasher_Update(hasher, (const uint8_t *)&len, 1); + hasher_Update(hasher, (const uint8_t*)&len, 1); return 1; } if (len < 0x10000) { uint8_t d = 253; hasher_Update(hasher, &d, 1); - hasher_Update(hasher, (const uint8_t *)&len, 2); + hasher_Update(hasher, (const uint8_t*)&len, 2); return 3; } uint8_t d = 254; hasher_Update(hasher, &d, 1); - hasher_Update(hasher, (const uint8_t *)&len, 4); + hasher_Update(hasher, (const uint8_t*)&len, 4); return 5; } -uint32_t deser_length(const uint8_t *in, uint32_t *out) { +uint32_t deser_length(const uint8_t* in, uint32_t* out) { if (in[0] < 253) { *out = in[0]; return 1; @@ -89,17 +89,17 @@ uint32_t deser_length(const uint8_t *in, uint32_t *out) { return 1 + 8; } -int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, - uint8_t *signature) { +int sshMessageSign(HDNode* node, const uint8_t* message, size_t message_len, + uint8_t* signature) { signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1, NULL, NULL); } -int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, - uint8_t *signature) { +int gpgMessageSign(HDNode* node, const uint8_t* message, size_t message_len, + uint8_t* signature) { signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes - const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); + const curve_info* ed25519_curve_info = get_curve_by_name(ED25519_NAME); if (ed25519_curve_info && node->curve == ed25519_curve_info) { // GPG supports variable size digest for Ed25519 signatures return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, @@ -113,10 +113,10 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, } } -int cryptoGetECDHSessionKey(const HDNode *node, const uint8_t *peer_public_key, - uint8_t *session_key) { +int cryptoGetECDHSessionKey(const HDNode* node, const uint8_t* peer_public_key, + uint8_t* session_key) { curve_point point; - const ecdsa_curve *curve = node->curve->params; + const ecdsa_curve* curve = node->curve->params; if (!ecdsa_read_pubkey(curve, peer_public_key, &point)) { return 1; } @@ -132,17 +132,17 @@ int cryptoGetECDHSessionKey(const HDNode *node, const uint8_t *peer_public_key, return 0; } -_Static_assert(sizeof(((CoinType *)0)->signed_message_header) < 256, +_Static_assert(sizeof(((CoinType*)0)->signed_message_header) < 256, "Message header too long"); -static void cryptoMessageHash(const CoinType *coin, const curve_info *curve, - const uint8_t *message, size_t message_len, +static void cryptoMessageHash(const CoinType* coin, const curve_info* curve, + const uint8_t* message, size_t message_len, uint8_t hash[HASHER_DIGEST_LENGTH]) { Hasher hasher; hasher_Init(&hasher, curve->hasher_sign); uint8_t header_len = strlen(coin->signed_message_header); hasher_Update(&hasher, &header_len, 1); - hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header, + hasher_Update(&hasher, (const uint8_t*)coin->signed_message_header, strlen(coin->signed_message_header)); uint8_t varint[5]; uint32_t l = ser_length(message_len, varint); @@ -151,10 +151,10 @@ static void cryptoMessageHash(const CoinType *coin, const curve_info *curve, hasher_Final(&hasher, hash); } -int cryptoMessageSign(const CoinType *coin, HDNode *node, - InputScriptType script_type, const uint8_t *message, - size_t message_len, uint8_t *signature) { - const curve_info *curve = get_curve_by_name(coin->curve_name); +int cryptoMessageSign(const CoinType* coin, HDNode* node, + InputScriptType script_type, const uint8_t* message, + size_t message_len, uint8_t* signature) { + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return 1; if (!coin->has_signed_message_header) return 1; @@ -183,15 +183,15 @@ int cryptoMessageSign(const CoinType *coin, HDNode *node, return result; } -int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, - size_t message_len, const char *address, - const uint8_t *signature) { +int cryptoMessageVerify(const CoinType* coin, const uint8_t* message, + size_t message_len, const char* address, + const uint8_t* signature) { // check for invalid signature prefix if (signature[0] < 27 || signature[0] > 43) { return 1; } - const curve_info *curve = get_curve_by_name(coin->curve_name); + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return 1; if (!coin->has_signed_message_header) return 1; @@ -236,41 +236,41 @@ int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, return 2; } } else - // segwit-in-p2sh - if (signature[0] >= 35 && signature[0] <= 38) { - size_t len = base58_decode_check(address, curve->hasher_base58, addr_raw, - MAX_ADDR_RAW_SIZE); - ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, - curve->hasher_pubkey, recovered_raw); - if (memcmp(recovered_raw, addr_raw, len) != 0 || - len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { - return 2; - } - } else + // segwit-in-p2sh + if (signature[0] >= 35 && signature[0] <= 38) { + size_t len = base58_decode_check(address, curve->hasher_base58, addr_raw, + MAX_ADDR_RAW_SIZE); + ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, + curve->hasher_pubkey, recovered_raw); + if (memcmp(recovered_raw, addr_raw, len) != 0 || + len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { + return 2; + } + } else // segwit if (signature[0] >= 39 && signature[0] <= 42) { - int witver; - size_t len; - if (!coin->has_bech32_prefix || - !segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix, - address)) { - return 4; - } - ecdsa_get_pubkeyhash(pubkey, curve->hasher_pubkey, addr_raw); - if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) { - return 2; - } - } else { - return 4; - } + int witver; + size_t len; + if (!coin->has_bech32_prefix || + !segwit_addr_decode(&witver, recovered_raw, &len, + coin->bech32_prefix, address)) { + return 4; + } + ecdsa_get_pubkeyhash(pubkey, curve->hasher_pubkey, addr_raw); + if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || + len != 20) { + return 2; + } + } else { + return 4; + } return 0; } -uint8_t *cryptoHDNodePathToPubkey(const CoinType *coin, - const HDNodePathType *hdnodepath) { - if (!hdnodepath || - !hdnodepath->node.has_public_key || +uint8_t* cryptoHDNodePathToPubkey(const CoinType* coin, + const HDNodePathType* hdnodepath) { + if (!hdnodepath || !hdnodepath->node.has_public_key || hdnodepath->node.public_key.size != 33) { return 0; } @@ -293,11 +293,11 @@ uint8_t *cryptoHDNodePathToPubkey(const CoinType *coin, return node.public_key; } -int cryptoMultisigPubkeyIndex(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - const uint8_t *pubkey) { +int cryptoMultisigPubkeyIndex(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + const uint8_t* pubkey) { for (size_t i = 0; i < multisig->pubkeys_count; i++) { - const uint8_t *node_pubkey = + const uint8_t* node_pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (node_pubkey && memcmp(node_pubkey, pubkey, 33) == 0) { return i; @@ -306,8 +306,8 @@ int cryptoMultisigPubkeyIndex(const CoinType *coin, return -1; } -int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, - uint8_t *hash) { +int cryptoMultisigFingerprint(const MultisigRedeemScriptType* multisig, + uint8_t* hash) { static const HDNodePathType *ptr[15], *swap; const uint32_t n = multisig->pubkeys_count; if (n < 1 || n > 15) { @@ -336,50 +336,50 @@ int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, // hash sorted nodes SHA256_CTX ctx; sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t)); + sha256_Update(&ctx, (const uint8_t*)&(multisig->m), sizeof(uint32_t)); for (uint32_t i = 0; i < n; i++) { animating_progress_handler("Calculating multisig fingerprint...", (i * 1000) / n); - sha256_Update(&ctx, (const uint8_t *)&(ptr[i]->node.depth), + sha256_Update(&ctx, (const uint8_t*)&(ptr[i]->node.depth), sizeof(uint32_t)); - sha256_Update(&ctx, (const uint8_t *)&(ptr[i]->node.fingerprint), + sha256_Update(&ctx, (const uint8_t*)&(ptr[i]->node.fingerprint), sizeof(uint32_t)); - sha256_Update(&ctx, (const uint8_t *)&(ptr[i]->node.child_num), + sha256_Update(&ctx, (const uint8_t*)&(ptr[i]->node.child_num), sizeof(uint32_t)); sha256_Update(&ctx, ptr[i]->node.chain_code.bytes, 32); sha256_Update(&ctx, ptr[i]->node.public_key.bytes, 33); } - sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t)); + sha256_Update(&ctx, (const uint8_t*)&n, sizeof(uint32_t)); sha256_Final(&ctx, hash); animating_progress_handler("Calculating multisig fingerprint...", 100 * 1000); return 1; } -int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) { +int cryptoIdentityFingerprint(const IdentityType* identity, uint8_t* hash) { SHA256_CTX ctx; sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)&(identity->index), sizeof(uint32_t)); + sha256_Update(&ctx, (const uint8_t*)&(identity->index), sizeof(uint32_t)); if (identity->has_proto && identity->proto[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->proto), + sha256_Update(&ctx, (const uint8_t*)(identity->proto), strlen(identity->proto)); - sha256_Update(&ctx, (const uint8_t *)"://", 3); + sha256_Update(&ctx, (const uint8_t*)"://", 3); } if (identity->has_user && identity->user[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->user), + sha256_Update(&ctx, (const uint8_t*)(identity->user), strlen(identity->user)); - sha256_Update(&ctx, (const uint8_t *)"@", 1); + sha256_Update(&ctx, (const uint8_t*)"@", 1); } if (identity->has_host && identity->host[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->host), + sha256_Update(&ctx, (const uint8_t*)(identity->host), strlen(identity->host)); } if (identity->has_port && identity->port[0]) { - sha256_Update(&ctx, (const uint8_t *)":", 1); - sha256_Update(&ctx, (const uint8_t *)(identity->port), + sha256_Update(&ctx, (const uint8_t*)":", 1); + sha256_Update(&ctx, (const uint8_t*)(identity->port), strlen(identity->port)); } if (identity->has_path && identity->path[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->path), + sha256_Update(&ctx, (const uint8_t*)(identity->path), strlen(identity->path)); } sha256_Final(&ctx, hash); diff --git a/lib/firmware/eip712.c b/lib/firmware/eip712.c index 25e14d3b1..1fec75f4b 100644 --- a/lib/firmware/eip712.c +++ b/lib/firmware/eip712.c @@ -16,21 +16,19 @@ * along with this library. If not, see . */ - -/* - Produces hashes based on the metamask v4 rules. This is different from the EIP-712 spec - in how arrays of structs are hashed but is compatable with metamask. - See https://github.com/MetaMask/eth-sig-util/pull/107 +/* + Produces hashes based on the metamask v4 rules. This is different from the + EIP-712 spec in how arrays of structs are hashed but is compatable with + metamask. See https://github.com/MetaMask/eth-sig-util/pull/107 eip712 data rules: Parser wants to see C strings, not javascript strings: - requires all complete json message strings to be enclosed by braces, i.e., { ... } - Cannot have entire json string quoted, i.e., "{ ... }" will not work. - Remove all quote escape chars, e.g., {"types": not {\"types\": - ints: Strings representing ints must fit into a long size (64-bits). - Note: Do not prefix ints or uints with 0x - All hex and byte strings must be big-endian - Byte strings and address should be prefixed by 0x + requires all complete json message strings to be enclosed by braces, + i.e., { ... } Cannot have entire json string quoted, i.e., "{ ... }" will not + work. Remove all quote escape chars, e.g., {"types": not {\"types\": ints: + Strings representing ints must fit into a long size (64-bits). Note: Do not + prefix ints or uints with 0x All hex and byte strings must be big-endian Byte + strings and address should be prefixed by 0x */ #include @@ -44,749 +42,745 @@ #include "trezor/crypto/sha3.h" #include "trezor/crypto/memzero.h" -static const char *udefList[MAX_USERDEF_TYPES] = {0}; +static const char* udefList[MAX_USERDEF_TYPES] = {0}; static dm confirmProp; -static const char *nameForValue; - -int encodableType(const char *typeStr) { - int ctr; - - if (0 == strncmp(typeStr, "address", sizeof("address")-1)) { - return ADDRESS; - } - if (0 == strncmp(typeStr, "string", sizeof("string")-1)) { - return STRING; - } - if (0 == strncmp(typeStr, "int", sizeof("int")-1)) { - // This could be 'int8', 'int16', ..., 'int256' - return INT; - } - if (0 == strncmp(typeStr, "uint", sizeof("uint")-1)) { - // This could be 'uint8', 'uint16', ..., 'uint256' - return UINT; - } - if (0 == strncmp(typeStr, "bytes", sizeof("bytes")-1)) { - // This could be 'bytes', 'bytes1', ..., 'bytes32' - if (0 == strcmp(typeStr, "bytes")) { - return BYTES; - } else { - // parse out the length val - uint8_t byteTypeSize = (uint8_t)(strtol((typeStr+5), NULL, 10)); - if (byteTypeSize > 32) { - return NOT_ENCODABLE; - } else { - return BYTES_N; - } - } - } - if (0 == strcmp(typeStr, "bool")) { - return BOOL; - } - - // See if type already defined. If so, skip, otherwise add it to list - for(ctr=0; ctr 32) { + return NOT_ENCODABLE; + } else { + return BYTES_N; + } + } + } + if (0 == strcmp(typeStr, "bool")) { + return BOOL; + } + + // See if type already defined. If so, skip, otherwise add it to list + for (ctr = 0; ctr < MAX_USERDEF_TYPES; ctr++) { + char typeNoArrTok[MAX_TYPESTRING] = {0}; + + strncpy(typeNoArrTok, typeStr, sizeof(typeNoArrTok) - 1); + strtok(typeNoArrTok, "["); // eliminate the array tokens if there + + if (udefList[ctr] != 0) { + if (0 == strncmp(udefList[ctr], typeNoArrTok, + strlen(udefList[ctr]) - strlen(typeNoArrTok))) { + return PREV_USERDEF; + } else { + } - } else { - udefList[ctr] = typeStr; - return UDEF_TYPE; - } - } - if (ctr == MAX_USERDEF_TYPES) { - return TOO_MANY_UDEFS; + } else { + udefList[ctr] = typeStr; + return UDEF_TYPE; } + } + if (ctr == MAX_USERDEF_TYPES) { + return TOO_MANY_UDEFS; + } - return NOT_ENCODABLE; // not encodable + return NOT_ENCODABLE; // not encodable } /* - Entry: + Entry: eip712Types points to eip712 json type structure to parse typeS points to the type to parse from jType - typeStr points to caller allocated, zeroized string buffer of size STRBUFSIZE+1 - Exit: - typeStr points to hashable type string - returns error list status + typeStr points to caller allocated, zeroized string buffer of size + STRBUFSIZE+1 Exit: typeStr points to hashable type string returns error list + status NOTE: reentrant! */ -int parseType(const json_t *eip712Types, const char *typeS, char *typeStr) { - json_t const *tarray, *pairs; - const json_t *jType; - char append[STRBUFSIZE+1] = {0}; - int encTest; - const char *typeType = NULL; - int errRet = SUCCESS; - const json_t *obTest; - const char *nameTest; - const char *pVal; - - if (NULL == (jType = json_getProperty(eip712Types, typeS))) { - errRet = JSON_TYPE_S_ERR; - return errRet; - } - - if (NULL == (nameTest = json_getName(jType))) { - errRet = JSON_TYPE_S_NAMEERR; - return errRet; - } - - strncat(typeStr, nameTest, STRBUFSIZE - strlen((const char *)typeStr)); - strncat(typeStr, "(", STRBUFSIZE - strlen((const char *)typeStr)); - - tarray = json_getChild(jType); - while (tarray != 0) { - if (NULL == (pairs = json_getChild(tarray))) { - errRet = JSON_NO_PAIRS; +int parseType(const json_t* eip712Types, const char* typeS, char* typeStr) { + json_t const *tarray, *pairs; + const json_t* jType; + char append[STRBUFSIZE + 1] = {0}; + const char* typeType = NULL; + const json_t* obTest; + const char* nameTest; + + if (NULL == (jType = json_getProperty(eip712Types, typeS))) { + return JSON_TYPE_S_ERR; + } + + if (NULL == (nameTest = json_getName(jType))) { + return JSON_TYPE_S_NAMEERR; + } + + strncat(typeStr, nameTest, STRBUFSIZE - strlen((const char*)typeStr)); + strncat(typeStr, "(", STRBUFSIZE - strlen((const char*)typeStr)); + + tarray = json_getChild(jType); + while (tarray != 0) { + if (NULL == (pairs = json_getChild(tarray))) { + return JSON_NO_PAIRS; + } + // should be type JSON_TEXT + if (pairs->type != JSON_TEXT) { + return JSON_PAIRS_NOTEXT; + } else { + if (NULL == (obTest = json_getSibling(pairs))) { + return JSON_NO_PAIRS_SIB; + } + typeType = json_getValue(obTest); + int encTest = encodableType(typeType); + if (encTest == UDEF_TYPE) { + // This is a user-defined type, parse it and append later + if (']' == typeType[strlen(typeType) - 1]) { + // array of structs. To parse name, remove array tokens. + char typeNoArrTok[MAX_TYPESTRING] = {0}; + strncpy(typeNoArrTok, typeType, sizeof(typeNoArrTok) - 1); + if (strlen(typeNoArrTok) < strlen(typeType)) { + return UDEF_NAME_ERROR; + } + + strtok(typeNoArrTok, "["); + int errRet; + if (STACK_GOOD != (errRet = memcheck(STACK_SIZE_GUARD))) { return errRet; - } - // should be type JSON_TEXT - if (pairs->type != JSON_TEXT) { - errRet = JSON_PAIRS_NOTEXT; + } + if (SUCCESS != + (errRet = parseType(eip712Types, typeNoArrTok, append))) { return errRet; + } } else { - if (NULL == (obTest = json_getSibling(pairs))) { - errRet = JSON_NO_PAIRS_SIB; - return errRet; - } - typeType = json_getValue(obTest); - encTest = encodableType(typeType); - if (encTest == UDEF_TYPE) { - //This is a user-defined type, parse it and append later - if (']' == typeType[strlen(typeType)-1]) { - // array of structs. To parse name, remove array tokens. - char typeNoArrTok[MAX_TYPESTRING] = {0}; - strncpy(typeNoArrTok, typeType, sizeof(typeNoArrTok)-1); - if (strlen(typeNoArrTok) < strlen(typeType)) { - return UDEF_NAME_ERROR; - } - - strtok(typeNoArrTok, "["); - if (STACK_GOOD != (errRet = memcheck(STACK_SIZE_GUARD))) { - return errRet; - } - if (SUCCESS != (errRet = parseType(eip712Types, typeNoArrTok, append))) { - return errRet; - } - } else { - if (STACK_GOOD != (errRet = memcheck(STACK_SIZE_GUARD))) { - return errRet; - } - if (SUCCESS != (errRet = parseType(eip712Types, typeType, append))) { - return errRet; - } - } - } else if (encTest == TOO_MANY_UDEFS) { - return UDEFS_OVERFLOW; - } else if (encTest == NOT_ENCODABLE) { - return TYPE_NOT_ENCODABLE; - } - - if (NULL == (pVal = json_getValue(pairs))) { - errRet = JSON_NOPAIRVAL; - return errRet; - } - strncat(typeStr, typeType, STRBUFSIZE - strlen((const char *)typeStr)); - strncat(typeStr, " ", STRBUFSIZE - strlen((const char *)typeStr)); - strncat(typeStr, pVal, STRBUFSIZE - strlen((const char *)typeStr)); - strncat(typeStr, ",", STRBUFSIZE - strlen((const char *)typeStr)); - + int errRet; + if (STACK_GOOD != (errRet = memcheck(STACK_SIZE_GUARD))) { + return errRet; + } + if (SUCCESS != (errRet = parseType(eip712Types, typeType, append))) { + return errRet; + } } - tarray = json_getSibling(tarray); - } - // typeStr ends with a ',' unless there are no parameters to the type. - if (typeStr[strlen(typeStr)-1] == ',') { - // replace last comma with a paren - typeStr[strlen(typeStr)-1] = ')'; - } else { - // append paren, there are no parameters - strncat(typeStr, ")", STRBUFSIZE - 1); - } - if (strlen(append) > 0) { - strncat(typeStr, append, STRBUFSIZE - strlen((const char *)append)); - } - - return SUCCESS; + } else if (encTest == TOO_MANY_UDEFS) { + return UDEFS_OVERFLOW; + } else if (encTest == NOT_ENCODABLE) { + return TYPE_NOT_ENCODABLE; + } + + const char* pVal = json_getValue(pairs); + if (NULL == pVal) { + return JSON_NOPAIRVAL; + } + strncat(typeStr, typeType, STRBUFSIZE - strlen((const char*)typeStr)); + strncat(typeStr, " ", STRBUFSIZE - strlen((const char*)typeStr)); + strncat(typeStr, pVal, STRBUFSIZE - strlen((const char*)typeStr)); + strncat(typeStr, ",", STRBUFSIZE - strlen((const char*)typeStr)); + } + tarray = json_getSibling(tarray); + } + // typeStr ends with a ',' unless there are no parameters to the type. + if (typeStr[strlen(typeStr) - 1] == ',') { + // replace last comma with a paren + typeStr[strlen(typeStr) - 1] = ')'; + } else { + // append paren, there are no parameters + strncat(typeStr, ")", STRBUFSIZE - 1); + } + if (strlen(append) > 0) { + strncat(typeStr, append, STRBUFSIZE - strlen((const char*)append)); + } + + return SUCCESS; } -int encAddress(const char *string, uint8_t *encoded) { - unsigned ctr; - char byteStrBuf[3] = {0}; - - if (string == NULL) { - return ADDR_STRING_NULL; - } - if (ADDRESS_SIZE < strlen(string)) { - return ADDR_STRING_VFLOW; - } - - for (ctr=0; ctr<12; ctr++) { - encoded[ctr] = '\0'; - } - for (ctr=12; ctr<32; ctr++) { - strncpy(byteStrBuf, &string[2*((ctr-12))+2], 2); - encoded[ctr] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); - } - return SUCCESS; +int encAddress(const char* string, uint8_t* encoded) { + unsigned ctr; + char byteStrBuf[3] = {0}; + + if (string == NULL) { + return ADDR_STRING_NULL; + } + if (ADDRESS_SIZE < strlen(string)) { + return ADDR_STRING_VFLOW; + } + + for (ctr = 0; ctr < 12; ctr++) { + encoded[ctr] = '\0'; + } + for (ctr = 12; ctr < 32; ctr++) { + strncpy(byteStrBuf, &string[2 * ((ctr - 12)) + 2], 2); + encoded[ctr] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); + } + return SUCCESS; } -int encString(const char *string, uint8_t *encoded) { - struct SHA3_CTX strCtx; +int encString(const char* string, uint8_t* encoded) { + struct SHA3_CTX strCtx; - sha3_256_Init(&strCtx); - sha3_Update(&strCtx, (const unsigned char *)string, (size_t)strlen(string)); - keccak_Final(&strCtx, encoded); - return SUCCESS; + sha3_256_Init(&strCtx); + sha3_Update(&strCtx, (const unsigned char*)string, (size_t)strlen(string)); + keccak_Final(&strCtx, encoded); + return SUCCESS; } -int encodeBytes(const char *string, uint8_t *encoded) { - struct SHA3_CTX byteCtx; - const char *valStrPtr = string+2; - uint8_t valByte[1]; - char byteStrBuf[3] = {0}; - - sha3_256_Init(&byteCtx); - while (*valStrPtr != '\0') { - strncpy(byteStrBuf, valStrPtr, 2); - valByte[0] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); - sha3_Update(&byteCtx, - (const unsigned char *)valByte, - (size_t)sizeof(uint8_t)); - valStrPtr+=2; - } - keccak_Final(&byteCtx, encoded); - return SUCCESS; +int encodeBytes(const char* string, uint8_t* encoded) { + struct SHA3_CTX byteCtx; + const char* valStrPtr = string + 2; + uint8_t valByte[1]; + char byteStrBuf[3] = {0}; + + sha3_256_Init(&byteCtx); + while (*valStrPtr != '\0') { + strncpy(byteStrBuf, valStrPtr, 2); + valByte[0] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); + sha3_Update(&byteCtx, (const unsigned char*)valByte, + (size_t)sizeof(uint8_t)); + valStrPtr += 2; + } + keccak_Final(&byteCtx, encoded); + return SUCCESS; } -int encodeBytesN(const char *typeT, const char *string, uint8_t *encoded) { - char byteStrBuf[3] = {0}; - unsigned ctr; - - if (MAX_ENCBYTEN_SIZE < strlen(string)) { - return BYTESN_STRING_ERROR; - } - - // parse out the length val - uint8_t byteTypeSize = (uint8_t)(strtol((typeT+5), NULL, 10)); - if (32 < byteTypeSize) { - return BYTESN_SIZE_ERROR; - } - for (ctr=0; ctr<32; ctr++) { - // zero padding - encoded[ctr] = 0; - } - unsigned zeroFillLen = 32 - ((strlen(string)-2/* skip '0x' */)/2); - // bytesN are zero padded on the right - for (ctr=zeroFillLen; ctr<32; ctr++) { - strncpy(byteStrBuf, &string[2+2*(ctr-zeroFillLen)], 2); - encoded[ctr-zeroFillLen] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); - } - return SUCCESS; +int encodeBytesN(const char* typeT, const char* string, uint8_t* encoded) { + char byteStrBuf[3] = {0}; + unsigned ctr; + + if (MAX_ENCBYTEN_SIZE < strlen(string)) { + return BYTESN_STRING_ERROR; + } + + // parse out the length val + uint8_t byteTypeSize = (uint8_t)(strtol((typeT + 5), NULL, 10)); + if (32 < byteTypeSize) { + return BYTESN_SIZE_ERROR; + } + for (ctr = 0; ctr < 32; ctr++) { + // zero padding + encoded[ctr] = 0; + } + unsigned zeroFillLen = 32 - ((strlen(string) - 2 /* skip '0x' */) / 2); + // bytesN are zero padded on the right + for (ctr = zeroFillLen; ctr < 32; ctr++) { + strncpy(byteStrBuf, &string[2 + 2 * (ctr - zeroFillLen)], 2); + encoded[ctr - zeroFillLen] = (uint8_t)(strtol(byteStrBuf, NULL, 16)); + } + return SUCCESS; } -int confirmName(const char *name, bool valAvailable) { - if (valAvailable) { - nameForValue = name; - } else { - (void)review(ButtonRequestType_ButtonRequest_Other, "MESSAGE DATA", "Press button to continue for\n\"%s\" values", name); - } - return SUCCESS; +int confirmName(const char* name, bool valAvailable) { + if (valAvailable) { + nameForValue = name; + } else { + (void)review(ButtonRequestType_ButtonRequest_Other, "MESSAGE DATA", + "Press button to continue for\n\"%s\" values", name); + } + return SUCCESS; } -int confirmValue(const char *value) { - (void)review(ButtonRequestType_ButtonRequest_Other, "MESSAGE DATA", "%s %s", nameForValue, value); - return SUCCESS; +int confirmValue(const char* value) { + (void)review(ButtonRequestType_ButtonRequest_Other, "MESSAGE DATA", "%s %s", + nameForValue, value); + return SUCCESS; } -static const char *dsname=NULL, *dsversion=NULL, *dschainId=NULL, *dsverifyingContract=NULL; -void marshallDsVals(const char *value) { - - if (0 == strncmp(nameForValue, "name", sizeof("name"))) { - dsname = value; - } - if (0 == strncmp(nameForValue, "version", sizeof("version"))) { - dsversion = value; - } - if (0 == strncmp(nameForValue, "chainId", sizeof("chainId"))) { - dschainId = value; - } - if (0 == strncmp(nameForValue, "verifyingContract", sizeof("verifyingContract"))) { - dsverifyingContract = value; - } - return; +static const char *dsname = NULL, *dsversion = NULL, *dschainId = NULL, + *dsverifyingContract = NULL; +void marshallDsVals(const char* value) { + if (0 == strncmp(nameForValue, "name", sizeof("name"))) { + dsname = value; + } + if (0 == strncmp(nameForValue, "version", sizeof("version"))) { + dsversion = value; + } + if (0 == strncmp(nameForValue, "chainId", sizeof("chainId"))) { + dschainId = value; + } + if (0 == + strncmp(nameForValue, "verifyingContract", sizeof("verifyingContract"))) { + dsverifyingContract = value; + } + return; } void dsConfirm(void) { - // First check if we recognize the contract - const TokenType *assetToken; - uint8_t addrHexStr[20] = {0}; - char name[41] = {0}; - char version[11] = {0}; - uint32_t chainInt; - bool noChain = true; - int ctr; - IconType iconNum = NO_ICON; - char title[64] = {0}; - char *fillerStr = ""; - char chainStr[33] = {0}; - char verifyingContract[65] = {0}; - - if (dsname != NULL) { - strncpy(name, dsname, 40); - } - if (dsversion != NULL) { - strncpy(version, dsversion, 10); - } - - if (dsverifyingContract != NULL) { - for (ctr=2; ctr<42; ctr+=2) { - sscanf((char *)&dsverifyingContract[ctr], "%2hhx", &addrHexStr[(ctr-2)/2]); - } - strcat(verifyingContract, "Verifying Contract: "); - strncat(verifyingContract, dsverifyingContract, sizeof(verifyingContract) - sizeof("Verifying Contract: ")); - } - - if (NULL != dschainId) { - noChain = false; + // First check if we recognize the contract + const TokenType* assetToken; + uint8_t addrHexStr[20] = {0}; + char name[41] = {0}; + char version[11] = {0}; + uint32_t chainInt; + bool noChain = true; + int ctr; + IconType iconNum = NO_ICON; + char title[64] = {0}; + char* fillerStr = ""; + char chainStr[33] = {0}; + char verifyingContract[65] = {0}; + + if (dsname != NULL) { + strncpy(name, dsname, 40); + } + if (dsversion != NULL) { + strncpy(version, dsversion, 10); + } + + if (dsverifyingContract != NULL) { + for (ctr = 2; ctr < 42; ctr += 2) { + sscanf((char*)&dsverifyingContract[ctr], "%2hhx", + &addrHexStr[(ctr - 2) / 2]); + } + strcat(verifyingContract, "Verifying Contract: "); + strncat(verifyingContract, dsverifyingContract, + sizeof(verifyingContract) - sizeof("Verifying Contract: ")); + } + + if (NULL != dschainId) { + noChain = false; #ifdef EMULATOR - sscanf((char *)dschainId, "%d", &chainInt); + sscanf((char*)dschainId, "%u", &chainInt); #else - sscanf((char *)dschainId, "%ld", &chainInt); + sscanf((char*)dschainId, "%ld", &chainInt); #endif - // As more chains are supported, add icon choice below - // TBD: not implemented for first release - // if (chainInt == 1) { - // iconNum = ETHEREUM_ICON; - // } - } - if (noChain == false && dsverifyingContract != NULL) { - assetToken = tokenByChainAddress(chainInt, (uint8_t *)addrHexStr); - if (strncmp(assetToken->ticker, " UNKN", 5) == 0) { - fillerStr = ""; - } else { - //verifyingContract = assetToken->ticker; - //fillerStr = "\n\n"; - fillerStr = ""; - } - } - - strncpy(title, name, 40); - if (NULL != dsversion) { - strncat(title, " Ver: ", 63-strlen(title)); - strncat(title, version, 63-strlen(title)); - } - if (NULL != dschainId) { - snprintf(chainStr, 32, "chain %s, ", dschainId); - } - //snprintf(contractStr, 64, "verifyingContract: %s", verifyingContract); - (void)review_with_icon(ButtonRequestType_ButtonRequest_Other, iconNum, - title, "%s %s%s", chainStr, verifyingContract, fillerStr); - dsname = NULL; - dsversion = NULL; - dschainId = NULL; - dsverifyingContract = NULL; + // As more chains are supported, add icon choice below + // TBD: not implemented for first release + // if (chainInt == 1) { + // iconNum = ETHEREUM_ICON; + // } + } + if (noChain == false && dsverifyingContract != NULL) { + assetToken = tokenByChainAddress(chainInt, (uint8_t*)addrHexStr); + (void)assetToken; + fillerStr = ""; + } + + strncpy(title, name, 40); + if (NULL != dsversion) { + strncat(title, " Ver: ", 63 - strlen(title)); + strncat(title, version, 63 - strlen(title)); + } + if (NULL != dschainId) { + snprintf(chainStr, 32, "chain %s, ", dschainId); + } + // snprintf(contractStr, 64, "verifyingContract: %s", verifyingContract); + (void)review_with_icon(ButtonRequestType_ButtonRequest_Other, iconNum, title, + "%s %s%s", chainStr, verifyingContract, fillerStr); + dsname = NULL; + dsversion = NULL; + dschainId = NULL; + dsverifyingContract = NULL; } /* - Entry: + Entry: eip712Types points to the eip712 types structure jType points to eip712 json type structure to parse nextVal points to the next value to encode - msgCtx points to caller allocated hash context to hash encoded values into. - Exit: - msgCtx points to current final hash context - returns error status + msgCtx points to caller allocated hash context to hash encoded + values into. Exit: msgCtx points to current final hash context returns error + status NOTE: reentrant! */ -int parseVals(const json_t *eip712Types, const json_t *jType, const json_t *nextVal, struct SHA3_CTX *msgCtx) { - json_t const *tarray, *pairs, *walkVals, *obTest; - int ctr; - const char *typeName = NULL, *typeType = NULL; - uint8_t encBytes[32] = {0}; // holds the encrypted bytes for the message - const char *valStr = NULL; - struct SHA3_CTX valCtx = {0}; // local hash context - bool hasValue = 0; - bool ds_vals = 0; // domain sep values are confirmed on a single screen - int errRet = SUCCESS; - - if (0 == strncmp(json_getName(jType), "EIP712Domain", sizeof("EIP712Domain"))) { - ds_vals = true; - } - - tarray = json_getChild(jType); - - while (tarray != 0) { - if (NULL == (pairs = json_getChild(tarray))) { - errRet = JSON_NO_PAIRS; - return errRet; - } - // should be type JSON_TEXT - if (pairs->type != JSON_TEXT) { - errRet = JSON_PAIRS_NOTEXT; - return errRet; +int parseVals(const json_t* eip712Types, const json_t* jType, + const json_t* nextVal, struct SHA3_CTX* msgCtx) { + json_t const *tarray, *pairs, *walkVals, *obTest; + int ctr; + const char* typeType = NULL; + uint8_t encBytes[32] = {0}; // holds the encrypted bytes for the message + const char* valStr = NULL; + struct SHA3_CTX valCtx = {0}; // local hash context + bool ds_vals = 0; // domain sep values are confirmed on a single screen + int errRet; + + if (0 == + strncmp(json_getName(jType), "EIP712Domain", sizeof("EIP712Domain"))) { + ds_vals = true; + } + + tarray = json_getChild(jType); + + while (tarray != 0) { + if (NULL == (pairs = json_getChild(tarray))) { + return JSON_NO_PAIRS; + } + // should be type JSON_TEXT + if (pairs->type != JSON_TEXT) { + return JSON_PAIRS_NOTEXT; + } else { + const char* typeName = json_getValue(pairs); + if (NULL == typeName) { + return JSON_NOPAIRNAME; + } + if (NULL == (obTest = json_getSibling(pairs))) { + return JSON_NO_PAIRS_SIB; + } + if (NULL == (typeType = json_getValue(obTest))) { + return JSON_TYPE_T_NOVAL; + } + walkVals = nextVal; + while (0 != walkVals) { + if (0 == strcmp(json_getName(walkVals), typeName)) { + valStr = json_getValue(walkVals); + break; } else { - if (NULL == (typeName = json_getValue(pairs))) { - errRet = JSON_NOPAIRNAME; + // keep looking for val + walkVals = json_getSibling(walkVals); + } + } + + bool hasValue = (JSON_TEXT == json_getType(walkVals) || + JSON_INTEGER == json_getType(walkVals)); + confirmName(typeName, hasValue); + + if (walkVals == 0) { + return JSON_TYPE_WNOVAL; + } else { + if (0 == strncmp("address", typeType, strlen("address") - 1)) { + if (']' == typeType[strlen(typeType) - 1]) { + // array of addresses + json_t const* addrVals = json_getChild(walkVals); + sha3_256_Init(&valCtx); // hash of concatenated encoded strings + while (0 != addrVals) { + // just walk the string values assuming, for fixed sizes, all + // values are there. + if (ds_vals) { + marshallDsVals(json_getValue(addrVals)); + } else { + confirmValue(json_getValue(addrVals)); + } + + errRet = encAddress(json_getValue(addrVals), encBytes); + if (SUCCESS != errRet) { return errRet; + } + sha3_Update(&valCtx, (const unsigned char*)encBytes, 32); + addrVals = json_getSibling(addrVals); } - if (NULL == (obTest = json_getSibling(pairs))) { - errRet = JSON_NO_PAIRS_SIB; - return errRet; + keccak_Final(&valCtx, encBytes); + } else { + if (ds_vals) { + marshallDsVals(valStr); + } else { + confirmValue(valStr); + } + errRet = encAddress(valStr, encBytes); + if (SUCCESS != errRet) { + return errRet; } - if (NULL == (typeType = json_getValue(obTest))) { - errRet = JSON_TYPE_T_NOVAL; + } + + } else if (0 == strncmp("string", typeType, strlen("string") - 1)) { + if (']' == typeType[strlen(typeType) - 1]) { + // array of strings + json_t const* stringVals = json_getChild(walkVals); + uint8_t strEncBytes[32]; + sha3_256_Init(&valCtx); // hash of concatenated encoded strings + while (0 != stringVals) { + // just walk the string values assuming, for fixed sizes, all + // values are there. + if (ds_vals) { + marshallDsVals(json_getValue(stringVals)); + } else { + confirmValue(json_getValue(stringVals)); + } + errRet = encString(json_getValue(stringVals), strEncBytes); + if (SUCCESS != errRet) { return errRet; + } + sha3_Update(&valCtx, (const unsigned char*)strEncBytes, 32); + stringVals = json_getSibling(stringVals); } - walkVals = nextVal; - while (0 != walkVals) { - if (0 == strcmp(json_getName(walkVals), typeName)) { - valStr = json_getValue(walkVals); - break; - } else { - // keep looking for val - walkVals = json_getSibling(walkVals); - } + keccak_Final(&valCtx, encBytes); + } else { + if (ds_vals) { + marshallDsVals(valStr); + } else { + confirmValue(valStr); } - - if (JSON_TEXT == json_getType(walkVals) || JSON_INTEGER == json_getType(walkVals)) { - hasValue = 1; + errRet = encString(valStr, encBytes); + if (SUCCESS != errRet) { + return errRet; + } + } + + } else if ((0 == strncmp("uint", typeType, strlen("uint") - 1)) || + (0 == strncmp("int", typeType, strlen("int") - 1))) { + if (']' == typeType[strlen(typeType) - 1]) { + return INT_ARRAY_ERROR; + } else { + if (ds_vals) { + marshallDsVals(valStr); + } else { + confirmValue(valStr); + } + uint8_t negInt = 0; // 0 is positive, 1 is negative + if (0 == strncmp("int", typeType, strlen("int") - 1)) { + if (*valStr == '-') { + negInt = 1; + } + } + // parse out the length val + for (ctr = 0; ctr < 32; ctr++) { + if (negInt) { + // sign extend negative values + encBytes[ctr] = 0xFF; + } else { + // zero padding for positive + encBytes[ctr] = 0; + } + } + // all int strings are assumed to be base 10 and fit into 64 bits + long long intVal = strtoll(valStr, NULL, 10); + // Needs to be big endian, so add to encBytes appropriately + encBytes[24] = (intVal >> 56) & 0xff; + encBytes[25] = (intVal >> 48) & 0xff; + encBytes[26] = (intVal >> 40) & 0xff; + encBytes[27] = (intVal >> 32) & 0xff; + encBytes[28] = (intVal >> 24) & 0xff; + encBytes[29] = (intVal >> 16) & 0xff; + encBytes[30] = (intVal >> 8) & 0xff; + encBytes[31] = (intVal) & 0xff; + } + + } else if (0 == strncmp("bytes", typeType, strlen("bytes"))) { + if (']' == typeType[strlen(typeType) - 1]) { + return BYTESN_ARRAY_ERROR; + } else { + // This could be 'bytes', 'bytes1', ..., 'bytes32' + if (ds_vals) { + marshallDsVals(valStr); } else { - hasValue = 0; + confirmValue(valStr); } - confirmName(typeName, hasValue); + if (0 == strcmp(typeType, "bytes")) { + errRet = encodeBytes(valStr, encBytes); + if (SUCCESS != errRet) { + return errRet; + } - if (walkVals == 0) { - errRet = JSON_TYPE_WNOVAL; + } else { + errRet = encodeBytesN(typeType, valStr, encBytes); + if (SUCCESS != errRet) { return errRet; + } + } + } + + } else if (0 == strncmp("bool", typeType, strlen(typeType))) { + if (']' == typeType[strlen(typeType) - 1]) { + return BOOL_ARRAY_ERROR; + } else { + if (ds_vals) { + marshallDsVals(valStr); } else { - if (0 == strncmp("address", typeType, strlen("address")-1)) { - if (']' == typeType[strlen(typeType)-1]) { - // array of addresses - json_t const *addrVals = json_getChild(walkVals); - sha3_256_Init(&valCtx); // hash of concatenated encoded strings - while (0 != addrVals) { - // just walk the string values assuming, for fixed sizes, all values are there. - if (ds_vals) { - marshallDsVals(json_getValue(addrVals)); - } else { - confirmValue(json_getValue(addrVals)); - } - - errRet = encAddress(json_getValue(addrVals), encBytes); - if (SUCCESS != errRet) { - return errRet; - } - sha3_Update(&valCtx, (const unsigned char *)encBytes, 32); - addrVals = json_getSibling(addrVals); - } - keccak_Final(&valCtx, encBytes); - } else { - if (ds_vals) { - marshallDsVals(valStr); - } else { - confirmValue(valStr); - } - errRet = encAddress(valStr, encBytes); - if (SUCCESS != errRet) { - return errRet; - } - } - - } else if (0 == strncmp("string", typeType, strlen("string")-1)) { - if (']' == typeType[strlen(typeType)-1]) { - // array of strings - json_t const *stringVals = json_getChild(walkVals); - uint8_t strEncBytes[32]; - sha3_256_Init(&valCtx); // hash of concatenated encoded strings - while (0 != stringVals) { - // just walk the string values assuming, for fixed sizes, all values are there. - if (ds_vals) { - marshallDsVals(json_getValue(stringVals)); - } else { - confirmValue(json_getValue(stringVals)); - } - errRet = encString(json_getValue(stringVals), strEncBytes); - if (SUCCESS != errRet) { - return errRet; - } - sha3_Update(&valCtx, (const unsigned char *)strEncBytes, 32); - stringVals = json_getSibling(stringVals); - } - keccak_Final(&valCtx, encBytes); - } else { - if (ds_vals) { - marshallDsVals(valStr); - } else { - confirmValue(valStr); - } - errRet = encString(valStr, encBytes); - if (SUCCESS != errRet) { - return errRet; - } - } - - } else if ((0 == strncmp("uint", typeType, strlen("uint")-1)) || - (0 == strncmp("int", typeType, strlen("int")-1))) { - - if (']' == typeType[strlen(typeType)-1]) { - return INT_ARRAY_ERROR; - } else { - if (ds_vals) { - marshallDsVals(valStr); - } else { - confirmValue(valStr); - } - uint8_t negInt = 0; // 0 is positive, 1 is negative - if (0 == strncmp("int", typeType, strlen("int")-1)) { - if (*valStr == '-') { - negInt = 1; - } - } - // parse out the length val - for (ctr=0; ctr<32; ctr++) { - if (negInt) { - // sign extend negative values - encBytes[ctr] = 0xFF; - } else { - // zero padding for positive - encBytes[ctr] = 0; - } - } - // all int strings are assumed to be base 10 and fit into 64 bits - long long intVal = strtoll(valStr, NULL, 10); - // Needs to be big endian, so add to encBytes appropriately - encBytes[24] = (intVal >> 56) & 0xff; - encBytes[25] = (intVal >> 48) & 0xff; - encBytes[26] = (intVal >> 40) & 0xff; - encBytes[27] = (intVal >> 32) & 0xff; - encBytes[28] = (intVal >> 24) & 0xff; - encBytes[29] = (intVal >> 16) & 0xff; - encBytes[30] = (intVal >> 8) & 0xff; - encBytes[31] = (intVal) & 0xff; - } - - } else if (0 == strncmp("bytes", typeType, strlen("bytes"))) { - if (']' == typeType[strlen(typeType)-1]) { - return BYTESN_ARRAY_ERROR; - } else { - // This could be 'bytes', 'bytes1', ..., 'bytes32' - if (ds_vals) { - marshallDsVals(valStr); - } else { - confirmValue(valStr); - } - if (0 == strcmp(typeType, "bytes")) { - errRet = encodeBytes(valStr, encBytes); - if (SUCCESS != errRet) { - return errRet; - } - - } else { - errRet = encodeBytesN(typeType, valStr, encBytes); - if (SUCCESS != errRet) { - return errRet; - } - } - } - - } else if (0 == strncmp("bool", typeType, strlen(typeType))) { - if (']' == typeType[strlen(typeType)-1]) { - return BOOL_ARRAY_ERROR; - } else { - if (ds_vals) { - marshallDsVals(valStr); - } else { - confirmValue(valStr); - } - for (ctr=0; ctr<32; ctr++) { - // leading zeros in bool - encBytes[ctr] = 0; - } - if (0 == strncmp(valStr, "true", sizeof("true"))) { - encBytes[31] = 0x01; - } - } - - } else { - // encode user defined type - char encSubTypeStr[STRBUFSIZE+1] = {0}; - // clear out the user-defined types list - for(ctr=0; ctrname == (ACTION), "Incorrect action name", false); \ } while (0) -bool eos_compileActionDelegate(const EosActionCommon *common, - const EosActionDelegate *action) { +bool eos_compileActionDelegate(const EosActionCommon* common, + const EosActionDelegate* action) { CHECK_COMMON(EOS_DelegateBW); CHECK_PARAM_RET(action->has_sender, "Required field missing", false); @@ -84,8 +84,8 @@ bool eos_compileActionDelegate(const EosActionCommon *common, uint32_t size = 8 + 8 + 16 + 16 + 1; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->sender, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->receiver, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->sender, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->receiver, 8); CHECK_PARAM_RET(eos_compileAsset(&action->net_quantity), "Cannot compile asset: net_quantity", false); @@ -99,8 +99,8 @@ bool eos_compileActionDelegate(const EosActionCommon *common, return true; } -bool eos_compileActionUndelegate(const EosActionCommon *common, - const EosActionUndelegate *action) { +bool eos_compileActionUndelegate(const EosActionCommon* common, + const EosActionUndelegate* action) { CHECK_COMMON(EOS_UndelegateBW); CHECK_PARAM_RET(action->has_sender, "Required field missing", false); @@ -139,8 +139,8 @@ bool eos_compileActionUndelegate(const EosActionCommon *common, uint32_t size = 8 + 8 + 16 + 16; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->sender, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->receiver, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->sender, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->receiver, 8); CHECK_PARAM_RET(eos_compileAsset(&action->net_quantity), "Cannot compile asset: net_quantity", false); @@ -151,8 +151,8 @@ bool eos_compileActionUndelegate(const EosActionCommon *common, return true; } -bool eos_compileActionRefund(const EosActionCommon *common, - const EosActionRefund *action) { +bool eos_compileActionRefund(const EosActionCommon* common, + const EosActionRefund* action) { CHECK_COMMON(EOS_Refund); CHECK_PARAM_RET(action->has_owner, "Required field missing", false); @@ -176,13 +176,13 @@ bool eos_compileActionRefund(const EosActionCommon *common, uint32_t size = 8; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->owner, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->owner, 8); return true; } -bool eos_compileActionBuyRam(const EosActionCommon *common, - const EosActionBuyRam *action) { +bool eos_compileActionBuyRam(const EosActionCommon* common, + const EosActionBuyRam* action) { CHECK_COMMON(EOS_BuyRam); CHECK_PARAM_RET(action->has_payer, "Required field missing", false); @@ -216,8 +216,8 @@ bool eos_compileActionBuyRam(const EosActionCommon *common, uint32_t size = 8 + 8 + 16; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->payer, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->receiver, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->payer, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->receiver, 8); CHECK_PARAM_RET(eos_compileAsset(&action->quantity), "Cannot compile asset: quantity", false); @@ -225,8 +225,8 @@ bool eos_compileActionBuyRam(const EosActionCommon *common, return true; } -bool eos_compileActionBuyRamBytes(const EosActionCommon *common, - const EosActionBuyRamBytes *action) { +bool eos_compileActionBuyRamBytes(const EosActionCommon* common, + const EosActionBuyRamBytes* action) { CHECK_COMMON(EOS_BuyRamBytes); CHECK_PARAM_RET(action->has_payer, "Required field missing", false); @@ -256,15 +256,15 @@ bool eos_compileActionBuyRamBytes(const EosActionCommon *common, uint32_t size = 8 + 8 + 4; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->payer, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->receiver, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->bytes, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->payer, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->receiver, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->bytes, 4); return true; } -bool eos_compileActionSellRam(const EosActionCommon *common, - const EosActionSellRam *action) { +bool eos_compileActionSellRam(const EosActionCommon* common, + const EosActionSellRam* action) { CHECK_COMMON(EOS_SellRam); CHECK_PARAM_RET(action->has_account, "Required field missing", false); @@ -290,14 +290,14 @@ bool eos_compileActionSellRam(const EosActionCommon *common, uint32_t size = 8 + 8; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->bytes, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->bytes, 8); return true; } -bool eos_compileActionVoteProducer(const EosActionCommon *common, - const EosActionVoteProducer *action) { +bool eos_compileActionVoteProducer(const EosActionCommon* common, + const EosActionVoteProducer* action) { CHECK_COMMON(EOS_VoteProducer); CHECK_PARAM_RET(action->has_voter, "Required field missing", false); @@ -327,8 +327,8 @@ bool eos_compileActionVoteProducer(const EosActionCommon *common, uint32_t size = 8 + 8 + eos_hashUInt(NULL, 0) + 0; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->voter, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->proxy, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->voter, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->proxy, 8); eos_hashUInt(&hasher_preimage, /*producers_count=*/0); @@ -392,14 +392,13 @@ bool eos_compileActionVoteProducer(const EosActionCommon *common, 8 * action->producers_count; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->voter, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->voter, 8); hasher_Update(&hasher_preimage, - (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); + (const uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); eos_hashUInt(&hasher_preimage, action->producers_count); for (size_t p = 0; p < action->producers_count; p++) { - hasher_Update(&hasher_preimage, (const uint8_t *)&action->producers[p], - 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->producers[p], 8); } } else { char voter[EOS_NAME_STR_SIZE]; @@ -418,24 +417,24 @@ bool eos_compileActionVoteProducer(const EosActionCommon *common, uint32_t size = 8 + 8 + eos_hashUInt(NULL, 0) + 0; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->voter, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->voter, 8); hasher_Update(&hasher_preimage, - (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); + (const uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); eos_hashUInt(&hasher_preimage, /*producers_count=*/0); } return true; } -static size_t eos_hashAuthorization(Hasher *h, const EosAuthorization *auth) { +static size_t eos_hashAuthorization(Hasher* h, const EosAuthorization* auth) { size_t count = 0; count += 4; - if (h) hasher_Update(h, (const uint8_t *)&auth->threshold, 4); + if (h) hasher_Update(h, (const uint8_t*)&auth->threshold, 4); count += eos_hashUInt(h, auth->keys_count); for (size_t i = 0; i < auth->keys_count; i++) { - const EosAuthorizationKey *auth_key = &auth->keys[i]; + const EosAuthorizationKey* auth_key = &auth->keys[i]; count += eos_hashUInt(NULL, auth_key->type); if (h) eos_hashUInt(h, auth_key->type); @@ -455,37 +454,37 @@ static size_t eos_hashAuthorization(Hasher *h, const EosAuthorization *auth) { } count += 2; - if (h) hasher_Update(h, (const uint8_t *)&auth_key->weight, 2); + if (h) hasher_Update(h, (const uint8_t*)&auth_key->weight, 2); } count += eos_hashUInt(h, auth->accounts_count); for (size_t i = 0; i < auth->accounts_count; i++) { count += 8; if (h) - hasher_Update(h, (const uint8_t *)&auth->accounts[i].account.actor, 8); + hasher_Update(h, (const uint8_t*)&auth->accounts[i].account.actor, 8); count += 8; if (h) - hasher_Update(h, (const uint8_t *)&auth->accounts[i].account.permission, + hasher_Update(h, (const uint8_t*)&auth->accounts[i].account.permission, 8); count += 2; - if (h) hasher_Update(h, (const uint8_t *)&auth->accounts[i].weight, 2); + if (h) hasher_Update(h, (const uint8_t*)&auth->accounts[i].weight, 2); } count += eos_hashUInt(h, auth->waits_count); for (size_t i = 0; i < auth->accounts_count; i++) { count += 4; - if (h) hasher_Update(h, (const uint8_t *)&auth->waits[i].wait_sec, 4); + if (h) hasher_Update(h, (const uint8_t*)&auth->waits[i].wait_sec, 4); count += 2; - if (h) hasher_Update(h, (const uint8_t *)&auth->waits[i].weight, 2); + if (h) hasher_Update(h, (const uint8_t*)&auth->waits[i].weight, 2); } return count; } -static bool isStandardAuthorization(const EosAuthorization *auth) { +static bool isStandardAuthorization(const EosAuthorization* auth) { if (!auth->has_threshold || auth->threshold != 1) return false; if (auth->keys_count != 1) return false; @@ -501,11 +500,11 @@ static bool isStandardAuthorization(const EosAuthorization *auth) { return true; } -static bool confirmStandardAuthorization(const char *title, - const EosAuthorization *auth) { +static bool confirmStandardAuthorization(const char* title, + const EosAuthorization* auth) { char node_str[NODE_STRING_LENGTH]; - const CoinType *coin; - const EosAuthorizationKey *auth_key = &auth->keys[0]; + const CoinType* coin; + const EosAuthorizationKey* auth_key = &auth->keys[0]; if ((coin = coinByName("EOS")) && !bip32_node_to_string(node_str, sizeof(node_str), coin, auth_key->address_n, auth_key->address_n_count, @@ -533,8 +532,8 @@ static bool confirmStandardAuthorization(const char *title, return true; } -static bool confirmArbitraryAuthorization(const char *title, - const EosAuthorization *auth) { +static bool confirmArbitraryAuthorization(const char* title, + const EosAuthorization* auth) { if (!confirm(ButtonRequestType_ButtonRequest_ConfirmEosAction, title, "Require an authorization threshold of %" PRIu32 "?", auth->threshold)) { @@ -545,7 +544,7 @@ static bool confirmArbitraryAuthorization(const char *title, } for (size_t i = 0; i < auth->keys_count; i++) { - const EosAuthorizationKey *auth_key = &auth->keys[i]; + const EosAuthorizationKey* auth_key = &auth->keys[i]; CHECK_PARAM_RET(auth_key->has_weight, "Required field missing", false); CHECK_PARAM_RET( @@ -563,8 +562,7 @@ static bool confirmArbitraryAuthorization(const char *title, return false; } } else { - const CoinType *coin; - if ((coin = coinByName("EOS")) && + if (coinByName("EOS") && !bip32_path_to_string(pubkey, sizeof(pubkey), auth_key->address_n, auth_key->address_n_count)) { memset(pubkey, 0, sizeof(pubkey)); @@ -635,7 +633,7 @@ static bool confirmArbitraryAuthorization(const char *title, return true; } -bool eos_compileAuthorization(const char *title, const EosAuthorization *auth) { +bool eos_compileAuthorization(const char* title, const EosAuthorization* auth) { CHECK_PARAM_RET(auth->has_threshold, "Required field missing", false); if (isStandardAuthorization(auth)) { @@ -649,8 +647,8 @@ bool eos_compileAuthorization(const char *title, const EosAuthorization *auth) { return true; } -bool eos_compileActionUpdateAuth(const EosActionCommon *common, - const EosActionUpdateAuth *action) { +bool eos_compileActionUpdateAuth(const EosActionCommon* common, + const EosActionUpdateAuth* action) { CHECK_COMMON(EOS_UpdateAuth); CHECK_PARAM_RET(action->has_account, "Required field missing", false); @@ -690,9 +688,9 @@ bool eos_compileActionUpdateAuth(const EosActionCommon *common, size_t size = 8 + 8 + 8 + auth_size; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->permission, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->parent, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->permission, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->parent, 8); snprintf(title, sizeof(title), "%s@%s", account, permission); if (!eos_compileAuthorization(title, &action->auth)) return false; @@ -700,8 +698,8 @@ bool eos_compileActionUpdateAuth(const EosActionCommon *common, return true; } -bool eos_compileActionDeleteAuth(const EosActionCommon *common, - const EosActionDeleteAuth *action) { +bool eos_compileActionDeleteAuth(const EosActionCommon* common, + const EosActionDeleteAuth* action) { CHECK_COMMON(EOS_DeleteAuth); CHECK_PARAM_RET(action->has_account, "Required field missing", false); @@ -729,14 +727,14 @@ bool eos_compileActionDeleteAuth(const EosActionCommon *common, size_t size = 8 + 8; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->permission, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->permission, 8); return true; } -bool eos_compileActionLinkAuth(const EosActionCommon *common, - const EosActionLinkAuth *action) { +bool eos_compileActionLinkAuth(const EosActionCommon* common, + const EosActionLinkAuth* action) { CHECK_COMMON(EOS_LinkAuth); CHECK_PARAM_RET(action->has_account, "Required field missing", false); @@ -773,16 +771,16 @@ bool eos_compileActionLinkAuth(const EosActionCommon *common, size_t size = 8 + 8 + 8 + 8; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->code, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->type, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->requirement, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->code, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->type, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->requirement, 8); return true; } -bool eos_compileActionUnlinkAuth(const EosActionCommon *common, - const EosActionUnlinkAuth *action) { +bool eos_compileActionUnlinkAuth(const EosActionCommon* common, + const EosActionUnlinkAuth* action) { CHECK_COMMON(EOS_UnlinkAuth); CHECK_PARAM_RET(action->has_account, "Required field missing", false); @@ -813,15 +811,15 @@ bool eos_compileActionUnlinkAuth(const EosActionCommon *common, size_t size = 8 + 8 + 8; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->code, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->type, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->code, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->type, 8); return true; } -bool eos_compileActionNewAccount(const EosActionCommon *common, - const EosActionNewAccount *action) { +bool eos_compileActionNewAccount(const EosActionCommon* common, + const EosActionNewAccount* action) { CHECK_COMMON(EOS_NewAccount); CHECK_PARAM_RET(action->has_creator, "Required field missing", false); @@ -857,8 +855,8 @@ bool eos_compileActionNewAccount(const EosActionCommon *common, size_t size = 8 + 8 + owner_size + active_size; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->creator, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->name, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->creator, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->name, 8); char title[SMALL_STR_BUF]; snprintf(title, sizeof(title), "%s@owner", name); diff --git a/lib/firmware/eos-contracts/eosio.token.c b/lib/firmware/eos-contracts/eosio.token.c index 3cd1ec2d9..1fb8bf3c6 100644 --- a/lib/firmware/eos-contracts/eosio.token.c +++ b/lib/firmware/eos-contracts/eosio.token.c @@ -41,8 +41,8 @@ CHECK_PARAM_RET(common->name == (ACTION), "Incorrect action name", false); \ } while (0) -bool eos_compileActionTransfer(const EosActionCommon *common, - const EosActionTransfer *action) { +bool eos_compileActionTransfer(const EosActionCommon* common, + const EosActionTransfer* action) { CHECK_COMMON(EOS_Transfer); CHECK_PARAM_RET(action->has_quantity, "Required field missing", false); @@ -84,7 +84,7 @@ bool eos_compileActionTransfer(const EosActionCommon *common, snprintf(title, sizeof(title), "Confirm Memo (%" PRIu32 " bytes)", (uint32_t)memo_len); if (!confirm_data(ButtonRequestType_ButtonRequest_ConfirmMemo, title, - (const uint8_t *)action->memo, memo_len)) { + (const uint8_t*)action->memo, memo_len)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Action Cancelled"); eos_signingAbort(); layoutHome(); @@ -97,14 +97,14 @@ bool eos_compileActionTransfer(const EosActionCommon *common, uint32_t size = 8 + 8 + 16 + eos_hashUInt(NULL, memo_len) + memo_len; eos_hashUInt(&hasher_preimage, size); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->sender, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&action->receiver, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->sender, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&action->receiver, 8); CHECK_PARAM_RET(eos_compileAsset(&action->quantity), "Cannot compile asset: quantity", false); eos_hashUInt(&hasher_preimage, memo_len); - hasher_Update(&hasher_preimage, (const uint8_t *)action->memo, memo_len); + hasher_Update(&hasher_preimage, (const uint8_t*)action->memo, memo_len); return true; } diff --git a/lib/firmware/eos.c b/lib/firmware/eos.c index f3c0cce80..45e7acacc 100644 --- a/lib/firmware/eos.c +++ b/lib/firmware/eos.c @@ -51,9 +51,9 @@ static uint32_t actions_remaining = 0; static uint32_t unknown_total = 0; static uint32_t unknown_remaining = 0; -bool eos_formatAsset(const EosAsset *asset, char str[EOS_ASSET_STR_SIZE]) { +bool eos_formatAsset(const EosAsset* asset, char str[EOS_ASSET_STR_SIZE]) { memset(str, 0, EOS_ASSET_STR_SIZE); - char *s = str; + char* s = str; uint64_t v = (uint64_t)asset->amount; // Sign @@ -197,7 +197,7 @@ bool eos_formatAsset(const EosAsset *asset, char str[EOS_ASSET_STR_SIZE]) { /// Ported from EOSIO libraries/chain/name.cpp bool eos_formatName(uint64_t name, char str[EOS_NAME_STR_SIZE]) { memset(str, '.', EOS_NAME_STR_SIZE); - static const char *charmap = ".12345abcdefghijklmnopqrstuvwxyz"; + static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz"; uint64_t tmp = name; for (uint32_t i = 0; i <= 12; ++i) { @@ -214,8 +214,8 @@ bool eos_formatName(uint64_t name, char str[EOS_NAME_STR_SIZE]) { return true; } -bool eos_derivePublicKey(const uint32_t *addr_n, size_t addr_n_count, - uint8_t *public_key, size_t len) { +bool eos_derivePublicKey(const uint32_t* addr_n, size_t addr_n_count, + uint8_t* public_key, size_t len) { if (len < sizeof(node.public_key)) return false; if (!eos_signingIsInited()) return false; @@ -233,15 +233,15 @@ bool eos_derivePublicKey(const uint32_t *addr_n, size_t addr_n_count, return true; } -bool eos_getPublicKey(const HDNode *n, const curve_info *curve, - EosPublicKeyKind kind, char *pubkey, size_t len) { +bool eos_getPublicKey(const HDNode* n, const curve_info* curve, + EosPublicKeyKind kind, char* pubkey, size_t len) { (void)curve; return eos_publicKeyToWif(n->public_key, kind, pubkey, len); } -bool eos_publicKeyToWif(const uint8_t *public_key, EosPublicKeyKind kind, - char *pubkey, size_t len) { - const char *prefix = ""; +bool eos_publicKeyToWif(const uint8_t* public_key, EosPublicKeyKind kind, + char* pubkey, size_t len) { + const char* prefix = ""; switch (kind) { case EosPublicKeyKind_EOS: prefix = "EOS"; @@ -267,7 +267,7 @@ bool eos_publicKeyToWif(const uint8_t *public_key, EosPublicKeyKind kind, } // https://github.com/EOSIO/fc/blob/30eb81c1d995f9cd9834701e03b83ec7e6468a0f/include/fc/io/raw.hpp#L214 -size_t eos_hashUInt(Hasher *hasher, uint64_t val) { +size_t eos_hashUInt(Hasher* hasher, uint64_t val) { size_t count = 0; do { uint8_t b = ((uint8_t)val) & 0x7f; @@ -279,8 +279,8 @@ size_t eos_hashUInt(Hasher *hasher, uint64_t val) { return count; } -void eos_signingInit(const uint8_t *chain_id, uint32_t num_actions, - const EosTxHeader *_header, const HDNode *_root, +void eos_signingInit(const uint8_t* chain_id, uint32_t num_actions, + const EosTxHeader* _header, const HDNode* _root, const uint32_t _address_n[8], size_t _address_n_count) { hasher_Init(&hasher_preimage, HASHER_SHA2); @@ -291,11 +291,11 @@ void eos_signingInit(const uint8_t *chain_id, uint32_t num_actions, address_n_count = _address_n_count; hasher_Update(&hasher_preimage, chain_id, 32); - hasher_Update(&hasher_preimage, (const uint8_t *)&header.expiration, 4); - hasher_Update(&hasher_preimage, (const uint8_t *)&header.ref_block_num, 2); - hasher_Update(&hasher_preimage, (const uint8_t *)&header.ref_block_prefix, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&header.expiration, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&header.ref_block_num, 2); + hasher_Update(&hasher_preimage, (const uint8_t*)&header.ref_block_prefix, 4); eos_hashUInt(&hasher_preimage, header.max_net_usage_words); - hasher_Update(&hasher_preimage, (const uint8_t *)&header.max_cpu_usage_ms, 1); + hasher_Update(&hasher_preimage, (const uint8_t*)&header.max_cpu_usage_ms, 1); eos_hashUInt(&hasher_preimage, header.delay_sec); // context_free_actions. count, followed by each action @@ -334,25 +334,25 @@ void eos_signingAbort(void) { unknown_total = 0; } -bool eos_compileAsset(const EosAsset *asset) { +bool eos_compileAsset(const EosAsset* asset) { if (!asset->has_amount) return false; if (!asset->has_symbol) return false; - hasher_Update(&hasher_preimage, (const uint8_t *)&asset->amount, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&asset->symbol, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&asset->amount, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&asset->symbol, 8); return true; } -bool eos_compileString(const char *str) { +bool eos_compileString(const char* str) { if (!str) return false; uint32_t len = strlen(str); eos_hashUInt(&hasher_preimage, len); - if (len) hasher_Update(&hasher_preimage, (const uint8_t *)str, len); + if (len) hasher_Update(&hasher_preimage, (const uint8_t*)str, len); return true; } -bool eos_compileActionCommon(const EosActionCommon *common) { +bool eos_compileActionCommon(const EosActionCommon* common) { if (!(actions_remaining--)) return false; if (!common->has_account) return false; @@ -361,8 +361,8 @@ bool eos_compileActionCommon(const EosActionCommon *common) { if (!common->authorization_count) return false; - hasher_Update(&hasher_preimage, (const uint8_t *)&common->account, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&common->name, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&common->account, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&common->name, 8); eos_hashUInt(&hasher_preimage, common->authorization_count); for (size_t i = 0; i < common->authorization_count; i++) { @@ -372,20 +372,20 @@ bool eos_compileActionCommon(const EosActionCommon *common) { return true; } -bool eos_compilePermissionLevel(const EosPermissionLevel *auth) { +bool eos_compilePermissionLevel(const EosPermissionLevel* auth) { if (!auth->has_actor) return false; if (!auth->has_permission) return false; - hasher_Update(&hasher_preimage, (const uint8_t *)&auth->actor, 8); - hasher_Update(&hasher_preimage, (const uint8_t *)&auth->permission, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&auth->actor, 8); + hasher_Update(&hasher_preimage, (const uint8_t*)&auth->permission, 8); return true; } bool eos_hasActionUnknownDataRemaining(void) { return 0 < unknown_remaining; } -static bool isSupportedAction(const EosActionCommon *common) { +static bool isSupportedAction(const EosActionCommon* common) { if (common->account == EOS_eosio || common->account == EOS_eosio_token) { switch (common->name) { case EOS_Transfer: @@ -408,8 +408,8 @@ static bool isSupportedAction(const EosActionCommon *common) { return false; } -bool eos_compileActionUnknown(const EosActionCommon *common, - const EosActionUnknown *action) { +bool eos_compileActionUnknown(const EosActionCommon* common, + const EosActionUnknown* action) { if (isSupportedAction(common)) { fsm_sendFailure( FailureType_Failure_SyntaxError, @@ -448,9 +448,9 @@ bool eos_compileActionUnknown(const EosActionCommon *common, return false; } - hasher_Update(&hasher_unknown, (const uint8_t *)action->data_chunk.bytes, + hasher_Update(&hasher_unknown, (const uint8_t*)action->data_chunk.bytes, action->data_chunk.size); - hasher_Update(&hasher_preimage, (const uint8_t *)action->data_chunk.bytes, + hasher_Update(&hasher_preimage, (const uint8_t*)action->data_chunk.bytes, action->data_chunk.size); unknown_remaining -= action->data_chunk.size; @@ -493,7 +493,7 @@ static int eos_is_canonic(uint8_t v, uint8_t signature[64]) { !(signature[32] == 0 && !(signature[33] & 0x80)); } -bool eos_signTx(EosSignedTx *tx) { +bool eos_signTx(EosSignedTx* tx) { memzero(tx, sizeof(*tx)); if (!eos_signingIsInited()) { diff --git a/lib/firmware/ethereum.c b/lib/firmware/ethereum.c index 2bdfc2426..130edffb8 100644 --- a/lib/firmware/ethereum.c +++ b/lib/firmware/ethereum.c @@ -60,10 +60,11 @@ static EthereumTxRequest msg_tx_request; static CONFIDENTIAL uint8_t privkey[32]; static uint32_t chain_id; static uint32_t wanchain_tx_type; // Wanchain only -static uint32_t ethereum_tx_type; // Ethereum tx type (0=Legacy, 1=EIP-2930, 2=EIP-1559) +static uint32_t + ethereum_tx_type; // Ethereum tx type (0=Legacy, 1=EIP-2930, 2=EIP-1559) struct SHA3_CTX keccak_ctx; -bool ethereum_isStandardERC20Transfer(const EthereumSignTx *msg) { +bool ethereum_isStandardERC20Transfer(const EthereumSignTx* msg) { if (msg->has_to && msg->to.size == 20 && msg->value.size == 0 && msg->data_initial_chunk.size == 68 && memcmp(msg->data_initial_chunk.bytes, @@ -74,7 +75,7 @@ bool ethereum_isStandardERC20Transfer(const EthereumSignTx *msg) { return false; } -bool ethereum_isStandardERC20Approve(const EthereumSignTx *msg) { +bool ethereum_isStandardERC20Approve(const EthereumSignTx* msg) { if (msg->has_to && msg->to.size == 20 && msg->value.size == 0 && msg->data_initial_chunk.size == 68 && memcmp(msg->data_initial_chunk.bytes, @@ -85,23 +86,23 @@ bool ethereum_isStandardERC20Approve(const EthereumSignTx *msg) { return false; } -bool ethereum_getStandardERC20Recipient(const EthereumSignTx *msg, - char *address, size_t len) { +bool ethereum_getStandardERC20Recipient(const EthereumSignTx* msg, + char* address, size_t len) { if (len < 2 * 20 + 1) return false; data2hex(msg->data_initial_chunk.bytes + 16, 20, address); return true; } -bool ethereum_getStandardERC20Coin(const EthereumSignTx *msg, CoinType *coin) { - const CoinType *found = +bool ethereum_getStandardERC20Coin(const EthereumSignTx* msg, CoinType* coin) { + const CoinType* found = coinByChainAddress(msg->has_chain_id ? msg->chain_id : 1, msg->to.bytes); if (found) { memcpy(coin, found, sizeof(*coin)); return true; } - const TokenType *token = + const TokenType* token = tokenByChainAddress(msg->has_chain_id ? msg->chain_id : 1, msg->to.bytes); if (token == UnknownToken) return false; @@ -109,7 +110,7 @@ bool ethereum_getStandardERC20Coin(const EthereumSignTx *msg, CoinType *coin) { return true; } -void bn_from_bytes(const uint8_t *value, size_t value_len, bignum256 *val) { +void bn_from_bytes(const uint8_t* value, size_t value_len, bignum256* val) { uint8_t pad_val[32]; memset(pad_val, 0, sizeof(pad_val)); memcpy(pad_val + (32 - value_len), value, value_len); @@ -117,7 +118,7 @@ void bn_from_bytes(const uint8_t *value, size_t value_len, bignum256 *val) { memzero(pad_val, sizeof(pad_val)); } -static inline void hash_data(const uint8_t *buf, size_t size) { +static inline void hash_data(const uint8_t* buf, size_t size) { sha3_Update(&keccak_ctx, buf, size); } @@ -178,7 +179,7 @@ static void hash_rlp_list_length(uint32_t length) { /* * Push an RLP encoded length field and data to the hash buffer. */ -static void hash_rlp_field(const uint8_t *buf, size_t size) { +static void hash_rlp_field(const uint8_t* buf, size_t size) { hash_rlp_length(size, buf[0]); hash_data(buf, size); } @@ -195,7 +196,7 @@ static void hash_rlp_number(uint32_t number) { data[0] = (number >> 24) & 0xff; data[1] = (number >> 16) & 0xff; data[2] = (number >> 8) & 0xff; - data[3] = (number)&0xff; + data[3] = (number) & 0xff; int offset = 0; while (!data[offset]) { offset++; @@ -310,11 +311,11 @@ static void send_signature(void) { * using standard ethereum units. * The buffer must be at least 25 bytes. */ -void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, - uint32_t cid, char *buf, int buflen) { +void ethereumFormatAmount(const bignum256* amnt, const TokenType* token, + uint32_t cid, char* buf, int buflen) { bignum256 bn1e9; bn_read_uint32(1000000000, &bn1e9); - const char *suffix = NULL; + const char* suffix = NULL; int decimals = 18; if (token == UnknownToken) { strlcpy(buf, "Unknown token value", buflen); @@ -355,7 +356,7 @@ void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, case 40: suffix = " TLOS"; break; // Telos - case 56: + case 56: suffix = " BNB"; break; // BNB Chain case 61: @@ -376,9 +377,9 @@ void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, bn_format(amnt, NULL, suffix, decimals, 0, false, buf, buflen); } -static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, - const uint8_t *value, uint32_t value_len, - const TokenType *token, char *out_str, +static void layoutEthereumConfirmTx(const uint8_t* to, uint32_t to_len, + const uint8_t* value, uint32_t value_len, + const TokenType* token, char* out_str, size_t out_str_len, bool approve) { bignum256 val; uint8_t pad_val[32]; @@ -409,7 +410,7 @@ static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, memcmp(value + 16, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0 && memcmp(value + 24, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0; - const char *address = addr; + const char* address = addr; if (to_len && makerdao_isOasisDEXAddress(to, chain_id)) { address = "OasisDEX"; } @@ -436,8 +437,8 @@ static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, } } -static void layoutEthereumData(const uint8_t *data, uint32_t len, - uint32_t total_len, char *out_str, +static void layoutEthereumData(const uint8_t* data, uint32_t len, + uint32_t total_len, char* out_str, size_t out_str_len) { char hexdata[3][17]; char summary[20]; @@ -453,13 +454,13 @@ static void layoutEthereumData(const uint8_t *data, uint32_t len, } strcpy(summary, "... bytes"); - char *p = summary + 11; + char* p = summary + 11; uint32_t number = total_len; while (number > 0) { *p-- = '0' + number % 10; number = number / 10; } - char *summarystart = summary; + const char* summarystart = summary; if (total_len == printed) summarystart = summary + 4; if ((uint32_t)snprintf(out_str, out_str_len, "%s%s\n%s%s", hexdata[0], @@ -469,7 +470,7 @@ static void layoutEthereumData(const uint8_t *data, uint32_t len, } } -static void formatEthereumFee(bignum256 *fee, const uint8_t *gas_price, +static void formatEthereumFee(bignum256* fee, const uint8_t* gas_price, const uint8_t gas_price_len) { uint8_t pad_val[32]; @@ -480,8 +481,8 @@ static void formatEthereumFee(bignum256 *fee, const uint8_t *gas_price, } static void formatEthereumFeeEIP1559( - bignum256 *fee, const uint8_t *max_fee_per_gas, - const uint8_t max_fee_per_gas_len, const uint8_t *max_priority_fee_per_gas, + bignum256* fee, const uint8_t* max_fee_per_gas, + const uint8_t max_fee_per_gas_len, const uint8_t* max_priority_fee_per_gas, const uint8_t max_priority_fee_per_gas_len) { bignum256 max_fee, max_pfee; uint8_t pad_val[32]; @@ -504,8 +505,8 @@ static void formatEthereumFeeEIP1559( } } -static void layoutEthereumFee(const EthereumSignTx *msg, bool is_token, - char *out_str, size_t out_str_len) { +static void layoutEthereumFee(const EthereumSignTx* msg, bool is_token, + char* out_str, size_t out_str_len) { bignum256 val, gas; char gas_value[32]; char tx_value[32]; @@ -559,7 +560,7 @@ static void layoutEthereumFee(const EthereumSignTx *msg, bool is_token, * - data (0 ..) */ -static bool ethereum_signing_check(EthereumSignTx *msg) { +static bool ethereum_signing_check(const EthereumSignTx* msg) { if (!msg->has_gas_limit) { return false; } @@ -586,7 +587,7 @@ static bool ethereum_signing_check(EthereumSignTx *msg) { return true; } -void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, +void ethereum_signing_init(EthereumSignTx* msg, const HDNode* node, bool needs_confirm) { char confirm_body_message[121] = {0}; @@ -668,7 +669,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, return; } - const TokenType *token = NULL; + const TokenType* token = NULL; // safety checks if (!ethereum_signing_check(msg)) { @@ -712,7 +713,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, sizeof(confirm_body_message), /*approve=*/false); } bool is_transfer = msg->address_type == OutputAddressType_TRANSFER; - const char *title; + const char* title; ButtonRequestType BRT; if (is_approve) { title = "Approve"; @@ -779,35 +780,40 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, // This is the chain ID length for 1559 tx (only one byte for now) rlp_length += rlp_calculate_number_length(chain_id); - //rlp_length += 1; + // rlp_length += 1; } rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); if (msg->has_max_fee_per_gas) { if (msg->has_max_priority_fee_per_gas) { - rlp_length += rlp_calculate_length(msg->max_priority_fee_per_gas.size, - msg->max_priority_fee_per_gas.bytes[0]); + rlp_length += + rlp_calculate_length(msg->max_priority_fee_per_gas.size, + msg->max_priority_fee_per_gas.bytes[0]); } rlp_length += rlp_calculate_length(msg->max_fee_per_gas.size, - msg->max_fee_per_gas.bytes[0]); + msg->max_fee_per_gas.bytes[0]); } else { - rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); + rlp_length += + rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); } - rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); + rlp_length += + rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); rlp_length += rlp_calculate_length(msg->to.size, msg->to.bytes[0]); rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); - rlp_length += rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); - + rlp_length += + rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); + if (ethereum_tx_type == ETHEREUM_TX_TYPE_EIP_1559) { // access list size - rlp_length += 1; // c0, keepkey does not support >0 length access list at this time + rlp_length += + 1; // c0, keepkey does not support >0 length access list at this time } if (wanchain_tx_type) { rlp_length += rlp_calculate_number_length(wanchain_tx_type); } - + if (ethereum_tx_type == ETHEREUM_TX_TYPE_LEGACY) { // legacy EIP-155 replay protection if (chain_id) { @@ -818,8 +824,9 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, } // Start the hash: - // keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, - // gas_limit, destination, amount, data, access_list])) + // keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, + // max_fee_per_gas, + // gas_limit, destination, amount, data, access_list])) // tx type should never be greater than one byte in length // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2718.md#transactiontype-only-goes-up-to-0x7f @@ -838,11 +845,11 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, if (ethereum_tx_type == ETHEREUM_TX_TYPE_EIP_1559) { // chain id goes here for 1559 (only one byte for now) - hash_rlp_field((uint8_t *)(&chain_id), sizeof(uint8_t)); + hash_rlp_field((uint8_t*)(&chain_id), sizeof(uint8_t)); } hash_rlp_field(msg->nonce.bytes, msg->nonce.size); - + if (msg->has_max_fee_per_gas) { if (msg->has_max_priority_fee_per_gas) { hash_rlp_field(msg->max_priority_fee_per_gas.bytes, @@ -852,7 +859,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, } else { hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); } - + hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); hash_rlp_field(msg->to.bytes, msg->to.size); hash_rlp_field(msg->value.bytes, msg->value.size); @@ -862,7 +869,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, if (ethereum_tx_type == ETHEREUM_TX_TYPE_EIP_1559) { // Keepkey does not support an access list size >0 at this time - uint8_t datbuf[1] = {0xC0}; // size of empty access list + uint8_t datbuf[1] = {0xC0}; // size of empty access list hash_data(datbuf, sizeof(datbuf)); } @@ -875,7 +882,7 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node, } } -void ethereum_signing_txack(EthereumTxAck *tx) { +void ethereum_signing_txack(EthereumTxAck* tx) { if (!ethereum_signing) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Ethereum signing mode")); @@ -914,7 +921,7 @@ void ethereum_signing_abort(void) { } } -static void ethereum_message_hash(const uint8_t *message, size_t message_len, +static void ethereum_message_hash(const uint8_t* message, size_t message_len, uint8_t hash[32]) { struct SHA3_CTX ctx; uint8_t c; @@ -963,8 +970,8 @@ static void ethereum_message_hash(const uint8_t *message, size_t message_len, keccak_Final(&ctx, hash); } -void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, - EthereumMessageSignature *resp) { +void ethereum_message_sign(const EthereumSignMessage* msg, const HDNode* node, + EthereumMessageSignature* resp) { uint8_t hash[32]; if (!hdnode_get_ethereum_pubkeyhash(node, resp->address.bytes)) { @@ -972,7 +979,7 @@ void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, } resp->has_address = true; resp->address.size = 20; - + ethereum_message_hash(msg->message.bytes, msg->message.size, hash); uint8_t v; @@ -988,7 +995,7 @@ void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, msg_write(MessageType_MessageType_EthereumMessageSignature, resp); } -int ethereum_message_verify(const EthereumVerifyMessage *msg) { +int ethereum_message_verify(const EthereumVerifyMessage* msg) { if (msg->signature.size != 65 || msg->address.size != 20) { fsm_sendFailure(FailureType_Failure_SyntaxError, _("Malformed data")); return 1; @@ -1023,7 +1030,6 @@ int ethereum_message_verify(const EthereumVerifyMessage *msg) { return 0; } - // /* * EIP-712 hashes might have no message_hash if primaryType="EIP712Domain". @@ -1034,7 +1040,7 @@ static void ethereum_typed_hash(const uint8_t domain_separator_hash[32], bool has_message_hash, uint8_t hash[32]) { struct SHA3_CTX ctx = {0}; sha3_256_Init(&ctx); - sha3_Update(&ctx, (const uint8_t *)"\x19\x01", 2); + sha3_Update(&ctx, (const uint8_t*)"\x19\x01", 2); sha3_Update(&ctx, domain_separator_hash, 32); if (has_message_hash) { sha3_Update(&ctx, message_hash, 32); @@ -1042,24 +1048,26 @@ static void ethereum_typed_hash(const uint8_t domain_separator_hash[32], keccak_Final(&ctx, hash); } -static int eip712_sign(const uint8_t *ds_hash, const uint8_t *msg_hash, bool has_msg_hash, - const HDNode *node, uint8_t *v, uint8_t *sig) { - +static int eip712_sign(const uint8_t* ds_hash, const uint8_t* msg_hash, + bool has_msg_hash, const HDNode* node, uint8_t* v, + uint8_t* sig) { uint8_t hash[32] = {0}; ethereum_typed_hash(ds_hash, msg_hash, has_msg_hash, hash); - return ecdsa_sign_digest(&secp256k1, node->private_key, hash, sig, v, ethereum_is_canonic); + return ecdsa_sign_digest(&secp256k1, node->private_key, hash, sig, v, + ethereum_is_canonic); } -void ethereum_typed_hash_sign(const EthereumSignTypedHash *msg, - const HDNode *node, - EthereumTypedDataSignature *resp) { - +void ethereum_typed_hash_sign(const EthereumSignTypedHash* msg, + const HDNode* node, + EthereumTypedDataSignature* resp) { uint8_t v = 0; - if (0 != eip712_sign(msg->domain_separator_hash.bytes, msg->message_hash.bytes, - msg->has_message_hash, node, &v, resp->signature.bytes)) { - fsm_sendFailure(FailureType_Failure_Other, _("EIP-712 hash signing failed")); + if (0 != eip712_sign(msg->domain_separator_hash.bytes, + msg->message_hash.bytes, msg->has_message_hash, node, &v, + resp->signature.bytes)) { + fsm_sendFailure(FailureType_Failure_Other, + _("EIP-712 hash signing failed")); return; } @@ -1070,52 +1078,52 @@ void ethereum_typed_hash_sign(const EthereumSignTypedHash *msg, void failMessage(int err); -const char *failMsgReturn[LAST_ERROR - 2] = { - "EIP-712 general error", // 3 - "EIP-712 user defined type name too long", - "EIP-712 too many user defined types", - "EIP-712 user defined type array name error", - "EIP-712 address string overflow", - "EIP-712 bytesN string overflow", - "EIP-712 bytesN size error", - "EIP-712 INT and UINT array parsing not implemented", - "EIP-712 bytesN array parsing not implemented", - "EIP-712 boolean array parsing not implemented", - "EIP-712 not enough memory to parse message", // 13 - "EIP-712 primaryType name error", - "EIP-712 primaryType value error", - "EIP-712 types property error", - "EIP-712 typeS (typestring) property error", - "EIP-712 domain property name error", - "EIP-712 domain seperator hash must be calculated first", - "EIP-712 message property name error", - "EIP-712 primary type object error", - "EIP-712 typeS not found in eip712types", - "EIP-712 typeS name missing", // 23 - "EIP-712 unused error 24", - "EIP-712 pairs are NULL", - "EIP-712 json pair type is not JSON_TEXT", - "EIP-712 pair does not have a sibling", - "EIP-712 typeType not encodable, possibly NULL", - "EIP-712 pair value is NULL", - "EIP-712 pair name is NULL", - "EIP-712 typeType has no name in parseVals", - "EIP-712 address string is NULL", - "EIP-712 no value for type during walkVals", // 33 - }; +const char* failMsgReturn[LAST_ERROR - 2] = { + "EIP-712 general error", // 3 + "EIP-712 user defined type name too long", + "EIP-712 too many user defined types", + "EIP-712 user defined type array name error", + "EIP-712 address string overflow", + "EIP-712 bytesN string overflow", + "EIP-712 bytesN size error", + "EIP-712 INT and UINT array parsing not implemented", + "EIP-712 bytesN array parsing not implemented", + "EIP-712 boolean array parsing not implemented", + "EIP-712 not enough memory to parse message", // 13 + "EIP-712 primaryType name error", + "EIP-712 primaryType value error", + "EIP-712 types property error", + "EIP-712 typeS (typestring) property error", + "EIP-712 domain property name error", + "EIP-712 domain seperator hash must be calculated first", + "EIP-712 message property name error", + "EIP-712 primary type object error", + "EIP-712 typeS not found in eip712types", + "EIP-712 typeS name missing", // 23 + "EIP-712 unused error 24", + "EIP-712 pairs are NULL", + "EIP-712 json pair type is not JSON_TEXT", + "EIP-712 pair does not have a sibling", + "EIP-712 typeType not encodable, possibly NULL", + "EIP-712 pair value is NULL", + "EIP-712 pair name is NULL", + "EIP-712 typeType has no name in parseVals", + "EIP-712 address string is NULL", + "EIP-712 no value for type during walkVals", // 33 +}; void failMessage(int err) { if (err < GENERAL_ERROR || err > LAST_ERROR) { // unknown error number fsm_sendFailure(FailureType_Failure_Other, _("EIP-712 unknown failure")); } else { - fsm_sendFailure(FailureType_Failure_Other, _(failMsgReturn[err-3])); + fsm_sendFailure(FailureType_Failure_Other, _(failMsgReturn[err - 3])); } return; } - -void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature *resp, const HDNode *node) { +void e712_types_values(Ethereum712TypesValues* msg, + EthereumTypedDataSignature* resp, const HDNode* node) { int errRet = SUCCESS; json_t memTypes[JSON_OBJ_POOL_SIZE] = {0}; json_t memVals[JSON_OBJ_POOL_SIZE] = {0}; @@ -1123,29 +1131,33 @@ void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature * json_t const* jsonT; json_t const* jsonV; json_t const* jsonPT; - char *typesJsonStr; - char *primaryTypeJsonStr; - char *valuesJsonStr; - const char *primeType; + char* typesJsonStr; + char* primaryTypeJsonStr; + char* valuesJsonStr; json_t const* obTest; - static uint8_t domainSeparatorHash[32]={0}; - static uint8_t messageHash[32]={0}; - static bool have_ds=false; + static uint8_t domainSeparatorHash[32] = {0}; + // cppcheck-suppress variableScope + static uint8_t messageHash[32] = {0}; + static bool have_ds = false; typesJsonStr = msg->eip712types; primaryTypeJsonStr = msg->eip712primetype; valuesJsonStr = msg->eip712data; - jsonT = json_create(typesJsonStr, memTypes, sizeof memTypes / sizeof *memTypes ); - jsonPT = json_create(primaryTypeJsonStr, memPType, sizeof memPType / sizeof *memPType ); - jsonV = json_create(valuesJsonStr, memVals, sizeof memVals / sizeof *memVals ); + jsonT = + json_create(typesJsonStr, memTypes, sizeof memTypes / sizeof *memTypes); + jsonPT = json_create(primaryTypeJsonStr, memPType, + sizeof memPType / sizeof *memPType); + jsonV = json_create(valuesJsonStr, memVals, sizeof memVals / sizeof *memVals); if (!jsonT) { - fsm_sendFailure(FailureType_Failure_Other, _("EIP-712 type property data error")); + fsm_sendFailure(FailureType_Failure_Other, + _("EIP-712 type property data error")); return; - } + } if (!jsonPT) { - fsm_sendFailure(FailureType_Failure_Other, _("EIP-712 primaryType property data error")); + fsm_sendFailure(FailureType_Failure_Other, + _("EIP-712 primaryType property data error")); return; } if (!jsonV) { @@ -1157,9 +1169,8 @@ void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature * // Compute domain seperator hash have_ds = false; memzero(domainSeparatorHash, 32); - if ((int)SUCCESS != (errRet = - encode(jsonT, jsonV, "EIP712Domain", resp->domain_separator_hash.bytes) - )) { + if ((int)SUCCESS != (errRet = encode(jsonT, jsonV, "EIP712Domain", + resp->domain_separator_hash.bytes))) { failMessage(errRet); return; } @@ -1178,11 +1189,14 @@ void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature * failMessage(JSON_PTYPENAMEERR); return; } + const char* primeType; if (0 == (primeType = json_getValue(obTest))) { failMessage(JSON_PTYPEVALERR); return; } - if (0 != strncmp(primeType, "EIP712Domain", strlen(primeType))) { // if primaryType is "EIP712Domain", message hash is NULL + if (0 != strncmp(primeType, "EIP712Domain", + strlen(primeType))) { // if primaryType is "EIP712Domain", + // message hash is NULL errRet = encode(jsonT, jsonV, primeType, resp->message_hash.bytes); if (!(SUCCESS == errRet || NULL_MSG_HASH == errRet)) { failMessage(errRet); @@ -1191,7 +1205,7 @@ void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature * } else { errRet = NULL_MSG_HASH; } - + if (NULL_MSG_HASH == errRet) { resp->has_message_hash = false; resp->has_msg_hash = false; @@ -1206,8 +1220,10 @@ void e712_types_values(Ethereum712TypesValues *msg, EthereumTypedDataSignature * resp->domain_separator_hash.size = 32; uint8_t v = 0; - if (0 != eip712_sign(domainSeparatorHash, messageHash, resp->has_msg_hash, node, &v, resp->signature.bytes)) { - fsm_sendFailure(FailureType_Failure_Other, _("EIP-712 typed hash signing failed")); + if (0 != eip712_sign(domainSeparatorHash, messageHash, resp->has_msg_hash, + node, &v, resp->signature.bytes)) { + fsm_sendFailure(FailureType_Failure_Other, + _("EIP-712 typed hash signing failed")); return; } diff --git a/lib/firmware/ethereum.h b/lib/firmware/ethereum.h index 0cd7a8b31..4f013839c 100644 --- a/lib/firmware/ethereum.h +++ b/lib/firmware/ethereum.h @@ -25,14 +25,13 @@ #include "bip32.h" #include "ethereum-messages.pb.h" -void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node); +void ethereum_signing_init(EthereumSignTx* msg, const HDNode* node); void ethereum_signing_abort(void); -void ethereum_signing_txack(EthereumTxAck *msg); +void ethereum_signing_txack(EthereumTxAck* tx); -void ethereum_message_sign(EthereumSignMessage *msg, const HDNode *node, - EthereumMessageSignature *resp); -int ethereum_message_verify(EthereumVerifyMessage *msg); -bool ethereum_isThorchainTx(const EthereumSignTx *msg); -uint8_t ethereum_extractThorchainData(const EthereumSignTx *msg, - char *buffer); +void ethereum_message_sign(EthereumSignMessage* msg, const HDNode* node, + EthereumMessageSignature* resp); +int ethereum_message_verify(EthereumVerifyMessage* msg); +bool ethereum_isThorchainTx(const EthereumSignTx* msg); +uint8_t ethereum_extractThorchainData(const EthereumSignTx* msg, char* buffer); #endif diff --git a/lib/firmware/ethereum_contracts.c b/lib/firmware/ethereum_contracts.c index 69fc6e694..87fb61488 100644 --- a/lib/firmware/ethereum_contracts.c +++ b/lib/firmware/ethereum_contracts.c @@ -28,8 +28,8 @@ #include "keepkey/firmware/ethereum_contracts/zxswap.h" #include "keepkey/firmware/ethereum_contracts/makerdao.h" -bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg, - const HDNode *node) { +bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx* msg, + const HDNode* node) { (void)node; if (sa_isWithdrawFromSalary(msg)) return true; @@ -45,8 +45,8 @@ bool ethereum_contractHandled(uint32_t data_total, const EthereumSignTx *msg, return false; } -bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx *msg, - const HDNode *node) { +bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx* msg, + const HDNode* node) { (void)node; if (sa_isWithdrawFromSalary(msg)) @@ -55,18 +55,15 @@ bool ethereum_contractConfirmed(uint32_t data_total, const EthereumSignTx *msg, if (zx_isZxTransformERC20(msg)) return zx_confirmZxTransERC20(data_total, msg); - if (zx_isZxSwap(msg)) - return zx_confirmZxSwap(data_total, msg); + if (zx_isZxSwap(msg)) return zx_confirmZxSwap(data_total, msg); - if (zx_isZxLiquidTx(msg)) - return zx_confirmZxLiquidTx(data_total, msg); + if (zx_isZxLiquidTx(msg)) return zx_confirmZxLiquidTx(data_total, msg); if (zx_isZxApproveLiquid(msg)) return zx_confirmApproveLiquidity(data_total, msg); - if (thor_isThorchainTx(msg)) - return thor_confirmThorTx(data_total, msg); - + if (thor_isThorchainTx(msg)) return thor_confirmThorTx(data_total, msg); + if (makerdao_isMakerDAO(data_total, msg)) return makerdao_confirmMakerDAO(data_total, msg); diff --git a/lib/firmware/ethereum_contracts/makerdao.c b/lib/firmware/ethereum_contracts/makerdao.c index fa9890be6..e53733896 100644 --- a/lib/firmware/ethereum_contracts/makerdao.c +++ b/lib/firmware/ethereum_contracts/makerdao.c @@ -26,7 +26,7 @@ #include "keepkey/firmware/fsm.h" #include "trezor/crypto/address.h" -static bool getCupId(const uint8_t *param, uint32_t *val) { +static bool getCupId(const uint8_t* param, uint32_t* val) { bignum256 value; bn_from_bytes(param, 32, &value); @@ -36,12 +36,12 @@ static bool getCupId(const uint8_t *param, uint32_t *val) { return true; } -static bool confirmParamIsTub(const uint8_t *param, uint32_t chain_id) { +static bool confirmParamIsTub(const uint8_t* param, uint32_t chain_id) { if (memcmp(param, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12) != 0) return false; - const uint8_t *address = param + 12; + const uint8_t* address = param + 12; // Mainnet // saiValuesAggregator: @@ -68,7 +68,7 @@ static bool confirmParamIsTub(const uint8_t *param, uint32_t chain_id) { "Confirm TUB:\n%s", contract); } -bool makerdao_isOasisDEXAddress(const uint8_t *address, uint32_t chain_id) { +bool makerdao_isOasisDEXAddress(const uint8_t* address, uint32_t chain_id) { // Mainnet // https://github.com/makerdao/scd-cdp-portal/blob/fac7b0571dc4128e89dcd5f7f8d44ded4b66073b/src/settings.json#L17 if (chain_id == 1 && memcmp(address, @@ -87,13 +87,13 @@ bool makerdao_isOasisDEXAddress(const uint8_t *address, uint32_t chain_id) { return false; } -static bool confirmParamIsOTCProvider(const uint8_t *param, uint32_t chain_id, - const char **otcProvider) { +static bool confirmParamIsOTCProvider(const uint8_t* param, uint32_t chain_id, + const char** otcProvider) { if (memcmp(param, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12) != 0) return false; - const uint8_t *address = param + 12; + const uint8_t* address = param + 12; if (makerdao_isOasisDEXAddress(address, chain_id)) { if (otcProvider) *otcProvider = " via OasisDEX"; @@ -107,13 +107,13 @@ static bool confirmParamIsOTCProvider(const uint8_t *param, uint32_t chain_id, "Confirm OTC:\n%s", contract); } -static bool confirmParamIsRegistryAddress(const uint8_t *param, +static bool confirmParamIsRegistryAddress(const uint8_t* param, uint32_t chain_id) { if (memcmp(param, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12) != 0) return false; - const uint8_t *address = param + 12; + const uint8_t* address = param + 12; // Mainnet // https://github.com/makerdao/scd-cdp-portal/blob/fac7b0571dc4128e89dcd5f7f8d44ded4b66073b/src/settings.json#L21 @@ -137,7 +137,7 @@ static bool confirmParamIsRegistryAddress(const uint8_t *param, "Confirm Proxy Registry:\n%s", contract); } -static bool confirmSaiProxyCreateAndExecuteAddress(const uint8_t *address, +static bool confirmSaiProxyCreateAndExecuteAddress(const uint8_t* address, uint32_t chain_id) { // Mainnet // https://github.com/makerdao/scd-cdp-portal/blob/fac7b0571dc4128e89dcd5f7f8d44ded4b66073b/src/settings.json#L22 @@ -168,7 +168,7 @@ static bool confirmSaiProxyCreateAndExecuteAddress(const uint8_t *address, "Confirm SaiProxyCreateAndExecute:\n%s", contract); } -static bool isProxyCall(const EthereumSignTx *msg) { +static bool isProxyCall(const EthereumSignTx* msg) { if (memcmp(msg->data_initial_chunk.bytes, "\x1c\xff\x79\xcd", 4) != 0) return false; @@ -193,7 +193,7 @@ static bool isProxyCall(const EthereumSignTx *msg) { return true; } -static bool confirmProxyCall(const EthereumSignTx *msg) { +static bool confirmProxyCall(const EthereumSignTx* msg) { if (memcmp(msg->data_initial_chunk.bytes + 4, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12) != 0) return false; @@ -205,19 +205,19 @@ static bool confirmProxyCall(const EthereumSignTx *msg) { return true; } -static inline bool hasParams(const EthereumSignTx *msg, size_t count) { +static inline bool hasParams(const EthereumSignTx* msg, size_t count) { return msg->data_initial_chunk.size == 4 + count * 32; } -static inline bool hasProxiedParams(const EthereumSignTx *msg, size_t count) { +static inline bool hasProxiedParams(const EthereumSignTx* msg, size_t count) { return msg->data_initial_chunk.size == 4 + 3 * 32 + 4 + count * 32 + (32 - 4); } -static inline const uint8_t *getMethod(const EthereumSignTx *msg) { +static inline const uint8_t* getMethod(const EthereumSignTx* msg) { return msg->data_initial_chunk.bytes; } -static inline const uint8_t *getParam(const EthereumSignTx *msg, size_t idx) { +static inline const uint8_t* getParam(const EthereumSignTx* msg, size_t idx) { if (isProxyCall(msg)) { return msg->data_initial_chunk.bytes + 4 + 3 * 32 + 4 + idx * 32; } @@ -225,11 +225,11 @@ static inline const uint8_t *getParam(const EthereumSignTx *msg, size_t idx) { return msg->data_initial_chunk.bytes + 4 + idx * 32; } -static inline const uint8_t *getProxiedMethod(const EthereumSignTx *msg) { +static inline const uint8_t* getProxiedMethod(const EthereumSignTx* msg) { return msg->data_initial_chunk.bytes + 4 + 3 * 32; } -static bool isMethod(const EthereumSignTx *msg, const char *hash, +static bool isMethod(const EthereumSignTx* msg, const char* hash, size_t arg_count) { if (isProxyCall(msg)) { if (!hasProxiedParams(msg, arg_count)) return false; @@ -246,34 +246,34 @@ static bool isMethod(const EthereumSignTx *msg, const char *hash, return true; } -static void getETHValue(const EthereumSignTx *msg, bignum256 *val) { +static void getETHValue(const EthereumSignTx* msg, bignum256* val) { uint8_t pad_val[32]; memset(pad_val, 0, sizeof(pad_val)); memcpy(pad_val + (32 - msg->value.size), msg->value.bytes, msg->value.size); bn_read_be(pad_val, val); } -static bool isETHValueZero(const EthereumSignTx *msg) { +static bool isETHValueZero(const EthereumSignTx* msg) { bignum256 val; getETHValue(msg, &val); return bn_is_zero(&val); } -bool makerdao_isOpen(const EthereumSignTx *msg) { +bool makerdao_isOpen(const EthereumSignTx* msg) { // `open(address)` if (!isMethod(msg, "\xc7\x40\x73\xa1", 1)) return false; return true; } -bool makerdao_confirmOpen(const EthereumSignTx *msg) { +bool makerdao_confirmOpen(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "MakerDAO", "Open CDP?"); } -bool makerdao_isClose(const EthereumSignTx *msg) { +bool makerdao_isClose(const EthereumSignTx* msg) { // `shut(address,bytes32)` // `shut(address,bytes32,address)` if (!isMethod(msg, "\xbc\x24\x4c\x11", 2) && @@ -288,13 +288,13 @@ bool makerdao_isClose(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmClose(const EthereumSignTx *msg) { +bool makerdao_confirmClose(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; if (!getCupId(getParam(msg, 1), &cupId)) return false; - const char *otcProvider = ""; + const char* otcProvider = ""; if (isMethod(msg, "\x79\x20\x37\xe3", 3)) { if (confirmParamIsOTCProvider(getParam(msg, 2), msg->chain_id, &otcProvider)) @@ -305,7 +305,7 @@ bool makerdao_confirmClose(const EthereumSignTx *msg) { "Close CDP %" PRIu32 "%s?", cupId, otcProvider); } -bool makerdao_isGive(const EthereumSignTx *msg) { +bool makerdao_isGive(const EthereumSignTx* msg) { // `give(address,bytes32,address)` if (!isMethod(msg, "\xda\x93\xdf\xcf", 3)) return false; @@ -321,7 +321,7 @@ bool makerdao_isGive(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmGive(const EthereumSignTx *msg) { +bool makerdao_confirmGive(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; @@ -335,14 +335,14 @@ bool makerdao_confirmGive(const EthereumSignTx *msg) { "Move CDP %" PRIu32 " to %s?", cupId, new_owner); } -bool makerdao_isLockAndDraw2(const EthereumSignTx *msg) { +bool makerdao_isLockAndDraw2(const EthereumSignTx* msg) { // `lockAndDraw(address,uint256)` if (!isMethod(msg, "\x51\x6e\x9a\xec", 2)) return false; return true; } -bool makerdao_confirmLockAndDraw2(const EthereumSignTx *msg) { +bool makerdao_confirmLockAndDraw2(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; bignum256 deposit_val; @@ -352,7 +352,7 @@ bool makerdao_confirmLockAndDraw2(const EthereumSignTx *msg) { ethereumFormatAmount(&deposit_val, NULL, msg->chain_id, deposit, sizeof(deposit)); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; bignum256 withdraw_val; @@ -367,14 +367,14 @@ bool makerdao_confirmLockAndDraw2(const EthereumSignTx *msg) { withdraw); } -bool makerdao_isCreateOpenLockAndDraw(const EthereumSignTx *msg) { +bool makerdao_isCreateOpenLockAndDraw(const EthereumSignTx* msg) { // `createOpenLockAndDraw(address,address,uint256)` if (!isMethod(msg, "\xd3\x14\x0a\x65", 3)) return false; return true; } -bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx *msg) { +bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx* msg) { if (!confirmParamIsRegistryAddress(getParam(msg, 0), msg->chain_id)) return false; @@ -387,7 +387,7 @@ bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx *msg) { ethereumFormatAmount(&deposit_val, NULL, msg->chain_id, deposit, sizeof(deposit)); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; bignum256 withdraw_val; @@ -403,7 +403,7 @@ bool makerdao_confirmCreateOpenLockAndDraw(const EthereumSignTx *msg) { withdraw); } -bool makerdao_isLock(const EthereumSignTx *msg) { +bool makerdao_isLock(const EthereumSignTx* msg) { // `lock(address,bytes32)` if (!isMethod(msg, "\xbc\x25\xa8\x10", 2)) return false; @@ -413,7 +413,7 @@ bool makerdao_isLock(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmLock(const EthereumSignTx *msg) { +bool makerdao_confirmLock(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; bignum256 deposit_val; @@ -430,7 +430,7 @@ bool makerdao_confirmLock(const EthereumSignTx *msg) { "Deposit %s into CDP %" PRIu32 "?", deposit, cupId); } -bool makerdao_isDraw(const EthereumSignTx *msg) { +bool makerdao_isDraw(const EthereumSignTx* msg) { // `draw(address,bytes32,uint256)` if (!isMethod(msg, "\x03\x44\xa3\x6f", 3)) return false; @@ -442,7 +442,7 @@ bool makerdao_isDraw(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmDraw(const EthereumSignTx *msg) { +bool makerdao_confirmDraw(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; @@ -451,7 +451,7 @@ bool makerdao_confirmDraw(const EthereumSignTx *msg) { bignum256 withdraw_val; bn_from_bytes(getParam(msg, 2), 32, &withdraw_val); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; char withdraw[32]; @@ -462,7 +462,7 @@ bool makerdao_confirmDraw(const EthereumSignTx *msg) { "Generate %s from CDP %" PRIu32 "?", withdraw, cupId); } -bool makerdao_isLockAndDraw3(const EthereumSignTx *msg) { +bool makerdao_isLockAndDraw3(const EthereumSignTx* msg) { // `lockAndDraw(address,bytes32,uint256)` if (!isMethod(msg, "\x1e\xdf\x0c\x1e", 3)) return false; @@ -472,7 +472,7 @@ bool makerdao_isLockAndDraw3(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmLockAndDraw3(const EthereumSignTx *msg) { +bool makerdao_confirmLockAndDraw3(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; bignum256 deposit_val; @@ -488,7 +488,7 @@ bool makerdao_confirmLockAndDraw3(const EthereumSignTx *msg) { bignum256 withdraw_val; bn_from_bytes(getParam(msg, 2), 32, &withdraw_val); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; char withdraw[32]; @@ -500,7 +500,7 @@ bool makerdao_confirmLockAndDraw3(const EthereumSignTx *msg) { cupId, withdraw); } -bool makerdao_isFree(const EthereumSignTx *msg) { +bool makerdao_isFree(const EthereumSignTx* msg) { // `free(address,bytes32,uint256)` if (!isMethod(msg, "\xf9\xef\x04\xbe", 3)) return false; @@ -512,7 +512,7 @@ bool makerdao_isFree(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmFree(const EthereumSignTx *msg) { +bool makerdao_confirmFree(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; @@ -529,7 +529,7 @@ bool makerdao_confirmFree(const EthereumSignTx *msg) { "Withdraw %s from CDP %" PRIu32 "?", withdraw, cupId); } -bool makerdao_isWipe(const EthereumSignTx *msg) { +bool makerdao_isWipe(const EthereumSignTx* msg) { // `wipe(address,bytes,uint256)` // `wipe(address,bytes,uint256,address)` if (!isMethod(msg, "\xa3\xdc\x65\xa7", 3) && @@ -544,7 +544,7 @@ bool makerdao_isWipe(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmWipe(const EthereumSignTx *msg) { +bool makerdao_confirmWipe(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; @@ -553,14 +553,14 @@ bool makerdao_confirmWipe(const EthereumSignTx *msg) { bignum256 deposit_val; bn_from_bytes(getParam(msg, 2), 32, &deposit_val); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; char deposit[32]; ethereumFormatAmount(&deposit_val, DAI, msg->chain_id, deposit, sizeof(deposit)); - const char *otcProvider = ""; + const char* otcProvider = ""; if (isMethod(msg, "\x8a\x9f\xc4\x75", 4)) { if (confirmParamIsOTCProvider(getParam(msg, 2), msg->chain_id, &otcProvider)) @@ -572,7 +572,7 @@ bool makerdao_confirmWipe(const EthereumSignTx *msg) { otcProvider); } -bool makerdao_isWipeAndFree(const EthereumSignTx *msg) { +bool makerdao_isWipeAndFree(const EthereumSignTx* msg) { // `wipeAndFree(address,bytes32,uint256,uint256)` // `wipeAndFree(address,bytes32,uint256,uint256,address)` if (!isMethod(msg, "\xfa\xed\x77\xab", 4) && @@ -587,7 +587,7 @@ bool makerdao_isWipeAndFree(const EthereumSignTx *msg) { return true; } -bool makerdao_confirmWipeAndFree(const EthereumSignTx *msg) { +bool makerdao_confirmWipeAndFree(const EthereumSignTx* msg) { if (!confirmParamIsTub(getParam(msg, 0), msg->chain_id)) return false; uint32_t cupId; @@ -596,7 +596,7 @@ bool makerdao_confirmWipeAndFree(const EthereumSignTx *msg) { bignum256 deposit_val; bn_from_bytes(getParam(msg, 2), 32, &deposit_val); - const TokenType *DAI; + const TokenType* DAI; if (!tokenByTicker(msg->chain_id, "DAI", &DAI)) return false; char deposit[32]; @@ -610,7 +610,7 @@ bool makerdao_confirmWipeAndFree(const EthereumSignTx *msg) { ethereumFormatAmount(&withdraw_val, NULL, msg->chain_id, withdraw, sizeof(withdraw)); - const char *otcProvider = ""; + const char* otcProvider = ""; if (isMethod(msg, "\x1b\x96\x81\x60", 5)) { if (!confirmParamIsOTCProvider(getParam(msg, 4), msg->chain_id, &otcProvider)) @@ -622,7 +622,7 @@ bool makerdao_confirmWipeAndFree(const EthereumSignTx *msg) { withdraw, cupId, otcProvider); } -bool makerdao_isMakerDAO(uint32_t data_total, const EthereumSignTx *msg) { +bool makerdao_isMakerDAO(uint32_t data_total, const EthereumSignTx* msg) { if (!msg->has_chain_id) return false; if (data_total != msg->data_initial_chunk.size) return false; @@ -652,7 +652,7 @@ bool makerdao_isMakerDAO(uint32_t data_total, const EthereumSignTx *msg) { return false; } -bool makerdao_confirmMakerDAO(uint32_t data_total, const EthereumSignTx *msg) { +bool makerdao_confirmMakerDAO(uint32_t data_total, const EthereumSignTx* msg) { (void)data_total; if (isProxyCall(msg)) { diff --git a/lib/firmware/ethereum_contracts/saproxy.c b/lib/firmware/ethereum_contracts/saproxy.c index 43f395b84..ccab30ba6 100644 --- a/lib/firmware/ethereum_contracts/saproxy.c +++ b/lib/firmware/ethereum_contracts/saproxy.c @@ -26,43 +26,46 @@ #include "keepkey/firmware/fsm.h" #include "trezor/crypto/address.h" -static bool isWithFromSalary(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\xfe\xa7\xc5\x3f", 4) == 0) - return true; +static bool isWithFromSalary(const EthereumSignTx* msg) { + if (memcmp(msg->data_initial_chunk.bytes, "\xfe\xa7\xc5\x3f", 4) == 0) + return true; - return false; + return false; } -bool sa_isWithdrawFromSalary(const EthereumSignTx *msg) { - - if (memcmp(msg->to.bytes, SAPROXY_ADDRESS, 20) == 0) { // correct proxy address? - if (isWithFromSalary(msg)) { // does kk handle call? - return true; - } +bool sa_isWithdrawFromSalary(const EthereumSignTx* msg) { + if (memcmp(msg->to.bytes, SAPROXY_ADDRESS, 20) == + 0) { // correct proxy address? + if (isWithFromSalary(msg)) { // does kk handle call? + return true; } - return false; + } + return false; } -bool sa_confirmWithdrawFromSalary(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; - char confStr[41]; - bignum256 salaryId, withdrawAmount; +bool sa_confirmWithdrawFromSalary(uint32_t data_total, + const EthereumSignTx* msg) { + (void)data_total; + char confStr[41]; + bignum256 salaryId, withdrawAmount; - bn_from_bytes(msg->data_initial_chunk.bytes + 4, 32, &salaryId); - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 1*32, 32, &withdrawAmount); + bn_from_bytes(msg->data_initial_chunk.bytes + 4, 32, &salaryId); + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 1 * 32, 32, + &withdrawAmount); - // confirm raw unformatted numbers - bn_format(&salaryId, NULL, "", 0, 0, false, confStr, sizeof(confStr)); - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Sablier", "Salary ID %s", confStr)) { - return false; - } + // confirm raw unformatted numbers + bn_format(&salaryId, NULL, "", 0, 0, false, confStr, sizeof(confStr)); + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Sablier", + "Salary ID %s", confStr)) { + return false; + } - // confirm raw unformatted numbers - bn_format(&withdrawAmount, NULL, " Token Units", 0, 0, false, confStr, sizeof(confStr)); - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Sablier", "Withdraw Amount %s", confStr)) { - return false; - } - return true; + // confirm raw unformatted numbers + bn_format(&withdrawAmount, NULL, " Token Units", 0, 0, false, confStr, + sizeof(confStr)); + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Sablier", + "Withdraw Amount %s", confStr)) { + return false; + } + return true; } diff --git a/lib/firmware/ethereum_contracts/thortx.c b/lib/firmware/ethereum_contracts/thortx.c index 07f3b41f4..6f48d1ab5 100644 --- a/lib/firmware/ethereum_contracts/thortx.c +++ b/lib/firmware/ethereum_contracts/thortx.c @@ -27,93 +27,115 @@ #include "keepkey/firmware/thorchain.h" #include "trezor/crypto/address.h" -bool thor_isThorchainTx(const EthereumSignTx *msg) { - if (msg->has_to && msg->to.size == 20 && - memcmp(msg->data_initial_chunk.bytes, - "\x1f\xec\xe7\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - 16) == 0) { +bool thor_has_deposit_selector(const EthereumSignTx* msg) { + if (msg->data_initial_chunk.size < 4) return false; + return (memcmp(msg->data_initial_chunk.bytes, THOR_SELECTOR_DEPOSIT, 4) == + 0 || + memcmp(msg->data_initial_chunk.bytes, + THOR_SELECTOR_DEPOSIT_WITH_EXPIRY, 4) == 0); +} + +bool thor_is_expiry_variant(const EthereumSignTx* msg) { + if (msg->data_initial_chunk.size < 4) return false; + return memcmp(msg->data_initial_chunk.bytes, + THOR_SELECTOR_DEPOSIT_WITH_EXPIRY, 4) == 0; +} + +bool thor_isThorchainTx(const EthereumSignTx* msg) { + if (msg->has_to && msg->to.size == 20 && thor_has_deposit_selector(msg)) { return true; } return false; } -bool thor_confirmThorTx(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; +bool thor_confirmThorTx(uint32_t data_total, const EthereumSignTx* msg) { + (void)data_total; + + /* Minimum calldata: selector(4) + vault(32) + asset(32) + amount(32) + + * memo_offset(32) + memo_length(32) = 164 bytes for deposit(), + * + expiry(32) = 196 bytes for depositWithExpiry(). */ + const bool is_expiry = thor_is_expiry_variant(msg); + const size_t min_chunk = is_expiry ? 260 : 228; + if (msg->data_initial_chunk.size < min_chunk) return false; + + char confStr[41], *conf; + const TokenType* assetToken; + uint8_t* thorchainData; + const uint8_t* contractAssetAddress; + const uint8_t *vaultAddress, *assetAddress; + uint32_t ctr; + bignum256 Amount; + + vaultAddress = (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 12); + contractAssetAddress = + (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 32 + 12); + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2 * 32, 32, &Amount); + /* deposit(): memo at 4 + 5*32; depositWithExpiry(): memo at 4 + 6*32 */ + thorchainData = + (uint8_t*)(msg->data_initial_chunk.bytes + 4 + (is_expiry ? 6 : 5) * 32); + + // Start confirmations + for (ctr = 0; ctr < 20; ctr++) { + snprintf(&confStr[ctr * 2], 3, "%02x", msg->to.bytes[ctr]); + } + if (strncmp(confStr, THOR_ROUTER, sizeof(THOR_ROUTER)) == 0) { + conf = "Thorchain router"; + } else { + conf = confStr; + } + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Thorchain data", + "Routing through %s", conf)) { + return false; + } - char confStr[41], *conf; - const TokenType *assetToken; - uint8_t *thorchainData, *vaultAddress, *assetAddress, *contractAssetAddress; - uint32_t ctr; - bignum256 Amount; + // just display token address and amount as string + for (ctr = 0; ctr < 20; ctr++) { + snprintf(&confStr[ctr * 2], 3, "%02x", vaultAddress[ctr]); + } + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Thorchain data", + "Using Asgard vault %s", confStr)) { + return false; + } - vaultAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 12); - contractAssetAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + 12); - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &Amount); - thorchainData = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 5*32); + if (memcmp(contractAssetAddress, ETH_ADDRESS, sizeof(ETH_ADDRESS)) == 0) { + assetAddress = (const uint8_t*) + ETH_NATIVE; // get eth native parameters if asset is not a token + } else { + assetAddress = contractAssetAddress; + } - // Start confirmations - for (ctr=0; ctr<20; ctr++) { - snprintf(&confStr[ctr*2], 3, "%02x", msg->to.bytes[ctr]); - } - if (strncmp(confStr, THOR_ROUTER, sizeof(THOR_ROUTER)) == 0) { - conf = "Thorchain router"; - } else { - conf = confStr; - } - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain data", "Routing through %s", conf)) { - return false; - } + assetToken = tokenByChainAddress(msg->chain_id, assetAddress); + if (strncmp(assetToken->ticker, " UNKN", 5) == 0) { // just display token address and amount as string - for (ctr=0; ctr<20; ctr++) { - snprintf(&confStr[ctr*2], 3, "%02x", vaultAddress[ctr]); + for (ctr = 0; ctr < 20; ctr++) { + snprintf(&confStr[ctr * 2], 3, "%02x", assetAddress[ctr]); } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain data", "Using Asgard vault %s", confStr)) { - return false; - } - - if (memcmp(contractAssetAddress, ETH_ADDRESS, sizeof(ETH_ADDRESS)) == 0) { - assetAddress = (uint8_t *)ETH_NATIVE; // get eth native parameters if asset is not a token - } else { - assetAddress = contractAssetAddress; - } - - assetToken = tokenByChainAddress(msg->chain_id, assetAddress); - - if (strncmp(assetToken->ticker, " UNKN", 5) == 0) { - - // just display token address and amount as string - for (ctr=0; ctr<20; ctr++) { - snprintf(&confStr[ctr*2], 3, "%02x", assetAddress[ctr]); - } - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Thorchain data", "from asset %s", confStr)) { - return false; - } - // We don't know what the exponent should be so just confirm raw unformatted number - bn_format(&Amount, NULL, " unformatted", 0, 0, false, confStr, sizeof(confStr)); + return false; + } + // We don't know what the exponent should be so just confirm raw unformatted + // number + bn_format(&Amount, NULL, " unformatted", 0, 0, false, confStr, + sizeof(confStr)); - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Thorchain data", "amount %s", confStr)) { - return false; - } - - } else { - ethereumFormatAmount(&Amount, assetToken, msg->chain_id, confStr, - sizeof(confStr)); - - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain data", "Confirm sending %s", confStr)) { - return false; - } + return false; } - if (!thorchain_parseConfirmMemo((const char *)thorchainData, 64)) - return false; + } else { + ethereumFormatAmount(&Amount, assetToken, msg->chain_id, confStr, + sizeof(confStr)); - return true; -} + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Thorchain data", "Confirm sending %s", confStr)) { + return false; + } + } + if (!thorchain_parseConfirmMemo((const char*)thorchainData, 64)) return false; + return true; +} diff --git a/lib/firmware/ethereum_contracts/zxappliquid.c b/lib/firmware/ethereum_contracts/zxappliquid.c index 7fa3db963..76d7d3cda 100644 --- a/lib/firmware/ethereum_contracts/zxappliquid.c +++ b/lib/firmware/ethereum_contracts/zxappliquid.c @@ -34,79 +34,81 @@ #include "trezor/crypto/memzero.h" #include "trezor/crypto/sha3.h" -bool zx_confirmApproveLiquidity(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; - const char *to, *tikstr, *poolstr, *allowance, *amt; - unsigned char data[40]; - uint8_t digest[SHA3_256_DIGEST_LENGTH] = {0}; - uint8_t tokdigest[SHA3_256_DIGEST_LENGTH] = {0}; - char digestStr[2*SHA3_256_DIGEST_LENGTH+1], amtStr[2*32+1] = {0}; - int32_t ctr, tokctr; - uint32_t wethord, ttokenord; - const TokenType *WETH, *ttoken; +bool zx_confirmApproveLiquidity(uint32_t data_total, + const EthereumSignTx *msg) { + (void)data_total; + const char *to, *tikstr, *poolstr, *allowance, *amt; + unsigned char data[40]; + uint8_t digest[SHA3_256_DIGEST_LENGTH] = {0}; + uint8_t tokdigest[SHA3_256_DIGEST_LENGTH] = {0}; + char digestStr[2 * SHA3_256_DIGEST_LENGTH + 1], amtStr[2 * 32 + 1] = {0}; + int32_t ctr, tokctr; + uint32_t wethord; + const TokenType *WETH, *ttoken; - if (!tokenByTicker(msg->chain_id, "WETH", &WETH)) return false; - wethord = read_be((const uint8_t *)WETH->address); - to = (const char *)msg->to.bytes; - tokctr = 0; - while (tokctr != -1) { - ttoken = tokenIter(&tokctr); + if (!tokenByTicker(msg->chain_id, "WETH", &WETH)) return false; + wethord = read_be((const uint8_t *)WETH->address); + to = (const char *)msg->to.bytes; + tokctr = 0; + while (tokctr != -1) { + ttoken = tokenIter(&tokctr); - //https://uniswap.org/docs/v2/smart-contract-integration/getting-pair-addresses/ - ttokenord = read_be((const uint8_t *)ttoken->address); - if (ttokenord < wethord) { - memcpy(data, ttoken->address, 20); - memcpy(&data[20], WETH->address, 20); - } else { - memcpy(data, WETH->address, 20); - memcpy(&data[20], ttoken->address, 20); - } - keccak_256(data, sizeof(data), tokdigest); - SHA3_CTX ctx = {0}; - keccak_256_Init(&ctx); - keccak_Update(&ctx, (unsigned char *)"\xff", 1); - keccak_Update(&ctx, (unsigned char *)"\x5C\x69\xbE\xe7\x01\xef\x81\x4a\x2B\x6a\x3E\xDD\x4B\x16\x52\xCB\x9c\xc5\xaA\x6f", 20); - keccak_Update(&ctx, tokdigest, sizeof(tokdigest)); - keccak_Update(&ctx, (unsigned char *)"\x96\xe8\xac\x42\x77\x19\x8f\xf8\xb6\xf7\x85\x47\x8a\xa9\xa3\x9f\x40\x3c\xb7\x68\xdd\x02\xcb\xee\x32\x6c\x3e\x7d\xa3\x48\x84\x5f", 32); - keccak_Final(&ctx, digest); - if (memcmp(to, &digest[12], 20) == 0) break; + // https://uniswap.org/docs/v2/smart-contract-integration/getting-pair-addresses/ + uint32_t ttokenord = read_be((const uint8_t *)ttoken->address); + if (ttokenord < wethord) { + memcpy(data, ttoken->address, 20); + memcpy(&data[20], WETH->address, 20); + } else { + memcpy(data, WETH->address, 20); + memcpy(&data[20], ttoken->address, 20); } + keccak_256(data, sizeof(data), tokdigest); + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, (unsigned char *)"\xff", 1); + keccak_Update(&ctx, (unsigned char *)"\x5C\x69\xbE\xe7\x01\xef\x81\x4a\x2B\x6a\x3E\xDD\x4B\x16\x52\xCB\x9c\xc5\xaA\x6f", 20); + keccak_Update(&ctx, tokdigest, sizeof(tokdigest)); + keccak_Update(&ctx, (unsigned char *)"\x96\xe8\xac\x42\x77\x19\x8f\xf8\xb6\xf7\x85\x47\x8a\xa9\xa3\x9f\x40\x3c\xb7\x68\xdd\x02\xcb\xee\x32\x6c\x3e\x7d\xa3\x48\x84\x5f", 32); + keccak_Final(&ctx, digest); + if (memcmp(to, &digest[12], 20) == 0) break; + } - if (tokctr != -1) { - for (ctr=0; ctrticker; - poolstr = &digestStr[12*2]; - } else { - for (ctr=0; ctr<20; ctr++) { - snprintf(&digestStr[ctr*2], 3, "%02x", to[ctr]); - } - tikstr = ""; - poolstr = digestStr; + if (tokctr != -1) { + for (ctr = 0; ctr < SHA3_256_DIGEST_LENGTH; ctr++) { + snprintf(&digestStr[ctr * 2], 3, "%02x", digest[ctr]); } + tikstr = ttoken->ticker; + poolstr = &digestStr[12 * 2]; + } else { + for (ctr = 0; ctr < 20; ctr++) { + snprintf(&digestStr[ctr * 2], 3, "%02x", to[ctr]); + } + tikstr = ""; + poolstr = digestStr; + } - allowance = (char *)(msg->data_initial_chunk.bytes + 4 +32); - if (memcmp(allowance, (uint8_t *)&MAX_ALLOWANCE, 32) == 0) { - amt = "full balance"; - } else { - for (ctr=0; ctr<32; ctr++) { - snprintf(&amtStr[ctr*2], 3, "%02x", allowance[ctr]); - } - amt = amtStr; + allowance = (char *)(msg->data_initial_chunk.bytes + 4 + 32); + if (memcmp(allowance, (uint8_t *)&MAX_ALLOWANCE, 32) == 0) { + amt = "full balance"; + } else { + for (ctr = 0; ctr < 32; ctr++) { + snprintf(&amtStr[ctr * 2], 3, "%02x", allowance[ctr]); } + amt = amtStr; + } - char *appStr = "uniswap approve liquidity"; - confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, appStr, - "Amount: %s", amt); - confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, appStr, - "approve for pool %s %s", tikstr, poolstr); - return true; + const char *appStr = "uniswap approve liquidity"; + confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, appStr, "Amount: %s", + amt); + confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, appStr, + "approve for pool %s %s", tikstr, poolstr); + return true; } bool zx_isZxApproveLiquid(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\x09\x5e\xa7\xb3", 4) == 0) - if (memcmp((uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 - 20), UNISWAP_ROUTER_ADDRESS, 20) == 0) - return true; - return false; + if (memcmp(msg->data_initial_chunk.bytes, "\x09\x5e\xa7\xb3", 4) == 0) + if (memcmp((uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 - 20), + UNISWAP_ROUTER_ADDRESS, 20) == 0) + return true; + return false; } diff --git a/lib/firmware/ethereum_contracts/zxliquidtx.c b/lib/firmware/ethereum_contracts/zxliquidtx.c index 1489913a4..e70ccd4e3 100644 --- a/lib/firmware/ethereum_contracts/zxliquidtx.c +++ b/lib/firmware/ethereum_contracts/zxliquidtx.c @@ -35,141 +35,149 @@ #include -static HDNode *zx_getDerivedNode(const char *curve, const uint32_t *address_n, - size_t address_n_count, - uint32_t *fingerprint) { - static HDNode CONFIDENTIAL node; - if (fingerprint) { - *fingerprint = 0; - } - - if (!get_curve_by_name(curve)) { - return 0; - } - - if (!storage_getRootNode(curve, true, &node)) { - return 0; - } - - if (!address_n || address_n_count == 0) { - return &node; - } - - if (hdnode_private_ckd_cached(&node, address_n, address_n_count, +static HDNode* zx_getDerivedNode(const char* curve, const uint32_t* address_n, + size_t address_n_count, + uint32_t* fingerprint) { + static HDNode CONFIDENTIAL node; + if (fingerprint) { + *fingerprint = 0; + } + + if (!get_curve_by_name(curve)) { + return 0; + } + + if (!storage_getRootNode(curve, true, &node)) { + return 0; + } + + if (!address_n || address_n_count == 0) { + return &node; + } + + if (hdnode_private_ckd_cached(&node, address_n, address_n_count, fingerprint) == 0) { - return 0; - } + return 0; + } - return &node; + return &node; } -static bool isAddLiquidityEthCall(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\xf3\x05\xd7\x19", 4) == 0) - return true; +static bool isAddLiquidityEthCall(const EthereumSignTx* msg) { + if (memcmp(msg->data_initial_chunk.bytes, "\xf3\x05\xd7\x19", 4) == 0) + return true; - return false; + return false; } -static bool isRemoveLiquidityEthCall(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\x02\x75\x1c\xec", 4) == 0) - return true; +static bool isRemoveLiquidityEthCall(const EthereumSignTx* msg) { + if (memcmp(msg->data_initial_chunk.bytes, "\x02\x75\x1c\xec", 4) == 0) + return true; - return false; + return false; } -static bool confirmFromAccountMatch(const EthereumSignTx *msg, char *addremStr) { - // Determine withdrawal address - char addressStr[43] = {'0', 'x', '\0'}; - char *fromSrc; - uint8_t *fromAddress; - uint8_t addressBytes[20]; - - HDNode *node = zx_getDerivedNode(SECP256K1_NAME, msg->address_n, - msg->address_n_count, NULL); - if (!node) return false; - - if (!hdnode_get_ethereum_pubkeyhash(node, addressBytes)) { - memzero(node, sizeof(*node)); - } - - fromAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 5*32 - 20); - - if (memcmp(fromAddress, addressBytes, 20) == 0) { - fromSrc = "self"; - } else { - fromSrc = "NOT this wallet"; - } - - for (uint32_t ctr=0; ctr<20; ctr++) { - snprintf(&addressStr[2+ctr*2], 3, "%02x", fromAddress[ctr]); - } - - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, addremStr, - "Confirming ETH address is %s: %s", fromSrc, addressStr)) { - return false; - } - return true; +static bool confirmFromAccountMatch(const EthereumSignTx* msg, + const char* addremStr) { + // Determine withdrawal address + char addressStr[43] = {'0', 'x', '\0'}; + const char* fromSrc; + const uint8_t* fromAddress; + uint8_t addressBytes[20]; + + HDNode* node = zx_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return false; + + if (!hdnode_get_ethereum_pubkeyhash(node, addressBytes)) { + memzero(node, sizeof(*node)); + } + + fromAddress = + (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 5 * 32 - 20); + + if (memcmp(fromAddress, addressBytes, 20) == 0) { + fromSrc = "self"; + } else { + fromSrc = "NOT this wallet"; + } + + for (uint32_t ctr = 0; ctr < 20; ctr++) { + snprintf(&addressStr[2 + ctr * 2], 3, "%02x", fromAddress[ctr]); + } + + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, addremStr, + "Confirming ETH address is %s: %s", fromSrc, addressStr)) { + return false; + } + return true; } -bool zx_isZxLiquidTx(const EthereumSignTx *msg) { - if (memcmp(msg->to.bytes, UNISWAP_ROUTER_ADDRESS, 20) == 0) { // correct contract address? +bool zx_isZxLiquidTx(const EthereumSignTx* msg) { + if (memcmp(msg->to.bytes, UNISWAP_ROUTER_ADDRESS, 20) == + 0) { // correct contract address? - if (isAddLiquidityEthCall(msg)) return true; - - if (isRemoveLiquidityEthCall(msg)) return true; - } - return false; + if (isAddLiquidityEthCall(msg)) return true; + + if (isRemoveLiquidityEthCall(msg)) return true; + } + return false; } -bool zx_confirmZxLiquidTx(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; - const TokenType *token; - char constr1[40], constr2[40], tokbuf[32], *arStr = ""; - uint8_t *tokenAddress, *deadlineBytes; - bignum256 Amount; - uint64_t deadline; - - if (isAddLiquidityEthCall(msg)) { - arStr = "uniswap add liquidity"; - } else if (isRemoveLiquidityEthCall(msg)) { - arStr = "uniswap remove liquidity"; - } else { - return false; - } - - tokenAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 - 20); - token = tokenByChainAddress(msg->chain_id, tokenAddress); - deadlineBytes = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 6*32 - 8); - deadline = ((uint64_t)deadlineBytes[0] << 8*7) | - ((uint64_t)deadlineBytes[1] << 8*6) | - ((uint64_t)deadlineBytes[2] << 8*5) | - ((uint64_t)deadlineBytes[3] << 8*4) | - ((uint64_t)deadlineBytes[4] << 8*3) | - ((uint64_t)deadlineBytes[5] << 8*2) | - ((uint64_t)deadlineBytes[6] << 8*1) | - ((uint64_t)deadlineBytes[7]); - - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 32, 32, &Amount); // token amount - ethereumFormatAmount(&Amount, token, msg->chain_id, tokbuf, sizeof(tokbuf)); - snprintf(constr1, 32, "%s", tokbuf); - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &Amount); // token min amount - ethereumFormatAmount(&Amount, token, msg->chain_id, tokbuf, sizeof(tokbuf)); - snprintf(constr2, 32, "%s", tokbuf); - confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, - "%s\nMinimum %s", constr1, constr2); - if (!confirmFromAccountMatch(msg, arStr)) { - return false; - } - - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 3*32, 32, &Amount); // eth min amount - ethereumFormatAmount(&Amount, NULL, msg->chain_id, tokbuf, sizeof(tokbuf)); - - snprintf(constr1, 32, "%s", tokbuf); - confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, - "Minimum %s", constr1); - - confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, - "Deadline %s", ctime((const time_t *)&deadline)); - - return true; +bool zx_confirmZxLiquidTx(uint32_t data_total, const EthereumSignTx* msg) { + (void)data_total; + const TokenType* token; + char constr1[40], constr2[40], tokbuf[32]; + const char* arStr = ""; + const uint8_t *tokenAddress, *deadlineBytes; + bignum256 Amount; + uint64_t deadline; + + if (isAddLiquidityEthCall(msg)) { + arStr = "uniswap add liquidity"; + } else if (isRemoveLiquidityEthCall(msg)) { + arStr = "uniswap remove liquidity"; + } else { + return false; + } + + tokenAddress = (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 32 - 20); + token = tokenByChainAddress(msg->chain_id, tokenAddress); + deadlineBytes = + (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 6 * 32 - 8); + deadline = ((uint64_t)deadlineBytes[0] << 8 * 7) | + ((uint64_t)deadlineBytes[1] << 8 * 6) | + ((uint64_t)deadlineBytes[2] << 8 * 5) | + ((uint64_t)deadlineBytes[3] << 8 * 4) | + ((uint64_t)deadlineBytes[4] << 8 * 3) | + ((uint64_t)deadlineBytes[5] << 8 * 2) | + ((uint64_t)deadlineBytes[6] << 8 * 1) | + ((uint64_t)deadlineBytes[7]); + + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 32, 32, + &Amount); // token amount + ethereumFormatAmount(&Amount, token, msg->chain_id, tokbuf, sizeof(tokbuf)); + snprintf(constr1, 32, "%s", tokbuf); + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2 * 32, 32, + &Amount); // token min amount + ethereumFormatAmount(&Amount, token, msg->chain_id, tokbuf, sizeof(tokbuf)); + snprintf(constr2, 32, "%s", tokbuf); + confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, + "%s\nMinimum %s", constr1, constr2); + if (!confirmFromAccountMatch(msg, arStr)) { + return false; + } + + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 3 * 32, 32, + &Amount); // eth min amount + ethereumFormatAmount(&Amount, NULL, msg->chain_id, tokbuf, sizeof(tokbuf)); + + snprintf(constr1, 32, "%s", tokbuf); + confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, "Minimum %s", + constr1); + + confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, arStr, "Deadline %s", + ctime((const time_t*)&deadline)); + + return true; } diff --git a/lib/firmware/ethereum_contracts/zxswap.c b/lib/firmware/ethereum_contracts/zxswap.c index fda00b8cd..5d033e804 100644 --- a/lib/firmware/ethereum_contracts/zxswap.c +++ b/lib/firmware/ethereum_contracts/zxswap.c @@ -26,75 +26,75 @@ #include "keepkey/firmware/fsm.h" #include "trezor/crypto/address.h" -static bool isSellToUniswapCall(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\xd9\x62\x7a\xa4", 4) == 0) - return true; +static bool isSellToUniswapCall(const EthereumSignTx* msg) { + if (memcmp(msg->data_initial_chunk.bytes, "\xd9\x62\x7a\xa4", 4) == 0) + return true; - return false; + return false; } -bool zx_isZxSwap(const EthereumSignTx *msg) { - - if (memcmp(msg->to.bytes, ZXSWAP_ADDRESS, 20) == 0) { // correct proxy address? - if (isSellToUniswapCall(msg)) { // does kk handle call? - return true; - } +bool zx_isZxSwap(const EthereumSignTx* msg) { + if (memcmp(msg->to.bytes, ZXSWAP_ADDRESS, 20) == + 0) { // correct proxy address? + if (isSellToUniswapCall(msg)) { // does kk handle call? + return true; } - return false; + } + return false; } -bool zx_confirmZxSwap(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; - const TokenType *from, *to; - uint8_t *fromAddress, *toAddress; - char constr1[40], constr2[40]; - uint32_t numOfTokens, adder, isSushi; - char *exchange; +bool zx_confirmZxSwap(uint32_t data_total, const EthereumSignTx* msg) { + (void)data_total; + const TokenType *from, *to; + const uint8_t *fromAddress, *toAddress; + char constr1[40], constr2[40]; + uint32_t numOfTokens, adder, isSushi; + const char* exchange; - numOfTokens = read_be(msg->data_initial_chunk.bytes + 4 + 5*32 - 4); - isSushi = read_be(msg->data_initial_chunk.bytes + 4 + 4*32 - 4); - if (isSushi == 0) { - exchange = "Uniswap"; - } else { - exchange = "Sushiswap"; - } + numOfTokens = read_be(msg->data_initial_chunk.bytes + 4 + 5 * 32 - 4); + isSushi = read_be(msg->data_initial_chunk.bytes + 4 + 4 * 32 - 4); + if (isSushi == 0) { + exchange = "Uniswap"; + } else { + exchange = "Sushiswap"; + } - switch (numOfTokens) { - case 2: - adder = 0; // only two tokens, swap to token second - break; - case 3: - adder = 1; // swap to token last in the list of 3 - break; - default: // can't interpret 0, 1, or >3 tokens - return false; - break; - } + switch (numOfTokens) { + case 2: + adder = 0; // only two tokens, swap to token second + break; + case 3: + adder = 1; // swap to token last in the list of 3 + break; + default: // can't interpret 0, 1, or >3 tokens + return false; + break; + } - fromAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 5*32 + 12); - toAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + (6+adder)*32 + 12); + fromAddress = + (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 5 * 32 + 12); + toAddress = (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + + (6 + adder) * 32 + 12); + from = tokenByChainAddress(msg->chain_id, fromAddress); + to = tokenByChainAddress(msg->chain_id, toAddress); - from = tokenByChainAddress(msg->chain_id, fromAddress); - to = tokenByChainAddress(msg->chain_id, toAddress); - - // Get token trade amount data - bignum256 sellTokenAmount, minBuyTokenAmount; - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 32, 32, &sellTokenAmount); - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &minBuyTokenAmount); + // Get token trade amount data + bignum256 sellTokenAmount, minBuyTokenAmount; + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 32, 32, &sellTokenAmount); + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2 * 32, 32, + &minBuyTokenAmount); - char sellToken[32]; - char minBuyToken[32]; - ethereumFormatAmount(&sellTokenAmount, from, msg->chain_id, sellToken, + char sellToken[32]; + char minBuyToken[32]; + ethereumFormatAmount(&sellTokenAmount, from, msg->chain_id, sellToken, sizeof(sellToken)); - ethereumFormatAmount(&minBuyTokenAmount, to, msg->chain_id, minBuyToken, + ethereumFormatAmount(&minBuyTokenAmount, to, msg->chain_id, minBuyToken, sizeof(minBuyToken)); - snprintf(constr1, 32, "%s", sellToken); - snprintf(constr2, 32, "%s", minBuyToken); + snprintf(constr1, 32, "%s", sellToken); + snprintf(constr2, 32, "%s", minBuyToken); - return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, exchange, + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, exchange, "Sell %s\nBuy at least %s", constr1, constr2); - - return true; } diff --git a/lib/firmware/ethereum_contracts/zxtransERC20.c b/lib/firmware/ethereum_contracts/zxtransERC20.c index 942dfef8b..75f64c160 100644 --- a/lib/firmware/ethereum_contracts/zxtransERC20.c +++ b/lib/firmware/ethereum_contracts/zxtransERC20.c @@ -26,50 +26,46 @@ #include "keepkey/firmware/fsm.h" #include "trezor/crypto/address.h" -static bool isTransERC20Call(const EthereumSignTx *msg) { - if (memcmp(msg->data_initial_chunk.bytes, "\x41\x55\x65\xb0", 4) == 0) - return true; +static bool isTransERC20Call(const EthereumSignTx* msg) { + if (memcmp(msg->data_initial_chunk.bytes, "\x41\x55\x65\xb0", 4) == 0) + return true; - return false; + return false; } -bool zx_isZxTransformERC20(const EthereumSignTx *msg) { - - if (memcmp(msg->to.bytes, ZXSWAP_ADDRESS, 20) == 0) { // correct proxy address? - if (isTransERC20Call(msg)) { // does kk handle call? - return true; - } +bool zx_isZxTransformERC20(const EthereumSignTx* msg) { + if (memcmp(msg->to.bytes, ZXSWAP_ADDRESS, 20) == + 0) { // correct proxy address? + if (isTransERC20Call(msg)) { // does kk handle call? + return true; } - return false; + } + return false; } -bool zx_confirmZxTransERC20(uint32_t data_total, const EthereumSignTx *msg) { - (void)data_total; - const TokenType *in, *out; - uint8_t *inAddress, *outAddress; - char constr1[40], constr2[40]; - bignum256 inAmount, outAmount; - char inToken[32], outToken[32]; +bool zx_confirmZxTransERC20(uint32_t data_total, const EthereumSignTx* msg) { + (void)data_total; + const TokenType *in, *out; + const uint8_t *inAddress, *outAddress; + char constr1[40], constr2[40]; + bignum256 inAmount, outAmount; + char inToken[32], outToken[32]; + inAddress = (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 12); + outAddress = (const uint8_t*)(msg->data_initial_chunk.bytes + 4 + 32 + 12); + in = tokenByChainAddress(msg->chain_id, inAddress); + out = tokenByChainAddress(msg->chain_id, outAddress); - inAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 12); - outAddress = (uint8_t *)(msg->data_initial_chunk.bytes + 4 + 32 + 12); - in = tokenByChainAddress(msg->chain_id, inAddress); - out = tokenByChainAddress(msg->chain_id, outAddress); - - // Get amount data - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2*32, 32, &inAmount); - bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 3*32, 32, &outAmount); + // Get amount data + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 2 * 32, 32, &inAmount); + bn_from_bytes(msg->data_initial_chunk.bytes + 4 + 3 * 32, 32, &outAmount); - ethereumFormatAmount(&inAmount, in, msg->chain_id, inToken, - sizeof(inToken)); - ethereumFormatAmount(&outAmount, out, msg->chain_id, outToken, + ethereumFormatAmount(&inAmount, in, msg->chain_id, inToken, sizeof(inToken)); + ethereumFormatAmount(&outAmount, out, msg->chain_id, outToken, sizeof(outToken)); - snprintf(constr1, 32, "%s", inToken); - snprintf(constr2, 32, "%s", outToken); + snprintf(constr1, 32, "%s", inToken); + snprintf(constr2, 32, "%s", outToken); - return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Transform ERC20", - "Input %s\nOutput %s", constr1, constr2); - - return true; + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Transform ERC20", "Input %s\nOutput %s", constr1, constr2); } diff --git a/lib/firmware/ethereum_tokens.c b/lib/firmware/ethereum_tokens.c index 0c78a30d9..15e5f0678 100644 --- a/lib/firmware/ethereum_tokens.c +++ b/lib/firmware/ethereum_tokens.c @@ -18,31 +18,31 @@ static const TokenType Unknown = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00", " UNKN", 1, 0}; -const TokenType *UnknownToken = (const TokenType *)&Unknown; +const TokenType* UnknownToken = (const TokenType*)&Unknown; static const TokenType Ethtest = { "\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee\xee" "\xee\xee", " ETH", 1, 18}; -const TokenType *EthTestToken = (const TokenType *)&Ethtest; +const TokenType* EthTestToken = (const TokenType*)&Ethtest; -const TokenType *tokenIter(int32_t *ctr) { +const TokenType* tokenIter(int32_t* ctr) { // return the next tok in the list. // input: *ctr = position of desired token (0 to TOKENS_COUNT) - // output: returns token at list count *ctr at input for 0 <= *ctr < TOKEN_COUNT + // output: returns token at list count *ctr at input for 0 <= *ctr < + // TOKEN_COUNT // *ctr = position of next token in list, OR -1 for end of list if (*ctr < 0 || *ctr >= TOKENS_COUNT) { *ctr = -1; return UnknownToken; } - - *ctr+=1; - return &(tokens[*ctr - 1]); + *ctr += 1; + return &(tokens[*ctr - 1]); } -const TokenType *tokenByChainAddress(uint8_t chain_id, const uint8_t *address) { +const TokenType* tokenByChainAddress(uint8_t chain_id, const uint8_t* address) { if (!address) return 0; for (int i = 0; i < TOKENS_COUNT; i++) { if (chain_id == tokens[i].chain_id && @@ -57,8 +57,8 @@ const TokenType *tokenByChainAddress(uint8_t chain_id, const uint8_t *address) { return UnknownToken; } -bool tokenByTicker(uint8_t chain_id, const char *ticker, - const TokenType **token) { +bool tokenByTicker(uint8_t chain_id, const char* ticker, + const TokenType** token) { *token = NULL; // First look in the legacy table, confirming that the entry also exists in @@ -87,7 +87,7 @@ bool tokenByTicker(uint8_t chain_id, const char *ticker, return *token; } -void coinFromToken(CoinType *coin, const TokenType *token) { +void coinFromToken(CoinType* coin, const TokenType* token) { memset(coin, 0, sizeof(*coin)); coin->has_coin_name = true; @@ -111,7 +111,7 @@ void coinFromToken(CoinType *coin, const TokenType *token) { coin->has_contract_address = true; coin->contract_address.size = 20; - memcpy((char *)&coin->contract_address.bytes[0], token->address, 20); + memcpy((char*)&coin->contract_address.bytes[0], token->address, 20); _Static_assert(20 <= sizeof(coin->contract_address.bytes), "contract_address is not large enough to hold an ETH address"); diff --git a/lib/firmware/fsm.c b/lib/firmware/fsm.c index 0d779f448..d9d8f4a1f 100644 --- a/lib/firmware/fsm.c +++ b/lib/firmware/fsm.c @@ -55,9 +55,12 @@ #include "keepkey/firmware/ripple.h" #include "keepkey/firmware/signing.h" #include "keepkey/firmware/signtx_tendermint.h" +#include "keepkey/firmware/solana.h" #include "keepkey/firmware/storage.h" #include "keepkey/firmware/tendermint.h" #include "keepkey/firmware/thorchain.h" +#include "keepkey/firmware/tron.h" +#include "keepkey/firmware/ton.h" #include "keepkey/firmware/transaction.h" #include "keepkey/firmware/txin_check.h" #include "keepkey/firmware/u2f.h" @@ -84,6 +87,9 @@ #include "messages-ripple.pb.h" #include "messages-thorchain.pb.h" #include "messages-mayachain.pb.h" +#include "messages-tron.pb.h" +#include "messages-ton.pb.h" +#include "messages-solana.pb.h" #include @@ -152,8 +158,8 @@ static const MessagesMap_t MessagesMap[] = { extern bool reset_msg_stack; -static const CoinType *fsm_getCoin(bool has_name, const char *name) { - const CoinType *coin; +static const CoinType* fsm_getCoin(bool has_name, const char* name) { + const CoinType* coin; if (has_name) { coin = coinByName(name); } else { @@ -168,9 +174,9 @@ static const CoinType *fsm_getCoin(bool has_name, const char *name) { return coin; } -static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, +static HDNode* fsm_getDerivedNode(const char* curve, const uint32_t* address_n, size_t address_n_count, - uint32_t *fingerprint) { + uint32_t* fingerprint) { static HDNode CONFIDENTIAL node; if (fingerprint) { *fingerprint = 0; @@ -204,7 +210,7 @@ static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, } #if DEBUG_LINK -static void sendFailureWrapper(FailureType code, const char *text) { +static void sendFailureWrapper(FailureType code, const char* text) { fsm_sendFailure(code, text); } #endif @@ -229,9 +235,9 @@ void fsm_init(void) { txin_dgst_initialize(); } -void fsm_sendSuccess(const char *text) { +void fsm_sendSuccess(const char* text) { if (reset_msg_stack) { - fsm_msgInitialize((Initialize *)0); + fsm_msgInitialize((Initialize*)0); reset_msg_stack = false; return; } @@ -246,9 +252,9 @@ void fsm_sendSuccess(const char *text) { msg_write(MessageType_MessageType_Success, resp); } -void fsm_sendFailure(FailureType code, const char *text) { +void fsm_sendFailure(FailureType code, const char* text) { if (reset_msg_stack) { - fsm_msgInitialize((Initialize *)0); + fsm_msgInitialize((Initialize*)0); reset_msg_stack = false; return; } @@ -264,7 +270,7 @@ void fsm_sendFailure(FailureType code, const char *text) { msg_write(MessageType_MessageType_Failure, resp); } -void fsm_msgClearSession(ClearSession *msg) { +void fsm_msgClearSession(ClearSession* msg) { (void)msg; session_clear(/*clear_pin=*/true); fsm_sendSuccess("Session cleared"); @@ -284,3 +290,6 @@ void fsm_msgClearSession(ClearSession *msg) { #include "fsm_msg_tendermint.h" #include "fsm_msg_thorchain.h" #include "fsm_msg_mayachain.h" +#include "fsm_msg_tron.h" +#include "fsm_msg_ton.h" +#include "fsm_msg_solana.h" diff --git a/lib/firmware/fsm_msg_binance.h b/lib/firmware/fsm_msg_binance.h index f7f279fee..0adf00c62 100644 --- a/lib/firmware/fsm_msg_binance.h +++ b/lib/firmware/fsm_msg_binance.h @@ -1,17 +1,17 @@ -void fsm_msgBinanceGetAddress(const BinanceGetAddress *msg) { +void fsm_msgBinanceGetAddress(const BinanceGetAddress* msg) { RESP_INIT(BinanceAddress); CHECK_INITIALIZED CHECK_PIN - const char *coin_name = "Binance"; - const CoinType *coin = fsm_getCoin(true, coin_name); + const char* coin_name = "Binance"; + const CoinType* coin = fsm_getCoin(true, coin_name); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -65,11 +65,11 @@ void fsm_msgBinanceGetAddress(const BinanceGetAddress *msg) { msg_write(MessageType_MessageType_BinanceAddress, resp); } -void fsm_msgBinanceSignTx(const BinanceSignTx *msg) { +void fsm_msgBinanceSignTx(const BinanceSignTx* msg) { CHECK_INITIALIZED CHECK_PIN - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -93,7 +93,7 @@ void fsm_msgBinanceSignTx(const BinanceSignTx *msg) { static void binance_response(void); -void fsm_msgBinanceTransferMsg(const BinanceTransferMsg *msg) { +void fsm_msgBinanceTransferMsg(const BinanceTransferMsg* msg) { CHECK_PARAM(binance_signingIsInited(), "Signing not in progress?"); CHECK_PARAM(msg->inputs_count == 1, "Malformed BinanceTransferMsg") CHECK_PARAM(msg->inputs[0].coins_count == 1, "Malformed BinanceTransferMsg") @@ -105,7 +105,7 @@ void fsm_msgBinanceTransferMsg(const BinanceTransferMsg *msg) { msg->outputs[0].coins[0].denom) == 0, "Malformed BinanceTransferMsg") - const CoinType *coin = fsm_getCoin(true, "Binance"); + const CoinType* coin = fsm_getCoin(true, "Binance"); if (!coin) { return; } @@ -115,7 +115,8 @@ void fsm_msgBinanceTransferMsg(const BinanceTransferMsg *msg) { default: { char amount_str[42]; char denom_str[14]; - snprintf(denom_str, strlen(msg->outputs[0].coins[0].denom) + 2, " %s", msg->outputs[0].coins[0].denom); + snprintf(denom_str, strlen(msg->outputs[0].coins[0].denom) + 2, " %s", + msg->outputs[0].coins[0].denom); bn_format_uint64(msg->outputs[0].coins[0].amount, NULL, denom_str, 8, 0, false, amount_str, sizeof(amount_str)); if (!confirm_transaction_output( @@ -148,12 +149,12 @@ static void binance_response(void) { return; } - const CoinType *coin = fsm_getCoin(true, "Binance"); + const CoinType* coin = fsm_getCoin(true, "Binance"); if (!coin) { return; } - const BinanceSignTx *sign_tx = binance_getBinanceSignTx(); + const BinanceSignTx* sign_tx = binance_getBinanceSignTx(); if (sign_tx->has_memo && !confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), "%s", sign_tx->memo)) { diff --git a/lib/firmware/fsm_msg_coin.h b/lib/firmware/fsm_msg_coin.h index e9d4a21e4..8b7267cd3 100644 --- a/lib/firmware/fsm_msg_coin.h +++ b/lib/firmware/fsm_msg_coin.h @@ -1,4 +1,4 @@ -void fsm_msgGetPublicKey(GetPublicKey *msg) { +void fsm_msgGetPublicKey(GetPublicKey* msg) { RESP_INIT(PublicKey); CHECK_INITIALIZED @@ -8,15 +8,15 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) { InputScriptType script_type = msg->has_script_type ? msg->script_type : InputScriptType_SPENDADDRESS; - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinType* coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; - const char *curve = coin->curve_name; + const char* curve = coin->curve_name; if (msg->has_ecdsa_curve_name) { curve = msg->ecdsa_curve_name; } uint32_t fingerprint; - HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, + HDNode* node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, &fingerprint); if (!node) return; hdnode_fill_public_key(node); @@ -80,7 +80,7 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) { layoutHome(); } -void fsm_msgSignTx(SignTx *msg) { +void fsm_msgSignTx(SignTx* msg) { CHECK_INITIALIZED CHECK_PARAM(msg->inputs_count > 0, @@ -92,11 +92,11 @@ void fsm_msgSignTx(SignTx *msg) { CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinType* coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) { return; } - const HDNode *node = fsm_getDerivedNode(coin->curve_name, 0, 0, NULL); + const HDNode* node = fsm_getDerivedNode(coin->curve_name, 0, 0, NULL); if (!node) { return; } @@ -106,7 +106,7 @@ void fsm_msgSignTx(SignTx *msg) { signing_init(msg, coin, node); } -void fsm_msgTxAck(TxAck *msg) { +void fsm_msgTxAck(TxAck* msg) { CHECK_PARAM(msg->has_tx, _("No transaction provided")); signing_txack(&(msg->tx)); @@ -114,7 +114,7 @@ void fsm_msgTxAck(TxAck *msg) { // NOTE: there is a very similar copy of this function in coins.c // PLEASE keep both copies in sync. -static bool path_mismatched(const CoinType *coin, const GetAddress *msg) { +static bool path_mismatched(const CoinType* coin, const GetAddress* msg) { bool mismatch = false; // m : no path @@ -191,16 +191,16 @@ static bool path_mismatched(const CoinType *coin, const GetAddress *msg) { return false; } -void fsm_msgGetAddress(GetAddress *msg) { +void fsm_msgGetAddress(GetAddress* msg) { RESP_INIT(Address); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinType* coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + HDNode* node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); if (!node) return; hdnode_fill_public_key(node); @@ -265,13 +265,13 @@ void fsm_msgGetAddress(GetAddress *msg) { layoutHome(); } -void fsm_msgSignMessage(SignMessage *msg) { +void fsm_msgSignMessage(SignMessage* msg) { RESP_INIT(MessageSignature); CHECK_INITIALIZED if (!confirm(ButtonRequestType_ButtonRequest_SignMessage, "Sign Message", - "%s", (char *)msg->message.bytes)) { + "%s", (char*)msg->message.bytes)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Sign message cancelled"); layoutHome(); @@ -280,9 +280,9 @@ void fsm_msgSignMessage(SignMessage *msg) { CHECK_PIN - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinType* coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + HDNode* node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -309,11 +309,11 @@ void fsm_msgSignMessage(SignMessage *msg) { layoutHome(); } -void fsm_msgVerifyMessage(VerifyMessage *msg) { +void fsm_msgVerifyMessage(VerifyMessage* msg) { CHECK_PARAM(msg->has_address, _("No address provided")); CHECK_PARAM(msg->has_message, _("No message provided")); - const CoinType *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + const CoinType* coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); if (!coin) return; layout_simple_message("Verifying Message..."); @@ -326,7 +326,7 @@ void fsm_msgVerifyMessage(VerifyMessage *msg) { return; } if (!review(ButtonRequestType_ButtonRequest_Other, "Message Verified", "%s", - (char *)msg->message.bytes)) { + (char*)msg->message.bytes)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Action cancelled by user")); layoutHome(); diff --git a/lib/firmware/fsm_msg_common.h b/lib/firmware/fsm_msg_common.h index 33cd3e10d..44173a24c 100644 --- a/lib/firmware/fsm_msg_common.h +++ b/lib/firmware/fsm_msg_common.h @@ -1,4 +1,4 @@ -void fsm_msgInitialize(Initialize *msg) { +void fsm_msgInitialize(Initialize* msg) { (void)msg; recovery_cipher_abort(); signing_abort(); @@ -10,13 +10,13 @@ void fsm_msgInitialize(Initialize *msg) { fsm_msgGetFeatures(0); } -static const char *model(void) { - const char *ret = flash_getModel(); +static const char* model(void) { + const char* ret = flash_getModel(); if (ret) return ret; return "Unknown"; } -void fsm_msgGetFeatures(GetFeatures *msg) { +void fsm_msgGetFeatures(GetFeatures* msg) { (void)msg; RESP_INIT(Features); @@ -53,6 +53,7 @@ void fsm_msgGetFeatures(GetFeatures *msg) { resp->has_wipe_code_protection = storage_hasWipeCode(); #ifdef SCM_REVISION + // cppcheck-suppress sizeofwithnumericparameter int len = sizeof(SCM_REVISION) - 1; resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, len); @@ -120,7 +121,7 @@ void fsm_msgGetFeatures(GetFeatures *msg) { msg_write(MessageType_MessageType_Features, resp); } -void fsm_msgGetCoinTable(GetCoinTable *msg) { +void fsm_msgGetCoinTable(GetCoinTable* msg) { RESP_INIT(CoinTable); CHECK_PARAM(msg->has_start == msg->has_end, @@ -158,7 +159,7 @@ void fsm_msgGetCoinTable(GetCoinTable *msg) { msg_write(MessageType_MessageType_CoinTable, resp); } -static bool isValidModelNumber(const char *model) { +static bool isValidModelNumber(const char* model) { #define MODEL_ENTRY(STRING, ENUM) \ if (!strcmp(model, STRING)) return true; #include "keepkey/board/models.def" @@ -167,13 +168,14 @@ static bool isValidModelNumber(const char *model) { void checkPassphrase(void) { if (!passphrase_protect()) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "authenticator needs passphrase"); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + "authenticator needs passphrase"); layoutHome(); return; } } -void fsm_msgPing(Ping *msg) { +void fsm_msgPing(Ping* msg) { RESP_INIT(Success); // If device is in manufacture mode, turn if off, lock it, and program the @@ -186,17 +188,17 @@ void fsm_msgPing(Ping *msg) { flash_setModel(&message); } - const char *errMsgStr[NUM_AUTHERRS] = { - "noerr", - "Authenticator secret storage full", - "Authenticator secret can't be decoded", - "Account name missing or too long, or seed/message string missing", - "Account not found", - "Slot request out of range", - "Authenticator secret seed too large", - "passphrase incorrect for authdata", - "Auth secret unknown error", -}; + const char* errMsgStr[NUM_AUTHERRS] = { + "noerr", + "Authenticator secret storage full", + "Authenticator secret can't be decoded", + "Account name missing or too long, or seed/message string missing", + "Account not found", + "Slot request out of range", + "Authenticator secret seed too large", + "passphrase incorrect for authdata", + "Auth secret unknown error", + }; typedef enum _AUTH_MSG_TYPE { INITAUTH = 0, @@ -207,18 +209,23 @@ void fsm_msgPing(Ping *msg) { NUM_AUTHMESSAGES } AUTH_MSG_TYPE; - - const char * const authMesStr[NUM_AUTHMESSAGES] = { - "\x15" "initializeAuth:", - "\x16" "generateOTPFrom:", - "\x17" "getAccount:", - "\x18" "removeAccount:", - "\x19" "wipeAuthdata:", + const char* const authMesStr[NUM_AUTHMESSAGES] = { + "\x15" + "initializeAuth:", + "\x16" + "generateOTPFrom:", + "\x17" + "getAccount:", + "\x18" + "removeAccount:", + "\x19" + "wipeAuthdata:", }; AUTH_MSG_TYPE authMsg; - for (authMsg=INITAUTH; authMsghas_message && 0 == strncmp(msg->message, authMesStr[authMsg], strlen(authMesStr[authMsg]))) { + for (authMsg = INITAUTH; authMsg < NUM_AUTHMESSAGES; authMsg++) { + if (msg->has_message && 0 == strncmp(msg->message, authMesStr[authMsg], + strlen(authMesStr[authMsg]))) { break; } } @@ -226,39 +233,42 @@ void fsm_msgPing(Ping *msg) { if (authMsg < NUM_AUTHMESSAGES) { // this is an authenticator message unsigned errcode; - char otp[9] = {0}; // allow room for an 8 digit otp - char acc[DOMAIN_SIZE+ACCOUNT_SIZE+2] = {0}; // allow room for domain + ":" + account + char otp[9] = {0}; // allow room for an 8 digit otp + char acc[DOMAIN_SIZE + ACCOUNT_SIZE + 2] = { + 0}; // allow room for domain + ":" + account CHECK_PIN checkPassphrase(); switch (authMsg) { - case INITAUTH: - //DEBUG_DISPLAY("secret %s", &msg->message[strlen(authMesStr[authMsg])]) + // DEBUG_DISPLAY("secret %s", + // &msg->message[strlen(authMesStr[authMsg])]) errcode = addAuthAccount(&msg->message[strlen(authMesStr[authMsg])]); resp->has_message = false; break; case GENOTP: - //DEBUG_DISPLAY("genotp %s", &msg->message[strlen(authMesStr[authMsg])]) + // DEBUG_DISPLAY("genotp %s", + // &msg->message[strlen(authMesStr[authMsg])]) errcode = generateOTP(&msg->message[strlen(authMesStr[authMsg])], otp); - #if DEBUG_LINK +#if DEBUG_LINK char authSlot[128] = {0}; // debug link only getAuthSlot(authSlot); resp->has_message = true; strlcpy(resp->message, otp, 9); strcat(resp->message, ":"); strcat(resp->message, authSlot); - #else +#else resp->has_message = false; - #endif +#endif break; case GETACC: - errcode = getAuthAccount(&msg->message[strlen(authMesStr[authMsg])], acc); + errcode = + getAuthAccount(&msg->message[strlen(authMesStr[authMsg])], acc); resp->has_message = true; - strlcpy(resp->message, acc, DOMAIN_SIZE+ACCOUNT_SIZE+2); + strlcpy(resp->message, acc, DOMAIN_SIZE + ACCOUNT_SIZE + 2); break; case REMACC: @@ -285,7 +295,6 @@ void fsm_msgPing(Ping *msg) { } } else { - if (msg->has_button_protection && msg->button_protection) if (!confirm(ButtonRequestType_ButtonRequest_Ping, "Ping", "%s", msg->message)) { @@ -315,7 +324,7 @@ void fsm_msgPing(Ping *msg) { layoutHome(); } -void fsm_msgChangePin(ChangePin *msg) { +void fsm_msgChangePin(ChangePin* msg) { bool removal = msg->has_remove && msg->remove; bool confirmed = false; @@ -365,11 +374,12 @@ void fsm_msgChangePin(ChangePin *msg) { layoutHome(); } -void fsm_msgChangeWipeCode(ChangeWipeCode *msg) { +void fsm_msgChangeWipeCode(ChangeWipeCode* msg) { bool removal = msg->has_remove && msg->remove; bool confirmed = false; -#ifdef ENABLE_WIPECODE // disable until oled-vuln and automated test is in place +#ifdef ENABLE_WIPECODE // disable until oled-vuln and automated test is in + // place if (removal) { if (storage_hasWipeCode()) { confirmed = confirm(ButtonRequestType_ButtonRequest_RemoveWipeCode, @@ -426,17 +436,16 @@ void fsm_msgChangeWipeCode(ChangeWipeCode *msg) { fsm_sendSuccess("Wipe code changed"); layoutHome(); -#else // ENABLE_WIPECODE +#else // ENABLE_WIPECODE (void)removal; (void)confirmed; fsm_sendSuccess("Wipe code function not available for this device"); layoutHome(); #endif - } -void fsm_msgWipeDevice(WipeDevice *msg) { +void fsm_msgWipeDevice(WipeDevice* msg) { (void)msg; if (!confirm(ButtonRequestType_ButtonRequest_WipeDevice, "Wipe Device", @@ -456,19 +465,19 @@ void fsm_msgWipeDevice(WipeDevice *msg) { layoutHome(); } -void fsm_msgFirmwareErase(FirmwareErase *msg) { +void fsm_msgFirmwareErase(FirmwareErase* msg) { (void)msg; fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in bootloader mode"); } -void fsm_msgFirmwareUpload(FirmwareUpload *msg) { +void fsm_msgFirmwareUpload(FirmwareUpload* msg) { (void)msg; fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in bootloader mode"); } -void fsm_msgGetEntropy(GetEntropy *msg) { +void fsm_msgGetEntropy(GetEntropy* msg) { if (!confirm(ButtonRequestType_ButtonRequest_GetEntropy, "Generate Entropy", "Do you want to generate and return entropy using the hardware " "RNG?")) { @@ -490,7 +499,7 @@ void fsm_msgGetEntropy(GetEntropy *msg) { layoutHome(); } -void fsm_msgLoadDevice(LoadDevice *msg) { +void fsm_msgLoadDevice(LoadDevice* msg) { CHECK_NOT_INITIALIZED if (!confirm_load_device(msg->has_node)) { @@ -518,7 +527,7 @@ void fsm_msgLoadDevice(LoadDevice *msg) { layoutHome(); } -void fsm_msgResetDevice(ResetDevice *msg) { +void fsm_msgResetDevice(ResetDevice* msg) { CHECK_NOT_INITIALIZED reset_init(msg->has_display_random && msg->display_random, @@ -533,7 +542,7 @@ void fsm_msgResetDevice(ResetDevice *msg) { msg->has_u2f_counter ? msg->u2f_counter : 0); } -void fsm_msgEntropyAck(EntropyAck *msg) { +void fsm_msgEntropyAck(EntropyAck* msg) { if (msg->has_entropy) { reset_entropy(msg->entropy.bytes, msg->entropy.size); } else { @@ -541,7 +550,7 @@ void fsm_msgEntropyAck(EntropyAck *msg) { } } -void fsm_msgCancel(Cancel *msg) { +void fsm_msgCancel(Cancel* msg) { (void)msg; recovery_cipher_abort(); signing_abort(); @@ -551,7 +560,7 @@ void fsm_msgCancel(Cancel *msg) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Aborted"); } -void fsm_msgApplySettings(ApplySettings *msg) { +void fsm_msgApplySettings(ApplySettings* msg) { if (msg->has_label) { if (!confirm(ButtonRequestType_ButtonRequest_ChangeLabel, "Change Label", "Do you want to change the label to \"%s\"?", msg->label)) { @@ -641,7 +650,7 @@ void fsm_msgApplySettings(ApplySettings *msg) { return; } -void fsm_msgRecoveryDevice(RecoveryDevice *msg) { +void fsm_msgRecoveryDevice(RecoveryDevice* msg) { if (msg->has_dry_run && msg->dry_run) { CHECK_INITIALIZED } else { @@ -660,7 +669,7 @@ void fsm_msgRecoveryDevice(RecoveryDevice *msg) { msg->has_dry_run ? msg->dry_run : false); } -void fsm_msgCharacterAck(CharacterAck *msg) { +void fsm_msgCharacterAck(CharacterAck* msg) { if (msg->has_delete && msg->del) { recovery_delete_character(); } else if (msg->has_done && msg->done) { @@ -671,7 +680,7 @@ void fsm_msgCharacterAck(CharacterAck *msg) { } } -void fsm_msgApplyPolicies(ApplyPolicies *msg) { +void fsm_msgApplyPolicies(ApplyPolicies* msg) { CHECK_PARAM(msg->policy_count > 0, "No policies provided"); for (size_t i = 0; i < msg->policy_count; ++i) { diff --git a/lib/firmware/fsm_msg_cosmos.h b/lib/firmware/fsm_msg_cosmos.h index c1450c4ca..082fe2eed 100644 --- a/lib/firmware/fsm_msg_cosmos.h +++ b/lib/firmware/fsm_msg_cosmos.h @@ -1,16 +1,16 @@ -void fsm_msgCosmosGetAddress(const CosmosGetAddress *msg) { +void fsm_msgCosmosGetAddress(const CosmosGetAddress* msg) { RESP_INIT(CosmosAddress); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, "Cosmos"); + const CoinType* coin = fsm_getCoin(true, "Cosmos"); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -69,7 +69,7 @@ void fsm_msgCosmosGetAddress(const CosmosGetAddress *msg) { layoutHome(); } -void fsm_msgCosmosSignTx(const CosmosSignTx *msg) { +void fsm_msgCosmosSignTx(const CosmosSignTx* msg) { CHECK_INITIALIZED CHECK_PIN @@ -82,7 +82,7 @@ void fsm_msgCosmosSignTx(const CosmosSignTx *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -92,8 +92,7 @@ void fsm_msgCosmosSignTx(const CosmosSignTx *msg) { RESP_INIT(CosmosMsgRequest); - if (!tendermint_signTxInit(node, (void *)msg, sizeof(CosmosSignTx), - "uatom")) { + if (!tendermint_signTxInit(node, (void*)msg, sizeof(CosmosSignTx), "uatom")) { tendermint_signAbort(); memzero(node, sizeof(*node)); fsm_sendFailure(FailureType_Failure_FirmwareError, @@ -107,16 +106,16 @@ void fsm_msgCosmosSignTx(const CosmosSignTx *msg) { layoutHome(); } -void fsm_msgCosmosMsgAck(const CosmosMsgAck *msg) { +void fsm_msgCosmosMsgAck(const CosmosMsgAck* msg) { // Confirm transaction basics CHECK_PARAM(tendermint_signingIsInited(), "Signing not in progress"); - const CoinType *coin = fsm_getCoin(true, "Cosmos"); + const CoinType* coin = fsm_getCoin(true, "Cosmos"); if (!coin) { return; } - const CosmosSignTx *sign_tx = (CosmosSignTx *)tendermint_getSignTx(); + const CosmosSignTx* sign_tx = (CosmosSignTx*)tendermint_getSignTx(); if (msg->has_send) { if (!msg->send.has_to_address || !msg->send.has_amount) { @@ -329,10 +328,10 @@ void fsm_msgCosmosMsgAck(const CosmosMsgAck *msg) { /** Confirm transaction parameters on-screen */ char amount_str[32]; bn_format_uint64(msg->rewards.amount, NULL, " ATOM", 6, 0, false, - amount_str, sizeof(amount_str)); + amount_str, sizeof(amount_str)); if (!confirm(ButtonRequestType_ButtonRequest_Other, "Claim Rewards", - "Claim %s?", amount_str)) { + "Claim %s?", amount_str)) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); @@ -340,7 +339,7 @@ void fsm_msgCosmosMsgAck(const CosmosMsgAck *msg) { } } else { if (!confirm(ButtonRequestType_ButtonRequest_Other, "Claim Rewards", - "Claim all available rewards?")) { + "Claim all available rewards?")) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); diff --git a/lib/firmware/fsm_msg_crypto.h b/lib/firmware/fsm_msg_crypto.h index 22ce35163..838b6bd05 100644 --- a/lib/firmware/fsm_msg_crypto.h +++ b/lib/firmware/fsm_msg_crypto.h @@ -1,4 +1,4 @@ -void fsm_msgCipherKeyValue(CipherKeyValue *msg) { +void fsm_msgCipherKeyValue(CipherKeyValue* msg) { CHECK_INITIALIZED CHECK_PARAM(msg->has_key, "No key provided"); @@ -9,7 +9,7 @@ void fsm_msgCipherKeyValue(CipherKeyValue *msg) { CHECK_PIN - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + const HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { @@ -30,11 +30,11 @@ void fsm_msgCipherKeyValue(CipherKeyValue *msg) { } uint8_t data[256 + 4]; - strlcpy((char *)data, msg->key, sizeof(data)); - strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); - strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); + strlcpy((char*)data, msg->key, sizeof(data)); + strlcat((char*)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); + strlcat((char*)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); - hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); + hmac_sha512(node->private_key, 32, data, strlen((char*)data), data); RESP_INIT(CipheredKeyValue); @@ -58,7 +58,7 @@ void fsm_msgCipherKeyValue(CipherKeyValue *msg) { layoutHome(); } -void fsm_msgSignIdentity(SignIdentity *msg) { +void fsm_msgSignIdentity(SignIdentity* msg) { RESP_INIT(SignedIdentity); CHECK_INITIALIZED @@ -93,11 +93,11 @@ void fsm_msgSignIdentity(SignIdentity *msg) { address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t)hash[15] << 24); - const char *curve = SECP256K1_NAME; + const char* curve = SECP256K1_NAME; if (msg->has_ecdsa_curve_name) { curve = msg->ecdsa_curve_name; } - HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); + HDNode* node = fsm_getDerivedNode(curve, address_n, 5, NULL); if (!node) { return; } @@ -119,7 +119,7 @@ void fsm_msgSignIdentity(SignIdentity *msg) { } else { uint8_t digest[64]; sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); - sha256_Raw((const uint8_t *)msg->challenge_visual, + sha256_Raw((const uint8_t*)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32); result = cryptoMessageSign(coinByName("Bitcoin"), node, InputScriptType_SPENDADDRESS, digest, 64, diff --git a/lib/firmware/fsm_msg_debug.h b/lib/firmware/fsm_msg_debug.h index d44c26109..8eaa0b312 100644 --- a/lib/firmware/fsm_msg_debug.h +++ b/lib/firmware/fsm_msg_debug.h @@ -1,5 +1,5 @@ #if DEBUG_LINK -void fsm_msgDebugLinkGetState(DebugLinkGetState *msg) { +void fsm_msgDebugLinkGetState(DebugLinkGetState* msg) { (void)msg; RESP_INIT(DebugLinkState); @@ -46,15 +46,44 @@ void fsm_msgDebugLinkGetState(DebugLinkGetState *msg) { resp->storage_hash.size = memory_storage_hash(resp->storage_hash.bytes, storage_getLocation()); + /* Render pending animations ONLY if the animation queue is active. + * Static layouts (warning screens, address displays) write directly + * to the canvas — calling animate() unconditionally overwrites them + * with stale animation frames. Only run animations when queued. */ + if (is_animating()) { + force_animation_start(); + animate(); + } + display_refresh(); + + /* Pack 256x64 grayscale canvas into 1bpp layout for screenshot capture. + * Each byte in layout holds 8 vertical pixels (LSB = top). + * Total: 256 columns x (64/8) rows = 2048 bytes. */ + { + const Canvas* c = display_canvas(); + if (c && c->buffer) { + resp->has_layout = true; + resp->layout.size = 2048; + memset(resp->layout.bytes, 0, 2048); + for (int x = 0; x < 256; x++) { + for (int y = 0; y < 64; y++) { + if (c->buffer[y * 256 + x] > 0) { + resp->layout.bytes[x + (y / 8) * 256] |= (1 << (y % 8)); + } + } + } + } + } + msg_debug_write(MessageType_MessageType_DebugLinkState, resp); } -void fsm_msgDebugLinkStop(DebugLinkStop *msg) { (void)msg; } +void fsm_msgDebugLinkStop(DebugLinkStop* msg) { (void)msg; } -void fsm_msgDebugLinkFlashDump(DebugLinkFlashDump *msg) { +void fsm_msgDebugLinkFlashDump(DebugLinkFlashDump* msg) { #ifndef EMULATOR if (!msg->has_length || - msg->length > sizeof(((DebugLinkFlashDumpResponse *)0)->data.bytes)) { + msg->length > sizeof(((DebugLinkFlashDumpResponse*)0)->data.bytes)) { #endif fsm_sendFailure(FailureType_Failure_Other, "Invalid FlashDump parameters"); layoutHome(); @@ -64,7 +93,7 @@ void fsm_msgDebugLinkFlashDump(DebugLinkFlashDump *msg) { RESP_INIT(DebugLinkFlashDumpResponse); - memcpy(resp->data.bytes, (void *)msg->address, msg->length); + memcpy(resp->data.bytes, (void*)msg->address, msg->length); resp->has_data = true; resp->data.size = msg->length; diff --git a/lib/firmware/fsm_msg_eos.h b/lib/firmware/fsm_msg_eos.h index 24d7c8c51..83a7bd01d 100644 --- a/lib/firmware/fsm_msg_eos.h +++ b/lib/firmware/fsm_msg_eos.h @@ -17,19 +17,19 @@ * along with this library. If not, see . */ -void fsm_msgEosGetPublicKey(const EosGetPublicKey *msg) { +void fsm_msgEosGetPublicKey(const EosGetPublicKey* msg) { CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, "EOS"); + const CoinType* coin = fsm_getCoin(true, "EOS"); if (!coin) return; - const curve_info *curve = get_curve_by_name(coin->curve_name); + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return; uint32_t fingerprint; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + HDNode* node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, &fingerprint); if (!node) return; hdnode_fill_public_key(node); @@ -76,7 +76,7 @@ void fsm_msgEosGetPublicKey(const EosGetPublicKey *msg) { layoutHome(); } -void fsm_msgEosSignTx(const EosSignTx *msg) { +void fsm_msgEosSignTx(const EosSignTx* msg) { CHECK_PARAM(msg->chain_id.size == 32, "Wrong chain_id size"); CHECK_PARAM(msg->has_header, "Must have transaction header"); CHECK_PARAM(msg->has_num_actions && 0 < msg->num_actions, @@ -89,7 +89,7 @@ void fsm_msgEosSignTx(const EosSignTx *msg) { CHECK_PIN - HDNode *root = fsm_getDerivedNode(SECP256K1_NAME, 0, 0, NULL); + HDNode* root = fsm_getDerivedNode(SECP256K1_NAME, 0, 0, NULL); if (!root) return; hdnode_fill_public_key(root); @@ -101,7 +101,7 @@ void fsm_msgEosSignTx(const EosSignTx *msg) { msg_write(MessageType_MessageType_EosTxActionRequest, resp); } -void fsm_msgEosTxActionAck(const EosTxActionAck *msg) { +void fsm_msgEosTxActionAck(const EosTxActionAck* msg) { CHECK_PARAM(eos_signingIsInited(), "Must call EosSignTx to initiate signing"); CHECK_PARAM(msg->has_common, "Must have common"); diff --git a/lib/firmware/fsm_msg_ethereum.h b/lib/firmware/fsm_msg_ethereum.h index 5ce121cbd..5c9827164 100644 --- a/lib/firmware/fsm_msg_ethereum.h +++ b/lib/firmware/fsm_msg_ethereum.h @@ -19,7 +19,7 @@ * along with this library. If not, see . */ -static int process_ethereum_xfer(const CoinType *coin, EthereumSignTx *msg) { +static int process_ethereum_xfer(const CoinType* coin, EthereumSignTx* msg) { if (!ethereum_isStandardERC20Transfer(msg) && msg->data_length != 0) return TXOUT_COMPILE_ERROR; @@ -33,9 +33,9 @@ static int process_ethereum_xfer(const CoinType *coin, EthereumSignTx *msg) { const uint32_t chain_id = coin->forkid; - const uint8_t *value_bytes; + const uint8_t* value_bytes; size_t value_size; - const TokenType *token; + const TokenType* token; if (ethereum_isStandardERC20Transfer(msg)) { value_bytes = msg->data_initial_chunk.bytes + 4 + 32; @@ -58,7 +58,7 @@ static int process_ethereum_xfer(const CoinType *coin, EthereumSignTx *msg) { node_str)) return TXOUT_CANCEL; - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->to_address_n, + const HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->to_address_n, msg->to_address_n_count, NULL); if (!node) return TXOUT_COMPILE_ERROR; @@ -76,12 +76,12 @@ static int process_ethereum_xfer(const CoinType *coin, EthereumSignTx *msg) { memcpy(msg->to.bytes, to_bytes, sizeof(to_bytes)); } - memzero((void *)node, sizeof(HDNode)); + memzero((void*)node, sizeof(HDNode)); return TXOUT_OK; } -static int process_ethereum_msg(EthereumSignTx *msg, bool *needs_confirm) { - const CoinType *coin = fsm_getCoin(true, ETHEREUM); +static int process_ethereum_msg(EthereumSignTx* msg, bool* needs_confirm) { + const CoinType* coin = fsm_getCoin(true, ETHEREUM); if (!coin) return TXOUT_COMPILE_ERROR; switch (msg->address_type) { @@ -95,7 +95,7 @@ static int process_ethereum_msg(EthereumSignTx *msg, bool *needs_confirm) { } } -void fsm_msgEthereumSignTx(EthereumSignTx *msg) { +void fsm_msgEthereumSignTx(EthereumSignTx* msg) { CHECK_INITIALIZED CHECK_PIN @@ -110,7 +110,7 @@ void fsm_msgEthereumSignTx(EthereumSignTx *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -118,16 +118,16 @@ void fsm_msgEthereumSignTx(EthereumSignTx *msg) { memzero(node, sizeof(*node)); } -void fsm_msgEthereumTxAck(EthereumTxAck *msg) { ethereum_signing_txack(msg); } +void fsm_msgEthereumTxAck(EthereumTxAck* msg) { ethereum_signing_txack(msg); } -void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) { +void fsm_msgEthereumGetAddress(EthereumGetAddress* msg) { RESP_INIT(EthereumAddress); CHECK_INITIALIZED CHECK_PIN - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -138,7 +138,7 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) { return; } - const CoinType *coin = NULL; + const CoinType* coin = NULL; bool rskip60 = false; uint32_t chain_id = 0; @@ -191,10 +191,10 @@ void fsm_msgEthereumGetAddress(EthereumGetAddress *msg) { layoutHome(); } -#define MSG_MAX (38*3) // 38 chars per line, three lines max -void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) { - char msgBuf[MSG_MAX+1] = {0}; - char *typeIndicator; +#define MSG_MAX (38 * 3) // 38 chars per line, three lines max +void fsm_msgEthereumSignMessage(EthereumSignMessage* msg) { + char msgBuf[MSG_MAX + 1] = {0}; + const char* typeIndicator; unsigned ctr; unsigned msgLen = 0; bool canPrint = true; @@ -210,7 +210,7 @@ void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) { if (msgLen > MSG_MAX) { msgLen = MSG_MAX; } - for (ctr=0; ctrmessage.size; ctr++) { + for (ctr = 0; ctr < msg->message.size; ctr++) { if (isprint(msg->message.bytes[ctr]) == false) { canPrint = false; break; @@ -218,14 +218,15 @@ void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) { } if (canPrint) { typeIndicator = "Sign Message"; - strncpy(msgBuf, (char *)msg->message.bytes, MSG_MAX+1); + strncpy(msgBuf, (char*)msg->message.bytes, MSG_MAX + 1); + msgBuf[MSG_MAX] = '\0'; } else { typeIndicator = "Sign Bytes"; - for (ctr=0; ctrmessage.bytes[ctr]); + for (ctr = 0; ctr < msgLen / 2; ctr++) { + snprintf(&msgBuf[2 * ctr], 3, "%02x", msg->message.bytes[ctr]); } } - + if (!confirm(ButtonRequestType_ButtonRequest_ProtectCall, _(typeIndicator), "%s", msgBuf)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); @@ -233,7 +234,7 @@ void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -242,9 +243,9 @@ void fsm_msgEthereumSignMessage(EthereumSignMessage *msg) { layoutHome(); } -void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) { - char msgBuf[MSG_MAX+1] = {0}; - char *typeIndicator; +void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage* msg) { + char msgBuf[MSG_MAX + 1] = {0}; + const char* typeIndicator; unsigned ctr; unsigned msgLen = 0; bool canPrint = true; @@ -270,7 +271,7 @@ void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) { if (msgLen > MSG_MAX) { msgLen = MSG_MAX; } - for (ctr=0; ctrmessage.bytes[ctr]) == false) { canPrint = false; break; @@ -278,15 +279,16 @@ void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) { } if (canPrint) { typeIndicator = "Message Verified"; - strncpy(msgBuf, (char *)msg->message.bytes, MSG_MAX+1); + strncpy(msgBuf, (char*)msg->message.bytes, MSG_MAX + 1); + msgBuf[MSG_MAX] = '\0'; } else { typeIndicator = "Bytes Verified"; - for (ctr=0; ctrmessage.bytes[ctr]); + for (ctr = 0; ctr < msgLen / 2; ctr++) { + snprintf(&msgBuf[2 * ctr], 3, "%02x", msg->message.bytes[ctr]); } } - if (!confirm(ButtonRequestType_ButtonRequest_Other, _(typeIndicator), - "%s", msgBuf)) { + if (!confirm(ButtonRequestType_ButtonRequest_Other, _(typeIndicator), "%s", + msgBuf)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); return; @@ -296,7 +298,7 @@ void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) { layoutHome(); } -void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) { +void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash* msg) { RESP_INIT(EthereumTypedDataSignature); CHECK_INITIALIZED @@ -305,11 +307,12 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) { if (msg->domain_separator_hash.size != 32 || (msg->has_message_hash && msg->message_hash.size != 32)) { - fsm_sendFailure(FailureType_Failure_Other, _("Invalid EIP-712 hash length")); + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid EIP-712 hash length")); return; } - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + const HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -321,34 +324,38 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) { resp->address[0] = '0'; resp->address[1] = 'x'; - ethereum_address_checksum(pubkeyhash, resp->address+2, false, 0); + ethereum_address_checksum(pubkeyhash, resp->address + 2, false, 0); // No message hash when setting primaryType="EIP712Domain" // https://ethereum-magicians.org/t/eip-712-standards-clarification-primarytype-as-domaintype/3286 - char str[64+1]; + char str[64 + 1]; int ctr; - confirm(ButtonRequestType_ButtonRequest_Other, "Verify Address", "Confirm address: %s", resp->address); + confirm(ButtonRequestType_ButtonRequest_Other, "Verify Address", + "Confirm address: %s", resp->address); - for (ctr=0; ctr<64/2; ctr++) { - snprintf(&str[2*ctr], 3, "%02x", msg->domain_separator_hash.bytes[ctr]); + for (ctr = 0; ctr < 64 / 2; ctr++) { + snprintf(&str[2 * ctr], 3, "%02x", msg->domain_separator_hash.bytes[ctr]); } - confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data domain", "Confirm hash digest: %s", str); + confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data domain", + "Confirm hash digest: %s", str); if (msg->has_message_hash) { - for (ctr=0; ctr<64/2; ctr++) { - snprintf(&str[2*ctr], 3, "%02x", msg->message_hash.bytes[ctr]); + for (ctr = 0; ctr < 64 / 2; ctr++) { + snprintf(&str[2 * ctr], 3, "%02x", msg->message_hash.bytes[ctr]); } - confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data message", "Confirm hash digest: %s", str); + confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data message", + "Confirm hash digest: %s", str); } else { - confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data message", "Confirm: No message"); + confirm(ButtonRequestType_ButtonRequest_Other, "Typed Data message", + "Confirm: No message"); } ethereum_typed_hash_sign(msg, node, resp); layoutHome(); } -void fsm_msgEthereum712TypesValues(Ethereum712TypesValues *msg) { +void fsm_msgEthereum712TypesValues(Ethereum712TypesValues* msg) { RESP_INIT(EthereumTypedDataSignature); CHECK_INITIALIZED @@ -356,11 +363,12 @@ void fsm_msgEthereum712TypesValues(Ethereum712TypesValues *msg) { CHECK_PIN if (strlen(msg->eip712types) == 0) { - fsm_sendFailure(FailureType_Failure_Other, _("Invalid EIP-712 types property string")); + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid EIP-712 types property string")); return; } - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + const HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; @@ -372,7 +380,7 @@ void fsm_msgEthereum712TypesValues(Ethereum712TypesValues *msg) { resp->address[0] = '0'; resp->address[1] = 'x'; - ethereum_address_checksum(pubkeyhash, resp->address+2, false, 0); + ethereum_address_checksum(pubkeyhash, resp->address + 2, false, 0); e712_types_values(msg, resp, node); diff --git a/lib/firmware/fsm_msg_mayachain.h b/lib/firmware/fsm_msg_mayachain.h index 359e0e6db..be9229665 100644 --- a/lib/firmware/fsm_msg_mayachain.h +++ b/lib/firmware/fsm_msg_mayachain.h @@ -1,20 +1,20 @@ -void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg) { +void fsm_msgMayachainGetAddress(const MayachainGetAddress* msg) { RESP_INIT(MayachainAddress); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, "MAYAChain"); + const CoinType* coin = fsm_getCoin(true, "MAYAChain"); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - char mainnet[] = "maya"; - char testnet[] = "smaya"; - char *pfix; + const char mainnet[] = "maya"; + const char testnet[] = "smaya"; + const char* pfix; if (!node) { return; @@ -78,7 +78,7 @@ void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg) { layoutHome(); } -void fsm_msgMayachainSignTx(const MayachainSignTx *msg) { +void fsm_msgMayachainSignTx(const MayachainSignTx* msg) { CHECK_INITIALIZED CHECK_PIN @@ -91,7 +91,7 @@ void fsm_msgMayachainSignTx(const MayachainSignTx *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -115,11 +115,12 @@ void fsm_msgMayachainSignTx(const MayachainSignTx *msg) { layoutHome(); } -void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg) { +void fsm_msgMayachainMsgAck(const MayachainMsgAck* msg) { // Confirm transaction basics // supports only 1 message ack CHECK_PARAM(mayachain_signingIsInited(), "Signing not in progress"); - if (msg->has_send && msg->send.has_to_address && msg->send.has_amount && msg->send.has_denom) { + if (msg->has_send && msg->send.has_to_address && msg->send.has_amount && + msg->send.has_denom) { // pass } else if (msg->has_deposit && msg->deposit.has_asset && msg->deposit.has_amount && msg->deposit.has_memo && @@ -133,12 +134,12 @@ void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg) { return; } - const CoinType *coin = fsm_getCoin(true, "MAYAChain"); + const CoinType* coin = fsm_getCoin(true, "MAYAChain"); if (!coin) { return; } - const MayachainSignTx *sign_tx = mayachain_getMayachainSignTx(); + const MayachainSignTx* sign_tx = mayachain_getMayachainSignTx(); if (msg->has_send) { switch (msg->send.address_type) { @@ -161,8 +162,7 @@ void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg) { break; } } - if (!mayachain_signTxUpdateMsgSend(msg->send.amount, - msg->send.to_address, + if (!mayachain_signTxUpdateMsgSend(msg->send.amount, msg->send.to_address, msg->send.denom)) { mayachain_signAbort(); fsm_sendFailure(FailureType_Failure_SyntaxError, @@ -244,8 +244,8 @@ void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg) { if (!confirm(ButtonRequestType_ButtonRequest_SignTx, node_str, "Sign this %s transaction on %s? " - "Additional network fees apply.", msg->has_send ? msg->send.denom : "CACAO", - sign_tx->chain_id)) { + "Additional network fees apply.", + msg->has_send ? msg->send.denom : "CACAO", sign_tx->chain_id)) { mayachain_signAbort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); diff --git a/lib/firmware/fsm_msg_nano.h b/lib/firmware/fsm_msg_nano.h index 67a0d82d8..a36ada416 100644 --- a/lib/firmware/fsm_msg_nano.h +++ b/lib/firmware/fsm_msg_nano.h @@ -1,16 +1,16 @@ #include "keepkey/firmware/nano.h" -void fsm_msgNanoGetAddress(NanoGetAddress *msg) { +void fsm_msgNanoGetAddress(NanoGetAddress* msg) { RESP_INIT(NanoAddress); CHECK_INITIALIZED CHECK_PIN - const char *coin_name = msg->has_coin_name ? msg->coin_name : "Nano"; - const CoinType *coin = fsm_getCoin(true, coin_name); + const char* coin_name = msg->has_coin_name ? msg->coin_name : "Nano"; + const CoinType* coin = fsm_getCoin(true, coin_name); if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + HDNode* node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); if (!node) return; hdnode_fill_public_key(node); @@ -64,16 +64,16 @@ void fsm_msgNanoGetAddress(NanoGetAddress *msg) { layoutHome(); } -void fsm_msgNanoSignTx(NanoSignTx *msg) { +void fsm_msgNanoSignTx(NanoSignTx* msg) { CHECK_INITIALIZED CHECK_PIN - const char *coin_name = msg->has_coin_name ? msg->coin_name : "Nano"; - const CoinType *coin = fsm_getCoin(true, coin_name); + const char* coin_name = msg->has_coin_name ? msg->coin_name : "Nano"; + const CoinType* coin = fsm_getCoin(true, coin_name); if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + HDNode* node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); if (!node) return; hdnode_fill_public_key(node); @@ -95,7 +95,7 @@ void fsm_msgNanoSignTx(NanoSignTx *msg) { return; } - HDNode *recip = NULL; + HDNode* recip = NULL; if (msg->link_recipient_n_count > 0) { recip = fsm_getDerivedNode(coin->curve_name, msg->link_recipient_n, msg->link_recipient_n_count, NULL); diff --git a/lib/firmware/fsm_msg_osmosis.h b/lib/firmware/fsm_msg_osmosis.h index dd246d732..c654f744d 100644 --- a/lib/firmware/fsm_msg_osmosis.h +++ b/lib/firmware/fsm_msg_osmosis.h @@ -2,22 +2,22 @@ #define OSMOSIS_PRECISION 6 #define OSMOSIS_LP_ASSET_PRECISION 18 -void fsm_msgOsmosisGetAddress(const OsmosisGetAddress *msg) { +void fsm_msgOsmosisGetAddress(const OsmosisGetAddress* msg) { RESP_INIT(OsmosisAddress); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, "Osmosis"); + const CoinType* coin = fsm_getCoin(true, "Osmosis"); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - char mainnet[] = "osmo"; - char testnet[] = "tosmo"; - char *pfix; + const char mainnet[] = "osmo"; + const char testnet[] = "tosmo"; + const char* pfix; if (!node) { return; @@ -81,7 +81,7 @@ void fsm_msgOsmosisGetAddress(const OsmosisGetAddress *msg) { layoutHome(); } -void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg) { +void fsm_msgOsmosisSignTx(const OsmosisSignTx* msg) { CHECK_INITIALIZED CHECK_PIN @@ -94,7 +94,7 @@ void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -119,16 +119,16 @@ void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg) { layoutHome(); } -void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { +void fsm_msgOsmosisMsgAck(const OsmosisMsgAck* msg) { /** Confirm transaction basics */ CHECK_PARAM(osmosis_signingIsInited(), "Signing not in progress"); - const CoinType *coin = fsm_getCoin(true, "Osmosis"); + const CoinType* coin = fsm_getCoin(true, "Osmosis"); if (!coin) { return; } - const OsmosisSignTx *sign_tx = osmosis_getOsmosisSignTx(); + const OsmosisSignTx* sign_tx = osmosis_getOsmosisSignTx(); /** Confirm required transaction parameters exist */ if (msg->has_send) { @@ -141,7 +141,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount = atof(msg->send.amount); - const char *denom = msg->send.denom; + const char* denom = msg->send.denom; if (!strcmp(msg->send.denom, "uosmo")) { amount /= pow(10, OSMOSIS_PRECISION); denom = "OSMO"; @@ -180,7 +180,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount = atof(msg->delegate.amount); - const char *denom = msg->delegate.denom; + const char* denom = msg->delegate.denom; if (!strcmp(msg->delegate.denom, "uosmo")) { amount /= pow(10, OSMOSIS_PRECISION); denom = "OSMO"; @@ -232,7 +232,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount = atof(msg->undelegate.amount); - const char *denom = msg->undelegate.denom; + const char* denom = msg->undelegate.denom; if (!strcmp(msg->undelegate.denom, "uosmo")) { amount /= pow(10, OSMOSIS_PRECISION); denom = "OSMO"; @@ -290,7 +290,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { strlcpy(insoamt, msg->lp_add.share_out_amount, sizeof(msg->lp_add.share_out_amount)); - if (base_to_precision(outsoamt, (uint8_t *)insoamt, sizeof(outsoamt), + if (base_to_precision(outsoamt, (uint8_t*)insoamt, sizeof(outsoamt), strlen(insoamt), OSMOSIS_LP_ASSET_PRECISION) < 0) { osmosis_signAbort(); fsm_sendFailure(FailureType_Failure_Other, NULL); @@ -299,14 +299,14 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount_in_max_b = atof(msg->lp_add.amount_in_max_b); - const char *denom_in_max_b = msg->lp_add.denom_in_max_b; + const char* denom_in_max_b = msg->lp_add.denom_in_max_b; if (!strcmp(msg->lp_add.denom_in_max_b, "uosmo")) { amount_in_max_b /= pow(10, OSMOSIS_PRECISION); denom_in_max_b = "OSMO"; } float amount_in_max_a = atof(msg->lp_add.amount_in_max_a); - const char *denom_in_max_a = msg->lp_add.denom_in_max_a; + const char* denom_in_max_a = msg->lp_add.denom_in_max_a; if (!strcmp(msg->lp_add.denom_in_max_a, "uosmo")) { amount_in_max_a /= pow(10, OSMOSIS_PRECISION); denom_in_max_a = "OSMO"; @@ -377,7 +377,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { strlcpy(insoamt, msg->lp_remove.share_in_amount, sizeof(msg->lp_remove.share_in_amount)); - if (base_to_precision(outsoamt, (uint8_t *)insoamt, sizeof(outsoamt), + if (base_to_precision(outsoamt, (uint8_t*)insoamt, sizeof(outsoamt), strlen(insoamt), OSMOSIS_LP_ASSET_PRECISION) < 0) { osmosis_signAbort(); fsm_sendFailure(FailureType_Failure_Other, NULL); @@ -386,14 +386,14 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount_out_min_b = atof(msg->lp_remove.amount_out_min_b); - const char *denom_out_min_b = msg->lp_remove.denom_out_min_b; + const char* denom_out_min_b = msg->lp_remove.denom_out_min_b; if (!strcmp(msg->lp_remove.denom_out_min_b, "uosmo")) { amount_out_min_b /= pow(10, OSMOSIS_PRECISION); denom_out_min_b = "OSMO"; } float amount_out_min_a = atof(msg->lp_remove.amount_out_min_a); - const char *denom_out_min_a = msg->lp_remove.denom_out_min_a; + const char* denom_out_min_a = msg->lp_remove.denom_out_min_a; if (!strcmp(msg->lp_remove.denom_out_min_a, "uosmo")) { amount_out_min_a /= pow(10, OSMOSIS_PRECISION); denom_out_min_a = "OSMO"; @@ -549,10 +549,9 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } } else if (msg->has_swap) { /** Confirm required transaction parameters exist */ - if (!msg->swap.has_sender || - !msg->swap.has_pool_id | !msg->swap.has_token_out_denom || - !msg->swap.has_token_in_denom || !msg->swap.has_token_in_amount || - !msg->swap.has_token_out_min_amount) { + if (!msg->swap.has_sender || !msg->swap.has_pool_id || + !msg->swap.has_token_out_denom || !msg->swap.has_token_in_denom || + !msg->swap.has_token_in_amount || !msg->swap.has_token_out_min_amount) { osmosis_signAbort(); fsm_sendFailure(FailureType_Failure_FirmwareError, _("Message is missing required parameters")); @@ -561,14 +560,14 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float token_in_amount = atof(msg->swap.token_in_amount); - const char *token_in_denom = msg->swap.token_in_denom; + const char* token_in_denom = msg->swap.token_in_denom; if (!strcmp(msg->swap.token_in_denom, "uosmo")) { token_in_amount /= pow(10, OSMOSIS_PRECISION); token_in_denom = "OSMO"; } float token_out_min_amount = atof(msg->swap.token_out_min_amount); - const char *token_out_denom = msg->swap.token_out_denom; + const char* token_out_denom = msg->swap.token_out_denom; if (!strcmp(msg->swap.token_out_denom, "uosmo")) { token_out_min_amount /= pow(10, OSMOSIS_PRECISION); token_out_denom = "OSMO"; @@ -619,7 +618,7 @@ void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { } float amount = atof(msg->ibc_transfer.amount); - const char *denom = msg->ibc_transfer.denom; + const char* denom = msg->ibc_transfer.denom; if (!strcmp(msg->ibc_transfer.denom, "uosmo")) { amount /= pow(10, OSMOSIS_PRECISION); denom = "OSMO"; diff --git a/lib/firmware/fsm_msg_ripple.h b/lib/firmware/fsm_msg_ripple.h index 109b22e6a..ddd25af35 100644 --- a/lib/firmware/fsm_msg_ripple.h +++ b/lib/firmware/fsm_msg_ripple.h @@ -17,19 +17,19 @@ * along with this library. If not, see . */ -void fsm_msgRippleGetAddress(const RippleGetAddress *msg) { +void fsm_msgRippleGetAddress(const RippleGetAddress* msg) { RESP_INIT(RippleAddress); CHECK_INITIALIZED CHECK_PIN - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; hdnode_fill_public_key(node); - const CoinType *coin = fsm_getCoin(true, "Ripple"); + const CoinType* coin = fsm_getCoin(true, "Ripple"); if (!ripple_getAddress(node->public_key, resp->address)) { memzero(node, sizeof(*node)); @@ -65,7 +65,7 @@ void fsm_msgRippleGetAddress(const RippleGetAddress *msg) { layoutHome(); } -void fsm_msgRippleSignTx(RippleSignTx *msg) { +void fsm_msgRippleSignTx(RippleSignTx* msg) { RESP_INIT(RippleSignedTx); CHECK_INITIALIZED @@ -76,7 +76,7 @@ void fsm_msgRippleSignTx(RippleSignTx *msg) { // TODO: handle trades and transfers - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) return; hdnode_fill_public_key(node); diff --git a/lib/firmware/fsm_msg_solana.h b/lib/firmware/fsm_msg_solana.h new file mode 100644 index 000000000..bb00eb894 --- /dev/null +++ b/lib/firmware/fsm_msg_solana.h @@ -0,0 +1,548 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Helper: Raw base58 encode (no checksum) for Solana pubkeys/addresses. + * Uses b58enc() from trezor-crypto, which is the raw base58 encoder. + * Solana addresses are raw base58-encoded 32-byte Ed25519 public keys. */ +static bool solana_base58_encode(const uint8_t* data, size_t data_len, + char* out, size_t* out_len) { + return b58enc(out, out_len, data, data_len); +} + +/* Helper: Base58-encode a 32-byte pubkey for display (full address). + * Solana base58 addresses are 32-44 chars; out must be >= 45 bytes. + * Never truncate — truncation is a spoofing vector. */ +static void solana_pubkeyToStr(const uint8_t key[SOL_PUBKEY_SIZE], char* out, + size_t out_len) { + size_t enc_len = out_len; + if (solana_base58_encode(key, SOL_PUBKEY_SIZE, out, &enc_len)) { + /* b58enc null-terminates and sets enc_len including the NUL */ + return; + } + /* Fallback to hex if base58 fails (middle-ellipsis OK for raw hex) */ + snprintf(out, out_len, "%02x%02x...%02x%02x", key[0], key[1], key[30], + key[31]); +} + +/* Confirm a single parsed instruction */ +static bool solana_confirmInstruction(const SolanaParsedInstruction* pi, + const SolanaSignTx* msg, uint8_t idx, + uint8_t total) { + char title[32]; + snprintf(title, sizeof(title), "Instr %d/%d", idx + 1, total); + + switch (pi->type) { + case SOL_INSTR_SYSTEM_TRANSFER: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + char to_str[45]; + solana_pubkeyToStr(pi->to, to_str, sizeof(to_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Send %s to %s?", amount_str, to_str); + } + + case SOL_INSTR_SYSTEM_CREATE_ACCOUNT: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Create account with %s?", amount_str); + } + + case SOL_INSTR_SYSTEM_ADVANCE_NONCE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Advance nonce account?"); + + case SOL_INSTR_SYSTEM_WITHDRAW_NONCE: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + char to_str[45]; + solana_pubkeyToStr(pi->to, to_str, sizeof(to_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Withdraw nonce %s to %s?", amount_str, to_str); + } + + case SOL_INSTR_SYSTEM_INITIALIZE_NONCE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Initialize nonce account?"); + + case SOL_INSTR_SYSTEM_AUTHORIZE_NONCE: { + char auth_str[45]; + solana_pubkeyToStr(pi->extra, auth_str, sizeof(auth_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Authorize nonce to %s?", auth_str); + } + + case SOL_INSTR_SYSTEM_ASSIGN: { + char prog_str[45]; + solana_pubkeyToStr(pi->extra, prog_str, sizeof(prog_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Assign account to %s?", prog_str); + } + + case SOL_INSTR_SYSTEM_ALLOCATE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Allocate %llu bytes?", + (unsigned long long)pi->extra_value); + + case SOL_INSTR_TOKEN_TRANSFER: + case SOL_INSTR_TOKEN_TRANSFER_CHECKED: { + char to_str[45]; + solana_pubkeyToStr(pi->to, to_str, sizeof(to_str)); + + /* Try to find token info from host-provided metadata */ + const SolanaTokenInfo* ti = NULL; + if (pi->has_mint) { + ti = solana_findTokenInfo(msg, pi->mint); + } + + if (ti && ti->has_symbol && ti->has_decimals) { + char amount_str[48]; + solana_formatTokenAmount(amount_str, sizeof(amount_str), pi->amount, + ti->symbol, (uint8_t)ti->decimals); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Send %s to %s?", amount_str, to_str); + } else { + char amount_str[32]; + snprintf(amount_str, sizeof(amount_str), "%llu tokens", + (unsigned long long)pi->amount); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Send %s to %s?", amount_str, to_str); + } + } + + case SOL_INSTR_TOKEN_APPROVE: { + char to_str[45]; + solana_pubkeyToStr(pi->to, to_str, sizeof(to_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Approve %llu tokens to %s?", + (unsigned long long)pi->amount, to_str); + } + + case SOL_INSTR_TOKEN_REVOKE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Revoke token approval?"); + + case SOL_INSTR_TOKEN_SET_AUTHORITY: { + char auth_str[45]; + solana_pubkeyToStr(pi->extra, auth_str, sizeof(auth_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set token authority to %s?", auth_str); + } + + case SOL_INSTR_TOKEN_MINT_TO: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Mint %llu tokens?", (unsigned long long)pi->amount); + + case SOL_INSTR_TOKEN_BURN: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Burn %llu tokens?", (unsigned long long)pi->amount); + + case SOL_INSTR_TOKEN_CLOSE_ACCOUNT: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Close token account?"); + + case SOL_INSTR_TOKEN_FREEZE_ACCOUNT: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Freeze token account?"); + + case SOL_INSTR_TOKEN_THAW_ACCOUNT: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Thaw token account?"); + + case SOL_INSTR_TOKEN_SYNC_NATIVE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Sync wrapped SOL?"); + + case SOL_INSTR_STAKE_DELEGATE: { + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Delegate stake?"); + } + + case SOL_INSTR_STAKE_WITHDRAW: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Withdraw %s from stake?", amount_str); + } + + case SOL_INSTR_STAKE_AUTHORIZE: { + char auth_str[45]; + solana_pubkeyToStr(pi->extra, auth_str, sizeof(auth_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Authorize stake to %s?", auth_str); + } + + case SOL_INSTR_STAKE_SPLIT: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Split stake by %s?", amount_str); + } + + case SOL_INSTR_STAKE_DEACTIVATE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Deactivate stake?"); + + case SOL_INSTR_STAKE_MERGE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Merge stake accounts?"); + + case SOL_INSTR_VOTE_AUTHORIZE: { + char auth_str[45]; + solana_pubkeyToStr(pi->extra, auth_str, sizeof(auth_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Authorize vote to %s?", auth_str); + } + + case SOL_INSTR_VOTE_WITHDRAW: { + char amount_str[32]; + solana_formatAmount(amount_str, sizeof(amount_str), pi->lamports); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Withdraw vote %s?", amount_str); + } + + case SOL_INSTR_VOTE_UPDATE_VALIDATOR: { + char validator_str[45]; + solana_pubkeyToStr(pi->extra, validator_str, sizeof(validator_str)); + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Update validator to %s?", validator_str); + } + + case SOL_INSTR_VOTE_UPDATE_COMMISSION: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set vote commission to %u%%?", pi->extra_u8); + + case SOL_INSTR_ATA_CREATE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Create associated token account?"); + + case SOL_INSTR_COMPUTE_BUDGET_HEAP_FRAME: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set heap frame to %llu bytes?", + (unsigned long long)pi->extra_value); + + case SOL_INSTR_COMPUTE_BUDGET_UNIT_LIMIT: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set compute unit limit to %llu?", + (unsigned long long)pi->extra_value); + + case SOL_INSTR_COMPUTE_BUDGET_UNIT_PRICE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set compute unit price to %llu?", + (unsigned long long)pi->extra_value); + + case SOL_INSTR_COMPUTE_BUDGET_LOADED_ACCOUNTS_SIZE: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Set loaded account data to %llu bytes?", + (unsigned long long)pi->extra_value); + + case SOL_INSTR_MEMO: + return confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, title, + "Memo attached"); + + case SOL_INSTR_UNKNOWN: + default: { + char prog_str[45]; + solana_pubkeyToStr(pi->program_id, prog_str, sizeof(prog_str)); + return confirm(ButtonRequestType_ButtonRequest_SignTx, title, + "Unknown instruction to program %s. " + "Cannot verify contents.", + prog_str); + } + } +} + +/* Validate Solana derivation path: m/44'/501'/account'[/change'] */ +static bool solana_pathIsStandard(const uint32_t* path, size_t count) { + if (count < 3 || count > 4) return false; + if (path[0] != (0x80000000 | 44)) return false; /* 44' */ + if (path[1] != (0x80000000 | 501)) return false; /* 501' */ + for (size_t i = 2; i < count; i++) { + if (!(path[i] & 0x80000000)) return false; /* must be hardened */ + } + return true; +} + +/* Verify derived pubkey appears in tx accounts[0..num_required_sigs) */ +static bool solana_signerInTx(const uint8_t* pubkey, const SolanaParsedTx* tx) { + for (uint8_t i = 0; i < tx->num_required_sigs && i < tx->num_accounts; i++) { + if (memcmp(pubkey, tx->accounts[i], SOL_PUBKEY_SIZE) == 0) return true; + } + return false; +} + +void fsm_msgSolanaGetAddress(const SolanaGetAddress* msg) { + RESP_INIT(SolanaAddress); + + CHECK_INITIALIZED + CHECK_PIN + + /* Path validation: warn on non-standard derivation */ + if (!solana_pathIsStandard(msg->address_n, msg->address_n_count)) { + if (!confirm(ButtonRequestType_ButtonRequest_Other, "WARNING", + "Non-standard Solana derivation path. Continue?")) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + HDNode* node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + /* Solana address = raw Base58 of the 32-byte Ed25519 public key. + * node->public_key is 33 bytes (0x00 prefix + 32 bytes for Ed25519). + * Use b58enc() for raw base58 encoding (no checksum). */ + char address[45]; + size_t addr_len = sizeof(address); + if (solana_base58_encode(node->public_key + 1, SOL_PUBKEY_SIZE, address, + &addr_len)) { + resp->has_address = true; + strncpy(resp->address, address, sizeof(resp->address) - 1); + } else { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Address encoding failed")); + layoutHome(); + return; + } + + if (msg->has_show_display && msg->show_display) { + if (!confirm_ethereum_address("Solana", resp->address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Show address cancelled")); + layoutHome(); + return; + } + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_SolanaAddress, resp); + layoutHome(); +} + +void fsm_msgSolanaSignTx(const SolanaSignTx* msg) { + RESP_INIT(SolanaSignedTx); + + CHECK_INITIALIZED + CHECK_PIN + + if (!msg->has_raw_tx || msg->raw_tx.size == 0) { + fsm_sendFailure(FailureType_Failure_SyntaxError, _("Missing raw_tx")); + layoutHome(); + return; + } + + /* Path validation: warn on non-standard derivation */ + if (!solana_pathIsStandard(msg->address_n, msg->address_n_count)) { + if (!confirm(ButtonRequestType_ButtonRequest_Other, "WARNING", + "Non-standard Solana derivation path. Continue?")) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + HDNode* node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + /* Classify transaction for verified vs opaque signing UX */ + SolanaParsedTx parsed; + SolanaTxReview tx_review = + solana_inspectTx(msg->raw_tx.bytes, msg->raw_tx.size, &parsed); + + /* Signer verification: derived key must be a required signer. + * For verified txs this is mandatory. For opaque txs we still check + * when we were able to parse the header (num_accounts > 0). */ + if (tx_review == SOL_TX_REVIEW_VERIFIED || + (tx_review == SOL_TX_REVIEW_OPAQUE && parsed.num_accounts > 0)) { + if (!solana_signerInTx(node->public_key + 1, &parsed)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, + _("Derived key is not a signer for this tx")); + layoutHome(); + return; + } + } + + if (tx_review == SOL_TX_REVIEW_VERIFIED) { + /* Per-instruction confirmation for fully verified messages */ + for (uint8_t i = 0; i < parsed.num_instructions; i++) { + if (!solana_confirmInstruction(&parsed.instructions[i], msg, i, + parsed.num_instructions)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled")); + layoutHome(); + return; + } + } + } else if (tx_review == SOL_TX_REVIEW_OPAQUE) { + /* Unsupported or opaque message: allow explicit blind-sign only. */ + if (!storage_isPolicyEnabled("AdvancedMode")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, + _("Enable AdvancedMode to blind-sign")); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_SignTx, "Blind Sign", + "Sign unverified Solana transaction? " + "The device cannot fully verify the contents.")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled")); + layoutHome(); + return; + } + } else { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_SyntaxError, + _("Malformed Solana transaction")); + layoutHome(); + return; + } + + /* Final confirmation */ + if (!confirm(ButtonRequestType_ButtonRequest_SignTx, "Solana", + "Sign this Solana transaction?")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled")); + layoutHome(); + return; + } + + if (!solana_signTx(node, msg, resp)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Signing failed")); + layoutHome(); + return; + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_SolanaSignedTx, resp); + layoutHome(); +} + +void fsm_msgSolanaSignMessage(const SolanaSignMessage* msg) { + RESP_INIT(SolanaMessageSignature); + + CHECK_INITIALIZED + CHECK_PIN + + if (!msg->has_message || msg->message.size == 0) { + fsm_sendFailure(FailureType_Failure_SyntaxError, _("Missing message")); + layoutHome(); + return; + } + + /* AdvancedMode gate: Solana message signing has no domain separation. + * A signed message is indistinguishable from a signed transaction on + * the Solana network (both are raw Ed25519 over arbitrary bytes). + * A malicious dApp could craft a message that is also a valid tx. + * See: https://github.com/trezor/trezor-firmware/issues/4371 + * Require AdvancedMode to proceed — same gate as ETH blind-signing. */ + if (!storage_isPolicyEnabled("AdvancedMode")) { + (void)review(ButtonRequestType_ButtonRequest_Other, "Blocked", + "Solana message signing is experimental. " + "Enable AdvancedMode in device settings."); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Message signing disabled by policy")); + layoutHome(); + return; + } + + /* Path validation: warn on non-standard derivation */ + if (!solana_pathIsStandard(msg->address_n, msg->address_n_count)) { + if (!confirm(ButtonRequestType_ButtonRequest_Other, "WARNING", + "Non-standard Solana derivation path. Continue?")) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + HDNode* node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + /* Always require on-device confirmation (matches Ethereum behavior). + * Display message content if printable, hex preview otherwise. */ + { + char msgBuf[129] = {0}; + const char* typeLabel; + bool printable = true; + for (unsigned i = 0; i < msg->message.size; i++) { + if (msg->message.bytes[i] < 0x20 || msg->message.bytes[i] > 0x7e) { + printable = false; + break; + } + } + if (printable && msg->message.size <= sizeof(msgBuf) - 1) { + typeLabel = "Sign Message"; + memcpy(msgBuf, msg->message.bytes, msg->message.size); + msgBuf[msg->message.size] = '\0'; + } else { + typeLabel = "Sign Bytes"; + /* Show hex preview (up to 64 hex chars = 32 bytes) */ + unsigned show = msg->message.size; + if (show > 32) show = 32; + for (unsigned i = 0; i < show; i++) { + snprintf(&msgBuf[2 * i], 3, "%02x", msg->message.bytes[i]); + } + msgBuf[2 * show] = '\0'; + if (msg->message.size > 32) { + snprintf(&msgBuf[64], sizeof(msgBuf) - 64, "... (%u bytes)", + (unsigned)msg->message.size); + } + } + if (!confirm(ButtonRequestType_ButtonRequest_ProtectCall, _(typeLabel), + "%s", msgBuf)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled")); + layoutHome(); + return; + } + } + + /* Ed25519 sign */ + uint8_t sig[SOL_SIG_SIZE]; + ed25519_sign(msg->message.bytes, msg->message.size, node->private_key, + node->public_key + 1, sig); + + resp->has_signature = true; + resp->signature.size = SOL_SIG_SIZE; + memcpy(resp->signature.bytes, sig, SOL_SIG_SIZE); + + resp->has_public_key = true; + resp->public_key.size = SOL_PUBKEY_SIZE; + memcpy(resp->public_key.bytes, node->public_key + 1, SOL_PUBKEY_SIZE); + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_SolanaMessageSignature, resp); + layoutHome(); +} diff --git a/lib/firmware/fsm_msg_tendermint.h b/lib/firmware/fsm_msg_tendermint.h index 285793acd..42a031e38 100644 --- a/lib/firmware/fsm_msg_tendermint.h +++ b/lib/firmware/fsm_msg_tendermint.h @@ -1,16 +1,16 @@ -void fsm_msgTendermintGetAddress(const TendermintGetAddress *msg) { +void fsm_msgTendermintGetAddress(const TendermintGetAddress* msg) { RESP_INIT(TendermintAddress); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, msg->chain_name); + const CoinType* coin = fsm_getCoin(true, msg->chain_name); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -19,7 +19,7 @@ void fsm_msgTendermintGetAddress(const TendermintGetAddress *msg) { hdnode_fill_public_key(node); if (!tendermint_getAddress(node, msg->chain_name, resp->address)) { - memzero((void *)node, sizeof(*node)); + memzero((void*)node, sizeof(*node)); fsm_sendFailure(FailureType_Failure_FirmwareError, _("Can't encode address")); layoutHome(); @@ -69,12 +69,12 @@ void fsm_msgTendermintGetAddress(const TendermintGetAddress *msg) { layoutHome(); } -void fsm_msgTendermintSignTx(const TendermintSignTx *msg) { +void fsm_msgTendermintSignTx(const TendermintSignTx* msg) { CHECK_INITIALIZED CHECK_PIN if (!msg->has_account_number || !msg->has_chain_id || !msg->has_fee_amount || - !msg->has_gas || !msg->has_sequence || !msg->has_chain_name || + !msg->has_gas || !msg->has_sequence || !msg->has_chain_name || !msg->has_denom || !msg->has_message_type_prefix) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_SyntaxError, @@ -82,8 +82,8 @@ void fsm_msgTendermintSignTx(const TendermintSignTx *msg) { layoutHome(); return; } - - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -93,7 +93,8 @@ void fsm_msgTendermintSignTx(const TendermintSignTx *msg) { RESP_INIT(TendermintMsgRequest); - if (!tendermint_signTxInit(node, (void *)msg, sizeof(TendermintSignTx), msg->denom)) { + if (!tendermint_signTxInit(node, (void*)msg, sizeof(TendermintSignTx), + msg->denom)) { tendermint_signAbort(); memzero(node, sizeof(*node)); fsm_sendFailure(FailureType_Failure_FirmwareError, @@ -107,37 +108,39 @@ void fsm_msgTendermintSignTx(const TendermintSignTx *msg) { layoutHome(); } -void fsm_msgTendermintMsgAck(const TendermintMsgAck *msg) { +void fsm_msgTendermintMsgAck(const TendermintMsgAck* msg) { // Confirm transaction basics CHECK_PARAM(tendermint_signingIsInited(), "Signing not in progress"); if (!msg->has_send || !msg->send.has_to_address || !msg->send.has_amount) { tendermint_signAbort(); // 8 + ^14 + 13 + 1 = 36 char failmsg[40]; - snprintf(failmsg, sizeof(failmsg), "Invalid %s Message Type", msg->chain_name); + snprintf(failmsg, sizeof(failmsg), "Invalid %s Message Type", + msg->chain_name); - fsm_sendFailure(FailureType_Failure_FirmwareError, - _(failmsg)); + fsm_sendFailure(FailureType_Failure_FirmwareError, _(failmsg)); layoutHome(); return; } - const CoinType *coin = fsm_getCoin(true, msg->chain_name); - if (!coin ||!coin->has_coin_shortcut || !coin->has_decimals) { + const CoinType* coin = fsm_getCoin(true, msg->chain_name); + if (!coin || !coin->has_coin_shortcut || !coin->has_decimals) { return; } - const TendermintSignTx *sign_tx = (TendermintSignTx *)tendermint_getSignTx(); + const TendermintSignTx* sign_tx = (TendermintSignTx*)tendermint_getSignTx(); switch (msg->send.address_type) { case OutputAddressType_TRANSFER: default: { char amount_str[32]; - char suffix[sizeof(coin->coin_shortcut) + 1]; // sizeof(coin->coin_shortcut) includes space for the terminator + char suffix[sizeof(coin->coin_shortcut) + + 1]; // sizeof(coin->coin_shortcut) includes space for the + // terminator strlcpy(suffix, " ", sizeof(suffix)); strlcat(suffix, coin->coin_shortcut, sizeof(suffix)); - bn_format_uint64(msg->send.amount, NULL, suffix, coin->decimals, 0, false, amount_str, - sizeof(amount_str)); + bn_format_uint64(msg->send.amount, NULL, suffix, coin->decimals, 0, false, + amount_str, sizeof(amount_str)); if (!confirm_transaction_output( ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, msg->send.to_address)) { @@ -151,8 +154,9 @@ void fsm_msgTendermintMsgAck(const TendermintMsgAck *msg) { } } - if (!tendermint_signTxUpdateMsgSend(msg->send.amount, msg->send.to_address, msg->chain_name, - msg->denom, msg->message_type_prefix)) { + if (!tendermint_signTxUpdateMsgSend(msg->send.amount, msg->send.to_address, + msg->chain_name, msg->denom, + msg->message_type_prefix)) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_SyntaxError, "Failed to include send message in transaction"); @@ -187,7 +191,8 @@ void fsm_msgTendermintMsgAck(const TendermintMsgAck *msg) { if (!confirm(ButtonRequestType_ButtonRequest_SignTx, node_str, "Sign %s transaction on %s? " "It includes a fee of %" PRIu32 " %s and %" PRIu32 " gas.", - msg->chain_name, sign_tx->chain_id, sign_tx->fee_amount, msg->denom, sign_tx->gas)) { + msg->chain_name, sign_tx->chain_id, sign_tx->fee_amount, + msg->denom, sign_tx->gas)) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); @@ -196,7 +201,8 @@ void fsm_msgTendermintMsgAck(const TendermintMsgAck *msg) { RESP_INIT(TendermintSignedTx); - if (!tendermint_signTxFinalize(resp->public_key.bytes, resp->signature.bytes)) { + if (!tendermint_signTxFinalize(resp->public_key.bytes, + resp->signature.bytes)) { tendermint_signAbort(); fsm_sendFailure(FailureType_Failure_SyntaxError, "Failed to finalize signature"); diff --git a/lib/firmware/fsm_msg_thorchain.h b/lib/firmware/fsm_msg_thorchain.h index 6a5c38fe2..801af7872 100644 --- a/lib/firmware/fsm_msg_thorchain.h +++ b/lib/firmware/fsm_msg_thorchain.h @@ -1,20 +1,20 @@ -void fsm_msgThorchainGetAddress(const ThorchainGetAddress *msg) { +void fsm_msgThorchainGetAddress(const ThorchainGetAddress* msg) { RESP_INIT(ThorchainAddress); CHECK_INITIALIZED CHECK_PIN - const CoinType *coin = fsm_getCoin(true, "THORChain"); + const CoinType* coin = fsm_getCoin(true, "THORChain"); if (!coin) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - char mainnet[] = "thor"; - char testnet[] = "tthor"; - char *pfix; + const char mainnet[] = "thor"; + const char testnet[] = "tthor"; + const char* pfix; if (!node) { return; @@ -78,7 +78,7 @@ void fsm_msgThorchainGetAddress(const ThorchainGetAddress *msg) { layoutHome(); } -void fsm_msgThorchainSignTx(const ThorchainSignTx *msg) { +void fsm_msgThorchainSignTx(const ThorchainSignTx* msg) { CHECK_INITIALIZED CHECK_PIN @@ -91,7 +91,7 @@ void fsm_msgThorchainSignTx(const ThorchainSignTx *msg) { return; } - HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); if (!node) { return; @@ -115,15 +115,16 @@ void fsm_msgThorchainSignTx(const ThorchainSignTx *msg) { layoutHome(); } -void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { +void fsm_msgThorchainMsgAck(const ThorchainMsgAck* msg) { // Confirm transaction basics // supports only 1 message ack CHECK_PARAM(thorchain_signingIsInited(), "Signing not in progress"); if (msg->has_send && msg->send.has_to_address && msg->send.has_amount) { // pass - } else if (msg->has_deposit && msg->deposit.has_asset && msg->deposit.has_amount && - msg->deposit.has_memo && msg->deposit.has_signer) { - // pass + } else if (msg->has_deposit && msg->deposit.has_asset && + msg->deposit.has_amount && msg->deposit.has_memo && + msg->deposit.has_signer) { + // pass } else { thorchain_signAbort(); fsm_sendFailure(FailureType_Failure_FirmwareError, @@ -132,20 +133,20 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { return; } - const CoinType *coin = fsm_getCoin(true, "THORChain"); + const CoinType* coin = fsm_getCoin(true, "THORChain"); if (!coin) { return; } - const ThorchainSignTx *sign_tx = thorchain_getThorchainSignTx(); + const ThorchainSignTx* sign_tx = thorchain_getThorchainSignTx(); if (msg->has_send) { switch (msg->send.address_type) { case OutputAddressType_TRANSFER: default: { char amount_str[32]; - bn_format_uint64(msg->send.amount, NULL, " RUNE", 8, 0, false, amount_str, - sizeof(amount_str)); + bn_format_uint64(msg->send.amount, NULL, " RUNE", 8, 0, false, + amount_str, sizeof(amount_str)); if (!confirm_transaction_output( ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, msg->send.to_address)) { @@ -158,7 +159,8 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { break; } } - if (!thorchain_signTxUpdateMsgSend(msg->send.amount, msg->send.to_address)) { + if (!thorchain_signTxUpdateMsgSend(msg->send.amount, + msg->send.to_address)) { thorchain_signAbort(); fsm_sendFailure(FailureType_Failure_SyntaxError, "Failed to include send message in transaction"); @@ -171,8 +173,8 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { char asset_str[21]; asset_str[0] = ' '; strlcpy(&(asset_str[1]), msg->deposit.asset, sizeof(asset_str) - 1); - bn_format_uint64(msg->deposit.amount, NULL, asset_str, 8, 0, false, amount_str, - sizeof(amount_str)); + bn_format_uint64(msg->deposit.amount, NULL, asset_str, 8, 0, false, + amount_str, sizeof(amount_str)); if (!confirm_transaction_output( ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, msg->deposit.signer)) { @@ -184,10 +186,11 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { if (msg->deposit.has_memo) { // See if we can parse the memo - if (!thorchain_parseConfirmMemo(msg->deposit.memo, sizeof(msg->deposit.memo))) { + if (!thorchain_parseConfirmMemo(msg->deposit.memo, + sizeof(msg->deposit.memo))) { // Memo not recognizable, ask to confirm it - if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), "%s", - msg->deposit.memo)) { + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), + "%s", msg->deposit.memo)) { thorchain_signAbort(); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); @@ -205,7 +208,6 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { } } - if (!thorchain_signingIsFinished()) { RESP_INIT(ThorchainMsgRequest); msg_write(MessageType_MessageType_ThorchainMsgRequest, resp); @@ -213,7 +215,8 @@ void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg) { } if (sign_tx->has_memo && !msg->deposit.has_memo) { - // See if we can parse the tx memo. This memo ignored if deposit msg has memo + // See if we can parse the tx memo. This memo ignored if deposit msg has + // memo if (!thorchain_parseConfirmMemo(sign_tx->memo, sizeof(sign_tx->memo))) { // Memo not recognizable, ask to confirm it if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), "%s", diff --git a/lib/firmware/fsm_msg_ton.h b/lib/firmware/fsm_msg_ton.h new file mode 100644 index 000000000..a0384b4f0 --- /dev/null +++ b/lib/firmware/fsm_msg_ton.h @@ -0,0 +1,150 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +void fsm_msgTonGetAddress(const TonGetAddress* msg) { + RESP_INIT(TonAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + // Validate path: m/44'/607'/... (all hardened for TON) + if (msg->address_n_count < 2 || msg->address_n[0] != (0x80000000 | 44) || + msg->address_n[1] != (0x80000000 | 607)) { + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid TON path (expected m/44'/607'/...)")); + layoutHome(); + return; + } + + // Derive node using Ed25519 curve + HDNode* node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + // Extract TON-specific parameters with defaults + bool bounceable = msg->has_bounceable ? msg->bounceable : true; + bool testnet = msg->has_testnet ? msg->testnet : false; + int32_t workchain = msg->has_workchain ? msg->workchain : 0; + + // Get TON address from public key (Base64 URL-safe encoding) + char address[MAX_ADDR_SIZE]; + char raw_address[MAX_ADDR_SIZE]; + if (!ton_get_address(&node->public_key[1], bounceable, testnet, workchain, + address, sizeof(address), raw_address, + sizeof(raw_address))) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Can't encode address")); + layoutHome(); + return; + } + + resp->has_address = true; + strlcpy(resp->address, address, sizeof(resp->address)); + resp->has_raw_address = true; + strlcpy(resp->raw_address, raw_address, sizeof(resp->raw_address)); + + // Show address on display if requested + if (msg->has_show_display && msg->show_display) { + char node_str[NODE_STRING_LENGTH]; + if (!bip32_path_to_string(node_str, sizeof(node_str), msg->address_n, + msg->address_n_count)) { + memset(node_str, 0, sizeof(node_str)); + } + + if (!confirm_ethereum_address(node_str, resp->address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Show address cancelled")); + layoutHome(); + return; + } + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_TonAddress, resp); + layoutHome(); +} + +void fsm_msgTonSignTx(TonSignTx* msg) { + RESP_INIT(TonSignedTx); + + CHECK_INITIALIZED + + CHECK_PIN + + // Validate path: m/44'/607'/... + if (msg->address_n_count < 2 || msg->address_n[0] != (0x80000000 | 44) || + msg->address_n[1] != (0x80000000 | 607)) { + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid TON path (expected m/44'/607'/...)")); + layoutHome(); + return; + } + + // Derive node using Ed25519 curve + HDNode* node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + if (!msg->has_raw_tx || msg->raw_tx.size == 0) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Missing transaction data")); + layoutHome(); + return; + } + + bool needs_confirm = true; + + // Display transaction details if available + if (needs_confirm && msg->has_to_address && msg->has_amount) { + char amount_str[32]; + ton_formatAmount(amount_str, sizeof(amount_str), msg->amount); + + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Send", + "Send %s TON to %s?", amount_str, msg->to_address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + } + + if (!confirm(ButtonRequestType_ButtonRequest_SignTx, "Transaction", + "Really sign this TON transaction?")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + + // Sign the transaction with Ed25519 + if (!ton_signTx(node, msg, resp)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("TON signing failed")); + layoutHome(); + return; + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_TonSignedTx, resp); + layoutHome(); +} diff --git a/lib/firmware/fsm_msg_tron.h b/lib/firmware/fsm_msg_tron.h new file mode 100644 index 000000000..849603225 --- /dev/null +++ b/lib/firmware/fsm_msg_tron.h @@ -0,0 +1,140 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +void fsm_msgTronGetAddress(const TronGetAddress* msg) { + RESP_INIT(TronAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + // Validate path: m/44'/195'/... + if (msg->address_n_count < 3 || msg->address_n[0] != (0x80000000 | 44) || + msg->address_n[1] != (0x80000000 | 195)) { + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid TRON path (expected m/44'/195'/...)")); + layoutHome(); + return; + } + + // Derive node using secp256k1 curve + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + // Get TRON address from public key (Base58Check with prefix 'T') + char address[MAX_ADDR_SIZE]; + if (!tron_getAddress(node->public_key, address, sizeof(address))) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Address derivation failed")); + layoutHome(); + return; + } + + resp->has_address = true; + strlcpy(resp->address, address, sizeof(resp->address)); + + // Show address on display if requested + if (msg->has_show_display && msg->show_display) { + char node_str[NODE_STRING_LENGTH]; + if (!bip32_path_to_string(node_str, sizeof(node_str), msg->address_n, + msg->address_n_count)) { + memset(node_str, 0, sizeof(node_str)); + } + + if (!confirm_ethereum_address(node_str, resp->address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Show address cancelled")); + layoutHome(); + return; + } + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_TronAddress, resp); + layoutHome(); +} + +void fsm_msgTronSignTx(TronSignTx* msg) { + RESP_INIT(TronSignedTx); + + CHECK_INITIALIZED + + CHECK_PIN + + // Validate path: m/44'/195'/... + if (msg->address_n_count < 3 || msg->address_n[0] != (0x80000000 | 44) || + msg->address_n[1] != (0x80000000 | 195)) { + fsm_sendFailure(FailureType_Failure_Other, + _("Invalid TRON path (expected m/44'/195'/...)")); + layoutHome(); + return; + } + + // Derive node using secp256k1 curve + HDNode* node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + if (!msg->has_raw_data || msg->raw_data.size == 0) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("Missing transaction data")); + layoutHome(); + return; + } + + bool needs_confirm = true; + + // Display transaction details if available + if (needs_confirm && msg->has_to_address && msg->has_amount) { + char amount_str[32]; + tron_formatAmount(amount_str, sizeof(amount_str), msg->amount); + + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Send", + "Send %s TRX to %s?", amount_str, msg->to_address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + } + + if (!confirm(ButtonRequestType_ButtonRequest_SignTx, "Transaction", + "Really sign this TRON transaction?")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + + // Sign the transaction with secp256k1 + if (!tron_signTx(node, msg, resp)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_Other, _("TRON signing failed")); + layoutHome(); + return; + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_TronSignedTx, resp); + layoutHome(); +} diff --git a/lib/firmware/home_sm.c b/lib/firmware/home_sm.c index 107b1105a..ab5f4c702 100644 --- a/lib/firmware/home_sm.c +++ b/lib/firmware/home_sm.c @@ -31,8 +31,8 @@ static HomeState home_state = AT_HOME; static uint32_t idle_time = 0; static void layoutLockedState(void) { - const Font *font = get_body_font(); - const char *state = + const Font* font = get_body_font(); + const char* state = (!storage_hasPin() || session_isPinCached()) ? "\x02" : "\x03"; DrawableParams sp; sp.x = 2; diff --git a/lib/firmware/mayachain.c b/lib/firmware/mayachain.c index e0b8a08ee..a4613ad11 100644 --- a/lib/firmware/mayachain.c +++ b/lib/firmware/mayachain.c @@ -1,8 +1,8 @@ /* * This file is part of the Keepkey project. * - * Copyright (C) 2021 Shapeshift - * + * Copyright (C) 2021 Shapeshift + * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -38,9 +38,9 @@ static uint32_t msgs_remaining; static MayachainSignTx msg; static bool testnet; -const MayachainSignTx *mayachain_getMayachainSignTx(void) { return &msg; } +const MayachainSignTx* mayachain_getMayachainSignTx(void) { return &msg; } -bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg) { +bool mayachain_signTxInit(const HDNode* _node, const MayachainSignTx* _msg) { initialized = true; msgs_remaining = _msg->msg_count; testnet = false; @@ -61,13 +61,13 @@ bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg) { // Each segment guaranteed to be less than or equal to 64 bytes // 19 + ^20 + 1 = ^40 if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), - "{\"account_number\":\"%" PRIu64 "\"", - msg.account_number)) + "{\"account_number\":\"%" PRIu64 "\"", + msg.account_number)) return false; // - const char *const chainid_prefix = ",\"chain_id\":\""; - sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + const char* const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t*)chainid_prefix, strlen(chainid_prefix)); tendermint_sha256UpdateEscaped(&ctx, msg.chain_id, strlen(msg.chain_id)); // 30 + ^10 + 19 = ^59 @@ -82,24 +82,23 @@ bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg) { ",\"gas\":\"%" PRIu32 "\"}", msg.gas); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); if (msg.has_memo) { tendermint_sha256UpdateEscaped(&ctx, msg.memo, strlen(msg.memo)); } // 10 - sha256_Update(&ctx, (uint8_t *)"\",\"msgs\":[", 10); + sha256_Update(&ctx, (uint8_t*)"\",\"msgs\":[", 10); return success; } bool mayachain_signTxUpdateMsgSend(const uint64_t amount, - const char *to_address, - const char *denom) { - char mainnetp[] = "maya"; - char testnetp[] = "smaya"; - char *pfix; + const char* to_address, const char* denom) { + const char mainnetp[] = "maya"; + const char testnetp[] = "smaya"; + const char* pfix; char buffer[64 + 1]; size_t decoded_len; @@ -122,13 +121,14 @@ bool mayachain_signTxUpdateMsgSend(const uint64_t amount, bool success = true; - const char *const prelude = "{\"type\":\"mayachain/MsgSend\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + const char* const prelude = "{\"type\":\"mayachain/MsgSend\",\"value\":{"; + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); // 21 + ^20 + 11 + ^69 + 3 = ^124 - success &= tendermint_snprintf( - &ctx, buffer, sizeof(buffer), - "\"amount\":[{\"amount\":\"%" PRIu64 "\",\"denom\":\"%s\"}]", amount, denom); + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"amount\":[{\"amount\":\"%" PRIu64 + "\",\"denom\":\"%s\"}]", + amount, denom); // 17 + 45 + 1 = 63 success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), @@ -142,27 +142,26 @@ bool mayachain_signTxUpdateMsgSend(const uint64_t amount, return success; } -bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit *depmsg) { +bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit* depmsg) { char buffer[64 + 1]; bool success = true; - const char *const prelude = "{\"type\":\"mayachain/MsgDeposit\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + const char* const prelude = "{\"type\":\"mayachain/MsgDeposit\",\"value\":{"; + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); // 20 + ^20 + 1 = ^41 - success &= tendermint_snprintf( - &ctx, buffer, sizeof(buffer), - "\"coins\":[{\"amount\":\"%" PRIu64 "\"", depmsg->amount); + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"coins\":[{\"amount\":\"%" PRIu64 "\"", + depmsg->amount); // 10 + ^20 + 3 = ^33 - success &= tendermint_snprintf( - &ctx, buffer, sizeof(buffer), - ",\"asset\":\"%s\"}]", depmsg->asset); + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"asset\":\"%s\"}]", depmsg->asset); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); tendermint_sha256UpdateEscaped(&ctx, depmsg->memo, strlen(depmsg->memo)); // 17 + 45 + 1 = 63 @@ -173,9 +172,9 @@ bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit *depmsg) { return success; } -bool mayachain_signTxFinalize(uint8_t *public_key, uint8_t *signature) { +bool mayachain_signTxFinalize(uint8_t* public_key, uint8_t* signature) { char buffer[64 + 1]; - + // 16 + ^20 = ^36 if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), "],\"sequence\":\"%" PRIu64 "\"}", msg.sequence)) @@ -201,7 +200,7 @@ void mayachain_signAbort(void) { memzero(&node, sizeof(node)); } -bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { +bool mayachain_parseConfirmMemo(const char* swapStr, size_t size) { /* Input: swapStr is candidate mayachain data size is the size of swapStr (<= 256) @@ -215,9 +214,9 @@ bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { Swap transactions can be indicated by "SWAP" or "s" or "=" */ - char *parseTokPtrs[7] = {NULL, NULL, NULL, NULL, + char* parseTokPtrs[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; // we can parse up to 7 tokens - char *tok; + char* tok; char memoBuf[256]; uint16_t ctr; @@ -265,7 +264,8 @@ bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Mayachain swap", "Confirm swap asset %s\n on chain %s", parseTokPtrs[2], parseTokPtrs[1])) { + "Mayachain swap", "Confirm swap asset %s\n on chain %s", + parseTokPtrs[2], parseTokPtrs[1])) { return false; } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, @@ -285,16 +285,18 @@ bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { if (tok != NULL) { // add liquidity pool address parseTokPtrs[3] = tok; - } + } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Mayachain add liquidity", "Confirm add asset %s\n on chain %s pool", - parseTokPtrs[2], parseTokPtrs[1])) { + "Mayachain add liquidity", + "Confirm add asset %s\n on chain %s pool", parseTokPtrs[2], + parseTokPtrs[1])) { return false; } if (tok != NULL) { if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Mayachain add liquidity", "Confirm to %s", parseTokPtrs[3])) { + "Mayachain add liquidity", "Confirm to %s", + parseTokPtrs[3])) { return false; } } @@ -302,19 +304,20 @@ bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { } // Check for withdraw liquidity - else if (strncmp(parseTokPtrs[0], "WITHDRAW", 8) == 0 || strncmp(parseTokPtrs[0], "wd", 2) == 0 || - *parseTokPtrs[0] == '-') { + else if (strncmp(parseTokPtrs[0], "WITHDRAW", 8) == 0 || + strncmp(parseTokPtrs[0], "wd", 2) == 0 || *parseTokPtrs[0] == '-') { if (tok != NULL) { // add liquidity pool address parseTokPtrs[3] = tok; } else { - return false; // malformed memo + return false; // malformed memo } float percent = (float)(atoi(parseTokPtrs[3])) / 100; if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Mayachain withdraw liquidity", "Confirm withdraw %3.2f%% of asset %s on chain %s", - percent, parseTokPtrs[2], parseTokPtrs[1])) { + "Mayachain withdraw liquidity", + "Confirm withdraw %3.2f%% of asset %s on chain %s", percent, + parseTokPtrs[2], parseTokPtrs[1])) { return false; } return true; diff --git a/lib/firmware/messagemap.def b/lib/firmware/messagemap.def index e8e374386..18de20abf 100644 --- a/lib/firmware/messagemap.def +++ b/lib/firmware/messagemap.def @@ -132,6 +132,29 @@ MSG_OUT(MessageType_MessageType_MayachainMsgRequest, MayachainMsgRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_MayachainSignedTx, MayachainSignedTx, NO_PROCESS_FUNC) + /* TRON */ + MSG_IN(MessageType_MessageType_TronGetAddress, TronGetAddress, fsm_msgTronGetAddress) + MSG_IN(MessageType_MessageType_TronSignTx, TronSignTx, fsm_msgTronSignTx) + + MSG_OUT(MessageType_MessageType_TronAddress, TronAddress, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_TronSignedTx, TronSignedTx, NO_PROCESS_FUNC) + + /* TON */ + MSG_IN(MessageType_MessageType_TonGetAddress, TonGetAddress, fsm_msgTonGetAddress) + MSG_IN(MessageType_MessageType_TonSignTx, TonSignTx, fsm_msgTonSignTx) + + MSG_OUT(MessageType_MessageType_TonAddress, TonAddress, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_TonSignedTx, TonSignedTx, NO_PROCESS_FUNC) + + /* Solana */ + MSG_IN(MessageType_MessageType_SolanaGetAddress, SolanaGetAddress, fsm_msgSolanaGetAddress) + MSG_IN(MessageType_MessageType_SolanaSignTx, SolanaSignTx, fsm_msgSolanaSignTx) + MSG_IN(MessageType_MessageType_SolanaSignMessage, SolanaSignMessage, fsm_msgSolanaSignMessage) + + MSG_OUT(MessageType_MessageType_SolanaAddress, SolanaAddress, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_SolanaSignedTx, SolanaSignedTx, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_SolanaMessageSignature, SolanaMessageSignature, NO_PROCESS_FUNC) + #if DEBUG_LINK /* Debug Messages */ DEBUG_IN(MessageType_MessageType_DebugLinkDecision, DebugLinkDecision, NO_PROCESS_FUNC) diff --git a/lib/firmware/nano.c b/lib/firmware/nano.c index 9d18dafbd..d6addbde6 100644 --- a/lib/firmware/nano.c +++ b/lib/firmware/nano.c @@ -27,7 +27,7 @@ static uint8_t balance_be[16]; static bool is_send = true; static char representative_address[MAX_NANO_ADDR_SIZE]; static char recipient_address[MAX_NANO_ADDR_SIZE]; -static const CoinType *coin = NULL; +static const CoinType* coin = NULL; static uint8_t const NANO_BLOCK_HASH_PREAMBLE[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -35,7 +35,7 @@ static uint8_t const NANO_BLOCK_HASH_PREAMBLE[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, }; -bool nano_path_mismatched(const CoinType *_coin, const uint32_t *address_n, +bool nano_path_mismatched(const CoinType* _coin, const uint32_t* address_n, const uint32_t address_n_count) { // m/44' : BIP44-like path // m / purpose' / bip44_account_path' / account' @@ -48,8 +48,8 @@ bool nano_path_mismatched(const CoinType *_coin, const uint32_t *address_n, return mismatch; } -bool nano_bip32_to_string(char *node_str, size_t len, const CoinType *_coin, - const uint32_t *address_n, +bool nano_bip32_to_string(char* node_str, size_t len, const CoinType* _coin, + const uint32_t* address_n, const size_t address_n_count) { if (address_n_count != 3) return false; @@ -79,10 +79,10 @@ void nano_hash_block_data(const uint8_t _account_pk[32], blake2b_Final(&ctx, _out_hash, 32); } -const char *nano_getKnownRepName(const char *addr) { +const char* nano_getKnownRepName(const char* addr) { static const struct { - const char *name; - const char *addr; + const char* name; + const char* addr; } reps[] = { {"@meltingice ", "xrb_1x7biz69cem95oo7gxkrw6kzhfywq4x5dupw4z1bdzkb74dk9kpxwzjbdhhs"}, @@ -129,7 +129,7 @@ const char *nano_getKnownRepName(const char *addr) { return NULL; } -void nano_truncateAddress(const CoinType *_coin, char *str) { +void nano_truncateAddress(const CoinType* _coin, char* str) { const size_t prefix_len = strlen(_coin->nanoaddr_prefix); const size_t str_len = strlen(str); @@ -157,8 +157,8 @@ void nano_signingAbort(void) { is_send = true; } -bool nano_signingInit(const NanoSignTx *msg, const HDNode *node, - const CoinType *_coin) { +bool nano_signingInit(const NanoSignTx* msg, const HDNode* node, + const CoinType* _coin) { nano_signingAbort(); memcpy(account_pk, &node->public_key[1], sizeof(account_pk)); @@ -187,7 +187,7 @@ bool nano_signingInit(const NanoSignTx *msg, const HDNode *node, return !invalid; } -bool nano_parentHash(const NanoSignTx *msg) { +bool nano_parentHash(const NanoSignTx* msg) { if (!msg->has_parent_block) return true; if (msg->parent_block.has_parent_hash) { @@ -217,7 +217,7 @@ bool nano_parentHash(const NanoSignTx *msg) { return true; } -bool nano_currentHash(const NanoSignTx *msg, const HDNode *recip) { +bool nano_currentHash(const NanoSignTx* msg, const HDNode* recip) { if (msg->has_link_hash) { memcpy(link, msg->link_hash.bytes, sizeof(link)); } else if (msg->link_recipient_n_count > 0) { @@ -246,7 +246,7 @@ bool nano_currentHash(const NanoSignTx *msg, const HDNode *recip) { } /// Some additional sanity checks now that balance values are known -bool nano_sanityCheck(const NanoSignTx *msg) { +bool nano_sanityCheck(const NanoSignTx* msg) { memset(recipient_address, 0, sizeof(recipient_address)); strlcpy(representative_address, msg->representative, @@ -293,7 +293,7 @@ bool nano_sanityCheck(const NanoSignTx *msg) { return !invalid; } -bool nano_signTx(const NanoSignTx *msg, HDNode *node, NanoSignedTx *resp) { +bool nano_signTx(const NanoSignTx* msg, HDNode* node, NanoSignedTx* resp) { // Determine what type of prompt to display bool needs_confirm = true; bool is_transfer = false; @@ -310,7 +310,7 @@ bool nano_signTx(const NanoSignTx *msg, HDNode *node, NanoSignedTx *resp) { if (needs_confirm) { if (strlen(representative_address) > 0) { - const char *rep_name = nano_getKnownRepName(representative_address); + const char* rep_name = nano_getKnownRepName(representative_address); if (rep_name) nano_truncateAddress(coin, representative_address); if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, "Representative", "Set account representative to\n%s%s?", diff --git a/lib/firmware/osmosis.c b/lib/firmware/osmosis.c index a9ac61037..6d0657257 100644 --- a/lib/firmware/osmosis.c +++ b/lib/firmware/osmosis.c @@ -38,9 +38,9 @@ static uint32_t msgs_remaining; static OsmosisSignTx msg; static bool testnet; -const OsmosisSignTx *osmosis_getOsmosisSignTx(void) { return &msg; } +const OsmosisSignTx* osmosis_getOsmosisSignTx(void) { return &msg; } -bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_msg) { +bool osmosis_signTxInit(const HDNode* _node, const OsmosisSignTx* _msg) { initialized = true; msgs_remaining = _msg->msg_count; testnet = false; @@ -66,8 +66,8 @@ bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_msg) { return false; // - const char *const chainid_prefix = ",\"chain_id\":\""; - sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + const char* const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t*)chainid_prefix, strlen(chainid_prefix)); tendermint_sha256UpdateEscaped(&ctx, msg.chain_id, strlen(msg.chain_id)); @@ -83,23 +83,23 @@ bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_msg) { ",\"gas\":\"%" PRIu32 "\"}", msg.gas); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); if (msg.has_memo) { tendermint_sha256UpdateEscaped(&ctx, msg.memo, strlen(msg.memo)); } // 10 - sha256_Update(&ctx, (uint8_t *)"\",\"msgs\":[", 10); + sha256_Update(&ctx, (uint8_t*)"\",\"msgs\":[", 10); return success; } -bool osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgSend(const char* amount, const char* to_address) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[64 + 1]; size_t decoded_len; @@ -122,8 +122,8 @@ bool osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address) { bool success = true; - const char *const prelude = "{\"type\":\"cosmos-sdk/MsgSend\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + const char* const prelude = "{\"type\":\"cosmos-sdk/MsgSend\",\"value\":{"; + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); // 21 + ^20 + 19 = ^60 success &= tendermint_snprintf( @@ -142,13 +142,13 @@ bool osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address) { return success; } -bool osmosis_signTxUpdateMsgDelegate(const char *amount, - const char *delegator_address, - const char *validator_address, - const char *denom) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgDelegate(const char* amount, + const char* delegator_address, + const char* validator_address, + const char* denom) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[128] = {0}; size_t decoded_len; @@ -203,13 +203,13 @@ bool osmosis_signTxUpdateMsgDelegate(const char *amount, return success; } -bool osmosis_signTxUpdateMsgUndelegate(const char *amount, - const char *delegator_address, - const char *validator_address, - const char *denom) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgUndelegate(const char* amount, + const char* delegator_address, + const char* validator_address, + const char* denom) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[128] = {0}; size_t decoded_len; @@ -263,14 +263,14 @@ bool osmosis_signTxUpdateMsgUndelegate(const char *amount, return success; } -bool osmosis_signTxUpdateMsgRedelegate(const char *amount, - const char *delegator_address, - const char *validator_src_address, - const char *validator_dst_address, - const char *denom) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgRedelegate(const char* amount, + const char* delegator_address, + const char* validator_src_address, + const char* validator_dst_address, + const char* denom) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[128] = {0}; size_t decoded_len; @@ -331,19 +331,19 @@ bool osmosis_signTxUpdateMsgRedelegate(const char *amount, return success; } -bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char *sender, - const char *share_out_amount, - const char *amount_in_max_a, - const char *denom_in_max_a, - const char *amount_in_max_b, - const char *denom_in_max_b) { +bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char* sender, + const char* share_out_amount, + const char* amount_in_max_a, + const char* denom_in_max_a, + const char* amount_in_max_b, + const char* denom_in_max_b) { char buffer[96 + 1] = {0}; bool success = true; - const char *const prelude = + const char* const prelude = "{\"type\":\"osmosis/gamm/join-pool\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"pool_id\":\"%" PRIu64 "\",", pool_id); @@ -378,19 +378,19 @@ bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char *sender, return success; } -bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char *sender, - const char *share_out_amount, - const char *amount_out_min_a, - const char *denom_out_min_a, - const char *amount_out_min_b, - const char *denom_out_min_b) { +bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char* sender, + const char* share_out_amount, + const char* amount_out_min_a, + const char* denom_out_min_a, + const char* amount_out_min_b, + const char* denom_out_min_b) { char buffer[96 + 1] = {0}; bool success = true; - const char *const prelude = + const char* const prelude = "{\"type\":\"osmosis/gamm/exit-pool\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"pool_id\":\"%" PRIu64 "\",", pool_id); @@ -425,11 +425,11 @@ bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char *sender, return success; } -bool osmosis_signTxUpdateMsgRewards(const char *delegator_address, - const char *validator_address) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgRewards(const char* delegator_address, + const char* validator_address) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[128] = {0}; size_t decoded_len; @@ -478,16 +478,16 @@ bool osmosis_signTxUpdateMsgRewards(const char *delegator_address, return success; } -bool osmosis_signTxUpdateMsgIBCTransfer(const char *amount, const char *sender, - const char *receiver, - const char *source_channel, - const char *source_port, - const char *revision_number, - const char *revision_height, - const char *denom) { - char mainnetp[] = "osmo"; - char testnetp[] = "tosmo"; - char *pfix; +bool osmosis_signTxUpdateMsgIBCTransfer(const char* amount, const char* sender, + const char* receiver, + const char* source_channel, + const char* source_port, + const char* revision_number, + const char* revision_height, + const char* denom) { + const char mainnetp[] = "osmo"; + const char testnetp[] = "tosmo"; + const char* pfix; char buffer[128] = {0}; size_t decoded_len; @@ -559,20 +559,20 @@ bool osmosis_signTxUpdateMsgIBCTransfer(const char *amount, const char *sender, } bool osmosis_signTxUpdateMsgSwap(const uint64_t pool_id, - const char *token_out_denom, - const char *sender, - const char *token_in_amount, - const char *token_in_denom, - const char *token_out_min_amount) { + const char* token_out_denom, + const char* sender, + const char* token_in_amount, + const char* token_in_denom, + const char* token_out_min_amount) { char buffer[96 + 1] = {0}; // TODO: add testnet support bool success = true; - const char *const prelude = + const char* const prelude = "{\"type\":\"osmosis/gamm/swap-exact-amount-in\","; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); success &= tendermint_snprintf( &ctx, buffer, sizeof(buffer), @@ -600,7 +600,7 @@ bool osmosis_signTxUpdateMsgSwap(const uint64_t pool_id, return success; } -bool osmosis_signTxFinalize(uint8_t *public_key, uint8_t *signature) { +bool osmosis_signTxFinalize(uint8_t* public_key, uint8_t* signature) { char buffer[128] = {0}; // 14 + ^20 + 2 = ^36 diff --git a/lib/firmware/passphrase_sm.c b/lib/firmware/passphrase_sm.c index e78e0fe9d..37c30bcef 100644 --- a/lib/firmware/passphrase_sm.c +++ b/lib/firmware/passphrase_sm.c @@ -53,7 +53,7 @@ static void send_passphrase_request(void) { * OUTPUT * none */ -static void wait_for_passphrase_ack(PassphraseInfo *passphrase_info) { +static void wait_for_passphrase_ack(PassphraseInfo* passphrase_info) { /* Listen for tiny messages */ uint8_t msg_tiny_buf[MSG_TINY_BFR_SZ]; uint16_t tiny_msg = wait_for_tiny_msg(msg_tiny_buf); @@ -62,7 +62,7 @@ static void wait_for_passphrase_ack(PassphraseInfo *passphrase_info) { /* Check for standard passphrase ack */ case MessageType_MessageType_PassphraseAck: passphrase_info->passphrase_ack_msg = PASSPHRASE_ACK_RECEIVED; - PassphraseAck *ppa = (PassphraseAck *)msg_tiny_buf; + PassphraseAck* ppa = (PassphraseAck*)msg_tiny_buf; strlcpy(passphrase_info->passphrase, ppa->passphrase, PASSPHRASE_BUF); break; @@ -91,8 +91,8 @@ static void wait_for_passphrase_ack(PassphraseInfo *passphrase_info) { * OUTPUT * none */ -static void run_passphrase_state(PassphraseState *passphrase_state, - PassphraseInfo *passphrase_info) { +static void run_passphrase_state(PassphraseState* passphrase_state, + PassphraseInfo* passphrase_info) { switch (*passphrase_state) { /* Send passphrase request */ case PASSPHRASE_REQUEST: @@ -128,7 +128,7 @@ static void run_passphrase_state(PassphraseState *passphrase_state, * OUTPUT * true/false whether passphrase was received */ -static bool passphrase_request(PassphraseInfo *passphrase_info) { +static bool passphrase_request(PassphraseInfo* passphrase_info) { bool ret = false; reset_msg_stack = false; PassphraseState passphrase_state = PASSPHRASE_REQUEST; @@ -144,12 +144,10 @@ static bool passphrase_request(PassphraseInfo *passphrase_info) { /* Check for passphrase cancel */ if (passphrase_info->passphrase_ack_msg == PASSPHRASE_ACK_RECEIVED) { - review(ButtonRequestType_ButtonRequest_Other, - "passphrase confirmation", - "If this is wrong, unplug/replug Keepkey:" - "%51s", - passphrase_info->passphrase - ); + review(ButtonRequestType_ButtonRequest_Other, "passphrase confirmation", + "If this is wrong, unplug/replug Keepkey:" + "%51s", + passphrase_info->passphrase); ret = true; } else { if (passphrase_info->passphrase_ack_msg == PASSPHRASE_ACK_CANCEL_BY_INIT) { diff --git a/lib/firmware/pin_sm.c b/lib/firmware/pin_sm.c index bfd91c8dd..525a0ace3 100644 --- a/lib/firmware/pin_sm.c +++ b/lib/firmware/pin_sm.c @@ -47,7 +47,7 @@ static void send_pin_request(PinMatrixRequestType type) { } /// Capture PIN entry from user over USB port -static void check_for_pin_ack(PINInfo *pin_info) { +static void check_for_pin_ack(PINInfo* pin_info) { /* Listen for tiny messages */ uint8_t msg_tiny_buf[MSG_TINY_BFR_SZ]; uint16_t tiny_msg; @@ -57,7 +57,7 @@ static void check_for_pin_ack(PINInfo *pin_info) { switch (tiny_msg) { case MessageType_MessageType_PinMatrixAck: pin_info->pin_ack_msg = PIN_ACK_RECEIVED; - PinMatrixAck *pma = (PinMatrixAck *)msg_tiny_buf; + PinMatrixAck* pma = (PinMatrixAck*)msg_tiny_buf; strlcpy(pin_info->pin, pma->pin, PIN_BUF); break; @@ -73,7 +73,7 @@ static void check_for_pin_ack(PINInfo *pin_info) { #if DEBUG_LINK case MessageType_MessageType_DebugLinkGetState: - call_msg_debug_link_get_state_handler((DebugLinkGetState *)msg_tiny_buf); + call_msg_debug_link_get_state_handler((DebugLinkGetState*)msg_tiny_buf); break; #endif @@ -86,7 +86,7 @@ static void check_for_pin_ack(PINInfo *pin_info) { /// Request and receive PIN from user over USB port /// \param pin_state state of request /// \param pin_info buffer for user PIN -static void run_pin_state(PINState *pin_state, PINInfo *pin_info) { +static void run_pin_state(PINState* pin_state, PINInfo* pin_info) { switch (*pin_state) { /* Send PIN request */ case PIN_REQUEST: @@ -117,7 +117,7 @@ static void run_pin_state(PINState *pin_state, PINInfo *pin_info) { /// Make sure that PIN is at least one digit and a char from 1 to 9. /// \returns true iff the pin is in the correct format -static bool check_pin_input(PINInfo *pin_info) { +static bool check_pin_input(PINInfo* pin_info) { bool ret = true; /* Check that PIN is at least 1 digit and no more than 9 */ @@ -138,7 +138,7 @@ static bool check_pin_input(PINInfo *pin_info) { } /// Decode user PIN entry. -static void decode_pin(PINInfo *pin_info) { +static void decode_pin(PINInfo* pin_info) { for (uint32_t i = 0; i < strlen(pin_info->pin); i++) { int32_t j = pin_info->pin[i] - '1'; @@ -153,7 +153,7 @@ static void decode_pin(PINInfo *pin_info) { /// Request user for PIN entry. /// \param prompt Text to display for the user along with PIN matrix. /// \returns true iff the pin was received. -static bool pin_request(const char *prompt, PINInfo *pin_info) { +static bool pin_request(const char* prompt, PINInfo* pin_info) { bool ret = false; reset_msg_stack = false; PINState pin_state = PIN_REQUEST; @@ -211,7 +211,7 @@ static void pin_fail_wait_handler(void) { (total_wait - remaining_wait) * 1000 / total_wait); } -bool pin_protect(const char *prompt) { +bool pin_protect(const char* prompt) { if (!storage_hasPin()) { return true; } @@ -268,16 +268,14 @@ bool pin_protect(const char *prompt) { } bool pin_protect_cached(void) { - if (session_isPinCached()) { - return true; - } + if (session_isPinCached()) { + return true; + } - return pin_protect("Enter\nYour PIN"); + return pin_protect("Enter\nYour PIN"); } -bool pin_protect_uncached(void) { - return pin_protect("Enter\nYour PIN"); -} +bool pin_protect_uncached(void) { return pin_protect("Enter\nYour PIN"); } bool change_pin(void) { PINInfo pin_info_first, pin_info_second; @@ -329,5 +327,5 @@ bool change_wipe_code(void) { #if DEBUG_LINK /// Gets randomized PIN matrix -const char *get_pin_matrix(void) { return pin_matrix; } +const char* get_pin_matrix(void) { return pin_matrix; } #endif \ No newline at end of file diff --git a/lib/firmware/policy.c b/lib/firmware/policy.c index e7729ff13..7a7f32a64 100644 --- a/lib/firmware/policy.c +++ b/lib/firmware/policy.c @@ -34,8 +34,8 @@ * OUTPUT * integer determining whether operation was succesful */ -int run_policy_compile_output(const CoinType *coin, const HDNode *root, - void *vin, void *vout, bool needs_confirm) { +int run_policy_compile_output(const CoinType* coin, const HDNode* root, + void* vin, void* vout, bool needs_confirm) { if (isAccountBased(coin->coin_name)) return TXOUT_OK; /* Bitcoin, Clones, Forks */ @@ -43,6 +43,6 @@ int run_policy_compile_output(const CoinType *coin, const HDNode *root, return TXOUT_COMPILE_ERROR; } - return compile_output(coin, root, (TxOutputType *)vin, - (TxOutputBinType *)vout, needs_confirm); + return compile_output(coin, root, (TxOutputType*)vin, (TxOutputBinType*)vout, + needs_confirm); } diff --git a/lib/firmware/recovery_cipher.c b/lib/firmware/recovery_cipher.c index 9677db5c6..0db3d1616 100644 --- a/lib/firmware/recovery_cipher.c +++ b/lib/firmware/recovery_cipher.c @@ -59,7 +59,7 @@ static char auto_completed_word[CURRENT_WORD_BUF]; #endif static uint32_t get_current_word_pos(void); -static void get_current_word(char *current_word); +static void get_current_word(char* current_word); void recovery_cipher_abort(void) { if (!dry_run) { @@ -81,7 +81,7 @@ void recovery_cipher_abort(void) { /// /// \param current_word[in] The string to format. /// \param auto_completed[in] Whether to format as an auto completed word. -static void format_current_word(uint32_t word_pos, const char *current_word, +static void format_current_word(uint32_t word_pos, const char* current_word, bool auto_completed, char (*formatted_word)[CURRENT_WORD_BUF + 10]) { uint32_t word_num = word_pos + 1; @@ -113,7 +113,7 @@ static void format_current_word(uint32_t word_pos, const char *current_word, * position in mnemonic */ static uint32_t get_current_word_pos(void) { - char *pos_num = strchr(mnemonic, ' '); + const char* pos_num = strchr(mnemonic, ' '); uint32_t word_pos = 0; while (pos_num != NULL) { @@ -126,8 +126,8 @@ static uint32_t get_current_word_pos(void) { /// \returns the current word being entered by parsing the mnemonic thus far /// \param current_word[out] Array to populate with current word. -static void get_current_word(char *current_word) { - char *pos = strrchr(mnemonic, ' '); +static void get_current_word(char* current_word) { + char* pos = strrchr(mnemonic, ' '); if (pos) { pos++; @@ -140,13 +140,13 @@ static void get_current_word(char *current_word) { _Static_assert(BIP39_WORDLIST_PADDED, "bip39 wordlist must be padded to 9 characters"); -bool exact_str_match(const char *str1, const char *str2, uint32_t len) { +bool exact_str_match(const char* str1, const char* str2, uint32_t len) { volatile uint32_t match = 0; // Access through volatile ptrs to prevent compiler optimizations that // might leak timing information. - const char volatile *volatile str1_v = str1; - const char volatile *volatile str2_v = str2; + const char volatile* volatile str1_v = str1; + const char volatile* volatile str2_v = str2; for (uint32_t i = 0; i < len && i < CURRENT_WORD_BUF && i <= BIP39_MAX_WORD_LEN; i++) { @@ -162,11 +162,11 @@ bool exact_str_match(const char *str1, const char *str2, uint32_t len) { #define BIP39_MAX_WORD_LEN 8 -bool attempt_auto_complete(char *partial_word) { +bool attempt_auto_complete(char* partial_word) { // Do lookup through volatile pointers to prevent the compiler from // optimizing this loop into something that can leak timing information. - const char *const volatile *volatile words = - (const char *const volatile *)wordlist; + const char* const volatile* volatile words = + (const char* const volatile*)wordlist; uint32_t partial_word_len = strlen(partial_word), match = 0, found = 0; bool precise_match = false; @@ -244,8 +244,8 @@ bool attempt_auto_complete(char *partial_word) { * none */ void recovery_cipher_init(uint32_t _word_count, bool passphrase_protection, - bool pin_protection, const char *language, - const char *label, bool _enforce_wordlist, + bool pin_protection, const char* language, + const char* label, bool _enforce_wordlist, uint32_t _auto_lock_delay_ms, uint32_t _u2f_counter, bool _dry_run) { // If word_count is known ahead of time, enforce that it's one of the standard @@ -389,7 +389,7 @@ void next_character(void) { * OUTPUT * none */ -void recovery_character(const char *character) { +void recovery_character(const char* character) { if (!awaiting_character || !recovery_started) { recovery_cipher_abort(); fsm_sendFailure(FailureType_Failure_UnexpectedMessage, @@ -406,7 +406,7 @@ void recovery_character(const char *character) { return; } - char *pos = strchr(cipher, character[0]); + const char* pos = strchr(cipher, character[0]); // If not a space and not a legitmate cipher character, send failure. if (character[0] != ' ' && pos == NULL) { @@ -561,7 +561,7 @@ void recovery_cipher_finalize(void) { memzero(temp_word, sizeof(temp_word)); /* Attempt to autocomplete each word */ - char *tok = strtok(mnemonic, " "); + char* tok = strtok(mnemonic, " "); while (tok) { strlcpy(temp_word, tok, CURRENT_WORD_BUF); @@ -588,7 +588,8 @@ void recovery_cipher_finalize(void) { } /* Truncate additional space at the end */ - new_mnemonic[MAX(1u, strnlen(new_mnemonic, sizeof(new_mnemonic))) - 1u] = '\0'; + new_mnemonic[MAX(1u, strnlen(new_mnemonic, sizeof(new_mnemonic))) - 1u] = + '\0'; if (!dry_run && (!enforce_wordlist || mnemonic_check(new_mnemonic))) { storage_setMnemonic(new_mnemonic); memzero(new_mnemonic, sizeof(new_mnemonic)); @@ -646,7 +647,7 @@ void recovery_cipher_finalize(void) { * OUTPUT * current cipher */ -const char *recovery_get_cipher(void) { return cipher; } +const char* recovery_get_cipher(void) { return cipher; } /* * recovery_get_auto_completed_word() - Gets last auto completed word @@ -656,7 +657,7 @@ const char *recovery_get_cipher(void) { return cipher; } * OUTPUT * last auto completed word */ -const char *recovery_get_auto_completed_word(void) { +const char* recovery_get_auto_completed_word(void) { return auto_completed_word; } #endif diff --git a/lib/firmware/reset.c b/lib/firmware/reset.c index b9317ead5..362b3b49e 100644 --- a/lib/firmware/reset.c +++ b/lib/firmware/reset.c @@ -45,7 +45,7 @@ static bool no_backup; void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, - const char *language, const char *label, bool _no_backup, + const char* language, const char* label, bool _no_backup, uint32_t _auto_lock_delay_ms, uint32_t _u2f_counter) { if (_strength != 128 && _strength != 192 && _strength != 256) { fsm_sendFailure( @@ -127,132 +127,133 @@ void reset_init(bool display_random, uint32_t _strength, awaiting_entropy = true; } -void reset_entropy(const uint8_t *ext_entropy, uint32_t len) -{ - if(!awaiting_entropy) - { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Reset mode")); - return; - } +void reset_entropy(const uint8_t* ext_entropy, uint32_t len) { + if (!awaiting_entropy) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in Reset mode")); + return; + } + + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, int_entropy, 32); + sha256_Update(&ctx, ext_entropy, len); + sha256_Final(&ctx, int_entropy); - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, int_entropy, 32); - sha256_Update(&ctx, ext_entropy, len); - sha256_Final(&ctx, int_entropy); + const char* temp_mnemonic = mnemonic_from_data(int_entropy, strength / 8); - const char *temp_mnemonic = mnemonic_from_data(int_entropy, strength / 8); + memzero(int_entropy, sizeof(int_entropy)); + awaiting_entropy = false; - memzero(int_entropy, sizeof(int_entropy)); - awaiting_entropy = false; + if (no_backup) { + storage_setNoBackup(); + storage_setMnemonic(temp_mnemonic); + mnemonic_clear(); + storage_commit(); + fsm_sendSuccess(_("Device reset")); + goto exit; + } else { + if (!confirm(ButtonRequestType_ButtonRequest_Other, + _("Recovery Seed Backup"), + "This recovery seed will only be shown ONCE. " + "Please write it down carefully,\n" + "and DO NOT share it with anyone. ")) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Reset cancelled")); + storage_reset(); + layoutHome(); + return; + } + } - if (no_backup) { - storage_setNoBackup(); - storage_setMnemonic(temp_mnemonic); - mnemonic_clear(); - storage_commit(); - fsm_sendSuccess(_("Device reset")); + /* + * Format mnemonic for user review + */ + uint32_t word_count = 0, page_count = 0; + static char CONFIDENTIAL tokened_mnemonic[TOKENED_MNEMONIC_BUF]; + static char CONFIDENTIAL + mnemonic_by_screen[MAX_PAGES][MNEMONIC_BY_SCREEN_BUF]; + static char CONFIDENTIAL + formatted_mnemonic[MAX_PAGES][FORMATTED_MNEMONIC_BUF]; + static char CONFIDENTIAL mnemonic_display[FORMATTED_MNEMONIC_BUF]; + static char CONFIDENTIAL formatted_word[MAX_WORD_LEN + ADDITIONAL_WORD_PAD]; + + strlcpy(tokened_mnemonic, temp_mnemonic, TOKENED_MNEMONIC_BUF); + + char* tok = strtok(tokened_mnemonic, " "); + + while (tok) { + snprintf(formatted_word, MAX_WORD_LEN + ADDITIONAL_WORD_PAD, + (word_count & 1) ? "%lu.%s\n" : "%lu.%s", + (unsigned long)(word_count + 1), tok); + + /* Check that we have enough room on display to show word */ + snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s %s", + formatted_mnemonic[page_count], formatted_word); + + if (calc_str_line(get_body_font(), mnemonic_display, BODY_WIDTH) > 3) { + page_count++; + + if (MAX_PAGES <= page_count) { + fsm_sendFailure(FailureType_Failure_Other, + _("Too many pages of mnemonic words")); + storage_reset(); goto exit; - } else { - if (!confirm(ButtonRequestType_ButtonRequest_Other, _("Recovery Seed Backup"), - "This recovery seed will only be shown ONCE. " - "Please write it down carefully,\n" - "and DO NOT share it with anyone. ")) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Reset cancelled")); - storage_reset(); - layoutHome(); - return; - } + } + + snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s %s", + formatted_mnemonic[page_count], formatted_word); } - /* - * Format mnemonic for user review - */ - uint32_t word_count = 0, page_count = 0; - static char CONFIDENTIAL tokened_mnemonic[TOKENED_MNEMONIC_BUF]; - static char CONFIDENTIAL mnemonic_by_screen[MAX_PAGES][MNEMONIC_BY_SCREEN_BUF]; - static char CONFIDENTIAL formatted_mnemonic[MAX_PAGES][FORMATTED_MNEMONIC_BUF]; - static char CONFIDENTIAL mnemonic_display[FORMATTED_MNEMONIC_BUF]; - static char CONFIDENTIAL formatted_word[MAX_WORD_LEN + ADDITIONAL_WORD_PAD]; - - strlcpy(tokened_mnemonic, temp_mnemonic, TOKENED_MNEMONIC_BUF); - - char *tok = strtok(tokened_mnemonic, " "); - - while(tok) - { - snprintf(formatted_word, MAX_WORD_LEN + ADDITIONAL_WORD_PAD, (word_count & 1) ? "%lu.%s\n" : "%lu.%s", - (unsigned long)(word_count + 1), tok); - - /* Check that we have enough room on display to show word */ - snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s %s", - formatted_mnemonic[page_count], formatted_word); - - if(calc_str_line(get_body_font(), mnemonic_display, BODY_WIDTH) > 3) - { - page_count++; - - if (MAX_PAGES <= page_count) { - fsm_sendFailure(FailureType_Failure_Other, _("Too many pages of mnemonic words")); - storage_reset(); - goto exit; - } - - snprintf(mnemonic_display, FORMATTED_MNEMONIC_BUF, "%s %s", - formatted_mnemonic[page_count], formatted_word); - } - - strlcpy(formatted_mnemonic[page_count], mnemonic_display, - FORMATTED_MNEMONIC_BUF); - - /* Save mnemonic for each screen */ - if(strlen(mnemonic_by_screen[page_count]) == 0) - { - strlcpy(mnemonic_by_screen[page_count], tok, MNEMONIC_BY_SCREEN_BUF); - } - else - { - strlcat(mnemonic_by_screen[page_count], " ", MNEMONIC_BY_SCREEN_BUF); - strlcat(mnemonic_by_screen[page_count], tok, MNEMONIC_BY_SCREEN_BUF); - } - - tok = strtok(NULL, " "); - word_count++; + strlcpy(formatted_mnemonic[page_count], mnemonic_display, + FORMATTED_MNEMONIC_BUF); + + /* Save mnemonic for each screen */ + if (strlen(mnemonic_by_screen[page_count]) == 0) { + strlcpy(mnemonic_by_screen[page_count], tok, MNEMONIC_BY_SCREEN_BUF); + } else { + strlcat(mnemonic_by_screen[page_count], " ", MNEMONIC_BY_SCREEN_BUF); + strlcat(mnemonic_by_screen[page_count], tok, MNEMONIC_BY_SCREEN_BUF); } - // Switch from 0-indexing to 1-indexing - page_count++; + tok = strtok(NULL, " "); + word_count++; + } - display_constant_power(true); + // Switch from 0-indexing to 1-indexing + page_count++; - /* Have user confirm mnemonic is sets of 12 words */ - for(uint32_t current_page = 0; current_page < page_count; current_page++) - { - char title[MEDIUM_STR_BUF] = _("Backup"); + display_constant_power(true); - /* make current screen mnemonic available via debuglink */ - strlcpy(current_words, mnemonic_by_screen[current_page], MNEMONIC_BY_SCREEN_BUF); + /* Have user confirm mnemonic is sets of 12 words */ + for (uint32_t current_page = 0; current_page < page_count; current_page++) { + char title[MEDIUM_STR_BUF] = _("Backup"); - if(page_count > 1) - { - /* snprintf: 20 + 10 (%d) + 1 (NULL) = 31 */ - snprintf(title, MEDIUM_STR_BUF, _("Backup %" PRIu32 "/%" PRIu32 ""), current_page + 1, page_count); - } + /* make current screen mnemonic available via debuglink */ + strlcpy(current_words, mnemonic_by_screen[current_page], + MNEMONIC_BY_SCREEN_BUF); - if(!confirm_constant_power(ButtonRequestType_ButtonRequest_ConfirmWord, title, "%s", - formatted_mnemonic[current_page])) - { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Reset cancelled")); - storage_reset(); - goto exit; - } + if (page_count > 1) { + /* snprintf: 20 + 10 (%d) + 1 (NULL) = 31 */ + snprintf(title, MEDIUM_STR_BUF, _("Backup %" PRIu32 "/%" PRIu32 ""), + current_page + 1, page_count); } - /* Save mnemonic */ - storage_setMnemonic(temp_mnemonic); - mnemonic_clear(); - storage_commit(); - fsm_sendSuccess(_("Device reset")); + if (!confirm_constant_power(ButtonRequestType_ButtonRequest_ConfirmWord, + title, "%s", + formatted_mnemonic[current_page])) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Reset cancelled")); + storage_reset(); + goto exit; + } + } + + /* Save mnemonic */ + storage_setMnemonic(temp_mnemonic); + mnemonic_clear(); + storage_commit(); + fsm_sendSuccess(_("Device reset")); exit: memzero(&ctx, sizeof(ctx)); @@ -265,10 +266,10 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len) } #if DEBUG_LINK -uint32_t reset_get_int_entropy(uint8_t *entropy) { +uint32_t reset_get_int_entropy(uint8_t* entropy) { memcpy(entropy, int_entropy, 32); return 32; } -const char *reset_get_word(void) { return current_words; } +const char* reset_get_word(void) { return current_words; } #endif diff --git a/lib/firmware/ripple.c b/lib/firmware/ripple.c index f1c5d60c6..eba787ee8 100644 --- a/lib/firmware/ripple.c +++ b/lib/firmware/ripple.c @@ -56,13 +56,13 @@ bool ripple_getAddress(const uint8_t public_key[33], return true; } -void ripple_formatAmount(char *buf, size_t len, uint64_t amount) { +void ripple_formatAmount(char* buf, size_t len, uint64_t amount) { bignum256 val; bn_read_uint64(amount, &val); bn_format(&val, NULL, " XRP", RIPPLE_DECIMALS, 0, false, buf, len); } -static void append_u8(bool *ok, uint8_t **buf, const uint8_t *end, +static void append_u8(bool* ok, uint8_t** buf, const uint8_t* end, uint8_t val) { if (!*ok) { return; @@ -77,8 +77,8 @@ static void append_u8(bool *ok, uint8_t **buf, const uint8_t *end, *buf += 1; } -void ripple_serializeType(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m) { +void ripple_serializeType(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m) { if (m->key <= 0xf) { append_u8(ok, buf, end, m->type << 4 | m->key); return; @@ -88,8 +88,8 @@ void ripple_serializeType(bool *ok, uint8_t **buf, const uint8_t *end, append_u8(ok, buf, end, m->key); } -void ripple_serializeInt16(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int16_t val) { +void ripple_serializeInt16(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int16_t val) { assert(m->type == RFT_INT16 && "wrong type?"); ripple_serializeType(ok, buf, end, m); @@ -97,8 +97,8 @@ void ripple_serializeInt16(bool *ok, uint8_t **buf, const uint8_t *end, append_u8(ok, buf, end, val & 0xff); } -void ripple_serializeInt32(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int32_t val) { +void ripple_serializeInt32(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int32_t val) { assert(m->type == RFT_INT32 && "wrong type?"); ripple_serializeType(ok, buf, end, m); @@ -108,8 +108,8 @@ void ripple_serializeInt32(bool *ok, uint8_t **buf, const uint8_t *end, append_u8(ok, buf, end, val & 0xff); } -void ripple_serializeAmount(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, int64_t amount) { +void ripple_serializeAmount(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, int64_t amount) { ripple_serializeType(ok, buf, end, m); assert(amount >= 0 && "amounts cannot be negative"); @@ -128,7 +128,7 @@ void ripple_serializeAmount(bool *ok, uint8_t **buf, const uint8_t *end, append_u8(ok, buf, end, amount & 0xff); } -void ripple_serializeVarint(bool *ok, uint8_t **buf, const uint8_t *end, +void ripple_serializeVarint(bool* ok, uint8_t** buf, const uint8_t* end, int val) { if (val < 0) { assert(false && "can't serialize 0-valued varint"); @@ -143,7 +143,7 @@ void ripple_serializeVarint(bool *ok, uint8_t **buf, const uint8_t *end, if (val <= 12480) { val -= 193; - append_u8(ok, buf, end, 193 + (val >> 8)); + append_u8(ok, buf, end, 193 + ((unsigned)val >> 8)); append_u8(ok, buf, end, val & 0xff); return; } @@ -151,8 +151,8 @@ void ripple_serializeVarint(bool *ok, uint8_t **buf, const uint8_t *end, if (val < 918744) { assert(*buf + 3 < end && "buffer not long enough"); val -= 12481; - append_u8(ok, buf, end, 241 + (val >> 16)); - append_u8(ok, buf, end, (val >> 8) & 0xff); + append_u8(ok, buf, end, 241 + ((unsigned)val >> 16)); + append_u8(ok, buf, end, ((unsigned)val >> 8) & 0xff); append_u8(ok, buf, end, val & 0xff); return; } @@ -161,8 +161,8 @@ void ripple_serializeVarint(bool *ok, uint8_t **buf, const uint8_t *end, *ok = false; } -void ripple_serializeBytes(bool *ok, uint8_t **buf, const uint8_t *end, - const uint8_t *bytes, size_t count) { +void ripple_serializeBytes(bool* ok, uint8_t** buf, const uint8_t* end, + const uint8_t* bytes, size_t count) { ripple_serializeVarint(ok, buf, end, count); if (!*ok || *buf + count > end) { @@ -175,8 +175,8 @@ void ripple_serializeBytes(bool *ok, uint8_t **buf, const uint8_t *end, *buf += count; } -void ripple_serializeAddress(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, const char *address) { +void ripple_serializeAddress(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, const char* address) { ripple_serializeType(ok, buf, end, m); uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; @@ -191,16 +191,16 @@ void ripple_serializeAddress(bool *ok, uint8_t **buf, const uint8_t *end, ripple_serializeBytes(ok, buf, end, addr_raw + 1, addr_raw_len - 1); } -void ripple_serializeVL(bool *ok, uint8_t **buf, const uint8_t *end, - const RippleFieldMapping *m, const uint8_t *bytes, +void ripple_serializeVL(bool* ok, uint8_t** buf, const uint8_t* end, + const RippleFieldMapping* m, const uint8_t* bytes, size_t count) { ripple_serializeType(ok, buf, end, m); ripple_serializeBytes(ok, buf, end, bytes, count); } -bool ripple_serialize(uint8_t **buf, const uint8_t *end, const RippleSignTx *tx, - const char *source_address, const uint8_t *pubkey, - const uint8_t *sig, size_t sig_len) { +bool ripple_serialize(uint8_t** buf, const uint8_t* end, const RippleSignTx* tx, + const char* source_address, const uint8_t* pubkey, + const uint8_t* sig, size_t sig_len) { bool ok = true; ripple_serializeInt16(&ok, buf, end, &RFM_type, /*Payment*/ 0); if (tx->has_flags) @@ -226,8 +226,8 @@ bool ripple_serialize(uint8_t **buf, const uint8_t *end, const RippleSignTx *tx, return ok; } -void ripple_signTx(const HDNode *node, RippleSignTx *tx, RippleSignedTx *resp) { - const curve_info *curve = get_curve_by_name("secp256k1"); +void ripple_signTx(const HDNode* node, RippleSignTx* tx, RippleSignedTx* resp) { + const curve_info* curve = get_curve_by_name("secp256k1"); if (!curve) return; // Set canonical flag, since trezor-crypto ECDSA implementation returns @@ -249,7 +249,7 @@ void ripple_signTx(const HDNode *node, RippleSignTx *tx, RippleSignedTx *resp) { char source_address[MAX_ADDR_SIZE]; if (!ripple_getAddress(node->public_key, source_address)) return; - uint8_t *buf = resp->serialized_tx.bytes + 4; + uint8_t* buf = resp->serialized_tx.bytes + 4; size_t len = sizeof(resp->serialized_tx.bytes) - 4; if (!ripple_serialize(&buf, buf + len, tx, source_address, node->public_key, NULL, 0)) diff --git a/lib/firmware/ripple_base58.c b/lib/firmware/ripple_base58.c index d699fa673..487695a83 100644 --- a/lib/firmware/ripple_base58.c +++ b/lib/firmware/ripple_base58.c @@ -48,20 +48,18 @@ typedef uint32_t b58_almostmaxint_t; static const b58_almostmaxint_t b58_almostmaxint_mask = ((((b58_maxint_t)1) << b58_almostmaxint_bits) - 1); -bool ripple_b58tobin(void *bin, size_t *binszp, const char *b58) { +bool ripple_b58tobin(void* bin, size_t* binszp, const char* b58) { size_t binsz = *binszp; if (binsz == 0) { return false; } - const unsigned char *b58u = (const unsigned char *)b58; - unsigned char *binu = bin; + const unsigned char* b58u = (const unsigned char*)b58; + unsigned char* binu = bin; size_t outisz = (binsz + sizeof(b58_almostmaxint_t) - 1) / sizeof(b58_almostmaxint_t); b58_almostmaxint_t outi[outisz]; - b58_maxint_t t = 0; - b58_almostmaxint_t c = 0; size_t i = 0, j = 0; uint8_t bytesleft = binsz % sizeof(b58_almostmaxint_t); b58_almostmaxint_t zeromask = @@ -82,9 +80,9 @@ bool ripple_b58tobin(void *bin, size_t *binszp, const char *b58) { if (ripple_b58digits_map[b58u[i]] == -1) // Invalid base58 digit return false; - c = (unsigned)ripple_b58digits_map[b58u[i]]; + b58_almostmaxint_t c = (unsigned)ripple_b58digits_map[b58u[i]]; for (j = outisz; j--;) { - t = ((b58_maxint_t)outi[j]) * 58 + c; + b58_maxint_t t = ((b58_maxint_t)outi[j]) * 58 + c; c = t >> b58_almostmaxint_bits; outi[j] = t & b58_almostmaxint_mask; } @@ -128,10 +126,10 @@ bool ripple_b58tobin(void *bin, size_t *binszp, const char *b58) { return true; } -int ripple_b58check(const void *bin, size_t binsz, HasherType hasher_type, - const char *base58str) { +int ripple_b58check(const void* bin, size_t binsz, HasherType hasher_type, + const char* base58str) { unsigned char buf[32] = {0}; - const uint8_t *binc = bin; + const uint8_t* binc = bin; unsigned i = 0; if (binsz < 4) return -4; hasher_Raw(hasher_type, bin, binsz - 4, buf); @@ -146,9 +144,8 @@ int ripple_b58check(const void *bin, size_t binsz, HasherType hasher_type, return binc[0]; } -bool ripple_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { - const uint8_t *bin = data; - int carry = 0; +bool ripple_b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz) { + const uint8_t* bin = data; size_t i = 0, j = 0, high = 0, zcount = 0; size_t size = 0; @@ -159,6 +156,7 @@ bool ripple_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { memzero(buf, size); for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { + int carry; for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { carry += 256 * buf[j]; buf[j] = carry % 58; @@ -170,8 +168,7 @@ bool ripple_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { } } - for (j = 0; j < size && !buf[j]; ++j) - ; + for (j = 0; j < size && !buf[j]; ++j); if (*b58sz <= zcount + size - j) { *b58sz = zcount + size - j + 1; @@ -187,14 +184,14 @@ bool ripple_b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { return true; } -int ripple_encode_check(const uint8_t *data, int datalen, - HasherType hasher_type, char *str, int strsize) { +int ripple_encode_check(const uint8_t* data, int datalen, + HasherType hasher_type, char* str, int strsize) { if (datalen > 128) { return 0; } uint8_t buf[datalen + 32]; memset(buf, 0, sizeof(buf)); - uint8_t *hash = buf + datalen; + uint8_t* hash = buf + datalen; memcpy(buf, data, datalen); hasher_Raw(hasher_type, data, datalen, hash); size_t res = strsize; @@ -203,7 +200,7 @@ int ripple_encode_check(const uint8_t *data, int datalen, return success ? res : 0; } -int ripple_decode_check(const char *str, HasherType hasher_type, uint8_t *data, +int ripple_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen) { if (datalen > 128) { return 0; @@ -214,7 +211,7 @@ int ripple_decode_check(const char *str, HasherType hasher_type, uint8_t *data, if (ripple_b58tobin(d, &res, str) != true) { return 0; } - uint8_t *nd = d + datalen + 4 - res; + const uint8_t* nd = d + datalen + 4 - res; if (ripple_b58check(nd, res, hasher_type, str) < 0) { return 0; } diff --git a/lib/firmware/signing.c b/lib/firmware/signing.c index ccc38c226..a7b367e0f 100644 --- a/lib/firmware/signing.c +++ b/lib/firmware/signing.c @@ -43,21 +43,22 @@ // Set DEBUG_UTXO to non-zero to display each stage of the utxo signing sequence #define D_DISPLAY_UTXO_STAGE(STAGE, IDX) -#define DEBUG_UTXO 0 +#define DEBUG_UTXO 0 #ifdef DEBUG_ON - #if DEBUG_UTXO - #undef D_DISPLAY_UTXO_STAGE - #define D_DISPLAY_UTXO_STAGE(STAGE, IDX) DEBUG_DISPLAY("%s, idx=%ld", STAGE, IDX) - // DEBUG_DISPLAY("%d %s", slot, account); +#if DEBUG_UTXO +#undef D_DISPLAY_UTXO_STAGE +#define D_DISPLAY_UTXO_STAGE(STAGE, IDX) \ + DEBUG_DISPLAY("%s, idx=%ld", STAGE, IDX) +// DEBUG_DISPLAY("%d %s", slot, account); - #endif +#endif #endif static uint32_t inputs_count; static uint32_t outputs_count; -static const CoinType *coin; -static const curve_info *curve; -static const HDNode *root; +static const CoinType* coin; +static const curve_info* curve; +static const HDNode* root; static CONFIDENTIAL HDNode node; static bool signing = false; enum { @@ -146,7 +147,7 @@ enum { void send_fsm_co_error_message(int co_error) { struct { int code; - const char *msg; + const char* msg; FailureType type; } errorCodes[] = { {TXOUT_COMPILE_ERROR, "Failed to compile output", @@ -427,8 +428,8 @@ void phase2_request_next_input(void) { /// Compares two BIP32 paths, returning true iff there is something mismatched /// about the mixed-mode change. static bool isCrossAccountSegwitChangeForbidden( - const uint32_t *lhs_address_n, size_t lhs_address_n_count, - const uint32_t *rhs_address_n, size_t rhs_address_n_count, + const uint32_t* lhs_address_n, size_t lhs_address_n_count, + const uint32_t* rhs_address_n, size_t rhs_address_n_count, OutputScriptType rhs_script_type) { (void)lhs_address_n; @@ -460,9 +461,9 @@ static bool isCrossAccountSegwitChangeForbidden( /// Compares two BIP32 paths, returning true iff the paths match for mixed-mode /// p2pkh + ph2sh-p2wsh + p2wsh accounts. -static bool isCrossAccountSegwitChangeAllowed(const uint32_t *lhs_address_n, +static bool isCrossAccountSegwitChangeAllowed(const uint32_t* lhs_address_n, size_t lhs_address_n_count, - const uint32_t *rhs_address_n, + const uint32_t* rhs_address_n, size_t rhs_address_n_count) { size_t count = rhs_address_n_count; if (count < 5) return false; @@ -502,7 +503,7 @@ static bool isCrossAccountSegwitChangeAllowed(const uint32_t *lhs_address_n, return true; } -void extract_input_bip32_path(const TxInputType *tinput) { +void extract_input_bip32_path(const TxInputType* tinput) { if (in_address_n_count == BIP32_NOCHANGEALLOWED) { return; } @@ -538,7 +539,7 @@ void extract_input_bip32_path(const TxInputType *tinput) { } } -bool check_change_bip32_path(const TxOutputType *toutput) { +bool check_change_bip32_path(const TxOutputType* toutput) { if (isCrossAccountSegwitChangeForbidden( in_address_n, in_address_n_count, toutput->address_n, toutput->address_n_count, toutput->script_type)) @@ -563,7 +564,7 @@ bool check_change_bip32_path(const TxOutputType *toutput) { toutput->address_n[count - 1] <= BIP32_MAX_LAST_ELEMENT); } -bool compile_input_script_sig(TxInputType *tinput) { +bool compile_input_script_sig(TxInputType* tinput) { if (!multisig_fp_mismatch) { // check that this is still multisig uint8_t h[32]; @@ -605,8 +606,8 @@ bool compile_input_script_sig(TxInputType *tinput) { return tinput->script_sig.size > 0; } -void signing_init(const SignTx *msg, const CoinType *_coin, - const HDNode *_root) { +void signing_init(const SignTx* msg, const CoinType* _coin, + const HDNode* _root) { inputs_count = msg->inputs_count; outputs_count = msg->outputs_count; coin = _coin; @@ -624,7 +625,7 @@ void signing_init(const SignTx *msg, const CoinType *_coin, branch_id = 0x5BA81B19; // Overwinter break; case 4: - branch_id = 0x76B809BB; // Sapling + branch_id = 0xC8E71055; // NU6 break; } } @@ -696,7 +697,7 @@ void signing_init(const SignTx *msg, const CoinType *_coin, send_req_1_input(); } -static bool is_multisig_input_script_type(const TxInputType *txinput) { +static bool is_multisig_input_script_type(const TxInputType* txinput) { if (txinput->script_type == InputScriptType_SPENDMULTISIG || txinput->script_type == InputScriptType_SPENDP2SHWITNESS || txinput->script_type == InputScriptType_SPENDWITNESS) { @@ -705,7 +706,7 @@ static bool is_multisig_input_script_type(const TxInputType *txinput) { return false; } -static bool is_multisig_output_script_type(const TxOutputType *txoutput) { +static bool is_multisig_output_script_type(const TxOutputType* txoutput) { if (txoutput->script_type == OutputScriptType_PAYTOMULTISIG || txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS || txoutput->script_type == OutputScriptType_PAYTOWITNESS) { @@ -714,7 +715,7 @@ static bool is_multisig_output_script_type(const TxOutputType *txoutput) { return false; } -static bool is_internal_input_script_type(const TxInputType *txinput) { +static bool is_internal_input_script_type(const TxInputType* txinput) { if (txinput->script_type == InputScriptType_SPENDADDRESS || txinput->script_type == InputScriptType_SPENDMULTISIG || txinput->script_type == InputScriptType_SPENDP2SHWITNESS || @@ -724,7 +725,7 @@ static bool is_internal_input_script_type(const TxInputType *txinput) { return false; } -static bool is_change_output_script_type(const TxOutputType *txoutput) { +static bool is_change_output_script_type(const TxOutputType* txoutput) { if (txoutput->script_type == OutputScriptType_PAYTOADDRESS || txoutput->script_type == OutputScriptType_PAYTOMULTISIG || txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS || @@ -734,7 +735,7 @@ static bool is_change_output_script_type(const TxOutputType *txoutput) { return false; } -static bool is_segwit_input_script_type(const TxInputType *txinput) { +static bool is_segwit_input_script_type(const TxInputType* txinput) { if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS || txinput->script_type == InputScriptType_SPENDWITNESS) { return true; @@ -742,7 +743,7 @@ static bool is_segwit_input_script_type(const TxInputType *txinput) { return false; } -static bool signing_validate_input(const TxInputType *txinput) { +static bool signing_validate_input(const TxInputType* txinput) { if (txinput->prev_hash.size != 32) { fsm_sendFailure(FailureType_Failure_Other, _("Encountered invalid prevhash 1")); @@ -796,7 +797,7 @@ static bool signing_validate_input(const TxInputType *txinput) { return true; } -static bool signing_validate_output(TxOutputType *txoutput) { +static bool signing_validate_output(const TxOutputType* txoutput) { if (txoutput->has_multisig && !is_multisig_output_script_type(txoutput)) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Multisig field provided but not expected.")); @@ -857,7 +858,7 @@ static bool signing_validate_output(TxOutputType *txoutput) { return true; } -static bool signing_validate_bin_output(TxOutputBinType *tx_bin_output) { +static bool signing_validate_bin_output(const TxOutputBinType* tx_bin_output) { if (!coin->decred && tx_bin_output->has_decred_script_version) { fsm_sendFailure( FailureType_Failure_UnexpectedMessage, @@ -868,7 +869,7 @@ static bool signing_validate_bin_output(TxOutputBinType *tx_bin_output) { return true; } -static bool signing_check_input(TxInputType *txinput) { +static bool signing_check_input(TxInputType* txinput) { /* compute multisig fingerprint */ /* (if all input share the same fingerprint, outputs having the same * fingerprint will be considered as change outputs) */ @@ -917,7 +918,7 @@ static bool signing_check_input(TxInputType *txinput) { // hash prevout and script type to check it later (relevant for fee // computation) tx_prevout_hash(&hasher_check, txinput); - hasher_Update(&hasher_check, (const uint8_t *)&txinput->script_type, + hasher_Update(&hasher_check, (const uint8_t*)&txinput->script_type, sizeof(&txinput->script_type)); return true; } @@ -936,7 +937,7 @@ static bool signing_check_prevtx_hash(void) { return true; } -static bool signing_check_output(TxOutputType *txoutput) { +static bool signing_check_output(TxOutputType* txoutput) { // Phase1: Check outputs // add it to hash_outputs // ask user for permission @@ -1076,26 +1077,26 @@ static void phase1_request_next_output(void) { } } -static void signing_hash_bip143(const TxInputType *txinput, uint8_t *hash) { +static void signing_hash_bip143(const TxInputType* txinput, uint8_t* hash) { uint32_t hash_type = signing_hash_type(); Hasher hasher_preimage; hasher_Init(&hasher_preimage, curve->hasher_sign); - hasher_Update(&hasher_preimage, (const uint8_t *)&version, 4); // nVersion - hasher_Update(&hasher_preimage, hash_prevouts, 32); // hashPrevouts - hasher_Update(&hasher_preimage, hash_sequence, 32); // hashSequence - tx_prevout_hash(&hasher_preimage, txinput); // outpoint + hasher_Update(&hasher_preimage, (const uint8_t*)&version, 4); // nVersion + hasher_Update(&hasher_preimage, hash_prevouts, 32); // hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // hashSequence + tx_prevout_hash(&hasher_preimage, txinput); // outpoint tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + hasher_Update(&hasher_preimage, (const uint8_t*)&txinput->amount, 8); // amount tx_sequence_hash(&hasher_preimage, txinput); // nSequence hasher_Update(&hasher_preimage, hash_outputs, 32); // hashOutputs - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, 4); // nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // nHashType + hasher_Update(&hasher_preimage, (const uint8_t*)&lock_time, 4); // nLockTime + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); // nHashType hasher_Final(&hasher_preimage, hash); } -static void signing_hash_zip143(const TxInputType *txinput, uint8_t *hash) { +static void signing_hash_zip143(const TxInputType* txinput, uint8_t* hash) { uint32_t hash_type = signing_hash_type(); uint8_t personal[16]; memcpy(personal, "ZcashSigHash", 12); @@ -1104,32 +1105,32 @@ static void signing_hash_zip143(const TxInputType *txinput, uint8_t *hash) { hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, sizeof(personal)); uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered - hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); - hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, + hasher_Update(&hasher_preimage, (const uint8_t*)&ver, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&version_group_id, 4); // 2. nVersionGroupId hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs // 6. hashJoinSplits hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, + hasher_Update(&hasher_preimage, (const uint8_t*)&lock_time, 4); // 7. nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, + hasher_Update(&hasher_preimage, (const uint8_t*)&expiry, 4); // 8. expiryHeight - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); // 9. nHashType tx_prevout_hash(&hasher_preimage, txinput); // 10a. outpoint tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // 10b. scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + hasher_Update(&hasher_preimage, (const uint8_t*)&txinput->amount, 8); // 10c. value tx_sequence_hash(&hasher_preimage, txinput); // 10d. nSequence hasher_Final(&hasher_preimage, hash); } -static void signing_hash_zip243(const TxInputType *txinput, uint8_t *hash) { +static void signing_hash_zip243(const TxInputType* txinput, uint8_t* hash) { uint32_t hash_type = signing_hash_type(); uint8_t personal[16]; memcpy(personal, "ZcashSigHash", 12); @@ -1138,8 +1139,8 @@ static void signing_hash_zip243(const TxInputType *txinput, uint8_t *hash) { hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, sizeof(personal)); uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered - hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); - hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, + hasher_Update(&hasher_preimage, (const uint8_t*)&ver, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&version_group_id, 4); // 2. nVersionGroupId hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence @@ -1150,38 +1151,38 @@ static void signing_hash_zip243(const TxInputType *txinput, uint8_t *hash) { hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); // 8. hashShieldedOutputs hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, + hasher_Update(&hasher_preimage, (const uint8_t*)&lock_time, 4); // 9. nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, + hasher_Update(&hasher_preimage, (const uint8_t*)&expiry, 4); // 10. expiryHeight hasher_Update(&hasher_preimage, - (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", + (const uint8_t*)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); // 11. valueBalance - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); // 12. nHashType tx_prevout_hash(&hasher_preimage, txinput); // 13a. outpoint tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // 13b. scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + hasher_Update(&hasher_preimage, (const uint8_t*)&txinput->amount, 8); // 13c. value tx_sequence_hash(&hasher_preimage, txinput); // 13d. nSequence hasher_Final(&hasher_preimage, hash); } -static void signing_hash_decred(const uint8_t *hash_witness, uint8_t *hash) { +static void signing_hash_decred(const uint8_t* hash_witness, uint8_t* hash) { uint32_t hash_type = signing_hash_type(); Hasher hasher_preimage; hasher_Init(&hasher_preimage, curve->hasher_sign); - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); hasher_Update(&hasher_preimage, hash_prefix, 32); hasher_Update(&hasher_preimage, hash_witness, 32); hasher_Final(&hasher_preimage, hash); } -static bool signing_sign_hash(TxInputType *txinput, const uint8_t *private_key, - const uint8_t *public_key, const uint8_t *hash) { +static bool signing_sign_hash(TxInputType* txinput, const uint8_t* private_key, + const uint8_t* public_key, const uint8_t* hash) { resp.serialized.has_signature_index = true; resp.serialized.signature_index = idx1; resp.serialized.has_signature = true; @@ -1237,7 +1238,7 @@ static bool signing_sign_input(void) { } uint32_t hash_type = signing_hash_type(); - hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); + hasher_Update(&ti.hasher, (const uint8_t*)&hash_type, 4); tx_hash_final(&ti, hash, false); resp.has_serialized = true; if (!signing_sign_hash(&input, privkey, pubkey, hash)) return false; @@ -1246,9 +1247,8 @@ static bool signing_sign_input(void) { return true; } -static bool signing_sign_segwit_input(TxInputType *txinput) { +static bool signing_sign_segwit_input(TxInputType* txinput) { // idx1: index to sign - uint8_t hash[32]; if (is_segwit_input_script_type(txinput)) { if (!compile_input_script_sig(txinput)) { @@ -1264,6 +1264,7 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { } authorized_bip143_in -= txinput->amount; + uint8_t hash[32]; signing_hash_bip143(txinput, hash); resp.has_serialized = true; @@ -1323,7 +1324,7 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { return true; } -static bool signing_sign_decred_input(TxInputType *txinput) { +static bool signing_sign_decred_input(TxInputType* txinput) { uint8_t hash[32], hash_witness[32]; tx_hash_final(&ti, hash_witness, false); signing_hash_decred(hash_witness, hash); @@ -1337,7 +1338,7 @@ static bool signing_sign_decred_input(TxInputType *txinput) { #define ENABLE_SEGWIT_NONSEGWIT_MIXING 1 -void signing_txack(TransactionType *tx) { +void signing_txack(TransactionType* tx) { if (!signing) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Signing mode")); @@ -1590,7 +1591,7 @@ void signing_txack(TransactionType *tx) { } // check prevouts and script type tx_prevout_hash(&hasher_check, tx->inputs); - hasher_Update(&hasher_check, (const uint8_t *)&tx->inputs[0].script_type, + hasher_Update(&hasher_check, (const uint8_t*)&tx->inputs[0].script_type, sizeof(&tx->inputs[0].script_type)); if (idx2 == idx1) { if (!compile_input_script_sig(&tx->inputs[0])) { diff --git a/lib/firmware/signtx_tendermint.c b/lib/firmware/signtx_tendermint.c index 8b87ebdc9..0f849d64b 100644 --- a/lib/firmware/signtx_tendermint.c +++ b/lib/firmware/signtx_tendermint.c @@ -39,12 +39,12 @@ static bool initialized; static uint32_t msgs_remaining; static TendermintSignTx tmsg; -const void *tendermint_getSignTx(void) { return (void *)&tmsg; } +const void* tendermint_getSignTx(void) { return (void*)&tmsg; } -bool tendermint_signTxInit(const HDNode *_node, const void *_msg, - const size_t msgsize, const char *denom) { +bool tendermint_signTxInit(const HDNode* _node, const void* _msg, + const size_t msgsize, const char* denom) { initialized = true; - msgs_remaining = ((TendermintSignTx *)_msg)->msg_count; + msgs_remaining = ((TendermintSignTx*)_msg)->msg_count; has_message = false; memzero(&node, sizeof(node)); @@ -65,7 +65,7 @@ bool tendermint_signTxInit(const HDNode *_node, const void *_msg, return false; } - memcpy((void *)&tmsg, _msg, msgsize); + memcpy((void*)&tmsg, _msg, msgsize); bool success = true; char buffer[128]; @@ -79,8 +79,8 @@ bool tendermint_signTxInit(const HDNode *_node, const void *_msg, tmsg.account_number); // - const char *const chainid_prefix = ",\"chain_id\":\""; - sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + const char* const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t*)chainid_prefix, strlen(chainid_prefix)); tendermint_sha256UpdateEscaped(&ctx, tmsg.chain_id, strlen(tmsg.chain_id)); // 30 + ^10 + 11 + ^9 + 3 = ^63 @@ -94,22 +94,22 @@ bool tendermint_signTxInit(const HDNode *_node, const void *_msg, ",\"gas\":\"%" PRIu32 "\"}", tmsg.gas); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); if (tmsg.has_memo) { tendermint_sha256UpdateEscaped(&ctx, tmsg.memo, strlen(tmsg.memo)); } // 10 - sha256_Update(&ctx, (uint8_t *)"\",\"msgs\":[", 10); + sha256_Update(&ctx, (uint8_t*)"\",\"msgs\":[", 10); return success; } bool tendermint_signTxUpdateMsgSend(const uint64_t amount, - const char *to_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix) { + const char* to_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -131,7 +131,7 @@ bool tendermint_signTxUpdateMsgSend(const uint64_t amount, } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -169,10 +169,10 @@ bool tendermint_signTxUpdateMsgSend(const uint64_t amount, } bool tendermint_signTxUpdateMsgDelegate(const uint64_t amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix) { + const char* delegator_address, + const char* validator_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -194,7 +194,7 @@ bool tendermint_signTxUpdateMsgDelegate(const uint64_t amount, } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -231,11 +231,11 @@ bool tendermint_signTxUpdateMsgDelegate(const uint64_t amount, return success; } bool tendermint_signTxUpdateMsgUndelegate(const uint64_t amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, - const char *denom, - const char *msgTypePrefix) { + const char* delegator_address, + const char* validator_address, + const char* chainstr, + const char* denom, + const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -257,7 +257,7 @@ bool tendermint_signTxUpdateMsgUndelegate(const uint64_t amount, } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -295,9 +295,9 @@ bool tendermint_signTxUpdateMsgUndelegate(const uint64_t amount, } bool tendermint_signTxUpdateMsgRedelegate( - const uint64_t amount, const char *delegator_address, - const char *validator_src_address, const char *validator_dst_address, - const char *chainstr, const char *denom, const char *msgTypePrefix) { + const uint64_t amount, const char* delegator_address, + const char* validator_src_address, const char* validator_dst_address, + const char* chainstr, const char* denom, const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -319,7 +319,7 @@ bool tendermint_signTxUpdateMsgRedelegate( } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -364,11 +364,11 @@ bool tendermint_signTxUpdateMsgRedelegate( return success; } -bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, - const char *delegator_address, - const char *validator_address, - const char *chainstr, const char *denom, - const char *msgTypePrefix) { +bool tendermint_signTxUpdateMsgRewards(const uint64_t* amount, + const char* delegator_address, + const char* validator_address, + const char* chainstr, const char* denom, + const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -390,7 +390,7 @@ bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -431,10 +431,10 @@ bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, } bool tendermint_signTxUpdateMsgIBCTransfer( - const uint64_t amount, const char *sender, const char *receiver, - const char *source_channel, const char *source_port, - const char *revision_number, const char *revision_height, - const char *chainstr, const char *denom, const char *msgTypePrefix) { + const uint64_t amount, const char* sender, const char* receiver, + const char* source_channel, const char* source_port, + const char* revision_number, const char* revision_height, + const char* chainstr, const char* denom, const char* msgTypePrefix) { char buffer[128]; size_t decoded_len; char hrp[45]; @@ -456,7 +456,7 @@ bool tendermint_signTxUpdateMsgIBCTransfer( } if (has_message) { - sha256_Update(&ctx, (uint8_t *)",", 1); + sha256_Update(&ctx, (uint8_t*)",", 1); } bool success = true; @@ -510,7 +510,7 @@ bool tendermint_signTxUpdateMsgIBCTransfer( return success; } -bool tendermint_signTxFinalize(uint8_t *public_key, uint8_t *signature) { +bool tendermint_signTxFinalize(uint8_t* public_key, uint8_t* signature) { char buffer[128]; // 14 + ^20 + 2 = ^36 diff --git a/lib/firmware/solana.c b/lib/firmware/solana.c new file mode 100644 index 000000000..4b8ee78c1 --- /dev/null +++ b/lib/firmware/solana.c @@ -0,0 +1,676 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2025 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "keepkey/firmware/solana.h" + +#include "trezor/crypto/memzero.h" + +#include +#include + +/* ------------------------------------------------------------------ */ +/* Well-known program IDs */ +/* ------------------------------------------------------------------ */ + +/* 11111111111111111111111111111111 */ +const uint8_t SOL_SYSTEM_PROGRAM[SOL_PUBKEY_SIZE] = {0}; + +/* TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA */ +const uint8_t SOL_TOKEN_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x06, 0xdd, 0xf6, 0xe1, 0xd7, 0x65, 0xa1, 0x93, 0xd9, 0xcb, 0xe1, + 0x46, 0xce, 0xeb, 0x79, 0xac, 0x1c, 0xb4, 0x85, 0xed, 0x5f, 0x5b, + 0x37, 0x91, 0x3a, 0x8c, 0xf5, 0x85, 0x7e, 0xff, 0x00, 0xa9}; + +/* TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb */ +const uint8_t SOL_TOKEN_2022_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x06, 0xdd, 0xf6, 0xe1, 0xee, 0x75, 0x8f, 0xde, 0x18, 0x42, 0x5d, + 0xbc, 0xe4, 0x6c, 0xcd, 0xda, 0xb6, 0x1a, 0xfc, 0x4d, 0x83, 0xb9, + 0x0d, 0x27, 0xfe, 0xbd, 0xf9, 0x28, 0xd8, 0xa1, 0x8b, 0xfc}; + +/* Stake11111111111111111111111111111111111111 */ +const uint8_t SOL_STAKE_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x06, 0xa1, 0xd8, 0x17, 0x91, 0x37, 0x54, 0x2a, 0x98, 0x34, 0x37, + 0xbd, 0xfe, 0x2a, 0x7a, 0xb2, 0x55, 0x7f, 0x53, 0x5c, 0x8a, 0x78, + 0x72, 0x2b, 0x68, 0xa4, 0x9d, 0xc0, 0x00, 0x00, 0x00, 0x00}; + +/* Vote111111111111111111111111111111111111111 */ +const uint8_t SOL_VOTE_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x07, 0x61, 0x48, 0x1d, 0x35, 0x74, 0x74, 0xbb, 0x7c, 0x4d, 0x76, + 0x24, 0xeb, 0xd3, 0xbd, 0xb3, 0xd8, 0x35, 0x5e, 0x73, 0xd1, 0x10, + 0x43, 0xfc, 0x0d, 0xa3, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00}; + +/* ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL */ +const uint8_t SOL_ATA_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x8c, 0x97, 0x25, 0x8f, 0x4e, 0x24, 0x89, 0xf1, 0xbb, 0x3d, 0x10, + 0x29, 0x14, 0x8e, 0x0d, 0x83, 0x0b, 0x5a, 0x13, 0x99, 0xda, 0xff, + 0x10, 0x84, 0x04, 0x8e, 0x7b, 0xd8, 0xdb, 0xe9, 0xf8, 0x59}; + +/* ComputeBudget111111111111111111111111111111 */ +const uint8_t SOL_COMPUTE_BUDGET_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x03, 0x06, 0x46, 0x6f, 0xe5, 0x21, 0x17, 0x32, 0xff, 0xec, 0xad, + 0xba, 0x72, 0xc3, 0x9b, 0xe7, 0xbc, 0x8c, 0xe5, 0xbb, 0xc5, 0xf7, + 0x12, 0x6b, 0x2c, 0x43, 0x9b, 0x3a, 0x40, 0x00, 0x00, 0x00}; + +/* MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr */ +const uint8_t SOL_MEMO_PROGRAM[SOL_PUBKEY_SIZE] = { + 0x05, 0x4a, 0x53, 0x5a, 0x99, 0x29, 0x21, 0x06, 0x4d, 0x24, 0xe8, + 0x71, 0x60, 0xda, 0x38, 0x7c, 0x7c, 0x35, 0xb5, 0xdd, 0xbc, 0x92, + 0xbb, 0x81, 0xe4, 0x1f, 0xa8, 0x40, 0x41, 0x05, 0x44, 0x8d}; + +/* ------------------------------------------------------------------ */ +/* Compact-u16 decoder (Solana transaction format) */ +/* ------------------------------------------------------------------ */ + +static int read_compact_u16(const uint8_t* data, size_t len, uint16_t* out) { + if (len < 1) return -1; + + if (data[0] < SOL_COMPACT_U16_CONTINUATION) { + *out = data[0]; + return 1; + } + + if (len < 2) return -1; + if (data[1] < SOL_COMPACT_U16_CONTINUATION) { + *out = (uint16_t)((data[0] & SOL_COMPACT_U16_DATA_MASK) | + ((uint16_t)data[1] << 7)); + return 2; + } + + if (len < 3) return -1; + /* Third byte uses bits 14-15, so only values 0-3 are valid + * (max compact-u16 value is 0xFFFF = 65535). */ + if (data[2] > SOL_COMPACT_U16_BYTE3_MAX) return -1; + *out = (uint16_t)((data[0] & SOL_COMPACT_U16_DATA_MASK) | + ((data[1] & SOL_COMPACT_U16_DATA_MASK) << 7) | + ((uint16_t)data[2] << 14)); + return 3; +} + +static uint64_t read_le64(const uint8_t* p) { + return (uint64_t)p[0] | ((uint64_t)p[1] << 8) | ((uint64_t)p[2] << 16) | + ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 32) | + ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | + ((uint64_t)p[7] << 56); +} + +static uint32_t read_le32(const uint8_t* p) { + return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | + ((uint32_t)p[3] << 24); +} + +static void copy_account(uint8_t out[SOL_PUBKEY_SIZE], const SolanaParsedTx* tx, + const uint8_t* acct_indices, uint16_t num_acct_indices, + uint16_t idx) { + if (idx < num_acct_indices) { + memcpy(out, tx->accounts[acct_indices[idx]], SOL_PUBKEY_SIZE); + } +} + +static int parse_instruction_section(const uint8_t* raw, size_t raw_len, + size_t* pos_io, SolanaParsedTx* tx, + uint16_t num_accounts, bool* has_unknown, + bool* force_opaque) { + size_t pos = *pos_io; + uint16_t num_instructions; + int n = read_compact_u16(raw + pos, raw_len - pos, &num_instructions); + if (n < 0) return -1; + pos += n; + + if (num_instructions > SOL_MAX_INSTRUCTIONS) { + *force_opaque = true; + tx->num_instructions = 0; + /* Don't attempt to parse instruction data — treat as opaque. */ + *pos_io = raw_len; + return 0; + } else { + tx->num_instructions = (uint8_t)num_instructions; + } + + for (uint16_t i = 0; i < num_instructions; i++) { + if (pos >= raw_len) return -1; + uint8_t program_idx = raw[pos++]; + if (program_idx >= num_accounts) return -1; + + uint16_t num_acct_indices; + n = read_compact_u16(raw + pos, raw_len - pos, &num_acct_indices); + if (n < 0) return -1; + pos += n; + + if (pos + num_acct_indices > raw_len) return -1; + const uint8_t* acct_indices = raw + pos; + pos += num_acct_indices; + + for (uint16_t j = 0; j < num_acct_indices; j++) { + if (acct_indices[j] >= num_accounts) return -1; + } + + uint16_t data_len; + n = read_compact_u16(raw + pos, raw_len - pos, &data_len); + if (n < 0) return -1; + pos += n; + + if (pos + data_len > raw_len) return -1; + const uint8_t* instr_data = raw + pos; + pos += data_len; + + if (i >= SOL_MAX_INSTRUCTIONS) { + continue; + } + + SolanaParsedInstruction* pi = &tx->instructions[i]; + memcpy(pi->program_id, tx->accounts[program_idx], SOL_PUBKEY_SIZE); + + /* Classify and decode */ + if (memcmp(pi->program_id, SOL_SYSTEM_PROGRAM, SOL_PUBKEY_SIZE) == 0) { + /* System program */ + if (data_len >= 4) { + uint32_t instr_type = read_le32(instr_data); + if (instr_type == SOL_SYS_TRANSFER && data_len >= 12) { + pi->type = SOL_INSTR_SYSTEM_TRANSFER; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + } else if (instr_type == SOL_SYS_CREATE_ACCOUNT && data_len >= 12) { + pi->type = SOL_INSTR_SYSTEM_CREATE_ACCOUNT; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + } else if (instr_type == SOL_SYS_ADVANCE_NONCE) { + pi->type = SOL_INSTR_SYSTEM_ADVANCE_NONCE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (instr_type == SOL_SYS_WITHDRAW_NONCE && data_len >= 12) { + pi->type = SOL_INSTR_SYSTEM_WITHDRAW_NONCE; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 4); + } else if (instr_type == SOL_SYS_INITIALIZE_NONCE && data_len >= 36) { + pi->type = SOL_INSTR_SYSTEM_INITIALIZE_NONCE; + memcpy(pi->authority, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + } else if (instr_type == SOL_SYS_AUTHORIZE_NONCE && data_len >= 36) { + pi->type = SOL_INSTR_SYSTEM_AUTHORIZE_NONCE; + memcpy(pi->extra, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + } else if (instr_type == SOL_SYS_ASSIGN && data_len >= 36) { + pi->type = SOL_INSTR_SYSTEM_ASSIGN; + memcpy(pi->extra, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + } else if (instr_type == SOL_SYS_ALLOCATE && data_len >= 12) { + pi->type = SOL_INSTR_SYSTEM_ALLOCATE; + pi->extra_value = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_TOKEN_PROGRAM, SOL_PUBKEY_SIZE) == + 0 || + memcmp(pi->program_id, SOL_TOKEN_2022_PROGRAM, + SOL_PUBKEY_SIZE) == 0) { + if (data_len >= 1) { + uint8_t token_instr = instr_data[0]; + if (token_instr == SOL_TOKEN_TRANSFER_IX && data_len >= 9) { + pi->type = SOL_INSTR_TOKEN_TRANSFER; + pi->amount = read_le64(instr_data + 1); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (token_instr == SOL_TOKEN_TRANSFER_CHECKED_IX && + data_len >= 9) { + pi->type = SOL_INSTR_TOKEN_TRANSFER_CHECKED; + pi->amount = read_le64(instr_data + 1); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 1); + pi->has_mint = (num_acct_indices >= 2); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 2); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 3); + pi->extra_u8 = data_len >= 10 ? instr_data[9] : 0; + } else if (token_instr == SOL_TOKEN_APPROVE_IX && data_len >= 9) { + pi->type = SOL_INSTR_TOKEN_APPROVE; + pi->amount = read_le64(instr_data + 1); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (token_instr == SOL_TOKEN_REVOKE_IX) { + pi->type = SOL_INSTR_TOKEN_REVOKE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + } else if (token_instr == SOL_TOKEN_SET_AUTHORITY_IX && data_len >= 2) { + pi->type = SOL_INSTR_TOKEN_SET_AUTHORITY; + pi->extra_u8 = instr_data[1]; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + if (data_len >= 35 && instr_data[2] == 1) { + memcpy(pi->extra, instr_data + 3, SOL_PUBKEY_SIZE); + } + } else if ((token_instr == SOL_TOKEN_MINT_TO_IX || + token_instr == SOL_TOKEN_MINT_TO_CHECKED_IX) && + data_len >= 9) { + pi->type = SOL_INSTR_TOKEN_MINT_TO; + pi->amount = read_le64(instr_data + 1); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 0); + pi->has_mint = (num_acct_indices >= 1); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + pi->extra_u8 = + (token_instr == SOL_TOKEN_MINT_TO_CHECKED_IX && data_len >= 10) + ? instr_data[9] + : 0; + } else if ((token_instr == SOL_TOKEN_BURN_IX || + token_instr == SOL_TOKEN_BURN_CHECKED_IX) && + data_len >= 9) { + pi->type = SOL_INSTR_TOKEN_BURN; + pi->amount = read_le64(instr_data + 1); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 1); + pi->has_mint = (num_acct_indices >= 2); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + pi->extra_u8 = + (token_instr == SOL_TOKEN_BURN_CHECKED_IX && data_len >= 10) + ? instr_data[9] + : 0; + } else if (token_instr == SOL_TOKEN_CLOSE_ACCOUNT_IX) { + pi->type = SOL_INSTR_TOKEN_CLOSE_ACCOUNT; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (token_instr == SOL_TOKEN_FREEZE_ACCOUNT_IX) { + pi->type = SOL_INSTR_TOKEN_FREEZE_ACCOUNT; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 1); + pi->has_mint = (num_acct_indices >= 2); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (token_instr == SOL_TOKEN_THAW_ACCOUNT_IX) { + pi->type = SOL_INSTR_TOKEN_THAW_ACCOUNT; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 1); + pi->has_mint = (num_acct_indices >= 2); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (token_instr == SOL_TOKEN_SYNC_NATIVE_IX) { + pi->type = SOL_INSTR_TOKEN_SYNC_NATIVE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_STAKE_PROGRAM, SOL_PUBKEY_SIZE) == + 0) { + if (data_len >= 4) { + uint32_t stake_instr = read_le32(instr_data); + if (stake_instr == SOL_STAKE_DELEGATE_IX) { + pi->type = SOL_INSTR_STAKE_DELEGATE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 5); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + } else if (stake_instr == SOL_STAKE_WITHDRAW_IX && data_len >= 12) { + pi->type = SOL_INSTR_STAKE_WITHDRAW; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 4); + } else if (stake_instr == SOL_STAKE_AUTHORIZE_IX && data_len >= 36) { + pi->type = SOL_INSTR_STAKE_AUTHORIZE; + memcpy(pi->extra, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + pi->extra_u8 = (uint8_t)read_le32(instr_data + 36); + } else if (stake_instr == SOL_STAKE_SPLIT_IX && data_len >= 12) { + pi->type = SOL_INSTR_STAKE_SPLIT; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (stake_instr == SOL_STAKE_DEACTIVATE_IX) { + pi->type = SOL_INSTR_STAKE_DEACTIVATE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (stake_instr == SOL_STAKE_MERGE_IX) { + pi->type = SOL_INSTR_STAKE_MERGE; + copy_account(pi->to, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 4); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_VOTE_PROGRAM, SOL_PUBKEY_SIZE) == 0) { + if (data_len >= 4) { + uint32_t vote_instr = read_le32(instr_data); + if (vote_instr == SOL_VOTE_AUTHORIZE_IX && data_len >= 40) { + pi->type = SOL_INSTR_VOTE_AUTHORIZE; + memcpy(pi->extra, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + pi->extra_u8 = (uint8_t)read_le32(instr_data + 36); + } else if (vote_instr == SOL_VOTE_WITHDRAW_IX && data_len >= 12) { + pi->type = SOL_INSTR_VOTE_WITHDRAW; + pi->lamports = read_le64(instr_data + 4); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + } else if (vote_instr == SOL_VOTE_UPDATE_VALIDATOR_IX && + data_len >= 36) { + pi->type = SOL_INSTR_VOTE_UPDATE_VALIDATOR; + memcpy(pi->extra, instr_data + 4, SOL_PUBKEY_SIZE); + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + } else if (vote_instr == SOL_VOTE_UPDATE_COMMISSION_IX && + data_len >= 5) { + pi->type = SOL_INSTR_VOTE_UPDATE_COMMISSION; + pi->extra_u8 = instr_data[4]; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 1); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_ATA_PROGRAM, SOL_PUBKEY_SIZE) == 0) { + if (data_len == 0 || (data_len == 1 && instr_data[0] == 0)) { + pi->type = SOL_INSTR_ATA_CREATE; + copy_account(pi->from, tx, acct_indices, num_acct_indices, 0); + copy_account(pi->to, tx, acct_indices, num_acct_indices, 1); + copy_account(pi->authority, tx, acct_indices, num_acct_indices, 2); + copy_account(pi->mint, tx, acct_indices, num_acct_indices, 3); + pi->has_mint = (num_acct_indices >= 4); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_COMPUTE_BUDGET_PROGRAM, + SOL_PUBKEY_SIZE) == 0) { + if (data_len >= 1) { + uint8_t cb_instr = instr_data[0]; + if (cb_instr == SOL_CB_REQUEST_HEAP_FRAME && data_len >= 5) { + pi->type = SOL_INSTR_COMPUTE_BUDGET_HEAP_FRAME; + pi->extra_value = read_le32(instr_data + 1); + } else if (cb_instr == SOL_CB_SET_COMPUTE_UNIT_LIMIT && data_len >= 5) { + pi->type = SOL_INSTR_COMPUTE_BUDGET_UNIT_LIMIT; + pi->extra_value = read_le32(instr_data + 1); + } else if (cb_instr == SOL_CB_SET_COMPUTE_UNIT_PRICE && data_len >= 9) { + pi->type = SOL_INSTR_COMPUTE_BUDGET_UNIT_PRICE; + pi->extra_value = read_le64(instr_data + 1); + } else if (cb_instr == SOL_CB_SET_LOADED_ACCOUNTS_SIZE && + data_len >= 5) { + pi->type = SOL_INSTR_COMPUTE_BUDGET_LOADED_ACCOUNTS_SIZE; + pi->extra_value = read_le32(instr_data + 1); + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } else if (memcmp(pi->program_id, SOL_MEMO_PROGRAM, SOL_PUBKEY_SIZE) == 0) { + pi->type = SOL_INSTR_MEMO; + } else { + pi->type = SOL_INSTR_UNKNOWN; + *has_unknown = true; + } + } + + *pos_io = pos; + return num_instructions; +} + +/* ------------------------------------------------------------------ */ +/* Transaction parser */ +/* ------------------------------------------------------------------ */ + +static SolanaTxReview solana_parseLegacyTx(const uint8_t* raw, size_t raw_len, + SolanaParsedTx* tx) { + memset(tx, 0, sizeof(*tx)); + size_t pos = 0; + bool has_unknown = false; + bool force_opaque = false; + + /* Header: num_required_sigs, num_readonly_signed, num_readonly_unsigned */ + if (raw_len < 3) return SOL_TX_REVIEW_MALFORMED; + tx->num_required_sigs = raw[pos++]; + tx->num_readonly_signed = raw[pos++]; + tx->num_readonly_unsigned = raw[pos++]; + + /* Account keys count (compact-u16) */ + uint16_t num_accounts; + int n = read_compact_u16(raw + pos, raw_len - pos, &num_accounts); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + pos += n; + + if (num_accounts > SOL_MAX_ACCOUNTS) return SOL_TX_REVIEW_OPAQUE; + tx->num_accounts = (uint8_t)num_accounts; + + /* Read account keys */ + for (uint16_t i = 0; i < num_accounts; i++) { + if (pos + SOL_PUBKEY_SIZE > raw_len) return SOL_TX_REVIEW_MALFORMED; + memcpy(tx->accounts[i], raw + pos, SOL_PUBKEY_SIZE); + pos += SOL_PUBKEY_SIZE; + } + + /* Recent blockhash */ + if (pos + SOL_PUBKEY_SIZE > raw_len) return SOL_TX_REVIEW_MALFORMED; + memcpy(tx->recent_blockhash, raw + pos, SOL_PUBKEY_SIZE); + pos += SOL_PUBKEY_SIZE; + + n = parse_instruction_section(raw, raw_len, &pos, tx, num_accounts, + &has_unknown, &force_opaque); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + + /* Reject if there are unconsumed bytes — prevents hidden trailing data */ + if (pos != raw_len) return SOL_TX_REVIEW_MALFORMED; + + if (tx->num_instructions == 0 || has_unknown || force_opaque) { + return SOL_TX_REVIEW_OPAQUE; + } + return SOL_TX_REVIEW_VERIFIED; +} + +static SolanaTxReview solana_parseVersionedTx(const uint8_t* raw, + size_t raw_len, + SolanaParsedTx* tx) { + memset(tx, 0, sizeof(*tx)); + size_t pos = 0; + bool has_unknown = false; + bool force_opaque = true; + + if (raw_len < 1) return SOL_TX_REVIEW_MALFORMED; + uint8_t version_prefix = raw[pos++]; + if ((version_prefix & SOL_VERSION_FLAG) == 0) return SOL_TX_REVIEW_MALFORMED; + if ((version_prefix & SOL_VERSION_MASK) != 0) return SOL_TX_REVIEW_OPAQUE; + + if (raw_len - pos < 3) return SOL_TX_REVIEW_MALFORMED; + tx->num_required_sigs = raw[pos++]; + tx->num_readonly_signed = raw[pos++]; + tx->num_readonly_unsigned = raw[pos++]; + + uint16_t num_accounts; + int n = read_compact_u16(raw + pos, raw_len - pos, &num_accounts); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + pos += n; + + if (num_accounts > SOL_MAX_ACCOUNTS) return SOL_TX_REVIEW_OPAQUE; + tx->num_accounts = (uint8_t)num_accounts; + + for (uint16_t i = 0; i < num_accounts; i++) { + if (pos + SOL_PUBKEY_SIZE > raw_len) return SOL_TX_REVIEW_MALFORMED; + memcpy(tx->accounts[i], raw + pos, SOL_PUBKEY_SIZE); + pos += SOL_PUBKEY_SIZE; + } + + if (pos + SOL_PUBKEY_SIZE > raw_len) return SOL_TX_REVIEW_MALFORMED; + memcpy(tx->recent_blockhash, raw + pos, SOL_PUBKEY_SIZE); + pos += SOL_PUBKEY_SIZE; + + n = parse_instruction_section(raw, raw_len, &pos, tx, num_accounts, + &has_unknown, &force_opaque); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + + uint16_t lookup_table_count; + n = read_compact_u16(raw + pos, raw_len - pos, &lookup_table_count); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + pos += n; + + for (uint16_t i = 0; i < lookup_table_count; i++) { + uint16_t writable_count, readonly_count; + if (pos + SOL_PUBKEY_SIZE > raw_len) return SOL_TX_REVIEW_MALFORMED; + pos += SOL_PUBKEY_SIZE; /* lookup table account key */ + + n = read_compact_u16(raw + pos, raw_len - pos, &writable_count); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + pos += n; + if (pos + writable_count > raw_len) return SOL_TX_REVIEW_MALFORMED; + pos += writable_count; + + n = read_compact_u16(raw + pos, raw_len - pos, &readonly_count); + if (n < 0) return SOL_TX_REVIEW_MALFORMED; + pos += n; + if (pos + readonly_count > raw_len) return SOL_TX_REVIEW_MALFORMED; + pos += readonly_count; + } + + if (pos != raw_len) return SOL_TX_REVIEW_MALFORMED; + return SOL_TX_REVIEW_OPAQUE; +} + +SolanaTxReview solana_inspectTx(const uint8_t* raw, size_t raw_len, + SolanaParsedTx* tx) { + if (raw_len == 0) { + memset(tx, 0, sizeof(*tx)); + return SOL_TX_REVIEW_MALFORMED; + } + + /* Skip signature count prefix if present. + * Clients may send either the raw message (header starts at byte 0) + * or a full unsigned transaction (compact-u16 sig count = 0 at byte 0). + * A valid legacy message header has num_required_sigs >= 1 at byte 0. + * If byte 0 is 0, it's a signature count prefix — skip it. */ + if (raw[0] == 0 && raw_len > 1) { + raw++; + raw_len--; + } + + /* Versioned Solana messages set the top bit in byte 0. + * Parse them structurally so malformed v0/ALT payloads fail closed, + * but keep the result opaque until the firmware can verify semantics. */ + if (raw[0] & SOL_VERSION_FLAG) { + return solana_parseVersionedTx(raw, raw_len, tx); + } + + return solana_parseLegacyTx(raw, raw_len, tx); +} + +bool solana_parseTx(const uint8_t* raw, size_t raw_len, SolanaParsedTx* tx) { + return solana_inspectTx(raw, raw_len, tx) == SOL_TX_REVIEW_VERIFIED; +} + +/* ------------------------------------------------------------------ */ +/* Formatting */ +/* ------------------------------------------------------------------ */ + +void solana_formatAmount(char* buf, size_t len, uint64_t lamports) { + uint64_t whole = lamports / SOL_LAMPORTS_DIVISOR; + uint64_t frac = lamports % SOL_LAMPORTS_DIVISOR; + snprintf(buf, len, "%llu.%09llu SOL", (unsigned long long)whole, + (unsigned long long)frac); +} + +void solana_formatTokenAmount(char* buf, size_t len, uint64_t amount, + const char* symbol, uint8_t decimals) { + if (decimals == 0 || decimals > SOL_MAX_TOKEN_DECIMALS) { + snprintf(buf, len, "%llu %s", (unsigned long long)amount, symbol); + return; + } + + uint64_t divisor = 1; + for (uint8_t i = 0; i < decimals; i++) divisor *= 10; + + uint64_t whole = amount / divisor; + uint64_t frac = amount % divisor; + + /* Format with appropriate decimal places (max 9 shown) */ + uint8_t show_dec = + decimals > SOL_MAX_DISPLAY_DECIMALS ? SOL_MAX_DISPLAY_DECIMALS : decimals; + uint64_t show_div = 1; + for (uint8_t i = 0; i < show_dec; i++) show_div *= 10; + (void)show_div; + uint64_t show_frac = frac; + if (decimals > SOL_MAX_DISPLAY_DECIMALS) { + for (uint8_t i = 0; i < decimals - SOL_MAX_DISPLAY_DECIMALS; i++) + show_frac /= 10; + } + + char frac_str[10]; + for (int8_t i = (int8_t)show_dec - 1; i >= 0; i--) { + frac_str[i] = '0' + (show_frac % 10); + show_frac /= 10; + } + frac_str[show_dec] = '\0'; + snprintf(buf, len, "%llu.%s %s", (unsigned long long)whole, frac_str, symbol); +} + +const SolanaTokenInfo* solana_findTokenInfo( + const SolanaSignTx* msg, const uint8_t mint[SOL_PUBKEY_SIZE]) { + for (size_t i = 0; i < msg->token_info_count; i++) { + if (msg->token_info[i].has_mint && + msg->token_info[i].mint.size == SOL_PUBKEY_SIZE && + memcmp(msg->token_info[i].mint.bytes, mint, SOL_PUBKEY_SIZE) == 0) { + return &msg->token_info[i]; + } + } + return NULL; +} + +/* ------------------------------------------------------------------ */ +/* Signing */ +/* ------------------------------------------------------------------ */ + +bool solana_signTx(const HDNode* node, const SolanaSignTx* msg, + SolanaSignedTx* resp) { + if (!msg->has_raw_tx || msg->raw_tx.size == 0) return false; + + /* Ed25519 sign the raw transaction message directly + * (Solana signs the serialized message, not a hash of it) */ + uint8_t sig[SOL_SIG_SIZE]; + ed25519_sign(msg->raw_tx.bytes, msg->raw_tx.size, node->private_key, + node->public_key + 1, sig); + + resp->has_signature = true; + resp->signature.size = SOL_SIG_SIZE; + memcpy(resp->signature.bytes, sig, SOL_SIG_SIZE); + + return true; +} diff --git a/lib/firmware/storage.c b/lib/firmware/storage.c index 211493ac9..c1cd77d12 100644 --- a/lib/firmware/storage.c +++ b/lib/firmware/storage.c @@ -33,7 +33,6 @@ #include "keepkey/board/confirm_sm.h" #include "keepkey/firmware/home_sm.h" - #include "keepkey/board/common.h" #include "keepkey/board/supervise.h" #include "keepkey/board/keepkey_board.h" @@ -60,10 +59,11 @@ #include #include -/* -The PIN_ITER defines below changed between storage version 15 and 16 to eliminate -the unacceptable multi-second wait while the pin was being stretched for a dubious -claim to better security. The defines help during upgrades from v15 to v16 +/* +The PIN_ITER defines below changed between storage version 15 and 16 to +eliminate the unacceptable multi-second wait while the pin was being stretched +for a dubious claim to better security. The defines help during upgrades from +v15 to v16 */ #if defined(EMULATOR) || defined(DEBUG_ON) @@ -103,8 +103,8 @@ static void get_u2froot_callback(uint32_t iter, uint32_t total) { layoutProgress(_("Updating"), 1000 * iter / total); } -static void storage_compute_u2froot(SessionState *ss, const char *mnemonic, - HDNodeType *u2froot) { +static void storage_compute_u2froot(SessionState* ss, const char* mnemonic, + HDNodeType* u2froot) { static CONFIDENTIAL HDNode node; mnemonic_to_seed(mnemonic, "", ss->seed, get_u2froot_callback); // BIP-0039 hdnode_from_seed(ss->seed, 64, NIST256P1_NAME, &node); @@ -120,7 +120,7 @@ static void storage_compute_u2froot(SessionState *ss, const char *mnemonic, memzero(&node, sizeof(node)); } -bool storage_getU2FRoot(HDNode *node) { +bool storage_getU2FRoot(HDNode* node) { return shadow_config.storage.pub.has_u2froot && hdnode_from_xprv(shadow_config.storage.pub.u2froot.depth, shadow_config.storage.pub.u2froot.child_num, @@ -140,12 +140,12 @@ void storage_setU2FCounter(uint32_t u2f_counter) { storage_commit(); } -static bool storage_isActiveSector(const char *flash) { - return memcmp(((const Metadata *)flash)->magic, STORAGE_MAGIC_STR, +static bool storage_isActiveSector(const char* flash) { + return memcmp(((const Metadata*)flash)->magic, STORAGE_MAGIC_STR, STORAGE_MAGIC_LEN) == 0; } -void storage_upgradePolicies(Storage *storage) { +void storage_upgradePolicies(Storage* storage) { for (int i = storage->pub.policies_count; i < (int)(POLICY_COUNT); ++i) { memcpy(&storage->pub.policies[i], &policies[i], sizeof(storage->pub.policies[i])); @@ -153,32 +153,32 @@ void storage_upgradePolicies(Storage *storage) { storage->pub.policies_count = POLICY_COUNT; } -void storage_resetPolicies(Storage *storage) { +void storage_resetPolicies(Storage* storage) { storage->pub.policies_count = 0; storage_upgradePolicies(storage); } -void storage_resetCache(Cache *cache) { memset(cache, 0, sizeof(*cache)); } +void storage_resetCache(Cache* cache) { memset(cache, 0, sizeof(*cache)); } -static uint8_t read_u8(const char *ptr) { return *ptr; } +static uint8_t read_u8(const char* ptr) { return *ptr; } -static void write_u8(char *ptr, uint8_t val) { *ptr = val; } +static void write_u8(char* ptr, uint8_t val) { *ptr = val; } -static uint32_t read_u32_le(const char *ptr) { +static uint32_t read_u32_le(const char* ptr) { return ((uint32_t)ptr[0]) | ((uint32_t)ptr[1]) << 8 | ((uint32_t)ptr[2]) << 16 | ((uint32_t)ptr[3]) << 24; } -static void write_u32_le(char *ptr, uint32_t val) { +static void write_u32_le(char* ptr, uint32_t val) { ptr[0] = val & 0xff; ptr[1] = (val >> 8) & 0xff; ptr[2] = (val >> 16) & 0xff; ptr[3] = (val >> 24) & 0xff; } -static bool read_bool(const char *ptr) { return *ptr; } +static bool read_bool(const char* ptr) { return *ptr; } -static void write_bool(char *ptr, bool val) { *ptr = val ? 1 : 0; } +static void write_bool(char* ptr, bool val) { *ptr = val ? 1 : 0; } enum StorageVersion { StorageVersion_NONE, @@ -187,9 +187,10 @@ enum StorageVersion { }; static enum StorageVersion version_from_int(int version) { -#define STORAGE_VERSION_LAST(VAL) \ - _Static_assert(VAL == STORAGE_VERSION, "need to update " \ - "storage_versions.inc"); +#define STORAGE_VERSION_LAST(VAL) \ + _Static_assert(VAL == STORAGE_VERSION, \ + "need to update " \ + "storage_versions.inc"); #include "storage_versions.inc" switch (version) { @@ -202,21 +203,21 @@ static enum StorageVersion version_from_int(int version) { } } -void storage_readMeta(Metadata *meta, const char *ptr, size_t len) { +void storage_readMeta(Metadata* meta, const char* ptr, size_t len) { if (len < 16 + STORAGE_UUID_STR_LEN) return; memcpy(meta->magic, ptr, STORAGE_MAGIC_LEN); memcpy(meta->uuid, ptr + 4, STORAGE_UUID_LEN); memcpy(meta->uuid_str, ptr + 16, STORAGE_UUID_STR_LEN); } -void storage_writeMeta(char *ptr, size_t len, const Metadata *meta) { +void storage_writeMeta(char* ptr, size_t len, const Metadata* meta) { if (len < 16 + STORAGE_UUID_STR_LEN) return; memcpy(ptr, meta->magic, STORAGE_MAGIC_LEN); memcpy(ptr + 4, meta->uuid, STORAGE_UUID_LEN); memcpy(ptr + 16, meta->uuid_str, STORAGE_UUID_STR_LEN); } -void storage_readPolicyV1(PolicyType *policy, const char *ptr, size_t len) { +void storage_readPolicyV1(PolicyType* policy, const char* ptr, size_t len) { if (len < 17) return; policy->has_policy_name = read_bool(ptr); memset(policy->policy_name, 0, sizeof(policy->policy_name)); @@ -225,7 +226,7 @@ void storage_readPolicyV1(PolicyType *policy, const char *ptr, size_t len) { policy->enabled = read_bool(ptr + 17); } -void storage_readPolicyV2(PolicyType *policy, const char *policy_name, +void storage_readPolicyV2(PolicyType* policy, const char* policy_name, bool enabled) { policy->has_policy_name = true; memset(policy->policy_name, 0, sizeof(policy->policy_name)); @@ -234,7 +235,7 @@ void storage_readPolicyV2(PolicyType *policy, const char *policy_name, policy->enabled = enabled; } -void storage_writePolicyV1(char *ptr, size_t len, const PolicyType *policy) { +void storage_writePolicyV1(char* ptr, size_t len, const PolicyType* policy) { if (len < 17) return; write_bool(ptr, policy->has_policy_name); memcpy(ptr + 1, policy->policy_name, 15); @@ -242,7 +243,7 @@ void storage_writePolicyV1(char *ptr, size_t len, const PolicyType *policy) { write_bool(ptr + 17, policy->enabled); } -void storage_readHDNode(HDNodeType *node, const char *ptr, size_t len) { +void storage_readHDNode(HDNodeType* node, const char* ptr, size_t len) { if (len < 96 + 33) return; node->depth = read_u32_le(ptr); node->fingerprint = read_u32_le(ptr + 4); @@ -257,7 +258,7 @@ void storage_readHDNode(HDNodeType *node, const char *ptr, size_t len) { memcpy(node->public_key.bytes, ptr + 96, 33); } -void storage_writeHDNode(char *ptr, size_t len, const HDNodeType *node) { +void storage_writeHDNode(char* ptr, size_t len, const HDNodeType* node) { if (len < 96 + 33) return; write_u32_le(ptr, node->depth); write_u32_le(ptr + 4, node->fingerprint); @@ -278,20 +279,19 @@ void storage_writeHDNode(char *ptr, size_t len, const HDNodeType *node) { memcpy(ptr + 96, node->public_key.bytes, 33); } -void storage_deriveWrappingKey(const char *pin, uint8_t wrapping_key[64], - bool sca_hardened, - bool v15_16_trans, - uint8_t random_salt[RANDOM_SALT_LEN], - const char *message) { +void storage_deriveWrappingKey(const char* pin, uint8_t wrapping_key[64], + bool sca_hardened, bool v15_16_trans, + const uint8_t random_salt[RANDOM_SALT_LEN], + const char* message) { size_t pin_len = strlen(pin); if (sca_hardened && pin_len > 0) { uint8_t salt[HW_ENTROPY_LEN + RANDOM_SALT_LEN]; int iterCount, iterChunk; - if (v15_16_trans) { // can use new counts + if (v15_16_trans) { // can use new counts iterCount = PIN_ITER_COUNT_v16; iterChunk = PIN_ITER_CHUNK_v16; - } else { // need to use storage version 15 counts to derive wrap key + } else { // need to use storage version 15 counts to derive wrap key iterCount = PIN_ITER_COUNT_v15; iterChunk = PIN_ITER_CHUNK_v15; } @@ -301,7 +301,7 @@ void storage_deriveWrappingKey(const char *pin, uint8_t wrapping_key[64], memcpy(salt + HW_ENTROPY_LEN, random_salt, RANDOM_SALT_LEN); PBKDF2_HMAC_SHA256_CTX ctx = {0}; - pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t *)pin, pin_len, salt, + pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t*)pin, pin_len, salt, sizeof(salt), 1); for (int i = 0; i < iterCount; i += iterChunk) { @@ -311,11 +311,10 @@ void storage_deriveWrappingKey(const char *pin, uint8_t wrapping_key[64], pbkdf2_hmac_sha256_Final(&ctx, wrapping_key); memzero(&ctx, sizeof(ctx)); - pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t *)pin, pin_len, salt, + pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t*)pin, pin_len, salt, sizeof(salt), 2); for (int i = 0; i < iterCount; i += iterChunk) { - layoutProgress(message, - 1000 * (i + iterCount) / (iterCount * 2)); + layoutProgress(message, 1000 * (i + iterCount) / (iterCount * 2)); pbkdf2_hmac_sha256_Update(&ctx, iterChunk); } layoutProgress(message, 1000); @@ -324,7 +323,7 @@ void storage_deriveWrappingKey(const char *pin, uint8_t wrapping_key[64], memzero(salt, sizeof(salt)); } else { - sha512_Raw((const uint8_t *)pin, pin_len, wrapping_key); + sha512_Raw((const uint8_t*)pin, pin_len, wrapping_key); } } @@ -360,10 +359,9 @@ void storage_keyFingerprint(const uint8_t key[64], uint8_t fingerprint[32]) { sha256_Raw(key, 64, fingerprint); } -pintest_t storage_isPinCorrect_impl(const char *pin, uint8_t wrapped_key[64], +pintest_t storage_isPinCorrect_impl(const char* pin, uint8_t wrapped_key[64], const uint8_t fingerprint[32], - bool *sca_hardened, - bool *v15_16_trans, + bool* sca_hardened, bool* v15_16_trans, uint8_t key[64], uint8_t random_salt[RANDOM_SALT_LEN]) { /* @@ -379,8 +377,8 @@ pintest_t storage_isPinCorrect_impl(const char *pin, uint8_t wrapped_key[64], required to update the flash with a storage_commit(). */ uint8_t wrapping_key[64]; - storage_deriveWrappingKey(pin, wrapping_key, *sca_hardened, *v15_16_trans, random_salt, - _("Verifying PIN")); + storage_deriveWrappingKey(pin, wrapping_key, *sca_hardened, *v15_16_trans, + random_salt, _("Verifying PIN")); // unwrap the storage key for fingerprint test if (*sca_hardened) { @@ -398,20 +396,19 @@ pintest_t storage_isPinCorrect_impl(const char *pin, uint8_t wrapped_key[64], if (memcmp_s(fp, fingerprint, 32) == 0) ret = PIN_GOOD; if (ret == PIN_GOOD) { - if (!*sca_hardened || (*sca_hardened && !*v15_16_trans)) { + if (!*sca_hardened || !*v15_16_trans) { // PIN is correct but: // 1. wrapping key needs to be regenerated using stretched key // 2. storage key needs a rewrap with new wrapping key and algorithm - storage_deriveWrappingKey(pin, wrapping_key, - true /* sca_hardened */, - true /* v15_16_trans */, - random_salt, _("Verifying PIN")); + storage_deriveWrappingKey(pin, wrapping_key, true /* sca_hardened */, + true /* v15_16_trans */, random_salt, + _("Verifying PIN")); storage_wrapStorageKey(wrapping_key, key, wrapped_key); *sca_hardened = true; *v15_16_trans = true; ret = PIN_REWRAP; - } } + } if (!ret) memzero(key, 64); memzero(wrapping_key, 64); @@ -419,7 +416,7 @@ pintest_t storage_isPinCorrect_impl(const char *pin, uint8_t wrapped_key[64], return ret; } -pintest_t storage_isWipeCodeCorrect_impl(const char *wipe_code, +pintest_t storage_isWipeCodeCorrect_impl(const char* wipe_code, uint8_t wrapped_key[64], const uint8_t fingerprint[32], uint8_t key[64], @@ -447,7 +444,7 @@ pintest_t storage_isWipeCodeCorrect_impl(const char *wipe_code, return ret; } -void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { +void storage_secMigrate(SessionState* ss, Storage* storage, bool encrypt) { static CONFIDENTIAL char scratch[V17_ENCSEC_SIZE]; _Static_assert(sizeof(scratch) == sizeof(storage->encrypted_sec), "Be extermely careful when changing the size of scratch."); @@ -462,13 +459,14 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { storage_writeHDNode(&scratch[0], 129, &storage->sec.node); memcpy(&scratch[0] + 129, storage->sec.mnemonic, 241); storage_writeCacheV1(&scratch[0] + 370, 75, &storage->sec.cache); - memcpy(&scratch[0] + 512, &storage->sec.authBlock, sizeof(storage->sec.authBlock)); + memcpy(&scratch[0] + 512, &storage->sec.authBlock, + sizeof(storage->sec.authBlock)); // 129 reserved bytes // Take a fingerprint of the secrets so we can tell whether they've // been correctly decrypted later. storage->has_sec_fingerprint = true; - sha256_Raw((const uint8_t *)scratch, sizeof(scratch), + sha256_Raw((const uint8_t*)scratch, sizeof(scratch), storage->sec_fingerprint); // Encrypt with the storage key. @@ -476,7 +474,7 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { memcpy(iv, ss->storageKey, sizeof(iv)); aes_encrypt_ctx ctx; aes_encrypt_key256(ss->storageKey, &ctx); - aes_cbc_encrypt((const uint8_t *)scratch, storage->encrypted_sec, + aes_cbc_encrypt((const uint8_t*)scratch, storage->encrypted_sec, sizeof(scratch), iv + 32, &ctx); memzero(&ctx, sizeof(ctx)); storage->encrypted_sec_version = STORAGE_VERSION; @@ -490,11 +488,11 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { aes_decrypt_ctx ctx; aes_decrypt_key256(ss->storageKey, &ctx); if (storage->encrypted_sec_version <= StorageVersion_16) { - aes_cbc_decrypt((const uint8_t *)storage->encrypted_sec, - (uint8_t *)&scratch[0], V16_ENCSEC_SIZE, iv + 32, &ctx); + aes_cbc_decrypt((const uint8_t*)storage->encrypted_sec, + (uint8_t*)&scratch[0], V16_ENCSEC_SIZE, iv + 32, &ctx); } else { - aes_cbc_decrypt((const uint8_t *)storage->encrypted_sec, - (uint8_t *)&scratch[0], sizeof(scratch), iv + 32, &ctx); + aes_cbc_decrypt((const uint8_t*)storage->encrypted_sec, + (uint8_t*)&scratch[0], sizeof(scratch), iv + 32, &ctx); } memzero(iv, sizeof(iv)); @@ -508,9 +506,9 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { // Check whether the secrets were correctly decrypted uint8_t sec_fingerprint[32]; if (storage->encrypted_sec_version <= StorageVersion_16) { - sha256_Raw((const uint8_t *)scratch, V16_ENCSEC_SIZE, sec_fingerprint); + sha256_Raw((const uint8_t*)scratch, V16_ENCSEC_SIZE, sec_fingerprint); } else { - sha256_Raw((const uint8_t *)scratch, sizeof(scratch), sec_fingerprint); + sha256_Raw((const uint8_t*)scratch, sizeof(scratch), sec_fingerprint); } if (storage->has_sec_fingerprint) { if (memcmp_s(storage->sec_fingerprint, sec_fingerprint, @@ -536,10 +534,12 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { if (storage->encrypted_sec_version <= StorageVersion_16) { // initialie authblock to zero and initialize the fingerprint memzero(&scratch[512], sizeof(storage->sec.authBlock)); - sha256_Raw((const uint8_t *)&scratch[512], sizeof(storage->sec.authBlock), storage->pub.authdata_fingerprint); + sha256_Raw((const uint8_t*)&scratch[512], sizeof(storage->sec.authBlock), + storage->pub.authdata_fingerprint); storage->pub.authdata_initialized = true; } else { - memcpy((void *)&storage->sec.authBlock, &scratch[512], sizeof(storage->sec.authBlock)); + memcpy((void*)&storage->sec.authBlock, &scratch[512], + sizeof(storage->sec.authBlock)); } #if DEBUG_LINK @@ -554,92 +554,106 @@ void storage_secMigrate(SessionState *ss, Storage *storage, bool encrypt) { memzero(scratch, sizeof(scratch)); } -#define AUTHDATA_BLOCKSIZE 512 +#define AUTHDATA_BLOCKSIZE 512 -void storage_deriveAuthdataKey(const char *passphrase, uint8_t authdataKey[64]) { +void storage_deriveAuthdataKey(const char* passphrase, + uint8_t authdataKey[64]) { storage_deriveWrappingKey(passphrase, authdataKey, - /*sca_hardened*/ true, - /*v15_16_trans*/ true, - shadow_config.storage.pub.random_salt, - "deriving authdata key"); + /*sca_hardened*/ true, + /*v15_16_trans*/ true, + shadow_config.storage.pub.random_salt, + "deriving authdata key"); return; } - -static void storage_cipherBlock(bool encrypt, uint8_t *key, - uint8_t *plaintextBlock, uint8_t *ciphertextBlock, size_t blockSize) { +static void storage_cipherBlock(bool encrypt, const uint8_t* key, + uint8_t* plaintextBlock, + uint8_t* ciphertextBlock, size_t blockSize) { uint8_t iv[64]; if (encrypt) { memcpy(iv, key, sizeof(iv)); aes_encrypt_ctx ctx; aes_encrypt_key256(key, &ctx); - aes_cbc_encrypt((const uint8_t *)plaintextBlock, ciphertextBlock, - blockSize, iv + 32, &ctx); + aes_cbc_encrypt((const uint8_t*)plaintextBlock, ciphertextBlock, blockSize, + iv + 32, &ctx); memzero(&ctx, sizeof(ctx)); } else { // decrypt memcpy(iv, key, sizeof(iv)); aes_decrypt_ctx ctx; aes_decrypt_key256(key, &ctx); - aes_cbc_decrypt((const uint8_t *)ciphertextBlock, - plaintextBlock, blockSize, iv + 32, &ctx); + aes_cbc_decrypt((const uint8_t*)ciphertextBlock, plaintextBlock, blockSize, + iv + 32, &ctx); memzero(iv, sizeof(iv)); } - + return; } void storage_wipeAuthData() { authBlockType plaintextAuthBlock = {0}; - memzero((void *)&plaintextAuthBlock, sizeof(plaintextAuthBlock.authData)); + memzero((void*)&plaintextAuthBlock, sizeof(plaintextAuthBlock.authData)); // randomize remaining zeros in reserved area - random_buffer(plaintextAuthBlock.reserved, sizeof(plaintextAuthBlock.reserved)); + random_buffer(plaintextAuthBlock.reserved, + sizeof(plaintextAuthBlock.reserved)); // fingerprint authdata - sha256_Raw((const uint8_t *)&plaintextAuthBlock, sizeof(plaintextAuthBlock), shadow_config.storage.pub.authdata_fingerprint); + sha256_Raw((const uint8_t*)&plaintextAuthBlock, sizeof(plaintextAuthBlock), + shadow_config.storage.pub.authdata_fingerprint); shadow_config.storage.pub.authdata_encrypted = false; - memcpy((void *)&shadow_config.storage.sec.authBlock, (const void *)&plaintextAuthBlock, sizeof(shadow_config.storage.sec.authBlock)); + memcpy((void*)&shadow_config.storage.sec.authBlock, + (const void*)&plaintextAuthBlock, + sizeof(shadow_config.storage.sec.authBlock)); storage_commit(); - memzero((void *)&plaintextAuthBlock, sizeof(plaintextAuthBlock)); + memzero((void*)&plaintextAuthBlock, sizeof(plaintextAuthBlock)); return; } -bool storage_getAuthData(authType *returnData) { +bool storage_getAuthData(authType* returnData) { uint8_t authdataKey[64] = {0}; uint8_t testFp[32] = {0}; authBlockType plaintextAuthBlock = {0}; - if (shadow_config.storage.pub.passphrase_protection && - session.passphraseCached && - 0 != strlen(session.passphrase) && - !shadow_config.storage.pub.authdata_encrypted) { - // This is the case where we have a non-null passphrase but the stored authblock is in plaintext. - // This happens in the special case where a passphrase is first entered or an upgrade to firmware that supports - // the authenticator feature (upgrade from storage version <= 16). Read-encrypt-write the authblock. - memcpy((uint8_t *)&plaintextAuthBlock, (unsigned char *)&shadow_config.storage.sec.authBlock, - sizeof(shadow_config.storage.sec.authBlock)); + if (shadow_config.storage.pub.passphrase_protection && + session.passphraseCached && 0 != strlen(session.passphrase) && + !shadow_config.storage.pub.authdata_encrypted) { + // This is the case where we have a non-null passphrase but the stored + // authblock is in plaintext. This happens in the special case where a + // passphrase is first entered or an upgrade to firmware that supports the + // authenticator feature (upgrade from storage version <= 16). + // Read-encrypt-write the authblock. + memcpy((uint8_t*)&plaintextAuthBlock, + (unsigned char*)&shadow_config.storage.sec.authBlock, + sizeof(shadow_config.storage.sec.authBlock)); storage_deriveAuthdataKey(session.passphrase, authdataKey); - storage_cipherBlock(true /*encrypt*/, authdataKey, (unsigned char *)&plaintextAuthBlock, - (unsigned char *)&shadow_config.storage.sec.authBlock, sizeof(shadow_config.storage.sec.authBlock)); + storage_cipherBlock(true /*encrypt*/, authdataKey, + (unsigned char*)&plaintextAuthBlock, + (unsigned char*)&shadow_config.storage.sec.authBlock, + sizeof(shadow_config.storage.sec.authBlock)); shadow_config.storage.pub.authdata_encrypted = true; storage_commit(); } - // copy the auth block to candidate plaintext block and determine if it is encrypted - memcpy((uint8_t *)&plaintextAuthBlock, (unsigned char *)&shadow_config.storage.sec.authBlock, - sizeof(shadow_config.storage.sec.authBlock)); + // copy the auth block to candidate plaintext block and determine if it is + // encrypted + memcpy((uint8_t*)&plaintextAuthBlock, + (unsigned char*)&shadow_config.storage.sec.authBlock, + sizeof(shadow_config.storage.sec.authBlock)); if (shadow_config.storage.pub.authdata_encrypted) { - if (shadow_config.storage.pub.passphrase_protection && session.passphraseCached && 0 != strlen(session.passphrase)) { + if (shadow_config.storage.pub.passphrase_protection && + session.passphraseCached && 0 != strlen(session.passphrase)) { // encrypted, candidate passphrase available storage_deriveAuthdataKey(session.passphrase, authdataKey); - storage_cipherBlock(false /*encrypt*/, authdataKey, (unsigned char *)&plaintextAuthBlock, - (unsigned char *)&shadow_config.storage.sec.authBlock, sizeof(shadow_config.storage.sec.authBlock)); + storage_cipherBlock(false /*encrypt*/, authdataKey, + (unsigned char*)&plaintextAuthBlock, + (unsigned char*)&shadow_config.storage.sec.authBlock, + sizeof(shadow_config.storage.sec.authBlock)); } else { // encrypted, passphrase not available return false; @@ -649,47 +663,58 @@ bool storage_getAuthData(authType *returnData) { } // fingerprint authdata - sha256_Raw((const uint8_t *)&plaintextAuthBlock, sizeof(plaintextAuthBlock), testFp); + sha256_Raw((const uint8_t*)&plaintextAuthBlock, sizeof(plaintextAuthBlock), + testFp); - // if fingerprints don't match, passphrase, or lack thereof, is not valid for authdata - if (0 != memcmp(shadow_config.storage.pub.authdata_fingerprint, testFp, sizeof(shadow_config.storage.pub.authdata_fingerprint))) { + // if fingerprints don't match, passphrase, or lack thereof, is not valid for + // authdata + if (0 != memcmp(shadow_config.storage.pub.authdata_fingerprint, testFp, + sizeof(shadow_config.storage.pub.authdata_fingerprint))) { return false; } - memcpy(returnData, plaintextAuthBlock.authData, sizeof(plaintextAuthBlock.authData)); + memcpy(returnData, plaintextAuthBlock.authData, + sizeof(plaintextAuthBlock.authData)); return true; - } -void storage_setAuthData(authType *setData) { +void storage_setAuthData(const authType* setData) { authBlockType plaintextAuthBlock = {0}; - uint8_t authdataKey[64] = {0}; - memcpy((void *)&plaintextAuthBlock, (void *)setData, sizeof(plaintextAuthBlock.authData)); + memcpy((void*)&plaintextAuthBlock, (void*)setData, + sizeof(plaintextAuthBlock.authData)); // randomize remaining zeros in reserved area - random_buffer(plaintextAuthBlock.reserved, sizeof(plaintextAuthBlock.reserved)); + random_buffer(plaintextAuthBlock.reserved, + sizeof(plaintextAuthBlock.reserved)); // fingerprint authdata - sha256_Raw((const uint8_t *)&plaintextAuthBlock, sizeof(plaintextAuthBlock), shadow_config.storage.pub.authdata_fingerprint); + sha256_Raw((const uint8_t*)&plaintextAuthBlock, sizeof(plaintextAuthBlock), + shadow_config.storage.pub.authdata_fingerprint); // encrypt if passphrase available - if (shadow_config.storage.pub.passphrase_protection && session.passphraseCached && 0 != strlen(session.passphrase)) { + if (shadow_config.storage.pub.passphrase_protection && + session.passphraseCached && 0 != strlen(session.passphrase)) { + uint8_t authdataKey[64] = {0}; storage_deriveAuthdataKey(session.passphrase, authdataKey); - storage_cipherBlock(true /*encrypt*/, authdataKey, (uint8_t *)&plaintextAuthBlock, (uint8_t *)&shadow_config.storage.sec.authBlock, + storage_cipherBlock(true /*encrypt*/, authdataKey, + (uint8_t*)&plaintextAuthBlock, + (uint8_t*)&shadow_config.storage.sec.authBlock, sizeof(shadow_config.storage.sec.authBlock)); shadow_config.storage.pub.authdata_encrypted = true; } else { // not encrypted - memcpy((void *)&shadow_config.storage.sec.authBlock, (const void *)&plaintextAuthBlock, sizeof(shadow_config.storage.sec.authBlock)); + memcpy((void*)&shadow_config.storage.sec.authBlock, + (const void*)&plaintextAuthBlock, + sizeof(shadow_config.storage.sec.authBlock)); } storage_commit(); - memzero((void *)&plaintextAuthBlock, sizeof(plaintextAuthBlock)); + memzero((void*)&plaintextAuthBlock, sizeof(plaintextAuthBlock)); return; } -void storage_readStorageV1(SessionState *ss, Storage *storage, const char *ptr, +void storage_readStorageV1(SessionState* ss, Storage* storage, const char* ptr, size_t len) { if (len < 464 + 17) return; storage->version = read_u32_le(ptr); @@ -758,7 +783,7 @@ void storage_readStorageV1(SessionState *ss, Storage *storage, const char *ptr, } } -void storage_writeStorageV11(char *ptr, size_t len, const Storage *storage) { +void storage_writeStorageV11(char* ptr, size_t len, const Storage* storage) { if (len < 852) return; write_u32_le(ptr, storage->version); @@ -777,6 +802,7 @@ void storage_writeStorageV11(char *ptr, size_t len, const Storage *storage) { (storage_isPolicyEnabled("AdvancedMode") ? (1u << 12) : 0) | (storage->pub.no_backup ? (1u << 13) : 0) | (storage->has_sec_fingerprint ? (1u << 14) : 0) | + // cppcheck-suppress badBitmaskCheck (storage->pub.sca_hardened ? (1u << 15) : 0) | /* reserved 31:16 */ 0; write_u32_le(ptr + 4, flags); @@ -813,7 +839,7 @@ void storage_writeStorageV11(char *ptr, size_t len, const Storage *storage) { memcpy(ptr + 468, storage->encrypted_sec, sizeof(storage->encrypted_sec)); } -void storage_readStorageV11(Storage *storage, const char *ptr, size_t len) { +void storage_readStorageV11(Storage* storage, const char* ptr, size_t len) { if (len < 852) return; storage->version = read_u32_le(ptr); @@ -873,7 +899,8 @@ void storage_readStorageV11(Storage *storage, const char *ptr, size_t len) { memcpy(storage->encrypted_sec, ptr + 468, sizeof(storage->encrypted_sec)); } -void storage_writeStorageV16Plaintext(char *ptr, size_t len, const Storage *storage) { +void storage_writeStorageV16Plaintext(char* ptr, size_t len, + const Storage* storage) { if (len < 852) return; write_u32_le(ptr, storage->version); @@ -894,6 +921,7 @@ void storage_writeStorageV16Plaintext(char *ptr, size_t len, const Storage *stor (storage->has_sec_fingerprint ? (1u << 14) : 0) | (storage->pub.sca_hardened ? (1u << 15) : 0) | (storage->pub.has_wipe_code ? (1u << 16) : 0) | + // cppcheck-suppress badBitmaskCheck (storage->pub.v15_16_trans ? (1u << 17) : 0) | /* reserved 31:18 */ 0; write_u32_le(ptr + 4, flags); @@ -920,7 +948,7 @@ void storage_writeStorageV16Plaintext(char *ptr, size_t len, const Storage *stor return; } -void storage_writeStorageV16(char *ptr, size_t len, const Storage *storage) { +void storage_writeStorageV16(char* ptr, size_t len, const Storage* storage) { // V16 shares the same non-secret storage format as V17 storage_writeStorageV16Plaintext(ptr, len, storage); @@ -938,8 +966,8 @@ void storage_writeStorageV16(char *ptr, size_t len, const Storage *storage) { memcpy(ptr + 1501, storage->encrypted_sec, sizeof(storage->encrypted_sec)); } - -void storage_readStorageV16Plaintext(Storage *storage, const char *ptr, size_t len) { +void storage_readStorageV16Plaintext(Storage* storage, const char* ptr, + size_t len) { if (len < 852) return; storage->version = read_u32_le(ptr); @@ -997,8 +1025,7 @@ void storage_readStorageV16Plaintext(Storage *storage, const char *ptr, size_t l return; } -void storage_readStorageV16(Storage *storage, const char *ptr, size_t len) { - +void storage_readStorageV16(Storage* storage, const char* ptr, size_t len) { // V16 shares the same non-secret storage format as V17 storage_readStorageV16Plaintext(storage, ptr, len); @@ -1010,7 +1037,7 @@ void storage_readStorageV16(Storage *storage, const char *ptr, size_t len) { memcpy(storage->encrypted_sec, ptr + 1501, V16_ENCSEC_SIZE); } -void storage_writeStorageV17(char *ptr, size_t len, const Storage *storage) { +void storage_writeStorageV17(char* ptr, size_t len, const Storage* storage) { // V16 shares most of the same non-secret storage format as V17 storage_writeStorageV16Plaintext(ptr, len, storage); @@ -1035,11 +1062,10 @@ void storage_writeStorageV17(char *ptr, size_t len, const Storage *storage) { memcpy(ptr + 1501, storage->encrypted_sec, sizeof(storage->encrypted_sec)); } -void storage_readStorageV17(Storage *storage, const char *ptr, size_t len) { - +void storage_readStorageV17(Storage* storage, const char* ptr, size_t len) { // V16 shares most of the same non-secret storage format as V17 storage_readStorageV16Plaintext(storage, ptr, len); - + // V17 additions uint32_t flags = read_u32_le(ptr + 4); storage->pub.authdata_initialized = flags & (1u << 18); @@ -1054,15 +1080,14 @@ void storage_readStorageV17(Storage *storage, const char *ptr, size_t len) { memcpy(storage->encrypted_sec, ptr + 1501, sizeof(storage->encrypted_sec)); } - -void storage_readCacheV1(Cache *cache, const char *ptr, size_t len) { +void storage_readCacheV1(Cache* cache, const char* ptr, size_t len) { if (len < 65 + 10) return; cache->root_seed_cache_status = read_u8(ptr); memcpy(cache->root_seed_cache, ptr + 1, 64); memcpy(cache->root_ecdsa_curve_type, ptr + 65, 10); } -void storage_writeCacheV1(char *ptr, size_t len, const Cache *cache) { +void storage_writeCacheV1(char* ptr, size_t len, const Cache* cache) { if (len < 65 + 10) return; write_u8(ptr, cache->root_seed_cache_status); memcpy(ptr + 1, cache->root_seed_cache, 64); @@ -1071,60 +1096,60 @@ void storage_writeCacheV1(char *ptr, size_t len, const Cache *cache) { _Static_assert(offsetof(Cache, root_seed_cache) == 1, "rsc"); _Static_assert(offsetof(Cache, root_ecdsa_curve_type) == 65, "rect"); -_Static_assert(sizeof(((Cache *)0)->root_ecdsa_curve_type) == 10, "rect"); +_Static_assert(sizeof(((Cache*)0)->root_ecdsa_curve_type) == 10, "rect"); -void storage_readV1(SessionState *ss, ConfigFlash *dst, const char *flash, +void storage_readV1(SessionState* ss, ConfigFlash* dst, const char* flash, size_t len) { if (len < 44 + 528) return; storage_readMeta(&dst->meta, flash, 44); storage_readStorageV1(ss, &dst->storage, flash + 44, 481); } -void storage_readV2(SessionState *ss, ConfigFlash *dst, const char *flash, +void storage_readV2(SessionState* ss, ConfigFlash* dst, const char* flash, size_t len) { if (len < 528 + 75) return; storage_readMeta(&dst->meta, flash, 44); storage_readStorageV1(ss, &dst->storage, flash + 44, 481); } -void storage_readV11(ConfigFlash *dst, const char *flash, size_t len) { +void storage_readV11(ConfigFlash* dst, const char* flash, size_t len) { if (len < 1024) return; storage_readMeta(&dst->meta, flash, 44); storage_readStorageV11(&dst->storage, flash + 44, 852); } -void storage_writeV11(char *flash, size_t len, const ConfigFlash *src) { +void storage_writeV11(char* flash, size_t len, const ConfigFlash* src) { if (len < 1024) return; storage_writeMeta(flash, 44, &src->meta); storage_writeStorageV11(flash + 44, 852, &src->storage); } -void storage_readV16(ConfigFlash *dst, const char *flash, size_t len) { +void storage_readV16(ConfigFlash* dst, const char* flash, size_t len) { if (len < 1024) return; storage_readMeta(&dst->meta, flash, 44); storage_readStorageV16(&dst->storage, flash + 44, 852); } -void storage_writeV16(char *flash, size_t len, const ConfigFlash *src) { +void storage_writeV16(char* flash, size_t len, const ConfigFlash* src) { if (len < 1024) return; storage_writeMeta(flash, 44, &src->meta); storage_writeStorageV16(flash + 44, 852, &src->storage); } -void storage_readV17(ConfigFlash *dst, const char *flash, size_t len) { +void storage_readV17(ConfigFlash* dst, const char* flash, size_t len) { if (len < 1024) return; storage_readMeta(&dst->meta, flash, 44); storage_readStorageV17(&dst->storage, flash + 44, 852); } -void storage_writeV17(char *flash, size_t len, const ConfigFlash *src) { +void storage_writeV17(char* flash, size_t len, const ConfigFlash* src) { if (len < 1024) return; storage_writeMeta(flash, 44, &src->meta); storage_writeStorageV17(flash + 44, 852, &src->storage); } -StorageUpdateStatus storage_fromFlash(SessionState *ss, ConfigFlash *dst, - const char *flash) { +StorageUpdateStatus storage_fromFlash(SessionState* ss, ConfigFlash* dst, + const char* flash) { memzero(dst, sizeof(*dst)); // Load config values from active config node. @@ -1226,8 +1251,8 @@ static void wear_leveling_shift(void) { /// \param cfg[in] The active storage sector. /// \param seed[in] Root seed to write into storage. /// \param curve[in] ECDSA curve name being used. -static void storage_setRootSeedCache(const SessionState *ss, ConfigFlash *cfg, - const uint8_t *seed, const char *curve) { +static void storage_setRootSeedCache(const SessionState* ss, ConfigFlash* cfg, + const uint8_t* seed, const char* curve) { // Don't cache when passphrase protection is enabled. if (cfg->storage.pub.passphrase_protection && strlen(ss->passphrase)) return; @@ -1250,9 +1275,9 @@ static void storage_setRootSeedCache(const SessionState *ss, ConfigFlash *cfg, /// \param curve[in] ECDSA curve name being used. /// \param seed[out] The root seed value. /// \returns true on success. -static bool storage_getRootSeedCache(const SessionState *ss, ConfigFlash *cfg, - const char *curve, bool usePassphrase, - uint8_t *seed) { +static bool storage_getRootSeedCache(const SessionState* ss, + const ConfigFlash* cfg, const char* curve, + bool usePassphrase, uint8_t* seed) { if (!cfg->storage.has_sec) return false; if (cfg->storage.sec.cache.root_seed_cache_status != CACHE_EXISTS) @@ -1282,7 +1307,7 @@ void storage_init(void) { // Otherwise initialize it to the default sector. storage_location = STORAGE_SECT_DEFAULT; } - const char *flash = (const char *)flash_write_helper(storage_location); + const char* flash = (const char*)flash_write_helper(storage_location); // Reset shadow configuration in RAM storage_reset_impl(&session, &shadow_config); @@ -1297,7 +1322,7 @@ void storage_init(void) { } // Otherwise clear out flash before looking for end config node. - memcpy(shadow_config.meta.uuid, ((const Metadata *)flash)->uuid, + memcpy(shadow_config.meta.uuid, ((const Metadata*)flash)->uuid, sizeof(shadow_config.meta.uuid)); data2hex(shadow_config.meta.uuid, sizeof(shadow_config.meta.uuid), shadow_config.meta.uuid_str); @@ -1329,20 +1354,20 @@ void storage_init(void) { void storage_resetUuid(void) { storage_resetUuid_impl(&shadow_config); } -void storage_resetUuid_impl(ConfigFlash *cfg) { +void storage_resetUuid_impl(ConfigFlash* cfg) { #ifdef EMULATOR random_buffer(cfg->meta.uuid, sizeof(cfg->meta.uuid)); #else _Static_assert(sizeof(cfg->meta.uuid) == 3 * sizeof(uint32_t), "uuid not large enough"); - desig_get_unique_id((uint32_t *)cfg->meta.uuid); + desig_get_unique_id((uint32_t*)cfg->meta.uuid); #endif data2hex(cfg->meta.uuid, sizeof(cfg->meta.uuid), cfg->meta.uuid_str); } void storage_reset(void) { storage_reset_impl(&session, &shadow_config); } -void storage_reset_impl(SessionState *ss, ConfigFlash *cfg) { +void storage_reset_impl(SessionState* ss, ConfigFlash* cfg) { memset(&cfg->storage, 0, sizeof(cfg->storage)); storage_resetPolicies(&cfg->storage); @@ -1382,7 +1407,7 @@ void session_clear(bool clear_pin) { } } -pintest_t session_clear_impl(SessionState *ss, Storage *storage, +pintest_t session_clear_impl(SessionState* ss, Storage* storage, bool clear_pin) { /* This is a *_impl() function that is assumed to not modify the flash @@ -1407,9 +1432,8 @@ pintest_t session_clear_impl(SessionState *ss, Storage *storage, if (!storage_hasPin_impl(storage)) { ret = storage_isPinCorrect_impl("", storage->pub.wrapped_storage_key, storage->pub.storage_key_fingerprint, - &storage->pub.sca_hardened, - &storage->pub.v15_16_trans, - ss->storageKey, + &storage->pub.sca_hardened, + &storage->pub.v15_16_trans, ss->storageKey, shadow_config.storage.pub.random_salt); if (ret == PIN_WRONG) { @@ -1476,20 +1500,20 @@ void storage_commit(void) { /* Write storage data first before writing storage magic */ if (!flash_write_word(storage_location, STORAGE_MAGIC_LEN, sizeof(flash_temp) - STORAGE_MAGIC_LEN, - (uint8_t *)flash_temp + STORAGE_MAGIC_LEN)) { + (uint8_t*)flash_temp + STORAGE_MAGIC_LEN)) { flash_erase_word(storage_location); continue; // Retry } if (!flash_write_word(storage_location, 0, STORAGE_MAGIC_LEN, - (uint8_t *)flash_temp)) { + (uint8_t*)flash_temp)) { flash_erase_word(storage_location); continue; // Retry } /* Flash write completed successfully. Verify CRC */ uint32_t shadow_flash_crc32 = - calc_crc32((const void *)flash_write_helper(storage_location), + calc_crc32((const void*)flash_write_helper(storage_location), sizeof(flash_temp) / sizeof(uint32_t)); if (shadow_flash_crc32 == shadow_ram_crc32) { @@ -1509,7 +1533,7 @@ void storage_commit(void) { } // Great candidate for C++ templates... sigh. -void storage_dumpNode(HDNodeType *dst, const HDNode *src) { +void storage_dumpNode(HDNodeType* dst, const HDNode* src) { #if DEBUG_LINK dst->depth = src->depth; dst->fingerprint = 0; @@ -1537,7 +1561,7 @@ void storage_dumpNode(HDNodeType *dst, const HDNode *src) { #endif } -void storage_loadNode(HDNode *dst, const HDNodeType *src) { +void storage_loadNode(HDNode* dst, const HDNodeType* src) { dst->depth = src->depth; dst->child_num = src->child_num; @@ -1563,7 +1587,7 @@ void storage_loadNode(HDNode *dst, const HDNodeType *src) { } } -void storage_loadDevice(LoadDevice *msg) { +void storage_loadDevice(LoadDevice* msg) { storage_reset_impl(&session, &shadow_config); shadow_config.storage.pub.imported = true; @@ -1613,7 +1637,7 @@ void storage_loadDevice(LoadDevice *msg) { } } -void storage_setLabel(const char *label) { +void storage_setLabel(const char* label) { if (!label) { return; } @@ -1625,7 +1649,7 @@ void storage_setLabel(const char *label) { sizeof(shadow_config.storage.pub.label)); } -const char *storage_getLabel(void) { +const char* storage_getLabel(void) { if (!shadow_config.storage.pub.has_label) { return NULL; } @@ -1633,7 +1657,7 @@ const char *storage_getLabel(void) { return shadow_config.storage.pub.label; } -void storage_setLanguage(const char *lang) { +void storage_setLanguage(const char* lang) { if (!lang) { return; } @@ -1648,7 +1672,7 @@ void storage_setLanguage(const char *lang) { } } -const char *storage_getLanguage(void) { +const char* storage_getLanguage(void) { if (!shadow_config.storage.pub.has_language) { return NULL; } @@ -1656,14 +1680,12 @@ const char *storage_getLanguage(void) { return shadow_config.storage.pub.language; } -bool storage_isPinCorrect(const char *pin) { - +bool storage_isPinCorrect(const char* pin) { pintest_t ret = storage_isPinCorrect_impl( pin, shadow_config.storage.pub.wrapped_storage_key, shadow_config.storage.pub.storage_key_fingerprint, - &shadow_config.storage.pub.sca_hardened, - &shadow_config.storage.pub.v15_16_trans, - session.storageKey, + &shadow_config.storage.pub.sca_hardened, + &shadow_config.storage.pub.v15_16_trans, session.storageKey, shadow_config.storage.pub.random_salt); switch (ret) { @@ -1691,11 +1713,11 @@ bool storage_hasPin(void) { return storage_hasPin_impl(&shadow_config.storage); } -bool storage_hasPin_impl(const Storage *storage) { +bool storage_hasPin_impl(const Storage* storage) { return storage->pub.has_pin; } -void storage_setPin(const char *pin) { +void storage_setPin(const char* pin) { storage_setPin_impl(&session, &shadow_config.storage, pin); session.pinCached = true; @@ -1705,12 +1727,12 @@ void storage_setPin(const char *pin) { #endif } -void storage_setPin_impl(SessionState *ss, Storage *storage, const char *pin) { +void storage_setPin_impl(SessionState* ss, Storage* storage, const char* pin) { // Derive the wrapping key for the new pin uint8_t wrapping_key[64]; - storage_deriveWrappingKey(pin, wrapping_key, /*sca_hardened=*/true, - /*v15_16_trans=*/true, - storage->pub.random_salt, _("Encrypting Secrets")); + storage_deriveWrappingKey(pin, wrapping_key, /*sca_hardened=*/true, + /*v15_16_trans=*/true, storage->pub.random_salt, + _("Encrypting Secrets")); // Derive a new storageKey. random_buffer(ss->storageKey, 64); @@ -1731,7 +1753,7 @@ void storage_setPin_impl(SessionState *ss, Storage *storage, const char *pin) { storage_secMigrate(ss, storage, /*encrypt=*/true); } -bool storage_isWipeCodeCorrect(const char *wipe_code) { +bool storage_isWipeCodeCorrect(const char* wipe_code) { uint8_t scratch_key[64]; pintest_t ret = storage_isWipeCodeCorrect_impl( wipe_code, shadow_config.storage.pub.wrapped_wipe_code_key, @@ -1751,11 +1773,11 @@ bool storage_hasWipeCode(void) { return storage_hasWipeCode_impl(&shadow_config.storage); } -bool storage_hasWipeCode_impl(const Storage *storage) { +bool storage_hasWipeCode_impl(const Storage* storage) { return storage->pub.has_wipe_code; } -void storage_setWipeCode(const char *wipe_code) { +void storage_setWipeCode(const char* wipe_code) { storage_setWipeCode_impl(&session, &shadow_config.storage, wipe_code); #if DEBUG_LINK @@ -1763,14 +1785,14 @@ void storage_setWipeCode(const char *wipe_code) { #endif } -void storage_setWipeCode_impl(SessionState *ss, Storage *storage, - const char *wipe_code) { +void storage_setWipeCode_impl(SessionState* ss, Storage* storage, + const char* wipe_code) { uint8_t scratch_key[64]; // Derive the wrapping key for the new wipe code uint8_t wrapping_key[64]; storage_deriveWrappingKey(wipe_code, wrapping_key, /*sca_hardened=*/true, - /*v15_16_trans=*/true, - storage->pub.random_salt, _("Updating Wipe Code")); + /*v15_16_trans=*/true, storage->pub.random_salt, + _("Updating Wipe Code")); // Derive a new wipe code key . random_buffer(scratch_key, 64); @@ -1817,7 +1839,7 @@ static void get_root_node_callback(uint32_t iter, uint32_t total) { animating_progress_handler(_("Waking up"), 1000 * iter / total); } -const uint8_t *storage_getSeed(const ConfigFlash *cfg, bool usePassphrase) { +const uint8_t* storage_getSeed(const ConfigFlash* cfg, bool usePassphrase) { // root node is properly cached if (usePassphrase == session.seedUsesPassphrase && session.seedCached) { return session.seed; @@ -1844,7 +1866,7 @@ const uint8_t *storage_getSeed(const ConfigFlash *cfg, bool usePassphrase) { return NULL; } -bool storage_getRootNode(const char *curve, bool usePassphrase, HDNode *node) { +bool storage_getRootNode(const char* curve, bool usePassphrase, HDNode* node) { // if storage has node, decrypt and use it if (shadow_config.storage.pub.has_node && strcmp(curve, SECP256K1_NAME) == 0) { @@ -1870,9 +1892,9 @@ bool storage_getRootNode(const char *curve, bool usePassphrase, HDNode *node) { // decrypt hd node static uint8_t CONFIDENTIAL secret[64]; PBKDF2_HMAC_SHA512_CTX pctx; - pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)session.passphrase, + pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t*)session.passphrase, strlen(session.passphrase), - (const uint8_t *)"TREZORHD", 8, 1); + (const uint8_t*)"TREZORHD", 8, 1); for (int i = 0; i < 8; i++) { pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8); get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, @@ -1912,6 +1934,7 @@ bool storage_getRootNode(const char *curve, bool usePassphrase, HDNode *node) { * sessionSeed/sessionSeedCached variables */ storage_getSeed(&shadow_config, usePassphrase); + // cppcheck-suppress identicalInnerCondition if (!session.seedCached) { return false; } @@ -1933,7 +1956,7 @@ bool storage_isInitialized(void) { shadow_config.storage.pub.has_mnemonic; } -const char *storage_getUuidStr(void) { return shadow_config.meta.uuid_str; } +const char* storage_getUuidStr(void) { return shadow_config.meta.uuid_str; } bool storage_getPassphraseProtected(void) { return shadow_config.storage.pub.passphrase_protection; @@ -1943,7 +1966,7 @@ void storage_setPassphraseProtected(bool passphrase) { shadow_config.storage.pub.passphrase_protection = passphrase; } -void session_cachePassphrase(const char *passphrase) { +void session_cachePassphrase(const char* passphrase) { strlcpy(session.passphrase, passphrase, sizeof(session.passphrase)); session.passphraseCached = true; } @@ -1977,7 +2000,7 @@ void storage_setMnemonicFromWords(const char (*words)[12], shadow_config.storage.pub.has_u2froot = true; } -void storage_setMnemonic(const char *m) { +void storage_setMnemonic(const char* m) { memset(shadow_config.storage.sec.mnemonic, 0, sizeof(shadow_config.storage.sec.mnemonic)); strlcpy(shadow_config.storage.sec.mnemonic, m, @@ -2001,18 +2024,18 @@ bool storage_hasMnemonic(void) { /* Check whether mnemonic matches storage. The mnemonic must be * a null-terminated string. */ -bool storage_containsMnemonic(const char *mnemonic) { +bool storage_containsMnemonic(const char* mnemonic) { if (!storage_hasMnemonic()) return false; if (!shadow_config.storage.has_sec) return false; // Compare the digests to mitigate side-channel attacks. uint8_t digest_stored[SHA256_DIGEST_LENGTH]; - sha256_Raw((const uint8_t *)shadow_config.storage.sec.mnemonic, + sha256_Raw((const uint8_t*)shadow_config.storage.sec.mnemonic, strnlen(shadow_config.storage.sec.mnemonic, MAX_MNEMONIC_LEN), digest_stored); uint8_t digest_input[SHA256_DIGEST_LENGTH]; - sha256_Raw((const uint8_t *)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN), + sha256_Raw((const uint8_t*)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN), digest_input); uint8_t diff = 0; @@ -2024,7 +2047,7 @@ bool storage_containsMnemonic(const char *mnemonic) { return diff == 0; } -const char *storage_getShadowMnemonic(void) { +const char* storage_getShadowMnemonic(void) { if (!shadow_config.storage.has_sec) return NULL; return shadow_config.storage.sec.mnemonic; } @@ -2037,13 +2060,13 @@ bool storage_hasNode(void) { return shadow_config.storage.pub.has_node; } Allocation storage_getLocation(void) { return storage_location; } -bool storage_setPolicy(const char *policy_name, bool enabled) { +bool storage_setPolicy(const char* policy_name, bool enabled) { return storage_setPolicy_impl(shadow_config.storage.pub.policies, policy_name, enabled); } bool storage_setPolicy_impl(PolicyType ps[POLICY_COUNT], - const char *policy_name, bool enabled) { + const char* policy_name, bool enabled) { for (unsigned i = 0; i < POLICY_COUNT; ++i) { if (strcmp(policy_name, ps[i].policy_name) == 0) { ps[i].has_enabled = true; @@ -2055,20 +2078,20 @@ bool storage_setPolicy_impl(PolicyType ps[POLICY_COUNT], return false; } -void storage_getPolicies(PolicyType *policy_data) { +void storage_getPolicies(PolicyType* policy_data) { for (size_t i = 0; i < POLICY_COUNT; ++i) { memcpy(&policy_data[i], &shadow_config.storage.pub.policies[i], sizeof(policy_data[i])); } } -bool storage_isPolicyEnabled(const char *policy_name) { +bool storage_isPolicyEnabled(const char* policy_name) { return storage_isPolicyEnabled_impl(shadow_config.storage.pub.policies, policy_name); } bool storage_isPolicyEnabled_impl(const PolicyType ps[POLICY_COUNT], - const char *policy_name) { + const char* policy_name) { for (unsigned i = 0; i < POLICY_COUNT; ++i) { if (strcmp(policy_name, ps[i].policy_name) == 0) { return ps[i].enabled; @@ -2095,11 +2118,11 @@ void storage_setAutoLockDelayMs(uint32_t auto_lock_delay_ms) { } #if DEBUG_LINK -const char *storage_getPin(void) { +const char* storage_getPin(void) { return shadow_config.storage.pub.has_pin ? debuglink_pin : NULL; } -const char *storage_getMnemonic(void) { return debuglink_mnemonic; } +const char* storage_getMnemonic(void) { return debuglink_mnemonic; } -HDNode *storage_getNode(void) { return &debuglink_node; } +HDNode* storage_getNode(void) { return &debuglink_node; } #endif diff --git a/lib/firmware/storage.h b/lib/firmware/storage.h index 7d23bd855..10b84217b 100644 --- a/lib/firmware/storage.h +++ b/lib/firmware/storage.h @@ -29,13 +29,13 @@ // The length of the external salt in bytes. #define EXTERNAL_SALT_SIZE 32 -#define V16_ENCSEC_SIZE 512 // for reading old encrypted sec size -#define V17_ENCSEC_SIZE 1024 +#define V16_ENCSEC_SIZE 512 // for reading old encrypted sec size +#define V17_ENCSEC_SIZE 1024 typedef struct _authBlockType { - authType authData[AUTHDATA_SIZE]; // 450 - uint8_t reserved[512-sizeof(authType)*AUTHDATA_SIZE]; // 62 -} authBlockType; + authType authData[AUTHDATA_SIZE]; // 450 + uint8_t reserved[512 - sizeof(authType) * AUTHDATA_SIZE]; // 62 +} authBlockType; typedef struct _Storage { uint32_t version; @@ -79,7 +79,7 @@ typedef struct _Storage { char mnemonic[241]; char pin[10]; Cache cache; - uint8_t authBlock[sizeof(authBlockType)]; + uint8_t authBlock[sizeof(authBlockType)]; } sec; bool has_sec_fingerprint; @@ -112,16 +112,15 @@ typedef enum { PIN_REWRAP // PIN correct but storage key rewrapped, requires storage update } pintest_t; -#define MAX_MNEMONIC_LEN 240 +#define MAX_MNEMONIC_LEN 240 -void storage_loadNode(HDNode *dst, const HDNodeType *src); +void storage_loadNode(HDNode* dst, const HDNodeType* src); /// Derive the wrapping key from the user's pin. -void storage_deriveWrappingKey(const char *pin, uint8_t wrapping_key[64], - bool sca_hardened, - bool v15_16_trans, - uint8_t random_salt[RANDOM_SALT_LEN], - const char *message); +void storage_deriveWrappingKey(const char* pin, uint8_t wrapping_key[64], + bool sca_hardened, bool v15_16_trans, + const uint8_t random_salt[RANDOM_SALT_LEN], + const char* message); /// Wrap the storage key. void storage_wrapStorageKey(const uint8_t wrapping_key[64], @@ -143,46 +142,44 @@ void storage_keyFingerprint(const uint8_t key[64], uint8_t fingerprint[32]); /// PIN_GOOD - PIN is correct /// PIN_REWRAPPED -> PIN is correct, storage key was rewrapped, CALLING /// FUNCTION SHOULD storage_commit() -pintest_t storage_isPinCorrect_impl(const char *pin, uint8_t wrapped_key[64], +pintest_t storage_isPinCorrect_impl(const char* pin, uint8_t wrapped_key[64], const uint8_t fingerprint[32], - bool *sca_hardened, - bool *v15_16_trans, + bool* sca_hardened, bool* v15_16_trans, uint8_t key[64], uint8_t random_salt[RANDOM_SALT_LEN]); -pintest_t storage_isWipeCodeCorrect_impl(const char *wipe_code, +pintest_t storage_isWipeCodeCorrect_impl(const char* wipe_code, uint8_t wrapped_key[64], const uint8_t fingerprint[32], uint8_t key[64], uint8_t random_salt[RANDOM_SALT_LEN]); /// Migrate data in Storage to/from sec/encrypted_sec. -void storage_secMigrate(SessionState *state, Storage *storage, bool encrypt); +void storage_secMigrate(SessionState* ss, Storage* storage, bool encrypt); -void storage_resetUuid_impl(ConfigFlash *cfg); +void storage_resetUuid_impl(ConfigFlash* cfg); -void storage_reset_impl(SessionState *session, ConfigFlash *cfg); +void storage_reset_impl(SessionState* ss, ConfigFlash* cfg); -void storage_setPin_impl(SessionState *session, Storage *storage, - const char *pin); +void storage_setPin_impl(SessionState* ss, Storage* storage, const char* pin); -bool storage_hasPin_impl(const Storage *storage); +bool storage_hasPin_impl(const Storage* storage); -void storage_setWipeCode_impl(SessionState *ss, Storage *storage, - const char *wipe_code); +void storage_setWipeCode_impl(SessionState* ss, Storage* storage, + const char* wipe_code); -bool storage_hasWipeCode_impl(const Storage *storage); +bool storage_hasWipeCode_impl(const Storage* storage); /// \return: PIN_WRONG - PIN is incorrect /// PIN_GOOD - PIN is correct /// PIN_REWRAP -> PIN is correct, storage key was rewrapped, CALLING /// FUNCTION SHOULD storage_commit() -pintest_t session_clear_impl(SessionState *session, Storage *storage, +pintest_t session_clear_impl(SessionState* ss, Storage* storage, bool clear_pin); /// \brief Get user private seed. /// \returns NULL on error, otherwise \returns the private seed. -const uint8_t *storage_getSeed(const ConfigFlash *cfg, bool usePassphrase); +const uint8_t* storage_getSeed(const ConfigFlash* cfg, bool usePassphrase); typedef enum { SUS_Invalid, @@ -193,43 +190,43 @@ typedef enum { /// \brief Copy configuration from storage partition in flash memory to shadow /// memory in RAM /// \returns true iff successful. -StorageUpdateStatus storage_fromFlash(SessionState *ss, ConfigFlash *dst, - const char *flash); +StorageUpdateStatus storage_fromFlash(SessionState* ss, ConfigFlash* dst, + const char* flash); -void storage_upgradePolicies(Storage *storage); -void storage_resetPolicies(Storage *storage); -void storage_resetCache(Cache *cache); +void storage_upgradePolicies(Storage* storage); +void storage_resetPolicies(Storage* storage); +void storage_resetCache(Cache* cache); -void storage_readV1(SessionState *session, ConfigFlash *dst, const char *ptr, +void storage_readV1(SessionState* ss, ConfigFlash* dst, const char* flash, size_t len); -void storage_readV2(SessionState *session, ConfigFlash *dst, const char *ptr, +void storage_readV2(SessionState* ss, ConfigFlash* dst, const char* flash, size_t len); -void storage_readV11(ConfigFlash *dst, const char *ptr, size_t len); -void storage_readV16(ConfigFlash *dst, const char *ptr, size_t len); -void storage_writeV11(char *ptr, size_t len, const ConfigFlash *src); -void storage_writeV16(char *ptr, size_t len, const ConfigFlash *src); - -void storage_readMeta(Metadata *meta, const char *ptr, size_t len); -void storage_readPolicyV1(PolicyType *policy, const char *ptr, size_t len); -void storage_readHDNode(HDNodeType *node, const char *ptr, size_t len); -void storage_readStorageV1(SessionState *session, Storage *storage, - const char *ptr, size_t len); -void storage_readStorageV11(Storage *storage, const char *ptr, size_t len); -void storage_readCacheV1(Cache *cache, const char *ptr, size_t len); - -void storage_writeMeta(char *ptr, size_t len, const Metadata *meta); -void storage_writePolicyV1(char *ptr, size_t len, const PolicyType *policy); -void storage_writeHDNode(char *ptr, size_t len, const HDNodeType *node); -void storage_writeStorageV11(char *ptr, size_t len, const Storage *storage); -void storage_writeCacheV1(char *ptr, size_t len, const Cache *cache); - -bool storage_setPolicy_impl(PolicyType policies[POLICY_COUNT], - const char *policy_name, bool enabled); -bool storage_isPolicyEnabled_impl(const PolicyType policies[POLICY_COUNT], - const char *policy_name); +void storage_readV11(ConfigFlash* dst, const char* flash, size_t len); +void storage_readV16(ConfigFlash* dst, const char* flash, size_t len); +void storage_writeV11(char* flash, size_t len, const ConfigFlash* src); +void storage_writeV16(char* flash, size_t len, const ConfigFlash* src); + +void storage_readMeta(Metadata* meta, const char* ptr, size_t len); +void storage_readPolicyV1(PolicyType* policy, const char* ptr, size_t len); +void storage_readHDNode(HDNodeType* node, const char* ptr, size_t len); +void storage_readStorageV1(SessionState* ss, Storage* storage, const char* ptr, + size_t len); +void storage_readStorageV11(Storage* storage, const char* ptr, size_t len); +void storage_readCacheV1(Cache* cache, const char* ptr, size_t len); + +void storage_writeMeta(char* ptr, size_t len, const Metadata* meta); +void storage_writePolicyV1(char* ptr, size_t len, const PolicyType* policy); +void storage_writeHDNode(char* ptr, size_t len, const HDNodeType* node); +void storage_writeStorageV11(char* ptr, size_t len, const Storage* storage); +void storage_writeCacheV1(char* ptr, size_t len, const Cache* cache); + +bool storage_setPolicy_impl(PolicyType ps[POLICY_COUNT], + const char* policy_name, bool enabled); +bool storage_isPolicyEnabled_impl(const PolicyType ps[POLICY_COUNT], + const char* policy_name); bool storageHasWipeCode(void); -bool storageChangeWipeCode(uint32_t pin, const uint8_t *ext_salt, +bool storageChangeWipeCode(uint32_t pin, const uint8_t* ext_salt, uint32_t wipe_code); #endif diff --git a/lib/firmware/tendermint.c b/lib/firmware/tendermint.c index 01fe86503..60d50a281 100644 --- a/lib/firmware/tendermint.c +++ b/lib/firmware/tendermint.c @@ -7,30 +7,30 @@ #include #include -static int convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) { - uint32_t val = 0; - int bits = 0; - uint32_t maxv = (((uint32_t)1) << outbits) - 1; - while (inlen--) { - val = (val << inbits) | *(in++); - bits += inbits; - while (bits >= outbits) { - bits -= outbits; - out[(*outlen)++] = (val >> bits) & maxv; - } +static int convert_bits(uint8_t* out, size_t* outlen, int outbits, + const uint8_t* in, size_t inlen, int inbits, int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while (inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while (bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; } - if (pad) { - if (bits) { - out[(*outlen)++] = (val << (outbits - bits)) & maxv; - } - } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { - return 0; + } + if (pad) { + if (bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; } - return 1; + } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; } - -bool tendermint_pathMismatched(const CoinType *coin, const uint32_t *address_n, +bool tendermint_pathMismatched(const CoinType* coin, const uint32_t* address_n, const uint32_t address_n_count) { // m / 44' / coin' / account' / 0 / 0 bool mismatch = false; @@ -52,24 +52,25 @@ bool tendermint_pathMismatched(const CoinType *coin, const uint32_t *address_n, * * \returns true if successful */ -bool tendermint_getAddress(const HDNode *node, const char *prefix, - char *address) { +bool tendermint_getAddress(const HDNode* node, const char* prefix, + char* address) { uint8_t hash160Buf[RIPEMD160_DIGEST_LENGTH]; ecdsa_get_pubkeyhash(node->public_key, HASHER_SHA2_RIPEMD, hash160Buf); uint8_t fiveBitExpanded[RIPEMD160_DIGEST_LENGTH * 8 / 5]; size_t len = 0; convert_bits(fiveBitExpanded, &len, 5, hash160Buf, 20, 8, 1); - return bech32_encode(address, prefix, fiveBitExpanded, len, BECH32_ENCODING_BECH32) == 1; + return bech32_encode(address, prefix, fiveBitExpanded, len, + BECH32_ENCODING_BECH32) == 1; } -void tendermint_sha256UpdateEscaped(SHA256_CTX *ctx, const char *s, +void tendermint_sha256UpdateEscaped(SHA256_CTX* ctx, const char* s, size_t len) { for (size_t i = 0; i != len; i++) { if (s[i] == '"') { - sha256_Update(ctx, (const uint8_t *)"\\\"", 2); + sha256_Update(ctx, (const uint8_t*)"\\\"", 2); } else if (s[i] == '\\') { - sha256_Update(ctx, (const uint8_t *)"\\\\", 2); + sha256_Update(ctx, (const uint8_t*)"\\\\", 2); } else { // The copy here is required (as opposed to a cast), since the // source is a character array, and sha256_Update uses it as if it @@ -81,8 +82,8 @@ void tendermint_sha256UpdateEscaped(SHA256_CTX *ctx, const char *s, } } -bool tendermint_snprintf(SHA256_CTX *ctx, char *temp, size_t len, - const char *format, ...) { +bool tendermint_snprintf(SHA256_CTX* ctx, char* temp, size_t len, + const char* format, ...) { va_list vl; va_start(vl, format); int n = vsnprintf(temp, len, format, vl); @@ -90,6 +91,6 @@ bool tendermint_snprintf(SHA256_CTX *ctx, char *temp, size_t len, if (n < 0 || (size_t)n >= len) return false; - sha256_Update(ctx, (const uint8_t *)temp, n); + sha256_Update(ctx, (const uint8_t*)temp, n); return true; } diff --git a/lib/firmware/thorchain.c b/lib/firmware/thorchain.c index 76d5730d8..92b075d4f 100644 --- a/lib/firmware/thorchain.c +++ b/lib/firmware/thorchain.c @@ -1,8 +1,8 @@ /* * This file is part of the Keepkey project. * - * Copyright (C) 2021 Shapeshift - * + * Copyright (C) 2021 Shapeshift + * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -38,9 +38,9 @@ static uint32_t msgs_remaining; static ThorchainSignTx msg; static bool testnet; -const ThorchainSignTx *thorchain_getThorchainSignTx(void) { return &msg; } +const ThorchainSignTx* thorchain_getThorchainSignTx(void) { return &msg; } -bool thorchain_signTxInit(const HDNode *_node, const ThorchainSignTx *_msg) { +bool thorchain_signTxInit(const HDNode* _node, const ThorchainSignTx* _msg) { initialized = true; msgs_remaining = _msg->msg_count; testnet = false; @@ -61,13 +61,13 @@ bool thorchain_signTxInit(const HDNode *_node, const ThorchainSignTx *_msg) { // Each segment guaranteed to be less than or equal to 64 bytes // 19 + ^20 + 1 = ^40 if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), - "{\"account_number\":\"%" PRIu64 "\"", - msg.account_number)) + "{\"account_number\":\"%" PRIu64 "\"", + msg.account_number)) return false; // - const char *const chainid_prefix = ",\"chain_id\":\""; - sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + const char* const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t*)chainid_prefix, strlen(chainid_prefix)); tendermint_sha256UpdateEscaped(&ctx, msg.chain_id, strlen(msg.chain_id)); // 30 + ^10 + 19 = ^59 @@ -82,23 +82,23 @@ bool thorchain_signTxInit(const HDNode *_node, const ThorchainSignTx *_msg) { ",\"gas\":\"%" PRIu32 "\"}", msg.gas); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); if (msg.has_memo) { tendermint_sha256UpdateEscaped(&ctx, msg.memo, strlen(msg.memo)); } // 10 - sha256_Update(&ctx, (uint8_t *)"\",\"msgs\":[", 10); + sha256_Update(&ctx, (uint8_t*)"\",\"msgs\":[", 10); return success; } bool thorchain_signTxUpdateMsgSend(const uint64_t amount, - const char *to_address) { - char mainnetp[] = "thor"; - char testnetp[] = "tthor"; - char *pfix; + const char* to_address) { + const char mainnetp[] = "thor"; + const char testnetp[] = "tthor"; + const char* pfix; char buffer[64 + 1]; size_t decoded_len; @@ -121,8 +121,8 @@ bool thorchain_signTxUpdateMsgSend(const uint64_t amount, bool success = true; - const char *const prelude = "{\"type\":\"thorchain/MsgSend\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + const char* const prelude = "{\"type\":\"thorchain/MsgSend\",\"value\":{"; + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); // 21 + ^20 + 19 = ^60 success &= tendermint_snprintf( @@ -141,27 +141,26 @@ bool thorchain_signTxUpdateMsgSend(const uint64_t amount, return success; } -bool thorchain_signTxUpdateMsgDeposit(const ThorchainMsgDeposit *depmsg) { +bool thorchain_signTxUpdateMsgDeposit(const ThorchainMsgDeposit* depmsg) { char buffer[64 + 1]; bool success = true; - const char *const prelude = "{\"type\":\"thorchain/MsgDeposit\",\"value\":{"; - sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + const char* const prelude = "{\"type\":\"thorchain/MsgDeposit\",\"value\":{"; + sha256_Update(&ctx, (uint8_t*)prelude, strlen(prelude)); // 20 + ^20 + 1 = ^41 - success &= tendermint_snprintf( - &ctx, buffer, sizeof(buffer), - "\"coins\":[{\"amount\":\"%" PRIu64 "\"", depmsg->amount); + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"coins\":[{\"amount\":\"%" PRIu64 "\"", + depmsg->amount); // 10 + ^20 + 3 = ^33 - success &= tendermint_snprintf( - &ctx, buffer, sizeof(buffer), - ",\"asset\":\"%s\"}]", depmsg->asset); + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"asset\":\"%s\"}]", depmsg->asset); // - const char *const memo_prefix = ",\"memo\":\""; - sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + const char* const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t*)memo_prefix, strlen(memo_prefix)); tendermint_sha256UpdateEscaped(&ctx, depmsg->memo, strlen(depmsg->memo)); // 17 + 45 + 1 = 63 @@ -172,9 +171,9 @@ bool thorchain_signTxUpdateMsgDeposit(const ThorchainMsgDeposit *depmsg) { return success; } -bool thorchain_signTxFinalize(uint8_t *public_key, uint8_t *signature) { +bool thorchain_signTxFinalize(uint8_t* public_key, uint8_t* signature) { char buffer[64 + 1]; - + // 16 + ^20 = ^36 if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), "],\"sequence\":\"%" PRIu64 "\"}", msg.sequence)) @@ -200,7 +199,7 @@ void thorchain_signAbort(void) { memzero(&node, sizeof(node)); } -bool thorchain_parseConfirmMemo(const char *swapStr, size_t size) { +bool thorchain_parseConfirmMemo(const char* swapStr, size_t size) { /* Input: swapStr is candidate thorchain data size is the size of swapStr (<= 256) @@ -214,9 +213,9 @@ bool thorchain_parseConfirmMemo(const char *swapStr, size_t size) { Swap transactions can be indicated by "SWAP" or "s" or "=" */ - char *parseTokPtrs[7] = {NULL, NULL, NULL, NULL, + char* parseTokPtrs[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; // we can parse up to 7 tokens - char *tok; + char* tok; char memoBuf[256]; uint16_t ctr; @@ -264,7 +263,8 @@ bool thorchain_parseConfirmMemo(const char *swapStr, size_t size) { } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain swap", "Confirm swap asset %s\n on chain %s", parseTokPtrs[2], parseTokPtrs[1])) { + "Thorchain swap", "Confirm swap asset %s\n on chain %s", + parseTokPtrs[2], parseTokPtrs[1])) { return false; } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, @@ -284,16 +284,18 @@ bool thorchain_parseConfirmMemo(const char *swapStr, size_t size) { if (tok != NULL) { // add liquidity pool address parseTokPtrs[3] = tok; - } + } if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain add liquidity", "Confirm add asset %s\n on chain %s pool", - parseTokPtrs[2], parseTokPtrs[1])) { + "Thorchain add liquidity", + "Confirm add asset %s\n on chain %s pool", parseTokPtrs[2], + parseTokPtrs[1])) { return false; } if (tok != NULL) { if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain add liquidity", "Confirm to %s", parseTokPtrs[3])) { + "Thorchain add liquidity", "Confirm to %s", + parseTokPtrs[3])) { return false; } } @@ -301,19 +303,20 @@ bool thorchain_parseConfirmMemo(const char *swapStr, size_t size) { } // Check for withdraw liquidity - else if (strncmp(parseTokPtrs[0], "WITHDRAW", 8) == 0 || strncmp(parseTokPtrs[0], "wd", 2) == 0 || - *parseTokPtrs[0] == '-') { + else if (strncmp(parseTokPtrs[0], "WITHDRAW", 8) == 0 || + strncmp(parseTokPtrs[0], "wd", 2) == 0 || *parseTokPtrs[0] == '-') { if (tok != NULL) { // add liquidity pool address parseTokPtrs[3] = tok; } else { - return false; // malformed memo + return false; // malformed memo } float percent = (float)(atoi(parseTokPtrs[3])) / 100; if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, - "Thorchain withdraw liquidity", "Confirm withdraw %3.2f%% of asset %s on chain %s", - percent, parseTokPtrs[2], parseTokPtrs[1])) { + "Thorchain withdraw liquidity", + "Confirm withdraw %3.2f%% of asset %s on chain %s", percent, + parseTokPtrs[2], parseTokPtrs[1])) { return false; } return true; diff --git a/lib/firmware/tiny-json.c b/lib/firmware/tiny-json.c index b591b996e..c915eff58 100644 --- a/lib/firmware/tiny-json.c +++ b/lib/firmware/tiny-json.c @@ -31,445 +31,461 @@ #include #include "keepkey/firmware/tiny-json.h" -//#include +// #include int errno = 0; /** Structure to handle a heap of JSON properties. */ typedef struct jsonStaticPool_s { - json_t* mem; /**< Pointer to array of json properties. */ - unsigned int qty; /**< Length of the array of json properties. */ - unsigned int nextFree; /**< The index of the next free json property. */ - jsonPool_t pool; + json_t* mem; /**< Pointer to array of json properties. */ + unsigned int qty; /**< Length of the array of json properties. */ + unsigned int nextFree; /**< The index of the next free json property. */ + jsonPool_t pool; } jsonStaticPool_t; /* Search a property by its name in a JSON object. */ -json_t const* json_getProperty( json_t const* obj, char const* property ) { - json_t const* sibling; - for( sibling = obj->u.c.child; sibling; sibling = sibling->sibling ) - if ( sibling->name && !strcmp( sibling->name, property ) ) - return sibling; - return 0; +json_t const* json_getProperty(json_t const* obj, char const* property) { + json_t const* sibling; + for (sibling = obj->u.c.child; sibling; sibling = sibling->sibling) + if (sibling->name && !strcmp(sibling->name, property)) return sibling; + return 0; } /* Search a property by its name in a JSON object and return its value. */ -char const* json_getPropertyValue( json_t const* obj, char const* property ) { - json_t const* field = json_getProperty( obj, property ); - if ( !field ) return 0; - jsonType_t type = json_getType( field ); - if ( JSON_ARRAY >= type ) return 0; - return json_getValue( field ); +char const* json_getPropertyValue(json_t const* obj, char const* property) { + json_t const* field = json_getProperty(obj, property); + if (!field) return 0; + jsonType_t type = json_getType(field); + if (JSON_ARRAY >= type) return 0; + return json_getValue(field); } /* Internal prototypes: */ -static char* goBlank( char* str ); -static char* goNum( char* str ); -static json_t* poolInit( jsonPool_t* pool ); -static json_t* poolAlloc( jsonPool_t* pool ); -static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ); -static char* setToNull( char* ch ); -static bool isEndOfPrimitive( char ch ); +static char* goBlank(char* str); +static char* goNum(char* str); +static json_t* poolInit(jsonPool_t* pool); +static json_t* poolAlloc(jsonPool_t* pool); +static char* objValue(char* ptr, json_t* obj, jsonPool_t* pool); +static char* setToNull(char* ch); +static bool isEndOfPrimitive(char ch); /* Parse a string to get a json. */ -json_t const* json_createWithPool( char *str, jsonPool_t *pool ) { - char* ptr = goBlank( str ); - if ( !ptr || (*ptr != '{' && *ptr != '[') ) { - errno = -1; - return 0; - } - json_t* obj = pool->init( pool ); - obj->name = 0; - obj->sibling = 0; - obj->u.c.child = 0; - ptr = objValue( ptr, obj, pool ); - if ( !ptr ) { - errno = -2; - return 0; - } - return obj; +json_t const* json_createWithPool(char* str, jsonPool_t* pool) { + char* ptr = goBlank(str); + if (!ptr || (*ptr != '{' && *ptr != '[')) { + errno = -1; + return 0; + } + json_t* obj = pool->init(pool); + obj->name = 0; + obj->sibling = 0; + obj->u.c.child = 0; + ptr = objValue(ptr, obj, pool); + if (!ptr) { + errno = -2; + return 0; + } + return obj; } /* Parse a string to get a json. */ -json_t const* json_create( char* str, json_t mem[], unsigned int qty ) { - jsonStaticPool_t spool; - spool.mem = mem; - spool.qty = qty; - spool.pool.init = poolInit; - spool.pool.alloc = poolAlloc; - return json_createWithPool( str, &spool.pool ); +json_t const* json_create(char* str, json_t mem[], unsigned int qty) { + jsonStaticPool_t spool; + spool.mem = mem; + spool.qty = qty; + spool.pool.init = poolInit; + spool.pool.alloc = poolAlloc; + return json_createWithPool(str, &spool.pool); } /** Get a special character with its escape character. Examples: - * 'b' -> '\\b', 'n' -> '\\n', 't' -> '\\t' - * @param ch The escape character. - * @retval The character code. */ -static char getEscape( char ch ) { - static struct { char ch; char code; } const pair[] = { - { '\"', '\"' }, { '\\', '\\' }, - { '/', '/' }, { 'b', '\b' }, - { 'f', '\f' }, { 'n', '\n' }, - { 'r', '\r' }, { 't', '\t' }, - }; - unsigned int i; - for( i = 0; i < sizeof pair / sizeof *pair; ++i ) - if ( pair[i].ch == ch ) - return pair[i].code; - return '\0'; + * 'b' -> '\\b', 'n' -> '\\n', 't' -> '\\t' + * @param ch The escape character. + * @retval The character code. */ +static char getEscape(char ch) { + static struct { + char ch; + char code; + } const pair[] = { + {'\"', '\"'}, {'\\', '\\'}, {'/', '/'}, {'b', '\b'}, + {'f', '\f'}, {'n', '\n'}, {'r', '\r'}, {'t', '\t'}, + }; + unsigned int i; + for (i = 0; i < sizeof pair / sizeof *pair; ++i) + if (pair[i].ch == ch) return pair[i].code; + return '\0'; } /** Parse 4 characters. - * @param str Pointer to first digit. - * @retval '?' If the four characters are hexadecimal digits. - * @retval '\0' In other cases. */ -static unsigned char getCharFromUnicode( unsigned char const* str ) { - unsigned int i; - for( i = 0; i < 4; ++i ) - if ( !isxdigit( str[i] ) ) - return '\0'; - return '?'; + * @param str Pointer to first digit. + * @retval '?' If the four characters are hexadecimal digits. + * @retval '\0' In other cases. */ +static unsigned char getCharFromUnicode(unsigned char const* str) { + unsigned int i; + for (i = 0; i < 4; ++i) + if (!isxdigit(str[i])) return '\0'; + return '?'; } /** Parse a string and replace the scape characters by their meaning characters. - * This parser stops when finds the character '\"'. Then replaces '\"' by '\0'. - * @param str Pointer to first character. - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* parseString( char* str ) { - unsigned char* head = (unsigned char*)str; - unsigned char* tail = (unsigned char*)str; - for( ; *head; ++head, ++tail ) { - if ( *head == '\"' ) { - *tail = '\0'; - return (char*)++head; - } - if ( *head == '\\' ) { - if ( *++head == 'u' ) { - char const ch = getCharFromUnicode( ++head ); - if ( ch == '\0' ) return 0; - *tail = ch; - head += 3; - } - else { - char const esc = getEscape( *head ); - if ( esc == '\0' ) return 0; - *tail = esc; - } - } - else *tail = *head; + * This parser stops when finds the character '\"'. Then replaces '\"' by '\0'. + * @param str Pointer to first character. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* parseString(char* str) { + unsigned char* head = (unsigned char*)str; + unsigned char* tail = (unsigned char*)str; + for (; *head; ++head, ++tail) { + if (*head == '\"') { + *tail = '\0'; + return (char*)++head; } - return 0; + if (*head == '\\') { + if (*++head == 'u') { + char const ch = getCharFromUnicode(++head); + if (ch == '\0') return 0; + *tail = ch; + head += 3; + } else { + char const esc = getEscape(*head); + if (esc == '\0') return 0; + *tail = esc; + } + } else + *tail = *head; + } + return 0; } /** Parse a string to get the name of a property. - * @param ptr Pointer to first character. - * @param property The property to assign the name. - * @retval Pointer to first of property value. If success. - * @retval Null pointer if any error occur. */ -static char* propertyName( char* ptr, json_t* property ) { - property->name = ++ptr; - ptr = parseString( ptr ); - if ( !ptr ) return 0; - ptr = goBlank( ptr ); - if ( !ptr ) return 0; - if ( *ptr++ != ':' ) return 0; - return goBlank( ptr ); + * @param ptr Pointer to first character. + * @param property The property to assign the name. + * @retval Pointer to first of property value. If success. + * @retval Null pointer if any error occur. */ +static char* propertyName(char* ptr, json_t* property) { + property->name = ++ptr; + ptr = parseString(ptr); + if (!ptr) return 0; + ptr = goBlank(ptr); + if (!ptr) return 0; + if (*ptr++ != ':') return 0; + return goBlank(ptr); } /** Parse a string to get the value of a property when its type is JSON_TEXT. - * @param ptr Pointer to first character ('\"'). - * @param property The property to assign the name. - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* textValue( char* ptr, json_t* property ) { - ++property->u.value; - ptr = parseString( ++ptr ); - if ( !ptr ) return 0; - property->type = JSON_TEXT; - return ptr; + * @param ptr Pointer to first character ('\"'). + * @param property The property to assign the name. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* textValue(char* ptr, json_t* property) { + ++property->u.value; + ptr = parseString(++ptr); + if (!ptr) return 0; + property->type = JSON_TEXT; + return ptr; } /** Compare two strings until get the null character in the second one. - * @param ptr sub string - * @param str main string - * @retval Pointer to next character. - * @retval Null pointer if any error occur. */ -static char* checkStr( char* ptr, char const* str ) { - while( *str ) - if ( *ptr++ != *str++ ) - return 0; - return ptr; + * @param ptr sub string + * @param str main string + * @retval Pointer to next character. + * @retval Null pointer if any error occur. */ +static char* checkStr(char* ptr, char const* str) { + while (*str) + if (*ptr++ != *str++) return 0; + return ptr; } /** Parser a string to get a primitive value. - * If the first character after the value is different of '}' or ']' is set to '\0'. - * @param ptr Pointer to first character. - * @param property Property handler to set the value and the type, (true, false or null). - * @param value String with the primitive literal. - * @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL ) - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* primitiveValue( char* ptr, json_t* property, char const* value, jsonType_t type ) { - ptr = checkStr( ptr, value ); - if ( !ptr || !isEndOfPrimitive( *ptr ) ) return 0; - ptr = setToNull( ptr ); - property->type = type; - return ptr; + * If the first character after the value is different of '}' or ']' is set to + * '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false + * or null). + * @param value String with the primitive literal. + * @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL ) + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* primitiveValue(char* ptr, json_t* property, char const* value, + jsonType_t type) { + ptr = checkStr(ptr, value); + if (!ptr || !isEndOfPrimitive(*ptr)) return 0; + ptr = setToNull(ptr); + property->type = type; + return ptr; } /** Parser a string to get a true value. - * If the first character after the value is different of '}' or ']' is set to '\0'. - * @param ptr Pointer to first character. - * @param property Property handler to set the value and the type, (true, false or null). - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* trueValue( char* ptr, json_t* property ) { - return primitiveValue( ptr, property, "true", JSON_BOOLEAN ); + * If the first character after the value is different of '}' or ']' is set to + * '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false + * or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* trueValue(char* ptr, json_t* property) { + return primitiveValue(ptr, property, "true", JSON_BOOLEAN); } /** Parser a string to get a false value. - * If the first character after the value is different of '}' or ']' is set to '\0'. - * @param ptr Pointer to first character. - * @param property Property handler to set the value and the type, (true, false or null). - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* falseValue( char* ptr, json_t* property ) { - return primitiveValue( ptr, property, "false", JSON_BOOLEAN ); + * If the first character after the value is different of '}' or ']' is set to + * '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false + * or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* falseValue(char* ptr, json_t* property) { + return primitiveValue(ptr, property, "false", JSON_BOOLEAN); } /** Parser a string to get a null value. - * If the first character after the value is different of '}' or ']' is set to '\0'. - * @param ptr Pointer to first character. - * @param property Property handler to set the value and the type, (true, false or null). - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* nullValue( char* ptr, json_t* property ) { - return primitiveValue( ptr, property, "null", JSON_NULL ); + * If the first character after the value is different of '}' or ']' is set to + * '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type, (true, false + * or null). + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* nullValue(char* ptr, json_t* property) { + return primitiveValue(ptr, property, "null", JSON_NULL); } /** Analyze the exponential part of a real number. - * @param ptr Pointer to first character. - * @retval Pointer to first non numerical after the string. If success. - * @retval Null pointer if any error occur. */ -static char* expValue( char* ptr ) { - if ( *ptr == '-' || *ptr == '+' ) ++ptr; - if ( !isdigit( (int)(*ptr) ) ) return 0; - ptr = goNum( ++ptr ); - return ptr; + * @param ptr Pointer to first character. + * @retval Pointer to first non numerical after the string. If success. + * @retval Null pointer if any error occur. */ +static char* expValue(char* ptr) { + if (*ptr == '-' || *ptr == '+') ++ptr; + if (!isdigit((int)(*ptr))) return 0; + ptr = goNum(++ptr); + return ptr; } /** Analyze the decimal part of a real number. - * @param ptr Pointer to first character. - * @retval Pointer to first non numerical after the string. If success. - * @retval Null pointer if any error occur. */ -static char* fraqValue( char* ptr ) { - if ( !isdigit( (int)(*ptr) ) ) return 0; - ptr = goNum( ++ptr ); - if ( !ptr ) return 0; - return ptr; + * @param ptr Pointer to first character. + * @retval Pointer to first non numerical after the string. If success. + * @retval Null pointer if any error occur. */ +static char* fraqValue(char* ptr) { + if (!isdigit((int)(*ptr))) return 0; + ptr = goNum(++ptr); + if (!ptr) return 0; + return ptr; } /** Parser a string to get a numerical value. - * If the first character after the value is different of '}' or ']' is set to '\0'. - * @param ptr Pointer to first character. - * @param property Property handler to set the value and the type: JSON_REAL or JSON_INTEGER. - * @retval Pointer to first non white space after the string. If success. - * @retval Null pointer if any error occur. */ -static char* numValue( char* ptr, json_t* property ) { - if ( *ptr == '-' ) ++ptr; - if ( !isdigit( (int)(*ptr) ) ) return 0; - if ( *ptr != '0' ) { - ptr = goNum( ptr ); - if ( !ptr ) return 0; - } - else if ( isdigit( (int)(*++ptr) ) ) return 0; - property->type = JSON_INTEGER; - if ( *ptr == '.' ) { - ptr = fraqValue( ++ptr ); - if ( !ptr ) return 0; - property->type = JSON_REAL; - } - if ( *ptr == 'e' || *ptr == 'E' ) { - ptr = expValue( ++ptr ); - if ( !ptr ) return 0; - property->type = JSON_REAL; - } - if ( !isEndOfPrimitive( *ptr ) ) return 0; - if ( JSON_INTEGER == property->type ) { - char const* value = property->u.value; - bool const negative = *value == '-'; - static char const min[] = "-9223372036854775808"; - static char const max[] = "9223372036854775807"; - unsigned int const maxdigits = ( negative? sizeof min: sizeof max ) - 1; - unsigned int const len = ( unsigned int const ) ( ptr - value ); - if ( len > maxdigits ) return 0; - if ( len == maxdigits ) { - char const tmp = *ptr; - *ptr = '\0'; - char const* const threshold = negative ? min: max; - if ( 0 > strcmp( threshold, value ) ) return 0; - *ptr = tmp; - } + * If the first character after the value is different of '}' or ']' is set to + * '\0'. + * @param ptr Pointer to first character. + * @param property Property handler to set the value and the type: JSON_REAL or + * JSON_INTEGER. + * @retval Pointer to first non white space after the string. If success. + * @retval Null pointer if any error occur. */ +static char* numValue(char* ptr, json_t* property) { + if (*ptr == '-') ++ptr; + if (!isdigit((int)(*ptr))) return 0; + if (*ptr != '0') { + ptr = goNum(ptr); + if (!ptr) return 0; + } else if (isdigit((int)(*++ptr))) + return 0; + property->type = JSON_INTEGER; + if (*ptr == '.') { + ptr = fraqValue(++ptr); + if (!ptr) return 0; + property->type = JSON_REAL; + } + if (*ptr == 'e' || *ptr == 'E') { + ptr = expValue(++ptr); + if (!ptr) return 0; + property->type = JSON_REAL; + } + if (!isEndOfPrimitive(*ptr)) return 0; + if (JSON_INTEGER == property->type) { + char const* value = property->u.value; + bool const negative = *value == '-'; + static char const min[] = "-9223372036854775808"; + static char const max[] = "9223372036854775807"; + unsigned int const maxdigits = (negative ? sizeof min : sizeof max) - 1; + unsigned int const len = (unsigned int const)(ptr - value); + if (len > maxdigits) return 0; + if (len == maxdigits) { + char const tmp = *ptr; + *ptr = '\0'; + char const* const threshold = negative ? min : max; + if (0 > strcmp(threshold, value)) return 0; + *ptr = tmp; } - ptr = setToNull( ptr ); - return ptr; + } + ptr = setToNull(ptr); + return ptr; } /** Add a property to a JSON object or array. - * @param obj The handler of the JSON object or array. - * @param property The handler of the property to be added. */ -static void add( json_t* obj, json_t* property ) { - property->sibling = 0; - if ( !obj->u.c.child ){ - obj->u.c.child = property; - obj->u.c.last_child = property; - } else { - obj->u.c.last_child->sibling = property; - obj->u.c.last_child = property; - } + * @param obj The handler of the JSON object or array. + * @param property The handler of the property to be added. */ +static void add(json_t* obj, json_t* property) { + property->sibling = 0; + if (!obj->u.c.child) { + obj->u.c.child = property; + obj->u.c.last_child = property; + } else { + obj->u.c.last_child->sibling = property; + obj->u.c.last_child = property; + } } /** Parser a string to get a json object value. - * @param ptr Pointer to first character. - * @param obj The handler of the JSON root object or array. - * @param pool The handler of a json pool for creating json instances. - * @retval Pointer to first character after the value. If success. - * @retval Null pointer if any error occur. */ -static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { - obj->type = *ptr == '{' ? JSON_OBJ : JSON_ARRAY; - obj->u.c.child = 0; - obj->sibling = 0; - ptr++; - for(;;) { - ptr = goBlank( ptr ); - if ( !ptr ) return 0; - if ( *ptr == ',' ) { - ++ptr; - continue; - } - char const endchar = ( obj->type == JSON_OBJ )? '}': ']'; - if ( *ptr == endchar ) { - *ptr = '\0'; - json_t* parentObj = obj->sibling; - if ( !parentObj ) return ++ptr; - obj->sibling = 0; - obj = parentObj; - ++ptr; - continue; - } - json_t* property = pool->alloc( pool ); - if ( !property ) { - // TODO: fix this error return - //printf("err: pool size too small"); - return 0; - } - if( obj->type != JSON_ARRAY ) { - if ( *ptr != '\"' ) return 0; - ptr = propertyName( ptr, property ); - if ( !ptr ) return 0; - } - else property->name = 0; - add( obj, property ); - property->u.value = ptr; - switch( *ptr ) { - case '{': - property->type = JSON_OBJ; - property->u.c.child = 0; - property->sibling = obj; - obj = property; - ++ptr; - break; - case '[': - property->type = JSON_ARRAY; - property->u.c.child = 0; - property->sibling = obj; - obj = property; - ++ptr; - break; - case '\"': ptr = textValue( ptr, property ); break; - case 't': ptr = trueValue( ptr, property ); break; - case 'f': ptr = falseValue( ptr, property ); break; - case 'n': ptr = nullValue( ptr, property ); break; - default: ptr = numValue( ptr, property ); break; - } - if ( !ptr ) return 0; + * @param ptr Pointer to first character. + * @param obj The handler of the JSON root object or array. + * @param pool The handler of a json pool for creating json instances. + * @retval Pointer to first character after the value. If success. + * @retval Null pointer if any error occur. */ +static char* objValue(char* ptr, json_t* obj, jsonPool_t* pool) { + obj->type = *ptr == '{' ? JSON_OBJ : JSON_ARRAY; + obj->u.c.child = 0; + obj->sibling = 0; + ptr++; + for (;;) { + ptr = goBlank(ptr); + if (!ptr) return 0; + if (*ptr == ',') { + ++ptr; + continue; + } + char const endchar = (obj->type == JSON_OBJ) ? '}' : ']'; + if (*ptr == endchar) { + *ptr = '\0'; + json_t* parentObj = obj->sibling; + if (!parentObj) return ++ptr; + obj->sibling = 0; + obj = parentObj; + ++ptr; + continue; + } + json_t* property = pool->alloc(pool); + if (!property) { + // TODO: fix this error return + // printf("err: pool size too small"); + return 0; } + if (obj->type != JSON_ARRAY) { + if (*ptr != '\"') return 0; + ptr = propertyName(ptr, property); + if (!ptr) return 0; + } else + property->name = 0; + add(obj, property); + property->u.value = ptr; + switch (*ptr) { + case '{': + property->type = JSON_OBJ; + property->u.c.child = 0; + property->sibling = obj; + obj = property; + ++ptr; + break; + case '[': + property->type = JSON_ARRAY; + property->u.c.child = 0; + property->sibling = obj; + obj = property; + ++ptr; + break; + case '\"': + ptr = textValue(ptr, property); + break; + case 't': + ptr = trueValue(ptr, property); + break; + case 'f': + ptr = falseValue(ptr, property); + break; + case 'n': + ptr = nullValue(ptr, property); + break; + default: + ptr = numValue(ptr, property); + break; + } + if (!ptr) return 0; + } } /** Initialize a json pool. - * @param pool The handler of the pool. - * @return a instance of a json. */ -static json_t* poolInit( jsonPool_t* pool ) { - jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool ); - spool->nextFree = 1; - return spool->mem; + * @param pool The handler of the pool. + * @return a instance of a json. */ +static json_t* poolInit(jsonPool_t* pool) { + jsonStaticPool_t* spool = json_containerOf(pool, jsonStaticPool_t, pool); + spool->nextFree = 1; + return spool->mem; } /** Create an instance of a json from a pool. - * @param pool The handler of the pool. - * @retval The handler of the new instance if success. - * @retval Null pointer if the pool was empty. */ -static json_t* poolAlloc( jsonPool_t* pool ) { - jsonStaticPool_t *spool = json_containerOf( pool, jsonStaticPool_t, pool ); - if ( spool->nextFree >= spool->qty ) return 0; - return spool->mem + spool->nextFree++; + * @param pool The handler of the pool. + * @retval The handler of the new instance if success. + * @retval Null pointer if the pool was empty. */ +static json_t* poolAlloc(jsonPool_t* pool) { + jsonStaticPool_t* spool = json_containerOf(pool, jsonStaticPool_t, pool); + if (spool->nextFree >= spool->qty) return 0; + return spool->mem + spool->nextFree++; } /** Checks whether an character belongs to set. - * @param ch Character value to be checked. - * @param set Set of characters. It is just a null-terminated string. - * @return true or false there is membership or not. */ -static bool isOneOfThem( char ch, char const* set ) { - while( *set != '\0' ) - if ( ch == *set++ ) - return true; - return false; + * @param ch Character value to be checked. + * @param set Set of characters. It is just a null-terminated string. + * @return true or false there is membership or not. */ +static bool isOneOfThem(char ch, char const* set) { + while (*set != '\0') + if (ch == *set++) return true; + return false; } /** Increases a pointer while it points to a character that belongs to a set. - * @param str The initial pointer value. - * @param set Set of characters. It is just a null-terminated string. - * @return The final pointer value or null pointer if the null character was found. */ -static char* goWhile( char* str, char const* set ) { - for(; *str != '\0'; ++str ) { - if ( !isOneOfThem( *str, set ) ) - return str; - } - return 0; + * @param str The initial pointer value. + * @param set Set of characters. It is just a null-terminated string. + * @return The final pointer value or null pointer if the null character was + * found. */ +static char* goWhile(char* str, char const* set) { + for (; *str != '\0'; ++str) { + if (!isOneOfThem(*str, set)) return str; + } + return 0; } /** Set of characters that defines a blank. */ static char const* const blank = " \n\r\t\f"; /** Increases a pointer while it points to a white space character. - * @param str The initial pointer value. - * @return The final pointer value or null pointer if the null character was found. */ -static char* goBlank( char* str ) { - return goWhile( str, blank ); -} + * @param str The initial pointer value. + * @return The final pointer value or null pointer if the null character was + * found. */ +static char* goBlank(char* str) { return goWhile(str, blank); } /** Increases a pointer while it points to a decimal digit character. - * @param str The initial pointer value. - * @return The final pointer value or null pointer if the null character was found. */ -static char* goNum( char* str ) { - for( ; *str != '\0'; ++str ) { - if ( !isdigit( (int)(*str) ) ) - return str; - } - return 0; + * @param str The initial pointer value. + * @return The final pointer value or null pointer if the null character was + * found. */ +static char* goNum(char* str) { + for (; *str != '\0'; ++str) { + if (!isdigit((int)(*str))) return str; + } + return 0; } /** Set of characters that defines the end of an array or a JSON object. */ static char const* const endofblock = "}]"; -/** Set a char to '\0' and increase its pointer if the char is different to '}' or ']'. - * @param ch Pointer to character. - * @return Final value pointer. */ -static char* setToNull( char* ch ) { - if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0'; - return ch; +/** Set a char to '\0' and increase its pointer if the char is different to '}' + * or ']'. + * @param ch Pointer to character. + * @return Final value pointer. */ +static char* setToNull(char* ch) { + if (!isOneOfThem(*ch, endofblock)) *ch++ = '\0'; + return ch; } /** Indicate if a character is the end of a primitive value. */ -static bool isEndOfPrimitive( char ch ) { - return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock ); +static bool isEndOfPrimitive(char ch) { + return ch == ',' || isOneOfThem(ch, blank) || isOneOfThem(ch, endofblock); } diff --git a/lib/firmware/ton.c b/lib/firmware/ton.c new file mode 100644 index 000000000..32354a71b --- /dev/null +++ b/lib/firmware/ton.c @@ -0,0 +1,254 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "keepkey/firmware/ton.h" + +#include "trezor/crypto/ed25519-donna/ed25519.h" +#include "trezor/crypto/hasher.h" +#include "trezor/crypto/memzero.h" +#include "trezor/crypto/sha2.h" + +#include +#include + +// Base64 URL-safe alphabet (RFC 4648) +static const char base64_url_alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +/** + * Encode data to Base64 URL-safe format (without padding) + */ +static bool base64_url_encode(const uint8_t* data, size_t data_len, char* out, + size_t out_len) { + size_t required_len = ((data_len + 2) / 3) * 4; + if (out_len < required_len + 1) { + return false; + } + + size_t i = 0, j = 0; + while (i < data_len) { + uint32_t octet_a = data[i++]; + uint32_t octet_b = i < data_len ? data[i++] : 0; + uint32_t octet_c = i < data_len ? data[i++] : 0; + + uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c; + + out[j++] = base64_url_alphabet[(triple >> 18) & 0x3F]; + out[j++] = base64_url_alphabet[(triple >> 12) & 0x3F]; + out[j++] = base64_url_alphabet[(triple >> 6) & 0x3F]; + out[j++] = base64_url_alphabet[triple & 0x3F]; + } + + // Remove padding for URL-safe variant + size_t padding = (3 - (data_len % 3)) % 3; + j -= padding; + out[j] = '\0'; + + return true; +} + +/** + * Compute CRC16-XMODEM checksum for TON address + */ +static uint16_t ton_crc16(const uint8_t* data, size_t len) { + uint16_t crc = 0; + + for (size_t i = 0; i < len; i++) { + crc ^= (uint16_t)data[i] << 8; + for (int j = 0; j < 8; j++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; + } else { + crc <<= 1; + } + } + } + + return crc; +} + +// Well-known v4r2 wallet contract code cell hash (constant) +// Source: https://github.com/ton-blockchain/wallet-contract (WalletV4R2) +static const uint8_t V4R2_CODE_HASH[32] = { + 0xfe, 0xb5, 0xff, 0x68, 0x20, 0xe2, 0xff, 0x0d, 0x94, 0x83, 0xe7, + 0xe0, 0xd6, 0x2c, 0x81, 0x7d, 0x84, 0x67, 0x89, 0xfb, 0x4a, 0xe5, + 0x80, 0xc8, 0x78, 0x86, 0x6d, 0x95, 0x9d, 0xab, 0xd5, 0xc0}; +// Code cell depth in the cell tree +#define V4R2_CODE_DEPTH 7 +// Standard v4r2 wallet_id for mainnet workchain 0 +#define V4R2_WALLET_ID 698983191u // 0x29A9A317 + +/** + * Compute the v4r2 data cell representation hash. + * Data cell layout: seqno(32b=0) + wallet_id(32b) + pubkey(256b) + + * plugins(1b=0) Total: 321 bits, d1=0x00 (no refs), d2=0x51 + * (floor(321/8)+ceil(321/8)=40+41=81) Augmented data: 40 full bytes + 0x40 + * (plugin=0, completion=1, pad=000000) + */ +static void ton_data_cell_hash(const uint8_t* public_key, uint8_t* out) { + // repr = d1(1) + d2(1) + augmented_data(41) = 43 bytes + uint8_t repr[43]; + repr[0] = 0x00; // d1: no refs + repr[1] = 0x51; // d2: 81 decimal + + // seqno = 0 (4 bytes) + memset(repr + 2, 0, 4); + + // wallet_id = 698983191 = 0x29A9A317 (4 bytes big-endian) + repr[6] = (V4R2_WALLET_ID >> 24) & 0xFF; + repr[7] = (V4R2_WALLET_ID >> 16) & 0xFF; + repr[8] = (V4R2_WALLET_ID >> 8) & 0xFF; + repr[9] = (V4R2_WALLET_ID) & 0xFF; + + // public key (32 bytes) + memcpy(repr + 10, public_key, 32); + + // plugins = 0 (1 bit) + completion tag (1 bit) + padding (6 bits) = 0x40 + repr[42] = 0x40; + + sha256_Raw(repr, 43, out); +} + +/** + * Compute the v4r2 StateInit representation hash. + * StateInit: split_depth(0) + special(0) + code(1,ref) + data(1,ref) + + * library(0) Total: 5 bits, d1=0x02 (2 refs), d2=0x01 Augmented: 00110 + 100 = + * 0x34 repr = d1 + d2 + data + depth(code) + depth(data) + hash(code) + + * hash(data) + */ +static void ton_stateinit_hash(const uint8_t* public_key, uint8_t* out) { + uint8_t data_hash[32]; + ton_data_cell_hash(public_key, data_hash); + + // repr = d1(1) + d2(1) + augmented(1) + code_depth(2) + data_depth(2) + + // code_hash(32) + data_hash(32) = 71 bytes + uint8_t repr[71]; + repr[0] = 0x02; // d1: 2 refs + repr[1] = 0x01; // d2: 1 + repr[2] = 0x34; // augmented: 00110100 + + // code cell depth = 7 (2 bytes big-endian) + repr[3] = 0x00; + repr[4] = V4R2_CODE_DEPTH; + + // data cell depth = 0 (no refs) + repr[5] = 0x00; + repr[6] = 0x00; + + // code cell hash (32 bytes) + memcpy(repr + 7, V4R2_CODE_HASH, 32); + + // data cell hash (32 bytes) + memcpy(repr + 39, data_hash, 32); + + sha256_Raw(repr, 71, out); + memzero(data_hash, sizeof(data_hash)); +} + +/** + * Generate TON v4r2 wallet address from Ed25519 public key. + * Address = base64url(tag || workchain || sha256(StateInit) || crc16) + * where StateInit = code_cell(v4r2) + data_cell(seqno=0, walletId, pubkey) + */ +bool ton_get_address(const ed25519_public_key public_key, bool bounceable, + bool testnet, int32_t workchain, char* address, + size_t address_len, char* raw_address, + size_t raw_address_len) { + if (address_len < TON_ADDRESS_MAX_LEN || + raw_address_len < TON_RAW_ADDRESS_MAX_LEN) { + return false; + } + + // Compute the v4r2 StateInit representation hash — this IS the address hash + uint8_t hash[32]; + ton_stateinit_hash(public_key, hash); + + // Construct address data: [tag][workchain][hash][crc16] + uint8_t addr_data[36]; + uint8_t tag = bounceable ? 0x11 : 0x51; + if (testnet) tag |= 0x80; + + addr_data[0] = tag; + addr_data[1] = (uint8_t)workchain; + memcpy(addr_data + 2, hash, 32); + + // Compute CRC16 checksum + uint16_t crc = ton_crc16(addr_data, 34); + addr_data[34] = (crc >> 8) & 0xFF; + addr_data[35] = crc & 0xFF; + + // Encode to Base64 URL-safe + if (!base64_url_encode(addr_data, 36, address, address_len)) { + memzero(hash, sizeof(hash)); + memzero(addr_data, sizeof(addr_data)); + return false; + } + + // Generate raw address format: workchain:hash_hex + char hash_hex[65]; + for (int i = 0; i < 32; i++) { + snprintf(hash_hex + (i * 2), 3, "%02x", hash[i]); + } + snprintf(raw_address, raw_address_len, "%ld:%s", (long)workchain, hash_hex); + + // Clean up sensitive data + memzero(hash, sizeof(hash)); + memzero(addr_data, sizeof(addr_data)); + + return true; +} + +/** + * Format TON amount (nanoTON) for display + * 1 TON = 1,000,000,000 nanoTON + */ +void ton_formatAmount(char* buf, size_t len, uint64_t amount) { + bignum256 val; + bn_read_uint64(amount, &val); + bn_format(&val, NULL, " TON", TON_DECIMALS, 0, false, buf, len); +} + +/** + * Sign a TON transaction with Ed25519 + */ +bool ton_signTx(const HDNode* node, const TonSignTx* msg, TonSignedTx* resp) { + if (!node || !msg || !resp) { + return false; + } + + // Verify we have raw transaction data + if (!msg->has_raw_tx || msg->raw_tx.size == 0) { + return false; + } + + // Ed25519 sign the transaction + ed25519_signature signature; + ed25519_sign(msg->raw_tx.bytes, msg->raw_tx.size, node->private_key, + &node->public_key[1], signature); + + // Copy signature to response (64 bytes) + resp->has_signature = true; + resp->signature.size = 64; + memcpy(resp->signature.bytes, signature, 64); + + // Zero out the signature buffer for security + memzero(signature, sizeof(signature)); + + return true; +} diff --git a/lib/firmware/transaction.c b/lib/firmware/transaction.c index aa37285e0..1405401a7 100644 --- a/lib/firmware/transaction.c +++ b/lib/firmware/transaction.c @@ -89,7 +89,7 @@ static inline uint32_t op_push_size(uint32_t i) { return 5; } -uint32_t op_push(uint32_t i, uint8_t *out) { +uint32_t op_push(uint32_t i, uint8_t* out) { if (i < 0x4C) { out[0] = i & 0xFF; return 1; @@ -113,18 +113,18 @@ uint32_t op_push(uint32_t i, uint8_t *out) { return 5; } -bool compute_address(const CoinType *coin, InputScriptType script_type, - const HDNode *node, bool has_multisig, - const MultisigRedeemScriptType *multisig, +bool compute_address(const CoinType* coin, InputScriptType script_type, + const HDNode* node, bool has_multisig, + const MultisigRedeemScriptType* multisig, char address[MAX_ADDR_SIZE]) { uint8_t raw[MAX_ADDR_RAW_SIZE]; uint8_t digest[32]; - size_t prelen; - const curve_info *curve = get_curve_by_name(coin->curve_name); + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return 0; if (has_multisig) { + size_t prelen; if (cryptoMultisigPubkeyIndex(coin, multisig, node->public_key) < 0) { return 0; } @@ -187,7 +187,7 @@ bool compute_address(const CoinType *coin, InputScriptType script_type, } } else if (script_type == InputScriptType_SPENDTAPROOT) { // we don't handle spendtaproot input types - return 0; + return 0; } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wpkh embedded in p2sh @@ -214,8 +214,8 @@ bool compute_address(const CoinType *coin, InputScriptType script_type, return 1; } -int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, - TxOutputBinType *out, bool needs_confirm) { +int compile_output(const CoinType* coin, const HDNode* root, TxOutputType* in, + TxOutputBinType* out, bool needs_confirm) { memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; out->decred_script_version = in->decred_script_version; @@ -238,10 +238,11 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, } } else { // is this thorchain data? - if (!thorchain_parseConfirmMemo((const char *)in->op_return_data.bytes, (size_t)in->op_return_data.size)) { + if (!thorchain_parseConfirmMemo((const char*)in->op_return_data.bytes, + (size_t)in->op_return_data.size)) { if (!confirm_data(ButtonRequestType_ButtonRequest_ConfirmOutput, - _("Confirm OP_RETURN"), in->op_return_data.bytes, - in->op_return_data.size)) { + _("Confirm OP_RETURN"), in->op_return_data.bytes, + in->op_return_data.size)) { return -1; // user aborted } } @@ -292,7 +293,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, return 0; // failed to compile output } - const curve_info *curve = get_curve_by_name(coin->curve_name); + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return 0; addr_raw_len = base58_decode_check(in->address, curve->hasher_base58, @@ -461,8 +462,8 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, return out->script_pubkey.size; } -uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, - uint8_t *out) { +uint32_t compile_script_sig(uint32_t address_type, const uint8_t* pubkeyhash, + uint8_t* out) { if (coinByAddressType(address_type)) { // valid coin type out[0] = 0x76; // OP_DUP out[1] = 0xA9; // OP_HASH_160 @@ -477,9 +478,9 @@ uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, } // if out == NULL just compute the length -uint32_t compile_script_multisig(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t *out) { +uint32_t compile_script_multisig(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t* out) { if (!multisig->has_m) return 0; const uint32_t m = multisig->m; const uint32_t n = multisig->pubkeys_count; @@ -492,7 +493,7 @@ uint32_t compile_script_multisig(const CoinType *coin, for (uint32_t i = 0; i < n; i++) { out[r] = 33; r++; // OP_PUSH 33 - const uint8_t *pubkey = + const uint8_t* pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (!pubkey) return 0; memcpy(out + r, pubkey, 33); @@ -508,16 +509,16 @@ uint32_t compile_script_multisig(const CoinType *coin, return r; } -uint32_t compile_script_multisig_hash(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t *hash) { +uint32_t compile_script_multisig_hash(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t* hash) { if (!multisig->has_m) return 0; const uint32_t m = multisig->m; const uint32_t n = multisig->pubkeys_count; if (m < 1 || m > 15) return 0; if (n < 1 || n > 15) return 0; - const curve_info *curve = get_curve_by_name(coin->curve_name); + const curve_info* curve = get_curve_by_name(coin->curve_name); if (!curve) return 0; Hasher hasher; @@ -529,7 +530,7 @@ uint32_t compile_script_multisig_hash(const CoinType *coin, for (uint32_t i = 0; i < n; i++) { d[0] = 33; hasher_Update(&hasher, d, 1); // OP_PUSH 33 - const uint8_t *pubkey = + const uint8_t* pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (!pubkey) return 0; hasher_Update(&hasher, pubkey, 33); @@ -543,9 +544,9 @@ uint32_t compile_script_multisig_hash(const CoinType *coin, return 1; } -uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, - const uint8_t *pubkey, uint32_t pubkey_len, - uint8_t sighash, uint8_t *out) { +uint32_t serialize_script_sig(const uint8_t* signature, uint32_t signature_len, + const uint8_t* pubkey, uint32_t pubkey_len, + uint8_t sighash, uint8_t* out) { uint32_t r = 0; r += op_push(signature_len + 1, out + r); memcpy(out + r, signature, signature_len); @@ -558,9 +559,9 @@ uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, return r; } -uint32_t serialize_script_multisig(const CoinType *coin, - const MultisigRedeemScriptType *multisig, - uint8_t sighash, uint8_t *out) { +uint32_t serialize_script_multisig(const CoinType* coin, + const MultisigRedeemScriptType* multisig, + uint8_t sighash, uint8_t* out) { uint32_t r = 0; if (!coin->decred) { // Decred fixed the off-by-one bug @@ -589,33 +590,33 @@ uint32_t serialize_script_multisig(const CoinType *coin, // tx methods -uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) { +uint32_t tx_prevout_hash(Hasher* hasher, const TxInputType* input) { for (int i = 0; i < 32; i++) { hasher_Update(hasher, &(input->prev_hash.bytes[31 - i]), 1); } - hasher_Update(hasher, (const uint8_t *)&input->prev_index, 4); + hasher_Update(hasher, (const uint8_t*)&input->prev_index, 4); return 36; } -uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data) { +uint32_t tx_script_hash(Hasher* hasher, uint32_t size, const uint8_t* data) { int r = ser_length_hash(hasher, size); hasher_Update(hasher, data, size); return r + size; } -uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input) { - hasher_Update(hasher, (const uint8_t *)&input->sequence, 4); +uint32_t tx_sequence_hash(Hasher* hasher, const TxInputType* input) { + hasher_Update(hasher, (const uint8_t*)&input->sequence, 4); return 4; } -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, +uint32_t tx_output_hash(Hasher* hasher, const TxOutputBinType* output, bool decred) { uint32_t r = 0; - hasher_Update(hasher, (const uint8_t *)&output->amount, 8); + hasher_Update(hasher, (const uint8_t*)&output->amount, 8); r += 8; if (decred) { uint16_t script_version = output->decred_script_version & 0xFFFF; - hasher_Update(hasher, (const uint8_t *)&script_version, 2); + hasher_Update(hasher, (const uint8_t*)&script_version, 2); r += 2; } r += tx_script_hash(hasher, output->script_pubkey.size, @@ -623,13 +624,13 @@ uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, return r; } -uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) { +uint32_t tx_serialize_script(uint32_t size, const uint8_t* data, uint8_t* out) { int r = ser_length(size, out); memcpy(out + r, data, size); return r + size; } -uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { +uint32_t tx_serialize_header(const TxStruct* tx, uint8_t* out) { int r = 4; if (tx->overwintered) { uint32_t ver = tx->version | TX_OVERWINTERED; @@ -646,15 +647,15 @@ uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { return r + ser_length(tx->inputs_len, out + r); } -uint32_t tx_serialize_header_hash(TxStruct *tx) { +uint32_t tx_serialize_header_hash(TxStruct* tx) { int r = 4; if (tx->overwintered) { uint32_t ver = tx->version | TX_OVERWINTERED; - hasher_Update(&(tx->hasher), (const uint8_t *)&ver, 4); - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version_group_id), 4); + hasher_Update(&(tx->hasher), (const uint8_t*)&ver, 4); + hasher_Update(&(tx->hasher), (const uint8_t*)&(tx->version_group_id), 4); r += 4; } else { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); + hasher_Update(&(tx->hasher), (const uint8_t*)&(tx->version), 4); if (tx->is_segwit) { hasher_Update(&(tx->hasher), segwit_header, 2); r += 2; @@ -663,8 +664,8 @@ uint32_t tx_serialize_header_hash(TxStruct *tx) { return r + ser_length_hash(&(tx->hasher), tx->inputs_len); } -uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, - uint8_t *out) { +uint32_t tx_serialize_input(TxStruct* tx, const TxInputType* input, + uint8_t* out) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; @@ -695,7 +696,7 @@ uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, return r; } -uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { +uint32_t tx_serialize_input_hash(TxStruct* tx, const TxInputType* input) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; @@ -707,7 +708,7 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { r += tx_prevout_hash(&(tx->hasher), input); if (tx->is_decred) { uint8_t tree = input->decred_tree & 0xFF; - hasher_Update(&(tx->hasher), (const uint8_t *)&(tree), 1); + hasher_Update(&(tx->hasher), (const uint8_t*)&(tree), 1); r++; } else { r += tx_script_hash(&(tx->hasher), input->script_sig.size, @@ -721,8 +722,8 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { return r; } -uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, - uint8_t *out) { +uint32_t tx_serialize_decred_witness(TxStruct* tx, const TxInputType* input, + uint8_t* out) { static const uint64_t amount = 0; static const uint32_t block_height = 0x00000000; static const uint32_t block_index = 0xFFFFFFFF; @@ -750,8 +751,8 @@ uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, return r; } -uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, - const TxInputType *input) { +uint32_t tx_serialize_decred_witness_hash(TxStruct* tx, + const TxInputType* input) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; @@ -773,15 +774,15 @@ uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, return r; } -uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) { +uint32_t tx_serialize_middle(const TxStruct* tx, uint8_t* out) { return ser_length(tx->outputs_len, out); } -uint32_t tx_serialize_middle_hash(TxStruct *tx) { +uint32_t tx_serialize_middle_hash(TxStruct* tx) { return ser_length_hash(&(tx->hasher), tx->outputs_len); } -uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { +uint32_t tx_serialize_footer(const TxStruct* tx, uint8_t* out) { memcpy(out, &(tx->lock_time), 4); if (tx->overwintered) { if (tx->version == 3) { @@ -804,22 +805,22 @@ uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { return 4; } -uint32_t tx_serialize_footer_hash(TxStruct *tx) { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); +uint32_t tx_serialize_footer_hash(TxStruct* tx) { + hasher_Update(&(tx->hasher), (const uint8_t*)&(tx->lock_time), 4); if (tx->overwintered) { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); - hasher_Update(&(tx->hasher), (const uint8_t *)"\x00", 1); // nJoinSplit + hasher_Update(&(tx->hasher), (const uint8_t*)&(tx->expiry), 4); + hasher_Update(&(tx->hasher), (const uint8_t*)"\x00", 1); // nJoinSplit return 9; } if (tx->is_decred) { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); + hasher_Update(&(tx->hasher), (const uint8_t*)&(tx->expiry), 4); return 8; } return 4; } -uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, - uint8_t *out) { +uint32_t tx_serialize_output(TxStruct* tx, const TxOutputBinType* output, + uint8_t* out) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided return 0; @@ -849,7 +850,7 @@ uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, return r; } -uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) { +uint32_t tx_serialize_output_hash(TxStruct* tx, const TxOutputBinType* output) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided return 0; @@ -871,7 +872,7 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) { return r; } -uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, +uint32_t tx_serialize_extra_data_hash(TxStruct* tx, const uint8_t* data, uint32_t datalen) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided @@ -891,7 +892,7 @@ uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, return datalen; } -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, +void tx_init(TxStruct* tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, uint32_t version_group_id) { @@ -912,7 +913,7 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, hasher_Init(&(tx->hasher), hasher_sign); } -void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { +void tx_hash_final(TxStruct* t, uint8_t* hash, bool reverse) { hasher_Final(&(t->hasher), hash); if (!reverse) return; for (uint8_t i = 0; i < 16; i++) { @@ -922,7 +923,7 @@ void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { } } -static uint32_t tx_input_script_size(const TxInputType *txinput) { +static uint32_t tx_input_script_size(const TxInputType* txinput) { uint32_t input_script_size; if (txinput->has_multisig) { uint32_t multisig_script_size = @@ -939,7 +940,7 @@ static uint32_t tx_input_script_size(const TxInputType *txinput) { return input_script_size; } -uint32_t tx_input_weight(const CoinType *coin, const TxInputType *txinput) { +uint32_t tx_input_weight(const CoinType* coin, const TxInputType* txinput) { if (coin->decred) { return 4 * (TXSIZE_INPUT + 1); // Decred tree } @@ -963,8 +964,8 @@ uint32_t tx_input_weight(const CoinType *coin, const TxInputType *txinput) { return weight; } -uint32_t tx_output_weight(const CoinType *coin, const curve_info *curve, - const TxOutputType *txoutput) { +uint32_t tx_output_weight(const CoinType* coin, const curve_info* curve, + const TxOutputType* txoutput) { uint32_t output_script_size = 0; if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) { output_script_size = 1 + op_push_size(txoutput->op_return_data.size) + @@ -1020,7 +1021,7 @@ uint32_t tx_output_weight(const CoinType *coin, const curve_info *curve, return 4 * (size + output_script_size); } -uint32_t tx_decred_witness_weight(const TxInputType *txinput) { +uint32_t tx_decred_witness_weight(const TxInputType* txinput) { uint32_t input_script_size = tx_input_script_size(txinput); uint32_t size = TXSIZE_DECRED_WITNESS + ser_length_size(input_script_size) + input_script_size; diff --git a/lib/firmware/tron.c b/lib/firmware/tron.c new file mode 100644 index 000000000..d44974fe7 --- /dev/null +++ b/lib/firmware/tron.c @@ -0,0 +1,132 @@ +/* + * This file is part of the KeepKey project. + * + * Copyright (C) 2024 KeepKey + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "keepkey/firmware/tron.h" + +#include "keepkey/crypto/curves.h" +#include "trezor/crypto/base58.h" +#include "trezor/crypto/ecdsa.h" +#include "trezor/crypto/memzero.h" +#include "trezor/crypto/secp256k1.h" +#include "trezor/crypto/sha3.h" + +#include + +#define TRON_ADDRESS_PREFIX 0x41 // Mainnet addresses start with 'T' + +/** + * Generate TRON address from secp256k1 public key + * TRON uses Keccak256(uncompressed_pubkey) and takes last 20 bytes, + * then prepends 0x41 and Base58Check encodes it + */ +bool tron_getAddress(const uint8_t public_key[33], char* address, + size_t address_len) { + if (address_len < TRON_ADDRESS_MAX_LEN) { + return false; + } + + uint8_t uncompressed_pubkey[65]; + uint8_t hash[32]; + uint8_t addr_bytes[21]; + + // Uncompress the public key + if (!ecdsa_uncompress_pubkey(&secp256k1, public_key, uncompressed_pubkey)) { + return false; + } + + // Keccak256 hash of uncompressed public key (skip first 0x04 byte) + keccak_256(uncompressed_pubkey + 1, 64, hash); + + // Take last 20 bytes of hash and prepend TRON prefix byte + addr_bytes[0] = TRON_ADDRESS_PREFIX; + memcpy(addr_bytes + 1, hash + 12, 20); + + // Base58Check encode with double SHA256 + if (!base58_encode_check(addr_bytes, 21, HASHER_SHA2D, address, + address_len)) { + return false; + } + + // Clean up sensitive data + memzero(uncompressed_pubkey, sizeof(uncompressed_pubkey)); + memzero(hash, sizeof(hash)); + + return true; +} + +/** + * Format TRON amount (SUN) for display + * 1 TRX = 1,000,000 SUN + */ +void tron_formatAmount(char* buf, size_t len, uint64_t amount) { + bignum256 val; + bn_read_uint64(amount, &val); + bn_format(&val, NULL, " TRX", TRON_DECIMALS, 0, false, buf, len); +} + +/** + * Sign a TRON transaction with secp256k1 + */ +bool tron_signTx(const HDNode* node, const TronSignTx* msg, + TronSignedTx* resp) { + if (!node || !msg || !resp) { + return false; + } + + // Verify we have raw transaction data + if (!msg->has_raw_data || msg->raw_data.size == 0) { + return false; + } + + // Get the curve for secp256k1 + const curve_info* curve = get_curve_by_name(SECP256K1_NAME); + if (!curve) { + return false; + } + + // Hash the transaction with SHA256 + uint8_t hash[32]; + sha256_Raw(msg->raw_data.bytes, msg->raw_data.size, hash); + + // Sign with secp256k1 (recoverable signature: 65 bytes including recovery + // ID) + uint8_t sig[65]; + uint8_t pby; + + if (ecdsa_sign_digest(&secp256k1, node->private_key, hash, sig, &pby, NULL) != + 0) { + memzero(hash, sizeof(hash)); + return false; + } + + // Convert to recoverable signature format (r + s + recovery_id) + // The recovery ID allows recovering the public key from the signature + sig[64] = pby; + + // Copy signature to response (65 bytes) + resp->has_signature = true; + resp->signature.size = 65; + memcpy(resp->signature.bytes, sig, 65); + + // Clean up sensitive data + memzero(hash, sizeof(hash)); + memzero(sig, sizeof(sig)); + + return true; +} diff --git a/lib/firmware/txin_check.c b/lib/firmware/txin_check.c index 1a9ac976e..a2d0430d7 100644 --- a/lib/firmware/txin_check.c +++ b/lib/firmware/txin_check.c @@ -47,7 +47,7 @@ void txin_dgst_initialize(void) { } // hash in a txin -void txin_dgst_addto(const uint8_t *data, size_t len) { +void txin_dgst_addto(const uint8_t* data, size_t len) { sha256_Update(&txin_hash_ctx, data, len); return; } @@ -60,7 +60,7 @@ void txin_dgst_final(void) { // compare dgst, amt, addr // returns True if warning condition met -bool txin_dgst_compare(const char *amt_str, const char *addr_str) { +bool txin_dgst_compare(const char* amt_str, const char* addr_str) { // if amt and addr are same AND digest is different, then warn if ((strncmp(amt_str, last_amount_str, AMT_STR_LEN) == 0) && (strncmp(addr_str, last_addr_str, ADDR_STR_LEN) == 0)) { @@ -73,7 +73,7 @@ bool txin_dgst_compare(const char *amt_str, const char *addr_str) { } // return string pointers to digests -void txin_dgst_getstrs(char *prev, char *cur, size_t len) { +void txin_dgst_getstrs(char* prev, char* cur, size_t len) { if (len != DIGEST_STR_LEN) { return; } @@ -85,7 +85,7 @@ void txin_dgst_getstrs(char *prev, char *cur, size_t len) { } // save last state and reset for next tx request -void txin_dgst_save_and_reset(char *amt_str, char *addr_str) { +void txin_dgst_save_and_reset(const char* amt_str, const char* addr_str) { memcpy(txin_last_digest, txin_current_digest, SHA256_DIGEST_LENGTH); memcpy(last_amount_str, amt_str, AMT_STR_LEN); memcpy(last_addr_str, addr_str, ADDR_STR_LEN); diff --git a/lib/firmware/u2f.c b/lib/firmware/u2f.c index cac0db6e5..34a8971f5 100644 --- a/lib/firmware/u2f.c +++ b/lib/firmware/u2f.c @@ -127,15 +127,15 @@ uint32_t next_cid(void) { #define U2F_MAXIMUM_PAYLOAD_LENGTH 7609 typedef struct { uint8_t buf[U2F_MAXIMUM_PAYLOAD_LENGTH]; - uint8_t *buf_ptr; + uint8_t* buf_ptr; uint32_t len; uint8_t seq; uint8_t cmd; } U2F_ReadBuffer; -U2F_ReadBuffer *reader; +U2F_ReadBuffer* reader; -void u2fhid_read(char tiny, const U2FHID_FRAME *f) { +void u2fhid_read(char tiny, const U2FHID_FRAME* f) { // Always handle init packets directly if (f->init.cmd == U2FHID_INIT) { u2fhid_init(f); @@ -182,7 +182,7 @@ void u2fhid_read(char tiny, const U2FHID_FRAME *f) { u2fhid_read_start(f); } -void u2fhid_init_cmd(const U2FHID_FRAME *f) { +void u2fhid_init_cmd(const U2FHID_FRAME* f) { reader->seq = 0; reader->buf_ptr = reader->buf; reader->len = MSG_LEN(*f); @@ -192,7 +192,7 @@ void u2fhid_init_cmd(const U2FHID_FRAME *f) { cid = f->cid; } -void u2fhid_read_start(const U2FHID_FRAME *f) { +void u2fhid_read_start(const U2FHID_FRAME* f) { U2F_ReadBuffer readbuffer; memzero(&readbuffer, sizeof(readbuffer)); @@ -244,7 +244,7 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { u2fhid_ping(reader->buf, reader->len); break; case U2FHID_MSG: - u2fhid_msg((APDU *)reader->buf, reader->len); + u2fhid_msg((APDU*)reader->buf, reader->len); break; case U2FHID_WINK: u2fhid_wink(reader->buf, reader->len); @@ -260,7 +260,8 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { bool saw_button_up_at_least_once = false; while (dialog_timeout > 0 && reader->cmd == 0) { dialog_timeout--; - saw_button_up_at_least_once = saw_button_up_at_least_once || keepkey_button_up(); + saw_button_up_at_least_once = + saw_button_up_at_least_once || keepkey_button_up(); usbPoll(); // may trigger new request // buttonUpdate(); if (saw_button_up_at_least_once && keepkey_button_down() && @@ -284,12 +285,12 @@ void u2fhid_read_start(const U2FHID_FRAME *f) { void u2fInit(void) { usb_set_u2f_rx_callback(u2fhid_read); } -void u2fhid_ping(const uint8_t *buf, uint32_t len) { +void u2fhid_ping(const uint8_t* buf, uint32_t len) { debugLog(0, "", "u2fhid_ping"); send_u2fhid_msg(U2FHID_PING, buf, len); } -void u2fhid_wink(const uint8_t *buf, uint32_t len) { +void u2fhid_wink(const uint8_t* buf, uint32_t len) { debugLog(0, "", "u2fhid_wink"); (void)buf; @@ -305,8 +306,8 @@ void u2fhid_wink(const uint8_t *buf, uint32_t len) { queue_u2f_pkt(&f); } -void u2fhid_init(const U2FHID_FRAME *in) { - const U2FHID_INIT_REQ *init_req = (const U2FHID_INIT_REQ *)&in->init.data; +void u2fhid_init(const U2FHID_FRAME* in) { + const U2FHID_INIT_REQ* init_req = (const U2FHID_INIT_REQ*)&in->init.data; U2FHID_FRAME f; U2FHID_INIT_RESP resp; memzero(&resp, sizeof(resp)); @@ -360,7 +361,7 @@ uint8_t *u2f_out_data(void) } #endif -void u2fhid_msg(const APDU *a, uint32_t len) { +void u2fhid_msg(const APDU* a, uint32_t len) { if ((APDU_LEN(*a) + sizeof(APDU)) > len) { debugLog(0, "", "BAD APDU LENGTH"); debugInt(APDU_LEN(*a)); @@ -389,7 +390,7 @@ void u2fhid_msg(const APDU *a, uint32_t len) { } } -void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, +void send_u2fhid_msg(const uint8_t cmd, const uint8_t* data, const uint32_t len) { if (len > U2F_MAXIMUM_PAYLOAD_LENGTH) { debugLog(0, "", "send_u2fhid_msg failed"); @@ -397,7 +398,7 @@ void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, } U2FHID_FRAME f; - uint8_t *p = (uint8_t *)data; + uint8_t* p = (uint8_t*)data; uint32_t l = len; uint32_t psz; uint8_t seq = 0; @@ -444,7 +445,7 @@ void send_u2fhid_error(uint32_t fcid, uint8_t err) { queue_u2f_pkt(&f); } -void u2f_version(const APDU *a) { +void u2f_version(const APDU* a) { if (APDU_LEN(*a) != 0) { debugLog(0, "", "u2f version - badlen"); send_u2f_error(U2F_SW_WRONG_LENGTH); @@ -458,16 +459,16 @@ void u2f_version(const APDU *a) { send_u2f_msg(version_response, sizeof(version_response)); } -const char *words_from_data(const uint8_t *data, int len) { +const char* words_from_data(const uint8_t* data, int len) { if (len > 32) return NULL; int mlen = len * 3 / 4; static char mnemo[24 * 10]; - int i, j, idx; - char *p = mnemo; + int i, j; + char* p = mnemo; for (i = 0; i < mlen; i++) { - idx = 0; + int idx = 0; for (j = 0; j < 11; j++) { idx <<= 1; idx += (data[(i * 11 + j) / 8] & (1 << (7 - ((i * 11 + j) % 8)))) > 0; @@ -482,7 +483,7 @@ const char *words_from_data(const uint8_t *data, int len) { } static bool getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], - const char **appname) { + const char** appname) { for (unsigned int i = 0; i < sizeof(u2f_well_known) / sizeof(U2FWellKnown); i++) { if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) { @@ -497,7 +498,7 @@ static bool getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], return false; } -static const HDNode *getDerivedNode(uint32_t *address_n, +static const HDNode* getDerivedNode(const uint32_t* address_n, size_t address_n_count) { static CONFIDENTIAL HDNode node; if (!storage_getU2FRoot(&node)) { @@ -518,7 +519,7 @@ static const HDNode *getDerivedNode(uint32_t *address_n, return &node; } -static const HDNode *generateKeyHandle(const uint8_t app_id[], +static const HDNode* generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) { uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; @@ -533,7 +534,7 @@ static const HDNode *generateKeyHandle(const uint8_t app_id[], memcpy(key_handle, key_path, KEY_PATH_LEN); // prepare keypair from /random data - const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + const HDNode* node = getDerivedNode(key_path, KEY_PATH_ENTRIES); if (!node) return NULL; // For second half of keyhandle @@ -547,7 +548,7 @@ static const HDNode *generateKeyHandle(const uint8_t app_id[], return node; } -static const HDNode *validateKeyHandle(const uint8_t app_id[], +static const HDNode* validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[]) { uint32_t key_path[KEY_PATH_ENTRIES]; memcpy(key_path, key_handle, KEY_PATH_LEN); @@ -558,7 +559,7 @@ static const HDNode *validateKeyHandle(const uint8_t app_id[], } } - const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + const HDNode* node = getDerivedNode(key_path, KEY_PATH_ENTRIES); if (!node) return NULL; uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; @@ -576,7 +577,7 @@ static const HDNode *validateKeyHandle(const uint8_t app_id[], return node; } -static void promptRegister(bool request, const U2F_REGISTER_REQ *req) { +static void promptRegister(bool request, const U2F_REGISTER_REQ* req) { #if 0 // Users find it confusing when a Ledger and a KeepKey are plugged in // at the same time. To avoid that, we elect not to show a message in @@ -588,19 +589,19 @@ static void promptRegister(bool request, const U2F_REGISTER_REQ *req) { #else { #endif - const char *appname = ""; - bool readable = getReadableAppId(req->appId, &appname); - layoutU2FDialog( - request, "U2F Register", - readable ? "Do you want to register with %s?" - : "Do you want to register with this U2F application?\n\n%s", - appname); -} + const char* appname = ""; + bool readable = getReadableAppId(req->appId, &appname); + layoutU2FDialog( + request, "U2F Register", + readable ? "Do you want to register with %s?" + : "Do you want to register with this U2F application?\n\n%s", + appname); + } } -void u2f_register(const APDU *a) { +void u2f_register(const APDU* a) { static U2F_REGISTER_REQ last_req; - const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data; + const U2F_REGISTER_REQ* req = (U2F_REGISTER_REQ*)a->data; if (!storage_isInitialized()) { layout_warning_static("Cannot register u2f: not initialized"); @@ -642,14 +643,14 @@ void u2f_register(const APDU *a) { // Buttons said yes if (last_req_state == REG_PASS) { uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; - U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data; + U2F_REGISTER_RESP* resp = (U2F_REGISTER_RESP*)&data; memzero(data, sizeof(data)); resp->registerId = U2F_REGISTER_ID; resp->keyHandleLen = KEY_HANDLE_LEN; // Generate keypair for this appId - const HDNode *node = - generateKeyHandle(req->appId, (uint8_t *)&resp->keyHandleCertSig); + const HDNode* node = + generateKeyHandle(req->appId, (uint8_t*)&resp->keyHandleCertSig); if (!node) { debugLog(0, "", "getDerivedNode Fail"); @@ -658,7 +659,7 @@ void u2f_register(const APDU *a) { } ecdsa_get_public_key65(node->curve->params, node->private_key, - (uint8_t *)&resp->pubKey); + (uint8_t*)&resp->pubKey); memcpy(resp->keyHandleCertSig + resp->keyHandleLen, U2F_ATT_CERT, sizeof(U2F_ATT_CERT)); @@ -671,14 +672,14 @@ void u2f_register(const APDU *a) { memcpy(sig_base.keyHandle, &resp->keyHandleCertSig, KEY_HANDLE_LEN); memcpy(sig_base.pubKey, &resp->pubKey, U2F_PUBKEY_LEN); if (ecdsa_sign(&nist256p1, HASHER_SHA2, U2F_ATT_PRIV_KEY, - (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, + (uint8_t*)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) { send_u2f_error(U2F_SW_WRONG_DATA); return; } // Where to write the signature in the response - uint8_t *resp_sig = + uint8_t* resp_sig = resp->keyHandleCertSig + resp->keyHandleLen + sizeof(U2F_ATT_CERT); // Convert to der for the response const uint8_t sig_len = ecdsa_sig_to_der(sig, resp_sig); @@ -703,16 +704,16 @@ void u2f_register(const APDU *a) { dialog_timeout = 0; } -static void promptAuthenticate(bool request, const U2F_AUTHENTICATE_REQ *req) { - const char *appname = ""; +static void promptAuthenticate(bool request, const U2F_AUTHENTICATE_REQ* req) { + const char* appname = ""; bool readable = getReadableAppId(req->appId, &appname); layoutU2FDialog(request, "U2F Authenticate", readable ? "Log in to %s?" : "Do you want to log in?\n\n%s", appname); } -void u2f_authenticate(const APDU *a) { - const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data; +void u2f_authenticate(const APDU* a) { + const U2F_AUTHENTICATE_REQ* req = (U2F_AUTHENTICATE_REQ*)a->data; static U2F_AUTHENTICATE_REQ last_req; if (!storage_isInitialized()) { @@ -734,7 +735,7 @@ void u2f_authenticate(const APDU *a) { return; } - const HDNode *node = validateKeyHandle(req->appId, req->keyHandle); + const HDNode* node = validateKeyHandle(req->appId, req->keyHandle); if (!node) { debugLog(0, "", "u2f auth - bad keyhandle len"); @@ -783,7 +784,7 @@ void u2f_authenticate(const APDU *a) { // Buttons said yes if (last_req_state == AUTH_PASS) { uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2]; - U2F_AUTHENTICATE_RESP *resp = (U2F_AUTHENTICATE_RESP *)&buf; + U2F_AUTHENTICATE_RESP* resp = (U2F_AUTHENTICATE_RESP*)&buf; const uint32_t ctr = storage_nextU2FCounter(); resp->flags = U2F_AUTH_FLAG_TUP; @@ -800,7 +801,7 @@ void u2f_authenticate(const APDU *a) { memcpy(sig_base.ctr, resp->ctr, 4); memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); if (ecdsa_sign(&nist256p1, HASHER_SHA2, node->private_key, - (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, + (uint8_t*)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) { send_u2f_error(U2F_SW_WRONG_DATA); return; @@ -828,6 +829,6 @@ void send_u2f_error(const uint16_t err) { send_u2f_msg(data, 2); } -void send_u2f_msg(const uint8_t *data, const uint32_t len) { +void send_u2f_msg(const uint8_t* data, const uint32_t len) { send_u2fhid_msg(U2FHID_MSG, data, len); } diff --git a/lib/firmware/u2f.h b/lib/firmware/u2f.h index c09b212cf..c436c3fa3 100644 --- a/lib/firmware/u2f.h +++ b/lib/firmware/u2f.h @@ -3,6 +3,6 @@ #include -const char *words_from_data(const uint8_t *data, int len); +const char* words_from_data(const uint8_t* data, int len); #endif diff --git a/lib/firmware/u2f_knownapps.h b/lib/firmware/u2f_knownapps.h index 5c266cc02..b7d9c626c 100644 --- a/lib/firmware/u2f_knownapps.h +++ b/lib/firmware/u2f_knownapps.h @@ -26,7 +26,7 @@ typedef struct { const uint8_t appid[U2F_APPID_SIZE]; - const char *appname; + const char* appname; } U2FWellKnown; static const U2FWellKnown u2f_well_known[] = { diff --git a/lib/firmware/variant.h b/lib/firmware/variant.h index b22053cd4..611d3c2ab 100644 --- a/lib/firmware/variant.h +++ b/lib/firmware/variant.h @@ -24,6 +24,6 @@ #include #include -const char *variant_getName(void); +const char* variant_getName(void); #endif diff --git a/lib/transport/CMakeLists.txt b/lib/transport/CMakeLists.txt index 41b8d5864..d42d3e113 100644 --- a/lib/transport/CMakeLists.txt +++ b/lib/transport/CMakeLists.txt @@ -15,6 +15,9 @@ set(protoc_pb_sources ${DEVICE_PROTOCOL}/messages-tendermint.proto ${DEVICE_PROTOCOL}/messages-thorchain.proto ${DEVICE_PROTOCOL}/messages-mayachain.proto + ${DEVICE_PROTOCOL}/messages-solana.proto + ${DEVICE_PROTOCOL}/messages-tron.proto + ${DEVICE_PROTOCOL}/messages-ton.proto ${DEVICE_PROTOCOL}/messages.proto) set(protoc_pb_options @@ -29,6 +32,9 @@ set(protoc_pb_options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-tendermint.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-thorchain.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-mayachain.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-solana.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-tron.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ton.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages.options) set(protoc_c_sources @@ -43,6 +49,9 @@ set(protoc_c_sources ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-solana.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-tron.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-ton.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages.pb.c) set(protoc_c_headers @@ -57,6 +66,9 @@ set(protoc_c_headers ${CMAKE_BINARY_DIR}/include/messages-tendermint.pb.h ${CMAKE_BINARY_DIR}/include/messages-thorchain.pb.h ${CMAKE_BINARY_DIR}/include/messages-mayachain.pb.h + ${CMAKE_BINARY_DIR}/include/messages-solana.pb.h + ${CMAKE_BINARY_DIR}/include/messages-tron.pb.h + ${CMAKE_BINARY_DIR}/include/messages-ton.pb.h ${CMAKE_BINARY_DIR}/include/messages.pb.h) set(protoc_pb_sources_moved @@ -71,6 +83,9 @@ set(protoc_pb_sources_moved ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-solana.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-tron.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-ton.proto ${CMAKE_BINARY_DIR}/lib/transport/messages.proto) add_custom_command( @@ -136,6 +151,18 @@ add_custom_command( ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb "--nanopb_out=-f messages-mayachain.options:." messages-mayachain.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb + "--nanopb_out=-f messages-solana.options:." messages-solana.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb + "--nanopb_out=-f messages-tron.options:." messages-tron.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb + "--nanopb_out=-f messages-ton.options:." messages-ton.proto COMMAND ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb diff --git a/lib/transport/pb_decode.c b/lib/transport/pb_decode.c index 5ff4ecd7e..cff906307 100644 --- a/lib/transport/pb_decode.c +++ b/lib/transport/pb_decode.c @@ -452,14 +452,18 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, } case PB_HTYPE_ONEOF: - *(pb_size_t *)iter->pSize = iter->pos->tag; - if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) { + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && + *(pb_size_t*)iter->pSize != iter->pos->tag) + { /* We memset to zero so that any callbacks are set to NULL. - * Then set any default values. */ + * This is because the callbacks might otherwise have values + * from some other union field. */ memset(iter->pData, 0, iter->pos->data_size); pb_message_set_to_defaults((const pb_field_t *)iter->pos->ptr, iter->pData); } + *(pb_size_t*)iter->pSize = iter->pos->tag; + return func(stream, iter->pos, iter->pData); default: diff --git a/scripts/emulator/Dockerfile b/scripts/emulator/Dockerfile index e0b2c6d3f..53c6bdd99 100644 --- a/scripts/emulator/Dockerfile +++ b/scripts/emulator/Dockerfile @@ -3,11 +3,13 @@ FROM kktech/firmware:v15 WORKDIR /kkemu COPY ./ /kkemu +ARG coinsupport="" RUN cmake -C ./cmake/caches/emulator.cmake . \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_BUILD_TYPE=Debug \ + ${coinsupport} \ -DCMAKE_COLOR_MAKEFILE=ON RUN make -j diff --git a/scripts/emulator/bridge.py b/scripts/emulator/bridge.py index 9d861a7b2..23d64666b 100644 --- a/scripts/emulator/bridge.py +++ b/scripts/emulator/bridge.py @@ -20,6 +20,10 @@ app = Flask(__name__) +@app.route('/health') +def health(): + return Response('ok', status=200) + @app.route('/exchange/', methods=['GET', 'POST']) def exchange(kind): diff --git a/scripts/emulator/docker-compose-zoo.yml b/scripts/emulator/docker-compose-zoo.yml new file mode 100644 index 000000000..1ce022939 --- /dev/null +++ b/scripts/emulator/docker-compose-zoo.yml @@ -0,0 +1,39 @@ +version: '3' +services: + kkemu: + image: kktech/kkemu:zoo + build: + context: '../../' + dockerfile: 'scripts/emulator/Dockerfile' + networks: + - zoo-net + ports: + - "127.0.0.1:11044:11044/udp" + - "127.0.0.1:11045:11045/udp" + restart: unless-stopped + + zoo-runner: + build: + context: '../../' + dockerfile: 'scripts/emulator/python-keepkey.Dockerfile' + volumes: + # Mount test scripts + zoo capture scripts from host (editable without rebuild) + - ../../deps/python-keepkey:/kkemu/deps/python-keepkey:ro + - ../../scripts/zoo:/kkemu/scripts/zoo:ro + # Output dir for screenshots (writable) + - ../../zoo-output/screenshots:/output:rw + environment: + - KK_TRANSPORT_MAIN=kkemu:11044 + - KK_TRANSPORT_DEBUG=kkemu:11045 + # ZOO_FLOW: run a single flow (e.g. recovery-cipher, btc-send) + # ZOO_MNEMONIC: custom mnemonic for recovery flows + # ZOO_ARGS: raw CLI args (overrides ZOO_FLOW/ZOO_MNEMONIC) + networks: + - zoo-net + depends_on: + - kkemu + entrypoint: ["/bin/sh", "-c", "pip3 install Pillow >/dev/null 2>&1 && python3 /kkemu/scripts/zoo/capture_screenshots.py --output=/output ${ZOO_FLOW:+--flow=$ZOO_FLOW} ${ZOO_MNEMONIC:+--mnemonic=\"$ZOO_MNEMONIC\"} $ZOO_ARGS"] + +networks: + zoo-net: + external: false diff --git a/scripts/emulator/docker-compose.yml b/scripts/emulator/docker-compose.yml index 89f39adc6..cdae9ab83 100644 --- a/scripts/emulator/docker-compose.yml +++ b/scripts/emulator/docker-compose.yml @@ -11,6 +11,12 @@ services: - "127.0.0.1:11044:11044/udp" - "127.0.0.1:11045:11045/udp" - "127.0.0.1:5000:5000" + healthcheck: + test: ["CMD-SHELL", "python3 -c \"import urllib.request; urllib.request.urlopen('http://localhost:5000/health')\" || exit 1"] + interval: 3s + timeout: 3s + retries: 20 + start_period: 10s python-keepkey: build: context: '../../' @@ -20,7 +26,8 @@ services: networks: - local-net depends_on: - - kkemu + kkemu: + condition: service_healthy firmware-unit: build: context: '../../' @@ -31,7 +38,8 @@ services: networks: - local-net depends_on: - - kkemu + kkemu: + condition: service_healthy networks: local-net: external: false diff --git a/scripts/emulator/python-keepkey-tests.sh b/scripts/emulator/python-keepkey-tests.sh index ef4433f01..28b122fc9 100755 --- a/scripts/emulator/python-keepkey-tests.sh +++ b/scripts/emulator/python-keepkey-tests.sh @@ -1,6 +1,99 @@ #!/bin/sh +set -e mkdir -p /kkemu/test-reports/python-keepkey +mkdir -p /kkemu/test-reports/screenshots + +# Wait for emulator +echo "=== Waiting for emulator ===" +for i in $(seq 1 20); do + if echo -n "PINGPING" | nc -u -w1 kkemu 11044 2>/dev/null | grep -q PONG; then + echo "Emulator ready (attempt $i)" + break + fi + echo " attempt $i/20..." + sleep 2 +done + cd deps/python-keepkey/tests -KK_TRANSPORT_MAIN=kkemu:11044 KK_TRANSPORT_DEBUG=kkemu:11045 pytest -v --junitxml=/kkemu/test-reports/python-keepkey/junit.xml -echo "$?" > /kkemu/test-reports/python-keepkey/status + +# Diagnostic: verify SCREENSHOT flag reaches Python +echo "=== Pre-flight diagnostic ===" +KEEPKEY_SCREENSHOT=1 python3 -c " +import os, sys +sys.path.insert(0, '..') +print('KEEPKEY_SCREENSHOT env:', os.environ.get('KEEPKEY_SCREENSHOT', 'NOT SET')) +from keepkeylib.client import SCREENSHOT +print('SCREENSHOT global:', SCREENSHOT) +# Check if _capture_oled has debug logging +import inspect +from keepkeylib.client import DebugLinkMixin +src = inspect.getsource(DebugLinkMixin._capture_oled) +has_debug = '[SCREENSHOT]' in src +print('_capture_oled has debug logging:', has_debug) +print('_capture_oled first 200 chars:', repr(src[:200])) +" 2>&1 +echo "=== End diagnostic ===" + +# Phase 1: Screenshot captures driven by report SECTIONS (single source of truth) +# +# generate-test-report.py --screenshot-filter reads SECTIONS and emits a pytest -k +# expression for every test with non-empty screenshot expectations. Adding screenshots +# to a test in SECTIONS automatically includes it here — no manual filter maintenance. +echo "=== Phase 1: Report-driven screenshot capture ===" +# Detect firmware version from CMakeLists if not set in env +if [ -z "$FW_VERSION" ]; then + FW_VERSION=$(sed -n '/^project/,/)/p' /kkemu/CMakeLists.txt | grep -oP '\d+\.\d+\.\d+' || echo "7.14.0") + echo "Detected FW_VERSION=$FW_VERSION from CMakeLists.txt" +fi +export FW_VERSION +SCREENSHOT_FILTER=$(python3 ../scripts/generate-test-report.py --screenshot-filter --fw-version=$FW_VERSION 2>/dev/null) +if [ -z "$SCREENSHOT_FILTER" ]; then + echo "WARNING: --screenshot-filter returned empty, falling back to full suite" + SCREENSHOT_FILTER="test_" +fi +echo "Filter: $SCREENSHOT_FILTER" +KEEPKEY_SCREENSHOT=1 \ +SCREENSHOT_DIR=/kkemu/test-reports/screenshots \ +KK_TRANSPORT_MAIN=kkemu:11044 \ +KK_TRANSPORT_DEBUG=kkemu:11045 \ +pytest -v --tb=short \ + -k "$SCREENSHOT_FILTER" \ + --junitxml=/kkemu/test-reports/python-keepkey/junit-screenshots.xml \ + -s 2>&1 || true +# pytest exit code is NOT the gate — screenshot count below is. +# Tests for features not yet merged (gated by requires_firmware/requires_message) +# may fail or skip here; the real check is: did screenshots get captured? + +# Gate: fail fast if screenshots broken +echo "=== Screenshot results ===" +find /kkemu/test-reports/screenshots -name '*.png' -ls 2>/dev/null || echo "NO SCREENSHOTS" +SCREENSHOT_COUNT=$(find /kkemu/test-reports/screenshots -name '*.png' 2>/dev/null | wc -l) +echo "Total PNGs: $SCREENSHOT_COUNT" +if [ "$SCREENSHOT_COUNT" -eq 0 ]; then + echo "FATAL: KEEPKEY_SCREENSHOT=1 but 0 PNGs captured. Screenshot pipeline is broken." + echo "1" > /kkemu/test-reports/python-keepkey/status + exit 1 +fi + +# Phase 2: Full test suite — SECTIONS is the source of truth. +# pytest may exit non-zero (some tests fail before gating kicks in), +# so we capture the JUnit XML regardless, then validate against SECTIONS. +# Tests that skip via requires_message/requires_firmware are OK. +# Tests that fail or are missing from JUnit = CI failure. +echo "=== Phase 2: Full test suite ===" +KK_TRANSPORT_MAIN=kkemu:11044 \ +KK_TRANSPORT_DEBUG=kkemu:11045 \ +pytest -v --junitxml=/kkemu/test-reports/python-keepkey/junit.xml +PYTEST_RC=$? + +echo "=== Phase 2: Generate test report ===" +python3 ../scripts/generate-test-report.py \ + --junit=/kkemu/test-reports/python-keepkey/junit.xml \ + ${FW_VERSION:+--fw-version=$FW_VERSION} || true + +echo "$PYTEST_RC" > /kkemu/test-reports/python-keepkey/status +if [ "$PYTEST_RC" -ne 0 ]; then + echo "pytest failed with exit code $PYTEST_RC" + exit "$PYTEST_RC" +fi diff --git a/scripts/generate-test-report.py b/scripts/generate-test-report.py new file mode 100644 index 000000000..2803adce8 --- /dev/null +++ b/scripts/generate-test-report.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +CI trigger for test report generation. + +The actual report generator lives in deps/python-keepkey/scripts/generate-test-report.py +(stdlib-only PDF writer with SECTIONS as single source of truth for test catalog, +screenshot filter, and report layout). + +This script finds the JUnit XML + screenshots from CI artifacts and calls through. +""" +import os +import sys +import glob +import subprocess + +REPORT_GENERATOR = os.path.join( + os.path.dirname(__file__), '..', 'deps', 'python-keepkey', 'scripts', 'generate-test-report.py' +) + +def main(): + if not os.path.exists(REPORT_GENERATOR): + print("ERROR: %s not found — is the python-keepkey submodule initialized?" % REPORT_GENERATOR, + file=sys.stderr) + sys.exit(1) + + # Collect JUnit XMLs from CI artifacts + junit_files = ( + glob.glob('test-reports/python-keepkey/junit*.xml') + + glob.glob('test-reports/firmware-unit/*.xml') + ) + + # Merge multiple JUnit XMLs into one for the report generator + merged = 'test-reports/junit-merged.xml' + if junit_files: + import xml.etree.ElementTree as ET + root = ET.Element('testsuites') + for jf in junit_files: + try: + tree = ET.parse(jf) + for suite in tree.iter('testsuite'): + root.append(suite) + except ET.ParseError: + print("WARN: skipping malformed %s" % jf, file=sys.stderr) + ET.ElementTree(root).write(merged, xml_declaration=True, encoding='unicode') + print("Merged %d JUnit files -> %s" % (len(junit_files), merged)) + else: + print("WARN: no JUnit XML files found", file=sys.stderr) + merged = None + + # Find screenshots directory + screenshot_dir = 'test-reports/screenshots' + if not os.path.isdir(screenshot_dir): + screenshot_dir = None + + # Build command + cmd = [sys.executable, REPORT_GENERATOR, '--output=test-report.pdf'] + if merged: + cmd.append('--junit=%s' % merged) + if screenshot_dir: + cmd.append('--screenshots=%s' % screenshot_dir) + fw_version = os.environ.get('FW_VERSION') + if fw_version: + cmd.append('--fw-version=%s' % fw_version) + + print("Running: %s" % ' '.join(cmd)) + result = subprocess.run(cmd) + + # Don't exit non-zero -- let the report be uploaded even with partial results + if result.returncode != 0: + print("WARN: report generator exited %d" % result.returncode, file=sys.stderr) + + if os.path.exists('test-report.pdf'): + size = os.path.getsize('test-report.pdf') + print("Generated test-report.pdf (%d bytes)" % size) + else: + print("ERROR: test-report.pdf not created", file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scripts/zoo/capture_screenshots.py b/scripts/zoo/capture_screenshots.py new file mode 100644 index 000000000..31b1ae16c --- /dev/null +++ b/scripts/zoo/capture_screenshots.py @@ -0,0 +1,534 @@ +#!/usr/bin/env python3 +""" +capture_screenshots.py — Orchestrates KeepKey emulator screenshot capture. + +Connects to a running emulator via UDP, executes transaction flows, +and captures the OLED display at each confirmation screen. + +Usage: + python3 capture_screenshots.py --output=/output [--flow=btc-send] + python3 capture_screenshots.py --output=/output --flow=recovery-cipher + python3 capture_screenshots.py --output=/output --flow=recovery-cipher --mnemonic="zoo zoo ... wrong" + +Requires: emulator running on KK_TRANSPORT_MAIN / KK_TRANSPORT_DEBUG +""" +import os +import sys +import argparse +import time +import json + +# Add python-keepkey to path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'deps', 'python-keepkey')) + +from keepkeylib.client import KeepKeyDebuglinkClient +from keepkeylib.transport_udp import UDPTransport +from keepkeylib import messages_pb2 as proto +from keepkeylib import messages_ethereum_pb2 as eth_proto +from keepkeylib import messages_solana_pb2 as sol_proto +from keepkeylib import messages_tron_pb2 as tron_proto +from keepkeylib import messages_ton_pb2 as ton_proto +from keepkeylib import messages_zcash_pb2 as zec_proto +from screenshot import capture_screenshot + + +def make_client(): + """Create a DebugLink client connected to the emulator.""" + main_host = os.environ.get('KK_TRANSPORT_MAIN', '127.0.0.1:11044') + debug_host = os.environ.get('KK_TRANSPORT_DEBUG', '127.0.0.1:11045') + + transport = UDPTransport(main_host) + client = KeepKeyDebuglinkClient(transport) + debug_transport = UDPTransport(debug_host) + client.set_debuglink(debug_transport) + # Disable auto-confirm so we control capture timing + client.auto_button = False + return client + + +def reset_device(client, mnemonic='all ' * 11 + 'all'): + """Wipe and load a known mnemonic for deterministic screenshots.""" + client.auto_button = True # auto-confirm for setup + client.wipe_device() + client.load_device_by_mnemonic( + mnemonic=mnemonic.strip(), + pin='', + passphrase_protection=False, + label='KeepKey Zoo', + language='english', + ) + client.auto_button = False # back to manual for captures + + +def capture(client, output_dir, name): + """Capture current OLED state to output_dir/name.png.""" + path = os.path.join(output_dir, name) + ok = capture_screenshot(client.debug, path, scale=3) + if ok: + fsize = os.path.getsize(path) + status = 'OK' if fsize > 400 else 'BLANK?' + print(f" [{status}] {name} ({fsize}B)") + return ok + + +def capture_with_meta(client, output_dir, name, meta): + """Capture OLED + save metadata JSON sidecar.""" + ok = capture(client, output_dir, name) + if ok and meta: + json_path = os.path.join(output_dir, name.replace('.png', '.json')) + with open(json_path, 'w') as f: + json.dump(meta, f, indent=2) + return ok + + +def send_and_capture(client, msg, output_dir, screen_name): + """Send msg, wait for ButtonRequest, capture OLED, then confirm. + + Core pattern: send -> ButtonRequest (screen displayed) -> capture -> press -> advance. + """ + ret = client.call_raw(msg) + while isinstance(ret, proto.ButtonRequest): + time.sleep(0.15) # animation flush + capture(client, output_dir, screen_name) + client.debug.press_yes() + ret = client.call_raw(proto.ButtonAck()) + return ret + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: BTC Send +# ═══════════════════════════════════════════════════════════════════════ + +def flow_btc_send(client, out): + """Bitcoin send flow — address display.""" + from keepkeylib import tx_api + client.set_tx_api(tx_api.TxApiBitcoin) + reset_device(client) + + print(" [btc-send] Get address (show on device)...") + send_and_capture(client, proto.GetAddress( + address_n=[44 | 0x80000000, 0 | 0x80000000, 0 | 0x80000000, 0, 0], + coin_name='Bitcoin', + show_display=True, + ), out, '01-btc-get-address.png') + + +def flow_btc_sign(client, out): + """Bitcoin sign transaction — confirm output + fee screens.""" + from keepkeylib import tx_api + client.set_tx_api(tx_api.TxApiBitcoin) + reset_device(client) + + print(" [btc-sign] Sign transaction...") + # Use a simple 1-in-1-out tx + inp1 = proto.TxInputType( + address_n=[44 | 0x80000000, 0 | 0x80000000, 0 | 0x80000000, 0, 0], + prev_hash=bytes.fromhex('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882'), + prev_index=0, + amount=390000, + ) + out1 = proto.TxOutputType( + address='1MJ2tj2ThBE62pXbBSwVDT1Gn72SKPkLhD', + amount=380000, + script_type=proto.OutputScriptType.Value('PAYTOADDRESS'), + ) + + try: + (signatures, serialized_tx) = client.sign_tx('Bitcoin', [inp1], [out1]) + # Screenshots captured during the sign flow via DebugLink auto-confirm + except Exception as e: + print(f" sign_tx raised: {e}") + + capture(client, out, '02-btc-confirm-output.png') + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: ETH Send +# ═══════════════════════════════════════════════════════════════════════ + +def flow_eth_send(client, out): + """Ethereum send flow — address display.""" + reset_device(client) + + print(" [eth-send] Get ETH address...") + send_and_capture(client, eth_proto.EthereumGetAddress( + address_n=[44 | 0x80000000, 60 | 0x80000000, 0 | 0x80000000, 0, 0], + show_display=True, + ), out, '01-eth-get-address.png') + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: Solana +# ═══════════════════════════════════════════════════════════════════════ + +def flow_solana_address(client, out): + """Solana get address — full 44-char base58.""" + reset_device(client) + + print(" [solana] Get Solana address...") + try: + send_and_capture(client, sol_proto.SolanaGetAddress( + address_n=[44 | 0x80000000, 501 | 0x80000000, 0 | 0x80000000], + show_display=True, + ), out, '01-sol-get-address.png') + except Exception as e: + print(f" solana not available: {e}") + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: Recovery Cipher (character-by-character seed entry) +# ═══════════════════════════════════════════════════════════════════════ + +def flow_recovery_cipher(client, out, mnemonic=None, word_count=12, with_pin=False): + """Recovery cipher flow — captures every screen of character-by-character + seed phrase entry. This is the major UX bottleneck for users. + + Captures: + - Initial "reminder" / instructions screen + - First cipher grid (scrambled alphabet) + - Character entry progression (each keystroke updates the grid) + - Auto-complete events (word matched from partial input) + - Word boundary transitions (space between words) + - Final confirmation / success screen + + Each screenshot gets a JSON sidecar with metadata: + - cipher: the 26-char scrambled alphabet mapping + - word_index: which word (0-based) + - char_index: which character within the word + - typed_so_far: characters entered for current word + - auto_completed: the word if auto-complete triggered + - phase: 'pin' | 'reminder' | 'cipher_grid' | 'char_entry' | 'auto_complete' | 'word_space' | 'done' + """ + + if mnemonic is None: + mnemonic = 'all ' * 11 + 'all' + mnemonic = mnemonic.strip() + mnemonic_words = mnemonic.split() + + if word_count is None: + word_count = len(mnemonic_words) + + print(f" [recovery-cipher] {word_count}-word recovery, {len(mnemonic_words)} words to enter") + print(f" [recovery-cipher] First 3 words: {' '.join(mnemonic_words[:3])}...") + + # Step 1: Wipe the device so it's uninitialized + client.wipe_device() + + step = 1 + + # Step 2: Start recovery + ret = client.call_raw(proto.RecoveryDevice( + word_count=word_count, + passphrase_protection=False, + pin_protection=with_pin, + label='KeepKey Zoo Recovery', + language='english', + enforce_wordlist=True, + use_character_cipher=True, + )) + + # Step 3: Handle optional PIN entry + if with_pin and isinstance(ret, proto.PinMatrixRequest): + capture_with_meta(client, out, f'{step:02d}-pin-entry-1.png', { + 'phase': 'pin', 'step': 'first_pin_entry', + }) + step += 1 + + pin_encoded = client.debug.encode_pin('135246') + ret = client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) + + if isinstance(ret, proto.PinMatrixRequest): + capture_with_meta(client, out, f'{step:02d}-pin-entry-2.png', { + 'phase': 'pin', 'step': 'confirm_pin_entry', + }) + step += 1 + pin_encoded = client.debug.encode_pin('135246') + ret = client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) + + # Step 4: Reminder / instructions screen (ButtonRequest) + if isinstance(ret, proto.ButtonRequest): + capture_with_meta(client, out, f'{step:02d}-recovery-reminder.png', { + 'phase': 'reminder', + 'description': 'Instructions screen before cipher entry begins', + }) + step += 1 + client.debug.press_yes() + ret = client.call_raw(proto.ButtonAck()) + + # Step 5: Character-by-character cipher entry + total_chars = 0 + total_auto_completes = 0 + recovery_log = [] + + for word_idx, word in enumerate(mnemonic_words): + word_chars_entered = [] + print(f" Word {word_idx + 1}/{len(mnemonic_words)}: '{word}'") + + for char_idx, character in enumerate(word): + if not isinstance(ret, proto.CharacterRequest): + print(f" WARNING: Expected CharacterRequest, got {ret.__class__.__name__}") + capture_with_meta(client, out, f'{step:02d}-unexpected-{ret.__class__.__name__}.png', { + 'phase': 'error', + 'expected': 'CharacterRequest', + 'got': ret.__class__.__name__, + 'word_index': word_idx, + 'char_index': char_idx, + }) + step += 1 + break + + # Read cipher + layout in one DebugLink call (avoids 3 round-trips) + recovery_state = client.debug.read_recovery_state() + cipher = recovery_state['cipher'] + + # Capture the cipher grid screen BEFORE sending character + is_first_char = (char_idx == 0) + capture_name = f'{step:02d}-w{word_idx + 1:02d}-c{char_idx + 1:02d}-cipher' + if is_first_char: + capture_name += '-initial' + capture_name += '.png' + + meta = { + 'phase': 'cipher_grid' if is_first_char else 'char_entry', + 'word_index': word_idx, + 'word': word, + 'char_index': char_idx, + 'target_char': character, + 'typed_so_far': ''.join(word_chars_entered), + 'cipher': cipher if isinstance(cipher, str) else cipher.decode('ascii', errors='replace'), + 'cipher_mapping': {chr(97 + i): (cipher[i] if isinstance(cipher[i], str) else chr(cipher[i])) + for i in range(min(26, len(cipher)))}, + } + capture_with_meta(client, out, capture_name, meta) + step += 1 + total_chars += 1 + + # Encode and send the character through the cipher + encoded_character = cipher[ord(character) - 97] + ret = client.call_raw(proto.CharacterAck(character=encoded_character)) + word_chars_entered.append(character) + + # Check for auto-complete (single call) + auto_completed = client.debug.read_recovery_auto_completed_word() + + if word == auto_completed: + total_auto_completes += 1 + print(f" Auto-completed '{word}' after {len(word_chars_entered)} chars") + + # Capture the auto-complete screen + capture_with_meta(client, out, f'{step:02d}-w{word_idx + 1:02d}-auto-complete.png', { + 'phase': 'auto_complete', + 'word_index': word_idx, + 'word': word, + 'chars_needed': len(word_chars_entered), + 'total_chars_in_word': len(word), + }) + step += 1 + + recovery_log.append({ + 'word': word, + 'chars_typed': len(word_chars_entered), + 'auto_completed': True, + }) + + # Send space to move to next word (except last) + if word_idx < len(mnemonic_words) - 1: + capture_with_meta(client, out, f'{step:02d}-w{word_idx + 1:02d}-word-space.png', { + 'phase': 'word_space', + 'word_index': word_idx, + 'completed_word': word, + 'next_word_index': word_idx + 1, + }) + step += 1 + ret = client.call_raw(proto.CharacterAck(character=' ')) + break + else: + # Word was fully typed without auto-complete + recovery_log.append({ + 'word': word, + 'chars_typed': len(word), + 'auto_completed': False, + }) + # Send space to move to next word (except last) + if word_idx < len(mnemonic_words) - 1: + if isinstance(ret, proto.CharacterRequest): + ret = client.call_raw(proto.CharacterAck(character=' ')) + + # Step 6: Final CharacterAck(done=True) + if isinstance(ret, proto.CharacterRequest): + capture_with_meta(client, out, f'{step:02d}-final-before-done.png', { + 'phase': 'done', + 'description': 'Final screen before submitting recovery', + 'total_chars_typed': total_chars, + 'total_auto_completes': total_auto_completes, + }) + step += 1 + ret = client.call_raw(proto.CharacterAck(done=True)) + + # Step 7: Capture the result + if isinstance(ret, proto.Success): + print(f" Recovery SUCCESS: {ret.message}") + capture_with_meta(client, out, f'{step:02d}-recovery-success.png', { + 'phase': 'success', + 'message': ret.message, + 'total_screenshots': step, + 'total_chars_typed': total_chars, + 'total_auto_completes': total_auto_completes, + }) + elif isinstance(ret, proto.Failure): + print(f" Recovery FAILED: {ret.message}") + capture_with_meta(client, out, f'{step:02d}-recovery-failure.png', { + 'phase': 'failure', + 'message': ret.message, + }) + else: + print(f" Unexpected response: {ret.__class__.__name__}") + capture_with_meta(client, out, f'{step:02d}-recovery-unexpected.png', { + 'phase': 'error', + 'response_type': ret.__class__.__name__, + }) + + step += 1 + + # Write summary log + summary = { + 'flow': 'recovery-cipher', + 'word_count': word_count, + 'total_screenshots': step - 1, + 'total_chars_typed': total_chars, + 'total_auto_completes': total_auto_completes, + 'with_pin': with_pin, + 'words': recovery_log, + } + summary_path = os.path.join(out, 'summary.json') + with open(summary_path, 'w') as f: + json.dump(summary, f, indent=2) + print(f" -> summary.json ({step - 1} screenshots, {total_chars} chars, {total_auto_completes} auto-completes)") + + +def flow_recovery_cipher_with_pin(client, out, mnemonic=None): + """Recovery cipher with PIN — tests the full PIN + cipher flow.""" + flow_recovery_cipher(client, out, mnemonic=mnemonic, with_pin=True) + + +def flow_recovery_cipher_24(client, out): + """Recovery cipher with 24-word mnemonic.""" + mnemonic_24 = 'dignity pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic' + flow_recovery_cipher(client, out, mnemonic=mnemonic_24, word_count=24) + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: TRON +# ═══════════════════════════════════════════════════════════════════════ + +def flow_tron_send(client, out): + """TRON address display.""" + reset_device(client) + + print(" [tron] Get TRON address...") + try: + send_and_capture(client, tron_proto.TronGetAddress( + address_n=[44 | 0x80000000, 195 | 0x80000000, 0 | 0x80000000, 0, 0], + show_display=True, + ), out, '01-tron-get-address.png') + except Exception as e: + print(f" tron not available: {e}") + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: TON +# ═══════════════════════════════════════════════════════════════════════ + +def flow_ton_send(client, out): + """TON address display.""" + reset_device(client) + + print(" [ton] Get TON address...") + try: + send_and_capture(client, ton_proto.TonGetAddress( + address_n=[44 | 0x80000000, 607 | 0x80000000, 0 | 0x80000000], + show_display=True, + ), out, '01-ton-get-address.png') + except Exception as e: + print(f" ton sign not available: {e}") + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow: Zcash Orchard +# ═══════════════════════════════════════════════════════════════════════ + +def flow_zcash_fvk(client, out): + """Zcash Orchard Full Viewing Key derivation.""" + reset_device(client) + + print(" [zcash] Get Orchard FVK...") + try: + send_and_capture(client, zec_proto.ZcashGetOrchardFVK( + address_n=[32 | 0x80000000, 133 | 0x80000000, 0 | 0x80000000], + ), out, '01-zcash-orchard-fvk.png') + except Exception as e: + print(f" zcash fvk not available: {e}") + + +# ═══════════════════════════════════════════════════════════════════════ +# Flow Registry +# ═══════════════════════════════════════════════════════════════════════ + +FLOWS = { + 'btc-send': flow_btc_send, + 'btc-sign': flow_btc_sign, + 'eth-send': flow_eth_send, + 'solana-address': flow_solana_address, + 'tron-send': flow_tron_send, + 'ton-send': flow_ton_send, + 'zcash-fvk': flow_zcash_fvk, + 'recovery-cipher': flow_recovery_cipher, + 'recovery-cipher-pin': flow_recovery_cipher_with_pin, + 'recovery-cipher-24': flow_recovery_cipher_24, +} + + +def main(): + parser = argparse.ArgumentParser(description='KeepKey Zoo Screenshot Capture') + parser.add_argument('--output', default='zoo-output/screenshots', help='Output directory') + parser.add_argument('--flow', default=None, help='Run single flow (default: all)') + parser.add_argument('--mnemonic', default=None, help='Custom mnemonic for recovery flows') + parser.add_argument('--list-flows', action='store_true', help='List available flows') + args = parser.parse_args() + + if args.list_flows: + print("\nAvailable flows:") + for name, func in FLOWS.items(): + doc = (func.__doc__ or '').split('\n')[0].strip() + print(f" {name:25s} {doc}") + print() + return + + print("\nKeepKey Zoo — Screenshot Capture\n") + + client = make_client() + + flows_to_run = {args.flow: FLOWS[args.flow]} if args.flow else FLOWS + + for name, func in flows_to_run.items(): + flow_dir = os.path.join(args.output, name) + os.makedirs(flow_dir, exist_ok=True) + print(f" Flow: {name}") + try: + # Pass mnemonic to recovery flows that accept it + if args.mnemonic and name.startswith('recovery-cipher') and name != 'recovery-cipher-24': + func(client, flow_dir, mnemonic=args.mnemonic) + else: + func(client, flow_dir) + except Exception as e: + import traceback + print(f" ERROR: {e}") + traceback.print_exc() + print() + + print(f" Screenshots: {args.output}/") + print(" Done.\n") + + +if __name__ == '__main__': + main() diff --git a/scripts/zoo/generate_report.py b/scripts/zoo/generate_report.py new file mode 100644 index 000000000..e71d850b1 --- /dev/null +++ b/scripts/zoo/generate_report.py @@ -0,0 +1,571 @@ +#!/usr/bin/env python3 +""" +generate_report.py — Firmware review PDF from real KeepKey emulator screenshots. + +Generates a human-readable firmware release document with: + - Executive summary of firmware capabilities + - Per-chain sections with real OLED captures + security context + - Feature highlights (what changed, why it matters) + - Recovery cipher UX walkthrough + +Usage: + python3 generate_report.py \ + --screenshots /tmp/zoo-test \ + --output artifacts/firmware-review.pdf \ + --version "7.14.0" +""" +import os +import sys +import json +import argparse +import glob +from datetime import datetime + +from fpdf import FPDF +from PIL import Image, ImageDraw + + +def safe(text): + """Strip non-latin1 chars for PDF core fonts.""" + return str(text).encode('latin-1', errors='replace').decode('latin-1') + + +# ═══════════════════════════════════════════════════════════════════════ +# Report content — what goes on each page +# ═══════════════════════════════════════════════════════════════════════ + +CHAIN_SECTIONS = [ + { + 'id': 'btc', + 'title': 'Bitcoin (BTC)', + 'accent': (247, 147, 26), + 'security': 'CRITICAL', + 'screenshots': ['btc-address.png', 'btc-segwit.png'], + 'what': 'Full address display with QR code. Supports legacy (P2PKH), SegWit (P2SH-P2WPKH), and native SegWit (bech32) address types.', + 'why': 'Address verification on the device screen is the primary defense against clipboard hijacking malware. The QR code enables air-gapped verification with a phone camera.', + 'details': [ + 'Legacy, SegWit, and native SegWit addresses supported', + 'QR code rendered on device for mobile verification', + 'Full address shown - no truncation', + 'Account and address index displayed', + ], + }, + { + 'id': 'eth', + 'title': 'Ethereum (ETH)', + 'accent': (98, 126, 234), + 'security': 'CRITICAL', + 'screenshots': ['eth-address.png'], + 'what': 'Full 42-character hex address display. Supports EIP-155 chain ID for all EVM networks (Polygon, Arbitrum, Optimism, Avalanche, BSC, Base).', + 'why': 'ETH addresses lack built-in checksums. The device screen is the only reliable verification point. Chain ID display prevents cross-network sends.', + 'details': [ + 'Full 0x-prefixed address (42 chars)', + 'EIP-155 chain ID for multi-chain EVM support', + 'Same derivation path m/44\'/60\'/0\'/0/0 for all EVM chains', + 'Gas price and limit shown during signing', + ], + }, + { + 'id': 'solana', + 'title': 'Solana (SOL)', + 'accent': (20, 241, 149), + 'security': 'CRITICAL', + 'new': True, + 'screenshots': ['solana-address.png'], + 'what': 'Full 44-character base58 address display. Native SOL transfers, SPL token transfers, and unknown program instruction handling with clear-signing.', + 'why': 'Solana base58 addresses are 32-44 characters. Middle-truncation (XXXX...XXXX) was a spoofing vector in older firmware -- attackers could craft keys with matching prefix+suffix. Full display eliminates this.', + 'details': [ + 'Full 44-character base58 address (no truncation)', + 'Native SOL and SPL token transfer parsing', + 'Unknown programs show full program ID for verification', + 'Transaction instruction count displayed', + ], + }, + { + 'id': 'cosmos', + 'title': 'Cosmos (ATOM)', + 'accent': (100, 100, 200), + 'security': 'HIGH', + 'screenshots': ['cosmos-address.png'], + 'what': 'Cosmos bech32 address display with full address verification. Supports MsgSend with amount, recipient, and memo.', + 'why': 'Cosmos addresses use bech32 encoding with chain-specific prefixes. Full display ensures the user verifies the correct chain and address.', + 'details': [ + 'Full bech32 address with cosmos1... prefix', + 'Amount, recipient, memo shown during signing', + 'QR code for mobile verification', + ], + }, + { + 'id': 'thorchain', + 'title': 'THORChain (RUNE)', + 'accent': (35, 220, 200), + 'security': 'HIGH', + 'screenshots': ['thorchain-address.png'], + 'what': 'THORChain address display and swap transaction signing with full memo verification.', + 'why': 'THORChain swap memos control the entire swap operation. Memo manipulation can redirect funds to an attacker address. Full memo display is essential.', + 'details': [ + 'Full thor1... bech32 address', + 'Swap memo shown in full during signing', + 'Amount and fee verification', + ], + }, + { + 'id': 'maya', + 'title': 'Maya Protocol (CACAO)', + 'accent': (60, 180, 220), + 'security': 'HIGH', + 'screenshots': ['maya-address.png'], + 'what': 'Maya Protocol address display and cross-chain swap signing.', + 'why': 'Maya is a THORChain fork -- same memo-based swap architecture requires full memo verification.', + 'details': [ + 'Full maya1... bech32 address', + 'Cross-chain swap memo verification', + ], + }, + { + 'id': 'osmosis', + 'title': 'Osmosis (OSMO)', + 'accent': (180, 80, 220), + 'security': 'HIGH', + 'screenshots': ['osmosis-address.png'], + 'what': 'Osmosis address display with IBC transfer and LP operation support.', + 'why': 'Osmosis IBC transfers cross chain boundaries. Full address and memo verification prevents fund loss to wrong chains.', + 'details': [ + 'Full osmo1... bech32 address', + 'IBC transfer memo support', + ], + }, + { + 'id': 'xrp', + 'title': 'Ripple (XRP)', + 'accent': (35, 160, 230), + 'security': 'HIGH', + 'screenshots': ['xrp-address.png'], + 'what': 'XRP address display with destination tag support.', + 'why': 'XRP destination tags route funds to specific accounts on exchanges. Missing or wrong tags cause permanent fund loss.', + 'details': [ + 'Full r... address display', + 'Destination tag shown when present', + 'Amount in drops converted to XRP', + ], + }, + { + 'id': 'ltc', + 'title': 'Litecoin (LTC)', + 'accent': (190, 190, 190), + 'security': 'HIGH', + 'screenshots': ['ltc-address.png'], + 'what': 'Litecoin address display with legacy and SegWit support.', + 'why': 'Same UTXO model as Bitcoin. Full address verification prevents address substitution attacks.', + 'details': ['Full L.../M.../ltc1... address with QR code'], + }, + { + 'id': 'bch', + 'title': 'Bitcoin Cash (BCH)', + 'accent': (140, 200, 60), + 'security': 'HIGH', + 'screenshots': ['bch-address.png'], + 'what': 'Bitcoin Cash CashAddr format display.', + 'why': 'BCH and BTC share similar address formats. Device shows the coin name to prevent cross-chain sends.', + 'details': ['CashAddr format with coin identification'], + }, + { + 'id': 'doge', + 'title': 'Dogecoin (DOGE)', + 'accent': (200, 180, 50), + 'security': 'HIGH', + 'screenshots': ['doge-address.png'], + 'what': 'Dogecoin address display with full verification.', + 'why': 'UTXO chain with Bitcoin-derived address format. Device verification prevents address swaps.', + 'details': ['Full D... address with QR code'], + }, + { + 'id': 'dash', + 'title': 'Dash (DASH)', + 'accent': (0, 140, 230), + 'security': 'HIGH', + 'screenshots': ['dash-address.png'], + 'what': 'Dash address display.', + 'why': 'UTXO chain requiring device-side address verification.', + 'details': ['Full X... address with QR code'], + }, +] + +FEATURE_SECTIONS = [ + { + 'id': 'recovery-cipher', + 'title': 'Recovery Cipher (Seed Entry)', + 'accent': (20, 241, 149), + 'security': 'CRITICAL', + 'new_in_release': True, + 'screenshots': { + 'dir': 'recovery-cipher-v10', + 'picks': ['01-recovery-reminder.png', '02-w01-c01-cipher-initial.png', + '07-w02-c01-cipher-initial.png', '63-w12-c01-cipher-initial.png', + '68-recovery-success.png'], + }, + 'what': 'Character-by-character seed phrase entry using a scrambled cipher grid. Previous completed word now displayed below current word progress (new in 7.14.0).', + 'why': 'The cipher grid reshuffles after every keystroke, preventing keylogger and screen-watching attacks. The new previous-word display (e.g. "11: anger") helps users track their position without losing context during the tedious 12/24 word entry process.', + 'highlights': [ + 'NEW: Previous completed word shown below current progress', + 'Full auto-completed BIP39 word displayed (not truncated)', + 'Cipher grid reshuffles every character entry', + 'Auto-complete after 3-4 chars when unique BIP39 match found', + 'Per-word BIP39 validation rejects invalid words immediately', + ], + }, +] + +SECURITY_COLORS = { + 'CRITICAL': (220, 50, 50), + 'HIGH': (240, 160, 20), + 'MEDIUM': (240, 190, 30), +} + + +# ═══════════════════════════════════════════════════════════════════════ +# PDF Builder +# ═══════════════════════════════════════════════════════════════════════ + +class FirmwareReport(FPDF): + + def __init__(self, version='7.14.0'): + super().__init__(orientation='P', unit='mm', format='letter') + self.fw_version = version + self.set_auto_page_break(auto=True, margin=15) + + def header(self): + if self.page_no() <= 1: + return + self.set_font('Helvetica', '', 7) + self.set_text_color(140, 140, 140) + self.cell(0, 4, safe(f'KeepKey Firmware {self.fw_version} - Screen Review'), align='L') + self.ln(6) + + def footer(self): + self.set_y(-10) + self.set_font('Helvetica', '', 7) + self.set_text_color(160, 160, 160) + self.cell(0, 4, f'Page {self.page_no()}', align='C') + + def _badge(self, x, y, text, color): + self.set_fill_color(*color) + w = self.get_string_width(text) + 6 + self.rect(x, y, w, 5, 'F') + self.set_xy(x + 3, y + 0.5) + self.set_font('Helvetica', 'B', 7) + self.set_text_color(255, 255, 255) + self.cell(0, 4, text) + + def _section_header(self, title, accent, security=None, is_new=False): + self.set_fill_color(18, 18, 26) + self.rect(0, self.get_y(), 216, 12, 'F') + self.set_fill_color(*accent) + self.rect(0, self.get_y() + 12, 216, 0.6, 'F') + self.set_xy(10, self.get_y() + 2) + self.set_font('Helvetica', 'B', 12) + self.set_text_color(255, 255, 255) + self.cell(0, 8, safe(title)) + if is_new: + self._badge(self.get_string_width(title) + 16, self.get_y() + 3, 'NEW', (20, 180, 80)) + if security: + sc = SECURITY_COLORS.get(security, (100, 100, 100)) + self.set_xy(180, self.get_y()) + self.set_font('Helvetica', 'B', 8) + self.set_text_color(*sc) + self.cell(0, 8, security) + self.set_y(self.get_y() + 15) + + def _embed_screenshot(self, path, caption=None): + """Embed a real emulator screenshot with device bezel.""" + if not os.path.exists(path): + return + try: + img = Image.open(path) + # Add black bezel + pw, ph = img.width + 16, img.height + 12 + bezel = Image.new('RGB', (pw, ph), (0, 0, 0)) + bezel.paste(img, (8, 6)) + draw = ImageDraw.Draw(bezel) + draw.rectangle([0, 0, pw - 1, ph - 1], outline=(80, 80, 80)) + + tmp = path + '.tmp.png' + bezel.save(tmp) + + w_mm = 140 + h_mm = w_mm * ph / pw + if h_mm > 50: + h_mm = 50 + w_mm = h_mm * pw / ph + + x = (216 - w_mm) / 2 # center + self.image(tmp, x=x, y=self.get_y(), w=w_mm) + self.set_y(self.get_y() + h_mm + 2) + os.remove(tmp) + + if caption: + self.set_font('Helvetica', 'I', 7) + self.set_text_color(120, 120, 120) + self.cell(0, 4, safe(caption), align='C') + self.ln(5) + except Exception as e: + self.set_font('Helvetica', '', 8) + self.set_text_color(200, 50, 50) + self.cell(0, 4, f'[image error: {e}]') + self.ln(5) + + # ── Pages ───────────────────────────────────────────────────────── + + def add_title_page(self, chain_count, feature_count, screenshot_count): + self.add_page() + + # Header + self.set_fill_color(18, 18, 26) + self.rect(0, 0, 216, 40, 'F') + self.set_fill_color(20, 241, 149) + self.rect(0, 40, 216, 1.2, 'F') + + self.set_xy(14, 8) + self.set_font('Helvetica', 'B', 24) + self.set_text_color(255, 255, 255) + self.cell(0, 10, 'KeepKey Firmware') + + self.set_xy(14, 22) + self.set_font('Helvetica', 'B', 14) + self.set_text_color(20, 241, 149) + self.cell(0, 10, safe(f'v{self.fw_version} Screen Review')) + + self.set_xy(160, 10) + self.set_font('Helvetica', '', 9) + self.set_text_color(160, 160, 160) + self.cell(0, 5, datetime.now().strftime('%Y-%m-%d')) + + # Summary box + y = 50 + self.set_xy(14, y) + self.set_font('Helvetica', 'B', 11) + self.set_text_color(40, 40, 40) + self.cell(0, 7, 'About This Report') + y += 9 + self.set_xy(14, y) + self.set_font('Helvetica', '', 9) + self.set_text_color(60, 60, 60) + self.multi_cell(185, 5, safe( + 'This document presents real screenshots captured from the KeepKey firmware emulator ' + 'via DebugLink. Every image is a pixel-accurate rendering of what users see on the ' + '256x64 OLED display during address verification, transaction signing, and device management. ' + 'No synthetic mockups -- all screens are captured from running firmware.' + )) + y = self.get_y() + 6 + + # Stats + self.set_xy(14, y) + self.set_font('Helvetica', 'B', 11) + self.cell(0, 7, 'Coverage') + y += 9 + stats = [ + (f'{chain_count} chains', 'Address display verified'), + (f'{feature_count} features', 'Security-critical flows documented'), + (f'{screenshot_count} screenshots', 'Real emulator captures (not mockups)'), + ] + for val, desc in stats: + self.set_xy(18, y) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(20, 140, 80) + self.cell(30, 5, val) + self.set_font('Helvetica', '', 9) + self.set_text_color(80, 80, 80) + self.cell(0, 5, desc) + y += 6 + + def add_chain_page(self, section, screenshots_dir): + self.add_page() + self._section_header( + section['title'], section['accent'], + section.get('security'), section.get('new', False)) + + y = self.get_y() + + # What it shows + self.set_xy(10, y) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'What The User Sees') + self.ln(6) + self.set_x(10) + self.set_font('Helvetica', '', 9) + self.set_text_color(60, 60, 60) + self.multi_cell(192, 5, safe(section['what'])) + self.ln(2) + + # Screenshots + for fname in section.get('screenshots', []): + path = os.path.join(screenshots_dir, fname) + self._embed_screenshot(path, fname.replace('.png', '').replace('-', ' ')) + + # Why it matters + self.set_x(10) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'Why This Matters') + self.ln(6) + self.set_x(10) + self.set_font('Helvetica', '', 9) + self.set_text_color(60, 60, 60) + self.multi_cell(192, 5, safe(section['why'])) + self.ln(3) + + # Details + if section.get('details'): + self.set_x(10) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'Details') + self.ln(5) + self.set_font('Helvetica', '', 8) + self.set_text_color(60, 60, 60) + for d in section['details']: + self.set_x(14) + self.cell(0, 4, safe(f'- {d}')) + self.ln(5) + + def add_feature_page(self, section, screenshots_base): + self.add_page() + self._section_header( + section['title'], section['accent'], + section.get('security'), + section.get('new_in_release', False)) + + # What changed + self.set_x(10) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'What Changed') + self.ln(6) + self.set_x(10) + self.set_font('Helvetica', '', 9) + self.set_text_color(60, 60, 60) + self.multi_cell(192, 5, safe(section['what'])) + self.ln(2) + + # Screenshots + ss_conf = section.get('screenshots', {}) + ss_dir = os.path.join(screenshots_base, ss_conf.get('dir', '')) + picks = ss_conf.get('picks', []) + shown = 0 + for fname in picks: + if shown >= 4: + break # Max 4 per feature page + path = os.path.join(ss_dir, fname) + if os.path.exists(path): + caption = fname.replace('.png', '').replace('-', ' ') + self._embed_screenshot(path, caption) + shown += 1 + + # Page break if getting long + if self.get_y() > 220 and shown < len(picks): + self.add_page() + + # Why it matters + if self.get_y() > 230: + self.add_page() + self.set_x(10) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'Why This Matters') + self.ln(6) + self.set_x(10) + self.set_font('Helvetica', '', 9) + self.set_text_color(60, 60, 60) + self.multi_cell(192, 5, safe(section['why'])) + self.ln(3) + + # Highlights + if section.get('highlights'): + self.set_x(10) + self.set_font('Helvetica', 'B', 9) + self.set_text_color(40, 40, 40) + self.cell(0, 5, 'Highlights') + self.ln(5) + self.set_font('Helvetica', '', 8) + for h in section['highlights']: + color = (60, 60, 60) + prefix = '-' + if h.startswith('NEW:'): + color = (20, 150, 60) + prefix = '+' + self.set_text_color(*color) + self.set_x(14) + self.cell(0, 4, safe(f'{prefix} {h}')) + self.ln(5) + + +# ═══════════════════════════════════════════════════════════════════════ +# Main +# ═══════════════════════════════════════════════════════════════════════ + +def main(): + parser = argparse.ArgumentParser(description='KeepKey Firmware Review PDF') + parser.add_argument('--screenshots', required=True, help='Base dir with captured screenshots') + parser.add_argument('--output', default='firmware-review.pdf', help='Output PDF path') + parser.add_argument('--version', default='7.14.0', help='Firmware version') + args = parser.parse_args() + + base = args.screenshots + print(f'\nKeepKey Firmware {args.version} - Report Generator\n') + + # Find available screenshots + all_flows_dir = os.path.join(base, 'all-flows') + if not os.path.isdir(all_flows_dir): + all_flows_dir = base # flat structure + + avail = set(os.listdir(all_flows_dir)) if os.path.isdir(all_flows_dir) else set() + print(f' Screenshots dir: {all_flows_dir}') + print(f' Available: {len([f for f in avail if f.endswith(".png")])} PNGs') + + # Count what we have + chains_with_screenshots = sum( + 1 for s in CHAIN_SECTIONS + if any(os.path.exists(os.path.join(all_flows_dir, f)) for f in s['screenshots']) + ) + total_pngs = len([f for f in avail if f.endswith('.png')]) + + # Feature screenshot counts + for fs in FEATURE_SECTIONS: + ss = fs.get('screenshots', {}) + d = os.path.join(base, ss.get('dir', '')) + if os.path.isdir(d): + total_pngs += len([f for f in os.listdir(d) if f.endswith('.png')]) + + pdf = FirmwareReport(version=args.version) + + # Title page + pdf.add_title_page(chains_with_screenshots, len(FEATURE_SECTIONS), total_pngs) + + # Feature sections first (what's new / important) + for section in FEATURE_SECTIONS: + print(f' + Feature: {section["title"]}') + pdf.add_feature_page(section, base) + + # Chain sections + for section in CHAIN_SECTIONS: + has_any = any( + os.path.exists(os.path.join(all_flows_dir, f)) + for f in section['screenshots'] + ) + if has_any: + print(f' + Chain: {section["title"]}') + pdf.add_chain_page(section, all_flows_dir) + else: + print(f' - Chain: {section["title"]} (no screenshots)') + + # Write + os.makedirs(os.path.dirname(args.output) or '.', exist_ok=True) + pdf.output(args.output) + size_kb = os.path.getsize(args.output) / 1024 + print(f'\n Report: {args.output} ({size_kb:.0f} KB)') + print(f' Done.\n') + + +if __name__ == '__main__': + main() diff --git a/scripts/zoo/package.json b/scripts/zoo/package.json new file mode 100644 index 000000000..1895d748d --- /dev/null +++ b/scripts/zoo/package.json @@ -0,0 +1,9 @@ +{ + "name": "keepkey-zoo-report-scripts", + "private": true, + "dependencies": { + "sharp": "^0.34.5", + "pdf-lib": "^1.17.1", + "qrcode": "^1.5.4" + } +} diff --git a/scripts/zoo/screenshot.py b/scripts/zoo/screenshot.py new file mode 100644 index 000000000..c8db53c54 --- /dev/null +++ b/scripts/zoo/screenshot.py @@ -0,0 +1,75 @@ +""" +screenshot.py — Capture and decode KeepKey emulator OLED screenshots. + +The DebugLinkGetState response contains a 2048-byte `layout` field +packed as 1 bit per pixel (256 wide x 64 tall). Each byte holds 8 +vertical pixels, LSB = topmost row within that byte. + +Byte index: x + (y // 8) * 256 +Bit within byte: y % 8 +""" +import os +from PIL import Image + + +OLED_W = 256 +OLED_H = 64 +LAYOUT_SIZE = OLED_W * OLED_H // 8 # 2048 bytes + +# Default upscale factor for readability (256x64 is tiny on modern screens) +DEFAULT_SCALE = 2 + + +def decode_layout(layout_bytes, scale=1): + """Decode 2048-byte bitfield into a PIL Image. + + Args: + layout_bytes: 2048-byte 1bpp bitfield from DebugLinkGetState.layout + scale: Integer upscale factor (1=native 256x64, 2=512x128, etc.) + """ + w, h = OLED_W * scale, OLED_H * scale + im = Image.new("RGB", (w, h), (0, 0, 0)) + pix = im.load() + + for x in range(OLED_W): + for y in range(OLED_H): + byte_idx = x + (y // 8) * OLED_W + if byte_idx < len(layout_bytes): + if (layout_bytes[byte_idx] >> (y % 8)) & 1: + # Fill scale x scale block + for sx in range(scale): + for sy in range(scale): + pix[x * scale + sx, y * scale + sy] = (255, 255, 255) + + return im + + +def capture_screenshot(debug_client, filename, scale=DEFAULT_SCALE): + """Capture current OLED state via DebugLink and save as PNG. + + Args: + debug_client: DebugLink instance (has read_state() or _call()) + filename: Output PNG path + scale: Integer upscale factor (default 2 for 512x128 output) + + Returns: + True if screenshot captured, False if layout unavailable. + """ + from keepkeylib import messages_pb2 as proto + + state = debug_client._call(proto.DebugLinkGetState()) + + if not state.HasField('layout') or len(state.layout) < LAYOUT_SIZE: + print(f" WARNING: layout field empty or too small ({len(state.layout) if state.HasField('layout') else 0} bytes)") + return False + + im = decode_layout(state.layout, scale=scale) + + os.makedirs(os.path.dirname(filename) or '.', exist_ok=True) + im.save(filename) + return True + + +def decode_layout_raw(layout_bytes): + """Decode layout to native 256x64 without scaling (for programmatic use).""" + return decode_layout(layout_bytes, scale=1) diff --git a/scripts/zoo/smoke_screenshot.py b/scripts/zoo/smoke_screenshot.py new file mode 100644 index 000000000..40353604a --- /dev/null +++ b/scripts/zoo/smoke_screenshot.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Minimal smoke test: boot emulator, read one OLED frame, save PNG, exit. + +Usage: python3 smoke_screenshot.py [output.png] +Exit 0 = screenshot captured, non-blank +Exit 1 = failed +""" +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'deps', 'python-keepkey')) + +from keepkeylib.client import KeepKeyDebuglinkClient +from keepkeylib.transport_udp import UDPTransport +from keepkeylib import messages_pb2 as proto + +out = sys.argv[1] if len(sys.argv) > 1 else '/tmp/smoke.png' +main_host = os.environ.get('KK_TRANSPORT_MAIN', '127.0.0.1:11044') +debug_host = os.environ.get('KK_TRANSPORT_DEBUG', '127.0.0.1:11045') + +print("1. Connecting to %s..." % main_host) +c = KeepKeyDebuglinkClient(UDPTransport(main_host)) +c.set_debuglink(UDPTransport(debug_host)) +v = c.features +print("2. Firmware: %s v%d.%d.%d" % (v.vendor, v.major_version, v.minor_version, v.patch_version)) + +print("3. DebugLinkGetState (read_layout)...") +state = c.debug._call(proto.DebugLinkGetState()) +layout = state.layout +print("4. Layout: %d bytes" % len(layout)) + +if len(layout) < 2048: + print("FAIL: layout too small") + sys.exit(1) + +# Check non-blank +nonzero = sum(1 for b in layout if (b if isinstance(b, int) else ord(b)) != 0) +print("5. Non-zero bytes: %d / 2048" % nonzero) + +# Decode to PNG +try: + from screenshot import capture_screenshot + os.makedirs(os.path.dirname(out) or '.', exist_ok=True) + capture_screenshot(c.debug, out, scale=3) + sz = os.path.getsize(out) + print("6. PNG: %s (%d bytes)" % (out, sz)) + if sz > 400: + print("PASS: real screenshot captured") + sys.exit(0) + else: + print("FAIL: PNG too small (likely blank)") + sys.exit(1) +except ImportError: + # No Pillow — just report layout stats + if nonzero > 10: + print("PASS: layout has content (%d non-zero bytes), but no Pillow to save PNG" % nonzero) + sys.exit(0) + else: + print("FAIL: layout is blank") + sys.exit(1) diff --git a/scripts/zoo/wait-for-emu.sh b/scripts/zoo/wait-for-emu.sh new file mode 100755 index 000000000..f5178177a --- /dev/null +++ b/scripts/zoo/wait-for-emu.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# Wait for KeepKey emulator to respond on UDP 11044 +# Usage: ./wait-for-emu.sh [host] [port] [timeout_seconds] + +HOST="${1:-127.0.0.1}" +PORT="${2:-11044}" +TIMEOUT="${3:-30}" + +echo "Waiting for emulator at $HOST:$PORT (timeout ${TIMEOUT}s)..." + +i=0 +while [ "$i" -lt "$TIMEOUT" ]; do + # Send PINGPING, expect PONGPONG back + RESP=$(echo -n "PINGPING" | nc -u -w1 "$HOST" "$PORT" 2>/dev/null | head -c8) + if [ "$RESP" = "PONGPONG" ]; then + echo "Emulator ready." + exit 0 + fi + sleep 1 + i=$((i + 1)) +done + +echo "ERROR: Emulator not responding after ${TIMEOUT}s" +exit 1 diff --git a/tools/bootloader/main.c b/tools/bootloader/main.c index 0982af3ad..7f62a4655 100644 --- a/tools/bootloader/main.c +++ b/tools/bootloader/main.c @@ -92,7 +92,7 @@ static inline void __attribute__((noreturn)) jump_to_firmware(int trust) { trust = SIG_OK; #ifdef TEST_UNSIGNED - trust = SIG_FAIL; + trust = SIG_FAIL; // cppcheck-suppress redundantAssignment #endif #endif @@ -103,6 +103,7 @@ static inline void __attribute__((noreturn)) jump_to_firmware(int trust) { const vector_table_t *new_vtable = (const vector_table_t *)(FLASH_APP_START); + // cppcheck-suppress duplicateBranch if (new_vtable->irq[NVIC_ETH_IRQ] != new_vtable->irq[NVIC_ETH_WKUP_IRQ]) { // If these vectors are not equal, the blupdater is in the firmware // space. Let it use its own vector table. msp will be used in @@ -203,6 +204,7 @@ static bool isFirmwareUpdateMode(void) { if (keepkey_button_down()) return true; // Firmware isn't there? + // cppcheck-suppress knownConditionTrueFalse if (!magic_ok()) return true; // Check if the firmware wants us to boot into firmware update mode. @@ -216,7 +218,7 @@ static bool isFirmwareUpdateMode(void) { bool magic_ok(void) { #ifndef DEBUG_ON - app_meta_td *app_meta = (app_meta_td *)FLASH_META_MAGIC; + const app_meta_td *app_meta = (const app_meta_td *)FLASH_META_MAGIC; return memcmp((void *)&app_meta->magic, META_MAGIC_STR, META_MAGIC_SIZE) == 0; #else @@ -246,6 +248,7 @@ const VariantInfo *variant_getInfo(void) { /// Runs through application firmware checking, and then boots. static void boot(void) { + // cppcheck-suppress knownConditionTrueFalse if (!magic_ok()) { layout_simple_message("Please visit keepkey.com/get-started"); return; diff --git a/tools/emulator/CMakeLists.txt b/tools/emulator/CMakeLists.txt index 7b0574e65..16aff0c9a 100644 --- a/tools/emulator/CMakeLists.txt +++ b/tools/emulator/CMakeLists.txt @@ -10,6 +10,11 @@ if(${KK_EMULATOR}) add_executable(kkemu ${sources}) + # Add linker flags for ARM64 Mac compatibility + if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") + target_link_options(kkemu PRIVATE "-Wl,-no_fixup_chains") + endif() + target_link_libraries(kkemu kkfirmware kkfirmware.keepkey diff --git a/unittests/firmware/solana.cpp b/unittests/firmware/solana.cpp new file mode 100644 index 000000000..36ccef252 --- /dev/null +++ b/unittests/firmware/solana.cpp @@ -0,0 +1,618 @@ +extern "C" { +#include "keepkey/firmware/solana.h" +#include "trezor/crypto/memzero.h" +} + +#include "gtest/gtest.h" +#include + +TEST(Solana, FormatAmount) { + char buf[32]; + + solana_formatAmount(buf, sizeof(buf), 1000000000ULL); + EXPECT_STREQ(buf, "1.000000000 SOL"); + + solana_formatAmount(buf, sizeof(buf), 0); + EXPECT_STREQ(buf, "0.000000000 SOL"); + + solana_formatAmount(buf, sizeof(buf), 2500000000ULL); + EXPECT_STREQ(buf, "2.500000000 SOL"); +} + +TEST(Solana, ParseSystemTransfer) { + /* Construct a minimal Solana transaction with a system transfer. + * + * Format: + * [header: 3 bytes] + * [compact-u16: num_accounts] + * [account keys: N * 32 bytes] + * [recent_blockhash: 32 bytes] + * [compact-u16: num_instructions] + * [instruction: program_idx, compact-u16 acct_count, acct_indices, + * compact-u16 data_len, data] + */ + uint8_t raw[256]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; /* num_required_sigs */ + raw[pos++] = 0; /* num_readonly_signed */ + raw[pos++] = 1; /* num_readonly_unsigned (system program) */ + + /* 3 accounts: sender, recipient, system program */ + raw[pos++] = 3; /* compact-u16 */ + + /* Account 0: sender (32 bytes of 0x11) */ + memset(raw + pos, 0x11, 32); + pos += 32; + /* Account 1: recipient (32 bytes of 0x22) */ + memset(raw + pos, 0x22, 32); + pos += 32; + /* Account 2: system program (all zeros) */ + memset(raw + pos, 0x00, 32); + pos += 32; + + /* Recent blockhash (32 bytes) */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 1 instruction */ + raw[pos++] = 1; /* compact-u16 */ + + /* Instruction: system transfer */ + raw[pos++] = 2; /* program_id index (system program) */ + raw[pos++] = 2; /* compact-u16: 2 account indices */ + raw[pos++] = 0; /* from (account 0) */ + raw[pos++] = 1; /* to (account 1) */ + raw[pos++] = 12; /* compact-u16: data length */ + + /* System transfer instruction data: + * u32 LE instruction type (2 = Transfer) + * u64 LE lamports */ + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + /* 1 SOL = 1000000000 = 0x3B9ACA00 */ + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + + EXPECT_EQ(tx.num_accounts, 3); + EXPECT_EQ(tx.num_instructions, 1); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_SYSTEM_TRANSFER); + EXPECT_EQ(tx.instructions[0].lamports, 1000000000ULL); + + /* Verify from/to accounts */ + uint8_t expected_from[32], expected_to[32]; + memset(expected_from, 0x11, 32); + memset(expected_to, 0x22, 32); + EXPECT_TRUE(memcmp(tx.instructions[0].from, expected_from, 32) == 0); + EXPECT_TRUE(memcmp(tx.instructions[0].to, expected_to, 32) == 0); +} + +TEST(Solana, ParseMultiInstruction) { + /* Transaction with 2 system transfers */ + uint8_t raw[512]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 4 accounts */ + raw[pos++] = 4; + memset(raw + pos, 0x11, 32); + pos += 32; /* account 0: sender */ + memset(raw + pos, 0x22, 32); + pos += 32; /* account 1: recipient 1 */ + memset(raw + pos, 0x33, 32); + pos += 32; /* account 2: recipient 2 */ + memset(raw + pos, 0x00, 32); + pos += 32; /* account 3: system program */ + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 2 instructions */ + raw[pos++] = 2; + + /* Instruction 1: transfer 1 SOL to acct 1 */ + raw[pos++] = 3; /* program = system */ + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 1; + raw[pos++] = 12; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + /* Instruction 2: transfer 2 SOL to acct 2 */ + raw[pos++] = 3; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 2; + raw[pos++] = 12; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0x94; + raw[pos++] = 0x35; + raw[pos++] = 0x77; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + + EXPECT_EQ(tx.num_instructions, 2); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_SYSTEM_TRANSFER); + EXPECT_EQ(tx.instructions[0].lamports, 1000000000ULL); + EXPECT_EQ(tx.instructions[1].type, SOL_INSTR_SYSTEM_TRANSFER); + EXPECT_EQ(tx.instructions[1].lamports, 2000000000ULL); +} + +TEST(Solana, ParseSPLTokenTransfer) { + /* Transaction with a SPL token transfer instruction */ + uint8_t raw[512]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 4 accounts: source_ata, dest_ata, authority, token_program */ + raw[pos++] = 4; + memset(raw + pos, 0x11, 32); + pos += 32; /* account 0: source ATA */ + memset(raw + pos, 0x22, 32); + pos += 32; /* account 1: dest ATA */ + memset(raw + pos, 0x33, 32); + pos += 32; /* account 2: authority */ + /* account 3: SPL Token program */ + memcpy(raw + pos, SOL_TOKEN_PROGRAM, 32); + pos += 32; + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 1 instruction */ + raw[pos++] = 1; + + /* SPL Token Transfer */ + raw[pos++] = 3; /* program index = token program */ + raw[pos++] = 3; /* 3 accounts */ + raw[pos++] = 0; /* source */ + raw[pos++] = 1; /* dest */ + raw[pos++] = 2; /* authority */ + raw[pos++] = 9; /* data length */ + raw[pos++] = 3; /* instruction type = Transfer */ + /* amount: 1000000 (1 USDC) in LE */ + raw[pos++] = 0x40; + raw[pos++] = 0x42; + raw[pos++] = 0x0F; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + + EXPECT_EQ(tx.num_instructions, 1); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_TOKEN_TRANSFER); + EXPECT_EQ(tx.instructions[0].amount, 1000000ULL); +} + +TEST(Solana, ParseAssociatedTokenAccountCreate) { + uint8_t raw[512]; + size_t pos = 0; + + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + raw[pos++] = 5; + memset(raw + pos, 0x11, 32); + pos += 32; /* funder */ + memset(raw + pos, 0x22, 32); + pos += 32; /* ata */ + memset(raw + pos, 0x33, 32); + pos += 32; /* owner */ + memset(raw + pos, 0x44, 32); + pos += 32; /* mint */ + memcpy(raw + pos, SOL_ATA_PROGRAM, 32); + pos += 32; /* program */ + + memset(raw + pos, 0xBB, 32); + pos += 32; + + raw[pos++] = 1; + raw[pos++] = 4; /* ata program */ + raw[pos++] = 4; /* 4 account indices */ + raw[pos++] = 0; + raw[pos++] = 1; + raw[pos++] = 2; + raw[pos++] = 3; + raw[pos++] = 0; /* empty data */ + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_ATA_CREATE); + EXPECT_TRUE(tx.instructions[0].has_mint); +} + +TEST(Solana, ParseComputeBudgetUnitPrice) { + uint8_t raw[256]; + size_t pos = 0; + + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + raw[pos++] = 2; + memset(raw + pos, 0x11, 32); + pos += 32; + memcpy(raw + pos, SOL_COMPUTE_BUDGET_PROGRAM, 32); + pos += 32; + + memset(raw + pos, 0xBB, 32); + pos += 32; + + raw[pos++] = 1; + raw[pos++] = 1; /* compute budget program */ + raw[pos++] = 0; /* no account indices */ + raw[pos++] = 9; /* data length */ + raw[pos++] = 3; /* SetComputeUnitPrice */ + raw[pos++] = 0x40; + raw[pos++] = 0x42; + raw[pos++] = 0x0F; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_COMPUTE_BUDGET_UNIT_PRICE); + EXPECT_EQ(tx.instructions[0].extra_value, 1000000ULL); +} + +TEST(Solana, UnknownProgram) { + uint8_t raw[256]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 2 accounts */ + raw[pos++] = 2; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0xFF, 32); + pos += 32; /* unknown program */ + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 1 instruction */ + raw[pos++] = 1; + raw[pos++] = 1; /* program index = 1 (unknown) */ + raw[pos++] = 1; + raw[pos++] = 0; /* 1 account */ + raw[pos++] = 4; /* data length */ + raw[pos++] = 0xDE; + raw[pos++] = 0xAD; + raw[pos++] = 0xBE; + raw[pos++] = 0xEF; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_OPAQUE); + ASSERT_FALSE(solana_parseTx(raw, pos, &tx)); + EXPECT_EQ(tx.num_instructions, 1); + EXPECT_EQ(tx.instructions[0].type, SOL_INSTR_UNKNOWN); +} + +TEST(Solana, ParseTxTooShort) { + uint8_t raw[2] = {0, 0}; + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, sizeof(raw), &tx), SOL_TX_REVIEW_MALFORMED); + EXPECT_FALSE(solana_parseTx(raw, sizeof(raw), &tx)); +} + +TEST(Solana, RejectsTrailingBytes) { + /* Build a valid 1-instruction system transfer, then append extra bytes */ + uint8_t raw[256]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 3 accounts */ + raw[pos++] = 3; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x22, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 1 instruction */ + raw[pos++] = 1; + raw[pos++] = 2; /* program = system */ + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 1; + raw[pos++] = 12; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + /* Verify the base transaction parses OK */ + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_VERIFIED); + ASSERT_TRUE(solana_parseTx(raw, pos, &tx)); + + /* Append trailing garbage */ + raw[pos++] = 0xDE; + raw[pos++] = 0xAD; + + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_MALFORMED); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +} + +TEST(Solana, RejectsOOBAccountIndex) { + /* Transaction with acct_indices[0] = 99 (> num_accounts) */ + uint8_t raw[256]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 3 accounts */ + raw[pos++] = 3; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x22, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 1 instruction */ + raw[pos++] = 1; + raw[pos++] = 2; /* program = system */ + raw[pos++] = 2; /* 2 account indices */ + raw[pos++] = 99; /* OOB: only 3 accounts exist */ + raw[pos++] = 1; + raw[pos++] = 12; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_MALFORMED); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +} + +TEST(Solana, RejectsExcessInstructions) { + /* Transaction with num_instructions = 9 (max is 8) */ + uint8_t raw[256]; + size_t pos = 0; + + /* Header */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + /* 2 accounts */ + raw[pos++] = 2; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; + + /* Blockhash */ + memset(raw + pos, 0xBB, 32); + pos += 32; + + /* 9 instructions (exceeds limit of 8) */ + raw[pos++] = 9; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_OPAQUE); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +} + +TEST(Solana, VersionedMessageIsOpaque) { + uint8_t raw[256]; + size_t pos = 0; + + raw[pos++] = 0x80; /* v0 prefix */ + raw[pos++] = 1; /* num_required_sigs */ + raw[pos++] = 0; /* num_readonly_signed */ + raw[pos++] = 1; /* num_readonly_unsigned */ + + raw[pos++] = 3; /* static accounts */ + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x22, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; /* system program */ + + memset(raw + pos, 0xBB, 32); + pos += 32; /* blockhash */ + + raw[pos++] = 1; /* instructions */ + raw[pos++] = 2; /* program = system */ + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 1; /* account indices */ + raw[pos++] = 12; /* data length */ + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + raw[pos++] = 0; /* zero lookup tables */ + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_OPAQUE); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +} + +TEST(Solana, VersionedMessageWithLookupTableIsOpaque) { + uint8_t raw[256]; + size_t pos = 0; + + raw[pos++] = 0x80; /* v0 prefix */ + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + raw[pos++] = 3; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x22, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; + + memset(raw + pos, 0xBB, 32); + pos += 32; + + raw[pos++] = 1; + raw[pos++] = 2; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 1; + raw[pos++] = 12; + raw[pos++] = 2; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0; + raw[pos++] = 0x00; + raw[pos++] = 0xCA; + raw[pos++] = 0x9A; + raw[pos++] = 0x3B; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + raw[pos++] = 0x00; + + raw[pos++] = 1; /* one lookup table */ + memset(raw + pos, 0x55, 32); + pos += 32; /* table key */ + raw[pos++] = 1; /* writable indexes count */ + raw[pos++] = 0; /* writable index */ + raw[pos++] = 2; /* readonly indexes count */ + raw[pos++] = 1; + raw[pos++] = 2; + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_OPAQUE); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +} + +TEST(Solana, MalformedVersionedLookupTableRejects) { + uint8_t raw[256]; + size_t pos = 0; + + raw[pos++] = 0x80; + raw[pos++] = 1; + raw[pos++] = 0; + raw[pos++] = 1; + + raw[pos++] = 3; + memset(raw + pos, 0x11, 32); + pos += 32; + memset(raw + pos, 0x22, 32); + pos += 32; + memset(raw + pos, 0x00, 32); + pos += 32; + + memset(raw + pos, 0xBB, 32); + pos += 32; + + raw[pos++] = 0; /* zero instructions */ + raw[pos++] = 1; /* one lookup table */ + memset(raw + pos, 0x55, 16); + pos += 16; /* truncated table key */ + + SolanaParsedTx tx; + EXPECT_EQ(solana_inspectTx(raw, pos, &tx), SOL_TX_REVIEW_MALFORMED); + EXPECT_FALSE(solana_parseTx(raw, pos, &tx)); +}