Skip to content

Commit

Permalink
include and forward encrypted secret in locked transfer message
Browse files Browse the repository at this point in the history
  • Loading branch information
Fernando Cezar Bernardelli committed Jul 13, 2021
1 parent ccd11e5 commit 2d13a7f
Show file tree
Hide file tree
Showing 19 changed files with 148 additions and 60 deletions.
48 changes: 18 additions & 30 deletions raiden/messages/encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,24 @@
SendSecretReveal,
SendUnlock,
)
from raiden.utils.typing import MYPY_ANNOTATION


_EVENT_MAP = {
SendLockExpired: LockExpired,
SendLockedTransfer: LockedTransfer,
SendProcessed: Processed,
SendSecretRequest: SecretRequest,
SendSecretReveal: RevealSecret,
SendUnlock: Unlock,
SendWithdrawConfirmation: WithdrawConfirmation,
SendWithdrawExpired: WithdrawExpired,
SendWithdrawRequest: WithdrawRequest,
}


def message_from_sendevent(send_event: SendMessageEvent) -> Message:
if type(send_event) == SendLockedTransfer:
assert isinstance(send_event, SendLockedTransfer), MYPY_ANNOTATION
return LockedTransfer.from_event(send_event)
elif type(send_event) == SendSecretReveal:
assert isinstance(send_event, SendSecretReveal), MYPY_ANNOTATION
return RevealSecret.from_event(send_event)
elif type(send_event) == SendUnlock:
assert isinstance(send_event, SendUnlock), MYPY_ANNOTATION
return Unlock.from_event(send_event)
elif type(send_event) == SendSecretRequest:
assert isinstance(send_event, SendSecretRequest), MYPY_ANNOTATION
return SecretRequest.from_event(send_event)
elif type(send_event) == SendLockExpired:
assert isinstance(send_event, SendLockExpired), MYPY_ANNOTATION
return LockExpired.from_event(send_event)
elif type(send_event) == SendWithdrawRequest:
assert isinstance(send_event, SendWithdrawRequest), MYPY_ANNOTATION
return WithdrawRequest.from_event(send_event)
elif type(send_event) == SendWithdrawConfirmation:
assert isinstance(send_event, SendWithdrawConfirmation), MYPY_ANNOTATION
return WithdrawConfirmation.from_event(send_event)
elif type(send_event) == SendWithdrawExpired:
assert isinstance(send_event, SendWithdrawExpired), MYPY_ANNOTATION
return WithdrawExpired.from_event(send_event)
elif type(send_event) == SendProcessed:
assert isinstance(send_event, SendProcessed), MYPY_ANNOTATION
return Processed.from_event(send_event)
else:
raise ValueError(f"Unknown event type {send_event}")
t_event = type(send_event)
EventClass = _EVENT_MAP.get(t_event)
if EventClass is None:
raise ValueError(f"Unknown event type {t_event}")
return EventClass.from_event(send_event) # type: ignore
25 changes: 14 additions & 11 deletions raiden/messages/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from raiden.messages.abstract import cached_property
from raiden.storage.serialization import serializer
from raiden.utils.typing import Address, AddressMetadata, Dict, List, Optional
from raiden.utils.typing import Address, AddressMetadata, Dict, EncryptedSecret, List, Optional
from raiden.utils.validation import MetadataValidation


Expand Down Expand Up @@ -79,6 +79,7 @@ class Metadata:
# inititally create the Metadata object as part of a message - this is the case when we are
# the initiator of a transfer.
original_data: Optional[Any] = None
encrypted_secret: Optional[EncryptedSecret] = None

class Meta:
"""
Expand All @@ -88,6 +89,7 @@ class Meta:
"""

unknown = EXCLUDE
serialize_missing = False

@cached_property
def hash(self) -> bytes:
Expand All @@ -101,18 +103,19 @@ def _post_load( # pylint: disable=no-self-use,unused-argument
return data

@post_dump(pass_many=True)
def _post_dump( # pylint: disable=no-self-use,unused-argument
def _post_dump( # pylint: disable=unused-argument
self, data: Dict[str, Any], many: bool
) -> Dict[str, Any]:
original_data = data.pop("original_data", {})
if original_data:
# We received the metadata (Mediator/Target) and read in the data
# for internal processing,
# so once we pass them to the next node just dump the data exactly as we received them
return original_data
# We initially created the Metadata (Initiator),
# so dump the known fields as per the Schema
return data
"""
`original_data` means we received the metadata (Mediator/Target) and read in the data
for internal processing, so once we pass them to the next node just dump the data exactly
as we received them.
Returning `data` means we initially created the Metadata (Initiator), so dump the known
fields as per the Schema
"""
dumped_data = data.pop("original_data", None) or data
return dumped_data

def __repr__(self) -> str:
return f"Metadata: routes: {[repr(route) for route in self.routes]}"
Expand Down
10 changes: 7 additions & 3 deletions raiden/messages/transfers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
SendSecretReveal,
SendUnlock,
)
from raiden.transfer.utils import hash_balance_data
from raiden.transfer.state import get_address_metadata
from raiden.transfer.utils import encrypt_secret, hash_balance_data
from raiden.utils.packing import pack_balance_proof
from raiden.utils.predicates import ishash
from raiden.utils.typing import (
Expand Down Expand Up @@ -379,7 +380,8 @@ def from_event(cls, event: Any) -> Any: # noqa: F811
RouteMetadata(route=r.route, address_metadata=r.address_to_metadata)
for r in transfer.route_states
]
metadata = Metadata(routes=routes, original_data=transfer.metadata)
target_metadata = get_address_metadata(transfer.target, transfer.route_states)
encrypted_secret = encrypt_secret(transfer.secret, target_metadata)

# pylint: disable=unexpected-keyword-arg
return cls(
Expand All @@ -398,7 +400,9 @@ def from_event(cls, event: Any) -> Any: # noqa: F811
target=transfer.target,
initiator=transfer.initiator,
signature=EMPTY_SIGNATURE,
metadata=metadata,
metadata=Metadata(
routes=routes, original_data=transfer.metadata, encrypted_secret=encrypted_secret
),
)

def _packed_data(self) -> bytes:
Expand Down
4 changes: 3 additions & 1 deletion raiden/storage/serialization/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
ChainID,
ChannelID,
EncodedData,
EncryptedSecret,
FeeAmount,
InitiatorAddress,
LockedAmount,
Expand Down Expand Up @@ -260,6 +261,7 @@ class Meta:
SecretHash: BytesField,
Signature: BytesField,
TransactionHash: BytesField,
EncryptedSecret: BytesField,
# Ints
BlockExpiration: IntegerToStringField,
BlockNumber: IntegerToStringField,
Expand Down Expand Up @@ -325,7 +327,7 @@ class Meta:

@post_dump(pass_original=True)
# pylint: disable=W0613,R0201
def _post_dump(self, data: Dict, original_data: Any, many: bool) -> Dict:
def __post_dump(self, data: Dict, original_data: Any, many: bool) -> Dict:
if self.opts.serialize_missing is False: # type: ignore
data = self.remove_missing(data)
if data:
Expand Down
4 changes: 4 additions & 0 deletions raiden/tests/unit/test_channelstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def test_channelstate_send_lockedtransfer():
lock_expiration = 10
lock_secret = keccak(b"test_end_state")
lock_secrethash = sha256(lock_secret).digest()
secret = None

lock = HashTimeLockState(lock_amount, lock_expiration, lock_secrethash)

Expand All @@ -397,6 +398,7 @@ def test_channelstate_send_lockedtransfer():
message_identifier,
payment_identifier,
lock_expiration,
secret,
lock_secrethash,
route_states=[
RouteState(
Expand Down Expand Up @@ -890,6 +892,7 @@ def test_channel_never_expires_lock_with_secret_onchain():
)

payment_identifier = 1
secret = None
message_identifier = random.randint(0, UINT64_MAX)
transfer_target = make_address()
transfer_initiator = make_address()
Expand All @@ -898,6 +901,7 @@ def test_channel_never_expires_lock_with_secret_onchain():
channel_state=channel_state,
initiator=transfer_initiator,
target=transfer_target,
secret=secret,
amount=lock_amount,
message_identifier=message_identifier,
payment_identifier=payment_identifier,
Expand Down
16 changes: 16 additions & 0 deletions raiden/tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
import pytest
import requests
import responses
from ecies import decrypt
from eth_keys.exceptions import BadSignature, ValidationError
from eth_utils import decode_hex, keccak, to_canonical_address

from raiden.api.v1.encoding import CapabilitiesSchema
from raiden.exceptions import InvalidSignature
from raiden.network.utils import get_average_http_response_time
from raiden.settings import CapabilitiesConfig
from raiden.transfer.utils import encrypt_secret
from raiden.utils.capabilities import capconfig_to_dict, capdict_to_config
from raiden.utils.keys import privatekey_to_publickey
from raiden.utils.signer import LocalSigner, Signer, recover
from raiden.utils.typing import UserID


def test_privatekey_to_publickey():
Expand All @@ -38,6 +41,19 @@ def test_signer_sign():
assert signer.sign(message) == signature


def test_encrypt_secret():
privkey = keccak(b"secret")
message = b"message"
signer: Signer = LocalSigner(privkey)
signature = signer.sign(message)

encrypted_secret = encrypt_secret(
message, {"user_id": UserID(message.decode()), "displayname": signature.hex()}
)

assert decrypt(privkey, encrypted_secret) == message


def test_recover():
account = to_canonical_address("0x38e959391dD8598aE80d5d6D114a7822A09d313A")
message = b"message"
Expand Down
2 changes: 2 additions & 0 deletions raiden/tests/utils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -1411,11 +1411,13 @@ def make_transfers_pair(
assert is_valid, msg

message_identifier = message_identifier_from_prng(pseudo_random_generator)
secret = None

lockedtransfer_event = channel.send_lockedtransfer(
channel_state=channels[payee_index],
initiator=UNIT_TRANSFER_INITIATOR,
target=UNIT_TRANSFER_TARGET,
secret=secret,
amount=amount,
message_identifier=message_identifier,
payment_identifier=UNIT_TRANSFER_IDENTIFIER,
Expand Down
4 changes: 4 additions & 0 deletions raiden/transfer/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,7 @@ def create_sendlockedtransfer(
message_identifier: MessageID,
payment_identifier: PaymentID,
expiration: BlockExpiration,
secret: Optional[Secret],
secrethash: SecretHash,
route_states: List[RouteState],
recipient_metadata: AddressMetadata = None,
Expand Down Expand Up @@ -1404,6 +1405,7 @@ def create_sendlockedtransfer(
payment_identifier=payment_identifier,
token=token,
balance_proof=balance_proof,
secret=secret,
lock=lock,
initiator=initiator,
target=target,
Expand Down Expand Up @@ -1504,6 +1506,7 @@ def send_lockedtransfer(
message_identifier: MessageID,
payment_identifier: PaymentID,
expiration: BlockExpiration,
secret: Optional[Secret],
secrethash: SecretHash,
route_states: List[RouteState],
recipient_metadata: AddressMetadata = None,
Expand All @@ -1517,6 +1520,7 @@ def send_lockedtransfer(
message_identifier=message_identifier,
payment_identifier=payment_identifier,
expiration=expiration,
secret=secret,
secrethash=secrethash,
route_states=route_states,
recipient_metadata=recipient_metadata,
Expand Down
1 change: 1 addition & 0 deletions raiden/transfer/mediated_transfer/initiator.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ def send_lockedtransfer(
message_identifier=message_identifier,
payment_identifier=transfer_description.payment_identifier,
expiration=lock_expiration,
secret=transfer_description.secret,
secrethash=transfer_description.secrethash,
route_states=route_states,
recipient_metadata=recipient_metadata,
Expand Down
1 change: 1 addition & 0 deletions raiden/transfer/mediated_transfer/mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ def forward_transfer_pair(
message_identifier=message_identifier,
payment_identifier=payer_transfer.payment_identifier,
expiration=payer_transfer.lock.expiration,
secret=payer_transfer.secret,
secrethash=payer_transfer.lock.secrethash,
route_states=payer_transfer.route_states,
recipient_metadata=recipient_metadata,
Expand Down
2 changes: 2 additions & 0 deletions raiden/transfer/mediated_transfer/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class LockedTransferUnsignedState(LockedTransferState):
balance_proof: BalanceProofUnsignedState
route_states: List[RouteState] = field(default_factory=list)
metadata: Optional[Dict[str, Any]] = field(repr=False, default=None)
secret: Optional[Secret] = field(repr=False, default=None)

def __post_init__(self) -> None:
super().__post_init__()
Expand Down Expand Up @@ -91,6 +92,7 @@ class LockedTransferSignedState(LockedTransferState):
route_states: List[RouteState]
balance_proof: BalanceProofSignedState = field(repr=False)
metadata: Optional[Dict[str, Any]] = field(repr=False, default=None)
secret: Optional[Secret] = field(repr=False, default=None)

def __post_init__(self) -> None:
typecheck(self.lock, HashTimeLockState)
Expand Down
26 changes: 26 additions & 0 deletions raiden/transfer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@
from random import Random
from typing import TYPE_CHECKING

from ecies import encrypt
from eth_hash.auto import keccak

from eth_utils import decode_hex

from raiden.constants import EMPTY_HASH, LOCKSROOT_OF_NO_LOCKS

from raiden.utils.signer import get_public_key
from raiden.utils.typing import (
AddressMetadata,
Any,
BalanceHash,
EncryptedSecret,
LockedAmount,
Locksroot,
Optional,
Secret,
SecretHash,
Signature,
TokenAmount,
Union,
)
Expand Down Expand Up @@ -53,3 +63,19 @@ def is_valid_secret_reveal(
transfer_secrethash: SecretHash,
) -> bool:
return state_change.secrethash == transfer_secrethash


def encrypt_secret(
secret: Secret, target_metadata: Optional[AddressMetadata]
) -> Optional[EncryptedSecret]:
if not target_metadata or not secret:
return None

message = target_metadata["user_id"].encode()
signature = Signature(decode_hex(target_metadata["displayname"]))

public_key = get_public_key(message, signature)
encrypted_secret = None
if public_key:
encrypted_secret = EncryptedSecret(encrypt(public_key.to_hex(), secret))
return encrypted_secret
Loading

0 comments on commit 2d13a7f

Please sign in to comment.