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 json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import tempfile
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -31,7 +30,6 @@ from constant_sorrow.constants import (
|
||||||
UNINITIALIZED_CONFIGURATION,
|
UNINITIALIZED_CONFIGURATION,
|
||||||
NO_KEYSTORE_ATTACHED,
|
NO_KEYSTORE_ATTACHED,
|
||||||
NO_BLOCKCHAIN_CONNECTION,
|
NO_BLOCKCHAIN_CONNECTION,
|
||||||
FEDERATED_ADDRESS,
|
|
||||||
DEVELOPMENT_CONFIGURATION,
|
DEVELOPMENT_CONFIGURATION,
|
||||||
LIVE_CONFIGURATION
|
LIVE_CONFIGURATION
|
||||||
)
|
)
|
||||||
|
@ -57,6 +55,7 @@ from nucypher.crypto.powers import CryptoPower, CryptoPowerUp
|
||||||
from nucypher.crypto.umbral_adapter import Signature
|
from nucypher.crypto.umbral_adapter import Signature
|
||||||
from nucypher.network.middleware import RestMiddleware
|
from nucypher.network.middleware import RestMiddleware
|
||||||
from nucypher.utilities.logging import Logger
|
from nucypher.utilities.logging import Logger
|
||||||
|
from umbral.signing import Signature
|
||||||
|
|
||||||
|
|
||||||
class BaseConfiguration(ABC):
|
class BaseConfiguration(ABC):
|
||||||
|
@ -758,7 +757,7 @@ class CharacterConfiguration(BaseConfiguration):
|
||||||
power_ups.append(power_up)
|
power_ups.append(power_up)
|
||||||
return power_ups
|
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."""
|
"""Initialize a new configuration and write installation files to disk."""
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
@ -769,7 +768,7 @@ class CharacterConfiguration(BaseConfiguration):
|
||||||
# Persistent
|
# Persistent
|
||||||
else:
|
else:
|
||||||
self._ensure_config_root_exists()
|
self._ensure_config_root_exists()
|
||||||
self.write_keystore(password=password)
|
self.write_keystore(password=password, force=force)
|
||||||
|
|
||||||
self._cache_runtime_filepaths()
|
self._cache_runtime_filepaths()
|
||||||
self.node_storage.initialize()
|
self.node_storage.initialize()
|
||||||
|
@ -783,8 +782,8 @@ class CharacterConfiguration(BaseConfiguration):
|
||||||
self.log.debug(message)
|
self.log.debug(message)
|
||||||
return self.config_root
|
return self.config_root
|
||||||
|
|
||||||
def write_keystore(self, password: str) -> Keystore:
|
def write_keystore(self, password: str, force: bool = False) -> Keystore:
|
||||||
self.__keystore = Keystore.generate(password=password, keystore_dir=self.keystore_dir)
|
self.__keystore = Keystore.generate(password=password, keystore_dir=self.keystore_dir, force=force)
|
||||||
return self.keystore
|
return self.keystore
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -20,13 +20,13 @@ import json
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import string
|
import string
|
||||||
import tempfile
|
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from os.path import abspath
|
from os.path import abspath
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from secrets import token_bytes
|
from secrets import token_bytes
|
||||||
from typing import Callable, ClassVar, Dict, List, Union, Optional, Tuple
|
from typing import Callable, ClassVar, Dict, List, Union, Optional, Tuple
|
||||||
|
|
||||||
|
import click
|
||||||
import time
|
import time
|
||||||
from constant_sorrow.constants import KEYSTORE_LOCKED
|
from constant_sorrow.constants import KEYSTORE_LOCKED
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
@ -36,6 +36,7 @@ from mnemonic.mnemonic import Mnemonic
|
||||||
from nacl.exceptions import CryptoError
|
from nacl.exceptions import CryptoError
|
||||||
from nacl.secret import SecretBox
|
from nacl.secret import SecretBox
|
||||||
|
|
||||||
|
from nucypher.characters.control.emitters import StdoutEmitter
|
||||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||||
from nucypher.crypto.constants import BLAKE2B
|
from nucypher.crypto.constants import BLAKE2B
|
||||||
from nucypher.crypto.keypairs import HostingKeypair
|
from nucypher.crypto.keypairs import HostingKeypair
|
||||||
|
@ -340,6 +341,7 @@ class Keystore:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def restore(cls, words: str, password: str, keystore_dir: Optional[Path] = None) -> 'Keystore':
|
def restore(cls, words: str, password: str, keystore_dir: Optional[Path] = None) -> 'Keystore':
|
||||||
|
"""Restore a keystore from seed words"""
|
||||||
__mnemonic = Mnemonic(_MNEMONIC_LANGUAGE)
|
__mnemonic = Mnemonic(_MNEMONIC_LANGUAGE)
|
||||||
__secret = __mnemonic.to_entropy(words)
|
__secret = __mnemonic.to_entropy(words)
|
||||||
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
||||||
|
@ -347,14 +349,40 @@ class Keystore:
|
||||||
return keystore
|
return keystore
|
||||||
|
|
||||||
@classmethod
|
@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)
|
mnemonic = Mnemonic(_MNEMONIC_LANGUAGE)
|
||||||
__words = mnemonic.generate(strength=_ENTROPY_BITS)
|
__words = mnemonic.generate(strength=_ENTROPY_BITS)
|
||||||
|
cls._confirm_generate(__words, force=force)
|
||||||
__secret = mnemonic.to_entropy(__words)
|
__secret = mnemonic.to_entropy(__words)
|
||||||
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
path = Keystore.__save(secret=__secret, password=password, keystore_dir=keystore_dir)
|
||||||
keystore = cls(keystore_path=path)
|
keystore = cls(keystore_path=path)
|
||||||
return keystore
|
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
|
@property
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
return self.__id
|
return self.__id
|
||||||
|
|
|
@ -1051,3 +1051,9 @@ def stakeholder_configuration_file_location(custom_filepath):
|
||||||
def mock_teacher_nodes(mocker):
|
def mock_teacher_nodes(mocker):
|
||||||
mock_nodes = tuple(u.rest_url() for u in MOCK_KNOWN_URSULAS_CACHE.values())[0:2]
|
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)
|
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