mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-03 16:56:07 +00:00
feat(legacy): Implement UnlockPath.
This commit is contained in:
parent
6d20fccefd
commit
a1afadfd01
@ -71,6 +71,8 @@ static uint8_t msg_resp[MSG_OUT_DECODED_SIZE] __attribute__((aligned));
|
|||||||
// Authorization message type triggered by DoPreauthorized.
|
// Authorization message type triggered by DoPreauthorized.
|
||||||
static MessageType authorization_type = 0;
|
static MessageType authorization_type = 0;
|
||||||
|
|
||||||
|
static uint32_t unlock_path = 0;
|
||||||
|
|
||||||
#define RESP_INIT(TYPE) \
|
#define RESP_INIT(TYPE) \
|
||||||
TYPE *resp = (TYPE *)(void *)msg_resp; \
|
TYPE *resp = (TYPE *)(void *)msg_resp; \
|
||||||
_Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \
|
_Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \
|
||||||
@ -407,6 +409,7 @@ void fsm_abortWorkflows(void) {
|
|||||||
recovery_abort();
|
recovery_abort();
|
||||||
signing_abort();
|
signing_abort();
|
||||||
authorization_type = 0;
|
authorization_type = 0;
|
||||||
|
unlock_path = 0;
|
||||||
#if !BITCOIN_ONLY
|
#if !BITCOIN_ONLY
|
||||||
ethereum_signing_abort();
|
ethereum_signing_abort();
|
||||||
stellar_signingAbort();
|
stellar_signingAbort();
|
||||||
|
@ -88,6 +88,7 @@ void fsm_msgGetOwnershipProof(const GetOwnershipProof *msg);
|
|||||||
void fsm_msgAuthorizeCoinJoin(const AuthorizeCoinJoin *msg);
|
void fsm_msgAuthorizeCoinJoin(const AuthorizeCoinJoin *msg);
|
||||||
void fsm_msgCancelAuthorization(const CancelAuthorization *msg);
|
void fsm_msgCancelAuthorization(const CancelAuthorization *msg);
|
||||||
void fsm_msgDoPreauthorized(const DoPreauthorized *msg);
|
void fsm_msgDoPreauthorized(const DoPreauthorized *msg);
|
||||||
|
void fsm_msgUnlockPath(const UnlockPath *msg);
|
||||||
|
|
||||||
// crypto
|
// crypto
|
||||||
void fsm_msgCipherKeyValue(const CipherKeyValue *msg);
|
void fsm_msgCipherKeyValue(const CipherKeyValue *msg);
|
||||||
|
@ -35,13 +35,15 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) {
|
|||||||
curve = msg->ecdsa_curve_name;
|
curve = msg->ecdsa_curve_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not allow access to SLIP25 paths.
|
// UnlockPath is required to access SLIP25 paths.
|
||||||
if (msg->address_n_count > 0 && msg->address_n[0] == PATH_SLIP25_PURPOSE &&
|
if (msg->address_n_count > 0 && msg->address_n[0] == PATH_SLIP25_PURPOSE) {
|
||||||
config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
|
// Verify that the desired path lies in the unlocked subtree.
|
||||||
|
if (msg->address_n[0] != unlock_path) {
|
||||||
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
|
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
|
||||||
layoutHome();
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// derive m/0' to obtain root_fingerprint
|
// derive m/0' to obtain root_fingerprint
|
||||||
uint32_t root_fingerprint;
|
uint32_t root_fingerprint;
|
||||||
@ -720,3 +722,65 @@ void fsm_msgDoPreauthorized(const DoPreauthorized *msg) {
|
|||||||
msg_write(MessageType_MessageType_PreauthorizedRequest, resp);
|
msg_write(MessageType_MessageType_PreauthorizedRequest, resp);
|
||||||
layoutHome();
|
layoutHome();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fsm_msgUnlockPath(const UnlockPath *msg) {
|
||||||
|
(void)msg;
|
||||||
|
|
||||||
|
RESP_INIT(UnlockedPathRequest);
|
||||||
|
|
||||||
|
CHECK_INITIALIZED
|
||||||
|
|
||||||
|
CHECK_PIN
|
||||||
|
|
||||||
|
const char *KEYCHAIN_MAC_KEY_PATH[] = {"TREZOR", "Keychain MAC key"};
|
||||||
|
|
||||||
|
// UnlockPath is relevant only for SLIP-25 paths.
|
||||||
|
// Note: Currently we only allow unlocking the entire SLIP-25 purpose subtree
|
||||||
|
// instead of per-coin or per-account unlocking in order to avoid UI
|
||||||
|
// complexity.
|
||||||
|
if (msg->address_n_count != 1 || msg->address_n[0] != PATH_SLIP25_PURPOSE) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid path"));
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t keychain_mac_key[32] = {0};
|
||||||
|
if (!fsm_getSlip21Key(KEYCHAIN_MAC_KEY_PATH, 2, keychain_mac_key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMAC_SHA256_CTX hctx;
|
||||||
|
hmac_sha256_Init(&hctx, keychain_mac_key, sizeof(keychain_mac_key));
|
||||||
|
for (size_t i = 0; i < msg->address_n_count; ++i) {
|
||||||
|
hmac_sha256_Update(&hctx, (const uint8_t *)&msg->address_n[i],
|
||||||
|
sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
hmac_sha256_Final(&hctx, resp->mac.bytes);
|
||||||
|
|
||||||
|
// Require confirmation to access SLIP25 paths unless already authorized.
|
||||||
|
if (msg->has_mac) {
|
||||||
|
uint8_t diff = 0;
|
||||||
|
for (size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||||
|
diff |= (msg->mac.bytes[i] - resp->mac.bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->mac.size != SHA256_DIGEST_LENGTH || diff != 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid MAC"));
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layoutConfirmCoinjoinAccess();
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_path = msg->address_n[0];
|
||||||
|
resp->mac.size = SHA256_DIGEST_LENGTH;
|
||||||
|
resp->has_mac = true;
|
||||||
|
msg_write(MessageType_MessageType_UnlockedPathRequest, resp);
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
@ -721,6 +721,12 @@ void layoutAuthorizeCoinJoin(const CoinInfo *coin, uint64_t max_rounds,
|
|||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void layoutConfirmCoinjoinAccess(void) {
|
||||||
|
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||||
|
_("Do you want to allow"), _("access to your"),
|
||||||
|
_("coinjoin account?"), NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void layoutVerifyAddress(const CoinInfo *coin, const char *address) {
|
void layoutVerifyAddress(const CoinInfo *coin, const char *address) {
|
||||||
render_address_dialog(coin, address, _("Confirm address?"),
|
render_address_dialog(coin, address, _("Confirm address?"),
|
||||||
_("Message signed by:"), 0);
|
_("Message signed by:"), 0);
|
||||||
|
@ -76,6 +76,7 @@ void layoutConfirmNondefaultLockTime(uint32_t lock_time,
|
|||||||
bool lock_time_disabled);
|
bool lock_time_disabled);
|
||||||
void layoutAuthorizeCoinJoin(const CoinInfo *coin, uint64_t max_rounds,
|
void layoutAuthorizeCoinJoin(const CoinInfo *coin, uint64_t max_rounds,
|
||||||
uint32_t max_fee_per_kvbyte);
|
uint32_t max_fee_per_kvbyte);
|
||||||
|
void layoutConfirmCoinjoinAccess(void);
|
||||||
void layoutVerifyAddress(const CoinInfo *coin, const char *address);
|
void layoutVerifyAddress(const CoinInfo *coin, const char *address);
|
||||||
void layoutCipherKeyValue(bool encrypt, const char *key);
|
void layoutCipherKeyValue(bool encrypt, const char *key);
|
||||||
void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing);
|
void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing);
|
||||||
|
@ -4,7 +4,7 @@ endif
|
|||||||
|
|
||||||
SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \
|
SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tezos WebAuthn \
|
||||||
DebugLinkRecordScreen DebugLinkEraseSdCard DebugLinkWatchLayout \
|
DebugLinkRecordScreen DebugLinkEraseSdCard DebugLinkWatchLayout \
|
||||||
DebugLinkLayout GetNonce SetBusy UnlockPath \
|
DebugLinkLayout GetNonce SetBusy \
|
||||||
TxAckInput TxAckOutput TxAckPrev TxAckPaymentRequest \
|
TxAckInput TxAckOutput TxAckPrev TxAckPaymentRequest \
|
||||||
EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \
|
EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \
|
||||||
EthereumTypedDataValueRequest EthereumTypedDataValueAck
|
EthereumTypedDataValueRequest EthereumTypedDataValueAck
|
||||||
|
@ -43,9 +43,8 @@ PIN = "1234"
|
|||||||
ROUND_ID_LEN = 32
|
ROUND_ID_LEN = 32
|
||||||
SLIP25_PATH = parse_path("m/10025h")
|
SLIP25_PATH = parse_path("m/10025h")
|
||||||
|
|
||||||
pytestmark = pytest.mark.skip_t1
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
@pytest.mark.setup_client(pin=PIN)
|
@pytest.mark.setup_client(pin=PIN)
|
||||||
def test_sign_tx(client: Client):
|
def test_sign_tx(client: Client):
|
||||||
# NOTE: FAKE input tx
|
# NOTE: FAKE input tx
|
||||||
@ -252,6 +251,7 @@ def test_sign_tx(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_sign_tx_large(client: Client):
|
def test_sign_tx_large(client: Client):
|
||||||
# NOTE: FAKE input tx
|
# NOTE: FAKE input tx
|
||||||
|
|
||||||
@ -399,6 +399,7 @@ def test_sign_tx_large(client: Client):
|
|||||||
assert delay <= max_expected_delay
|
assert delay <= max_expected_delay
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_sign_tx_spend(client: Client):
|
def test_sign_tx_spend(client: Client):
|
||||||
# NOTE: FAKE input tx
|
# NOTE: FAKE input tx
|
||||||
|
|
||||||
@ -473,6 +474,7 @@ def test_sign_tx_spend(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_wrong_coordinator(client: Client):
|
def test_wrong_coordinator(client: Client):
|
||||||
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
||||||
|
|
||||||
@ -499,6 +501,7 @@ def test_wrong_coordinator(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_wrong_account_type(client: Client):
|
def test_wrong_account_type(client: Client):
|
||||||
params = {
|
params = {
|
||||||
"client": client,
|
"client": client,
|
||||||
@ -525,6 +528,7 @@ def test_wrong_account_type(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_cancel_authorization(client: Client):
|
def test_cancel_authorization(client: Client):
|
||||||
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
||||||
|
|
||||||
@ -608,6 +612,7 @@ def test_get_public_key(client: Client):
|
|||||||
assert resp.xpub == EXPECTED_XPUB
|
assert resp.xpub == EXPECTED_XPUB
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_get_address(client: Client):
|
def test_get_address(client: Client):
|
||||||
# Ensure that the SLIP-0025 external chain is inaccessible without user confirmation.
|
# Ensure that the SLIP-0025 external chain is inaccessible without user confirmation.
|
||||||
with pytest.raises(TrezorFailure, match="Forbidden key path"):
|
with pytest.raises(TrezorFailure, match="Forbidden key path"):
|
||||||
@ -689,6 +694,7 @@ def test_get_address(client: Client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip_t1
|
||||||
def test_multisession_authorization(client: Client):
|
def test_multisession_authorization(client: Client):
|
||||||
# Authorize CoinJoin with www.example1.com in session 1.
|
# Authorize CoinJoin with www.example1.com in session 1.
|
||||||
btc.authorize_coinjoin(
|
btc.authorize_coinjoin(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"T1": {
|
"T1": {
|
||||||
"device_tests": {
|
"device_tests": {
|
||||||
|
"T1_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "9b3c916759b79048a4ab3e3fe8ce0ea0cf8d4ae6cfb66a5d712f21edfdb01782",
|
||||||
"T1_bitcoin-test_bcash.py::test_attack_change_input": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
"T1_bitcoin-test_bcash.py::test_attack_change_input": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
||||||
"T1_bitcoin-test_bcash.py::test_send_bch_change": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
"T1_bitcoin-test_bcash.py::test_send_bch_change": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
||||||
"T1_bitcoin-test_bcash.py::test_send_bch_multisig_change": "0962a2e630e06b6d20282cc241be40f41bc1648d0a26247c7c008f32a197d0cb",
|
"T1_bitcoin-test_bcash.py::test_send_bch_multisig_change": "0962a2e630e06b6d20282cc241be40f41bc1648d0a26247c7c008f32a197d0cb",
|
||||||
|
Loading…
Reference in New Issue
Block a user