Skip to content

Commit 3646a56

Browse files
committed
Address mismatch check
1 parent d92b19d commit 3646a56

3 files changed

Lines changed: 59 additions & 1 deletion

File tree

src/handlers/get_shielded_addr.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use zcash_address::unified::{Address as UnifiedAddress, Encoding, Receiver};
44
use ledger_device_sdk::io::Comm;
55
use ledger_device_sdk::log::{error, info};
66

7+
use crate::consts::UNHARDENED_MASK;
8+
79
use crate::app_ui::address::ui_display_shielded_address;
810
use crate::utils::base58_address::{Base58Address, ToBase58Address};
911
use crate::utils::bip32_path::Bip32Path;
@@ -64,6 +66,10 @@ pub fn handler_get_shielded_addr(
6466
error!("Transparent address path not BIP44 compliant");
6567
return Err(AppSW::IncorrectData);
6668
}
69+
if (path.as_slice()[2] & UNHARDENED_MASK) != (transparent_path.as_slice()[2] & UNHARDENED_MASK) {
70+
error!("Orchard and transparent address paths must have matching accounts");
71+
return Err(AppSW::IncorrectData);
72+
}
6773
}
6874

6975
let orchard_fvk = derive_orchard_fvk(&path)?;

src/handlers/get_vk.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use zcash_address::unified::{Encoding, Fvk, Ufvk};
22

33
use alloc::format;
4-
use ledger_device_sdk::info;
4+
use ledger_device_sdk::log::{error, info};
55
use ledger_device_sdk::io::Comm;
66

77
use crate::app_ui::address::{ui_display_orchard_fvk, ui_display_ufvk};
8+
use crate::consts::{UNHARDENED_MASK, ZCASH_BIP44_COIN_TYPE};
89
use crate::utils::{HexSlice, encode_string_response};
910
use crate::zip32::{derive_orchard_fvk, derive_transparent_account_pubkey, orchard_network};
1011
use crate::{
@@ -15,6 +16,16 @@ use crate::{
1516

1617
const VK_RESPONSE_CHUNK_LEN: usize = 255;
1718

19+
fn check_transparent_vk_path(path: &Bip32Path) -> bool {
20+
const HARDENED: u32 = 0x8000_0000;
21+
const BIP44_PURPOSE: u32 = 44;
22+
let p = path.as_slice();
23+
p.len() == 3
24+
&& (p[0] & UNHARDENED_MASK) == BIP44_PURPOSE
25+
&& (p[1] & UNHARDENED_MASK) == ZCASH_BIP44_COIN_TYPE
26+
&& p[2] & HARDENED != 0
27+
}
28+
1829
fn parse_vk_paths(data: &[u8], mode: P2VkMode) -> Result<(Bip32Path, Option<Bip32Path>), AppSW> {
1930
match mode {
2031
P2VkMode::OrchardFvk => Ok((Bip32Path::try_from(data)?, None)),
@@ -63,6 +74,19 @@ pub fn handler_get_vk(
6374
ctx.vk_response = None;
6475

6576
let (path, transparent_path) = parse_vk_paths(data, mode)?;
77+
78+
if let P2VkMode::Ufvk = mode {
79+
let t_path = transparent_path.as_ref().ok_or(AppSW::WrongApduLength)?;
80+
if !check_transparent_vk_path(t_path) {
81+
error!("Transparent VK path is not a valid account-level BIP44 path");
82+
return Err(AppSW::IncorrectData);
83+
}
84+
if (path.as_slice()[2] & UNHARDENED_MASK) != (t_path.as_slice()[2] & UNHARDENED_MASK) {
85+
error!("Orchard and transparent VK paths must have matching accounts");
86+
return Err(AppSW::IncorrectData);
87+
}
88+
}
89+
6690
let orchard_fvk = derive_orchard_fvk(&path)?;
6791

6892
let response_bytes = match mode {

tests/standalone/test_pubkey_cmd.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,20 @@ def test_get_ufvk_requires_transparent_path(backend):
164164
assert len(e.value.data) == 0
165165

166166

167+
def test_get_ufvk_account_mismatch(backend):
168+
with pytest.raises(ExceptionRAPDU) as e:
169+
backend.exchange(
170+
cla=CLA,
171+
ins=InsType.GET_VK,
172+
p1=P1.P1_GET_VK_FIRST,
173+
p2=GetVkMode.UFVK,
174+
data=pack_derivation_path("m/32'/133'/0'") + pack_derivation_path("m/44'/133'/1'"),
175+
)
176+
177+
assert e.value.status == Errors.SW_INVALID_TRANSACTION
178+
assert len(e.value.data) == 0
179+
180+
167181
def test_get_orchard_fvk_confirm_accepted(backend, scenario_navigator):
168182
REF_ORCHARD_FVK_ACC_0 = bytes.fromhex(
169183
"e129bb7d06ed69a5ac01a664482ec9987fd19c40940bf76d98eb8b952974852949b0128d5072f9f92c7f7e8eb49a5434d2c04b67a30a55946d8322df3e484426f6151235e5897d34196943cb8f968312f1c8fba9ed82830b59f801b6de5da835"
@@ -301,3 +315,17 @@ def test_get_orchard_uaddress_requires_transparent_path(backend):
301315

302316
assert e.value.status == Errors.SW_APP_WRONG_APDU_LENGTH
303317
assert len(e.value.data) == 0
318+
319+
320+
def test_get_orchard_uaddress_account_mismatch(backend):
321+
with pytest.raises(ExceptionRAPDU) as e:
322+
backend.exchange(
323+
cla=CLA,
324+
ins=InsType.GET_SHIELDED_ADDRESS,
325+
p1=P1.P1_GET_PUBLIC_KEY_NO_DISPLAY,
326+
p2=GetShieldedAddressMode.UADDRESS,
327+
data=pack_derivation_path("m/32'/133'/0'") + pack_derivation_path("m/44'/133'/1'/0/0"),
328+
)
329+
330+
assert e.value.status == Errors.SW_INVALID_TRANSACTION
331+
assert len(e.value.data) == 0

0 commit comments

Comments
 (0)