mirror of https://github.com/nucypher/nucypher.git
Notify and interactively confirm mnemonic generation.
parent
efee48aaba
commit
b92d04ab00
|
@ -19,7 +19,6 @@
|
|||
import json
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from abc import ABC, abstractmethod
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
@ -31,7 +30,6 @@ from constant_sorrow.constants import (
|
|||
UNINITIALIZED_CONFIGURATION,
|
||||
NO_KEYSTORE_ATTACHED,
|
||||
NO_BLOCKCHAIN_CONNECTION,
|
||||
FEDERATED_ADDRESS,
|
||||
DEVELOPMENT_CONFIGURATION,
|
||||
LIVE_CONFIGURATION
|
||||
)
|
||||
|
@ -57,6 +55,7 @@ from nucypher.crypto.powers import CryptoPower, CryptoPowerUp
|
|||
from nucypher.crypto.umbral_adapter import Signature
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.utilities.logging import Logger
|
||||
from umbral.signing import Signature
|
||||
|
||||
|
||||
class BaseConfiguration(ABC):
|
||||
|
@ -758,7 +757,7 @@ class CharacterConfiguration(BaseConfiguration):
|
|||
power_ups.append(power_up)
|
||||
return power_ups
|
||||
|
||||
def initialize(self, password: str) -> str:
|
||||
def initialize(self, password: str, force: bool = False) -> str:
|
||||
"""Initialize a new configuration and write installation files to disk."""
|
||||
|
||||
# Development
|
||||
|
@ -769,7 +768,7 @@ class CharacterConfiguration(BaseConfiguration):
|
|||
# Persistent
|
||||
else:
|
||||
self._ensure_config_root_exists()
|
||||
self.write_keystore(password=password)
|
||||
self.write_keystore(password=password, force=force)
|
||||
|
||||
self._cache_runtime_filepaths()
|
||||
self.node_storage.initialize()
|
||||
|
@ -783,8 +782,8 @@ class CharacterConfiguration(BaseConfiguration):
|
|||
self.log.debug(message)
|
||||
return self.config_root
|
||||
|
||||
def write_keystore(self, password: str) -> Keystore:
|
||||
self.__keystore = Keystore.generate(password=password, keystore_dir=self.keystore_dir)
|
||||
def write_keystore(self, password: str, force: bool = False) -> Keystore:
|
||||
self.__keystore = Keystore.generate(password=password, keystore_dir=self.keystore_dir, force=force)
|
||||
return self.keystore
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -20,13 +20,13 @@ import json
|
|||
import os
|
||||
import stat
|
||||
import string
|
||||
import tempfile
|
||||
from json import JSONDecodeError
|
||||
from os.path import abspath
|
||||
from pathlib import Path
|
||||
from secrets import token_bytes
|
||||
from typing import Callable, ClassVar, Dict, List, Union, Optional, Tuple
|
||||
|
||||
import click
|
||||
import time
|
||||
from constant_sorrow.constants import KEYSTORE_LOCKED
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
@ -36,6 +36,7 @@ from mnemonic.mnemonic import Mnemonic
|
|||
from nacl.exceptions import CryptoError
|
||||
from nacl.secret import SecretBox
|
||||
|
||||
from nucypher.characters.control.emitters import StdoutEmitter
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from nucypher.crypto.constants import BLAKE2B
|
||||
from nucypher.crypto.keypairs import HostingKeypair
|
||||
|
@ -340,6 +341,7 @@ class Keystore:
|
|||
|
||||
@classmethod
|
||||
def restore(cls, words: str, password: str, keystore_dir: Optional[Path] = None) -> 'Keystore':
|
||||
"""Restore a keystore from seed words"""
|
||||
__mnemonic = Mnemonic(_MNEMONIC_LANGUAGE)
|
||||
__secret = __mnemonic.to_entropy(words)
|
||||
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
||||
|
@ -347,14 +349,40 @@ class Keystore:
|
|||
return keystore
|
||||
|
||||
@classmethod
|
||||
def generate(cls, password: str, keystore_dir: Optional[Path] = None) -> 'Keystore':
|
||||
def generate(cls, password: str, keystore_dir: Optional[Path] = None, force: bool = False) -> 'Keystore':
|
||||
"""Generate a new nucypher keystore for use with characters"""
|
||||
mnemonic = Mnemonic(_MNEMONIC_LANGUAGE)
|
||||
__words = mnemonic.generate(strength=_ENTROPY_BITS)
|
||||
cls._confirm_generate(__words, force=force)
|
||||
__secret = mnemonic.to_entropy(__words)
|
||||
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
||||
keystore = cls(keystore_path=path)
|
||||
return keystore
|
||||
|
||||
@staticmethod
|
||||
def _confirm_generate(__words: str, force: bool) -> None:
|
||||
"""
|
||||
Inform the caller of new keystore seed words generation the console
|
||||
and optionally perform interactive confirmation
|
||||
"""
|
||||
|
||||
# notification
|
||||
emitter = StdoutEmitter()
|
||||
emitter.message(f'Backup your seed words, you will not be able to view them again.\n')
|
||||
emitter.message(f'{__words}\n', color='cyan')
|
||||
|
||||
# confirmation
|
||||
if not force:
|
||||
if not click.confirm("Have you backed up your seed phrase?"):
|
||||
emitter.message('Keystore generation aborted.', color='red')
|
||||
raise click.Abort()
|
||||
click.clear()
|
||||
|
||||
__response = click.prompt("Confirm seed words")
|
||||
if __response != __words:
|
||||
raise ValueError('Incorrect seed word confirmation. No keystore has been created, try again.')
|
||||
click.clear()
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
return self.__id
|
||||
|
|
|
@ -1051,3 +1051,9 @@ def stakeholder_configuration_file_location(custom_filepath):
|
|||
def mock_teacher_nodes(mocker):
|
||||
mock_nodes = tuple(u.rest_url() for u in MOCK_KNOWN_URSULAS_CACHE.values())[0:2]
|
||||
mocker.patch.dict(TEACHER_NODES, {TEMPORARY_DOMAIN: mock_nodes}, clear=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def disable_interactive_keystore_generation(mocker):
|
||||
# Do not notify or confirm mnemonic seed words during tests normally
|
||||
mocker.patch.object(Keystore, '_confirm_generate')
|
||||
|
|
Loading…
Reference in New Issue