def fleet_of_highperf_mocked_ursulas(ursula_federated_test_config, request): try: quantity = request.param except AttributeError: quantity = 5000 # Bigass fleet by default; that's kinda the point. with GlobalLoggerSettings.pause_all_logging_while(): with mock_secret_source(): with mock_cert_storage, mock_cert_loading, mock_rest_app_creation, mock_cert_generation, mock_remember_node, mock_message_verification: _ursulas = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=quantity, know_each_other=False) all_ursulas = {u.checksum_address: u for u in _ursulas} for ursula in _ursulas: ursula.known_nodes._nodes = all_ursulas ursula.known_nodes.checksum = b"This is a fleet state checksum..".hex( ) return _ursulas
def test_worker_failure_non_resilience(): """ abort on error is True for this one """ # Control time clock = Clock() worktracker = WorkTrackerThatFailsHalfTheTime(clock, True) def advance_one_cycle(_): clock.advance(WorkTrackerThatFailsHalfTheTime.INTERVAL_CEIL) def checkworkstate(_): assert worktracker.workdone == 0 def start(): worktracker.start() d = threads.deferToThread(start) for i in range(10): d.addCallback(advance_one_cycle) d.addCallback(checkworkstate) critical = [] def critical_trapper(event): if event['log_level'] == LogLevel.critical: critical.append(event) globalLogPublisher.addObserver(critical_trapper) with GlobalLoggerSettings.pause_all_logging_while( ): # To suppress the traceback being displayed from the cricial error. globalLogPublisher.addObserver(critical_trapper) yield d globalLogPublisher.removeObserver(critical_trapper) assert len(critical) == 1 assert critical[0]['failure'].getErrorMessage( ) == "zomg something went wrong"
def fleet_of_highperf_mocked_ursulas(ursula_federated_test_config, request): mocks = ( mock_cert_storage, mock_cert_loading, mock_rest_app_creation, mock_cert_generation, mock_remember_node, mock_message_verification, ) try: quantity = request.param except AttributeError: quantity = 5000 # Bigass fleet by default; that's kinda the point. with GlobalLoggerSettings.pause_all_logging_while(): with contextlib.ExitStack() as stack: for mock in mocks: stack.enter_context(mock) _ursulas = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=quantity, know_each_other=False) all_ursulas = {u.checksum_address: u for u in _ursulas} for ursula in _ursulas: # FIXME #2588: FleetSensor should not own fully-functional Ursulas. # It only needs to see whatever public info we can normally get via REST. # Also sharing mutable Ursulas like that can lead to unpredictable results. ursula.known_nodes.current_state._nodes = all_ursulas ursula.known_nodes.current_state.checksum = b"This is a fleet state checksum..".hex( ) yield _ursulas for ursula in _ursulas: del MOCK_KNOWN_URSULAS_CACHE[ursula.rest_interface.port]
def fleet_of_highperf_mocked_ursulas(ursula_federated_test_config, request): # good_serials = _determine_good_serials(10000, 50000) try: quantity = request.param except AttributeError: quantity = 5000 # Bigass fleet by default; that's kinda the point. with GlobalLoggerSettings.pause_all_logging_while(): with mock_secret_source(): with mock_cert_storage, mock_cert_loading, mock_rest_app_creation, mock_cert_generation, mock_remember_node, mock_message_verification: _ursulas = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=quantity, know_each_other=False) all_ursulas = {u.checksum_address: u for u in _ursulas} for ursula in _ursulas: ursula.known_nodes.current_state._nodes = all_ursulas ursula.known_nodes.current_state.checksum = b"This is a fleet state checksum..".hex( ) yield _ursulas for ursula in _ursulas: del MOCK_KNOWN_URSULAS_CACHE[ursula.rest_interface.port]
def pytest_collection_modifyitems(config, items): # # Handle slow tests marker # if not config.getoption("--runslow"): # --runslow given in cli: do not skip slow tests skip_slow = pytest.mark.skip(reason="need --runslow option to run") for item in items: if "slow" in item.keywords: item.add_marker(skip_slow) # # Handle Log Level # log_level_name = config.getoption("--log-level", "info", skip=True) GlobalLoggerSettings.set_log_level(log_level_name) GlobalLoggerSettings.start_text_file_logging() GlobalLoggerSettings.start_json_file_logging()
import maya from nucypher.blockchain.eth.signers.base import Signer from nucypher.characters.lawful import Alice, Bob from nucypher.characters.lawful import Enrico as Enrico from nucypher.crypto.powers import SigningPower, DecryptingPower from nucypher.policy.payment import SubscriptionManagerPayment from nucypher.utilities.ethereum import connect_web3_provider from nucypher.utilities.logging import GlobalLoggerSettings ###################### # Boring setup stuff # ###################### LOG_LEVEL = 'info' GlobalLoggerSettings.set_log_level(log_level_name=LOG_LEVEL) GlobalLoggerSettings.start_console_logging() BOOK_PATH = Path('finnegans-wake-excerpt.txt') try: # Replace with ethereum RPC endpoint L1_PROVIDER = os.environ['DEMO_L1_PROVIDER_URI'] L2_PROVIDER = os.environ['DEMO_L2_PROVIDER_URI'] # Replace with wallet filepath. WALLET_FILEPATH = os.environ['DEMO_L2_WALLET_FILEPATH'] SIGNER_URI = f'keystore://{WALLET_FILEPATH}' # Replace with alice's ethereum address
def run_entire_cli_lifecycle(click_runner, random_policy_label, ursulas, custom_filepath, custom_filepath_2, registry_filepath=None, testerchain=None): """ This is an end to end integration test that runs each cli call in it's own process using only CLI character control entry points, and a mock side channel that runs in the control process """ federated = list(ursulas)[0].federated_only # Boring Setup Stuff alice_config_root = str(custom_filepath) bob_config_root = str(custom_filepath_2) envvars = {NUCYPHER_ENVVAR_KEYRING_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD} # A side channel exists - Perhaps a dApp side_channel = MockSideChannel() shutil.rmtree(str(custom_filepath), ignore_errors=True) shutil.rmtree(str(custom_filepath_2), ignore_errors=True) """ Scene 1: Alice Installs nucypher to a custom filepath and examines her configuration """ # Alice performs an installation for the first time alice_init_args = ('alice', 'init', '--network', TEMPORARY_DOMAIN, '--config-root', alice_config_root) if federated: alice_init_args += ('--federated-only', ) else: alice_init_args += ('--provider', TEST_PROVIDER_URI, '--pay-with', testerchain.alice_account, '--registry-filepath', str(registry_filepath)) alice_init_response = click_runner.invoke(nucypher_cli, alice_init_args, catch_exceptions=False, env=envvars) assert alice_init_response.exit_code == 0 # Prevent previous global logger settings set by aboce command from writing non-IPC messages to stdout GlobalLoggerSettings.stop_console_logging() # Alice uses her configuration file to run the character "view" command alice_configuration_file_location = os.path.join( alice_config_root, AliceConfiguration.generate_filename()) alice_view_args = ('alice', 'public-keys', '--json-ipc', '--config-file', alice_configuration_file_location) alice_view_result = click_runner.invoke( nucypher_cli, alice_view_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False, env=envvars) assert alice_view_result.exit_code == 0 try: alice_view_response = json.loads(alice_view_result.output) except JSONDecodeError: pytest.fail("Invalid JSON response from JSON-RPC Character process.") # Alice expresses her desire to participate in data sharing with nucypher # by saving her public key somewhere Bob and Enrico can find it. side_channel.save_alice_pubkey( alice_view_response['result']['alice_verifying_key']) """ Scene 2: Bob installs nucypher, examines his configuration and expresses his interest to participate in data retrieval by posting his public keys somewhere public (side-channel). """ bob_init_args = ('bob', 'init', '--network', TEMPORARY_DOMAIN, '--config-root', bob_config_root) if federated: bob_init_args += ('--federated-only', ) else: bob_init_args += ('--provider', TEST_PROVIDER_URI, '--registry-filepath', str(registry_filepath), '--checksum-address', testerchain.bob_account) bob_init_response = click_runner.invoke(nucypher_cli, bob_init_args, catch_exceptions=False, env=envvars) assert bob_init_response.exit_code == 0 # Alice uses her configuration file to run the character "view" command bob_configuration_file_location = os.path.join( bob_config_root, BobConfiguration.generate_filename()) bob_view_args = ( 'bob', 'public-keys', '--json-ipc', '--mock-networking', # TODO: It's absurd for this public-keys command to connect at all. 1710 '--lonely', # TODO: This needs to be implied by `public-keys`. '--config-file', bob_configuration_file_location) bob_view_result = click_runner.invoke(nucypher_cli, bob_view_args, catch_exceptions=False, env=envvars) assert bob_view_result.exit_code == 0 bob_view_response = json.loads(bob_view_result.output) # Bob interacts with the sidechannel bob_public_keys = MockSideChannel.BobPublicKeys( bob_view_response['result']['bob_encrypting_key'], bob_view_response['result']['bob_verifying_key']) side_channel.save_bob_public_keys(bob_public_keys) """ Scene 3: Alice derives a policy keypair, and saves its public key to a sidechannel. """ random_label = random_policy_label.decode() # Unicode string derive_args = ('alice', 'derive-policy-pubkey', '--mock-networking', '--json-ipc', '--config-file', alice_configuration_file_location, '--label', random_label) derive_response = click_runner.invoke(nucypher_cli, derive_args, catch_exceptions=False, env=envvars) assert derive_response.exit_code == 0 derive_response = json.loads(derive_response.output) assert derive_response['result']['label'] == random_label # Alice and the sidechannel: at Tinagre policy = MockSideChannel.PolicyAndLabel( encrypting_key=derive_response['result']['policy_encrypting_key'], label=derive_response['result']['label']) side_channel.save_policy(policy=policy) """ Scene 4: Enrico encrypts some data for some policy public key and saves it to a side channel. """ def enrico_encrypts(): # Fetch! policy = side_channel.fetch_policy() enrico_args = ('enrico', 'encrypt', '--json-ipc', '--policy-encrypting-key', policy.encrypting_key, '--message', PLAINTEXT) encrypt_result = click_runner.invoke(nucypher_cli, enrico_args, catch_exceptions=False, env=envvars) assert encrypt_result.exit_code == 0 encrypt_result = json.loads(encrypt_result.output) encrypted_message = encrypt_result['result'][ 'message_kit'] # type: str side_channel.save_message_kit(message_kit=encrypted_message) return encrypt_result def _alice_decrypts(encrypt_result): """ alice forgot what exactly she encrypted for bob. she decrypts it just to make sure. """ policy = side_channel.fetch_policy() alice_signing_key = side_channel.fetch_alice_pubkey() message_kit = encrypt_result['result']['message_kit'] decrypt_args = ( 'alice', 'decrypt', '--mock-networking', '--json-ipc', '--config-file', alice_configuration_file_location, '--message-kit', message_kit, '--label', policy.label, ) if federated: decrypt_args += ('--federated-only', ) decrypt_response_fail = click_runner.invoke(nucypher_cli, decrypt_args[0:7], catch_exceptions=False, env=envvars) assert decrypt_response_fail.exit_code == 2 decrypt_response = click_runner.invoke(nucypher_cli, decrypt_args, catch_exceptions=False, env=envvars) decrypt_result = json.loads(decrypt_response.output) for cleartext in decrypt_result['result']['cleartexts']: assert b64decode(cleartext.encode()).decode() == PLAINTEXT # replenish the side channel side_channel.save_policy(policy=policy) side_channel.save_alice_pubkey(alice_signing_key) return encrypt_result """ Scene 5: Alice grants access to Bob: We catch up with Alice later on, but before she has learned about existing Ursulas... """ if federated: teacher = list(ursulas)[0] else: teacher = list(ursulas)[1] teacher_uri = teacher.seed_node_metadata(as_teacher_uri=True) # Some Ursula is running somewhere def _run_teacher(_encrypt_result): # start_pytest_ursula_services(ursula=teacher) return teacher_uri def _grant(teacher_uri): # Alice fetched Bob's public keys from the side channel bob_keys = side_channel.fetch_bob_public_keys() bob_encrypting_key = bob_keys.bob_encrypting_key bob_verifying_key = bob_keys.bob_verifying_key grant_args = ('alice', 'grant', '--mock-networking', '--json-ipc', '--network', TEMPORARY_DOMAIN, '--teacher', teacher_uri, '--config-file', alice_configuration_file_location, '--m', 2, '--n', 3, '--expiration', (maya.now() + datetime.timedelta(days=3)).iso8601(), '--label', random_label, '--bob-encrypting-key', bob_encrypting_key, '--bob-verifying-key', bob_verifying_key) if federated: grant_args += ('--federated-only', ) else: grant_args += ('--provider', TEST_PROVIDER_URI, '--rate', Web3.toWei(9, 'gwei')) # TODO: Stop. grant_result = click_runner.invoke(nucypher_cli, grant_args, catch_exceptions=False, env=envvars) assert grant_result.exit_code == 0 grant_result = json.loads(grant_result.output) # TODO: Expand test to consider manual treasure map handing # # Alice puts the Treasure Map somewhere Bob can get it. # side_channel.save_treasure_map(treasure_map=grant_result['result']['treasure_map']) return grant_result def _bob_retrieves(_grant_result): """ Scene 6: Bob retrieves encrypted data from the side channel and uses nucypher to re-encrypt it """ # Bob interacts with a sidechannel ciphertext_message_kit = side_channel.fetch_message_kit() policy = side_channel.fetch_policy() policy_encrypting_key, label = policy alice_signing_key = side_channel.fetch_alice_pubkey() retrieve_args = ('bob', 'retrieve', '--mock-networking', '--json-ipc', '--teacher', teacher_uri, '--config-file', bob_configuration_file_location, '--message-kit', ciphertext_message_kit, '--label', label, '--policy-encrypting-key', policy_encrypting_key, '--alice-verifying-key', alice_signing_key) # TODO: Remove - Federated not used for retrieve any more # if federated: # retrieve_args += ('--federated-only',) retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) assert retrieve_response.exit_code == 0 retrieve_response = json.loads(retrieve_response.output) for cleartext in retrieve_response['result']['cleartexts']: assert b64decode(cleartext.encode()).decode() == PLAINTEXT return # Run the Callbacks d = threads.deferToThread(enrico_encrypts) # scene 4 d.addCallback(_alice_decrypts) # scene 5 (uncertainty) d.addCallback(_run_teacher) # scene 6 (preamble) d.addCallback(_grant) # scene 7 d.addCallback(_bob_retrieves) # scene 8 return d
import maya import msgpack import os import shutil import sys from nucypher.characters.lawful import Bob, Enrico, Ursula from nucypher.config.constants import TEMPORARY_DOMAIN from nucypher.crypto.keypairs import DecryptingKeypair, SigningKeypair from nucypher.crypto.kits import UmbralMessageKit from nucypher.crypto.powers import DecryptingPower, SigningPower from nucypher.crypto.umbral_adapter import PublicKey from nucypher.network.middleware import RestMiddleware from nucypher.utilities.logging import GlobalLoggerSettings GlobalLoggerSettings.start_console_logging() ###################### # Boring setup stuff # ###################### try: SEEDNODE_URI = sys.argv[1] except IndexError: SEEDNODE_URI = "localhost:11500" # TODO: path joins? TEMP_DOCTOR_DIR = "{}/doctor-files".format( os.path.dirname(os.path.abspath(__file__))) # Remove previous demo files and create new ones
def __init__(self, json_ipc: bool, verbose: bool, quiet: bool, no_logs: bool, console_logs: bool, file_logs: bool, sentry_logs: bool, log_level: bool, debug: bool): self.log = Logger(self.__class__.__name__) # Session Emitter for pre and post character control engagement. if verbose and quiet: raise click.BadOptionUsage( option_name="quiet", message="--verbose and --quiet are mutually exclusive " "and cannot be used at the same time.") if verbose: GroupGeneralConfig.verbosity = 2 elif quiet: GroupGeneralConfig.verbosity = 0 else: GroupGeneralConfig.verbosity = 1 if json_ipc: GlobalLoggerSettings._json_ipc = True # TODO #1754 emitter = JSONRPCStdoutEmitter( verbosity=GroupGeneralConfig.verbosity) else: emitter = StdoutEmitter(verbosity=GroupGeneralConfig.verbosity) self.emitter = emitter if verbose: self.emitter.message("Verbose mode is enabled", color='blue') # Logging if debug and no_logs: message = "--debug and --no-logs cannot be used at the same time." raise click.BadOptionUsage(option_name="no-logs", message=message) # Defaults if file_logs is None: file_logs = self.log_to_file if sentry_logs is None: sentry_logs = self.log_to_sentry if debug: console_logs = True file_logs = True sentry_logs = False log_level = 'debug' if no_logs: console_logs = False file_logs = False sentry_logs = False if json_ipc: console_logs = False GlobalLoggerSettings.set_log_level(log_level_name=log_level) if console_logs: GlobalLoggerSettings.start_console_logging() if file_logs: GlobalLoggerSettings.start_text_file_logging() GlobalLoggerSettings.start_json_file_logging() if sentry_logs: GlobalLoggerSettings.start_sentry_logging(self.sentry_endpoint) if json_ipc: GlobalLoggerSettings.stop_console_logging() # JSON-RPC Protection self.debug = debug self.json_ipc = json_ipc
def setup(): """Prepares the filesystem and logger for grant metrics collection""" shutil.rmtree(TEMP_ALICE_DIR, ignore_errors=True) GlobalLoggerSettings.start_console_logging() GlobalLoggerSettings.set_log_level('info')
def set_options(self, mock_networking, no_registry, json_ipc, verbose, quiet, no_logs, console_logs, file_logs, sentry_logs, log_level, debug): # Session Emitter for pre and post character control engagement. if verbose and quiet: raise click.BadOptionUsage( option_name="quiet", message="--verbose and --quiet are mutually exclusive " "and cannot be used at the same time.") if verbose: verbosity = 2 elif quiet: verbosity = 0 else: verbosity = 1 if json_ipc: emitter = JSONRPCStdoutEmitter(verbosity=verbosity) else: emitter = StdoutEmitter(verbosity=verbosity) self.attach_emitter(emitter) if verbose: self.emitter.message("Verbose mode is enabled", color='blue') # Logging if debug and no_logs: raise click.BadOptionUsage( option_name="no-logs", message="--debug and --no-logs cannot be used at the same time." ) # Defaults if file_logs is None: file_logs = self.log_to_file if sentry_logs is None: sentry_logs = self.log_to_sentry if debug: console_logs = True file_logs = True sentry_logs = False log_level = 'debug' if no_logs: console_logs = False file_logs = False sentry_logs = False GlobalLoggerSettings.set_log_level(log_level_name=log_level) if console_logs: GlobalLoggerSettings.start_console_logging() if file_logs: GlobalLoggerSettings.start_text_file_logging() GlobalLoggerSettings.start_json_file_logging() if sentry_logs: GlobalLoggerSettings.start_sentry_logging(self.sentry_endpoint) # CLI Session Configuration self.mock_networking = mock_networking self.no_registry = no_registry self.debug = debug self.json_ipc = json_ipc # Only used for testing outputs; # Redirects outputs to in-memory python containers. if mock_networking: self.emitter.message("WARNING: Mock networking is enabled") self.middleware = MockRestMiddleware() else: self.middleware = RestMiddleware()
def test_bob_retrieves_twice_via_cli(click_runner, capsule_side_channel, enacted_federated_policy, federated_ursulas, custom_filepath_2, federated_alice ): teacher = list(federated_ursulas)[0] first_message = capsule_side_channel.reset(plaintext_passthrough=True) three_message_kits = [capsule_side_channel(), capsule_side_channel(), capsule_side_channel()] bob_config_root = custom_filepath_2 bob_configuration_file_location = os.path.join(bob_config_root, BobConfiguration.generate_filename()) label = enacted_federated_policy.label # I already have a Bob. # Need to init so that the config file is made, even though we won't use this Bob. bob_init_args = ('bob', 'init', '--network', TEMPORARY_DOMAIN, '--config-root', bob_config_root, '--federated-only') envvars = {'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD} log.info("Init'ing a normal Bob; we'll substitute the Policy Bob in shortly.") bob_init_response = click_runner.invoke(nucypher_cli, bob_init_args, catch_exceptions=False, env=envvars) message_kit_bytes = bytes(three_message_kits[0]) message_kit_b64_bytes = b64encode(message_kit_bytes) UmbralMessageKit.from_bytes(message_kit_bytes) retrieve_args = ('bob', 'retrieve', '--mock-networking', '--json-ipc', '--teacher', teacher.seed_node_metadata(as_teacher_uri=True), '--config-file', bob_configuration_file_location, '--message-kit', message_kit_b64_bytes, '--label', label, '--policy-encrypting-key', bytes(federated_alice.get_policy_encrypting_key_from_label(label)).hex(), '--alice-verifying-key', bytes(federated_alice.public_keys(SigningPower)).hex() ) from nucypher.cli import actions def substitute_bob(*args, **kwargs): log.info("Substituting the Policy's Bob in CLI runtime.") this_fuckin_guy = enacted_federated_policy.bob somebody_else = Ursula.from_teacher_uri(teacher_uri=kwargs['teacher_uri'], min_stake=0, federated_only=True, network_middleware=this_fuckin_guy.network_middleware) this_fuckin_guy.remember_node(somebody_else) this_fuckin_guy.controller.emitter = JSONRPCStdoutEmitter() return this_fuckin_guy _old_make_character_function = actions.make_cli_character try: log.info("Patching make_cli_character with substitute_bob") actions.make_cli_character = substitute_bob # Once... with GlobalLoggerSettings.pause_all_logging_while(): retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) log.info(f"First retrieval response: {retrieve_response.output}") assert retrieve_response.exit_code == 0 retrieve_response = json.loads(retrieve_response.output) for cleartext in retrieve_response['result']['cleartexts']: assert cleartext.encode() == capsule_side_channel.plaintexts[1] # and again! with GlobalLoggerSettings.pause_all_logging_while(): retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) log.info(f"Second retrieval response: {retrieve_response.output}") assert retrieve_response.exit_code == 0 retrieve_response = json.loads(retrieve_response.output) for cleartext in retrieve_response['result']['cleartexts']: assert cleartext.encode() == capsule_side_channel.plaintexts[1] finally: log.info("un-patching make_cli_character") actions.make_cli_character = _old_make_character_function
from nucypher.utilities.logging import GlobalLoggerSettings from nucypher.utilities.sandbox.constants import TEMPORARY_DOMAIN ###################### # Boring setup stuff # ###################### # Execute the download script (download_finnegans_wake.sh) to retrieve the book BOOK_PATH = os.path.join('.', 'finnegans-wake.txt') # Change this value to to perform more or less total re-encryptions # in order to avoid processing the entire book's text. (it's long) NUMBER_OF_LINES_TO_REENCRYPT = 25 # Twisted Logger GlobalLoggerSettings.set_log_level(log_level_name='debug') GlobalLoggerSettings.start_console_logging() ####################################### # Finnegan's Wake on NuCypher Testnet # # (will fail with bad connection) ##### ####################################### # if your ursulas are NOT running on your current host, # run like this: python finnegans-wake-demo.py 172.28.1.3:11500 # otherwise the default will be fine. try: SEEDNODE_URI = sys.argv[1] except IndexError: SEEDNODE_URI = "localhost:11500"
def test_bob_retrieve_and_decrypt(click_runner, capsule_side_channel, enacted_federated_policy, federated_ursulas, custom_filepath_2: Path, federated_alice, federated_bob, mocker): teacher = list(federated_ursulas)[0] first_message, _ = capsule_side_channel.reset(plaintext_passthrough=True) message_kits_b64 = [ b64encode(bytes(message_kit)).decode() for message_kit in [ first_message, capsule_side_channel(), capsule_side_channel(), capsule_side_channel() ] ] bob_config_root = custom_filepath_2 bob_configuration_file_location = bob_config_root / BobConfiguration.generate_filename( ) # I already have a Bob. # Need to init so that the config file is made, even though we won't use this Bob. bob_init_args = ('bob', 'init', '--network', TEMPORARY_DOMAIN, '--config-root', str(bob_config_root.absolute()), '--federated-only') envvars = {'NUCYPHER_KEYSTORE_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD} log.info( "Init'ing a normal Bob; we'll substitute the Policy Bob in shortly.") bob_init_response = click_runner.invoke(nucypher_cli, bob_init_args, catch_exceptions=False, env=envvars) assert bob_init_response.exit_code == 0, bob_init_response.output teacher_uri = teacher.seed_node_metadata(as_teacher_uri=True) bob_config_file = str(bob_configuration_file_location.absolute()) policy_encrypting_key_hex = bytes( enacted_federated_policy.public_key).hex() alice_verifying_key_hex = bytes( federated_alice.public_keys(SigningPower)).hex() encrypted_treasure_map_b64 = b64encode( bytes(enacted_federated_policy.treasure_map)).decode() # Retrieve without --alice_verifying_key or --alice specified - tests override of schema definition for CLI retrieve_args = ( 'bob', 'retrieve-and-decrypt', '--mock-networking', '--json-ipc', '--teacher', teacher_uri, '--config-file', bob_config_file, '--message-kit', message_kits_b64[0], '--treasure-map', encrypted_treasure_map_b64, ) retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) assert retrieve_response.exit_code != 0, "no alice_verifying_key specified" assert "Pass either '--alice_verifying_key' or '--alice'; got neither" in retrieve_response.output, retrieve_response.output # Retrieve with both --alice_verifying_key and --alice specified - should not be allowed retrieve_args = ( 'bob', 'retrieve-and-decrypt', '--mock-networking', '--json-ipc', '--teacher', teacher_uri, '--config-file', bob_config_file, '--message-kit', message_kits_b64[0], '--alice-verifying-key', alice_verifying_key_hex, '--alice', 'rando-card-nickname', '--treasure-map', encrypted_treasure_map_b64, ) retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) assert retrieve_response.exit_code != 0, "both alice_verifying_key and alice can't be specified" assert "Pass either '--alice_verifying_key' or '--alice'; got both" in retrieve_response.output, retrieve_response.output # # Perform actual retrieve and decrypts # def substitute_bob(*args, **kwargs): log.info("Substituting the Bob used in the CLI runtime.") this_fuckin_guy = federated_bob this_fuckin_guy.controller.emitter = JSONRPCStdoutEmitter() return this_fuckin_guy with mocker.patch.object(BobCharacterOptions, 'create_character', side_effect=substitute_bob): # # Retrieve one message kit # retrieve_args = ( 'bob', 'retrieve-and-decrypt', '--mock-networking', '--json-ipc', '--teacher', teacher_uri, '--config-file', bob_config_file, '--message-kit', message_kits_b64[0], '--alice-verifying-key', alice_verifying_key_hex, '--treasure-map', encrypted_treasure_map_b64, ) with GlobalLoggerSettings.pause_all_logging_while(): retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) log.info(f"Retrieval response: {retrieve_response.output}") assert retrieve_response.exit_code == 0, retrieve_response.output retrieve_response = json.loads(retrieve_response.output) cleartexts = retrieve_response['result']['cleartexts'] assert len(cleartexts) == 1 assert cleartexts[0].encode() == capsule_side_channel.plaintexts[0] # # Retrieve and decrypt multiple message kits # retrieve_args = ( 'bob', 'retrieve-and-decrypt', '--mock-networking', '--json-ipc', '--teacher', teacher_uri, '--config-file', bob_config_file, # use multiple message kits '--message-kit', message_kits_b64[0], '--message-kit', message_kits_b64[1], '--message-kit', message_kits_b64[2], '--message-kit', message_kits_b64[3], '--alice-verifying-key', alice_verifying_key_hex, '--treasure-map', encrypted_treasure_map_b64) with GlobalLoggerSettings.pause_all_logging_while(): retrieve_response = click_runner.invoke(nucypher_cli, retrieve_args, catch_exceptions=False, env=envvars) log.info(f"Retrieval response: {retrieve_response.output}") assert retrieve_response.exit_code == 0, retrieve_response.output retrieve_response = json.loads(retrieve_response.output) cleartexts = retrieve_response['result']['cleartexts'] assert len(cleartexts) == len(message_kits_b64) for index, cleartext in enumerate(cleartexts): assert cleartext.encode() == capsule_side_channel.plaintexts[index]