Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0e3b23d
chore: update submodules + nanopb options for 7.14.0 proto chains
BitHighlander Mar 17, 2026
10f1d5f
fix(security): harden signatures_ok() with infective aggregation and …
BitHighlander Mar 12, 2026
9dbd56a
fix(security): replace firmware-side signatures_ok() with metadata ch…
BitHighlander Mar 12, 2026
6b0498b
fix: backport nanopb oneof memory leak fix (upstream 4fe23595)
BitHighlander Mar 12, 2026
a733078
fix: enforce BIP39 wordlist validation during cipher recovery
BitHighlander Mar 8, 2026
e107267
fix: add per-word BIP39 validation during cipher recovery
BitHighlander Mar 8, 2026
4ef4566
feat: add Lynx (LYNX) to coins table
BitHighlander Mar 12, 2026
a12fc38
feat: add BIP-85 child mnemonic derivation
BitHighlander Mar 11, 2026
169ed84
fix: address code review issues in BIP-85 implementation
BitHighlander Mar 12, 2026
b22ed09
fix(bip85): remove has_ checks on required nanopb fields
BitHighlander Mar 12, 2026
1e73bd7
feat(solana): add Solana support (GetAddress, SignTx, SignMessage)
BitHighlander Mar 11, 2026
6c6efe4
fix(solana): address code review findings (C1,C3,C4,H1,H3,H4,L1,L4,L5…
BitHighlander Mar 7, 2026
fc262d6
fix: address code review issues in Solana implementation
BitHighlander Mar 12, 2026
1e8e885
feat(tron,ton): add TRON and TON chain support
BitHighlander Mar 11, 2026
e142df1
fix: address code review issues in Tron and TON implementation
BitHighlander Mar 12, 2026
8b7fb57
fix(ton): compute v4r2 StateInit hash for correct wallet address
BitHighlander Mar 14, 2026
2bf5c47
fix: tron_signTx return false on curve lookup failure
BitHighlander Mar 17, 2026
88cbdfb
chore: fix submodule pointers to upstream master
BitHighlander Mar 17, 2026
40ffd80
chore: fix submodule pointers to upstream master
BitHighlander Mar 17, 2026
8c7f145
Merge branch 'feat/bip85-lynx' into feat/btc-only-build
BitHighlander Mar 17, 2026
be27482
Merge branch 'feat/solana-support' into feat/btc-only-build
BitHighlander Mar 17, 2026
892a00f
Merge branch 'feat/tron-ton-support' into feat/btc-only-build
BitHighlander Mar 17, 2026
2db352b
feat: add BTC-only conditional compilation guards and build scripts
BitHighlander Mar 17, 2026
781aa84
chore: fix submodule pointers
BitHighlander Mar 17, 2026
cf3ad92
ci: add BTC-only firmware build to CI pipeline
BitHighlander Mar 17, 2026
ccf3947
fix: move BIP-85 outside BITCOIN_ONLY guard + remove duplicate nanopb…
BitHighlander Mar 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 95 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,26 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Install clang-format
run: sudo apt-get install -y clang-format-14

- name: Check code formatting
run: |
# Use pre-installed clang-format (ubuntu-latest ships with it)
CF=$(command -v clang-format-14 || command -v clang-format || true)
if [ -z "$CF" ]; then
echo "::warning::clang-format not found, skipping lint"
exit 0
fi
echo "Using: $CF ($($CF --version | head -1))"
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-14 --style=file --dry-run --Werror "$f" 2>/dev/null; then
if ! $CF --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 "Run: clang-format -i <file> to fix formatting"
echo "Run: $CF -i <file> to fix formatting"
echo "Note: treating as warning until codebase is reformatted"
fi
# Warn-only until existing code is reformatted
Expand Down Expand Up @@ -339,6 +343,91 @@ jobs:
bin/*.elf
retention-days: 90

build-arm-firmware-btc-only:
needs: [check-submodules, secret-scan]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
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@v4
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}) [BTC-only]"

- name: Cross-compile BTC-only 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 \
-DCOIN_SUPPORT=BTC \
-DVARIANTS=NoObsoleteVariants \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_COLOR_MAKEFILE=ON && \
make && \
mkdir -p /root/keepkey-firmware/bin-btc && \
cp bin/*.bin /root/keepkey-firmware/bin-btc/ && \
cp bin/*.elf /root/keepkey-firmware/bin-btc/ && \
chmod -R a+rw /root/keepkey-firmware/bin-btc"

- name: Rename firmware artifacts
run: |
cd bin-btc
for f in *.bin; do
[ -f "$f" ] || continue
mv "$f" "firmware.keepkey.btc-only.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.btc-only.v${{ steps.version.outputs.fw_version }}-${{ steps.version.outputs.git_short }}-${f}"
done
ls -lh
echo "::notice::BTC-only firmware v${{ steps.version.outputs.fw_version }} built successfully"

- name: Upload BTC-only firmware artifacts
uses: actions/upload-artifact@v4
with:
name: firmware-btc-only-v${{ steps.version.outputs.fw_version }}-${{ steps.version.outputs.git_short }}
path: |
bin-btc/*.bin
bin-btc/*.elf
retention-days: 90

# ═══════════════════════════════════════════════════════════
# STAGE 3: TEST — run only after builds succeed
# ═══════════════════════════════════════════════════════════
Expand Down Expand Up @@ -431,7 +520,7 @@ jobs:
# ═══════════════════════════════════════════════════════════

publish-emulator:
needs: [unit-tests, python-integration-tests, build-arm-firmware]
needs: [unit-tests, python-integration-tests, build-arm-firmware, build-arm-firmware-btc-only]
if: >-
github.event_name == 'workflow_dispatch' &&
github.event.inputs.publish_emulator == 'true'
Expand Down
14 changes: 9 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ jobs:
if: steps.cache-base.outputs.cache-hit == 'true'
run: docker load -i /tmp/base-image.tar

- name: Cross-compile firmware
- name: Cross-compile firmware (BTC-only)
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 \
-DCOIN_SUPPORT=BTC \
-DVARIANTS=NoObsoleteVariants \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_COLOR_MAKEFILE=ON && \
make && \
Expand Down Expand Up @@ -147,9 +149,11 @@ jobs:
if: steps.cache-base.outputs.cache-hit == 'true'
run: docker load -i /tmp/base-image.tar

- name: Build and test emulator
- name: Build and test emulator (BTC-only)
run: |
docker build -t kkemu-release -f scripts/emulator/Dockerfile .
docker build \
--build-arg coinsupport="-DCOIN_SUPPORT=BTC" \
-t kkemu-release -f scripts/emulator/Dockerfile .
docker run --rm --entrypoint /bin/sh kkemu-release \
-c "make xunit; RC=\$?; exit \$RC"

Expand Down Expand Up @@ -178,9 +182,9 @@ jobs:
cat > release-body.md <<EOF
## KeepKey Firmware v${VER}

### Reproducible Build Verification
### Reproducible Build Verification (BTC-only)
\`\`\`bash
./scripts/build/docker/device/release.sh
./scripts/build/docker/device/btcrelease.sh
tail -c +257 bin/firmware.keepkey.bin | shasum -a 256
\`\`\`

Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bin
build
bin/
build/
.DS_Store
.vscode/

4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ else()
add_definitions(-DDEBUG_LINK=0)
endif()

if("${COIN_SUPPORT}" STREQUAL "BTC")
add_definitions(-DBITCOIN_ONLY)
endif()

if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_definitions(-DDEBUG_ON)
add_definitions(-DMEMORY_PROTECT=0)
Expand Down
22 changes: 22 additions & 0 deletions include/keepkey/firmware/bip85.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef BIP85_H
#define BIP85_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
* Derive a child BIP-39 mnemonic via BIP-85.
*
* Path: m/83696968'/39'/0'/<word_count>'/<index>'
*
* @param word_count Number of words: 12, 18, or 24.
* @param index Child index (0-based).
* @param mnemonic Output buffer (must be at least 241 bytes).
* @param mnemonic_len Size of the output buffer.
* @return true on success, false on error.
*/
bool bip85_derive_mnemonic(uint32_t word_count, uint32_t index,
char *mnemonic, size_t mnemonic_len);

#endif
3 changes: 2 additions & 1 deletion include/keepkey/firmware/coins.def
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//coin_name coin_shortcut address_type maxfee_kb p2sh signed_message_header bip44_account_path forkid/chain_id decimals contract_address xpub_magic segwit force_bip143 curve_name cashaddr_prefix bech32_prefix decred xpub_magic_segwit_p2sh xpub_mmagic_segwit_native nanoaddr_prefix taproot
X(true, "Bitcoin", true, "BTC", true, 0, true, 100000, true, 5, true, "Bitcoin Signed Message:\n", true, 0x80000000, false, 0, true, 8, false, NO_CONTRACT, true, 76067358, true, true, true, false, true, SECP256K1_STRING, false, "", true, "bc", false, false, true, 77429938, true, 78792518, false, "", true, true )
X(true, "Testnet", true, "TEST", true, 111, true, 10000000, true, 196, true, "Bitcoin Signed Message:\n", true, 0x80000001, false, 0, true, 8, false, NO_CONTRACT, true, 70617039, true, true, true, false, true, SECP256K1_STRING, false, "", true, "tb", false, false, true, 71979618, true, 73342198, false, "", true, true )
#ifndef BITCOIN_ONLY
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 )
Expand Down Expand Up @@ -46,6 +47,6 @@ X(true, "Terra", true, "LUNA", false, NA, false, NA, false, N
X(true, "Kava", true, "KAVA", false, NA, false, NA, false, NA, false, {0}, true, 0x800001cb, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "kava", false, false, false, 0, false, 0, false, "", true, false )
X(true, "Secret", true, "SCRT", false, NA, false, NA, false, NA, false, {0}, true, 0x80000211, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "secret", false, false, false, 0, false, 0, false, "", true, false )
X(true, "MAYAChain", true, "CACAO", false, NA, false, NA, false, NA, false, {0}, true, 0x800003a3, false, 0, true, 10, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "maya", false, false, false, 0, false, 0, false, "", true, false )

#endif // BITCOIN_ONLY
#undef X
#undef NO_CONTRACT
8 changes: 7 additions & 1 deletion include/keepkey/firmware/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ enum {
CONCAT(CoinIndex, __COUNTER__),
#include "keepkey/firmware/coins.def"

#ifdef BITCOIN_ONLY
// For full-featured keepkey, this is defined in ethereum_tokens.h. For bitcoin only keepkey, need to
// define it here because ethereum_tokens.h is not included in any file
#define TOKENS_COUNT 0
#else
#define X(INDEX, NAME, SYMBOL, DECIMALS, CONTRACT_ADDRESS) \
CONCAT(CoinIndex, __COUNTER__),
#include "keepkey/firmware/tokens.def"

#endif
CoinIndexLast,
CoinIndexFirst = 0
};


#define COINS_COUNT ((int)CoinIndexLast - (int)CoinIndexFirst)
#define NODE_STRING_LENGTH 50

Expand Down
13 changes: 13 additions & 0 deletions include/keepkey/firmware/fsm.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* This file is part of the TREZOR project.
*
Expand Down Expand Up @@ -77,6 +77,10 @@
void fsm_msgCharacterAck(CharacterAck *msg);
void fsm_msgApplyPolicies(ApplyPolicies *msg);

// BIP-85 (chain-agnostic, available in all builds)
void fsm_msgGetBip85Mnemonic(const GetBip85Mnemonic *msg);

#ifndef BITCOIN_ONLY
// ethereum
void fsm_msgEthereumGetAddress(EthereumGetAddress *msg);
void fsm_msgEthereumSignTx(EthereumSignTx *msg);
Expand Down Expand Up @@ -118,6 +122,15 @@
void fsm_msgMayachainSignTx(const MayachainSignTx *msg);
void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg);

void fsm_msgSolanaGetAddress(const SolanaGetAddress *msg);
void fsm_msgSolanaSignTx(const SolanaSignTx *msg);
void fsm_msgSolanaSignMessage(const SolanaSignMessage *msg);
void fsm_msgTronGetAddress(const TronGetAddress *msg);
void fsm_msgTronSignTx(TronSignTx *msg);
void fsm_msgTonGetAddress(const TonGetAddress *msg);
void fsm_msgTonSignTx(TonSignTx *msg);
#endif // BITCOIN_ONLY

#if DEBUG_LINK
// void fsm_msgDebugLinkDecision(DebugLinkDecision *msg);
void fsm_msgDebugLinkGetState(DebugLinkGetState *msg);
Expand Down
44 changes: 44 additions & 0 deletions include/keepkey/firmware/solana.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef KEEPKEY_FIRMWARE_SOLANA_H
#define KEEPKEY_FIRMWARE_SOLANA_H

#include "trezor/crypto/bip32.h"
#include "messages-solana.pb.h"

#define SOLANA_ADDRESS_SIZE 50 // Base58-encoded Ed25519 public key (typically 44 chars + null)
#define SOLANA_SIGNATURE_SIZE 64 // Ed25519 signature size

// Convert Ed25519 public key to Solana Base58 address
bool solana_publicKeyToAddress(const uint8_t public_key[32], char *address,
size_t address_size);

// Sign Solana transaction (returns false on failure)
bool solana_signTx(const HDNode *node, const SolanaSignTx *msg,
SolanaSignedTx *resp);

// Sign Solana message (off-chain signature)
void solana_signMessage(const HDNode *node, const uint8_t *message,
size_t message_len, uint8_t *signature_out);

// Display message to user for confirmation
bool solana_confirmMessage(const uint8_t *message, size_t message_len);

#endif
Loading
Loading