Enrico uses a web3 wallet to encrypt-then-sign.

pull/3213/head
Kieran Prasch 2023-09-11 19:05:12 +02:00
parent 243448d015
commit 783f2dcd76
8 changed files with 63 additions and 21 deletions

View File

@ -81,6 +81,7 @@ from nucypher.blockchain.eth.registry import (
BaseContractRegistry, BaseContractRegistry,
InMemoryContractRegistry, InMemoryContractRegistry,
) )
from nucypher.blockchain.eth.signers import Signer
from nucypher.blockchain.eth.signers.software import Web3Signer from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.characters.banners import ( from nucypher.characters.banners import (
ALICE_BANNER, ALICE_BANNER,
@ -1457,9 +1458,14 @@ class Enrico:
banner = ENRICO_BANNER banner = ENRICO_BANNER
def __init__(self, encrypting_key: Union[PublicKey, DkgPublicKey]): def __init__(
self,
encrypting_key: Union[PublicKey, DkgPublicKey],
signer: Optional[Signer] = None,
):
self.signer = signer
self.signing_power = SigningPower() self.signing_power = SigningPower()
self._policy_pubkey = encrypting_key self._encrypting_key = encrypting_key
self.log = Logger(f"{self.__class__.__name__}-{encrypting_key}") self.log = Logger(f"{self.__class__.__name__}-{encrypting_key}")
self.log.info(self.banner.format(encrypting_key)) self.log.info(self.banner.format(encrypting_key))
@ -1477,20 +1483,27 @@ class Enrico:
return message_kit return message_kit
def encrypt_for_dkg( def encrypt_for_dkg(
self, plaintext: bytes, conditions: Lingo self,
plaintext: bytes,
conditions: Lingo,
) -> ThresholdMessageKit: ) -> ThresholdMessageKit:
if not self.signer:
raise TypeError("This Enrico doesn't have a signer.")
validate_condition_lingo(conditions) validate_condition_lingo(conditions)
conditions_json = json.dumps(conditions) conditions_json = json.dumps(conditions)
access_conditions = Conditions(conditions_json) access_conditions = Conditions(conditions_json)
# encrypt for DKG
ciphertext, auth_data = encrypt_for_dkg( ciphertext, auth_data = encrypt_for_dkg(
plaintext, self.policy_pubkey, access_conditions plaintext, self.policy_pubkey, access_conditions
) )
# authentication for AllowLogic # authentication message for TACo
# TODO Replace with `Signer` to be passed as parameter
header_hash = keccak_digest(bytes(ciphertext.header)) header_hash = keccak_digest(bytes(ciphertext.header))
authorization = self.signing_power.keypair.sign(header_hash).to_be_bytes() authorization = self.signer.sign_message(
message=header_hash, account=self.signer.accounts[0]
)
return ThresholdMessageKit( return ThresholdMessageKit(
ciphertext=ciphertext, ciphertext=ciphertext,
@ -1509,8 +1522,8 @@ class Enrico:
@property @property
def policy_pubkey(self): def policy_pubkey(self):
if not self._policy_pubkey: if not self._encrypting_key:
raise TypeError( raise TypeError(
"This Enrico doesn't know which policy encrypting key he used. Oh well." "This Enrico doesn't know which policy encrypting key he used. Oh well."
) )
return self._policy_pubkey return self._encrypting_key

View File

@ -2,6 +2,7 @@ import pytest
import pytest_twisted import pytest_twisted
from twisted.internet.threads import deferToThread from twisted.internet.threads import deferToThread
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.trackers.dkg import EventScannerTask from nucypher.blockchain.eth.trackers.dkg import EventScannerTask
from nucypher.characters.lawful import Enrico from nucypher.characters.lawful import Enrico
from nucypher.policy.conditions.lingo import ConditionLingo from nucypher.policy.conditions.lingo import ConditionLingo
@ -134,9 +135,12 @@ def test_ursula_ritualist(
# prepare message and conditions # prepare message and conditions
plaintext = PLAINTEXT.encode() plaintext = PLAINTEXT.encode()
# create Enrico
signer = Web3Signer(client=testerchain.client)
enrico = Enrico(encrypting_key=encrypting_key, signer=signer)
# encrypt # encrypt
# print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}') print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}")
enrico = Enrico(encrypting_key=encrypting_key)
threshold_message_kit = enrico.encrypt_for_dkg( threshold_message_kit = enrico.encrypt_for_dkg(
plaintext=plaintext, conditions=CONDITIONS plaintext=plaintext, conditions=CONDITIONS
) )

View File

@ -60,6 +60,7 @@ def test_character_transacting_power_signing(testerchain, test_registry):
restored_dict = restored_transaction.as_dict() restored_dict = restored_transaction.as_dict()
assert to_checksum_address(restored_dict['to']) == transaction_dict['to'] assert to_checksum_address(restored_dict['to']) == transaction_dict['to']
def test_transacting_power_sign_message(testerchain): def test_transacting_power_sign_message(testerchain):
# Manually create a TransactingPower # Manually create a TransactingPower

View File

@ -10,6 +10,7 @@ from twisted.internet.threads import deferToThread
from web3.datastructures import AttributeDict from web3.datastructures import AttributeDict
from nucypher.blockchain.eth.agents import CoordinatorAgent from nucypher.blockchain.eth.agents import CoordinatorAgent
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.characters.lawful import Enrico, Ursula from nucypher.characters.lawful import Enrico, Ursula
from nucypher.policy.conditions.lingo import ConditionLingo from nucypher.policy.conditions.lingo import ConditionLingo
from tests.constants import TESTERCHAIN_CHAIN_ID from tests.constants import TESTERCHAIN_CHAIN_ID
@ -116,6 +117,7 @@ def execute_round_2(ritual_id: int, cohort: List[Ursula]):
ursula.ritual_tracker._handle_ritual_event(event, get_block_when=lambda x: event) ursula.ritual_tracker._handle_ritual_event(event, get_block_when=lambda x: event)
@pytest.mark.usefixtures("mock_sign_message")
@pytest.mark.parametrize('dkg_size, ritual_id, variant', PARAMS) @pytest.mark.parametrize('dkg_size, ritual_id, variant', PARAMS)
@pytest_twisted.inlineCallbacks() @pytest_twisted.inlineCallbacks()
def test_ursula_ritualist( def test_ursula_ritualist(
@ -195,9 +197,12 @@ def test_ursula_ritualist(
# prepare message and conditions # prepare message and conditions
plaintext = PLAINTEXT.encode() plaintext = PLAINTEXT.encode()
# create Enrico
signer = Web3Signer(client=testerchain.client)
enrico = Enrico(encrypting_key=encrypting_key, signer=signer)
# encrypt # encrypt
# print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}') print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}")
enrico = Enrico(encrypting_key=encrypting_key)
threshold_message_kit = enrico.encrypt_for_dkg( threshold_message_kit = enrico.encrypt_for_dkg(
plaintext=plaintext, conditions=CONDITIONS plaintext=plaintext, conditions=CONDITIONS
) )

View File

@ -1,5 +1,6 @@
import pytest import pytest
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.characters.chaotic import ( from nucypher.characters.chaotic import (
NiceGuyEddie, NiceGuyEddie,
ThisBobAlwaysDecrypts, ThisBobAlwaysDecrypts,
@ -14,10 +15,11 @@ from tests.constants import (
) )
def _attempt_decryption(BobClass, plaintext): def _attempt_decryption(BobClass, plaintext, testerchain):
trinket = 80 # Doens't matter. trinket = 80 # Doens't matter.
enrico = NiceGuyEddie(encrypting_key=trinket) signer = Web3Signer(client=testerchain.client)
enrico = NiceGuyEddie(encrypting_key=trinket, signer=signer)
bob = BobClass( bob = BobClass(
registry=MOCK_REGISTRY_FILEPATH, registry=MOCK_REGISTRY_FILEPATH,
domain="lynx", domain="lynx",
@ -46,13 +48,15 @@ def _attempt_decryption(BobClass, plaintext):
return decrypted_cleartext return decrypted_cleartext
def test_user_controls_success(): @pytest.mark.usefixtures("mock_sign_message")
def test_user_controls_success(testerchain):
plaintext = b"ever thus to deadbeats" plaintext = b"ever thus to deadbeats"
result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext) result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext, testerchain)
assert bytes(result) == bytes(plaintext) assert bytes(result) == bytes(plaintext)
def test_user_controls_failure(): @pytest.mark.usefixtures("mock_sign_message")
def test_user_controls_failure(testerchain):
plaintext = b"ever thus to deadbeats" plaintext = b"ever thus to deadbeats"
with pytest.raises(Ursula.NotEnoughUrsulas): with pytest.raises(Ursula.NotEnoughUrsulas):
_ = _attempt_decryption(ThisBobAlwaysFails, plaintext) _ = _attempt_decryption(ThisBobAlwaysFails, plaintext, testerchain)

View File

@ -151,6 +151,7 @@ def test_tls_hosting_certificate_remains_the_same(temp_dir_path, mocker):
recreated_ursula.disenchant() recreated_ursula.disenchant()
@pytest.mark.usefixtures("mock_sign_message")
def test_ritualist(temp_dir_path, testerchain, dkg_public_key): def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
keystore = Keystore.generate( keystore = Keystore.generate(
password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path
@ -186,11 +187,15 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
}, },
} }
# create enrico
signer = Web3Signer(client=testerchain.client)
enrico = Enrico(encrypting_key=dkg_public_key, signer=signer)
# encrypt # encrypt
enrico = Enrico(encrypting_key=dkg_public_key)
threshold_message_kit = enrico.encrypt_for_dkg( threshold_message_kit = enrico.encrypt_for_dkg(
plaintext=plaintext, conditions=CONDITIONS plaintext=plaintext, conditions=CONDITIONS
) )
decryption_request = ThresholdDecryptionRequest( decryption_request = ThresholdDecryptionRequest(
ritual_id=ritual_id, ritual_id=ritual_id,
variant=FerveoVariant.Simple, variant=FerveoVariant.Simple,

View File

@ -1,3 +1,4 @@
import os
from pathlib import Path from pathlib import Path
from typing import Iterable, Optional from typing import Iterable, Optional
@ -20,6 +21,7 @@ from nucypher.blockchain.eth.interfaces import (
from nucypher.blockchain.eth.networks import NetworksInventory from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.signers import KeystoreSigner from nucypher.blockchain.eth.signers import KeystoreSigner
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.characters.lawful import Ursula from nucypher.characters.lawful import Ursula
from nucypher.cli.types import ChecksumAddress from nucypher.cli.types import ChecksumAddress
from nucypher.config.characters import UrsulaConfiguration from nucypher.config.characters import UrsulaConfiguration
@ -59,6 +61,14 @@ def mock_sample_reservoir(testerchain, mock_contract_agency):
mock_agent.get_staking_provider_reservoir = mock_reservoir mock_agent.get_staking_provider_reservoir = mock_reservoir
@pytest.fixture(scope="function")
def mock_sign_message(mocker):
mocked_sign_message = mocker.patch.object(
Web3Signer, "sign_message", return_value=os.urandom(32)
)
return mocked_sign_message
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def mock_application_agent( def mock_application_agent(
testerchain, application_economics, mock_contract_agency, mocker testerchain, application_economics, mock_contract_agency, mocker

View File

@ -7,7 +7,7 @@ from typing import Union
from hexbytes import HexBytes from hexbytes import HexBytes
from nucypher.blockchain.eth.clients import EthereumClient from nucypher.blockchain.eth.clients import EthereumTesterClient
from nucypher.blockchain.eth.networks import NetworksInventory from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.blockchain.eth.registry import ( from nucypher.blockchain.eth.registry import (
BaseContractRegistry, BaseContractRegistry,
@ -82,7 +82,7 @@ class MockBlockchain(TesterBlockchain):
return self.FAKE_RECEIPT return self.FAKE_RECEIPT
class MockEthereumClient(EthereumClient): class MockEthereumClient(EthereumTesterClient):
def __init__(self, w3): def __init__(self, w3):
super().__init__(w3=w3, node_technology=None, version=None, platform=None, backend=None) super().__init__(w3=w3, node_technology=None, version=None, platform=None, backend=None)