diff --git a/shared/teleport.py b/shared/teleport.py index be69a128..fd7dcaf8 100644 --- a/shared/teleport.py +++ b/shared/teleport.py @@ -120,7 +120,13 @@ def decrypt_rx_pubkey(code, payload): rx_pubkey[0] &= 0x01 rx_pubkey[0] |= 0x02 - return rx_pubkey + # validate that it's on the curve... otherwise the code is wrong + try: + ngu.secp256k1.pubkey(rx_pubkey) + + return rx_pubkey + except: + return None async def tk_show_payload(type_code, payload, title, msg, cta=None): # show the QR and/or NFC @@ -153,13 +159,22 @@ async def kt_start_send(rx_data): # - they want to send to this guy # - ask them what to send, etc - # - ask for the sender's password -- any value will be accepted - code = await ux_input_text('', confirm_exit=False, hex_only=True, max_len=8, + while 1: + # - ask for the sender's password -- nearly any value will be accepted + code = await ux_input_text('', confirm_exit=False, hex_only=True, max_len=8, prompt='Teleport Password (number)', min_len=8, b39_complete=False, scan_ok=False, placeholder='########', funct_keys=None, force_xy=None) - if not code: return + if not code: return - rx_pubkey = decrypt_rx_pubkey(code, rx_data) + rx_pubkey = decrypt_rx_pubkey(code, rx_data) + + if not rx_pubkey: + # I think only about 50% odds of catching an incorrect code. Not sure + ch = await ux_show_story( + "Incorrect Teleport Password. You can try again or CANCEL to stop.") + if ch == 'x': return + + break msg = '''You can now teleport secrets! Select from seed words, seed vault keys, \ secure notes or passwords. \ @@ -316,6 +331,8 @@ async def kt_accept_values(dtype, raw): elif dtype == 'p': # raw PSBT -- much bigger more complex from auth import sign_transaction, TXN_INPUT_OFFSET + from public_constants import STXN_FINALIZE + psbt_len = len(raw) # copy into PSRAM @@ -323,7 +340,7 @@ async def kt_accept_values(dtype, raw): out.write(raw) # This will take over UX w/ the signing process - sign_transaction(psbt_len, flags=0x0) + sign_transaction(psbt_len, flags=STXN_FINALIZE) return elif dtype in 'nv': @@ -383,6 +400,9 @@ def encode_payload(my_keypair, his_pubkey, noid_key, body, for_psbt=False): assert len(his_pubkey) == 33 assert len(noid_key) == 5 + # this can fail with ValueError: secp256k1_ec_pubkey_parse + # if the user has provided the wrong value for numeric password + # - better to catch this sooner in decrypt_rx_pubkey session_key = my_keypair.ecdh_multiply(his_pubkey) # stretch noid key out -- will be slow diff --git a/testing/test_teleport.py b/testing/test_teleport.py index 559f4ac9..ce3dec82 100644 --- a/testing/test_teleport.py +++ b/testing/test_teleport.py @@ -15,6 +15,7 @@ from constants import * from test_bbqr import readback_bbqr, split_scan_bbqr from test_notes import need_some_notes, need_some_passwords from test_ephemeral import SEEDVAULT_TEST_DATA +from test_nfc import ndef_parse_txn_psbt # All tests in this file are exclusively meant for Q # @@ -119,10 +120,13 @@ def rx_complete(press_select, need_keypress, press_cancel, cap_story, scan_a_qr, assert len(data) < 2000 # USB protocol limit scan_a_qr(data) - time.sleep(.250) # required - if expect_fail: return - scr = cap_screen() - assert 'Teleport Password' in scr + if expect_fail: + time.sleep(.200) + return + for retries in range(20): + scr = cap_screen() + if 'Teleport Password' in scr: break + time.sleep(.200) if expect_xfp: assert xfp2str(expect_xfp) in scr @@ -137,7 +141,7 @@ def rx_complete(press_select, need_keypress, press_cancel, cap_story, scan_a_qr, def tx_start(press_select, need_keypress, press_cancel, goto_home, pick_menu_item, cap_story, scan_a_qr, enter_complex, cap_screen): # start the Tx process, capturing password and leaving you are picker menu - def doit(rx_qr, rx_code, expect_fail=None): + def doit(rx_qr, rx_code, expect_fail=None, expect_wrong_code=False): goto_home() need_keypress(KEY_QR) time.sleep(.250) # required @@ -154,7 +158,13 @@ def tx_start(press_select, need_keypress, press_cancel, goto_home, pick_menu_ite enter_complex(rx_code) time.sleep(.150) # required + title, story = cap_story() + if expect_wrong_code: + # not a sure thing + if 'Incorrect Teleport Pass' in story: + return True + assert title == 'Key Teleport: Send' assert 'secure notes' in story @@ -349,8 +359,17 @@ def test_tx_wrong_pub(rx_start, tx_start, cap_menu, enter_complex, pick_menu_ite # simulate wrong numeric code only -- sender doesn't know right_code, rx_pubkey = rx_start() - code = '00000000' - pw = tx_start(rx_pubkey, code) + for attempt in range(20): + code = '%08d' % attempt + failed = tx_start(rx_pubkey, code, expect_wrong_code=True) + + if failed: + # 50% odds (apx, maybe?) of wrong code being detected. + print(f'{code} => wasnt accepted') + continue + break + else: + raise pytest.fail('huh') # other contents require other features to be enabled pick_menu_item('Master Seed Words') @@ -373,12 +392,13 @@ def test_tx_wrong_pub(rx_start, tx_start, cap_menu, enter_complex, pick_menu_ite @pytest.mark.unfinalized @pytest.mark.parametrize('num_ins', [ 15 ]) -@pytest.mark.parametrize('M', [1, 4]) # [ 2, 4, 1]) +@pytest.mark.parametrize('M', [2, 4]) @pytest.mark.parametrize('segwit', [True]) -@pytest.mark.parametrize('incl_xpubs', [ False, True ]) +@pytest.mark.parametrize('incl_xpubs', [ False ]) def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, segwit, num_ins, dev, clear_ms, fake_ms_txn, try_sign, incl_xpubs, bitcoind, cap_story, need_keypress, - cap_menu, pick_menu_item, grab_payload, rx_complete, press_select, settings_get, settings_set): + cap_menu, pick_menu_item, grab_payload, rx_complete, press_select, ndef_parse_txn_psbt, + press_nfc, nfc_read, settings_get, settings_set): # IMPORTANT: wont work if you start simulator with --ms flag. Use no args @@ -388,8 +408,6 @@ def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, segwit, num_ins, d clear_ms() use_regtest() - settings_set('finms', 1) - # create a wallet, with 3 bip39 pw's keys, select_wallet = make_myself_wallet(M, do_import=(not incl_xpubs)) N = len(keys) @@ -468,7 +486,21 @@ def test_teleport_ms_sign(M, use_regtest, make_myself_wallet, segwit, num_ins, d assert title == 'Teleport PSBT?' assert 'more signatures' in body - pdb.set_trace() + assert title == 'Final TXID' + txid = body.split()[0] + + # share signed txn via low-level NFC + press_nfc() + time.sleep(.1) + contents = nfc_read() + + got_psbt, got_txn, _ = ndef_parse_txn_psbt(contents, txid, expect_finalized=True) + + assert not got_psbt + assert got_txn + + + # TODO # - send single-sig PSBT