def test_update_pfs(): balance_proof = BalanceProofUnsignedState.from_dict( make_balance_proof(signer=signer, amount=1).to_dict(), ) message = UpdatePFS.from_balance_proof( balance_proof=balance_proof, reveal_timeout=1, ) assert message.signature == b'' message.sign(signer) assert recover(message._data_to_sign(), message.signature) == ADDRESS
def test_monitoring_global_messages( local_matrix_servers, private_rooms, retry_interval, retries_before_backoff, ): """ Test that RaidenService sends RequestMonitoring messages to global MONITORING_BROADCASTING_ROOM room on newly received balance proofs. """ transport = MatrixTransport({ 'global_rooms': ['discovery', MONITORING_BROADCASTING_ROOM], 'retries_before_backoff': retries_before_backoff, 'retry_interval': retry_interval, 'server': local_matrix_servers[0], 'server_name': local_matrix_servers[0].netloc, 'available_servers': [local_matrix_servers[0]], 'private_rooms': private_rooms, }) transport._client.api.retry_timeout = 0 transport._send_raw = MagicMock() raiden_service = MockRaidenService(None) raiden_service.config = dict(services=dict(monitoring_enabled=True)) transport.start( raiden_service, raiden_service.message_handler, None, ) ms_room_name = make_room_alias(transport.network_id, MONITORING_BROADCASTING_ROOM) ms_room = transport._global_rooms.get(ms_room_name) assert isinstance(ms_room, Room) ms_room.send_text = MagicMock(spec=ms_room.send_text) raiden_service.transport = transport transport.log = MagicMock() balance_proof = make_balance_proof(signer=LocalSigner(HOP1_KEY), amount=1) update_monitoring_service_from_balance_proof( raiden_service, balance_proof, ) gevent.idle() assert ms_room.send_text.call_count == 1 transport.stop() transport.get()
def f(chain_id=1, **kwargs): balance_proof = make_balance_proof(signer=signer, **kwargs) balance_proof.chain_id = chain_id partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state( balance_proof) request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=TokenAmount(55)) request_monitoring.sign(non_closing_signer) # usually not a property of RequestMonitoring, but added for convenience in these tests request_monitoring.non_closing_signer = to_checksum_address( # type: ignore non_closing_signer.address) return request_monitoring
def f(amount=1, nonce=1): balance_proof = make_balance_proof(signer=signer, amount=amount, nonce=nonce) partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state( balance_proof, ) request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) request_monitoring.sign(non_closing_signer) request_monitoring.non_closing_signer = to_checksum_address( non_closing_signer.address) return request_monitoring
def test_update_pfs(): balance_proof = make_balance_proof(signer=signer, amount=1) channel_state = make_channel_state() channel_state.our_state.balance_proof = balance_proof channel_state.partner_state.balance_proof = balance_proof message = UpdatePFS.from_channel_state(channel_state=channel_state) assert message.signature == b'' privkey2, address2 = make_privkey_address() signer2 = LocalSigner(privkey2) message.sign(signer2) assert recover(message._data_to_sign(), message.signature) == address2 assert message == UpdatePFS.from_dict(message.to_dict())
def test_request_monitoring(): partner_signer = LocalSigner(PARTNER_PRIVKEY) balance_proof = make_balance_proof(signer=partner_signer, amount=1) partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state( balance_proof, ) request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) assert request_monitoring with pytest.raises(ValueError): request_monitoring.to_dict() request_monitoring.sign(signer) as_dict = request_monitoring.to_dict() assert RequestMonitoring.from_dict(as_dict) == request_monitoring packed = request_monitoring.pack(request_monitoring.packed()) assert RequestMonitoring.unpack(packed) == request_monitoring # RequestMonitoring can be created directly from BalanceProofSignedState direct_created = RequestMonitoring.from_balance_proof_signed_state( balance_proof, reward_amount=55, ) with pytest.raises(ValueError): # equality test uses `validated` packed format assert direct_created == request_monitoring direct_created.sign(signer) # Instances created from same balance proof are equal assert direct_created == request_monitoring other_balance_proof = make_balance_proof(signer=partner_signer, amount=2) other_instance = RequestMonitoring.from_balance_proof_signed_state( other_balance_proof, reward_amount=55, ) other_instance.sign(signer) # different balance proof ==> non-equality assert other_instance != request_monitoring # test signature verification reward_proof_data = pack_reward_proof( request_monitoring.balance_proof.channel_identifier, request_monitoring.reward_amount, request_monitoring.balance_proof.token_network_address, request_monitoring.balance_proof.chain_id, request_monitoring.balance_proof.nonce, ) assert recover(reward_proof_data, request_monitoring.reward_proof_signature) == ADDRESS blinded_data = pack_balance_proof_update( nonce=request_monitoring.balance_proof.nonce, balance_hash=request_monitoring.balance_proof.balance_hash, additional_hash=request_monitoring.balance_proof.additional_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=request_monitoring.balance_proof.chain_id, token_network_address=request_monitoring.balance_proof. token_network_address, channel_identifier=request_monitoring.balance_proof. channel_identifier, ), partner_signature=request_monitoring.balance_proof.signature, ) assert recover(blinded_data, request_monitoring.non_closing_signature) == ADDRESS balance_proof_data = pack_balance_proof( nonce=request_monitoring.balance_proof.nonce, balance_hash=request_monitoring.balance_proof.balance_hash, additional_hash=request_monitoring.balance_proof.additional_hash, canonical_identifier=CanonicalIdentifier( chain_identifier=request_monitoring.balance_proof.chain_id, token_network_address=request_monitoring.balance_proof. token_network_address, channel_identifier=request_monitoring.balance_proof. channel_identifier, ), ) assert recover( balance_proof_data, request_monitoring.balance_proof.signature, ) == PARTNER_ADDRESS assert request_monitoring.verify_request_monitoring( PARTNER_ADDRESS, ADDRESS)
def make_unsigned_balance_proof(nonce): return BalanceProofUnsignedState.from_dict( make_balance_proof(nonce=nonce, signer=LocalSigner(HOP1_KEY), amount=1).to_dict(), )
def test_tamper_request_monitoring(): """ This test shows ways, how the current implementation of the RequestMonitoring's signature scheme might be used by an attacker to tamper with the BalanceProof that is incorporated in the RequestMonitoring message, if not all three signatures are verified.""" partner_signer = LocalSigner(PARTNER_PRIVKEY) balance_proof = make_balance_proof(signer=partner_signer, amount=1) partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state( balance_proof, ) request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) request_monitoring.sign(signer) # This is the signature, that is supposed to authenticate the message that a monitoring # service receives from a node. Note: It is generated on a valid Balance proof here and reused # to authenticate invalid messages throughout the rest of the test. exploited_signature = request_monitoring.reward_proof_signature reward_proof_data = pack_reward_proof( canonical_identifier=make_canonical_identifier( chain_identifier=request_monitoring.balance_proof.chain_id, token_network_address=request_monitoring.balance_proof. token_network_address, channel_identifier=request_monitoring.balance_proof. channel_identifier, ), reward_amount=request_monitoring.reward_amount, nonce=request_monitoring.balance_proof.nonce, ) # An attacker might change the balance hash partner_signed_balance_proof.balance_hash = 'tampered'.encode() tampered_balance_hash_request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) tampered_bp = tampered_balance_hash_request_monitoring.balance_proof tampered_balance_hash_reward_proof_data = pack_reward_proof( canonical_identifier=make_canonical_identifier( chain_identifier=tampered_bp.chain_id, token_network_address=tampered_bp.token_network_address, channel_identifier=tampered_bp.channel_identifier, ), reward_amount=tampered_balance_hash_request_monitoring.reward_amount, nonce=tampered_balance_hash_request_monitoring.balance_proof.nonce, ) # The signature works/is unaffected by that change... recovered_address_tampered = recover( tampered_balance_hash_reward_proof_data, exploited_signature, ) assert recover(reward_proof_data, exploited_signature) == recovered_address_tampered assert recover(tampered_balance_hash_reward_proof_data, exploited_signature) == ADDRESS # ...but overall verification fails assert not tampered_balance_hash_request_monitoring.verify_request_monitoring( PARTNER_ADDRESS, ADDRESS, ) # An attacker might change the additional_hash partner_signed_balance_proof.additional_hash = 'tampered'.encode() tampered_additional_hash_request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) tampered_bp = tampered_additional_hash_request_monitoring.balance_proof tampered_additional_hash_reward_proof_data = pack_reward_proof( canonical_identifier=make_canonical_identifier( chain_identifier=tampered_bp.chain_id, token_network_address=tampered_bp.token_network_address, channel_identifier=tampered_bp.channel_identifier, ), reward_amount=tampered_additional_hash_request_monitoring. reward_amount, nonce=tampered_additional_hash_request_monitoring.balance_proof.nonce, ) # The signature works/is unaffected by that change... recovered_address_tampered = recover( tampered_additional_hash_reward_proof_data, exploited_signature, ) assert recover(reward_proof_data, exploited_signature) == recovered_address_tampered assert recovered_address_tampered == ADDRESS # ...but overall verification fails assert not tampered_balance_hash_request_monitoring.verify_request_monitoring( PARTNER_ADDRESS, ADDRESS, ) # An attacker can change the non_closing_signature partner_signed_balance_proof.non_closing_signature = 'tampered'.encode() tampered_non_closing_signature_request_monitoring = RequestMonitoring( onchain_balance_proof=partner_signed_balance_proof, reward_amount=55, ) tampered_bp = tampered_non_closing_signature_request_monitoring.balance_proof tampered_non_closing_signature_reward_proof_data = pack_reward_proof( canonical_identifier=make_canonical_identifier( chain_identifier=tampered_bp.chain_id, token_network_address=tampered_bp.token_network_address, channel_identifier=tampered_bp.channel_identifier, ), reward_amount=tampered_non_closing_signature_request_monitoring. reward_amount, nonce=tampered_non_closing_signature_request_monitoring.balance_proof. nonce, ) # The signature works/is unaffected by that change... recovered_address_tampered = recover( tampered_non_closing_signature_reward_proof_data, exploited_signature, ) assert recover(reward_proof_data, exploited_signature) == recovered_address_tampered assert recovered_address_tampered == ADDRESS # ...but overall verification fails assert not tampered_non_closing_signature_request_monitoring.verify_request_monitoring( PARTNER_ADDRESS, ADDRESS, )
def test_pfs_global_messages( local_matrix_servers, private_rooms, retry_interval, retries_before_backoff, monkeypatch, ): """ Test that RaidenService sends UpdatePFS messages to global PATH_FINDING_BROADCASTING_ROOM room on newly received balance proofs. """ transport = MatrixTransport({ 'global_rooms': ['discovery', PATH_FINDING_BROADCASTING_ROOM], 'retries_before_backoff': retries_before_backoff, 'retry_interval': retry_interval, 'server': local_matrix_servers[0], 'server_name': local_matrix_servers[0].netloc, 'available_servers': [local_matrix_servers[0]], 'private_rooms': private_rooms, }) transport._client.api.retry_timeout = 0 transport._send_raw = MagicMock() raiden_service = MockRaidenService(None) raiden_service.config = dict(services=dict(monitoring_enabled=True)) transport.start( raiden_service, raiden_service.message_handler, None, ) pfs_room_name = make_room_alias(transport.network_id, PATH_FINDING_BROADCASTING_ROOM) pfs_room = transport._global_rooms.get(pfs_room_name) assert isinstance(pfs_room, Room) pfs_room.send_text = MagicMock(spec=pfs_room.send_text) raiden_service.transport = transport transport.log = MagicMock() balance_proof = make_balance_proof(signer=LocalSigner(HOP1_KEY), amount=1) channel_state = make_channel_state() channel_state.our_state.balance_proof = balance_proof channel_state.partner_state.balance_proof = balance_proof monkeypatch.setattr( raiden.transfer.views, 'get_channelstate_by_canonical_identifier', lambda *a, **kw: channel_state, ) update_path_finding_service_from_balance_proof( raiden=raiden_service, chain_state=None, new_balance_proof=balance_proof, ) gevent.idle() with gevent.Timeout(2): while pfs_room.send_text.call_count < 1: gevent.idle() assert pfs_room.send_text.call_count == 1 transport.stop() transport.get()