Support key material importing by Alice, Bob, ans Ursula via CLI. Emit a warning when doing so.

pull/2742/head
Kieran R. Prasch 2021-07-13 15:30:09 -07:00
parent 8e4c7c1594
commit a9022379d1
5 changed files with 32 additions and 14 deletions

View File

@ -55,16 +55,15 @@ from nucypher.cli.options import (
option_teacher_uri,
option_threshold,
option_lonely,
option_max_gas_price
option_max_gas_price,
option_key_material
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.painting.policies import paint_single_card
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS
from nucypher.cli.utils import make_cli_character, setup_emitter
from nucypher.config.characters import AliceConfiguration
from nucypher.config.constants import (
TEMPORARY_DOMAIN,
)
from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.crypto.keystore import Keystore
from nucypher.network.middleware import RestMiddleware
from nucypher.policy.identity import Card
@ -185,7 +184,7 @@ class AliceFullConfigOptions:
self.shares = shares
self.payment_periods = payment_periods
def generate_config(self, emitter: StdoutEmitter, config_root: Path) -> AliceConfiguration:
def generate_config(self, emitter: StdoutEmitter, config_root: Path, key_material: str) -> AliceConfiguration:
opts = self.config_options
@ -207,6 +206,7 @@ class AliceFullConfigOptions:
return AliceConfiguration.generate(
password=get_nucypher_password(emitter=emitter, confirm=True),
key_material=bytes.fromhex(key_material) if key_material else None,
config_root=config_root,
checksum_address=pay_with,
domain=opts.domain,
@ -295,12 +295,15 @@ def alice():
@group_full_config_options
@option_config_root
@group_general_config
def init(general_config, full_config_options, config_root):
@option_key_material
def init(general_config, full_config_options, config_root, key_material):
"""Create a brand new persistent Alice."""
emitter = setup_emitter(general_config)
if not config_root:
config_root = general_config.config_root
new_alice_config = full_config_options.generate_config(emitter, config_root)
new_alice_config = full_config_options.generate_config(emitter=emitter,
config_root=config_root,
key_material=key_material)
filepath = new_alice_config.to_configuration_file()
paint_new_installation_help(emitter, new_configuration=new_alice_config, filepath=filepath)

View File

@ -14,6 +14,8 @@
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
from base64 import b64decode
from pathlib import Path
@ -50,7 +52,8 @@ from nucypher.cli.options import (
option_signer_uri,
option_teacher_uri,
option_lonely,
option_max_gas_price
option_max_gas_price,
option_key_material
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.painting.policies import paint_single_card
@ -133,7 +136,7 @@ class BobConfigOptions:
handle_missing_configuration_file(character_config_class=BobConfiguration,
config_file=config_file)
def generate_config(self, emitter: StdoutEmitter, config_root: Path) -> BobConfiguration:
def generate_config(self, emitter: StdoutEmitter, config_root: Path, key_material: str) -> BobConfiguration:
checksum_address = self.checksum_address
if not checksum_address and not self.federated_only:
@ -143,6 +146,7 @@ class BobConfigOptions:
return BobConfiguration.generate(
password=get_nucypher_password(emitter=emitter, confirm=True),
key_material=bytes.fromhex(key_material) if key_material else None,
config_root=config_root,
checksum_address=checksum_address,
domain=self.domain,
@ -227,12 +231,15 @@ def bob():
@option_federated_only
@option_config_root
@group_general_config
def init(general_config, config_options, config_root):
@option_key_material
def init(general_config, config_options, config_root, key_material):
"""Create a brand new persistent Bob."""
emitter = setup_emitter(general_config)
if not config_root:
config_root = general_config.config_root
new_bob_config = config_options.generate_config(emitter, config_root)
new_bob_config = config_options.generate_config(emitter=emitter,
config_root=config_root,
key_material=key_material)
filepath = new_bob_config.to_configuration_file()
paint_new_installation_help(emitter, new_configuration=new_bob_config, filepath=filepath)

View File

@ -53,7 +53,8 @@ from nucypher.cli.options import (
option_signer_uri,
option_teacher_uri,
option_lonely,
option_max_gas_price
option_max_gas_price,
option_key_material
)
from nucypher.cli.painting.help import paint_new_installation_help
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, NETWORK_PORT, WORKER_IP
@ -296,7 +297,7 @@ def ursula():
@option_force
@option_config_root
@group_general_config
@click.option('--key-material', help="An custom pre-secured secret hex blob to use for key derivations", type=click.STRING)
@option_key_material
def init(general_config, config_options, force, config_root, key_material):
"""Create a new Ursula node configuration."""
emitter = setup_emitter(general_config, config_options.worker_address)
@ -307,7 +308,10 @@ def init(general_config, config_options, force, config_root, key_material):
raise click.BadOptionUsage('--provider', message="--provider is required to initialize a new ursula.")
if not config_options.federated_only and not config_options.domain:
config_options.domain = select_network(emitter)
ursula_config = config_options.generate_config(emitter, config_root, force, key_material)
ursula_config = config_options.generate_config(emitter=emitter,
config_root=config_root,
force=force,
key_material=key_material)
filepath = ursula_config.to_configuration_file()
paint_new_installation_help(emitter, new_configuration=ursula_config, filepath=filepath)

View File

@ -49,6 +49,7 @@ option_federated_only = click.option('--federated-only/--decentralized', '-F', h
option_force = click.option('--force', help="Don't ask for confirmation", is_flag=True)
option_gas_price = click.option('--gas-price', help="Set a static gas price (in GWEI)", type=GWEI)
option_gas_strategy = click.option('--gas-strategy', help="Operate with a specified gas price strategy", type=click.STRING) # TODO: GAS_STRATEGY_CHOICES
option_key_material = click.option('--key-material', help="An custom pre-secured secret hex blob to use for key derivations", type=click.STRING)
option_max_gas_price = click.option('--max-gas-price', help="Maximum acceptable gas price (in GWEI)", type=GWEI)
option_hw_wallet = click.option('--hw-wallet/--no-hw-wallet')
option_light = click.option('--light', help="Indicate that node is light", is_flag=True, default=None)

View File

@ -327,6 +327,9 @@ class Keystore:
This method of keystore creation does not generate a mnemonic phrase - it is assumed
that the provided blob is recoverable and secure.
"""
emitter = StdoutEmitter()
emitter.message(f'WARNING: Key importing assumes that you have already secured your secret '
f'and can recover it. No mnemonic will be generated.\n', color='yellow')
path = Keystore.__save(secret=key_material, password=password, keystore_dir=keystore_dir)
keystore = cls(keystore_path=path)
return keystore