diff --git a/releases/Next-ChangeLog.md b/releases/Next-ChangeLog.md index 0952721f..5951180a 100644 --- a/releases/Next-ChangeLog.md +++ b/releases/Next-ChangeLog.md @@ -8,6 +8,7 @@ This lists the new changes that have not yet been published in a normal release. (read more [BIP-322 Proof of Reserves documentation](../docs/proof-of-reserves-bip-322.md) ) - Enhancement: WIF Store export watch-only descriptor - Enhancement: WIF Store address detection without the need for PSBT_IN_BIP32_DERIVATION (Electrum support) +- Enhancement: Improve USB length validation - Bugfix: Disable Virtual Disk and NFC before activating HSM - Bugfix: Custom address default menu position wrong - Bugfix: Delta Mode Trick PIN was never restored from backup diff --git a/shared/usb.py b/shared/usb.py index 1f48a86d..bb5c3c9a 100644 --- a/shared/usb.py +++ b/shared/usb.py @@ -416,10 +416,12 @@ class USBHandler: if cmd == 'dwld': offset, length, fileno = unpack_from('= 4, 'badlen' # regression patch of AFC_BECH32M flag # fixed here https://github.com/Coldcard/ckcc-protocol/commit/a6d901f9fca50755835eca895586ca74d0ca81ed if addr_fmt == 0x17: # old P2TR @@ -506,6 +510,7 @@ class USBHandler: # - text config file must already be uploaded file_len, file_sha = unpack_from(' (384*1024) and mk_num <= 3: raise pytest.skip('mk4+ only case') - import os data = os.urandom(f_len) ll, sha = dev.upload_file(data, verify=True) assert ll == len(data) == f_len @@ -224,4 +218,188 @@ def test_remote_up_download(f_len, dev, mk_num): rb = dev.download_file(ll, sha, file_number=0) assert rb == data + +def test_dwld_offset_at_max(dev, mk_num): + max_txn = 2*1024*1024 + msg = struct.pack('<4sIII', b'dwld', max_txn, 1, 1) + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'bad offset' in str(e.value) + +def test_dwld_offset_one_past_max(dev, mk_num): + max_txn = 2*1024*1024 + msg = struct.pack('<4sIII', b'dwld', max_txn + 1, 1, 1) + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'bad offset' in str(e.value) + +def test_smsg_zero_length_message(dev): + subpath = b'm' + msg = struct.pack('<4sIII', b'smsg', 0x01, len(subpath), 0) + subpath + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'msg too short (min. 2)' in str(e.value) + +def test_smsg_oversized_message(dev): + subpath = b'm' + raw_msg = b'a' * (MSG_SIGNING_MAX_LENGTH + 1) + msg = struct.pack('<4sIII', b'smsg', 0x01, len(subpath), len(raw_msg)) + subpath + raw_msg + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'msg too long (max. 240)' in str(e.value) + +def test_ncry_invalid_pubkey(dev): + msg = struct.pack('<4sI64s', b'ncry', 0x01, bytes(64)) + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'secp256k1_ec_pubkey_parse' in str(e.value) + +@pytest.mark.parametrize("file_no", [0, 1]) +def test_dwld_oob_psram_read(file_no, dev, mk_num): + max_txn = 2*1024*1024 + msg = struct.pack('<4sIII', b'dwld', max_txn - 1, 2, file_no) + with pytest.raises(CCProtoError) as e: + dev.send_recv(msg, encrypt=False) + assert 'bad offset' in str(e.value) + +def test_p2sh_truncated_xfp_paths(dev): + AF_P2SH = 0x08 + header = struct.pack('